fix: surface legacy data warning in instinct-cli status (#2127)

* fix: surface legacy data warning in instinct-cli status (#2036)

When the data directory moved from ~/.claude/homunculus/ to the
XDG-compliant ~/.local/share/ecc-homunculus/, legacy installs with data
still in the old path saw "No instincts found" with no explanation.

Add _warn_legacy_data() to cmd_status so users get a clear, actionable
warning pointing them to the migration script or the CLV2_HOMUNCULUS_DIR
override. Wrap the directory scan in try/except to handle permission
errors gracefully.

Closes #2036

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix: address review feedback — drop unused f-strings, resolve absolute migrate path

Remove extraneous f-prefix from strings without interpolation (ruff F541).
Resolve migrate-homunculus.sh path relative to instinct-cli.py instead of
hard-coding a repo-relative path that only works from the repo root.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix: quote migrate script path to handle spaces

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: kky <lingmu141592@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
xiaoxi
2026-06-07 13:26:22 +08:00
committed by GitHub
parent 3248ac69f0
commit e4dfc1679b
2 changed files with 96 additions and 0 deletions

View File

@@ -219,6 +219,65 @@ test('projects merge deduplicates instincts, appends observations, and removes s
}
});
test('status warns when legacy ~/.claude/homunculus contains files', () => {
const root = createTempDir();
try {
const legacyDir = path.join(root, 'home', '.claude', 'homunculus', 'instincts', 'personal');
fs.mkdirSync(legacyDir, { recursive: true });
fs.writeFileSync(path.join(legacyDir, 'old-instinct.yaml'), '---\nid: old\n---\nOld instinct.\n');
const result = runCli(root, ['status']);
assert.strictEqual(result.status, 0, result.stderr);
assert.match(result.stdout, /LEGACY DATA DETECTED/);
assert.match(result.stdout, /legacy path/i);
assert.match(result.stdout, /migration script/i);
} finally {
cleanupDir(root);
}
});
test('status does not warn when legacy dir is empty', () => {
const root = createTempDir();
try {
const legacyDir = path.join(root, 'home', '.claude', 'homunculus');
fs.mkdirSync(legacyDir, { recursive: true });
const result = runCli(root, ['status']);
assert.strictEqual(result.status, 0, result.stderr);
assert.doesNotMatch(result.stdout, /LEGACY DATA DETECTED/);
} finally {
cleanupDir(root);
}
});
test('status does not warn when no legacy dir exists', () => {
const root = createTempDir();
try {
const result = runCli(root, ['status']);
assert.strictEqual(result.status, 0, result.stderr);
assert.doesNotMatch(result.stdout, /LEGACY DATA DETECTED/);
} finally {
cleanupDir(root);
}
});
test('status does not warn when CLV2_HOMUNCULUS_DIR points at legacy path', () => {
const root = createTempDir();
try {
const legacyDir = path.join(root, 'home', '.claude', 'homunculus', 'instincts', 'personal');
fs.mkdirSync(legacyDir, { recursive: true });
fs.writeFileSync(path.join(legacyDir, 'active.yaml'), '---\nid: active\n---\nActive.\n');
const result = runCli(root, ['status'], {
env: { CLV2_HOMUNCULUS_DIR: path.join(root, 'home', '.claude', 'homunculus') },
});
assert.strictEqual(result.status, 0, result.stderr);
assert.doesNotMatch(result.stdout, /LEGACY DATA DETECTED/);
} finally {
cleanupDir(root);
}
});
test('status migrates legacy no-remote linked worktree project dirs to main worktree id', () => {
const root = createTempDir();
const repoParent = createTempDir();