mirror of
https://github.com/Yeachan-Heo/oh-my-claudecode.git
synced 2026-10-21 10:49:50 -05:00
fix(installer): log CLAUDE.md as already up to date when install leaves it unchanged (#4377)
executeClaudeMdTransaction drops write operations whose planned bytes equal the existing file, so a repeated install over an up-to-date CLAUDE.md returns no CLAUDE.md write. install() chose 'Created CLAUDE.md' whenever no write with existedBefore was present, so the second run logged 'Created CLAUDE.md' for a file that already existed and was not modified. Look up the CLAUDE.md write operation first. No operation logs 'CLAUDE.md already up to date'; an operation with existedBefore keeps 'Updated CLAUDE.md (merged with existing content)'; otherwise 'Created CLAUDE.md' is unchanged. Adds a test that installs twice into one temp config dir and checks the second run logs the new message, not 'Created CLAUDE.md', and leaves the file byte-identical. Co-authored-by: gaebal-gajae <clawdbot@users.noreply.github.com> Co-authored-by: gaebal-gajae <gaebal-gajae@users.noreply.github.com>
This commit is contained in:
3 files changed
+46
-8
No files matched your search
@@ -5,15 +5,15 @@
|
||||
"provenance": {
|
||||
"base": "05c800f40d1ad53b42a78609d2667ef4f726808b",
|
||||
"planningHead": "0a91273e61dbbd47eb0af4c02844409251e08398",
|
||||
"head": "fa0d0b1357fd6a053dd54640d66f1780272a5839",
|
||||
"sourceSha256": "0d81f6b34855bc712fbadff4ff960cdc2217040c2d880a08b331bc01cbf270a6",
|
||||
"head": "36dd164b7200d34d31840f9522e3758d2372916b",
|
||||
"sourceSha256": "29c9bef99d25ae952beb32f0b2665eef0290f836d5bfdb15e81e778100eb5b58",
|
||||
"generatedAt": null,
|
||||
"generator": "scripts/generate-inventory-graph.mjs"
|
||||
},
|
||||
"base": "05c800f40d1ad53b42a78609d2667ef4f726808b",
|
||||
"planningHead": "0a91273e61dbbd47eb0af4c02844409251e08398",
|
||||
"head": "fa0d0b1357fd6a053dd54640d66f1780272a5839",
|
||||
"sourceSha256": "0d81f6b34855bc712fbadff4ff960cdc2217040c2d880a08b331bc01cbf270a6",
|
||||
"head": "36dd164b7200d34d31840f9522e3758d2372916b",
|
||||
"sourceSha256": "29c9bef99d25ae952beb32f0b2665eef0290f836d5bfdb15e81e778100eb5b58",
|
||||
"counts": {
|
||||
"public": {
|
||||
"skills": 48,
|
||||
@@ -88363,5 +88363,5 @@
|
||||
}
|
||||
},
|
||||
"inventorySha256": "d11a3ba23d8ffd964115b1f162f7d2e98bcce5564b14699355479249007ebecd",
|
||||
"manifestSha256": "14757239a259fe1756ffdca667b2c4c9c94f94aeb3212a11ecd97f729b0ff608"
|
||||
"manifestSha256": "89811521915ee6ac40a8c3315eb97a0dee0c1f187255125c4b23db6488374ce8"
|
||||
}
|
||||
@@ -291,4 +291,37 @@ describe('installer bundled + standalone skill sync', () => {
|
||||
logSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it('reports an unchanged CLAUDE.md as already up to date on a repeated install', async () => {
|
||||
const claudeMdPath = join(claudeConfigDir, 'CLAUDE.md');
|
||||
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
try {
|
||||
const firstInstaller = await loadInstallerWithEnv(claudeConfigDir, homeDir);
|
||||
const firstResult = firstInstaller.install({
|
||||
skipClaudeCheck: true,
|
||||
skipHud: true,
|
||||
verbose: true,
|
||||
});
|
||||
|
||||
expect(firstResult.success).toBe(true);
|
||||
expect(logSpy).toHaveBeenCalledWith('Created CLAUDE.md');
|
||||
const firstContent = readFileSync(claudeMdPath, 'utf-8');
|
||||
|
||||
logSpy.mockClear();
|
||||
const secondInstaller = await loadInstallerWithEnv(claudeConfigDir, homeDir);
|
||||
const secondResult = secondInstaller.install({
|
||||
skipClaudeCheck: true,
|
||||
skipHud: true,
|
||||
verbose: true,
|
||||
});
|
||||
|
||||
expect(secondResult.success).toBe(true);
|
||||
expect(logSpy).toHaveBeenCalledWith('CLAUDE.md already up to date');
|
||||
expect(logSpy).not.toHaveBeenCalledWith('Created CLAUDE.md');
|
||||
expect(logSpy).not.toHaveBeenCalledWith('Updated CLAUDE.md (merged with existing content)');
|
||||
expect(readFileSync(claudeMdPath, 'utf-8')).toBe(firstContent);
|
||||
} finally {
|
||||
logSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -2733,9 +2733,14 @@ export function install(options: InstallOptions = {}): InstallResult {
|
||||
});
|
||||
if (!transaction.ok) throw new Error(transaction.error ?? 'CLAUDE.md transaction failed');
|
||||
for (const backupPath of transaction.backups) log(`Backed up existing CLAUDE.md to ${backupPath}`);
|
||||
log(transaction.operations.some(operation => operation.type === 'write' && operation.existedBefore && basename(operation.path) === 'CLAUDE.md')
|
||||
? 'Updated CLAUDE.md (merged with existing content)'
|
||||
: 'Created CLAUDE.md');
|
||||
// The transaction drops writes whose bytes already match, so a missing
|
||||
// CLAUDE.md write op means the existing file was left untouched.
|
||||
const claudeMdWrite = transaction.operations.find(operation => operation.type === 'write' && basename(operation.path) === 'CLAUDE.md');
|
||||
log(!claudeMdWrite
|
||||
? 'CLAUDE.md already up to date'
|
||||
: claudeMdWrite.existedBefore
|
||||
? 'Updated CLAUDE.md (merged with existing content)'
|
||||
: 'Created CLAUDE.md');
|
||||
}
|
||||
|
||||
// Install HUD statusline (skip for project-scoped plugins, skipHud option, or hudEnabled config)
|
||||
|
||||
Reference in new issue
Block a user