hippo-memory 1.61.0 → 1.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -53
- package/dist/agent-memories/apply.d.ts +3 -1
- package/dist/agent-memories/apply.js +23 -1
- package/dist/agent-memories/claude-code.d.ts +4 -1
- package/dist/agent-memories/claude-code.js +53 -9
- package/dist/agent-memories/report.d.ts +1 -0
- package/dist/agent-memories/report.js +2 -2
- package/dist/agent-memories/sync.d.ts +3 -3
- package/dist/agent-memories/sync.js +46 -24
- package/dist/agent-memories/types.d.ts +0 -2
- package/dist/ambient-store.d.ts +4 -4
- package/dist/ambient-store.js +4 -3
- package/dist/api/assemble.d.ts +7 -10
- package/dist/api/assemble.js +62 -66
- package/dist/api/audit.d.ts +2 -2
- package/dist/api/audit.js +2 -2
- package/dist/api/auth.d.ts +6 -6
- package/dist/api/auth.js +5 -7
- package/dist/api/context-select.d.ts +50 -0
- package/dist/api/context-select.js +342 -0
- package/dist/api/context-types.d.ts +12 -17
- package/dist/api/context.d.ts +5 -4
- package/dist/api/context.js +208 -535
- package/dist/api/dormant.js +1 -1
- package/dist/api/drill-down.d.ts +4 -4
- package/dist/api/drill-down.js +56 -41
- package/dist/api/outcome.d.ts +8 -13
- package/dist/api/outcome.js +13 -17
- package/dist/api/promote.d.ts +5 -8
- package/dist/api/promote.js +55 -66
- package/dist/api/quarantine.js +3 -2
- package/dist/api/recall-types.d.ts +43 -55
- package/dist/api/recall.d.ts +3 -3
- package/dist/api/recall.js +310 -456
- package/dist/api/remember.d.ts +2 -2
- package/dist/api/sleep.d.ts +7 -35
- package/dist/api/sleep.js +206 -220
- package/dist/api/tokens.d.ts +2 -2
- package/dist/api/tokens.js +2 -2
- package/dist/api/types.d.ts +6 -10
- package/dist/api/types.js +2 -4
- package/dist/audit-prune.d.ts +4 -6
- package/dist/audit-prune.js +3 -5
- package/dist/audit.js +11 -33
- package/dist/auth.d.ts +8 -9
- package/dist/auth.js +5 -7
- package/dist/autolearn.d.ts +1 -1
- package/dist/autolearn.js +1 -1
- package/dist/availability.js +3 -5
- package/dist/capture/command.d.ts +5 -4
- package/dist/capture/command.js +8 -22
- package/dist/capture/compact.d.ts +2 -2
- package/dist/capture/compact.js +11 -15
- package/dist/capture/extract.js +37 -115
- package/dist/capture-error.d.ts +1 -1
- package/dist/capture-error.js +1 -1
- package/dist/churn-git.d.ts +1 -1
- package/dist/churn-git.js +1 -1
- package/dist/cli/audit.js +3 -4
- package/dist/cli/auth.js +3 -6
- package/dist/cli/briefs.js +324 -306
- package/dist/cli/context.js +44 -34
- package/dist/cli/continuity.js +283 -271
- package/dist/cli/curate.d.ts +1 -1
- package/dist/cli/curate.js +43 -58
- package/dist/cli/dag.js +5 -9
- package/dist/cli/decisions.js +334 -345
- package/dist/cli/explain.js +68 -61
- package/dist/cli/goals.js +1 -1
- package/dist/cli/init.js +1 -1
- package/dist/cli/maintenance.js +62 -51
- package/dist/cli/playbooks.js +391 -379
- package/dist/cli/projects.js +11 -6
- package/dist/cli/recall.js +30 -44
- package/dist/cli/remember.js +118 -87
- package/dist/cli/session-hooks.js +106 -115
- package/dist/cli/setup.d.ts +1 -1
- package/dist/cli/setup.js +267 -250
- package/dist/cli/shared.js +3 -3
- package/dist/cli/slack.js +1 -1
- package/dist/cli/sleep.js +27 -1
- package/dist/cli/status.d.ts +4 -4
- package/dist/cli/status.js +80 -76
- package/dist/cli/transfer.js +88 -107
- package/dist/cli/usage.js +9 -6
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +4 -9
- package/dist/compaction-record.d.ts +2 -2
- package/dist/compaction-record.js +89 -68
- package/dist/compare.d.ts +11 -16
- package/dist/compare.js +11 -16
- package/dist/config.d.ts +19 -18
- package/dist/config.js +90 -67
- package/dist/connectors/github/backfill.d.ts +2 -2
- package/dist/connectors/github/backfill.js +8 -15
- package/dist/connectors/github/cli-impl.js +3 -8
- package/dist/connectors/github/deletion.d.ts +5 -12
- package/dist/connectors/github/deletion.js +5 -12
- package/dist/connectors/github/dlq.d.ts +6 -9
- package/dist/connectors/github/dlq.js +2 -3
- package/dist/connectors/github/ingest.d.ts +5 -7
- package/dist/connectors/github/ingest.js +8 -12
- package/dist/connectors/github/octokit-client.d.ts +3 -5
- package/dist/connectors/github/octokit-client.js +5 -6
- package/dist/connectors/github/signature.d.ts +9 -39
- package/dist/connectors/github/signature.js +9 -39
- package/dist/connectors/github/tenant-routing.d.ts +1 -1
- package/dist/connectors/github/tenant-routing.js +1 -1
- package/dist/connectors/github/transform.js +2 -2
- package/dist/connectors/github/types.d.ts +2 -10
- package/dist/connectors/github/types.js +1 -3
- package/dist/connectors/slack/deletion.d.ts +3 -8
- package/dist/connectors/slack/deletion.js +3 -8
- package/dist/connectors/slack/dlq.d.ts +1 -1
- package/dist/connectors/slack/ingest.d.ts +1 -1
- package/dist/connectors/slack/ingest.js +7 -16
- package/dist/connectors/slack/signature.d.ts +1 -1
- package/dist/connectors/slack/tenant-routing.d.ts +3 -5
- package/dist/connectors/slack/tenant-routing.js +3 -5
- package/dist/connectors/slack/transform.d.ts +5 -6
- package/dist/connectors/slack/transform.js +5 -6
- package/dist/connectors/slack/types.d.ts +2 -6
- package/dist/connectors/slack/types.js +1 -3
- package/dist/connectors/slack/web-client.js +10 -3
- package/dist/connectors/slack/workspaces.d.ts +3 -5
- package/dist/connectors/slack/workspaces.js +3 -5
- package/dist/consolidate/conflicts.js +3 -14
- package/dist/consolidate/decay.js +9 -29
- package/dist/consolidate/llm-passes.js +4 -5
- package/dist/consolidate/merge.js +8 -23
- package/dist/consolidate/run.d.ts +1 -8
- package/dist/consolidate/run.js +3 -25
- package/dist/consolidate/sleep.js +5 -17
- package/dist/consolidate/traces.js +9 -21
- package/dist/customer-notes.d.ts +5 -7
- package/dist/customer-notes.js +82 -76
- package/dist/dag.d.ts +10 -21
- package/dist/dag.js +189 -203
- package/dist/db/continuity.js +2 -2
- package/dist/db/migrations/v14.js +1 -1
- package/dist/db/migrations/v15.js +1 -2
- package/dist/db/migrations/v16.js +3 -4
- package/dist/db/migrations/v17.js +2 -3
- package/dist/db/migrations/v19.js +1 -1
- package/dist/db/migrations/v20.js +1 -1
- package/dist/db/migrations/v21.js +2 -6
- package/dist/db/migrations/v22.js +2 -4
- package/dist/db/migrations/v23.js +1 -1
- package/dist/db/migrations/v24.js +4 -6
- package/dist/db/migrations/v25.js +2 -3
- package/dist/db/migrations/v26.js +3 -3
- package/dist/db/migrations/v27.js +2 -10
- package/dist/db/migrations/v28.js +5 -8
- package/dist/db/migrations/v29.js +3 -4
- package/dist/db/migrations/v30.js +2 -2
- package/dist/db/migrations/v31.js +1 -1
- package/dist/db/migrations/v32.js +1 -1
- package/dist/db/migrations/v33.js +3 -3
- package/dist/db/migrations/v34.js +1 -1
- package/dist/db/migrations/v35.js +3 -4
- package/dist/db/migrations/v36.js +3 -4
- package/dist/db/migrations/v37.js +5 -5
- package/dist/db/migrations/v38.js +7 -8
- package/dist/db/migrations/v39.js +1 -1
- package/dist/db/migrations/v40.js +4 -16
- package/dist/db/migrations/v41.js +3 -4
- package/dist/db/migrations/v42.js +3 -4
- package/dist/db/migrations/v45.js +1 -1
- package/dist/db/migrations/v46.js +1 -1
- package/dist/db/migrations/v47.js +1 -1
- package/dist/db/migrations/v48.js +1 -1
- package/dist/decisions.d.ts +2 -2
- package/dist/decisions.js +97 -80
- package/dist/dedupe.js +86 -61
- package/dist/delivery-recorder.js +154 -135
- package/dist/doctor.js +129 -110
- package/dist/dormant.js +1 -4
- package/dist/embedding-provider.d.ts +4 -8
- package/dist/embedding-provider.js +4 -8
- package/dist/embeddings.js +55 -47
- package/dist/env.d.ts +1 -1
- package/dist/env.js +12 -12
- package/dist/escape.d.ts +5 -0
- package/dist/escape.js +10 -0
- package/dist/eval-stats.d.ts +1 -2
- package/dist/eval-stats.js +1 -2
- package/dist/eval-suite.js +27 -21
- package/dist/extract.js +4 -9
- package/dist/failure-log.d.ts +3 -3
- package/dist/failure-log.js +1 -1
- package/dist/forward-claim-detector.d.ts +2 -4
- package/dist/forward-claim-detector.js +6 -11
- package/dist/goals.d.ts +3 -3
- package/dist/goals.js +103 -91
- package/dist/graph/read.d.ts +2 -2
- package/dist/graph/read.js +5 -6
- package/dist/graph/types.d.ts +8 -8
- package/dist/graph/write.d.ts +7 -14
- package/dist/graph/write.js +16 -23
- package/dist/graph-extract.d.ts +7 -8
- package/dist/graph-extract.js +62 -72
- package/dist/graph-recall.d.ts +2 -2
- package/dist/graph-recall.js +55 -49
- package/dist/graph-stream.d.ts +5 -6
- package/dist/graph-stream.js +66 -57
- package/dist/graph-view.d.ts +2 -2
- package/dist/graph-view.js +7 -7
- package/dist/half-life-migration.d.ts +1 -2
- package/dist/half-life-migration.js +2 -3
- package/dist/hooks/codex-session.js +1 -1
- package/dist/hooks/codex-wrapper.d.ts +1 -1
- package/dist/hooks/codex-wrapper.js +3 -2
- package/dist/hooks/json-hooks.d.ts +2 -2
- package/dist/hooks/json-hooks.js +5 -4
- package/dist/hooks/opencode.d.ts +1 -1
- package/dist/hooks/opencode.js +5 -4
- package/dist/hooks/shared.d.ts +3 -7
- package/dist/hooks/shared.js +1 -8
- package/dist/http-util.d.ts +2 -3
- package/dist/http-util.js +3 -0
- package/dist/importers/core.d.ts +2 -9
- package/dist/importers/core.js +15 -30
- package/dist/importers/sources.js +2 -1
- package/dist/importers/vault.js +2 -20
- package/dist/incidents.d.ts +1 -1
- package/dist/incidents.js +46 -39
- package/dist/instruction-detect.d.ts +1 -1
- package/dist/instruction-detect.js +1 -1
- package/dist/invalidation.d.ts +3 -0
- package/dist/invalidation.js +160 -114
- package/dist/json.d.ts +5 -0
- package/dist/json.js +4 -0
- package/dist/judgment.js +1 -2
- package/dist/local-embedding.js +1 -1
- package/dist/mcp/admin-tools.js +7 -17
- package/dist/mcp/format.js +1 -1
- package/dist/mcp/framing.js +3 -6
- package/dist/mcp/protocol.d.ts +2 -5
- package/dist/mcp/protocol.js +1 -3
- package/dist/mcp/recall-tools.js +12 -15
- package/dist/mcp/request.js +4 -3
- package/dist/mcp/session-state.js +2 -3
- package/dist/mcp/stdio.js +2 -1
- package/dist/mcp/tools.js +9 -6
- package/dist/memory-value-weights.d.ts +5 -8
- package/dist/memory-value-weights.js +5 -8
- package/dist/memory-value.d.ts +13 -13
- package/dist/memory-value.js +26 -37
- package/dist/memory.d.ts +20 -22
- package/dist/memory.js +24 -48
- package/dist/multihop.d.ts +1 -1
- package/dist/multihop.js +3 -2
- package/dist/owner-validation.d.ts +4 -5
- package/dist/owner-validation.js +4 -5
- package/dist/physics.d.ts +4 -4
- package/dist/physics.js +7 -9
- package/dist/policies.d.ts +9 -10
- package/dist/policies.js +96 -81
- package/dist/postinstall.js +3 -6
- package/dist/predictions/planning-fallacy.d.ts +9 -14
- package/dist/predictions/planning-fallacy.js +10 -16
- package/dist/predictions/store.d.ts +15 -23
- package/dist/predictions/store.js +36 -33
- package/dist/processes.d.ts +2 -7
- package/dist/processes.js +88 -72
- package/dist/project-briefs.d.ts +2 -3
- package/dist/project-briefs.js +141 -118
- package/dist/project-identity.d.ts +22 -9
- package/dist/project-identity.js +47 -12
- package/dist/project-merge.d.ts +28 -5
- package/dist/project-merge.js +213 -46
- package/dist/project-remote.d.ts +12 -0
- package/dist/project-remote.js +138 -0
- package/dist/prompt-recall.js +1 -2
- package/dist/rate-limit.d.ts +1 -1
- package/dist/rate-limit.js +1 -1
- package/dist/raw-archive.d.ts +9 -0
- package/dist/raw-archive.js +70 -53
- package/dist/recall-history.d.ts +19 -20
- package/dist/recall-history.js +24 -42
- package/dist/recall-pipeline.js +4 -28
- package/dist/recall-scope.d.ts +7 -8
- package/dist/recall-scope.js +7 -8
- package/dist/recall-trace.d.ts +5 -9
- package/dist/recall-trace.js +6 -10
- package/dist/refine-llm.d.ts +1 -1
- package/dist/refine-llm.js +2 -2
- package/dist/reject-flow.d.ts +3 -4
- package/dist/reject-flow.js +122 -117
- package/dist/rejection.d.ts +5 -6
- package/dist/rejection.js +7 -15
- package/dist/rerankers/clef.d.ts +1 -1
- package/dist/rerankers/jev.d.ts +1 -2
- package/dist/rerankers/jev.js +4 -5
- package/dist/rerankers/llm.d.ts +1 -2
- package/dist/rerankers/llm.js +1 -2
- package/dist/rerankers/types.d.ts +1 -2
- package/dist/rrf.d.ts +2 -2
- package/dist/rrf.js +2 -2
- package/dist/search/bm25-search.d.ts +1 -1
- package/dist/search/bm25-search.js +2 -1
- package/dist/search/boosts.js +2 -1
- package/dist/search/hybrid.d.ts +1 -1
- package/dist/search/hybrid.js +2 -1
- package/dist/search/physics-search.d.ts +1 -1
- package/dist/search/physics-search.js +2 -1
- package/dist/search/types.d.ts +2 -0
- package/dist/search/types.js +3 -1
- package/dist/secret-detect.d.ts +4 -5
- package/dist/secret-detect.js +6 -10
- package/dist/server/auth.js +5 -5
- package/dist/server/client-ip.js +1 -1
- package/dist/server/cursor.js +2 -1
- package/dist/server/mcp-http.js +4 -4
- package/dist/server/request.d.ts +3 -6
- package/dist/server/request.js +6 -7
- package/dist/server/routes/admin.js +5 -4
- package/dist/server/routes/customer-notes.js +6 -5
- package/dist/server/routes/decisions.js +4 -3
- package/dist/server/routes/incidents.js +7 -5
- package/dist/server/routes/memories.js +7 -7
- package/dist/server/routes/policies.js +3 -2
- package/dist/server/routes/predictions.js +12 -15
- package/dist/server/routes/processes.js +3 -2
- package/dist/server/routes/project-briefs.js +8 -7
- package/dist/server/routes/recall.js +95 -93
- package/dist/server/routes/skills.js +6 -5
- package/dist/server/types.d.ts +1 -1
- package/dist/server/validation.d.ts +1 -2
- package/dist/server/validation.js +7 -14
- package/dist/server-detect.js +72 -58
- package/dist/server.d.ts +2 -2
- package/dist/server.js +131 -117
- package/dist/shared.d.ts +26 -17
- package/dist/shared.js +102 -104
- package/dist/skills.d.ts +3 -3
- package/dist/skills.js +88 -72
- package/dist/store/audit-event.d.ts +2 -2
- package/dist/store/audit-event.js +1 -1
- package/dist/store/candidates.d.ts +2 -2
- package/dist/store/candidates.js +4 -3
- package/dist/store/conflicts.js +30 -22
- package/dist/store/delete-and-batch.d.ts +11 -14
- package/dist/store/delete-and-batch.js +40 -91
- package/dist/store/entry-reads.d.ts +17 -25
- package/dist/store/entry-reads.js +59 -40
- package/dist/store/entry-row.d.ts +6 -24
- package/dist/store/entry-row.js +6 -24
- package/dist/store/entry-writes.d.ts +6 -7
- package/dist/store/entry-writes.js +13 -11
- package/dist/store/handoffs.d.ts +1 -1
- package/dist/store/handoffs.js +7 -10
- package/dist/store/index-and-stats.d.ts +2 -6
- package/dist/store/index-and-stats.js +4 -10
- package/dist/store/mirrors.d.ts +6 -19
- package/dist/store/mirrors.js +14 -39
- package/dist/store/open.js +9 -31
- package/dist/store/rows.d.ts +5 -11
- package/dist/store/rows.js +6 -11
- package/dist/store/search-rows.d.ts +17 -34
- package/dist/store/search-rows.js +34 -56
- package/dist/store/sessions.d.ts +4 -5
- package/dist/store/sessions.js +5 -6
- package/dist/store/summaries.d.ts +13 -17
- package/dist/store/summaries.js +26 -70
- package/dist/support-bundle.js +4 -8
- package/dist/tenant.d.ts +1 -5
- package/dist/token-ledger.d.ts +1 -1
- package/dist/token-ledger.js +3 -5
- package/dist/trace.js +1 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/working-memory.d.ts +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/cli/setup.js
CHANGED
|
@@ -15,196 +15,208 @@ import { log } from '../log.js';
|
|
|
15
15
|
import { printError } from './output.js';
|
|
16
16
|
import { printAgentImport, installCodexMemoryHooks, setupDailySchedule } from './shared.js';
|
|
17
17
|
import { HOOK_MARKERS, HOOKS, hippoBlock } from './hook-blocks.js';
|
|
18
|
+
import { escapeRegex } from '../escape.js';
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
19
20
|
// Hook install/uninstall
|
|
20
21
|
// ---------------------------------------------------------------------------
|
|
21
|
-
export function cmdHook(args
|
|
22
|
+
export function cmdHook(args) {
|
|
22
23
|
const subcommand = args[0];
|
|
23
24
|
const target = args[1];
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (subcommand === '
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
if (subcommand === 'list')
|
|
26
|
+
return hookList();
|
|
27
|
+
if (subcommand === 'install')
|
|
28
|
+
return hookInstall(target);
|
|
29
|
+
if (subcommand === 'uninstall')
|
|
30
|
+
return hookUninstall(target);
|
|
31
|
+
printError('Usage: hippo hook <install|uninstall|list> [target]');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
// Cursor has no init row, so the AGENTS.md block is usually another agent's, and Cursor reads that one as it is.
|
|
35
|
+
function othersBlock(target, text) {
|
|
36
|
+
return target === 'cursor' && hippoBlock(text)?.owner !== 'cursor';
|
|
37
|
+
}
|
|
38
|
+
function hookList() {
|
|
39
|
+
console.log('Available hooks:\n');
|
|
40
|
+
for (const [name, hook] of Object.entries(HOOKS)) {
|
|
41
|
+
console.log(` ${name.padEnd(15)} -> ${hook.file} (${hook.description})`);
|
|
34
42
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
agentFileTouched = true;
|
|
43
|
+
console.log('\nUsage: hippo hook install <name>');
|
|
44
|
+
console.log(' hippo hook uninstall <name>');
|
|
45
|
+
}
|
|
46
|
+
function hookInstall(target) {
|
|
47
|
+
if (!target || !HOOKS[target]) {
|
|
48
|
+
printError(`Unknown hook target: ${target ?? '(none)'}`);
|
|
49
|
+
printError(` Available: ${Object.keys(HOOKS).join(', ')}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const hook = HOOKS[target];
|
|
53
|
+
patchAgentFile(hook, target);
|
|
54
|
+
// For Claude Code, also install SessionEnd+SessionStart entries in its
|
|
55
|
+
// settings file.
|
|
56
|
+
if (target === 'claude-code') {
|
|
57
|
+
printClaudeHookInstall(installJsonHooks(target));
|
|
58
|
+
}
|
|
59
|
+
else if (target === 'opencode') {
|
|
60
|
+
installOpencodeHook();
|
|
61
|
+
}
|
|
62
|
+
else if (target === 'codex') {
|
|
63
|
+
installCodexMemoryHooks('');
|
|
64
|
+
// The wrapper stays the capture path; the hooks above work without it, so a missing launcher is not an error.
|
|
65
|
+
if (detectRealCodexPath()) {
|
|
66
|
+
const result = installCodexWrapper();
|
|
67
|
+
console.log(`Installed Codex session-end integration -> ${result.metadataPath}`);
|
|
68
|
+
console.log(` Wrapped detected Codex launcher at ${result.commandPath}`);
|
|
62
69
|
}
|
|
63
70
|
else {
|
|
64
|
-
|
|
65
|
-
// in directories that don't already have one — avoids polluting cwd with
|
|
66
|
-
// files the user didn't ask for. The settings.json hook below is still
|
|
67
|
-
// installed for claude-code, so the consolidation hook still runs.
|
|
68
|
-
console.log(`${hook.file} not found in ${process.cwd()} — skipping agent-instructions patch.`);
|
|
69
|
-
console.log(` Create ${hook.file} and re-run \`hippo hook install ${target}\` if you want the agent prompt.`);
|
|
70
|
-
}
|
|
71
|
-
// For Claude Code, also install SessionEnd+SessionStart entries in its
|
|
72
|
-
// settings file.
|
|
73
|
-
if (target === 'claude-code') {
|
|
74
|
-
const result = installJsonHooks(target);
|
|
75
|
-
if (result.installedSessionEnd) {
|
|
76
|
-
console.log(`Installed hippo session-end SessionEnd hook in ${result.target} settings`);
|
|
77
|
-
}
|
|
78
|
-
if (result.installedSessionStart) {
|
|
79
|
-
console.log(`Installed hippo last-sleep SessionStart hook in ${result.target} settings`);
|
|
80
|
-
}
|
|
81
|
-
if (result.installedUserPromptSubmit) {
|
|
82
|
-
console.log(`Installed hippo pinned-inject UserPromptSubmit hook in ${result.target} settings`);
|
|
83
|
-
}
|
|
84
|
-
if (result.installedPreCompact) {
|
|
85
|
-
console.log(`Installed hippo pre-compact PreCompact hook in ${result.target} settings`);
|
|
86
|
-
}
|
|
87
|
-
if (result.installedCompactResume) {
|
|
88
|
-
console.log(`Installed hippo compact-resume SessionStart(compact) hook in ${result.target} settings`);
|
|
89
|
-
}
|
|
90
|
-
if (result.installedPostCompact) {
|
|
91
|
-
console.log(`Installed hippo post-compact PostCompact hook in ${result.target} settings`);
|
|
92
|
-
}
|
|
93
|
-
if (result.installedCaptureError) {
|
|
94
|
-
console.log(`Installed hippo capture-error PostToolUseFailure hook in ${result.target} settings`);
|
|
95
|
-
}
|
|
96
|
-
if (result.migratedFromStop) {
|
|
97
|
-
console.log(`Migrated legacy Stop hook → SessionEnd (was running every turn; now fires once on session exit)`);
|
|
98
|
-
}
|
|
99
|
-
if (result.migratedSplitSessionEnd) {
|
|
100
|
-
console.log(`Migrated split sleep+capture SessionEnd entries → single detached hippo session-end`);
|
|
101
|
-
}
|
|
102
|
-
else if (result.migratedLegacySessionEnd) {
|
|
103
|
-
console.log(`Migrated legacy SessionEnd entry to the new detached form`);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
else if (target === 'opencode') {
|
|
107
|
-
// opencode uses a TS plugin, not JSON hooks. See src/hooks.ts.
|
|
108
|
-
const result = installOpencodePlugin();
|
|
109
|
-
if (result.installed) {
|
|
110
|
-
console.log(`Installed hippo opencode plugin at ${result.pluginPath}`);
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
console.log(`opencode plugin already up to date at ${result.pluginPath}`);
|
|
114
|
-
}
|
|
115
|
-
if (result.migratedLegacyHooks) {
|
|
116
|
-
console.log(`Removed legacy Claude Code-style hooks block from opencode.json — opencode can now launch`);
|
|
117
|
-
}
|
|
118
|
-
if (result.jsonRepairFailed) {
|
|
119
|
-
console.log(`WARNING: opencode.json is unparseable; legacy hooks block could not be auto-removed. Fix the file manually.`);
|
|
120
|
-
}
|
|
71
|
+
console.log('No codex launcher on PATH, so session-end capture was not set up; re-run once `codex` is on PATH.');
|
|
121
72
|
}
|
|
122
|
-
else if (target === 'codex') {
|
|
123
|
-
installCodexMemoryHooks('');
|
|
124
|
-
// The wrapper stays the capture path; the hooks above work without it, so a missing launcher is not an error.
|
|
125
|
-
if (detectRealCodexPath()) {
|
|
126
|
-
const result = installCodexWrapper();
|
|
127
|
-
console.log(`Installed Codex session-end integration -> ${result.metadataPath}`);
|
|
128
|
-
console.log(` Wrapped detected Codex launcher at ${result.commandPath}`);
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
console.log('No codex launcher on PATH, so session-end capture was not set up; re-run once `codex` is on PATH.');
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return;
|
|
135
73
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
74
|
+
}
|
|
75
|
+
function patchAgentFile(hook, target) {
|
|
76
|
+
const filepath = path.resolve(process.cwd(), hook.file);
|
|
77
|
+
const block = `${HOOK_MARKERS.start}\n${hook.content}\n${HOOK_MARKERS.end}`;
|
|
78
|
+
if (fs.existsSync(filepath)) {
|
|
79
|
+
const existing = fs.readFileSync(filepath, 'utf8');
|
|
80
|
+
if (existing.includes(HOOK_MARKERS.start) && othersBlock(target, existing)) {
|
|
81
|
+
console.log(`${hook.file} already has a hippo block, which Cursor reads; left it as is.`);
|
|
140
82
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const owner = hippoBlock(existing)?.owner;
|
|
147
|
-
const whose = owner ? `hippo wrote it for ${owner}` : 'it has been edited, so hippo cannot tell whose it is';
|
|
148
|
-
console.log(`Left the hippo block in ${hook.file}: ${whose}. Delete it by hand if no agent needs it.`);
|
|
149
|
-
}
|
|
150
|
-
else if (existing.includes(HOOK_MARKERS.start)) {
|
|
151
|
-
fs.writeFileSync(filepath, withoutHookBlock(existing) + '\n', 'utf8');
|
|
152
|
-
console.log(`Removed Hippo hook from ${hook.file}`);
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
console.log(`No Hippo hook found in ${hook.file}.`);
|
|
156
|
-
}
|
|
83
|
+
else if (existing.includes(HOOK_MARKERS.start)) {
|
|
84
|
+
const re = new RegExp(`${escapeRegex(HOOK_MARKERS.start)}[\\s\\S]*?${escapeRegex(HOOK_MARKERS.end)}`, 'g');
|
|
85
|
+
const updated = existing.replace(re, block);
|
|
86
|
+
fs.writeFileSync(filepath, updated, 'utf8');
|
|
87
|
+
console.log(`Updated Hippo hook in ${hook.file}`);
|
|
157
88
|
}
|
|
158
89
|
else {
|
|
159
|
-
|
|
90
|
+
const sep = existing.endsWith('\n') ? '\n' : '\n\n';
|
|
91
|
+
fs.writeFileSync(filepath, existing + sep + block + '\n', 'utf8');
|
|
92
|
+
console.log(`Installed Hippo hook in ${hook.file} (appended)`);
|
|
160
93
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Do not create a new agent-instructions file (CLAUDE.md, AGENTS.md, etc.) in directories that don't already have one —
|
|
97
|
+
// avoids polluting cwd with files the user didn't ask for. The settings.json hook below is still installed for
|
|
98
|
+
// claude-code, so the consolidation hook still runs.
|
|
99
|
+
console.log(`${hook.file} not found in ${process.cwd()} — skipping agent-instructions patch.`);
|
|
100
|
+
console.log(` Create ${hook.file} and re-run \`hippo hook install ${target}\` if you want the agent prompt.`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function printClaudeHookInstall(result) {
|
|
104
|
+
if (result.installedSessionEnd) {
|
|
105
|
+
console.log(`Installed hippo session-end SessionEnd hook in ${result.target} settings`);
|
|
106
|
+
}
|
|
107
|
+
if (result.installedSessionStart) {
|
|
108
|
+
console.log(`Installed hippo last-sleep SessionStart hook in ${result.target} settings`);
|
|
109
|
+
}
|
|
110
|
+
if (result.installedUserPromptSubmit) {
|
|
111
|
+
console.log(`Installed hippo pinned-inject UserPromptSubmit hook in ${result.target} settings`);
|
|
112
|
+
}
|
|
113
|
+
if (result.installedPreCompact) {
|
|
114
|
+
console.log(`Installed hippo pre-compact PreCompact hook in ${result.target} settings`);
|
|
115
|
+
}
|
|
116
|
+
if (result.installedCompactResume) {
|
|
117
|
+
console.log(`Installed hippo compact-resume SessionStart(compact) hook in ${result.target} settings`);
|
|
118
|
+
}
|
|
119
|
+
if (result.installedPostCompact) {
|
|
120
|
+
console.log(`Installed hippo post-compact PostCompact hook in ${result.target} settings`);
|
|
121
|
+
}
|
|
122
|
+
if (result.installedCaptureError) {
|
|
123
|
+
console.log(`Installed hippo capture-error PostToolUseFailure hook in ${result.target} settings`);
|
|
124
|
+
}
|
|
125
|
+
if (result.migratedFromStop) {
|
|
126
|
+
console.log(`Migrated legacy Stop hook → SessionEnd (was running every turn; now fires once on session exit)`);
|
|
127
|
+
}
|
|
128
|
+
if (result.migratedSplitSessionEnd) {
|
|
129
|
+
console.log(`Migrated split sleep+capture SessionEnd entries → single detached hippo session-end`);
|
|
130
|
+
}
|
|
131
|
+
else if (result.migratedLegacySessionEnd) {
|
|
132
|
+
console.log(`Migrated legacy SessionEnd entry to the new detached form`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function installOpencodeHook() {
|
|
136
|
+
// opencode uses a TS plugin, not JSON hooks. See src/hooks.ts.
|
|
137
|
+
const result = installOpencodePlugin();
|
|
138
|
+
if (result.installed) {
|
|
139
|
+
console.log(`Installed hippo opencode plugin at ${result.pluginPath}`);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
console.log(`opencode plugin already up to date at ${result.pluginPath}`);
|
|
143
|
+
}
|
|
144
|
+
if (result.migratedLegacyHooks) {
|
|
145
|
+
console.log(`Removed legacy Claude Code-style hooks block from opencode.json — opencode can now launch`);
|
|
146
|
+
}
|
|
147
|
+
if (result.jsonRepairFailed) {
|
|
148
|
+
console.log(`WARNING: opencode.json is unparseable; legacy hooks block could not be auto-removed. Fix the file manually.`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function hookUninstall(target) {
|
|
152
|
+
if (!target || !HOOKS[target]) {
|
|
153
|
+
printError(`Unknown hook target: ${target ?? '(none)'}`);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
unpatchAgentFile(HOOKS[target], target);
|
|
157
|
+
// For Claude Code, also strip its SessionEnd/SessionStart entries.
|
|
158
|
+
if (target === 'claude-code') {
|
|
159
|
+
if (uninstallJsonHooks(target)) {
|
|
160
|
+
console.log(`Removed hippo hooks from ${target} settings`);
|
|
166
161
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
162
|
+
}
|
|
163
|
+
else if (target === 'opencode') {
|
|
164
|
+
// opencode uses a TS plugin; uninstall removes the plugin file AND
|
|
165
|
+
// also runs the legacy-hooks migration so the downgrade/remove path
|
|
166
|
+
// leaves opencode launchable.
|
|
167
|
+
if (uninstallOpencodePlugin()) {
|
|
168
|
+
console.log(`Removed hippo opencode plugin (and any legacy hooks block from opencode.json)`);
|
|
174
169
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
if (uninstallCodexWrapper()) {
|
|
180
|
-
console.log('Removed Codex wrapper integration');
|
|
181
|
-
}
|
|
170
|
+
}
|
|
171
|
+
else if (target === 'codex') {
|
|
172
|
+
if (uninstallJsonHooks('codex')) {
|
|
173
|
+
console.log(`Removed hippo's Codex memory hooks from ${resolveJsonHookPaths('codex').settings}`);
|
|
182
174
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const legacy = path.resolve(process.cwd(), '.cursorrules');
|
|
186
|
-
const old = fs.existsSync(legacy) ? fs.readFileSync(legacy, 'utf8') : '';
|
|
187
|
-
if (old.includes(HOOK_MARKERS.start)) {
|
|
188
|
-
const left = withoutHookBlock(old);
|
|
189
|
-
if (left)
|
|
190
|
-
fs.writeFileSync(legacy, left + '\n', 'utf8');
|
|
191
|
-
else
|
|
192
|
-
fs.unlinkSync(legacy);
|
|
193
|
-
console.log(left ? 'Removed the old Hippo hook from .cursorrules' : 'Deleted .cursorrules, which held only the old Hippo hook');
|
|
194
|
-
}
|
|
175
|
+
if (uninstallCodexWrapper()) {
|
|
176
|
+
console.log('Removed Codex wrapper integration');
|
|
195
177
|
}
|
|
178
|
+
}
|
|
179
|
+
else if (target === 'cursor') {
|
|
180
|
+
removeLegacyCursorRules();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function unpatchAgentFile(hook, target) {
|
|
184
|
+
const filepath = path.resolve(process.cwd(), hook.file);
|
|
185
|
+
if (!fs.existsSync(filepath)) {
|
|
186
|
+
console.log(`${hook.file} not found, skipping agent-instructions uninstall.`);
|
|
196
187
|
return;
|
|
197
188
|
}
|
|
198
|
-
|
|
199
|
-
|
|
189
|
+
const existing = fs.readFileSync(filepath, 'utf8');
|
|
190
|
+
if (existing.includes(HOOK_MARKERS.start) && othersBlock(target, existing)) {
|
|
191
|
+
const owner = hippoBlock(existing)?.owner;
|
|
192
|
+
const whose = owner ? `hippo wrote it for ${owner}` : 'it has been edited, so hippo cannot tell whose it is';
|
|
193
|
+
console.log(`Left the hippo block in ${hook.file}: ${whose}. Delete it by hand if no agent needs it.`);
|
|
194
|
+
}
|
|
195
|
+
else if (existing.includes(HOOK_MARKERS.start)) {
|
|
196
|
+
fs.writeFileSync(filepath, withoutHookBlock(existing) + '\n', 'utf8');
|
|
197
|
+
console.log(`Removed Hippo hook from ${hook.file}`);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
console.log(`No Hippo hook found in ${hook.file}.`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Older hippo wrote Cursor's block to .cursorrules, creating the file when it was missing.
|
|
204
|
+
function removeLegacyCursorRules() {
|
|
205
|
+
const legacy = path.resolve(process.cwd(), '.cursorrules');
|
|
206
|
+
const old = fs.existsSync(legacy) ? fs.readFileSync(legacy, 'utf8') : '';
|
|
207
|
+
if (old.includes(HOOK_MARKERS.start)) {
|
|
208
|
+
const left = withoutHookBlock(old);
|
|
209
|
+
if (left)
|
|
210
|
+
fs.writeFileSync(legacy, left + '\n', 'utf8');
|
|
211
|
+
else
|
|
212
|
+
fs.unlinkSync(legacy);
|
|
213
|
+
console.log(left ? 'Removed the old Hippo hook from .cursorrules' : 'Deleted .cursorrules, which held only the old Hippo hook');
|
|
214
|
+
}
|
|
200
215
|
}
|
|
201
216
|
function withoutHookBlock(text) {
|
|
202
217
|
const re = new RegExp(`\\n?${escapeRegex(HOOK_MARKERS.start)}[\\s\\S]*?${escapeRegex(HOOK_MARKERS.end)}\\n?`, 'g');
|
|
203
218
|
return text.replace(re, '\n').replace(/\n{3,}/g, '\n\n').trim();
|
|
204
219
|
}
|
|
205
|
-
function escapeRegex(s) {
|
|
206
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
207
|
-
}
|
|
208
220
|
// `hippo setup` -- one-shot configuration for every AI coding tool on the box.
|
|
209
221
|
// Detection and install logic live in ./hooks.ts.
|
|
210
222
|
export function cmdSetup(flags) {
|
|
@@ -223,97 +235,18 @@ export function cmdSetup(flags) {
|
|
|
223
235
|
console.log('No JSON-hook-capable tools detected (checked: claude-code).');
|
|
224
236
|
console.log('Run with --all to install hooks anyway.');
|
|
225
237
|
}
|
|
226
|
-
for (const tool of jsonTools)
|
|
227
|
-
|
|
228
|
-
// Resolve the real settings path so the filename is right for each tool
|
|
229
|
-
// (claude-code -> settings.json, opencode -> opencode.json).
|
|
230
|
-
const { settings } = resolveJsonHookPaths(tool.name);
|
|
231
|
-
console.log(`[dry-run] would install hooks in ${settings}`);
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
const result = installJsonHooks(tool.name);
|
|
235
|
-
const bits = [];
|
|
236
|
-
if (result.installedSessionEnd)
|
|
237
|
-
bits.push('SessionEnd (session-end)');
|
|
238
|
-
if (result.installedSessionStart)
|
|
239
|
-
bits.push('SessionStart');
|
|
240
|
-
if (result.installedUserPromptSubmit)
|
|
241
|
-
bits.push('UserPromptSubmit (pinned-inject)');
|
|
242
|
-
if (result.installedPreCompact)
|
|
243
|
-
bits.push('PreCompact (pre-compact)');
|
|
244
|
-
if (result.installedCompactResume)
|
|
245
|
-
bits.push('SessionStart(compact) (compact-resume)');
|
|
246
|
-
if (result.installedPostCompact)
|
|
247
|
-
bits.push('PostCompact (post-compact)');
|
|
248
|
-
if (result.installedCaptureError)
|
|
249
|
-
bits.push('PostToolUseFailure (capture-error)');
|
|
250
|
-
if (result.migratedFromStop)
|
|
251
|
-
bits.push('migrated legacy Stop');
|
|
252
|
-
if (result.migratedSplitSessionEnd)
|
|
253
|
-
bits.push('migrated split SessionEnd → session-end');
|
|
254
|
-
else if (result.migratedLegacySessionEnd)
|
|
255
|
-
bits.push('migrated legacy SessionEnd');
|
|
256
|
-
if (bits.length === 0) {
|
|
257
|
-
console.log(` ${tool.name.padEnd(14)} already configured (${result.settingsPath})`);
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
console.log(` ${tool.name.padEnd(14)} ${bits.join(', ')} -> ${result.settingsPath}`);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
238
|
+
for (const tool of jsonTools)
|
|
239
|
+
setupJsonTool(tool, dryRun);
|
|
263
240
|
for (const tool of skipped) {
|
|
264
241
|
console.log(` ${tool.name.padEnd(14)} not detected at ${tool.configDir} -- skipping`);
|
|
265
242
|
}
|
|
266
|
-
for (const tool of wrapperTools)
|
|
267
|
-
|
|
268
|
-
if (tool.name === 'codex')
|
|
269
|
-
console.log(`[dry-run] would install Codex memory hooks in ${resolveJsonHookPaths('codex').settings}`);
|
|
270
|
-
console.log(`[dry-run] would wrap the detected ${tool.name} launcher in place`);
|
|
271
|
-
continue;
|
|
272
|
-
}
|
|
273
|
-
if (tool.name === 'codex') {
|
|
274
|
-
installCodexMemoryHooks(` ${tool.name.padEnd(14)} `);
|
|
275
|
-
const result = ensureCodexWrapperInstalled();
|
|
276
|
-
if (result.status === 'installed') {
|
|
277
|
-
console.log(` ${tool.name.padEnd(14)} wrapped launcher -> ${result.commandPath}`);
|
|
278
|
-
}
|
|
279
|
-
else if (result.status === 'already-installed') {
|
|
280
|
-
console.log(` ${tool.name.padEnd(14)} already wrapped -> ${result.commandPath}`);
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
283
|
-
console.log(` ${tool.name.padEnd(14)} not found on PATH -- skipping`);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
243
|
+
for (const tool of wrapperTools)
|
|
244
|
+
setupWrapperTool(tool, dryRun);
|
|
287
245
|
if (pluginTools.length > 0) {
|
|
288
246
|
console.log('');
|
|
289
247
|
console.log('Plugin-based tools (hook API via plugin, not JSON):');
|
|
290
|
-
for (const tool of pluginTools)
|
|
291
|
-
|
|
292
|
-
if (dryRun) {
|
|
293
|
-
console.log(` ${tool.name.padEnd(14)} [dry-run] would install hippo plugin at ${resolveOpencodePluginPath()}`);
|
|
294
|
-
continue;
|
|
295
|
-
}
|
|
296
|
-
const result = installOpencodePlugin();
|
|
297
|
-
const bits = [];
|
|
298
|
-
if (result.installed)
|
|
299
|
-
bits.push('installed plugin');
|
|
300
|
-
if (result.migratedLegacyHooks)
|
|
301
|
-
bits.push('migrated legacy hooks block');
|
|
302
|
-
if (result.jsonRepairFailed)
|
|
303
|
-
bits.push('WARNING: opencode.json unparseable — manual fix needed');
|
|
304
|
-
if (bits.length === 0) {
|
|
305
|
-
console.log(` ${tool.name.padEnd(14)} already configured (${result.pluginPath})`);
|
|
306
|
-
}
|
|
307
|
-
else {
|
|
308
|
-
console.log(` ${tool.name.padEnd(14)} ${bits.join(', ')} -> ${result.pluginPath}`);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
// Other plugin tools (openclaw) have their own installer; the notes
|
|
313
|
-
// line points the user at it.
|
|
314
|
-
console.log(` ${tool.name.padEnd(14)} ${tool.notes}`);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
248
|
+
for (const tool of pluginTools)
|
|
249
|
+
setupPluginTool(tool, dryRun);
|
|
317
250
|
}
|
|
318
251
|
if (markdownTools.length > 0) {
|
|
319
252
|
console.log('');
|
|
@@ -338,6 +271,90 @@ export function cmdSetup(flags) {
|
|
|
338
271
|
console.log('');
|
|
339
272
|
console.log('Done. Restart your AI tool to activate the hooks.');
|
|
340
273
|
}
|
|
274
|
+
function setupJsonTool(tool, dryRun) {
|
|
275
|
+
if (dryRun) {
|
|
276
|
+
// Resolve the real settings path so the filename is right for each tool
|
|
277
|
+
// (claude-code -> settings.json, opencode -> opencode.json).
|
|
278
|
+
const { settings } = resolveJsonHookPaths(tool.name);
|
|
279
|
+
console.log(`[dry-run] would install hooks in ${settings}`);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const result = installJsonHooks(tool.name);
|
|
283
|
+
const bits = [];
|
|
284
|
+
if (result.installedSessionEnd)
|
|
285
|
+
bits.push('SessionEnd (session-end)');
|
|
286
|
+
if (result.installedSessionStart)
|
|
287
|
+
bits.push('SessionStart');
|
|
288
|
+
if (result.installedUserPromptSubmit)
|
|
289
|
+
bits.push('UserPromptSubmit (pinned-inject)');
|
|
290
|
+
if (result.installedPreCompact)
|
|
291
|
+
bits.push('PreCompact (pre-compact)');
|
|
292
|
+
if (result.installedCompactResume)
|
|
293
|
+
bits.push('SessionStart(compact) (compact-resume)');
|
|
294
|
+
if (result.installedPostCompact)
|
|
295
|
+
bits.push('PostCompact (post-compact)');
|
|
296
|
+
if (result.installedCaptureError)
|
|
297
|
+
bits.push('PostToolUseFailure (capture-error)');
|
|
298
|
+
if (result.migratedFromStop)
|
|
299
|
+
bits.push('migrated legacy Stop');
|
|
300
|
+
if (result.migratedSplitSessionEnd)
|
|
301
|
+
bits.push('migrated split SessionEnd → session-end');
|
|
302
|
+
else if (result.migratedLegacySessionEnd)
|
|
303
|
+
bits.push('migrated legacy SessionEnd');
|
|
304
|
+
if (bits.length === 0) {
|
|
305
|
+
console.log(` ${tool.name.padEnd(14)} already configured (${result.settingsPath})`);
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
console.log(` ${tool.name.padEnd(14)} ${bits.join(', ')} -> ${result.settingsPath}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function setupWrapperTool(tool, dryRun) {
|
|
312
|
+
if (dryRun) {
|
|
313
|
+
if (tool.name === 'codex')
|
|
314
|
+
console.log(`[dry-run] would install Codex memory hooks in ${resolveJsonHookPaths('codex').settings}`);
|
|
315
|
+
console.log(`[dry-run] would wrap the detected ${tool.name} launcher in place`);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (tool.name !== 'codex')
|
|
319
|
+
return;
|
|
320
|
+
installCodexMemoryHooks(` ${tool.name.padEnd(14)} `);
|
|
321
|
+
const result = ensureCodexWrapperInstalled();
|
|
322
|
+
if (result.status === 'installed') {
|
|
323
|
+
console.log(` ${tool.name.padEnd(14)} wrapped launcher -> ${result.commandPath}`);
|
|
324
|
+
}
|
|
325
|
+
else if (result.status === 'already-installed') {
|
|
326
|
+
console.log(` ${tool.name.padEnd(14)} already wrapped -> ${result.commandPath}`);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
console.log(` ${tool.name.padEnd(14)} not found on PATH -- skipping`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function setupPluginTool(tool, dryRun) {
|
|
333
|
+
if (tool.name !== 'opencode') {
|
|
334
|
+
// Other plugin tools (openclaw) have their own installer; the notes
|
|
335
|
+
// line points the user at it.
|
|
336
|
+
console.log(` ${tool.name.padEnd(14)} ${tool.notes}`);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (dryRun) {
|
|
340
|
+
console.log(` ${tool.name.padEnd(14)} [dry-run] would install hippo plugin at ${resolveOpencodePluginPath()}`);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const result = installOpencodePlugin();
|
|
344
|
+
const bits = [];
|
|
345
|
+
if (result.installed)
|
|
346
|
+
bits.push('installed plugin');
|
|
347
|
+
if (result.migratedLegacyHooks)
|
|
348
|
+
bits.push('migrated legacy hooks block');
|
|
349
|
+
if (result.jsonRepairFailed)
|
|
350
|
+
bits.push('WARNING: opencode.json unparseable — manual fix needed');
|
|
351
|
+
if (bits.length === 0) {
|
|
352
|
+
console.log(` ${tool.name.padEnd(14)} already configured (${result.pluginPath})`);
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
console.log(` ${tool.name.padEnd(14)} ${bits.join(', ')} -> ${result.pluginPath}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
341
358
|
export function cmdDailyRunner() {
|
|
342
359
|
const globalRoot = getGlobalRoot();
|
|
343
360
|
// No workspace sleep ever opens the global store, yet hooks in folders without a store compact into it.
|
package/dist/cli/shared.js
CHANGED
|
@@ -91,14 +91,14 @@ export function requireInit(hippoRoot) {
|
|
|
91
91
|
/** Runs detectChurnStale against every store this repo's memories can live in. */
|
|
92
92
|
export function runChurnStaleForRepo(hippoRoot, dryRun) {
|
|
93
93
|
const repoRoot = execFileSync('git', ['rev-parse', '--show-toplevel'], { cwd: process.cwd(), encoding: 'utf8', timeout: 10_000, windowsHide: true }).trim();
|
|
94
|
-
const projectName = resolveProjectIdentity(process.cwd())
|
|
94
|
+
const { name: projectName, legacyName } = resolveProjectIdentity(process.cwd());
|
|
95
95
|
const globalRoot = getGlobalRoot();
|
|
96
96
|
const roots = globalRoot !== hippoRoot && isInitialized(globalRoot) ? [hippoRoot, globalRoot] : [hippoRoot];
|
|
97
97
|
const tenantId = resolveTenantId({});
|
|
98
98
|
return roots.map((root) => {
|
|
99
99
|
// One store failing must not abort sleep's later phases or skip the other store.
|
|
100
100
|
try {
|
|
101
|
-
return { root, result: detectChurnStale(root, repoRoot, { tenantId, projectName, dryRun }) };
|
|
101
|
+
return { root, result: detectChurnStale(root, repoRoot, { tenantId, projectName, legacyName, dryRun }) };
|
|
102
102
|
}
|
|
103
103
|
catch (err) {
|
|
104
104
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -405,7 +405,7 @@ export function cardStringFlag(flags, key) {
|
|
|
405
405
|
}
|
|
406
406
|
// Claude Code exports its own session var, not ours; without the fallback agent-run recalls trace with no session.
|
|
407
407
|
export function hostSessionId() {
|
|
408
|
-
return envHippoSessionId()
|
|
408
|
+
return envHippoSessionId() ?? envClaudeCodeSessionId();
|
|
409
409
|
}
|
|
410
410
|
/**
|
|
411
411
|
* Compaction drops the pinned blocks the per-prompt hook injected
|
package/dist/cli/slack.js
CHANGED
|
@@ -10,7 +10,7 @@ import { addWorkspace as addSlackWorkspace, listWorkspaces as listSlackWorkspace
|
|
|
10
10
|
import { printError } from './output.js';
|
|
11
11
|
import { printSlackBackfillUsage, printSlackWorkspacesUsage } from './usage.js';
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
|
-
// Slack subcommands (
|
|
13
|
+
// Slack subcommands (`hippo slack backfill` / `hippo slack dlq list`)
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
15
|
function cmdSlackBackfill(hippoRoot, flags) {
|
|
16
16
|
const channel = typeof flags['channel'] === 'string' ? flags['channel'] : undefined;
|
package/dist/cli/sleep.js
CHANGED
|
@@ -8,7 +8,9 @@ import { replayCompactionsAt } from '../compaction-record.js';
|
|
|
8
8
|
import * as api from '../api.js';
|
|
9
9
|
import { resolveTenantId } from '../tenant.js';
|
|
10
10
|
import { renderAmbientSummary } from '../ambient.js';
|
|
11
|
-
import { log } from '../log.js';
|
|
11
|
+
import { errorMessage, log } from '../log.js';
|
|
12
|
+
import { closeHippoDb, openHippoDb } from '../db.js';
|
|
13
|
+
import { repairOnceOnSleep } from '../project-merge.js';
|
|
12
14
|
import { requireInit, learnFromRepo, runChurnStaleForRepo, printAgentImport } from './shared.js';
|
|
13
15
|
import { printError } from './output.js';
|
|
14
16
|
/** Runs `hippo sleep`; with `--log-file` it also tees its output to that file. */
|
|
@@ -127,6 +129,29 @@ export function renderSleepResult(result) {
|
|
|
127
129
|
console.log(`\nGraph: rebuilt ${tenants} tenant${tenants === 1 ? '' : 's'} (${entities} entities, ${relations} relations).`);
|
|
128
130
|
}
|
|
129
131
|
}
|
|
132
|
+
/** Fault-isolated: a failed repair warns and runs again next sleep, and never stops the sleep. */
|
|
133
|
+
function repairProjectTagsOnce(hippoRoot) {
|
|
134
|
+
let db;
|
|
135
|
+
try {
|
|
136
|
+
db = openHippoDb(hippoRoot);
|
|
137
|
+
const r = repairOnceOnSleep(db, hippoRoot, resolveTenantId({}));
|
|
138
|
+
if (r === null)
|
|
139
|
+
return;
|
|
140
|
+
const parts = [
|
|
141
|
+
r.copies.length > 0 ? `set aside ${r.copies.length} misfiled note imports` : '',
|
|
142
|
+
r.folds.length > 0 ? `folded ${r.folds.map((f) => `${f.from} into ${f.into}`).join(', ')}` : '',
|
|
143
|
+
r.toProject.length + r.setAside.length > 0 ? `re-tagged ${r.toProject.length + r.setAside.length} merged memories` : '',
|
|
144
|
+
].filter((p) => p !== '');
|
|
145
|
+
console.log(`Repaired project tags once after the upgrade: ${parts.join('; ')} (backup: ${r.backup}).`);
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
log.warn(`project tag repair skipped, retried next sleep: ${errorMessage(err)}`);
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
if (db)
|
|
152
|
+
closeHippoDb(db);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
130
155
|
async function cmdSleepCore(hippoRoot, flags) {
|
|
131
156
|
requireInit(hippoRoot);
|
|
132
157
|
// Phase 1: Auto-learn from git and every coding agent's own memories (CLI-only, uses process.cwd() / os.homedir()).
|
|
@@ -157,6 +182,7 @@ async function cmdSleepCore(hippoRoot, flags) {
|
|
|
157
182
|
const finished = replayCompactionsAt(hippoRoot, (message) => log.warn(`compaction replay: ${message}`));
|
|
158
183
|
if (finished > 0)
|
|
159
184
|
console.log(`Finished saving ${finished} compaction${finished === 1 ? '' : 's'} left over from earlier sessions.`);
|
|
185
|
+
repairProjectTagsOnce(hippoRoot);
|
|
160
186
|
}
|
|
161
187
|
// Phase 2-6: Pure-storage pipeline (consolidate + dedup + audit + share + ambient).
|
|
162
188
|
const ctx = {
|