mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-06-13 19:51:24 +08:00
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:
@@ -703,10 +703,47 @@ def cmd_status(args) -> int:
|
|||||||
days_left = max(0, PENDING_TTL_DAYS - item["age_days"])
|
days_left = max(0, PENDING_TTL_DAYS - item["age_days"])
|
||||||
print(f" - {item['name']} ({days_left}d remaining)")
|
print(f" - {item['name']} ({days_left}d remaining)")
|
||||||
|
|
||||||
|
# Legacy data warning
|
||||||
|
_warn_legacy_data()
|
||||||
|
|
||||||
print(f"\n{'='*60}\n")
|
print(f"\n{'='*60}\n")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def _warn_legacy_data() -> None:
|
||||||
|
"""Warn if legacy ~/.claude/homunculus/ contains data while the active
|
||||||
|
path has moved to the XDG directory."""
|
||||||
|
legacy_dir = Path.home() / ".claude" / "homunculus"
|
||||||
|
if legacy_dir == HOMUNCULUS_DIR:
|
||||||
|
return # CLV2_HOMUNCULUS_DIR explicitly points at the legacy path
|
||||||
|
if not legacy_dir.is_dir():
|
||||||
|
return
|
||||||
|
|
||||||
|
# Count substantive files (skip empty dirs and the directory itself)
|
||||||
|
try:
|
||||||
|
legacy_files = [f for f in legacy_dir.rglob("*") if f.is_file()]
|
||||||
|
except (PermissionError, OSError):
|
||||||
|
print(f"\n Note: legacy directory exists but cannot be read: {legacy_dir}", file=sys.stderr)
|
||||||
|
return
|
||||||
|
if not legacy_files:
|
||||||
|
return
|
||||||
|
|
||||||
|
migrate_script = Path(__file__).resolve().parent / "migrate-homunculus.sh"
|
||||||
|
|
||||||
|
print(f"\n{'!'*60}")
|
||||||
|
print(" LEGACY DATA DETECTED")
|
||||||
|
print(f"{'!'*60}")
|
||||||
|
print(f" Found {len(legacy_files)} file(s) in legacy path:")
|
||||||
|
print(f" {legacy_dir}")
|
||||||
|
print(" Active data directory:")
|
||||||
|
print(f" {HOMUNCULUS_DIR}")
|
||||||
|
print()
|
||||||
|
print(" Run the migration script to move your data:")
|
||||||
|
print(f' bash "{migrate_script}"')
|
||||||
|
print(f" Or set CLV2_HOMUNCULUS_DIR={legacy_dir} to use the legacy path.")
|
||||||
|
print(f"{'!'*60}\n")
|
||||||
|
|
||||||
|
|
||||||
def _print_instincts_by_domain(instincts: list[dict]) -> None:
|
def _print_instincts_by_domain(instincts: list[dict]) -> None:
|
||||||
"""Helper to print instincts grouped by domain."""
|
"""Helper to print instincts grouped by domain."""
|
||||||
by_domain = defaultdict(list)
|
by_domain = defaultdict(list)
|
||||||
|
|||||||
@@ -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', () => {
|
test('status migrates legacy no-remote linked worktree project dirs to main worktree id', () => {
|
||||||
const root = createTempDir();
|
const root = createTempDir();
|
||||||
const repoParent = createTempDir();
|
const repoParent = createTempDir();
|
||||||
|
|||||||
Reference in New Issue
Block a user