blun-king-cli 9.1.563 → 9.1.565

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.
Files changed (85) hide show
  1. package/agent-spine-plugin/.claude-plugin/marketplace.json +20 -20
  2. package/agent-spine-plugin/.claude-plugin/plugin.json +14 -14
  3. package/agent-spine-plugin/.codex-plugin/plugin.json +34 -34
  4. package/agent-spine-plugin/.mcp.json +8 -8
  5. package/agent-spine-plugin/CONTRIBUTING.md +52 -0
  6. package/agent-spine-plugin/LICENSE +186 -186
  7. package/agent-spine-plugin/README.md +394 -0
  8. package/agent-spine-plugin/SECURITY.md +47 -0
  9. package/agent-spine-plugin/assets/agentspine-banner.svg +32 -32
  10. package/agent-spine-plugin/bin/agentspine-mcp.js +4 -4
  11. package/agent-spine-plugin/bin/agentspine.js +7 -7
  12. package/agent-spine-plugin/blun.plugin.json +33 -33
  13. package/agent-spine-plugin/hooks/codex.json +47 -47
  14. package/agent-spine-plugin/hooks/hooks.json +106 -106
  15. package/agent-spine-plugin/hooks/version.json +5 -5
  16. package/agent-spine-plugin/package.json +69 -69
  17. package/agent-spine-plugin/scripts/check-hosts.js +199 -199
  18. package/agent-spine-plugin/skill/SKILL.md +76 -76
  19. package/agent-spine-plugin/skills/agent-spine/SKILL.md +85 -85
  20. package/agent-spine-plugin/src/cli.js +1488 -1442
  21. package/agent-spine-plugin/src/hook.js +886 -812
  22. package/agent-spine-plugin/src/index.js +93 -93
  23. package/agent-spine-plugin/src/lib/acceptance.js +333 -333
  24. package/agent-spine-plugin/src/lib/attention.js +755 -755
  25. package/agent-spine-plugin/src/lib/audit.js +351 -342
  26. package/agent-spine-plugin/src/lib/authentication.js +515 -515
  27. package/agent-spine-plugin/src/lib/briefing.js +317 -317
  28. package/agent-spine-plugin/src/lib/catalog.js +167 -167
  29. package/agent-spine-plugin/src/lib/channel-runtime.js +665 -665
  30. package/agent-spine-plugin/src/lib/context.js +154 -154
  31. package/agent-spine-plugin/src/lib/continuity.js +338 -338
  32. package/agent-spine-plugin/src/lib/coordination.js +577 -577
  33. package/agent-spine-plugin/src/lib/documents.js +217 -217
  34. package/agent-spine-plugin/src/lib/feed-transport.js +499 -499
  35. package/agent-spine-plugin/src/lib/filesystem-retry.js +32 -32
  36. package/agent-spine-plugin/src/lib/gateway-runtime.js +1119 -760
  37. package/agent-spine-plugin/src/lib/graph.js +337 -337
  38. package/agent-spine-plugin/src/lib/hook-audit.js +29 -0
  39. package/agent-spine-plugin/src/lib/https-transport.js +392 -392
  40. package/agent-spine-plugin/src/lib/indexed-memory-offline.js +40 -40
  41. package/agent-spine-plugin/src/lib/indexed-memory.js +281 -281
  42. package/agent-spine-plugin/src/lib/learning.js +6923 -6466
  43. package/agent-spine-plugin/src/lib/object-transport.js +206 -206
  44. package/agent-spine-plugin/src/lib/owned-file-lock.js +143 -143
  45. package/agent-spine-plugin/src/lib/paths.js +109 -109
  46. package/agent-spine-plugin/src/lib/peer-transport.js +283 -283
  47. package/agent-spine-plugin/src/lib/persona-runtime.js +581 -581
  48. package/agent-spine-plugin/src/lib/preflight.js +702 -702
  49. package/agent-spine-plugin/src/lib/runtime.js +13 -13
  50. package/agent-spine-plugin/src/lib/selfstarter.js +892 -819
  51. package/agent-spine-plugin/src/lib/sharing.js +969 -969
  52. package/agent-spine-plugin/src/lib/source-roots.js +529 -482
  53. package/agent-spine-plugin/src/lib/sqlite-transport.js +501 -501
  54. package/agent-spine-plugin/src/lib/telegram-adapter.js +119 -119
  55. package/agent-spine-plugin/src/lib/voice-runtime.js +39 -39
  56. package/agent-spine-plugin/src/mcp.js +597 -572
  57. package/agent-spine-plugin/src/version.js +1 -1
  58. package/agent-spine-plugin/src/worker.js +202 -195
  59. package/bin/active-steer-priority-policy.cjs +24 -0
  60. package/bin/launcher-runtime.js +8 -1
  61. package/bin/mnemo-tool-agent-policy.cjs +22 -0
  62. package/bin/thinking-activity-status-policy.cjs +132 -0
  63. package/bin/thinking-only-guard.cjs +75 -0
  64. package/bin/tool-call-loop-policy.cjs +53 -0
  65. package/bin/turn-thinking-policy.cjs +25 -1
  66. package/blun.mjs +554 -64
  67. package/package.json +4 -1
  68. package/standard-skills/translate-native/LICENSE +21 -21
  69. package/standard-skills/translate-native/references/evaluation-protocol.md +95 -95
  70. package/standard-skills/translate-native/references/native-orthography.md +79 -79
  71. package/standard-skills/translate-native/references/native-translation-standard.md +94 -94
  72. package/standard-skills/translate-native/references/structured-content.md +72 -72
  73. package/standard-skills/translate-native/references/translationese-review.md +77 -77
  74. package/standard-skills/translate-native/scripts/blun_language_guard.py +697 -697
  75. package/standard-skills/translate-native/scripts/check_diacritics.py +353 -353
  76. package/standard-skills/translate-native/scripts/guard_service_client.py +264 -264
  77. package/standard-skills/translate-native/scripts/language_gateway.py +62 -62
  78. package/standard-skills/translate-native/scripts/language_quality.py +377 -377
  79. package/standard-skills/translate-native/scripts/pre_output_guard.py +64 -64
  80. package/standard-skills/translate-native/scripts/translation_guard.py +916 -916
  81. package/standard-tools/language-guard/blun_language_guard.py +697 -697
  82. package/standard-tools/language-guard/check_diacritics.py +353 -353
  83. package/standard-tools/language-guard/guard_service_client.py +264 -264
  84. package/standard-tools/language-guard/language_quality.py +377 -377
  85. package/standard-tools/language-guard/translation_guard.py +916 -916
@@ -1,1442 +1,1488 @@
1
- import { fileURLToPath } from "node:url";
2
- import { scanAndSave, verifyCatalog } from "./lib/catalog.js";
3
- import { readDocument, resolveContext } from "./lib/context.js";
4
- import { sessionBriefing } from "./lib/briefing.js";
5
- import { runAudit } from "./lib/audit.js";
6
- import {
7
- attentionContext, configureAttention, deleteAttention, loadAttention,
8
- recordActivity, resolveAttention, upsertAttention
9
- } from "./lib/attention.js";
10
- import {
11
- addLearningEvidence, beginLearningRevalidation, configureLearning, deleteLearning, evaluateLearning,
12
- learningContext, learningOutcomeStatus, loadLearning, proposeLearning,
13
- purgeStaleLearningApplications, purgeStaleLearningMeasurements, recordLearningMeasurement,
14
- recordLearningOutcome, registerLearningEvaluation, registerLearningEvaluator, renewLearningValidation, revokeLearningEvaluator,
15
- reviewLearning, revokeLearningApplication, revokeLearningDelivery, revokeLearningEvaluation, revokeLearningEvidence, revokeLearningMeasurement, revokeLearningOutcome, revokeLearningTrialFailure, revokeLearningValidation,
16
- rollbackLearning
17
- } from "./lib/learning.js";
18
- import { configureContinuity, loadContinuity, purgeContinuity } from "./lib/continuity.js";
19
- import {
20
- checkDelegation, createTask, deleteTask, grantDelegation, loadDelegationPolicy,
21
- revokeDelegation, taskContext, updateTask
22
- } from "./lib/coordination.js";
23
- import {
24
- cancelJob, deleteJob, grantExecution, loadExecutionPolicy, registerJob,
25
- revokeExecution, selfstarterContext
26
- } from "./lib/selfstarter.js";
27
- import {
28
- channelRuntimeContext, grantChannelBinding, loadChannelPolicy, revokeChannelBinding
29
- } from "./lib/channel-runtime.js";
30
- import { personaContext, syncPersonaRosterFromEnvironment } from "./lib/persona-runtime.js";
31
- import { assignGoal, gatewayContext, setGatewayControl } from "./lib/gateway-runtime.js";
32
- import {
33
- configureSharing, deleteShared, initDirectoryAdapter, publishLearning, pullShared,
34
- reviewShared, rollbackShared, sharedContext, sharedInbox
35
- } from "./lib/sharing.js";
36
- import {
37
- generateSigningIdentity, listSigningIdentities, revokeTrustedSigner,
38
- trustedSignerContext, trustSigner
39
- } from "./lib/authentication.js";
40
- import { exportHttpsSnapshot, pullHttpsSnapshot } from "./lib/https-transport.js";
41
- import { publishHttpsSnapshot } from "./lib/object-transport.js";
42
- import { loadHttpsFeedState, publishHttpsFeed, pullHttpsFeed } from "./lib/feed-transport.js";
43
- import { pullPeerCommand, servePeerOnce } from "./lib/peer-transport.js";
44
- import {
45
- initSqliteAdapter, inspectSqliteAdapter, publishSqliteSnapshot, pullSqliteSnapshot
46
- } from "./lib/sqlite-transport.js";
47
- import {
48
- annotateDocument, linkDocuments, linkEntities,
49
- relationshipContext, upsertEntity
50
- } from "./lib/graph.js";
51
- import { checkHosts } from "../scripts/check-hosts.js";
52
- import { renderAcceptanceReport, runVisibleAcceptance } from "./lib/acceptance.js";
53
- import {
54
- bindSourceRoot, inspectSourceRegistry, purgeSourceBinding, resolveHostSourceCatalog,
55
- rollbackSourceBinding
56
- } from "./lib/source-roots.js";
57
- import { VERSION } from "./version.js";
58
- import { isMainModule } from "./lib/runtime.js";
59
- import { scanIndexedMemoryOrphans } from "./lib/indexed-memory-offline.js";
60
- import {
61
- configurePreflightPolicy, confirmMustRemember, preflightStatus, proposeMustRemember,
62
- purgeMustRemember, rollbackMustRemember
63
- } from "./lib/preflight.js";
64
- import { readFile } from "node:fs/promises";
65
-
66
- function parse(argv) {
67
- const [command = "help", ...rest] = argv;
68
- const flags = {};
69
- const positional = [];
70
- for (let index = 0; index < rest.length; index += 1) {
71
- const token = rest[index];
72
- if (!token.startsWith("--")) {
73
- positional.push(token);
74
- continue;
75
- }
76
- const [name, inline] = token.slice(2).split("=", 2);
77
- if (inline !== undefined) flags[name] = inline;
78
- else if (rest[index + 1] && !rest[index + 1].startsWith("--")) flags[name] = rest[++index];
79
- else flags[name] = true;
80
- }
81
- return { command, flags, positional };
82
- }
83
-
84
- function output(value, json = false) {
85
- if (json) {
86
- process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
87
- return;
88
- }
89
- if (typeof value === "string") process.stdout.write(`${value}\n`);
90
- else process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
91
- }
92
-
93
- function booleanFlag(value, fallback = false) {
94
- if (value === undefined) return fallback;
95
- if (value === true || value === "true" || value === "1") return true;
96
- if (value === false || value === "false" || value === "0") return false;
97
- throw new Error(`expected boolean flag, received: ${value}`);
98
- }
99
-
100
- function learningScope(flags) {
101
- return {
102
- personaId: flags.persona || null,
103
- userId: flags.user || null,
104
- tenantId: flags.tenant || null,
105
- projectId: flags.project || null,
106
- groupId: flags.group || null,
107
- taskId: flags.task || null
108
- };
109
- }
110
-
111
- function hasLearningScope(flags) {
112
- return ["persona", "user", "tenant", "project", "group", "task"].some((name) => flags[name] !== undefined);
113
- }
114
-
115
- function evaluatorRootsFlag(value) {
116
- return String(value || "").split(",").filter(Boolean).map((entry) => {
117
- const separator = entry.lastIndexOf("=");
118
- if (separator <= 0) throw new Error("evaluator roots must use evaluator-id=sha256 entries");
119
- return { evaluatorId: entry.slice(0, separator), principalDigest: entry.slice(separator + 1) };
120
- });
121
- }
122
-
123
- function help() {
124
- return `AgentSpine ${VERSION}
125
-
126
- Usage:
127
- agentspine scan [root] [--json]
128
- agentspine context [root] [--cwd path] [--host codex|claude|generic] [--max-bytes n] [--json]
129
- agentspine briefing [root] [--host codex|claude|generic] [--entity id] [--group id] [--project id] [--current-task id] [--include-private] [--max-bytes n] [--allow-attention] [--no-source-content]
130
- agentspine read <relative-path> [--root path] [--offset n] [--length n] [--json]
131
- agentspine verify [root] [--json]
132
- agentspine link <from.md> <to.md> --relation related [--reason text] [--confidence 0.8]
133
- agentspine annotate <path.md> --layer soul [--reason text] [--confidence 0.8]
134
- agentspine entity <id> --kind person [--name text] [--privacy private]
135
- agentspine relate <from> <to> --relation works-with [--privacy private]
136
- agentspine relationships <entity-id> [--group id] [--include-private] [--json]
137
- agentspine attention [root] [--group id] [--include-private] [--focus-active] [--mark-presented]
138
- agentspine attention-events [root] [--include-history]
139
- agentspine attention-add [id] --kind promise --summary text [--entity id] [--group id] [--due date]
140
- agentspine attention-resolve <id> [--status completed|dismissed|open]
141
- agentspine attention-touch <entity-id> [--kind interaction] [--at date]
142
- agentspine attention-delete <signal-id>
143
- agentspine attention-event-delete <event-id>
144
- agentspine attention-purge <entity-id>
145
- agentspine attention-config [root] [--enabled true|false] [--quiet-start 22 --quiet-end 7 --utc-offset 120]
146
- agentspine learn-propose [id] --kind preference|behavior --claim text --evidence text [--persona id --user id --tenant id --project id --group id --task id]
147
- agentspine learn-evidence <id> --summary text [--type interaction] [--source path.md]
148
- agentspine learn-evidence-revoke <learning-id> --evidence-id id --reason-code retracted|source-invalid|measurement-invalid|duplicate|other --reason text --confirm-local-evidence
149
- agentspine learn-review <id> --decision accept|reject --reason text [--confirmed-by-user]
150
- agentspine learn-context [root] [--group id] [--include-private] [--kind preference,goal]
151
- agentspine learn-evaluate [root]
152
- agentspine learn-evaluator-register <id> --principal-digest sha256 --confirm-local-evaluator
153
- agentspine learn-evaluator-revoke <id> --reason text --confirm-local-evaluator
154
- agentspine learn-evaluation <id> --learning id --metric name --direction higher|lower --task-digest sha256 --dataset-digest sha256 --protocol-digest sha256 --min-cases n --evaluators id,id --evaluator-roots id=sha256,id=sha256 [--expires-at date] [--retry-trial-failure id --confirm-local-trial-retry] --confirm-local-evaluation
155
- agentspine learn-evaluation-revoke <evaluation-id> --reason-code benchmark-invalid|protocol-invalid|scope-invalid|threshold-invalid|duplicate|other --reason text --confirm-local-evaluation-revocation
156
- agentspine learn-validation-revoke <validation-lease-id> --reason-code decision-invalid|cohort-invalid|binding-invalid|scope-invalid|duplicate|other --reason text --confirm-local-validation-revocation
157
- agentspine learn-trial-failure-revoke <trial-failure-id> --reason-code clock-invalid|host-invalid|receipt-invalid|scope-invalid|duplicate|other --reason text --confirm-local-trial-failure-revocation
158
- agentspine learn-revalidation-start <learning-id> --confirm-local-validation
159
- agentspine learn-revalidate <learning-id> --measurements id,id --applications id,id --deliveries id,id --confirm-local-validation
160
- agentspine learn-measurement <id> --learning id --evaluation id --phase before|after --metric name --direction higher|lower --value 0..1 --measurement objective|user-feedback|model-suggestion --evaluator id --run id --source-digest sha256 --dataset-digest sha256 --case-count n --confirm-local-measurement
161
- agentspine learn-measurement-revoke <measurement-id> --reason-code source-invalid|evaluator-invalid|protocol-invalid|duplicate|other --reason text --confirm-local-measurement-revocation
162
- agentspine learn-application-revoke <application-id> --reason-code preflight-invalid|scope-invalid|projection-invalid|duplicate|other --reason text --confirm-local-application-revocation
163
- agentspine learn-delivery-revoke <delivery-id> --reason-code host-invalid|session-invalid|hook-invalid|duplicate|other --reason text --confirm-local-delivery-revocation
164
- agentspine learn-outcome-revoke <outcome-id> --reason-code binding-invalid|phase-invalid|scope-invalid|duplicate|other --reason text --confirm-local-outcome-revocation
165
- agentspine learn-outcome <learning-id> --id id --evaluation id --measurement-receipt id [--application id --delivery id]
166
- agentspine learn-status [root] [--persona id --user id --tenant id --project id --group id --task id]
167
- agentspine learn-delivery-purge [root] --confirm-local-purge
168
- agentspine learn-measurement-purge [root] --confirm-local-purge
169
- agentspine learn-rollback <id> --reason text
170
- agentspine learn-delete <id>
171
- agentspine learn-config [root] [--auto-promote true|false] [--min-confidence 0.85] [--min-outcomes 2 --min-improvement 0.05 --canary-receipts 2 --canary-ttl-days 14]
172
- agentspine continuity-config [root] [--enabled true|false] [--entity id] [--project id] [--confirm-local-opt-in]
173
- agentspine continuity-status [root]
174
- agentspine continuity-purge <entity-id> [--root path] --confirm-local-purge
175
- agentspine preflight-policy <policy.json> --confirm-local-policy
176
- agentspine preflight-status
177
- agentspine remember-propose --claim text --user id --tenant id [--project id] [--group id] [--task id]
178
- agentspine remember-confirm <candidate-id> [--supersedes id] --confirm-local-user
179
- agentspine remember-rollback <id> --confirm-local-user
180
- agentspine remember-purge <id> --confirm-local-purge
181
- agentspine source-status --host claude|codex [--cwd path]
182
- agentspine doctor --host claude [--cwd path] [--offline-memory-orphans]
183
- agentspine source-bind <state-root> --host all|claude|codex --scope state-user --project path --host-home path --confirm-local-binding
184
- agentspine source-rollback <binding-id> --confirm-local-binding
185
- agentspine source-purge <binding-id> --confirm-local-binding
186
- agentspine delegation-grant <actor-id> --actions assign,manage --targets agent:id --reason text [--confirm-local-policy]
187
- agentspine delegation-revoke <grant-id> --reason text [--confirm-local-policy]
188
- agentspine delegation-check <actor-id> --action assign [--target agent:id]
189
- agentspine delegation-policy [root]
190
- agentspine task-create [id] --actor id --title text [--assignee id] [--kind task|open-thread|handoff] [--privacy private|shared|group]
191
- agentspine task-update <id> --actor id [--status in-progress] [--assignee id|--unassign]
192
- agentspine tasks [root] [--assignee id] [--project id] [--include-private] [--group id]
193
- agentspine task-delete <id> [--confirm-local-policy]
194
- agentspine execution-grant <job-id> --actor id --task task:id --target id --project project:id --host claude|codex --capabilities tool:Write --reason text [--expires date] --confirm-local-execution
195
- agentspine execution-revoke <grant-id> --reason text --confirm-local-execution
196
- agentspine execution-policy [root]
197
- agentspine job-register <job-id> --grant grant:id [--max-retries 3] [--lease-seconds 120] --confirm-local-execution
198
- agentspine jobs [root] [--actor id] [--project id] [--task id] [--include-terminal]
199
- agentspine job-cancel <job-id> --reason text --confirm-local-execution
200
- agentspine job-delete <job-id> --confirm-local-execution
201
- agentspine channel-bind <binding-id> --provider telegram --tenant id --account id --chat id --senders id,id --agent agent:id --project project:id --session key --secret-env VARIABLE [--outbound-secret-env VARIABLE] --confirm-local-channel
202
- agentspine channel-revoke <binding-id> --reason text --confirm-local-channel
203
- agentspine channel-policy [root]
204
- agentspine channel-events [root] [--agent id] [--project id] [--group id] [--provider telegram] [--include-terminal]
205
- agentspine persona-sync [root] --roster absolute-path --confirm-local-persona
206
- agentspine personas [root] [--persona id] [--group id] [--include-inactive]
207
- agentspine goal-assign <goal-id> --agent id --owner id --project id --success text --next-step text [--group id] [--deadline date] --confirm-local-goal
208
- agentspine gateway-control [root] [--enabled true|false] [--kill-switch true|false] --confirm-local-gateway
209
- agentspine gateway-status [root] [--agent id]
210
- agentspine share-keygen <signer-id> [--public-out signer.json] [--rotate] [--confirm-local-share]
211
- agentspine share-signers [root]
212
- agentspine share-trust <signer.json> [--root path] [--confirm-local-share]
213
- agentspine share-trust-revoke <key-id> --reason text [--root path] [--confirm-local-share]
214
- agentspine share-trust-list [root] [--include-revoked]
215
- agentspine share-init <directory> --scope team:id [--adapter adapter:id] [--signer signer:id] [--confirm-local-share]
216
- agentspine share-publish <directory> --learning id [--id shared:id] [--signer signer:id] [--supersedes shared:id] [--confirm-local-share]
217
- agentspine share-pull <directory> [--root path] [--require-authenticated]
218
- agentspine share-snapshot-export <directory> --out snapshot.json [--id snapshot:id] [--confirm-local-share]
219
- agentspine share-https-publish <directory> --base https://store.example/spine [--id snapshot:id] [--token-env VARIABLE] [--timeout-ms 10000] [--allow-private-network] --confirm-local-share
220
- agentspine share-https-pull <https-url> [--token-env VARIABLE] [--timeout-ms 10000] [--allow-private-network --confirm-local-share]
221
- agentspine share-feed-publish <directory> --base https://store.example/spine --feed team:id --signer signer:id [--id snapshot:id] [--token-env VARIABLE] --confirm-local-share
222
- agentspine share-feed-pull --base https://store.example/spine --feed team:id [--root path] [--token-env VARIABLE] [--allow-private-network --confirm-local-share]
223
- agentspine share-feed-state [root]
224
- agentspine share-peer-serve <directory> --root path --signer signer:id [--timeout-ms 10000] --confirm-local-share
225
- agentspine share-peer-pull --root path --command-json '["ssh","host","agentspine",…]' [--timeout-ms 10000] [--max-bytes n] --confirm-local-share
226
- agentspine share-sqlite-init <directory> --database path --confirm-local-share
227
- agentspine share-sqlite-publish <directory> --database path [--id snapshot:id] --confirm-local-share
228
- agentspine share-sqlite-inspect --database path [--root path]
229
- agentspine share-sqlite-pull --database path [--root path]
230
- agentspine share-inbox [root] [--status pending|accepted|rejected|superseded|rolled-back]
231
- agentspine share-review <id> --decision accept|reject --reason text [--confirmed-by-user]
232
- agentspine share-context [root] [--scope team:id] [--group group:id] [--kind preference,goal]
233
- agentspine share-rollback <id> --reason text
234
- agentspine share-delete <id> [--confirm-local-share]
235
- agentspine share-config [root] --max-items 12
236
- agentspine audit [root] [--json]
237
- agentspine acceptance [--json]
238
- agentspine doctor [--json]
239
- agentspine mcp
240
-
241
- AgentSpine reads existing Markdown in place. It never rewrites source documents.`;
242
- }
243
-
244
- export async function run(argv = process.argv.slice(2)) {
245
- const { command, flags, positional } = parse(argv);
246
- const json = Boolean(flags.json);
247
- if (command === "help" || command === "--help" || command === "-h") return output(help());
248
- if (command === "version" || command === "--version" || command === "-v") return output(VERSION);
249
-
250
- if (command === "acceptance") {
251
- if (positional.length || Object.keys(flags).some((name) => name !== "json")) {
252
- throw new Error("acceptance supports only --json");
253
- }
254
- const report = await runVisibleAcceptance();
255
- if (json) return output(report, true);
256
- process.stdout.write(renderAcceptanceReport(report));
257
- return;
258
- }
259
-
260
- if (command === "scan") {
261
- const result = await scanAndSave(positional[0] || process.cwd());
262
- return output(json ? result : {
263
- root: result.catalog.root,
264
- catalog: result.catalogPath,
265
- documents: result.catalog.summary.total,
266
- protected: result.catalog.summary.protected,
267
- layers: result.catalog.summary.byLayer
268
- }, json);
269
- }
270
-
271
- if (command === "context") {
272
- const root = positional[0] || process.cwd();
273
- const result = await resolveContext({
274
- root,
275
- cwd: flags.cwd || root,
276
- host: flags.host || "generic",
277
- maxBytes: Number(flags["max-bytes"] || 65536),
278
- includeContent: flags["no-content"] !== true
279
- });
280
- return output(result, json);
281
- }
282
-
283
- if (command === "briefing") {
284
- const root = positional[0] || process.cwd();
285
- return output(await sessionBriefing({
286
- root, cwd: flags.cwd || root, host: flags.host || "generic",
287
- entityId: flags.entity || null, groupId: flags.group || null,
288
- userId: flags.user || null, tenantId: flags.tenant || null,
289
- projectId: flags.project || null, currentTaskId: flags["current-task"] || null,
290
- includePrivate: booleanFlag(flags["include-private"]),
291
- focusActive: !booleanFlag(flags["allow-attention"]),
292
- includeSourceContent: !booleanFlag(flags["no-source-content"]),
293
- maxBytes: Number(flags["max-bytes"] ?? 16384)
294
- }), json);
295
- }
296
-
297
- if (command === "read") {
298
- const result = await readDocument({
299
- root: flags.root || process.cwd(),
300
- path: positional[0],
301
- offset: Number(flags.offset || 0),
302
- length: Number(flags.length || 65536)
303
- });
304
- return output(json ? result : result.content, json);
305
- }
306
-
307
- if (command === "verify") {
308
- const result = await verifyCatalog(positional[0] || process.cwd());
309
- output(result, json);
310
- if (!result.ok) process.exitCode = 1;
311
- return;
312
- }
313
-
314
- if (command === "link") {
315
- const result = await linkDocuments({
316
- root: flags.root || process.cwd(), from: positional[0], to: positional[1],
317
- relation: flags.relation || "related", reason: flags.reason || "",
318
- confidence: Number(flags.confidence ?? 0.5)
319
- });
320
- return output(result, json);
321
- }
322
-
323
- if (command === "annotate") {
324
- const result = await annotateDocument({
325
- root: flags.root || process.cwd(), path: positional[0], layer: flags.layer,
326
- reason: flags.reason || "", confidence: Number(flags.confidence ?? 0.5)
327
- });
328
- return output(result, json);
329
- }
330
-
331
- if (command === "entity") {
332
- const result = await upsertEntity({
333
- root: flags.root || process.cwd(), id: positional[0], kind: flags.kind,
334
- displayName: flags.name || "", confidence: Number(flags.confidence ?? 0.5),
335
- privacy: flags.privacy || "private"
336
- });
337
- return output(result, json);
338
- }
339
-
340
- if (command === "relate") {
341
- const result = await linkEntities({
342
- root: flags.root || process.cwd(), from: positional[0], to: positional[1],
343
- relation: flags.relation || "related", reason: flags.reason || "",
344
- confidence: Number(flags.confidence ?? 0.5), privacy: flags.privacy || "private"
345
- });
346
- return output(result, json);
347
- }
348
-
349
- if (command === "relationships") {
350
- return output(await relationshipContext({
351
- root: flags.root || process.cwd(), entityId: positional[0],
352
- includePrivate: Boolean(flags["include-private"]), groupId: flags.group || null
353
- }), json);
354
- }
355
-
356
- if (command === "attention") {
357
- return output(await attentionContext({
358
- root: positional[0] || process.cwd(),
359
- includePrivate: booleanFlag(flags["include-private"]),
360
- focusActive: booleanFlag(flags["focus-active"]),
361
- markPresented: booleanFlag(flags["mark-presented"]),
362
- entityId: flags.entity || null,
363
- groupId: flags.group || null,
364
- projectId: flags.project || null,
365
- currentTaskId: flags.task || null,
366
- maxItems: flags["max-items"] === undefined ? null : Number(flags["max-items"])
367
- }), json);
368
- }
369
-
370
- if (command === "attention-events") {
371
- const { attention, attentionPath } = await loadAttention(positional[0] || process.cwd());
372
- return output({
373
- schema: "agentspine.attention-events/v1", attentionPath,
374
- events: attention.events,
375
- receipts: attention.receipts,
376
- history: booleanFlag(flags["include-history"]) ? attention.history.filter((item) => item.kind === "attention-event") : [],
377
- authority: "context-only"
378
- }, json);
379
- }
380
-
381
- if (command === "attention-add") {
382
- return output(await upsertAttention({
383
- root: flags.root || process.cwd(), id: positional[0], kind: flags.kind,
384
- summary: flags.summary, entityId: flags.entity || null, dueAt: flags.due || null,
385
- priority: Number(flags.priority ?? 50), privacy: flags.privacy || "private",
386
- groupId: flags.group || null,
387
- sourceDocument: flags.source || null, confidence: Number(flags.confidence ?? 0.5)
388
- }), json);
389
- }
390
-
391
- if (command === "attention-resolve") {
392
- return output(await resolveAttention({
393
- root: flags.root || process.cwd(), id: positional[0], status: flags.status || "completed"
394
- }), json);
395
- }
396
-
397
- if (command === "attention-touch") {
398
- return output(await recordActivity({
399
- root: flags.root || process.cwd(), entityId: positional[0], kind: flags.kind || "interaction",
400
- at: flags.at || new Date(), privacy: flags.privacy || "private", groupId: flags.group || null
401
- }), json);
402
- }
403
-
404
- if (command === "attention-delete") {
405
- return output(await deleteAttention({ root: flags.root || process.cwd(), signalId: positional[0] }), json);
406
- }
407
-
408
- if (command === "attention-event-delete") {
409
- return output(await deleteAttention({ root: flags.root || process.cwd(), eventId: positional[0] }), json);
410
- }
411
-
412
- if (command === "attention-purge") {
413
- return output(await deleteAttention({ root: flags.root || process.cwd(), entityId: positional[0] }), json);
414
- }
415
-
416
- if (command === "attention-config") {
417
- const root = positional[0] || process.cwd();
418
- const config = {};
419
- if (flags.enabled !== undefined) config.enabled = booleanFlag(flags.enabled);
420
- if (flags["min-interval-hours"] !== undefined) config.minIntervalHours = Number(flags["min-interval-hours"]);
421
- if (flags["silence-days"] !== undefined) config.entitySilenceDays = Number(flags["silence-days"]);
422
- if (flags["heartbeat-stale-minutes"] !== undefined) config.heartbeatStaleMinutes = Number(flags["heartbeat-stale-minutes"]);
423
- if (flags["max-items"] !== undefined) config.maxItems = Number(flags["max-items"]);
424
- if (flags["quiet-off"] !== undefined) config.quietHours = null;
425
- else if (flags["quiet-start"] !== undefined || flags["quiet-end"] !== undefined) {
426
- config.quietHours = {
427
- start: Number(flags["quiet-start"]), end: Number(flags["quiet-end"]),
428
- utcOffsetMinutes: Number(flags["utc-offset"] ?? 0)
429
- };
430
- }
431
- if (!Object.keys(config).length) return output((await loadAttention(root)).attention.config, json);
432
- return output(await configureAttention({ root, config }), json);
433
- }
434
-
435
- if (command === "learn-propose") {
436
- return output(await proposeLearning({
437
- root: flags.root || process.cwd(), id: positional[0], kind: flags.kind,
438
- claim: flags.claim, subjectId: flags.subject || null, privacy: flags.privacy || "private",
439
- groupId: flags.group || null, supersedesId: flags.supersedes || null,
440
- scope: hasLearningScope(flags) ? learningScope(flags) : null,
441
- evidence: {
442
- id: flags["evidence-id"], type: flags["evidence-type"] || "user-statement",
443
- summary: flags.evidence, sourceDocument: flags.source || null,
444
- confidence: Number(flags.confidence ?? 0.5), observedAt: flags.at
445
- }
446
- }), json);
447
- }
448
-
449
- if (command === "learn-evidence") {
450
- return output(await addLearningEvidence({
451
- root: flags.root || process.cwd(), id: positional[0],
452
- evidence: {
453
- id: flags["evidence-id"], type: flags.type || "interaction", summary: flags.summary,
454
- sourceDocument: flags.source || null, confidence: Number(flags.confidence ?? 0.5), observedAt: flags.at
455
- }
456
- }), json);
457
- }
458
-
459
- if (command === "learn-evidence-revoke") {
460
- return output(await revokeLearningEvidence({
461
- root: flags.root || process.cwd(), learningId: positional[0], evidenceId: flags["evidence-id"],
462
- reasonCode: flags["reason-code"], reason: flags.reason,
463
- confirmation: booleanFlag(flags["confirm-local-evidence"])
464
- ? "local-evidence-revocation-confirmed" : null
465
- }), json);
466
- }
467
-
468
- if (command === "learn-review") {
469
- return output(await reviewLearning({
470
- root: flags.root || process.cwd(), id: positional[0], decision: flags.decision,
471
- reason: flags.reason, confirmedByUser: booleanFlag(flags["confirmed-by-user"])
472
- }), json);
473
- }
474
-
475
- if (command === "learn-context") {
476
- return output(await learningContext({
477
- root: positional[0] || process.cwd(), includePrivate: booleanFlag(flags["include-private"]),
478
- groupId: flags.group || null,
479
- scope: hasLearningScope(flags) ? learningScope(flags) : null,
480
- kinds: flags.kind ? String(flags.kind).split(",").filter(Boolean) : null,
481
- subjectIds: flags.subject ? String(flags.subject).split(",").filter(Boolean) : null,
482
- maxItems: flags["max-items"] === undefined ? null : Number(flags["max-items"])
483
- }), json);
484
- }
485
-
486
- if (command === "learn-evaluate") {
487
- return output(await evaluateLearning({ root: positional[0] || process.cwd() }), json);
488
- }
489
-
490
- if (command === "learn-evaluator-register") {
491
- return output(await registerLearningEvaluator({
492
- root: flags.root || process.cwd(), id: positional[0], principalDigest: flags["principal-digest"],
493
- confirmLocalEvaluator: booleanFlag(flags["confirm-local-evaluator"])
494
- }), json);
495
- }
496
-
497
- if (command === "learn-evaluator-revoke") {
498
- return output(await revokeLearningEvaluator({
499
- root: flags.root || process.cwd(), id: positional[0], reason: flags.reason,
500
- confirmLocalEvaluator: booleanFlag(flags["confirm-local-evaluator"])
501
- }), json);
502
- }
503
-
504
- if (command === "learn-evaluation") {
505
- return output(await registerLearningEvaluation({
506
- root: flags.root || process.cwd(), id: positional[0], learningId: flags.learning,
507
- scope: learningScope(flags), metric: { name: flags.metric, direction: flags.direction },
508
- benchmark: {
509
- taskDigest: flags["task-digest"], datasetDigest: flags["dataset-digest"],
510
- protocolDigest: flags["protocol-digest"], minCases: Number(flags["min-cases"])
511
- },
512
- evaluatorIds: String(flags.evaluators || "").split(",").filter(Boolean),
513
- evaluatorRoots: evaluatorRootsFlag(flags["evaluator-roots"]),
514
- expiresAt: flags["expires-at"] || null,
515
- retryTrialFailureId: flags["retry-trial-failure"] || null,
516
- confirmLocalTrialRetry: booleanFlag(flags["confirm-local-trial-retry"]),
517
- confirmLocalEvaluation: booleanFlag(flags["confirm-local-evaluation"])
518
- }), json);
519
- }
520
-
521
- if (command === "learn-revalidation-start") {
522
- return output(await beginLearningRevalidation({
523
- root: flags.root || process.cwd(), learningId: positional[0],
524
- confirmLocalValidation: booleanFlag(flags["confirm-local-validation"])
525
- }), json);
526
- }
527
-
528
- if (command === "learn-revalidate") {
529
- const measurements = String(flags.measurements || "").split(",").filter(Boolean);
530
- const applications = String(flags.applications || "").split(",").filter(Boolean);
531
- const deliveries = String(flags.deliveries || "").split(",").filter(Boolean);
532
- if (!measurements.length || measurements.length !== applications.length || measurements.length !== deliveries.length) {
533
- throw new Error("revalidation requires equally sized measurement, application, and delivery lists");
534
- }
535
- return output(await renewLearningValidation({
536
- root: flags.root || process.cwd(), learningId: positional[0],
537
- evidence: measurements.map((measurementId, index) => ({ measurementId,
538
- applicationId: applications[index], deliveryId: deliveries[index] })),
539
- confirmLocalValidation: booleanFlag(flags["confirm-local-validation"])
540
- }), json);
541
- }
542
-
543
- if (command === "learn-measurement") {
544
- return output(await recordLearningMeasurement({
545
- root: flags.root || process.cwd(), id: positional[0], learningId: flags.learning,
546
- evaluationId: flags.evaluation, phase: flags.phase, scope: learningScope(flags),
547
- metric: {
548
- name: flags.metric, direction: flags.direction, value: Number(flags.value),
549
- blockingDefects: Number(flags["blocking-defects"] ?? 0)
550
- },
551
- measurement: {
552
- kind: flags.measurement || "objective", evaluatorId: flags.evaluator,
553
- runId: flags.run, sourceDigest: flags["source-digest"]
554
- },
555
- coverage: { datasetDigest: flags["dataset-digest"], caseCount: Number(flags["case-count"]) },
556
- measuredAt: flags.at,
557
- confirmLocalMeasurement: booleanFlag(flags["confirm-local-measurement"])
558
- }), json);
559
- }
560
-
561
- if (command === "learn-measurement-revoke") {
562
- return output(await revokeLearningMeasurement({
563
- root: flags.root || process.cwd(), measurementId: positional[0], reasonCode: flags["reason-code"],
564
- reason: flags.reason,
565
- confirmation: booleanFlag(flags["confirm-local-measurement-revocation"])
566
- ? "local-measurement-revocation-confirmed" : null
567
- }), json);
568
- }
569
-
570
- if (command === "learn-evaluation-revoke") {
571
- return output(await revokeLearningEvaluation({
572
- root: flags.root || process.cwd(), evaluationId: positional[0], reasonCode: flags["reason-code"],
573
- reason: flags.reason,
574
- confirmation: booleanFlag(flags["confirm-local-evaluation-revocation"])
575
- ? "local-evaluation-revocation-confirmed" : null
576
- }), json);
577
- }
578
-
579
- if (command === "learn-validation-revoke") {
580
- return output(await revokeLearningValidation({
581
- root: flags.root || process.cwd(), validationLeaseId: positional[0], reasonCode: flags["reason-code"],
582
- reason: flags.reason,
583
- confirmation: booleanFlag(flags["confirm-local-validation-revocation"])
584
- ? "local-validation-revocation-confirmed" : null
585
- }), json);
586
- }
587
-
588
- if (command === "learn-trial-failure-revoke") {
589
- return output(await revokeLearningTrialFailure({
590
- root: flags.root || process.cwd(), trialFailureId: positional[0], reasonCode: flags["reason-code"],
591
- reason: flags.reason,
592
- confirmation: booleanFlag(flags["confirm-local-trial-failure-revocation"])
593
- ? "local-trial-failure-revocation-confirmed" : null
594
- }), json);
595
- }
596
-
597
- if (command === "learn-application-revoke") {
598
- return output(await revokeLearningApplication({
599
- root: flags.root || process.cwd(), applicationId: positional[0], reasonCode: flags["reason-code"],
600
- reason: flags.reason,
601
- confirmation: booleanFlag(flags["confirm-local-application-revocation"])
602
- ? "local-application-revocation-confirmed" : null
603
- }), json);
604
- }
605
-
606
- if (command === "learn-delivery-revoke") {
607
- return output(await revokeLearningDelivery({
608
- root: flags.root || process.cwd(), deliveryId: positional[0], reasonCode: flags["reason-code"],
609
- reason: flags.reason,
610
- confirmation: booleanFlag(flags["confirm-local-delivery-revocation"])
611
- ? "local-delivery-revocation-confirmed" : null
612
- }), json);
613
- }
614
-
615
- if (command === "learn-outcome-revoke") {
616
- return output(await revokeLearningOutcome({
617
- root: flags.root || process.cwd(), outcomeId: positional[0], reasonCode: flags["reason-code"],
618
- reason: flags.reason,
619
- confirmation: booleanFlag(flags["confirm-local-outcome-revocation"])
620
- ? "local-outcome-revocation-confirmed" : null
621
- }), json);
622
- }
623
-
624
- if (command === "learn-outcome") {
625
- const lineage = flags["measurement-receipt"] || null;
626
- return output(await recordLearningOutcome({
627
- root: flags.root || process.cwd(), learningId: positional[0], id: flags.id,
628
- evaluationId: flags.evaluation, measurementReceiptId: lineage,
629
- applicationId: flags.application || null, deliveryId: flags.delivery || null,
630
- ...(lineage ? {} : { phase: flags.phase, scope: learningScope(flags), metric: {
631
- name: flags.metric, direction: flags.direction, value: Number(flags.value),
632
- blockingDefects: Number(flags["blocking-defects"] ?? 0)
633
- },
634
- measurement: {
635
- kind: flags.measurement || "objective", evaluatorId: flags.evaluator,
636
- sourceDigest: flags["source-digest"] || null
637
- },
638
- coverage: {
639
- datasetDigest: flags["dataset-digest"],
640
- caseCount: Number(flags["case-count"])
641
- },
642
- measuredAt: flags.at })
643
- }), json);
644
- }
645
-
646
- if (command === "learn-status") {
647
- return output(await learningOutcomeStatus({
648
- root: positional[0] || process.cwd(), scope: hasLearningScope(flags) ? learningScope(flags) : null
649
- }), json);
650
- }
651
-
652
- if (command === "learn-delivery-purge") {
653
- return output(await purgeStaleLearningApplications({
654
- root: positional[0] || process.cwd(),
655
- confirmation: booleanFlag(flags["confirm-local-purge"]) ? "local-user-purge-confirmed" : null
656
- }), json);
657
- }
658
-
659
- if (command === "learn-measurement-purge") {
660
- return output(await purgeStaleLearningMeasurements({
661
- root: positional[0] || process.cwd(),
662
- confirmation: booleanFlag(flags["confirm-local-purge"]) ? "local-user-purge-confirmed" : null
663
- }), json);
664
- }
665
-
666
- if (command === "learn-rollback") {
667
- return output(await rollbackLearning({
668
- root: flags.root || process.cwd(), id: positional[0], reason: flags.reason
669
- }), json);
670
- }
671
-
672
- if (command === "learn-delete") {
673
- return output(await deleteLearning({ root: flags.root || process.cwd(), id: positional[0] }), json);
674
- }
675
-
676
- if (command === "learn-config") {
677
- const root = positional[0] || process.cwd();
678
- const config = {};
679
- if (flags["auto-promote"] !== undefined) config.autoPromote = booleanFlag(flags["auto-promote"]);
680
- if (flags["min-confidence"] !== undefined) config.minConfidence = Number(flags["min-confidence"]);
681
- if (flags["min-evidence"] !== undefined) config.minEvidence = Number(flags["min-evidence"]);
682
- if (flags["max-items"] !== undefined) config.maxContextItems = Number(flags["max-items"]);
683
- if (flags["min-outcomes"] !== undefined) config.minOutcomeReceipts = Number(flags["min-outcomes"]);
684
- if (flags["min-improvement"] !== undefined) config.minImprovement = Number(flags["min-improvement"]);
685
- if (flags["regression-tolerance"] !== undefined) config.regressionTolerance = Number(flags["regression-tolerance"]);
686
- if (flags["outcome-max-age-days"] !== undefined) config.outcomeMaxAgeDays = Number(flags["outcome-max-age-days"]);
687
- if (flags["canary-receipts"] !== undefined) config.canaryReceipts = Number(flags["canary-receipts"]);
688
- if (flags["canary-ttl-days"] !== undefined) config.canaryTtlDays = Number(flags["canary-ttl-days"]);
689
- if (flags["initial-trial-outcome-timeout-minutes"] !== undefined) {
690
- config.initialTrialOutcomeTimeoutMinutes = Number(flags["initial-trial-outcome-timeout-minutes"]);
691
- }
692
- if (!Object.keys(config).length) return output((await loadLearning(root)).learning.config, json);
693
- return output(await configureLearning({ root, config }), json);
694
- }
695
-
696
- if (command === "continuity-config") {
697
- const root = positional[0] || process.cwd();
698
- const config = {};
699
- if (flags.enabled !== undefined) config.enabled = booleanFlag(flags.enabled);
700
- if (flags.entity !== undefined) config.defaultEntityId = flags.entity === "none" ? null : flags.entity;
701
- if (flags.project !== undefined) config.defaultProjectId = flags.project === "none" ? null : flags.project;
702
- if (flags["min-confidence"] !== undefined) config.minConfidence = Number(flags["min-confidence"]);
703
- if (flags["min-directness"] !== undefined) config.minDirectness = Number(flags["min-directness"]);
704
- if (flags["min-evidence"] !== undefined) config.minEvidence = Number(flags["min-evidence"]);
705
- if (flags["max-prompt-bytes"] !== undefined) config.maxPromptBytes = Number(flags["max-prompt-bytes"]);
706
- if (flags["max-briefing-bytes"] !== undefined) config.maxBriefingBytes = Number(flags["max-briefing-bytes"]);
707
- if (!Object.keys(config).length) return output((await loadContinuity(root)).continuity.config, json);
708
- return output(await configureContinuity({
709
- root, config,
710
- confirmation: booleanFlag(flags["confirm-local-opt-in"]) ? "local-user-opt-in" : null
711
- }), json);
712
- }
713
-
714
- if (command === "continuity-status") {
715
- const { continuity, continuityPath } = await loadContinuity(positional[0] || process.cwd());
716
- return output({
717
- schema: continuity.schema, config: continuity.config,
718
- signals: continuity.signals.length, history: continuity.history.length,
719
- continuityPath, authority: "context-only"
720
- }, json);
721
- }
722
-
723
- if (command === "continuity-purge") {
724
- return output(await purgeContinuity({
725
- root: flags.root || process.cwd(), subjectId: positional[0],
726
- confirmation: booleanFlag(flags["confirm-local-purge"]) ? "local-user-confirmed" : null
727
- }), json);
728
- }
729
-
730
- if (command === "preflight-policy") {
731
- if (!positional[0]) throw new Error("preflight-policy requires one local JSON policy file");
732
- const profile = JSON.parse(await readFile(positional[0], "utf8"));
733
- return output(await configurePreflightPolicy({ profile,
734
- confirmation: booleanFlag(flags["confirm-local-policy"]) ? "local-owner-confirmed" : null }), json);
735
- }
736
-
737
- if (command === "preflight-status") return output(await preflightStatus(), json);
738
-
739
- if (command === "remember-propose") {
740
- return output(await proposeMustRemember({ claim: flags.claim, kind: flags.kind || "critical",
741
- userId: flags.user, tenantId: flags.tenant, projectId: flags.project || null,
742
- groupId: flags.group || null, taskId: flags.task || null, sourceDigest: flags["source-digest"] || null }), json);
743
- }
744
-
745
- if (command === "remember-confirm") {
746
- return output(await confirmMustRemember({ candidateId: positional[0], supersedes: flags.supersedes || null,
747
- confirmation: booleanFlag(flags["confirm-local-user"]) ? "local-user-confirmed" : null }), json);
748
- }
749
-
750
- if (command === "remember-rollback") {
751
- return output(await rollbackMustRemember({ id: positional[0],
752
- confirmation: booleanFlag(flags["confirm-local-user"]) ? "local-user-confirmed" : null }), json);
753
- }
754
-
755
- if (command === "remember-purge") {
756
- return output(await purgeMustRemember({ id: positional[0],
757
- confirmation: booleanFlag(flags["confirm-local-purge"]) ? "local-user-purge-confirmed" : null }), json);
758
- }
759
-
760
- if (command === "source-status") {
761
- const host = flags.host || process.env.AGENTSPINE_HOST;
762
- if (!["claude", "codex"].includes(host)) throw new Error("source-status requires --host claude or --host codex");
763
- const resolved = await resolveHostSourceCatalog({ host, cwd: flags.cwd || process.cwd() });
764
- const registry = await inspectSourceRegistry();
765
- return output({ diagnostics: resolved.diagnostics, sources: resolved.catalog.documents.map((item) => ({
766
- id: item.relativePath, scope: item.sourceScope, binding: item.sourceBinding, sha256: item.sha256,
767
- bytes: item.bytes, authority: item.authority
768
- })), bindings: registry.registry.bindings, authority: "context-only" }, json);
769
- }
770
-
771
- if (command === "source-bind") {
772
- return output(await bindSourceRoot({
773
- host: flags.host, hostHome: flags["host-home"] || process.env.CLAUDE_CONFIG_DIR || process.env.CODEX_HOME || process.cwd(),
774
- projectRoot: flags.project || process.cwd(), sourceRoot: positional[0], scope: flags.scope || "state-user",
775
- confirmation: booleanFlag(flags["confirm-local-binding"]) ? "local-user-confirmed" : null
776
- }), json);
777
- }
778
-
779
- if (command === "source-rollback") {
780
- return output(await rollbackSourceBinding({ id: positional[0],
781
- confirmation: booleanFlag(flags["confirm-local-binding"]) ? "local-user-confirmed" : null }), json);
782
- }
783
-
784
- if (command === "source-purge") {
785
- return output(await purgeSourceBinding({ id: positional[0],
786
- confirmation: booleanFlag(flags["confirm-local-binding"]) ? "local-user-confirmed" : null }), json);
787
- }
788
-
789
- if (command === "delegation-grant") {
790
- return output(await grantDelegation({
791
- root: flags.root || process.cwd(), actorId: positional[0],
792
- id: flags.id, actions: String(flags.actions || "").split(",").filter(Boolean),
793
- targetIds: String(flags.targets || "*").split(",").filter(Boolean), reason: flags.reason,
794
- confirmation: booleanFlag(flags["confirm-local-policy"]) ? "local-owner-confirmed" : null
795
- }), json);
796
- }
797
-
798
- if (command === "delegation-revoke") {
799
- return output(await revokeDelegation({
800
- root: flags.root || process.cwd(), id: positional[0], reason: flags.reason,
801
- confirmation: booleanFlag(flags["confirm-local-policy"]) ? "local-owner-confirmed" : null
802
- }), json);
803
- }
804
-
805
- if (command === "delegation-check") {
806
- return output(await checkDelegation({
807
- root: flags.root || process.cwd(), actorId: positional[0], action: flags.action,
808
- targetId: flags.target || null
809
- }), json);
810
- }
811
-
812
- if (command === "delegation-policy") {
813
- return output((await loadDelegationPolicy(positional[0] || process.cwd())).policy, json);
814
- }
815
-
816
- if (command === "task-create") {
817
- return output(await createTask({
818
- root: flags.root || process.cwd(), id: positional[0], actorId: flags.actor,
819
- assigneeId: flags.assignee || null, kind: flags.kind || "task", title: flags.title,
820
- summary: flags.summary || null, projectId: flags.project || null,
821
- privacy: flags.privacy || "private", groupId: flags.group || null,
822
- priority: Number(flags.priority ?? 50), dueAt: flags.due || null
823
- }), json);
824
- }
825
-
826
- if (command === "task-update") {
827
- const patch = {};
828
- if (flags.status !== undefined) patch.status = flags.status;
829
- if (flags.assignee !== undefined) patch.assigneeId = flags.assignee;
830
- if (flags.unassign !== undefined) patch.assigneeId = null;
831
- if (flags.title !== undefined) patch.title = flags.title;
832
- if (flags.summary !== undefined) patch.summary = flags.summary;
833
- if (flags.priority !== undefined) patch.priority = Number(flags.priority);
834
- if (flags.due !== undefined) patch.dueAt = flags.due;
835
- if (flags["clear-due"] !== undefined) patch.dueAt = null;
836
- if (flags.note !== undefined) patch.note = flags.note;
837
- return output(await updateTask({
838
- root: flags.root || process.cwd(), id: positional[0], actorId: flags.actor, patch
839
- }), json);
840
- }
841
-
842
- if (command === "tasks") {
843
- return output(await taskContext({
844
- root: positional[0] || process.cwd(), includePrivate: booleanFlag(flags["include-private"]),
845
- groupId: flags.group || null, actorId: flags.actor || null, assigneeId: flags.assignee || null,
846
- projectId: flags.project || null, includeClosed: booleanFlag(flags.closed),
847
- maxItems: Number(flags["max-items"] ?? 20)
848
- }), json);
849
- }
850
-
851
- if (command === "task-delete") {
852
- return output(await deleteTask({
853
- root: flags.root || process.cwd(), id: positional[0],
854
- confirmation: booleanFlag(flags["confirm-local-policy"]) ? "local-owner-confirmed" : null
855
- }), json);
856
- }
857
-
858
- if (command === "execution-grant") {
859
- return output(await grantExecution({
860
- root: flags.root || process.cwd(), id: flags.id, jobId: positional[0], actorId: flags.actor,
861
- taskId: flags.task, targetId: flags.target, projectId: flags.project, groupId: flags.group || null,
862
- host: flags.host, capabilities: String(flags.capabilities || "").split(",").filter(Boolean),
863
- reason: flags.reason, expiresAt: flags.expires || null,
864
- confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
865
- }), json);
866
- }
867
-
868
- if (command === "execution-revoke") {
869
- return output(await revokeExecution({
870
- root: flags.root || process.cwd(), id: positional[0], reason: flags.reason,
871
- confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
872
- }), json);
873
- }
874
-
875
- if (command === "execution-policy") {
876
- return output((await loadExecutionPolicy(positional[0] || process.cwd())).policy, json);
877
- }
878
-
879
- if (command === "job-register") {
880
- return output(await registerJob({
881
- root: flags.root || process.cwd(), id: positional[0], grantId: flags.grant,
882
- maxRetries: Number(flags["max-retries"] ?? 3), leaseSeconds: Number(flags["lease-seconds"] ?? 120),
883
- baseRetrySeconds: Number(flags["retry-seconds"] ?? 5),
884
- confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
885
- }), json);
886
- }
887
-
888
- if (command === "jobs") {
889
- return output(await selfstarterContext({
890
- root: positional[0] || process.cwd(), actorId: flags.actor || null,
891
- projectId: flags.project || null, taskId: flags.task || null,
892
- includeTerminal: booleanFlag(flags["include-terminal"])
893
- }), json);
894
- }
895
-
896
- if (command === "job-cancel") {
897
- return output(await cancelJob({
898
- root: flags.root || process.cwd(), id: positional[0], reason: flags.reason,
899
- confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
900
- }), json);
901
- }
902
-
903
- if (command === "job-delete") {
904
- return output(await deleteJob({
905
- root: flags.root || process.cwd(), id: positional[0],
906
- confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
907
- }), json);
908
- }
909
-
910
- if (command === "channel-bind") {
911
- return output(await grantChannelBinding({
912
- root: flags.root || process.cwd(), id: positional[0], provider: flags.provider,
913
- tenantId: flags.tenant, accountId: flags.account, chatId: flags.chat,
914
- threadId: flags.thread || null,
915
- senderIds: String(flags.senders || "").split(",").filter(Boolean),
916
- agentId: flags.agent, projectId: flags.project, groupId: flags.group || null,
917
- sessionKey: flags.session, secretEnv: flags["secret-env"], outboundSecretEnv: flags["outbound-secret-env"] || null,
918
- capabilities: String(flags.capabilities || "receive,reply").split(",").filter(Boolean),
919
- confirmation: booleanFlag(flags["confirm-local-channel"]) ? "local-owner-confirmed" : null
920
- }), json);
921
- }
922
-
923
- if (command === "channel-revoke") {
924
- return output(await revokeChannelBinding({
925
- root: flags.root || process.cwd(), id: positional[0], reason: flags.reason,
926
- confirmation: booleanFlag(flags["confirm-local-channel"]) ? "local-owner-confirmed" : null
927
- }), json);
928
- }
929
-
930
- if (command === "channel-policy") {
931
- return output((await loadChannelPolicy(positional[0] || process.cwd())).policy, json);
932
- }
933
-
934
- if (command === "channel-events") {
935
- return output(await channelRuntimeContext({
936
- root: positional[0] || process.cwd(), agentId: flags.agent || null,
937
- projectId: flags.project || null,
938
- ...(flags.group !== undefined ? { groupId: flags.group || null } : {}),
939
- provider: flags.provider || null,
940
- includeTerminal: booleanFlag(flags["include-terminal"]),
941
- maxItems: Number(flags["max-items"] ?? 20)
942
- }), json);
943
- }
944
-
945
- if (command === "persona-sync") {
946
- if (!booleanFlag(flags["confirm-local-persona"])) throw new Error("persona synchronization requires --confirm-local-persona");
947
- return output(await syncPersonaRosterFromEnvironment({
948
- root: positional[0] || process.cwd(), env: { ...process.env, AGENTSPINE_PERSONA_ROSTER_FILE: flags.roster }
949
- }), json);
950
- }
951
-
952
- if (command === "personas") {
953
- return output(await personaContext({
954
- root: positional[0] || process.cwd(), personaId: flags.persona || null,
955
- ...(flags.group !== undefined ? { groupId: flags.group || null } : {}),
956
- includeInactive: booleanFlag(flags["include-inactive"])
957
- }), json);
958
- }
959
-
960
- if (command === "goal-assign") {
961
- return output(await assignGoal({
962
- root: flags.root || process.cwd(), goalId: positional[0], agentId: flags.agent,
963
- ownerSubjectId: flags.owner, projectId: flags.project, groupId: flags.group || null,
964
- priority: Number(flags.priority ?? 70), successCriterion: flags.success,
965
- nextSafeStep: flags["next-step"], deadline: flags.deadline || null,
966
- confirmation: booleanFlag(flags["confirm-local-goal"]) ? "local-owner-confirmed" : null
967
- }), json);
968
- }
969
-
970
- if (command === "gateway-control") {
971
- return output(await setGatewayControl({
972
- root: positional[0] || process.cwd(),
973
- ...(flags.enabled !== undefined ? { enabled: booleanFlag(flags.enabled) } : {}),
974
- ...(flags["kill-switch"] !== undefined ? { killSwitch: booleanFlag(flags["kill-switch"]) } : {}),
975
- confirmation: booleanFlag(flags["confirm-local-gateway"]) ? "local-owner-confirmed" : null
976
- }), json);
977
- }
978
-
979
- if (command === "gateway-status") {
980
- return output(await gatewayContext({ root: positional[0] || process.cwd(), agentId: flags.agent || null }), json);
981
- }
982
-
983
- if (command === "share-init") {
984
- return output(await initDirectoryAdapter({
985
- root: flags.root || process.cwd(), directory: positional[0], scopeId: flags.scope,
986
- adapterId: flags.adapter, signerId: flags.signer || null,
987
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
988
- }), json);
989
- }
990
-
991
- if (command === "share-keygen") {
992
- return output(await generateSigningIdentity({
993
- root: flags.root || process.cwd(), signerId: positional[0], rotate: booleanFlag(flags.rotate),
994
- publicOut: flags["public-out"] || null,
995
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
996
- }), json);
997
- }
998
-
999
- if (command === "share-signers") {
1000
- return output(await listSigningIdentities({ root: positional[0] || process.cwd() }), json);
1001
- }
1002
-
1003
- if (command === "share-trust") {
1004
- return output(await trustSigner({
1005
- root: flags.root || process.cwd(), publicIdentityPath: positional[0],
1006
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1007
- }), json);
1008
- }
1009
-
1010
- if (command === "share-trust-revoke") {
1011
- return output(await revokeTrustedSigner({
1012
- root: flags.root || process.cwd(), keyId: positional[0], reason: flags.reason,
1013
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1014
- }), json);
1015
- }
1016
-
1017
- if (command === "share-trust-list") {
1018
- return output(await trustedSignerContext({
1019
- root: positional[0] || process.cwd(), includeRevoked: booleanFlag(flags["include-revoked"])
1020
- }), json);
1021
- }
1022
-
1023
- if (command === "share-publish") {
1024
- return output(await publishLearning({
1025
- root: flags.root || process.cwd(), directory: positional[0], learningId: flags.learning,
1026
- eventId: flags.id, supersedesEventId: flags.supersedes || null, signerId: flags.signer || null,
1027
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1028
- }), json);
1029
- }
1030
-
1031
- if (command === "share-pull") {
1032
- return output(await pullShared({
1033
- root: flags.root || process.cwd(), directory: positional[0],
1034
- requireAuthenticated: booleanFlag(flags["require-authenticated"])
1035
- }), json);
1036
- }
1037
-
1038
- if (command === "share-snapshot-export") {
1039
- return output(await exportHttpsSnapshot({
1040
- root: flags.root || process.cwd(), directory: positional[0], output: flags.out,
1041
- snapshotId: flags.id,
1042
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1043
- }), json);
1044
- }
1045
-
1046
- if (command === "share-https-publish") {
1047
- return output(await publishHttpsSnapshot({
1048
- root: flags.root || process.cwd(), directory: positional[0], baseUrl: flags.base,
1049
- snapshotId: flags.id, tokenEnv: flags["token-env"] || null,
1050
- timeoutMs: Number(flags["timeout-ms"] ?? 10000),
1051
- allowPrivateNetwork: booleanFlag(flags["allow-private-network"]),
1052
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1053
- }), json);
1054
- }
1055
-
1056
- if (command === "share-https-pull") {
1057
- return output(await pullHttpsSnapshot({
1058
- root: flags.root || process.cwd(), url: positional[0], tokenEnv: flags["token-env"] || null,
1059
- timeoutMs: Number(flags["timeout-ms"] ?? 10000),
1060
- allowPrivateNetwork: booleanFlag(flags["allow-private-network"]),
1061
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1062
- }), json);
1063
- }
1064
-
1065
- if (command === "share-feed-publish") {
1066
- return output(await publishHttpsFeed({
1067
- root: flags.root || process.cwd(), directory: positional[0], baseUrl: flags.base,
1068
- feedId: flags.feed, signerId: flags.signer, snapshotId: flags.id,
1069
- tokenEnv: flags["token-env"] || null, timeoutMs: Number(flags["timeout-ms"] ?? 10000),
1070
- allowPrivateNetwork: booleanFlag(flags["allow-private-network"]),
1071
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1072
- }), json);
1073
- }
1074
-
1075
- if (command === "share-feed-pull") {
1076
- return output(await pullHttpsFeed({
1077
- root: flags.root || process.cwd(), baseUrl: flags.base, feedId: flags.feed,
1078
- tokenEnv: flags["token-env"] || null, timeoutMs: Number(flags["timeout-ms"] ?? 10000),
1079
- allowPrivateNetwork: booleanFlag(flags["allow-private-network"]),
1080
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1081
- }), json);
1082
- }
1083
-
1084
- if (command === "share-feed-state") {
1085
- return output(await loadHttpsFeedState(positional[0] || process.cwd()), json);
1086
- }
1087
-
1088
- if (command === "share-peer-serve") {
1089
- await servePeerOnce({
1090
- root: flags.root || process.cwd(), directory: positional[0], signerId: flags.signer,
1091
- input: process.stdin, output: process.stdout,
1092
- timeoutMs: Number(flags["timeout-ms"] ?? 10000),
1093
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1094
- });
1095
- return;
1096
- }
1097
-
1098
- if (command === "share-peer-pull") {
1099
- return output(await pullPeerCommand({
1100
- root: flags.root || process.cwd(), commandJson: flags["command-json"],
1101
- timeoutMs: Number(flags["timeout-ms"] ?? 10000),
1102
- maxBytes: Number(flags["max-bytes"] ?? 22 * 1024 * 1024),
1103
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1104
- }), json);
1105
- }
1106
-
1107
- if (command === "share-sqlite-init") {
1108
- return output(await initSqliteAdapter({
1109
- root: flags.root || process.cwd(), directory: positional[0], database: flags.database,
1110
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1111
- }), json);
1112
- }
1113
-
1114
- if (command === "share-sqlite-publish") {
1115
- return output(await publishSqliteSnapshot({
1116
- root: flags.root || process.cwd(), directory: positional[0], database: flags.database,
1117
- snapshotId: flags.id,
1118
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1119
- }), json);
1120
- }
1121
-
1122
- if (command === "share-sqlite-inspect") {
1123
- return output(await inspectSqliteAdapter({
1124
- root: flags.root || process.cwd(), database: flags.database
1125
- }), json);
1126
- }
1127
-
1128
- if (command === "share-sqlite-pull") {
1129
- return output(await pullSqliteSnapshot({
1130
- root: flags.root || process.cwd(), database: flags.database
1131
- }), json);
1132
- }
1133
-
1134
- if (command === "share-inbox") {
1135
- return output(await sharedInbox({ root: positional[0] || process.cwd(), status: flags.status || "pending" }), json);
1136
- }
1137
-
1138
- if (command === "share-review") {
1139
- return output(await reviewShared({
1140
- root: flags.root || process.cwd(), id: positional[0], decision: flags.decision,
1141
- reason: flags.reason, confirmedByUser: booleanFlag(flags["confirmed-by-user"])
1142
- }), json);
1143
- }
1144
-
1145
- if (command === "share-context") {
1146
- return output(await sharedContext({
1147
- root: positional[0] || process.cwd(), scopeId: flags.scope || null, groupId: flags.group || null,
1148
- includePrivate: booleanFlag(flags["include-private"]),
1149
- kinds: flags.kind ? String(flags.kind).split(",").filter(Boolean) : null,
1150
- subjectIds: flags.subject ? String(flags.subject).split(",").filter(Boolean) : null,
1151
- maxItems: flags["max-items"] === undefined ? null : Number(flags["max-items"])
1152
- }), json);
1153
- }
1154
-
1155
- if (command === "share-rollback") {
1156
- return output(await rollbackShared({
1157
- root: flags.root || process.cwd(), id: positional[0], reason: flags.reason
1158
- }), json);
1159
- }
1160
-
1161
- if (command === "share-delete") {
1162
- return output(await deleteShared({
1163
- root: flags.root || process.cwd(), id: positional[0],
1164
- confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1165
- }), json);
1166
- }
1167
-
1168
- if (command === "share-config") {
1169
- return output(await configureSharing({
1170
- root: positional[0] || process.cwd(), maxContextItems: Number(flags["max-items"])
1171
- }), json);
1172
- }
1173
-
1174
- if (command === "doctor") {
1175
- let hostIntegration;
1176
- try {
1177
- hostIntegration = await checkHosts(fileURLToPath(new URL("..", import.meta.url)));
1178
- } catch (error) {
1179
- hostIntegration = { ok: false, error: error.message };
1180
- }
1181
- let sourceResolution = null;
1182
- let orphanScan = null;
1183
- const sourceHost = flags.host || process.env.AGENTSPINE_HOST;
1184
- if (["claude", "codex"].includes(sourceHost)) {
1185
- try {
1186
- const resolved = await resolveHostSourceCatalog({ host: sourceHost, cwd: flags.cwd || process.cwd() });
1187
- sourceResolution = resolved.diagnostics;
1188
- if (booleanFlag(flags["offline-memory-orphans"])) {
1189
- if (sourceHost !== "claude" || !resolved.memoryRoot) throw new Error("offline memory orphan scan requires a resolved Claude project-memory root");
1190
- orphanScan = await scanIndexedMemoryOrphans(resolved.memoryRoot);
1191
- }
1192
- }
1193
- catch (error) { sourceResolution = { status: "failed-closed", reason: error.message }; }
1194
- }
1195
- let preflight;
1196
- try { preflight = await preflightStatus(); }
1197
- catch (error) { preflight = { status: "failed-closed", error: error.message }; }
1198
- let learningOutcomes;
1199
- try {
1200
- const status = await learningOutcomeStatus({ root: positional[0] || process.cwd() });
1201
- const unboundAfterReceipts = status.records.reduce((sum, item) => sum + item.afterReceipts - item.boundAfterReceipts, 0);
1202
- const undeliveredAfterReceipts = status.records.reduce((sum, item) => sum + item.afterReceipts - item.deliveredAfterReceipts, 0);
1203
- const stalePendingApplications = status.records.reduce((sum, item) => sum + item.stalePendingApplications, 0);
1204
- const totalOutcomeReceipts = status.records.reduce((sum, item) => sum + item.beforeReceipts + item.afterReceipts, 0);
1205
- const plannedOutcomeReceipts = status.records.reduce((sum, item) => sum + item.plannedOutcomeReceipts, 0);
1206
- const coverageBoundReceipts = status.records.reduce((sum, item) => sum + item.coverageBoundReceipts, 0);
1207
- const legacyCoverageReceipts = status.records.reduce((sum, item) => sum + item.legacyCoverageReceipts, 0);
1208
- const provenanceBoundReceipts = status.records.reduce((sum, item) => sum + item.provenanceBoundReceipts, 0);
1209
- const legacyProvenanceReceipts = status.records.reduce((sum, item) => sum + item.legacyProvenanceReceipts, 0);
1210
- const measurementReceipts = status.records.reduce((sum, item) => sum + item.measurementReceipts, 0);
1211
- const measurementLineageReceipts = status.records.reduce((sum, item) => sum + item.measurementLineageReceipts, 0);
1212
- const consumedMeasurementReceipts = status.records.reduce((sum, item) => sum + item.consumedMeasurementReceipts, 0);
1213
- const staleUnconsumedMeasurements = status.records.reduce((sum, item) => sum + item.staleUnconsumedMeasurements, 0);
1214
- const lineageBoundReceipts = status.records.reduce((sum, item) => sum + item.lineageBoundReceipts, 0);
1215
- const pairedOutcomeReceipts = status.records.reduce((sum, item) => sum + item.pairedOutcomeReceipts, 0);
1216
- const pairedEvaluatorPairs = status.records.reduce((sum, item) => sum + item.pairedEvaluatorPairs, 0);
1217
- const evaluatorRootBoundReceipts = status.records.reduce((sum, item) => sum + item.evaluatorRootBoundReceipts, 0);
1218
- const independentEvaluatorRoots = status.records.reduce((sum, item) => sum + item.independentEvaluatorRoots, 0);
1219
- const evaluatorRegistryContracts = status.records.reduce((sum, item) => sum + item.evaluatorRegistryContracts, 0);
1220
- const inactiveEvaluatorRegistryContracts = status.records.reduce((sum, item) => sum + item.inactiveEvaluatorRegistryContracts, 0);
1221
- const currentValidatedLessons = status.records.filter((item) => item.validationLeaseStatus === "current-validated").length;
1222
- const staleValidatedLessons = status.records.filter((item) => item.validationLeaseStatus === "stale-validated").length;
1223
- const unprovenValidatedLessons = status.records.filter((item) => item.validationLeaseStatus === "unproven-validated").length;
1224
- const revokedValidatedLessons = status.records.filter((item) => item.validationLeaseStatus === "revoked-validated").length;
1225
- const renewedValidationLeases = status.records.filter((item) =>
1226
- ["agentspine.learning-validation/v2", "agentspine.learning-validation/v3",
1227
- "agentspine.learning-validation/v4", "agentspine.learning-validation/v5"].includes(item.validationLeaseSchema)).length;
1228
- const fixedCohortValidationLeases = status.records.filter((item) =>
1229
- ["agentspine.learning-validation/v3", "agentspine.learning-validation/v4",
1230
- "agentspine.learning-validation/v5"]
1231
- .includes(item.validationLeaseSchema)).length;
1232
- const admissionBoundValidationLeases = status.records.filter((item) =>
1233
- ["agentspine.learning-validation/v4", "agentspine.learning-validation/v5"]
1234
- .includes(item.validationLeaseSchema)).length;
1235
- const trialBoundValidationLeases = status.records.filter((item) =>
1236
- item.validationLeaseSchema === "agentspine.learning-validation/v5").length;
1237
- const activeRevalidations = status.records.filter((item) => item.revalidationStatus === "active").length;
1238
- const staleRevalidations = status.records.filter((item) => item.revalidationStatus === "stale").length;
1239
- const fixedCohortRevalidations = status.records.filter((item) =>
1240
- item.revalidationStatus === "active"
1241
- && ["first-completed-turns", "first-admitted-turns",
1242
- "first-admitted-trials"].includes(item.revalidationSelectionMode)).length;
1243
- const admissionBoundRevalidations = status.records.filter((item) =>
1244
- item.revalidationStatus === "active" && ["first-admitted-turns", "first-admitted-trials"]
1245
- .includes(item.revalidationSelectionMode)).length;
1246
- const trialBoundRevalidations = status.records.filter((item) =>
1247
- item.revalidationStatus === "active" && item.revalidationSelectionMode === "first-admitted-trials").length;
1248
- const requiredRevalidationDeliveries = status.records.reduce((sum, item) =>
1249
- sum + item.revalidationRequiredDeliveries, 0);
1250
- const completedRevalidationDeliveries = status.records.reduce((sum, item) =>
1251
- sum + item.revalidationCompletedDeliveries, 0);
1252
- const admittedRevalidationApplications = status.records.reduce((sum, item) =>
1253
- sum + item.revalidationAdmittedApplications, 0);
1254
- const initialTrialContracts = status.records.filter((item) => item.initialTrialMode === "first-admitted-trials").length;
1255
- const requiredInitialTrials = status.records.reduce((sum, item) => sum + item.initialTrialSlots, 0);
1256
- const admittedInitialApplications = status.records.reduce((sum, item) =>
1257
- sum + item.initialAdmittedApplications, 0);
1258
- const completedInitialDeliveries = status.records.reduce((sum, item) =>
1259
- sum + item.initialCompletedDeliveries, 0);
1260
- const incompleteInitialAdmissions = status.records.reduce((sum, item) =>
1261
- sum + item.incompleteInitialAdmissions, 0);
1262
- const targetBoundEvaluationContracts = status.records.reduce((sum, item) =>
1263
- sum + item.targetBoundEvaluationContracts, 0);
1264
- const targetBoundApplications = status.records.reduce((sum, item) =>
1265
- sum + item.targetBoundApplications, 0);
1266
- const deadlineBoundEvaluationContracts = status.records.reduce((sum, item) =>
1267
- sum + item.deadlineBoundEvaluationContracts, 0);
1268
- const stalenessBoundEvaluationContracts = status.records.reduce((sum, item) =>
1269
- sum + item.stalenessBoundEvaluationContracts, 0);
1270
- const promotionBoundEvaluationContracts = status.records.reduce((sum, item) =>
1271
- sum + item.promotionBoundEvaluationContracts, 0);
1272
- const candidateAdmissionEvaluationContracts = status.records.reduce((sum, item) =>
1273
- sum + item.candidateAdmissionEvaluationContracts, 0);
1274
- const candidateEvidenceCohortEvaluationContracts = status.records.reduce((sum, item) =>
1275
- sum + item.candidateEvidenceCohortEvaluationContracts, 0);
1276
- const blockingDefectBoundEvaluationContracts = status.records.reduce((sum, item) =>
1277
- sum + item.blockingDefectBoundEvaluationContracts, 0);
1278
- const evidenceSourceBoundEvaluationContracts = status.records.reduce((sum, item) =>
1279
- sum + item.evidenceSourceBoundEvaluationContracts, 0);
1280
- const blockingDefectOutcomeReceipts = status.records.reduce((sum, item) =>
1281
- sum + item.blockingDefectOutcomeReceipts, 0);
1282
- const deadlineBoundApplications = status.records.reduce((sum, item) =>
1283
- sum + item.deadlineBoundApplications, 0);
1284
- const trialRetryEvaluationContracts = status.records.reduce((sum, item) =>
1285
- sum + item.trialRetryEvaluationContracts, 0);
1286
- const comparableTrialRetryEvaluationContracts = status.records.reduce((sum, item) =>
1287
- sum + item.comparableTrialRetryEvaluationContracts, 0);
1288
- const boundedTrialRetryEvaluationContracts = status.records.reduce((sum, item) =>
1289
- sum + item.boundedTrialRetryEvaluationContracts, 0);
1290
- const trialRetryExhaustionReceipts = status.records.reduce((sum, item) =>
1291
- sum + item.trialRetryExhaustionReceipts, 0);
1292
- const trialFailureReceipts = status.records.reduce((sum, item) => sum + item.trialFailureReceipts, 0);
1293
- const trialFailureRevocationReceipts = status.records.reduce((sum, item) =>
1294
- sum + item.trialFailureRevocationReceipts, 0);
1295
- const evaluationRevocationReceipts = status.records.reduce((sum, item) => sum + item.evaluationRevocationReceipts, 0);
1296
- const validationRevocationReceipts = status.records.reduce((sum, item) => sum + item.validationRevocationReceipts, 0);
1297
- const evidenceRevocationReceipts = status.records.reduce((sum, item) => sum + item.evidenceRevocationReceipts, 0);
1298
- const measurementRevocationReceipts = status.records.reduce((sum, item) => sum + item.measurementRevocationReceipts, 0);
1299
- const applicationRevocationReceipts = status.records.reduce((sum, item) => sum + item.applicationRevocationReceipts, 0);
1300
- const deliveryRevocationReceipts = status.records.reduce((sum, item) => sum + item.deliveryRevocationReceipts, 0);
1301
- const outcomeRevocationReceipts = status.records.reduce((sum, item) => sum + item.outcomeRevocationReceipts, 0);
1302
- const deliveryTimeoutFailures = status.records.reduce((sum, item) => sum + item.deliveryTimeoutFailures, 0);
1303
- const outcomeTimeoutFailures = status.records.reduce((sum, item) => sum + item.outcomeTimeoutFailures, 0);
1304
- const pendingInitialOutcomes = status.records.reduce((sum, item) => sum + item.pendingInitialOutcomes, 0);
1305
- const staleInitialOutcomes = status.records.reduce((sum, item) => sum + item.staleInitialOutcomes, 0);
1306
- const unplannedOutcomeReceipts = totalOutcomeReceipts - plannedOutcomeReceipts;
1307
- learningOutcomes = {
1308
- status: status.records.some((item) => ["stale", "revoked", "revoked-evaluation", "revoked-validation", "revoked-evidence", "revoked-measurement", "revoked-application", "revoked-delivery", "revoked-outcome", "unproven", "failed-trial"].includes(item.canaryStatus))
1309
- || unboundAfterReceipts > 0 || undeliveredAfterReceipts > 0 || unplannedOutcomeReceipts > 0
1310
- || stalePendingApplications > 0 || staleUnconsumedMeasurements > 0
1311
- || inactiveEvaluatorRegistryContracts > 0 || staleRevalidations > 0
1312
- || staleInitialOutcomes > 0 || trialFailureRevocationReceipts > 0 ? "degraded" : "healthy",
1313
- candidates: status.records.length,
1314
- activeCanaries: status.records.filter((item) => item.canaryStatus === "active").length,
1315
- validatedCanaries: status.records.filter((item) => item.canaryStatus === "validated").length,
1316
- staleCanaries: status.records.filter((item) => item.canaryStatus === "stale").length,
1317
- awaitingApplication: status.records.filter((item) => item.canaryStatus === "active" && item.applicationReceipts === 0).length,
1318
- applicationReceipts: status.records.reduce((sum, item) => sum + item.applicationReceipts, 0),
1319
- deliveryReceipts: status.records.reduce((sum, item) => sum + item.deliveryReceipts, 0),
1320
- pendingApplications: status.records.reduce((sum, item) => sum + item.pendingApplications, 0),
1321
- stalePendingApplications,
1322
- evaluationContracts: status.records.reduce((sum, item) => sum + item.evaluationContracts, 0),
1323
- targetBoundEvaluationContracts,
1324
- targetBoundApplications,
1325
- deadlineBoundEvaluationContracts,
1326
- stalenessBoundEvaluationContracts,
1327
- promotionBoundEvaluationContracts,
1328
- candidateAdmissionEvaluationContracts,
1329
- candidateEvidenceCohortEvaluationContracts,
1330
- blockingDefectBoundEvaluationContracts,
1331
- evidenceSourceBoundEvaluationContracts,
1332
- blockingDefectOutcomeReceipts,
1333
- deadlineBoundApplications,
1334
- trialRetryEvaluationContracts,
1335
- comparableTrialRetryEvaluationContracts,
1336
- boundedTrialRetryEvaluationContracts,
1337
- trialRetryExhaustionReceipts,
1338
- trialFailureReceipts,
1339
- trialFailureRevocationReceipts,
1340
- evaluationRevocationReceipts,
1341
- validationRevocationReceipts,
1342
- evidenceRevocationReceipts,
1343
- measurementRevocationReceipts,
1344
- applicationRevocationReceipts,
1345
- deliveryRevocationReceipts,
1346
- outcomeRevocationReceipts,
1347
- deliveryTimeoutFailures,
1348
- outcomeTimeoutFailures,
1349
- pendingInitialOutcomes,
1350
- staleInitialOutcomes,
1351
- plannedOutcomeReceipts,
1352
- coverageBoundReceipts,
1353
- legacyCoverageReceipts,
1354
- provenanceBoundReceipts,
1355
- legacyProvenanceReceipts,
1356
- measurementReceipts,
1357
- measurementLineageReceipts,
1358
- consumedMeasurementReceipts,
1359
- staleUnconsumedMeasurements,
1360
- lineageBoundReceipts,
1361
- pairedOutcomeReceipts,
1362
- pairedEvaluatorPairs,
1363
- evaluatorRootBoundReceipts,
1364
- independentEvaluatorRoots,
1365
- activeEvaluatorRoots: status.evaluatorRegistry.active,
1366
- revokedEvaluatorRoots: status.evaluatorRegistry.revoked,
1367
- evaluatorRegistryBindings: status.evaluatorRegistry.bindings,
1368
- validationLeases: status.evaluatorRegistry.validationLeases,
1369
- renewedValidationLeases,
1370
- fixedCohortValidationLeases,
1371
- admissionBoundValidationLeases,
1372
- trialBoundValidationLeases,
1373
- initialTrialContracts,
1374
- requiredInitialTrials,
1375
- admittedInitialApplications,
1376
- completedInitialDeliveries,
1377
- incompleteInitialAdmissions,
1378
- activeRevalidations,
1379
- staleRevalidations,
1380
- fixedCohortRevalidations,
1381
- admissionBoundRevalidations,
1382
- trialBoundRevalidations,
1383
- requiredRevalidationDeliveries,
1384
- admittedRevalidationApplications,
1385
- completedRevalidationDeliveries,
1386
- currentValidatedLessons,
1387
- staleValidatedLessons,
1388
- unprovenValidatedLessons,
1389
- revokedValidatedLessons,
1390
- evaluatorRegistryContracts,
1391
- inactiveEvaluatorRegistryContracts,
1392
- unplannedOutcomeReceipts,
1393
- boundAfterReceipts: status.records.reduce((sum, item) => sum + item.boundAfterReceipts, 0),
1394
- unboundAfterReceipts,
1395
- deliveredAfterReceipts: status.records.reduce((sum, item) => sum + item.deliveredAfterReceipts, 0),
1396
- undeliveredAfterReceipts,
1397
- contradictions: status.records.filter((item) => item.conflictsWith.length > 0).length,
1398
- authority: "context-only"
1399
- };
1400
- } catch (error) { learningOutcomes = { status: "failed-closed", error: error.message, authority: "context-only" }; }
1401
- const result = {
1402
- ok: Number(process.versions.node.split(".")[0]) >= 20 && hostIntegration.ok
1403
- && (!sourceResolution || sourceResolution.status === "loaded")
1404
- && learningOutcomes.status !== "failed-closed",
1405
- version: VERSION,
1406
- node: process.versions.node,
1407
- platform: process.platform,
1408
- preservationMode: "read-only-source-overlay",
1409
- stateDirectory: process.env.AGENTSPINE_STATE_DIR || "platform-default",
1410
- hostIntegration,
1411
- sourceResolution,
1412
- orphanScan,
1413
- preflight,
1414
- learningOutcomes
1415
- };
1416
- output(result, json);
1417
- if (!result.ok) process.exitCode = 1;
1418
- return;
1419
- }
1420
-
1421
- if (command === "audit") {
1422
- const result = await runAudit(positional[0] || process.cwd(), { host: flags.host || process.env.AGENTSPINE_HOST || null });
1423
- output(result, json);
1424
- if (!result.ok) process.exitCode = 1;
1425
- return;
1426
- }
1427
-
1428
- if (command === "mcp") {
1429
- const { startMcpServer } = await import("./mcp.js");
1430
- startMcpServer();
1431
- return;
1432
- }
1433
-
1434
- throw new Error(`Unknown command: ${command}`);
1435
- }
1436
-
1437
- if (isMainModule(import.meta.url)) {
1438
- run().catch((error) => {
1439
- process.stderr.write(`AgentSpine: ${error.message}\n`);
1440
- process.exitCode = 1;
1441
- });
1442
- }
1
+ import { fileURLToPath } from "node:url";
2
+ import { scanAndSave, verifyCatalog } from "./lib/catalog.js";
3
+ import { readDocument, resolveContext } from "./lib/context.js";
4
+ import { sessionBriefing } from "./lib/briefing.js";
5
+ import { runAudit } from "./lib/audit.js";
6
+ import {
7
+ attentionContext, configureAttention, deleteAttention, loadAttention,
8
+ recordActivity, resolveAttention, upsertAttention
9
+ } from "./lib/attention.js";
10
+ import {
11
+ addLearningEvidence, beginLearningRevalidation, configureLearning, deleteLearning, evaluateLearning,
12
+ learningContext, learningOutcomeStatus, loadLearning, proposeLearning,
13
+ purgeStaleLearningApplications, purgeStaleLearningMeasurements, recordLearningMeasurement,
14
+ recordLearningOutcome, registerLearningEvaluation, registerLearningEvaluator, renewLearningValidation, revokeLearningEvaluator,
15
+ reviewLearning, revokeLearningApplication, revokeLearningDelivery, revokeLearningEvaluation, revokeLearningEvidence, revokeLearningMeasurement, revokeLearningOutcome, revokeLearningTrialFailure, revokeLearningValidation,
16
+ revokeLearningEvidenceSourceAttestation,
17
+ rollbackLearning
18
+ } from "./lib/learning.js";
19
+ import { configureContinuity, loadContinuity, purgeContinuity } from "./lib/continuity.js";
20
+ import {
21
+ checkDelegation, createTask, deleteTask, grantDelegation, loadDelegationPolicy,
22
+ revokeDelegation, taskContext, updateTask
23
+ } from "./lib/coordination.js";
24
+ import {
25
+ cancelJob, deleteJob, grantExecution, loadExecutionPolicy, registerJob,
26
+ revokeExecution, selfstarterContext
27
+ } from "./lib/selfstarter.js";
28
+ import {
29
+ channelRuntimeContext, grantChannelBinding, loadChannelPolicy, revokeChannelBinding
30
+ } from "./lib/channel-runtime.js";
31
+ import { personaContext, syncPersonaRosterFromEnvironment } from "./lib/persona-runtime.js";
32
+ import { assignGoal, gatewayContext, resolveGoalKnowledgeGap, setGatewayControl } from "./lib/gateway-runtime.js";
33
+ import {
34
+ configureSharing, deleteShared, initDirectoryAdapter, publishLearning, pullShared,
35
+ reviewShared, rollbackShared, sharedContext, sharedInbox
36
+ } from "./lib/sharing.js";
37
+ import {
38
+ generateSigningIdentity, listSigningIdentities, revokeTrustedSigner,
39
+ trustedSignerContext, trustSigner
40
+ } from "./lib/authentication.js";
41
+ import { exportHttpsSnapshot, pullHttpsSnapshot } from "./lib/https-transport.js";
42
+ import { publishHttpsSnapshot } from "./lib/object-transport.js";
43
+ import { loadHttpsFeedState, publishHttpsFeed, pullHttpsFeed } from "./lib/feed-transport.js";
44
+ import { pullPeerCommand, servePeerOnce } from "./lib/peer-transport.js";
45
+ import {
46
+ initSqliteAdapter, inspectSqliteAdapter, publishSqliteSnapshot, pullSqliteSnapshot
47
+ } from "./lib/sqlite-transport.js";
48
+ import {
49
+ annotateDocument, linkDocuments, linkEntities,
50
+ relationshipContext, upsertEntity
51
+ } from "./lib/graph.js";
52
+ import { checkHosts } from "../scripts/check-hosts.js";
53
+ import { renderAcceptanceReport, runVisibleAcceptance } from "./lib/acceptance.js";
54
+ import {
55
+ bindSourceRoot, inspectSourceRegistry, purgeSourceBinding, resolveHostSourceCatalog,
56
+ rollbackSourceBinding
57
+ } from "./lib/source-roots.js";
58
+ import { VERSION } from "./version.js";
59
+ import { isMainModule } from "./lib/runtime.js";
60
+ import { scanIndexedMemoryOrphans } from "./lib/indexed-memory-offline.js";
61
+ import {
62
+ configurePreflightPolicy, confirmMustRemember, preflightStatus, proposeMustRemember,
63
+ purgeMustRemember, rollbackMustRemember
64
+ } from "./lib/preflight.js";
65
+ import { readFile } from "node:fs/promises";
66
+
67
+ function parse(argv) {
68
+ const [command = "help", ...rest] = argv;
69
+ const flags = {};
70
+ const positional = [];
71
+ for (let index = 0; index < rest.length; index += 1) {
72
+ const token = rest[index];
73
+ if (!token.startsWith("--")) {
74
+ positional.push(token);
75
+ continue;
76
+ }
77
+ const [name, inline] = token.slice(2).split("=", 2);
78
+ if (inline !== undefined) flags[name] = inline;
79
+ else if (rest[index + 1] && !rest[index + 1].startsWith("--")) flags[name] = rest[++index];
80
+ else flags[name] = true;
81
+ }
82
+ return { command, flags, positional };
83
+ }
84
+
85
+ function output(value, json = false) {
86
+ if (json) {
87
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
88
+ return;
89
+ }
90
+ if (typeof value === "string") process.stdout.write(`${value}\n`);
91
+ else process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
92
+ }
93
+
94
+ function booleanFlag(value, fallback = false) {
95
+ if (value === undefined) return fallback;
96
+ if (value === true || value === "true" || value === "1") return true;
97
+ if (value === false || value === "false" || value === "0") return false;
98
+ throw new Error(`expected boolean flag, received: ${value}`);
99
+ }
100
+
101
+ async function goalPlanFlag(path) {
102
+ if (!path) return null;
103
+ const content = await readFile(path, "utf8");
104
+ if (Buffer.byteLength(content) > 64 * 1024) throw new Error("goal plan exceeds 64 KiB");
105
+ const parsed = JSON.parse(content);
106
+ const steps = Array.isArray(parsed) ? parsed : parsed?.steps;
107
+ if (!Array.isArray(steps)) throw new Error("goal plan must be a JSON array or an object with a steps array");
108
+ return steps;
109
+ }
110
+
111
+ function learningScope(flags) {
112
+ return {
113
+ personaId: flags.persona || null,
114
+ userId: flags.user || null,
115
+ tenantId: flags.tenant || null,
116
+ projectId: flags.project || null,
117
+ groupId: flags.group || null,
118
+ taskId: flags.task || null
119
+ };
120
+ }
121
+
122
+ function hasLearningScope(flags) {
123
+ return ["persona", "user", "tenant", "project", "group", "task"].some((name) => flags[name] !== undefined);
124
+ }
125
+
126
+ function evaluatorRootsFlag(value) {
127
+ return String(value || "").split(",").filter(Boolean).map((entry) => {
128
+ const separator = entry.lastIndexOf("=");
129
+ if (separator <= 0) throw new Error("evaluator roots must use evaluator-id=sha256 entries");
130
+ return { evaluatorId: entry.slice(0, separator), principalDigest: entry.slice(separator + 1) };
131
+ });
132
+ }
133
+
134
+ function help() {
135
+ return `AgentSpine ${VERSION}
136
+
137
+ Usage:
138
+ agentspine scan [root] [--json]
139
+ agentspine context [root] [--cwd path] [--host codex|claude|generic] [--max-bytes n] [--json]
140
+ agentspine briefing [root] [--host codex|claude|generic] [--entity id] [--group id] [--project id] [--current-task id] [--include-private] [--max-bytes n] [--allow-attention] [--no-source-content]
141
+ agentspine read <relative-path> [--root path] [--offset n] [--length n] [--json]
142
+ agentspine verify [root] [--json]
143
+ agentspine link <from.md> <to.md> --relation related [--reason text] [--confidence 0.8]
144
+ agentspine annotate <path.md> --layer soul [--reason text] [--confidence 0.8]
145
+ agentspine entity <id> --kind person [--name text] [--privacy private]
146
+ agentspine relate <from> <to> --relation works-with [--privacy private]
147
+ agentspine relationships <entity-id> [--group id] [--include-private] [--json]
148
+ agentspine attention [root] [--group id] [--include-private] [--focus-active] [--mark-presented]
149
+ agentspine attention-events [root] [--include-history]
150
+ agentspine attention-add [id] --kind promise --summary text [--entity id] [--group id] [--due date]
151
+ agentspine attention-resolve <id> [--status completed|dismissed|open]
152
+ agentspine attention-touch <entity-id> [--kind interaction] [--at date]
153
+ agentspine attention-delete <signal-id>
154
+ agentspine attention-event-delete <event-id>
155
+ agentspine attention-purge <entity-id>
156
+ agentspine attention-config [root] [--enabled true|false] [--quiet-start 22 --quiet-end 7 --utc-offset 120]
157
+ agentspine learn-propose [id] --kind preference|behavior --claim text --evidence text [--persona id --user id --tenant id --project id --group id --task id]
158
+ agentspine learn-evidence <id> --summary text [--type interaction] [--source path.md]
159
+ agentspine learn-evidence-revoke <learning-id> --evidence-id id --reason-code retracted|source-invalid|measurement-invalid|duplicate|other --reason text --confirm-local-evidence
160
+ agentspine learn-review <id> --decision accept|reject --reason text [--confirmed-by-user]
161
+ agentspine learn-context [root] [--group id] [--include-private] [--kind preference,goal]
162
+ agentspine learn-evaluate [root]
163
+ agentspine learn-evaluator-register <id> --principal-digest sha256 --confirm-local-evaluator
164
+ agentspine learn-evaluator-revoke <id> --reason text --confirm-local-evaluator
165
+ agentspine learn-evaluation <id> --learning id --metric name --direction higher|lower --task-digest sha256 --dataset-digest sha256 --protocol-digest sha256 --min-cases n --evaluators id,id --evaluator-roots id=sha256,id=sha256 [--expires-at date] [--retry-trial-failure id --confirm-local-trial-retry] --confirm-local-evaluation --confirm-local-evidence-sources
166
+ agentspine learn-evaluation-revoke <evaluation-id> --reason-code benchmark-invalid|protocol-invalid|scope-invalid|threshold-invalid|duplicate|other --reason text --confirm-local-evaluation-revocation
167
+ agentspine learn-evidence-source-attestation-revoke <evaluation-id> --evidence-digest sha256 --reason-code source-class-invalid|confirmation-invalid|scope-invalid|duplicate|other --reason text --confirm-local-evidence-source-attestation-revocation
168
+ agentspine learn-validation-revoke <validation-lease-id> --reason-code decision-invalid|cohort-invalid|binding-invalid|scope-invalid|duplicate|other --reason text --confirm-local-validation-revocation
169
+ agentspine learn-trial-failure-revoke <trial-failure-id> --reason-code clock-invalid|host-invalid|receipt-invalid|scope-invalid|duplicate|other --reason text --confirm-local-trial-failure-revocation
170
+ agentspine learn-revalidation-start <learning-id> --confirm-local-validation
171
+ agentspine learn-revalidate <learning-id> --measurements id,id --applications id,id --deliveries id,id --confirm-local-validation
172
+ agentspine learn-measurement <id> --learning id --evaluation id --phase before|after --metric name --direction higher|lower --value 0..1 --measurement objective|user-feedback|model-suggestion --evaluator id --run id --source-digest sha256 --dataset-digest sha256 --case-count n --confirm-local-measurement
173
+ agentspine learn-measurement-revoke <measurement-id> --reason-code source-invalid|evaluator-invalid|protocol-invalid|duplicate|other --reason text --confirm-local-measurement-revocation
174
+ agentspine learn-application-revoke <application-id> --reason-code preflight-invalid|scope-invalid|projection-invalid|duplicate|other --reason text --confirm-local-application-revocation
175
+ agentspine learn-delivery-revoke <delivery-id> --reason-code host-invalid|session-invalid|hook-invalid|duplicate|other --reason text --confirm-local-delivery-revocation
176
+ agentspine learn-outcome-revoke <outcome-id> --reason-code binding-invalid|phase-invalid|scope-invalid|duplicate|other --reason text --confirm-local-outcome-revocation
177
+ agentspine learn-outcome <learning-id> --id id --evaluation id --measurement-receipt id [--application id --delivery id]
178
+ agentspine learn-status [root] [--persona id --user id --tenant id --project id --group id --task id]
179
+ agentspine learn-delivery-purge [root] --confirm-local-purge
180
+ agentspine learn-measurement-purge [root] --confirm-local-purge
181
+ agentspine learn-rollback <id> --reason text
182
+ agentspine learn-delete <id>
183
+ agentspine learn-config [root] [--auto-promote true|false] [--min-confidence 0.85] [--min-outcomes 2 --min-improvement 0.05 --canary-receipts 2 --canary-ttl-days 14]
184
+ agentspine continuity-config [root] [--enabled true|false] [--entity id] [--project id] [--confirm-local-opt-in]
185
+ agentspine continuity-status [root]
186
+ agentspine continuity-purge <entity-id> [--root path] --confirm-local-purge
187
+ agentspine preflight-policy <policy.json> --confirm-local-policy
188
+ agentspine preflight-status
189
+ agentspine remember-propose --claim text --user id --tenant id [--project id] [--group id] [--task id]
190
+ agentspine remember-confirm <candidate-id> [--supersedes id] --confirm-local-user
191
+ agentspine remember-rollback <id> --confirm-local-user
192
+ agentspine remember-purge <id> --confirm-local-purge
193
+ agentspine source-status --host claude|codex [--cwd path]
194
+ agentspine doctor --host claude [--cwd path] [--offline-memory-orphans]
195
+ agentspine source-bind <state-root> --host all|claude|codex --scope state-user --project path --host-home path --confirm-local-binding
196
+ agentspine source-rollback <binding-id> --confirm-local-binding
197
+ agentspine source-purge <binding-id> --confirm-local-binding
198
+ agentspine delegation-grant <actor-id> --actions assign,manage --targets agent:id --reason text [--confirm-local-policy]
199
+ agentspine delegation-revoke <grant-id> --reason text [--confirm-local-policy]
200
+ agentspine delegation-check <actor-id> --action assign [--target agent:id]
201
+ agentspine delegation-policy [root]
202
+ agentspine task-create [id] --actor id --title text [--assignee id] [--kind task|open-thread|handoff] [--privacy private|shared|group]
203
+ agentspine task-update <id> --actor id [--status in-progress] [--assignee id|--unassign]
204
+ agentspine tasks [root] [--assignee id] [--project id] [--include-private] [--group id]
205
+ agentspine task-delete <id> [--confirm-local-policy]
206
+ agentspine execution-grant <job-id> --actor id --task task:id --target id --project project:id --host claude|codex --capabilities tool:Write --reason text [--expires date] --confirm-local-execution
207
+ agentspine execution-revoke <grant-id> --reason text --confirm-local-execution
208
+ agentspine execution-policy [root]
209
+ agentspine job-register <job-id> --grant grant:id [--max-retries 3] [--lease-seconds 120] --confirm-local-execution
210
+ agentspine jobs [root] [--actor id] [--project id] [--task id] [--include-terminal]
211
+ agentspine job-cancel <job-id> --reason text --confirm-local-execution
212
+ agentspine job-delete <job-id> --confirm-local-execution
213
+ agentspine channel-bind <binding-id> --provider telegram --tenant id --account id --chat id --senders id,id --agent agent:id --project project:id --session key --secret-env VARIABLE [--outbound-secret-env VARIABLE] --confirm-local-channel
214
+ agentspine channel-revoke <binding-id> --reason text --confirm-local-channel
215
+ agentspine channel-policy [root]
216
+ agentspine channel-events [root] [--agent id] [--project id] [--group id] [--provider telegram] [--include-terminal]
217
+ agentspine persona-sync [root] --roster absolute-path --confirm-local-persona
218
+ agentspine personas [root] [--persona id] [--group id] [--include-inactive]
219
+ agentspine goal-assign <goal-id> --agent id --owner id --project id --success text (--next-step text | --plan plan.json) [--group id] [--deadline date] --confirm-local-goal
220
+ agentspine goal-clarify <goal-id> --gap id --answer text [--source owner-input|objective-observation] [--source-digest sha256] --confirm-local-goal
221
+ agentspine gateway-control [root] [--enabled true|false] [--kill-switch true|false] --confirm-local-gateway
222
+ agentspine gateway-status [root] [--agent id]
223
+ agentspine share-keygen <signer-id> [--public-out signer.json] [--rotate] [--confirm-local-share]
224
+ agentspine share-signers [root]
225
+ agentspine share-trust <signer.json> [--root path] [--confirm-local-share]
226
+ agentspine share-trust-revoke <key-id> --reason text [--root path] [--confirm-local-share]
227
+ agentspine share-trust-list [root] [--include-revoked]
228
+ agentspine share-init <directory> --scope team:id [--adapter adapter:id] [--signer signer:id] [--confirm-local-share]
229
+ agentspine share-publish <directory> --learning id [--id shared:id] [--signer signer:id] [--supersedes shared:id] [--confirm-local-share]
230
+ agentspine share-pull <directory> [--root path] [--require-authenticated]
231
+ agentspine share-snapshot-export <directory> --out snapshot.json [--id snapshot:id] [--confirm-local-share]
232
+ agentspine share-https-publish <directory> --base https://store.example/spine [--id snapshot:id] [--token-env VARIABLE] [--timeout-ms 10000] [--allow-private-network] --confirm-local-share
233
+ agentspine share-https-pull <https-url> [--token-env VARIABLE] [--timeout-ms 10000] [--allow-private-network --confirm-local-share]
234
+ agentspine share-feed-publish <directory> --base https://store.example/spine --feed team:id --signer signer:id [--id snapshot:id] [--token-env VARIABLE] --confirm-local-share
235
+ agentspine share-feed-pull --base https://store.example/spine --feed team:id [--root path] [--token-env VARIABLE] [--allow-private-network --confirm-local-share]
236
+ agentspine share-feed-state [root]
237
+ agentspine share-peer-serve <directory> --root path --signer signer:id [--timeout-ms 10000] --confirm-local-share
238
+ agentspine share-peer-pull --root path --command-json '["ssh","host","agentspine",…]' [--timeout-ms 10000] [--max-bytes n] --confirm-local-share
239
+ agentspine share-sqlite-init <directory> --database path --confirm-local-share
240
+ agentspine share-sqlite-publish <directory> --database path [--id snapshot:id] --confirm-local-share
241
+ agentspine share-sqlite-inspect --database path [--root path]
242
+ agentspine share-sqlite-pull --database path [--root path]
243
+ agentspine share-inbox [root] [--status pending|accepted|rejected|superseded|rolled-back]
244
+ agentspine share-review <id> --decision accept|reject --reason text [--confirmed-by-user]
245
+ agentspine share-context [root] [--scope team:id] [--group group:id] [--kind preference,goal]
246
+ agentspine share-rollback <id> --reason text
247
+ agentspine share-delete <id> [--confirm-local-share]
248
+ agentspine share-config [root] --max-items 12
249
+ agentspine audit [root] [--json]
250
+ agentspine acceptance [--json]
251
+ agentspine doctor [--json]
252
+ agentspine mcp
253
+
254
+ AgentSpine reads existing Markdown in place. It never rewrites source documents.`;
255
+ }
256
+
257
+ export async function run(argv = process.argv.slice(2)) {
258
+ const { command, flags, positional } = parse(argv);
259
+ const json = Boolean(flags.json);
260
+ if (command === "help" || command === "--help" || command === "-h") return output(help());
261
+ if (command === "version" || command === "--version" || command === "-v") return output(VERSION);
262
+
263
+ if (command === "acceptance") {
264
+ if (positional.length || Object.keys(flags).some((name) => name !== "json")) {
265
+ throw new Error("acceptance supports only --json");
266
+ }
267
+ const report = await runVisibleAcceptance();
268
+ if (json) return output(report, true);
269
+ process.stdout.write(renderAcceptanceReport(report));
270
+ return;
271
+ }
272
+
273
+ if (command === "scan") {
274
+ const result = await scanAndSave(positional[0] || process.cwd());
275
+ return output(json ? result : {
276
+ root: result.catalog.root,
277
+ catalog: result.catalogPath,
278
+ documents: result.catalog.summary.total,
279
+ protected: result.catalog.summary.protected,
280
+ layers: result.catalog.summary.byLayer
281
+ }, json);
282
+ }
283
+
284
+ if (command === "context") {
285
+ const root = positional[0] || process.cwd();
286
+ const result = await resolveContext({
287
+ root,
288
+ cwd: flags.cwd || root,
289
+ host: flags.host || "generic",
290
+ maxBytes: Number(flags["max-bytes"] || 65536),
291
+ includeContent: flags["no-content"] !== true
292
+ });
293
+ return output(result, json);
294
+ }
295
+
296
+ if (command === "briefing") {
297
+ const root = positional[0] || process.cwd();
298
+ return output(await sessionBriefing({
299
+ root, cwd: flags.cwd || root, host: flags.host || "generic",
300
+ entityId: flags.entity || null, groupId: flags.group || null,
301
+ userId: flags.user || null, tenantId: flags.tenant || null,
302
+ projectId: flags.project || null, currentTaskId: flags["current-task"] || null,
303
+ includePrivate: booleanFlag(flags["include-private"]),
304
+ focusActive: !booleanFlag(flags["allow-attention"]),
305
+ includeSourceContent: !booleanFlag(flags["no-source-content"]),
306
+ maxBytes: Number(flags["max-bytes"] ?? 16384)
307
+ }), json);
308
+ }
309
+
310
+ if (command === "read") {
311
+ const result = await readDocument({
312
+ root: flags.root || process.cwd(),
313
+ path: positional[0],
314
+ offset: Number(flags.offset || 0),
315
+ length: Number(flags.length || 65536)
316
+ });
317
+ return output(json ? result : result.content, json);
318
+ }
319
+
320
+ if (command === "verify") {
321
+ const result = await verifyCatalog(positional[0] || process.cwd());
322
+ output(result, json);
323
+ if (!result.ok) process.exitCode = 1;
324
+ return;
325
+ }
326
+
327
+ if (command === "link") {
328
+ const result = await linkDocuments({
329
+ root: flags.root || process.cwd(), from: positional[0], to: positional[1],
330
+ relation: flags.relation || "related", reason: flags.reason || "",
331
+ confidence: Number(flags.confidence ?? 0.5)
332
+ });
333
+ return output(result, json);
334
+ }
335
+
336
+ if (command === "annotate") {
337
+ const result = await annotateDocument({
338
+ root: flags.root || process.cwd(), path: positional[0], layer: flags.layer,
339
+ reason: flags.reason || "", confidence: Number(flags.confidence ?? 0.5)
340
+ });
341
+ return output(result, json);
342
+ }
343
+
344
+ if (command === "entity") {
345
+ const result = await upsertEntity({
346
+ root: flags.root || process.cwd(), id: positional[0], kind: flags.kind,
347
+ displayName: flags.name || "", confidence: Number(flags.confidence ?? 0.5),
348
+ privacy: flags.privacy || "private"
349
+ });
350
+ return output(result, json);
351
+ }
352
+
353
+ if (command === "relate") {
354
+ const result = await linkEntities({
355
+ root: flags.root || process.cwd(), from: positional[0], to: positional[1],
356
+ relation: flags.relation || "related", reason: flags.reason || "",
357
+ confidence: Number(flags.confidence ?? 0.5), privacy: flags.privacy || "private"
358
+ });
359
+ return output(result, json);
360
+ }
361
+
362
+ if (command === "relationships") {
363
+ return output(await relationshipContext({
364
+ root: flags.root || process.cwd(), entityId: positional[0],
365
+ includePrivate: Boolean(flags["include-private"]), groupId: flags.group || null
366
+ }), json);
367
+ }
368
+
369
+ if (command === "attention") {
370
+ return output(await attentionContext({
371
+ root: positional[0] || process.cwd(),
372
+ includePrivate: booleanFlag(flags["include-private"]),
373
+ focusActive: booleanFlag(flags["focus-active"]),
374
+ markPresented: booleanFlag(flags["mark-presented"]),
375
+ entityId: flags.entity || null,
376
+ groupId: flags.group || null,
377
+ projectId: flags.project || null,
378
+ currentTaskId: flags.task || null,
379
+ maxItems: flags["max-items"] === undefined ? null : Number(flags["max-items"])
380
+ }), json);
381
+ }
382
+
383
+ if (command === "attention-events") {
384
+ const { attention, attentionPath } = await loadAttention(positional[0] || process.cwd());
385
+ return output({
386
+ schema: "agentspine.attention-events/v1", attentionPath,
387
+ events: attention.events,
388
+ receipts: attention.receipts,
389
+ history: booleanFlag(flags["include-history"]) ? attention.history.filter((item) => item.kind === "attention-event") : [],
390
+ authority: "context-only"
391
+ }, json);
392
+ }
393
+
394
+ if (command === "attention-add") {
395
+ return output(await upsertAttention({
396
+ root: flags.root || process.cwd(), id: positional[0], kind: flags.kind,
397
+ summary: flags.summary, entityId: flags.entity || null, dueAt: flags.due || null,
398
+ priority: Number(flags.priority ?? 50), privacy: flags.privacy || "private",
399
+ groupId: flags.group || null,
400
+ sourceDocument: flags.source || null, confidence: Number(flags.confidence ?? 0.5)
401
+ }), json);
402
+ }
403
+
404
+ if (command === "attention-resolve") {
405
+ return output(await resolveAttention({
406
+ root: flags.root || process.cwd(), id: positional[0], status: flags.status || "completed"
407
+ }), json);
408
+ }
409
+
410
+ if (command === "attention-touch") {
411
+ return output(await recordActivity({
412
+ root: flags.root || process.cwd(), entityId: positional[0], kind: flags.kind || "interaction",
413
+ at: flags.at || new Date(), privacy: flags.privacy || "private", groupId: flags.group || null
414
+ }), json);
415
+ }
416
+
417
+ if (command === "attention-delete") {
418
+ return output(await deleteAttention({ root: flags.root || process.cwd(), signalId: positional[0] }), json);
419
+ }
420
+
421
+ if (command === "attention-event-delete") {
422
+ return output(await deleteAttention({ root: flags.root || process.cwd(), eventId: positional[0] }), json);
423
+ }
424
+
425
+ if (command === "attention-purge") {
426
+ return output(await deleteAttention({ root: flags.root || process.cwd(), entityId: positional[0] }), json);
427
+ }
428
+
429
+ if (command === "attention-config") {
430
+ const root = positional[0] || process.cwd();
431
+ const config = {};
432
+ if (flags.enabled !== undefined) config.enabled = booleanFlag(flags.enabled);
433
+ if (flags["min-interval-hours"] !== undefined) config.minIntervalHours = Number(flags["min-interval-hours"]);
434
+ if (flags["silence-days"] !== undefined) config.entitySilenceDays = Number(flags["silence-days"]);
435
+ if (flags["heartbeat-stale-minutes"] !== undefined) config.heartbeatStaleMinutes = Number(flags["heartbeat-stale-minutes"]);
436
+ if (flags["max-items"] !== undefined) config.maxItems = Number(flags["max-items"]);
437
+ if (flags["quiet-off"] !== undefined) config.quietHours = null;
438
+ else if (flags["quiet-start"] !== undefined || flags["quiet-end"] !== undefined) {
439
+ config.quietHours = {
440
+ start: Number(flags["quiet-start"]), end: Number(flags["quiet-end"]),
441
+ utcOffsetMinutes: Number(flags["utc-offset"] ?? 0)
442
+ };
443
+ }
444
+ if (!Object.keys(config).length) return output((await loadAttention(root)).attention.config, json);
445
+ return output(await configureAttention({ root, config }), json);
446
+ }
447
+
448
+ if (command === "learn-propose") {
449
+ return output(await proposeLearning({
450
+ root: flags.root || process.cwd(), id: positional[0], kind: flags.kind,
451
+ claim: flags.claim, subjectId: flags.subject || null, privacy: flags.privacy || "private",
452
+ groupId: flags.group || null, supersedesId: flags.supersedes || null,
453
+ scope: hasLearningScope(flags) ? learningScope(flags) : null,
454
+ evidence: {
455
+ id: flags["evidence-id"], type: flags["evidence-type"] || "user-statement",
456
+ summary: flags.evidence, sourceDocument: flags.source || null,
457
+ confidence: Number(flags.confidence ?? 0.5), observedAt: flags.at
458
+ }
459
+ }), json);
460
+ }
461
+
462
+ if (command === "learn-evidence") {
463
+ return output(await addLearningEvidence({
464
+ root: flags.root || process.cwd(), id: positional[0],
465
+ evidence: {
466
+ id: flags["evidence-id"], type: flags.type || "interaction", summary: flags.summary,
467
+ sourceDocument: flags.source || null, confidence: Number(flags.confidence ?? 0.5), observedAt: flags.at
468
+ }
469
+ }), json);
470
+ }
471
+
472
+ if (command === "learn-evidence-revoke") {
473
+ return output(await revokeLearningEvidence({
474
+ root: flags.root || process.cwd(), learningId: positional[0], evidenceId: flags["evidence-id"],
475
+ reasonCode: flags["reason-code"], reason: flags.reason,
476
+ confirmation: booleanFlag(flags["confirm-local-evidence"])
477
+ ? "local-evidence-revocation-confirmed" : null
478
+ }), json);
479
+ }
480
+
481
+ if (command === "learn-review") {
482
+ return output(await reviewLearning({
483
+ root: flags.root || process.cwd(), id: positional[0], decision: flags.decision,
484
+ reason: flags.reason, confirmedByUser: booleanFlag(flags["confirmed-by-user"])
485
+ }), json);
486
+ }
487
+
488
+ if (command === "learn-context") {
489
+ return output(await learningContext({
490
+ root: positional[0] || process.cwd(), includePrivate: booleanFlag(flags["include-private"]),
491
+ groupId: flags.group || null,
492
+ scope: hasLearningScope(flags) ? learningScope(flags) : null,
493
+ kinds: flags.kind ? String(flags.kind).split(",").filter(Boolean) : null,
494
+ subjectIds: flags.subject ? String(flags.subject).split(",").filter(Boolean) : null,
495
+ maxItems: flags["max-items"] === undefined ? null : Number(flags["max-items"])
496
+ }), json);
497
+ }
498
+
499
+ if (command === "learn-evaluate") {
500
+ return output(await evaluateLearning({ root: positional[0] || process.cwd() }), json);
501
+ }
502
+
503
+ if (command === "learn-evaluator-register") {
504
+ return output(await registerLearningEvaluator({
505
+ root: flags.root || process.cwd(), id: positional[0], principalDigest: flags["principal-digest"],
506
+ confirmLocalEvaluator: booleanFlag(flags["confirm-local-evaluator"])
507
+ }), json);
508
+ }
509
+
510
+ if (command === "learn-evaluator-revoke") {
511
+ return output(await revokeLearningEvaluator({
512
+ root: flags.root || process.cwd(), id: positional[0], reason: flags.reason,
513
+ confirmLocalEvaluator: booleanFlag(flags["confirm-local-evaluator"])
514
+ }), json);
515
+ }
516
+
517
+ if (command === "learn-evaluation") {
518
+ return output(await registerLearningEvaluation({
519
+ root: flags.root || process.cwd(), id: positional[0], learningId: flags.learning,
520
+ scope: learningScope(flags), metric: { name: flags.metric, direction: flags.direction },
521
+ benchmark: {
522
+ taskDigest: flags["task-digest"], datasetDigest: flags["dataset-digest"],
523
+ protocolDigest: flags["protocol-digest"], minCases: Number(flags["min-cases"])
524
+ },
525
+ evaluatorIds: String(flags.evaluators || "").split(",").filter(Boolean),
526
+ evaluatorRoots: evaluatorRootsFlag(flags["evaluator-roots"]),
527
+ expiresAt: flags["expires-at"] || null,
528
+ retryTrialFailureId: flags["retry-trial-failure"] || null,
529
+ confirmLocalTrialRetry: booleanFlag(flags["confirm-local-trial-retry"]),
530
+ confirmLocalEvidenceSources: booleanFlag(flags["confirm-local-evidence-sources"]),
531
+ confirmLocalEvaluation: booleanFlag(flags["confirm-local-evaluation"])
532
+ }), json);
533
+ }
534
+
535
+ if (command === "learn-revalidation-start") {
536
+ return output(await beginLearningRevalidation({
537
+ root: flags.root || process.cwd(), learningId: positional[0],
538
+ confirmLocalValidation: booleanFlag(flags["confirm-local-validation"])
539
+ }), json);
540
+ }
541
+
542
+ if (command === "learn-revalidate") {
543
+ const measurements = String(flags.measurements || "").split(",").filter(Boolean);
544
+ const applications = String(flags.applications || "").split(",").filter(Boolean);
545
+ const deliveries = String(flags.deliveries || "").split(",").filter(Boolean);
546
+ if (!measurements.length || measurements.length !== applications.length || measurements.length !== deliveries.length) {
547
+ throw new Error("revalidation requires equally sized measurement, application, and delivery lists");
548
+ }
549
+ return output(await renewLearningValidation({
550
+ root: flags.root || process.cwd(), learningId: positional[0],
551
+ evidence: measurements.map((measurementId, index) => ({ measurementId,
552
+ applicationId: applications[index], deliveryId: deliveries[index] })),
553
+ confirmLocalValidation: booleanFlag(flags["confirm-local-validation"])
554
+ }), json);
555
+ }
556
+
557
+ if (command === "learn-measurement") {
558
+ return output(await recordLearningMeasurement({
559
+ root: flags.root || process.cwd(), id: positional[0], learningId: flags.learning,
560
+ evaluationId: flags.evaluation, phase: flags.phase, scope: learningScope(flags),
561
+ metric: {
562
+ name: flags.metric, direction: flags.direction, value: Number(flags.value),
563
+ blockingDefects: Number(flags["blocking-defects"] ?? 0)
564
+ },
565
+ measurement: {
566
+ kind: flags.measurement || "objective", evaluatorId: flags.evaluator,
567
+ runId: flags.run, sourceDigest: flags["source-digest"]
568
+ },
569
+ coverage: { datasetDigest: flags["dataset-digest"], caseCount: Number(flags["case-count"]) },
570
+ measuredAt: flags.at,
571
+ confirmLocalMeasurement: booleanFlag(flags["confirm-local-measurement"])
572
+ }), json);
573
+ }
574
+
575
+ if (command === "learn-measurement-revoke") {
576
+ return output(await revokeLearningMeasurement({
577
+ root: flags.root || process.cwd(), measurementId: positional[0], reasonCode: flags["reason-code"],
578
+ reason: flags.reason,
579
+ confirmation: booleanFlag(flags["confirm-local-measurement-revocation"])
580
+ ? "local-measurement-revocation-confirmed" : null
581
+ }), json);
582
+ }
583
+
584
+ if (command === "learn-evaluation-revoke") {
585
+ return output(await revokeLearningEvaluation({
586
+ root: flags.root || process.cwd(), evaluationId: positional[0], reasonCode: flags["reason-code"],
587
+ reason: flags.reason,
588
+ confirmation: booleanFlag(flags["confirm-local-evaluation-revocation"])
589
+ ? "local-evaluation-revocation-confirmed" : null
590
+ }), json);
591
+ }
592
+
593
+ if (command === "learn-evidence-source-attestation-revoke") {
594
+ return output(await revokeLearningEvidenceSourceAttestation({
595
+ root: flags.root || process.cwd(), evaluationId: positional[0], evidenceDigest: flags["evidence-digest"],
596
+ reasonCode: flags["reason-code"], reason: flags.reason,
597
+ confirmation: booleanFlag(flags["confirm-local-evidence-source-attestation-revocation"])
598
+ ? "local-evidence-source-attestation-revocation-confirmed" : null
599
+ }), json);
600
+ }
601
+
602
+ if (command === "learn-validation-revoke") {
603
+ return output(await revokeLearningValidation({
604
+ root: flags.root || process.cwd(), validationLeaseId: positional[0], reasonCode: flags["reason-code"],
605
+ reason: flags.reason,
606
+ confirmation: booleanFlag(flags["confirm-local-validation-revocation"])
607
+ ? "local-validation-revocation-confirmed" : null
608
+ }), json);
609
+ }
610
+
611
+ if (command === "learn-trial-failure-revoke") {
612
+ return output(await revokeLearningTrialFailure({
613
+ root: flags.root || process.cwd(), trialFailureId: positional[0], reasonCode: flags["reason-code"],
614
+ reason: flags.reason,
615
+ confirmation: booleanFlag(flags["confirm-local-trial-failure-revocation"])
616
+ ? "local-trial-failure-revocation-confirmed" : null
617
+ }), json);
618
+ }
619
+
620
+ if (command === "learn-application-revoke") {
621
+ return output(await revokeLearningApplication({
622
+ root: flags.root || process.cwd(), applicationId: positional[0], reasonCode: flags["reason-code"],
623
+ reason: flags.reason,
624
+ confirmation: booleanFlag(flags["confirm-local-application-revocation"])
625
+ ? "local-application-revocation-confirmed" : null
626
+ }), json);
627
+ }
628
+
629
+ if (command === "learn-delivery-revoke") {
630
+ return output(await revokeLearningDelivery({
631
+ root: flags.root || process.cwd(), deliveryId: positional[0], reasonCode: flags["reason-code"],
632
+ reason: flags.reason,
633
+ confirmation: booleanFlag(flags["confirm-local-delivery-revocation"])
634
+ ? "local-delivery-revocation-confirmed" : null
635
+ }), json);
636
+ }
637
+
638
+ if (command === "learn-outcome-revoke") {
639
+ return output(await revokeLearningOutcome({
640
+ root: flags.root || process.cwd(), outcomeId: positional[0], reasonCode: flags["reason-code"],
641
+ reason: flags.reason,
642
+ confirmation: booleanFlag(flags["confirm-local-outcome-revocation"])
643
+ ? "local-outcome-revocation-confirmed" : null
644
+ }), json);
645
+ }
646
+
647
+ if (command === "learn-outcome") {
648
+ const lineage = flags["measurement-receipt"] || null;
649
+ return output(await recordLearningOutcome({
650
+ root: flags.root || process.cwd(), learningId: positional[0], id: flags.id,
651
+ evaluationId: flags.evaluation, measurementReceiptId: lineage,
652
+ applicationId: flags.application || null, deliveryId: flags.delivery || null,
653
+ ...(lineage ? {} : { phase: flags.phase, scope: learningScope(flags), metric: {
654
+ name: flags.metric, direction: flags.direction, value: Number(flags.value),
655
+ blockingDefects: Number(flags["blocking-defects"] ?? 0)
656
+ },
657
+ measurement: {
658
+ kind: flags.measurement || "objective", evaluatorId: flags.evaluator,
659
+ sourceDigest: flags["source-digest"] || null
660
+ },
661
+ coverage: {
662
+ datasetDigest: flags["dataset-digest"],
663
+ caseCount: Number(flags["case-count"])
664
+ },
665
+ measuredAt: flags.at })
666
+ }), json);
667
+ }
668
+
669
+ if (command === "learn-status") {
670
+ return output(await learningOutcomeStatus({
671
+ root: positional[0] || process.cwd(), scope: hasLearningScope(flags) ? learningScope(flags) : null
672
+ }), json);
673
+ }
674
+
675
+ if (command === "learn-delivery-purge") {
676
+ return output(await purgeStaleLearningApplications({
677
+ root: positional[0] || process.cwd(),
678
+ confirmation: booleanFlag(flags["confirm-local-purge"]) ? "local-user-purge-confirmed" : null
679
+ }), json);
680
+ }
681
+
682
+ if (command === "learn-measurement-purge") {
683
+ return output(await purgeStaleLearningMeasurements({
684
+ root: positional[0] || process.cwd(),
685
+ confirmation: booleanFlag(flags["confirm-local-purge"]) ? "local-user-purge-confirmed" : null
686
+ }), json);
687
+ }
688
+
689
+ if (command === "learn-rollback") {
690
+ return output(await rollbackLearning({
691
+ root: flags.root || process.cwd(), id: positional[0], reason: flags.reason
692
+ }), json);
693
+ }
694
+
695
+ if (command === "learn-delete") {
696
+ return output(await deleteLearning({ root: flags.root || process.cwd(), id: positional[0] }), json);
697
+ }
698
+
699
+ if (command === "learn-config") {
700
+ const root = positional[0] || process.cwd();
701
+ const config = {};
702
+ if (flags["auto-promote"] !== undefined) config.autoPromote = booleanFlag(flags["auto-promote"]);
703
+ if (flags["min-confidence"] !== undefined) config.minConfidence = Number(flags["min-confidence"]);
704
+ if (flags["min-evidence"] !== undefined) config.minEvidence = Number(flags["min-evidence"]);
705
+ if (flags["max-items"] !== undefined) config.maxContextItems = Number(flags["max-items"]);
706
+ if (flags["min-outcomes"] !== undefined) config.minOutcomeReceipts = Number(flags["min-outcomes"]);
707
+ if (flags["min-improvement"] !== undefined) config.minImprovement = Number(flags["min-improvement"]);
708
+ if (flags["regression-tolerance"] !== undefined) config.regressionTolerance = Number(flags["regression-tolerance"]);
709
+ if (flags["outcome-max-age-days"] !== undefined) config.outcomeMaxAgeDays = Number(flags["outcome-max-age-days"]);
710
+ if (flags["canary-receipts"] !== undefined) config.canaryReceipts = Number(flags["canary-receipts"]);
711
+ if (flags["canary-ttl-days"] !== undefined) config.canaryTtlDays = Number(flags["canary-ttl-days"]);
712
+ if (flags["initial-trial-outcome-timeout-minutes"] !== undefined) {
713
+ config.initialTrialOutcomeTimeoutMinutes = Number(flags["initial-trial-outcome-timeout-minutes"]);
714
+ }
715
+ if (!Object.keys(config).length) return output((await loadLearning(root)).learning.config, json);
716
+ return output(await configureLearning({ root, config }), json);
717
+ }
718
+
719
+ if (command === "continuity-config") {
720
+ const root = positional[0] || process.cwd();
721
+ const config = {};
722
+ if (flags.enabled !== undefined) config.enabled = booleanFlag(flags.enabled);
723
+ if (flags.entity !== undefined) config.defaultEntityId = flags.entity === "none" ? null : flags.entity;
724
+ if (flags.project !== undefined) config.defaultProjectId = flags.project === "none" ? null : flags.project;
725
+ if (flags["min-confidence"] !== undefined) config.minConfidence = Number(flags["min-confidence"]);
726
+ if (flags["min-directness"] !== undefined) config.minDirectness = Number(flags["min-directness"]);
727
+ if (flags["min-evidence"] !== undefined) config.minEvidence = Number(flags["min-evidence"]);
728
+ if (flags["max-prompt-bytes"] !== undefined) config.maxPromptBytes = Number(flags["max-prompt-bytes"]);
729
+ if (flags["max-briefing-bytes"] !== undefined) config.maxBriefingBytes = Number(flags["max-briefing-bytes"]);
730
+ if (!Object.keys(config).length) return output((await loadContinuity(root)).continuity.config, json);
731
+ return output(await configureContinuity({
732
+ root, config,
733
+ confirmation: booleanFlag(flags["confirm-local-opt-in"]) ? "local-user-opt-in" : null
734
+ }), json);
735
+ }
736
+
737
+ if (command === "continuity-status") {
738
+ const { continuity, continuityPath } = await loadContinuity(positional[0] || process.cwd());
739
+ return output({
740
+ schema: continuity.schema, config: continuity.config,
741
+ signals: continuity.signals.length, history: continuity.history.length,
742
+ continuityPath, authority: "context-only"
743
+ }, json);
744
+ }
745
+
746
+ if (command === "continuity-purge") {
747
+ return output(await purgeContinuity({
748
+ root: flags.root || process.cwd(), subjectId: positional[0],
749
+ confirmation: booleanFlag(flags["confirm-local-purge"]) ? "local-user-confirmed" : null
750
+ }), json);
751
+ }
752
+
753
+ if (command === "preflight-policy") {
754
+ if (!positional[0]) throw new Error("preflight-policy requires one local JSON policy file");
755
+ const profile = JSON.parse(await readFile(positional[0], "utf8"));
756
+ return output(await configurePreflightPolicy({ profile,
757
+ confirmation: booleanFlag(flags["confirm-local-policy"]) ? "local-owner-confirmed" : null }), json);
758
+ }
759
+
760
+ if (command === "preflight-status") return output(await preflightStatus(), json);
761
+
762
+ if (command === "remember-propose") {
763
+ return output(await proposeMustRemember({ claim: flags.claim, kind: flags.kind || "critical",
764
+ userId: flags.user, tenantId: flags.tenant, projectId: flags.project || null,
765
+ groupId: flags.group || null, taskId: flags.task || null, sourceDigest: flags["source-digest"] || null }), json);
766
+ }
767
+
768
+ if (command === "remember-confirm") {
769
+ return output(await confirmMustRemember({ candidateId: positional[0], supersedes: flags.supersedes || null,
770
+ confirmation: booleanFlag(flags["confirm-local-user"]) ? "local-user-confirmed" : null }), json);
771
+ }
772
+
773
+ if (command === "remember-rollback") {
774
+ return output(await rollbackMustRemember({ id: positional[0],
775
+ confirmation: booleanFlag(flags["confirm-local-user"]) ? "local-user-confirmed" : null }), json);
776
+ }
777
+
778
+ if (command === "remember-purge") {
779
+ return output(await purgeMustRemember({ id: positional[0],
780
+ confirmation: booleanFlag(flags["confirm-local-purge"]) ? "local-user-purge-confirmed" : null }), json);
781
+ }
782
+
783
+ if (command === "source-status") {
784
+ const host = flags.host || process.env.AGENTSPINE_HOST;
785
+ if (!["claude", "codex"].includes(host)) throw new Error("source-status requires --host claude or --host codex");
786
+ const resolved = await resolveHostSourceCatalog({ host, cwd: flags.cwd || process.cwd() });
787
+ const registry = await inspectSourceRegistry();
788
+ return output({ diagnostics: resolved.diagnostics, sources: resolved.catalog.documents.map((item) => ({
789
+ id: item.relativePath, scope: item.sourceScope, binding: item.sourceBinding, sha256: item.sha256,
790
+ bytes: item.bytes, authority: item.authority
791
+ })), bindings: registry.registry.bindings, authority: "context-only" }, json);
792
+ }
793
+
794
+ if (command === "source-bind") {
795
+ return output(await bindSourceRoot({
796
+ host: flags.host, hostHome: flags["host-home"] || process.env.CLAUDE_CONFIG_DIR || process.env.CODEX_HOME || process.cwd(),
797
+ projectRoot: flags.project || process.cwd(), sourceRoot: positional[0], scope: flags.scope || "state-user",
798
+ confirmation: booleanFlag(flags["confirm-local-binding"]) ? "local-user-confirmed" : null
799
+ }), json);
800
+ }
801
+
802
+ if (command === "source-rollback") {
803
+ return output(await rollbackSourceBinding({ id: positional[0],
804
+ confirmation: booleanFlag(flags["confirm-local-binding"]) ? "local-user-confirmed" : null }), json);
805
+ }
806
+
807
+ if (command === "source-purge") {
808
+ return output(await purgeSourceBinding({ id: positional[0],
809
+ confirmation: booleanFlag(flags["confirm-local-binding"]) ? "local-user-confirmed" : null }), json);
810
+ }
811
+
812
+ if (command === "delegation-grant") {
813
+ return output(await grantDelegation({
814
+ root: flags.root || process.cwd(), actorId: positional[0],
815
+ id: flags.id, actions: String(flags.actions || "").split(",").filter(Boolean),
816
+ targetIds: String(flags.targets || "*").split(",").filter(Boolean), reason: flags.reason,
817
+ confirmation: booleanFlag(flags["confirm-local-policy"]) ? "local-owner-confirmed" : null
818
+ }), json);
819
+ }
820
+
821
+ if (command === "delegation-revoke") {
822
+ return output(await revokeDelegation({
823
+ root: flags.root || process.cwd(), id: positional[0], reason: flags.reason,
824
+ confirmation: booleanFlag(flags["confirm-local-policy"]) ? "local-owner-confirmed" : null
825
+ }), json);
826
+ }
827
+
828
+ if (command === "delegation-check") {
829
+ return output(await checkDelegation({
830
+ root: flags.root || process.cwd(), actorId: positional[0], action: flags.action,
831
+ targetId: flags.target || null
832
+ }), json);
833
+ }
834
+
835
+ if (command === "delegation-policy") {
836
+ return output((await loadDelegationPolicy(positional[0] || process.cwd())).policy, json);
837
+ }
838
+
839
+ if (command === "task-create") {
840
+ return output(await createTask({
841
+ root: flags.root || process.cwd(), id: positional[0], actorId: flags.actor,
842
+ assigneeId: flags.assignee || null, kind: flags.kind || "task", title: flags.title,
843
+ summary: flags.summary || null, projectId: flags.project || null,
844
+ privacy: flags.privacy || "private", groupId: flags.group || null,
845
+ priority: Number(flags.priority ?? 50), dueAt: flags.due || null
846
+ }), json);
847
+ }
848
+
849
+ if (command === "task-update") {
850
+ const patch = {};
851
+ if (flags.status !== undefined) patch.status = flags.status;
852
+ if (flags.assignee !== undefined) patch.assigneeId = flags.assignee;
853
+ if (flags.unassign !== undefined) patch.assigneeId = null;
854
+ if (flags.title !== undefined) patch.title = flags.title;
855
+ if (flags.summary !== undefined) patch.summary = flags.summary;
856
+ if (flags.priority !== undefined) patch.priority = Number(flags.priority);
857
+ if (flags.due !== undefined) patch.dueAt = flags.due;
858
+ if (flags["clear-due"] !== undefined) patch.dueAt = null;
859
+ if (flags.note !== undefined) patch.note = flags.note;
860
+ return output(await updateTask({
861
+ root: flags.root || process.cwd(), id: positional[0], actorId: flags.actor, patch
862
+ }), json);
863
+ }
864
+
865
+ if (command === "tasks") {
866
+ return output(await taskContext({
867
+ root: positional[0] || process.cwd(), includePrivate: booleanFlag(flags["include-private"]),
868
+ groupId: flags.group || null, actorId: flags.actor || null, assigneeId: flags.assignee || null,
869
+ projectId: flags.project || null, includeClosed: booleanFlag(flags.closed),
870
+ maxItems: Number(flags["max-items"] ?? 20)
871
+ }), json);
872
+ }
873
+
874
+ if (command === "task-delete") {
875
+ return output(await deleteTask({
876
+ root: flags.root || process.cwd(), id: positional[0],
877
+ confirmation: booleanFlag(flags["confirm-local-policy"]) ? "local-owner-confirmed" : null
878
+ }), json);
879
+ }
880
+
881
+ if (command === "execution-grant") {
882
+ return output(await grantExecution({
883
+ root: flags.root || process.cwd(), id: flags.id, jobId: positional[0], actorId: flags.actor,
884
+ taskId: flags.task, targetId: flags.target, projectId: flags.project, groupId: flags.group || null,
885
+ host: flags.host, capabilities: String(flags.capabilities || "").split(",").filter(Boolean),
886
+ reason: flags.reason, expiresAt: flags.expires || null,
887
+ confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
888
+ }), json);
889
+ }
890
+
891
+ if (command === "execution-revoke") {
892
+ return output(await revokeExecution({
893
+ root: flags.root || process.cwd(), id: positional[0], reason: flags.reason,
894
+ confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
895
+ }), json);
896
+ }
897
+
898
+ if (command === "execution-policy") {
899
+ return output((await loadExecutionPolicy(positional[0] || process.cwd())).policy, json);
900
+ }
901
+
902
+ if (command === "job-register") {
903
+ return output(await registerJob({
904
+ root: flags.root || process.cwd(), id: positional[0], grantId: flags.grant,
905
+ maxRetries: Number(flags["max-retries"] ?? 3), leaseSeconds: Number(flags["lease-seconds"] ?? 120),
906
+ baseRetrySeconds: Number(flags["retry-seconds"] ?? 5),
907
+ confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
908
+ }), json);
909
+ }
910
+
911
+ if (command === "jobs") {
912
+ return output(await selfstarterContext({
913
+ root: positional[0] || process.cwd(), actorId: flags.actor || null,
914
+ projectId: flags.project || null, taskId: flags.task || null,
915
+ includeTerminal: booleanFlag(flags["include-terminal"])
916
+ }), json);
917
+ }
918
+
919
+ if (command === "job-cancel") {
920
+ return output(await cancelJob({
921
+ root: flags.root || process.cwd(), id: positional[0], reason: flags.reason,
922
+ confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
923
+ }), json);
924
+ }
925
+
926
+ if (command === "job-delete") {
927
+ return output(await deleteJob({
928
+ root: flags.root || process.cwd(), id: positional[0],
929
+ confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
930
+ }), json);
931
+ }
932
+
933
+ if (command === "channel-bind") {
934
+ return output(await grantChannelBinding({
935
+ root: flags.root || process.cwd(), id: positional[0], provider: flags.provider,
936
+ tenantId: flags.tenant, accountId: flags.account, chatId: flags.chat,
937
+ threadId: flags.thread || null,
938
+ senderIds: String(flags.senders || "").split(",").filter(Boolean),
939
+ agentId: flags.agent, projectId: flags.project, groupId: flags.group || null,
940
+ sessionKey: flags.session, secretEnv: flags["secret-env"], outboundSecretEnv: flags["outbound-secret-env"] || null,
941
+ capabilities: String(flags.capabilities || "receive,reply").split(",").filter(Boolean),
942
+ confirmation: booleanFlag(flags["confirm-local-channel"]) ? "local-owner-confirmed" : null
943
+ }), json);
944
+ }
945
+
946
+ if (command === "channel-revoke") {
947
+ return output(await revokeChannelBinding({
948
+ root: flags.root || process.cwd(), id: positional[0], reason: flags.reason,
949
+ confirmation: booleanFlag(flags["confirm-local-channel"]) ? "local-owner-confirmed" : null
950
+ }), json);
951
+ }
952
+
953
+ if (command === "channel-policy") {
954
+ return output((await loadChannelPolicy(positional[0] || process.cwd())).policy, json);
955
+ }
956
+
957
+ if (command === "channel-events") {
958
+ return output(await channelRuntimeContext({
959
+ root: positional[0] || process.cwd(), agentId: flags.agent || null,
960
+ projectId: flags.project || null,
961
+ ...(flags.group !== undefined ? { groupId: flags.group || null } : {}),
962
+ provider: flags.provider || null,
963
+ includeTerminal: booleanFlag(flags["include-terminal"]),
964
+ maxItems: Number(flags["max-items"] ?? 20)
965
+ }), json);
966
+ }
967
+
968
+ if (command === "persona-sync") {
969
+ if (!booleanFlag(flags["confirm-local-persona"])) throw new Error("persona synchronization requires --confirm-local-persona");
970
+ return output(await syncPersonaRosterFromEnvironment({
971
+ root: positional[0] || process.cwd(), env: { ...process.env, AGENTSPINE_PERSONA_ROSTER_FILE: flags.roster }
972
+ }), json);
973
+ }
974
+
975
+ if (command === "personas") {
976
+ return output(await personaContext({
977
+ root: positional[0] || process.cwd(), personaId: flags.persona || null,
978
+ ...(flags.group !== undefined ? { groupId: flags.group || null } : {}),
979
+ includeInactive: booleanFlag(flags["include-inactive"])
980
+ }), json);
981
+ }
982
+
983
+ if (command === "goal-assign") {
984
+ return output(await assignGoal({
985
+ root: flags.root || process.cwd(), goalId: positional[0], agentId: flags.agent,
986
+ ownerSubjectId: flags.owner, projectId: flags.project, groupId: flags.group || null,
987
+ priority: Number(flags.priority ?? 70), successCriterion: flags.success,
988
+ nextSafeStep: flags["next-step"] || null, steps: await goalPlanFlag(flags.plan), deadline: flags.deadline || null,
989
+ confirmation: booleanFlag(flags["confirm-local-goal"]) ? "local-owner-confirmed" : null
990
+ }), json);
991
+ }
992
+
993
+ if (command === "goal-clarify") {
994
+ return output(await resolveGoalKnowledgeGap({
995
+ root: flags.root || process.cwd(), goalId: positional[0], gapId: flags.gap,
996
+ answer: flags.answer, answerSource: flags.source || "owner-input",
997
+ sourceDigest: flags["source-digest"] || null,
998
+ confirmation: booleanFlag(flags["confirm-local-goal"]) ? "local-owner-confirmed" : null
999
+ }), json);
1000
+ }
1001
+
1002
+ if (command === "gateway-control") {
1003
+ return output(await setGatewayControl({
1004
+ root: positional[0] || process.cwd(),
1005
+ ...(flags.enabled !== undefined ? { enabled: booleanFlag(flags.enabled) } : {}),
1006
+ ...(flags["kill-switch"] !== undefined ? { killSwitch: booleanFlag(flags["kill-switch"]) } : {}),
1007
+ confirmation: booleanFlag(flags["confirm-local-gateway"]) ? "local-owner-confirmed" : null
1008
+ }), json);
1009
+ }
1010
+
1011
+ if (command === "gateway-status") {
1012
+ return output(await gatewayContext({ root: positional[0] || process.cwd(), agentId: flags.agent || null }), json);
1013
+ }
1014
+
1015
+ if (command === "share-init") {
1016
+ return output(await initDirectoryAdapter({
1017
+ root: flags.root || process.cwd(), directory: positional[0], scopeId: flags.scope,
1018
+ adapterId: flags.adapter, signerId: flags.signer || null,
1019
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1020
+ }), json);
1021
+ }
1022
+
1023
+ if (command === "share-keygen") {
1024
+ return output(await generateSigningIdentity({
1025
+ root: flags.root || process.cwd(), signerId: positional[0], rotate: booleanFlag(flags.rotate),
1026
+ publicOut: flags["public-out"] || null,
1027
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1028
+ }), json);
1029
+ }
1030
+
1031
+ if (command === "share-signers") {
1032
+ return output(await listSigningIdentities({ root: positional[0] || process.cwd() }), json);
1033
+ }
1034
+
1035
+ if (command === "share-trust") {
1036
+ return output(await trustSigner({
1037
+ root: flags.root || process.cwd(), publicIdentityPath: positional[0],
1038
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1039
+ }), json);
1040
+ }
1041
+
1042
+ if (command === "share-trust-revoke") {
1043
+ return output(await revokeTrustedSigner({
1044
+ root: flags.root || process.cwd(), keyId: positional[0], reason: flags.reason,
1045
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1046
+ }), json);
1047
+ }
1048
+
1049
+ if (command === "share-trust-list") {
1050
+ return output(await trustedSignerContext({
1051
+ root: positional[0] || process.cwd(), includeRevoked: booleanFlag(flags["include-revoked"])
1052
+ }), json);
1053
+ }
1054
+
1055
+ if (command === "share-publish") {
1056
+ return output(await publishLearning({
1057
+ root: flags.root || process.cwd(), directory: positional[0], learningId: flags.learning,
1058
+ eventId: flags.id, supersedesEventId: flags.supersedes || null, signerId: flags.signer || null,
1059
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1060
+ }), json);
1061
+ }
1062
+
1063
+ if (command === "share-pull") {
1064
+ return output(await pullShared({
1065
+ root: flags.root || process.cwd(), directory: positional[0],
1066
+ requireAuthenticated: booleanFlag(flags["require-authenticated"])
1067
+ }), json);
1068
+ }
1069
+
1070
+ if (command === "share-snapshot-export") {
1071
+ return output(await exportHttpsSnapshot({
1072
+ root: flags.root || process.cwd(), directory: positional[0], output: flags.out,
1073
+ snapshotId: flags.id,
1074
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1075
+ }), json);
1076
+ }
1077
+
1078
+ if (command === "share-https-publish") {
1079
+ return output(await publishHttpsSnapshot({
1080
+ root: flags.root || process.cwd(), directory: positional[0], baseUrl: flags.base,
1081
+ snapshotId: flags.id, tokenEnv: flags["token-env"] || null,
1082
+ timeoutMs: Number(flags["timeout-ms"] ?? 10000),
1083
+ allowPrivateNetwork: booleanFlag(flags["allow-private-network"]),
1084
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1085
+ }), json);
1086
+ }
1087
+
1088
+ if (command === "share-https-pull") {
1089
+ return output(await pullHttpsSnapshot({
1090
+ root: flags.root || process.cwd(), url: positional[0], tokenEnv: flags["token-env"] || null,
1091
+ timeoutMs: Number(flags["timeout-ms"] ?? 10000),
1092
+ allowPrivateNetwork: booleanFlag(flags["allow-private-network"]),
1093
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1094
+ }), json);
1095
+ }
1096
+
1097
+ if (command === "share-feed-publish") {
1098
+ return output(await publishHttpsFeed({
1099
+ root: flags.root || process.cwd(), directory: positional[0], baseUrl: flags.base,
1100
+ feedId: flags.feed, signerId: flags.signer, snapshotId: flags.id,
1101
+ tokenEnv: flags["token-env"] || null, timeoutMs: Number(flags["timeout-ms"] ?? 10000),
1102
+ allowPrivateNetwork: booleanFlag(flags["allow-private-network"]),
1103
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1104
+ }), json);
1105
+ }
1106
+
1107
+ if (command === "share-feed-pull") {
1108
+ return output(await pullHttpsFeed({
1109
+ root: flags.root || process.cwd(), baseUrl: flags.base, feedId: flags.feed,
1110
+ tokenEnv: flags["token-env"] || null, timeoutMs: Number(flags["timeout-ms"] ?? 10000),
1111
+ allowPrivateNetwork: booleanFlag(flags["allow-private-network"]),
1112
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1113
+ }), json);
1114
+ }
1115
+
1116
+ if (command === "share-feed-state") {
1117
+ return output(await loadHttpsFeedState(positional[0] || process.cwd()), json);
1118
+ }
1119
+
1120
+ if (command === "share-peer-serve") {
1121
+ await servePeerOnce({
1122
+ root: flags.root || process.cwd(), directory: positional[0], signerId: flags.signer,
1123
+ input: process.stdin, output: process.stdout,
1124
+ timeoutMs: Number(flags["timeout-ms"] ?? 10000),
1125
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1126
+ });
1127
+ return;
1128
+ }
1129
+
1130
+ if (command === "share-peer-pull") {
1131
+ return output(await pullPeerCommand({
1132
+ root: flags.root || process.cwd(), commandJson: flags["command-json"],
1133
+ timeoutMs: Number(flags["timeout-ms"] ?? 10000),
1134
+ maxBytes: Number(flags["max-bytes"] ?? 22 * 1024 * 1024),
1135
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1136
+ }), json);
1137
+ }
1138
+
1139
+ if (command === "share-sqlite-init") {
1140
+ return output(await initSqliteAdapter({
1141
+ root: flags.root || process.cwd(), directory: positional[0], database: flags.database,
1142
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1143
+ }), json);
1144
+ }
1145
+
1146
+ if (command === "share-sqlite-publish") {
1147
+ return output(await publishSqliteSnapshot({
1148
+ root: flags.root || process.cwd(), directory: positional[0], database: flags.database,
1149
+ snapshotId: flags.id,
1150
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1151
+ }), json);
1152
+ }
1153
+
1154
+ if (command === "share-sqlite-inspect") {
1155
+ return output(await inspectSqliteAdapter({
1156
+ root: flags.root || process.cwd(), database: flags.database
1157
+ }), json);
1158
+ }
1159
+
1160
+ if (command === "share-sqlite-pull") {
1161
+ return output(await pullSqliteSnapshot({
1162
+ root: flags.root || process.cwd(), database: flags.database
1163
+ }), json);
1164
+ }
1165
+
1166
+ if (command === "share-inbox") {
1167
+ return output(await sharedInbox({ root: positional[0] || process.cwd(), status: flags.status || "pending" }), json);
1168
+ }
1169
+
1170
+ if (command === "share-review") {
1171
+ return output(await reviewShared({
1172
+ root: flags.root || process.cwd(), id: positional[0], decision: flags.decision,
1173
+ reason: flags.reason, confirmedByUser: booleanFlag(flags["confirmed-by-user"])
1174
+ }), json);
1175
+ }
1176
+
1177
+ if (command === "share-context") {
1178
+ return output(await sharedContext({
1179
+ root: positional[0] || process.cwd(), scopeId: flags.scope || null, groupId: flags.group || null,
1180
+ includePrivate: booleanFlag(flags["include-private"]),
1181
+ kinds: flags.kind ? String(flags.kind).split(",").filter(Boolean) : null,
1182
+ subjectIds: flags.subject ? String(flags.subject).split(",").filter(Boolean) : null,
1183
+ maxItems: flags["max-items"] === undefined ? null : Number(flags["max-items"])
1184
+ }), json);
1185
+ }
1186
+
1187
+ if (command === "share-rollback") {
1188
+ return output(await rollbackShared({
1189
+ root: flags.root || process.cwd(), id: positional[0], reason: flags.reason
1190
+ }), json);
1191
+ }
1192
+
1193
+ if (command === "share-delete") {
1194
+ return output(await deleteShared({
1195
+ root: flags.root || process.cwd(), id: positional[0],
1196
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
1197
+ }), json);
1198
+ }
1199
+
1200
+ if (command === "share-config") {
1201
+ return output(await configureSharing({
1202
+ root: positional[0] || process.cwd(), maxContextItems: Number(flags["max-items"])
1203
+ }), json);
1204
+ }
1205
+
1206
+ if (command === "doctor") {
1207
+ let hostIntegration;
1208
+ try {
1209
+ hostIntegration = await checkHosts(fileURLToPath(new URL("..", import.meta.url)));
1210
+ } catch (error) {
1211
+ hostIntegration = { ok: false, error: error.message };
1212
+ }
1213
+ let sourceResolution = null;
1214
+ let orphanScan = null;
1215
+ const sourceHost = flags.host || process.env.AGENTSPINE_HOST;
1216
+ if (["claude", "codex"].includes(sourceHost)) {
1217
+ try {
1218
+ const resolved = await resolveHostSourceCatalog({ host: sourceHost, cwd: flags.cwd || process.cwd() });
1219
+ sourceResolution = resolved.diagnostics;
1220
+ if (booleanFlag(flags["offline-memory-orphans"])) {
1221
+ if (sourceHost !== "claude" || !resolved.memoryRoot) throw new Error("offline memory orphan scan requires a resolved Claude project-memory root");
1222
+ orphanScan = await scanIndexedMemoryOrphans(resolved.memoryRoot);
1223
+ }
1224
+ }
1225
+ catch (error) { sourceResolution = { status: "failed-closed", reason: error.message }; }
1226
+ }
1227
+ let preflight;
1228
+ try { preflight = await preflightStatus(); }
1229
+ catch (error) { preflight = { status: "failed-closed", error: error.message }; }
1230
+ let learningOutcomes;
1231
+ try {
1232
+ const status = await learningOutcomeStatus({ root: positional[0] || process.cwd() });
1233
+ const unboundAfterReceipts = status.records.reduce((sum, item) => sum + item.afterReceipts - item.boundAfterReceipts, 0);
1234
+ const undeliveredAfterReceipts = status.records.reduce((sum, item) => sum + item.afterReceipts - item.deliveredAfterReceipts, 0);
1235
+ const stalePendingApplications = status.records.reduce((sum, item) => sum + item.stalePendingApplications, 0);
1236
+ const totalOutcomeReceipts = status.records.reduce((sum, item) => sum + item.beforeReceipts + item.afterReceipts, 0);
1237
+ const plannedOutcomeReceipts = status.records.reduce((sum, item) => sum + item.plannedOutcomeReceipts, 0);
1238
+ const coverageBoundReceipts = status.records.reduce((sum, item) => sum + item.coverageBoundReceipts, 0);
1239
+ const legacyCoverageReceipts = status.records.reduce((sum, item) => sum + item.legacyCoverageReceipts, 0);
1240
+ const provenanceBoundReceipts = status.records.reduce((sum, item) => sum + item.provenanceBoundReceipts, 0);
1241
+ const legacyProvenanceReceipts = status.records.reduce((sum, item) => sum + item.legacyProvenanceReceipts, 0);
1242
+ const measurementReceipts = status.records.reduce((sum, item) => sum + item.measurementReceipts, 0);
1243
+ const measurementLineageReceipts = status.records.reduce((sum, item) => sum + item.measurementLineageReceipts, 0);
1244
+ const candidateEvidenceLineageReceipts = status.candidateEvidenceLineageReceipts;
1245
+ const consumedMeasurementReceipts = status.records.reduce((sum, item) => sum + item.consumedMeasurementReceipts, 0);
1246
+ const staleUnconsumedMeasurements = status.records.reduce((sum, item) => sum + item.staleUnconsumedMeasurements, 0);
1247
+ const lineageBoundReceipts = status.records.reduce((sum, item) => sum + item.lineageBoundReceipts, 0);
1248
+ const pairedOutcomeReceipts = status.records.reduce((sum, item) => sum + item.pairedOutcomeReceipts, 0);
1249
+ const pairedEvaluatorPairs = status.records.reduce((sum, item) => sum + item.pairedEvaluatorPairs, 0);
1250
+ const evaluatorRootBoundReceipts = status.records.reduce((sum, item) => sum + item.evaluatorRootBoundReceipts, 0);
1251
+ const independentEvaluatorRoots = status.records.reduce((sum, item) => sum + item.independentEvaluatorRoots, 0);
1252
+ const evaluatorRegistryContracts = status.records.reduce((sum, item) => sum + item.evaluatorRegistryContracts, 0);
1253
+ const inactiveEvaluatorRegistryContracts = status.records.reduce((sum, item) => sum + item.inactiveEvaluatorRegistryContracts, 0);
1254
+ const currentValidatedLessons = status.records.filter((item) => item.validationLeaseStatus === "current-validated").length;
1255
+ const staleValidatedLessons = status.records.filter((item) => item.validationLeaseStatus === "stale-validated").length;
1256
+ const unprovenValidatedLessons = status.records.filter((item) => item.validationLeaseStatus === "unproven-validated").length;
1257
+ const revokedValidatedLessons = status.records.filter((item) => item.validationLeaseStatus === "revoked-validated").length;
1258
+ const renewedValidationLeases = status.records.filter((item) =>
1259
+ ["agentspine.learning-validation/v2", "agentspine.learning-validation/v3",
1260
+ "agentspine.learning-validation/v4", "agentspine.learning-validation/v5"].includes(item.validationLeaseSchema)).length;
1261
+ const fixedCohortValidationLeases = status.records.filter((item) =>
1262
+ ["agentspine.learning-validation/v3", "agentspine.learning-validation/v4",
1263
+ "agentspine.learning-validation/v5"]
1264
+ .includes(item.validationLeaseSchema)).length;
1265
+ const admissionBoundValidationLeases = status.records.filter((item) =>
1266
+ ["agentspine.learning-validation/v4", "agentspine.learning-validation/v5"]
1267
+ .includes(item.validationLeaseSchema)).length;
1268
+ const trialBoundValidationLeases = status.records.filter((item) =>
1269
+ item.validationLeaseSchema === "agentspine.learning-validation/v5").length;
1270
+ const activeRevalidations = status.records.filter((item) => item.revalidationStatus === "active").length;
1271
+ const staleRevalidations = status.records.filter((item) => item.revalidationStatus === "stale").length;
1272
+ const fixedCohortRevalidations = status.records.filter((item) =>
1273
+ item.revalidationStatus === "active"
1274
+ && ["first-completed-turns", "first-admitted-turns",
1275
+ "first-admitted-trials"].includes(item.revalidationSelectionMode)).length;
1276
+ const admissionBoundRevalidations = status.records.filter((item) =>
1277
+ item.revalidationStatus === "active" && ["first-admitted-turns", "first-admitted-trials"]
1278
+ .includes(item.revalidationSelectionMode)).length;
1279
+ const trialBoundRevalidations = status.records.filter((item) =>
1280
+ item.revalidationStatus === "active" && item.revalidationSelectionMode === "first-admitted-trials").length;
1281
+ const requiredRevalidationDeliveries = status.records.reduce((sum, item) =>
1282
+ sum + item.revalidationRequiredDeliveries, 0);
1283
+ const completedRevalidationDeliveries = status.records.reduce((sum, item) =>
1284
+ sum + item.revalidationCompletedDeliveries, 0);
1285
+ const admittedRevalidationApplications = status.records.reduce((sum, item) =>
1286
+ sum + item.revalidationAdmittedApplications, 0);
1287
+ const initialTrialContracts = status.records.filter((item) => item.initialTrialMode === "first-admitted-trials").length;
1288
+ const requiredInitialTrials = status.records.reduce((sum, item) => sum + item.initialTrialSlots, 0);
1289
+ const admittedInitialApplications = status.records.reduce((sum, item) =>
1290
+ sum + item.initialAdmittedApplications, 0);
1291
+ const completedInitialDeliveries = status.records.reduce((sum, item) =>
1292
+ sum + item.initialCompletedDeliveries, 0);
1293
+ const incompleteInitialAdmissions = status.records.reduce((sum, item) =>
1294
+ sum + item.incompleteInitialAdmissions, 0);
1295
+ const targetBoundEvaluationContracts = status.records.reduce((sum, item) =>
1296
+ sum + item.targetBoundEvaluationContracts, 0);
1297
+ const targetBoundApplications = status.records.reduce((sum, item) =>
1298
+ sum + item.targetBoundApplications, 0);
1299
+ const deadlineBoundEvaluationContracts = status.records.reduce((sum, item) =>
1300
+ sum + item.deadlineBoundEvaluationContracts, 0);
1301
+ const stalenessBoundEvaluationContracts = status.records.reduce((sum, item) =>
1302
+ sum + item.stalenessBoundEvaluationContracts, 0);
1303
+ const promotionBoundEvaluationContracts = status.records.reduce((sum, item) =>
1304
+ sum + item.promotionBoundEvaluationContracts, 0);
1305
+ const candidateAdmissionEvaluationContracts = status.records.reduce((sum, item) =>
1306
+ sum + item.candidateAdmissionEvaluationContracts, 0);
1307
+ const evidenceSourceAttestedEvaluationContracts = status.records.reduce((sum, item) =>
1308
+ sum + item.evidenceSourceAttestedEvaluationContracts, 0);
1309
+ const candidateEvidenceLineageEvaluationContracts = status.records.reduce((sum, item) =>
1310
+ sum + item.candidateEvidenceLineageEvaluationContracts, 0);
1311
+ const candidateEvidenceCohortEvaluationContracts = status.records.reduce((sum, item) =>
1312
+ sum + item.candidateEvidenceCohortEvaluationContracts, 0);
1313
+ const blockingDefectBoundEvaluationContracts = status.records.reduce((sum, item) =>
1314
+ sum + item.blockingDefectBoundEvaluationContracts, 0);
1315
+ const evidenceSourceBoundEvaluationContracts = status.records.reduce((sum, item) =>
1316
+ sum + item.evidenceSourceBoundEvaluationContracts, 0);
1317
+ const blockingDefectOutcomeReceipts = status.records.reduce((sum, item) =>
1318
+ sum + item.blockingDefectOutcomeReceipts, 0);
1319
+ const deadlineBoundApplications = status.records.reduce((sum, item) =>
1320
+ sum + item.deadlineBoundApplications, 0);
1321
+ const trialRetryEvaluationContracts = status.records.reduce((sum, item) =>
1322
+ sum + item.trialRetryEvaluationContracts, 0);
1323
+ const comparableTrialRetryEvaluationContracts = status.records.reduce((sum, item) =>
1324
+ sum + item.comparableTrialRetryEvaluationContracts, 0);
1325
+ const boundedTrialRetryEvaluationContracts = status.records.reduce((sum, item) =>
1326
+ sum + item.boundedTrialRetryEvaluationContracts, 0);
1327
+ const trialRetryExhaustionReceipts = status.records.reduce((sum, item) =>
1328
+ sum + item.trialRetryExhaustionReceipts, 0);
1329
+ const trialFailureReceipts = status.records.reduce((sum, item) => sum + item.trialFailureReceipts, 0);
1330
+ const trialFailureRevocationReceipts = status.records.reduce((sum, item) =>
1331
+ sum + item.trialFailureRevocationReceipts, 0);
1332
+ const evaluationRevocationReceipts = status.records.reduce((sum, item) => sum + item.evaluationRevocationReceipts, 0);
1333
+ const evidenceSourceAttestationRevocationReceipts = status.records.reduce((sum, item) =>
1334
+ sum + item.evidenceSourceAttestationRevocationReceipts, 0);
1335
+ const validationRevocationReceipts = status.records.reduce((sum, item) => sum + item.validationRevocationReceipts, 0);
1336
+ const evidenceRevocationReceipts = status.records.reduce((sum, item) => sum + item.evidenceRevocationReceipts, 0);
1337
+ const measurementRevocationReceipts = status.records.reduce((sum, item) => sum + item.measurementRevocationReceipts, 0);
1338
+ const applicationRevocationReceipts = status.records.reduce((sum, item) => sum + item.applicationRevocationReceipts, 0);
1339
+ const deliveryRevocationReceipts = status.records.reduce((sum, item) => sum + item.deliveryRevocationReceipts, 0);
1340
+ const outcomeRevocationReceipts = status.records.reduce((sum, item) => sum + item.outcomeRevocationReceipts, 0);
1341
+ const deliveryTimeoutFailures = status.records.reduce((sum, item) => sum + item.deliveryTimeoutFailures, 0);
1342
+ const outcomeTimeoutFailures = status.records.reduce((sum, item) => sum + item.outcomeTimeoutFailures, 0);
1343
+ const pendingInitialOutcomes = status.records.reduce((sum, item) => sum + item.pendingInitialOutcomes, 0);
1344
+ const staleInitialOutcomes = status.records.reduce((sum, item) => sum + item.staleInitialOutcomes, 0);
1345
+ const unplannedOutcomeReceipts = totalOutcomeReceipts - plannedOutcomeReceipts;
1346
+ learningOutcomes = {
1347
+ status: status.records.some((item) => ["stale", "revoked", "revoked-evaluation",
1348
+ "revoked-evidence-source-attestation", "revoked-validation", "revoked-evidence",
1349
+ "revoked-measurement", "revoked-application", "revoked-delivery", "revoked-outcome",
1350
+ "unproven", "failed-trial"].includes(item.canaryStatus))
1351
+ || unboundAfterReceipts > 0 || undeliveredAfterReceipts > 0 || unplannedOutcomeReceipts > 0
1352
+ || stalePendingApplications > 0 || staleUnconsumedMeasurements > 0
1353
+ || inactiveEvaluatorRegistryContracts > 0 || staleRevalidations > 0
1354
+ || staleInitialOutcomes > 0 || trialFailureRevocationReceipts > 0 ? "degraded" : "healthy",
1355
+ candidates: status.records.length,
1356
+ activeCanaries: status.records.filter((item) => item.canaryStatus === "active").length,
1357
+ validatedCanaries: status.records.filter((item) => item.canaryStatus === "validated").length,
1358
+ staleCanaries: status.records.filter((item) => item.canaryStatus === "stale").length,
1359
+ awaitingApplication: status.records.filter((item) => item.canaryStatus === "active" && item.applicationReceipts === 0).length,
1360
+ applicationReceipts: status.records.reduce((sum, item) => sum + item.applicationReceipts, 0),
1361
+ deliveryReceipts: status.records.reduce((sum, item) => sum + item.deliveryReceipts, 0),
1362
+ pendingApplications: status.records.reduce((sum, item) => sum + item.pendingApplications, 0),
1363
+ stalePendingApplications,
1364
+ evaluationContracts: status.records.reduce((sum, item) => sum + item.evaluationContracts, 0),
1365
+ targetBoundEvaluationContracts,
1366
+ targetBoundApplications,
1367
+ deadlineBoundEvaluationContracts,
1368
+ stalenessBoundEvaluationContracts,
1369
+ promotionBoundEvaluationContracts,
1370
+ candidateAdmissionEvaluationContracts,
1371
+ evidenceSourceAttestedEvaluationContracts,
1372
+ candidateEvidenceLineageEvaluationContracts,
1373
+ candidateEvidenceLineageReceipts,
1374
+ candidateEvidenceCohortEvaluationContracts,
1375
+ blockingDefectBoundEvaluationContracts,
1376
+ evidenceSourceBoundEvaluationContracts,
1377
+ blockingDefectOutcomeReceipts,
1378
+ deadlineBoundApplications,
1379
+ trialRetryEvaluationContracts,
1380
+ comparableTrialRetryEvaluationContracts,
1381
+ boundedTrialRetryEvaluationContracts,
1382
+ trialRetryExhaustionReceipts,
1383
+ trialFailureReceipts,
1384
+ trialFailureRevocationReceipts,
1385
+ evaluationRevocationReceipts,
1386
+ evidenceSourceAttestationRevocationReceipts,
1387
+ validationRevocationReceipts,
1388
+ evidenceRevocationReceipts,
1389
+ measurementRevocationReceipts,
1390
+ applicationRevocationReceipts,
1391
+ deliveryRevocationReceipts,
1392
+ outcomeRevocationReceipts,
1393
+ deliveryTimeoutFailures,
1394
+ outcomeTimeoutFailures,
1395
+ pendingInitialOutcomes,
1396
+ staleInitialOutcomes,
1397
+ plannedOutcomeReceipts,
1398
+ coverageBoundReceipts,
1399
+ legacyCoverageReceipts,
1400
+ provenanceBoundReceipts,
1401
+ legacyProvenanceReceipts,
1402
+ measurementReceipts,
1403
+ measurementLineageReceipts,
1404
+ consumedMeasurementReceipts,
1405
+ staleUnconsumedMeasurements,
1406
+ lineageBoundReceipts,
1407
+ pairedOutcomeReceipts,
1408
+ pairedEvaluatorPairs,
1409
+ evaluatorRootBoundReceipts,
1410
+ independentEvaluatorRoots,
1411
+ activeEvaluatorRoots: status.evaluatorRegistry.active,
1412
+ revokedEvaluatorRoots: status.evaluatorRegistry.revoked,
1413
+ evaluatorRegistryBindings: status.evaluatorRegistry.bindings,
1414
+ validationLeases: status.evaluatorRegistry.validationLeases,
1415
+ renewedValidationLeases,
1416
+ fixedCohortValidationLeases,
1417
+ admissionBoundValidationLeases,
1418
+ trialBoundValidationLeases,
1419
+ initialTrialContracts,
1420
+ requiredInitialTrials,
1421
+ admittedInitialApplications,
1422
+ completedInitialDeliveries,
1423
+ incompleteInitialAdmissions,
1424
+ activeRevalidations,
1425
+ staleRevalidations,
1426
+ fixedCohortRevalidations,
1427
+ admissionBoundRevalidations,
1428
+ trialBoundRevalidations,
1429
+ requiredRevalidationDeliveries,
1430
+ admittedRevalidationApplications,
1431
+ completedRevalidationDeliveries,
1432
+ currentValidatedLessons,
1433
+ staleValidatedLessons,
1434
+ unprovenValidatedLessons,
1435
+ revokedValidatedLessons,
1436
+ evaluatorRegistryContracts,
1437
+ inactiveEvaluatorRegistryContracts,
1438
+ unplannedOutcomeReceipts,
1439
+ boundAfterReceipts: status.records.reduce((sum, item) => sum + item.boundAfterReceipts, 0),
1440
+ unboundAfterReceipts,
1441
+ deliveredAfterReceipts: status.records.reduce((sum, item) => sum + item.deliveredAfterReceipts, 0),
1442
+ undeliveredAfterReceipts,
1443
+ contradictions: status.records.filter((item) => item.conflictsWith.length > 0).length,
1444
+ authority: "context-only"
1445
+ };
1446
+ } catch (error) { learningOutcomes = { status: "failed-closed", error: error.message, authority: "context-only" }; }
1447
+ const result = {
1448
+ ok: Number(process.versions.node.split(".")[0]) >= 20 && hostIntegration.ok
1449
+ && (!sourceResolution || sourceResolution.status === "loaded")
1450
+ && learningOutcomes.status !== "failed-closed",
1451
+ version: VERSION,
1452
+ node: process.versions.node,
1453
+ platform: process.platform,
1454
+ preservationMode: "read-only-source-overlay",
1455
+ stateDirectory: process.env.AGENTSPINE_STATE_DIR || "platform-default",
1456
+ hostIntegration,
1457
+ sourceResolution,
1458
+ orphanScan,
1459
+ preflight,
1460
+ learningOutcomes
1461
+ };
1462
+ output(result, json);
1463
+ if (!result.ok) process.exitCode = 1;
1464
+ return;
1465
+ }
1466
+
1467
+ if (command === "audit") {
1468
+ const result = await runAudit(positional[0] || process.cwd(), { host: flags.host || process.env.AGENTSPINE_HOST || null });
1469
+ output(result, json);
1470
+ if (!result.ok) process.exitCode = 1;
1471
+ return;
1472
+ }
1473
+
1474
+ if (command === "mcp") {
1475
+ const { startMcpServer } = await import("./mcp.js");
1476
+ startMcpServer();
1477
+ return;
1478
+ }
1479
+
1480
+ throw new Error(`Unknown command: ${command}`);
1481
+ }
1482
+
1483
+ if (isMainModule(import.meta.url)) {
1484
+ run().catch((error) => {
1485
+ process.stderr.write(`AgentSpine: ${error.message}\n`);
1486
+ process.exitCode = 1;
1487
+ });
1488
+ }