blun-king-cli 9.1.486 → 9.1.488

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 (97) hide show
  1. package/LIESMICH.txt +26 -7
  2. package/README.md +14 -3
  3. package/agent-spine-plugin/.claude-plugin/marketplace.json +20 -0
  4. package/agent-spine-plugin/.claude-plugin/plugin.json +14 -0
  5. package/agent-spine-plugin/.codex-plugin/plugin.json +34 -0
  6. package/agent-spine-plugin/.mcp.json +8 -0
  7. package/agent-spine-plugin/CHANGELOG.md +277 -0
  8. package/agent-spine-plugin/LICENSE +186 -0
  9. package/agent-spine-plugin/README.md +342 -0
  10. package/agent-spine-plugin/assets/agentspine-banner.svg +32 -0
  11. package/agent-spine-plugin/bin/agentspine-mcp.js +4 -0
  12. package/agent-spine-plugin/bin/agentspine.js +7 -0
  13. package/agent-spine-plugin/blun.plugin.json +33 -0
  14. package/agent-spine-plugin/docs/acceptance.md +61 -0
  15. package/agent-spine-plugin/docs/architecture.md +183 -0
  16. package/agent-spine-plugin/docs/attention.md +121 -0
  17. package/agent-spine-plugin/docs/automatic-continuity.md +79 -0
  18. package/agent-spine-plugin/docs/channel-runtime.md +92 -0
  19. package/agent-spine-plugin/docs/coordination.md +138 -0
  20. package/agent-spine-plugin/docs/feed-transport.md +99 -0
  21. package/agent-spine-plugin/docs/gateway-runtime.md +109 -0
  22. package/agent-spine-plugin/docs/harness-reference.md +45 -0
  23. package/agent-spine-plugin/docs/host-integration.md +129 -0
  24. package/agent-spine-plugin/docs/https-transport.md +116 -0
  25. package/agent-spine-plugin/docs/learning.md +98 -0
  26. package/agent-spine-plugin/docs/object-transport.md +93 -0
  27. package/agent-spine-plugin/docs/peer-transport.md +88 -0
  28. package/agent-spine-plugin/docs/preservation-contract.md +53 -0
  29. package/agent-spine-plugin/docs/quality-gates.md +50 -0
  30. package/agent-spine-plugin/docs/relationships.md +67 -0
  31. package/agent-spine-plugin/docs/releasing.md +83 -0
  32. package/agent-spine-plugin/docs/roadmap.md +307 -0
  33. package/agent-spine-plugin/docs/selfstarter.md +88 -0
  34. package/agent-spine-plugin/docs/session-briefing.md +74 -0
  35. package/agent-spine-plugin/docs/shared-memory.md +259 -0
  36. package/agent-spine-plugin/docs/source-roots.md +86 -0
  37. package/agent-spine-plugin/docs/sqlite-transport.md +76 -0
  38. package/agent-spine-plugin/hooks/hooks.json +95 -0
  39. package/agent-spine-plugin/hooks/version.json +5 -0
  40. package/agent-spine-plugin/package.json +69 -0
  41. package/agent-spine-plugin/scripts/check-hosts.js +193 -0
  42. package/agent-spine-plugin/scripts/check-install.js +551 -0
  43. package/agent-spine-plugin/scripts/check-syntax.js +29 -0
  44. package/agent-spine-plugin/scripts/github-actions.js +11 -0
  45. package/agent-spine-plugin/scripts/release-check.js +127 -0
  46. package/agent-spine-plugin/scripts/run-acceptance.js +19 -0
  47. package/agent-spine-plugin/scripts/run-checks.js +46 -0
  48. package/agent-spine-plugin/scripts/run-tests-hermetic.js +73 -0
  49. package/agent-spine-plugin/skill/SKILL.md +76 -0
  50. package/agent-spine-plugin/skills/agent-spine/SKILL.md +84 -0
  51. package/agent-spine-plugin/spine-example/1-identity.md +12 -0
  52. package/agent-spine-plugin/spine-example/2-voice.md +6 -0
  53. package/agent-spine-plugin/spine-example/3-conduct.md +8 -0
  54. package/agent-spine-plugin/spine-example/4-history.md +4 -0
  55. package/agent-spine-plugin/src/cli.js +950 -0
  56. package/agent-spine-plugin/src/hook.js +646 -0
  57. package/agent-spine-plugin/src/index.js +84 -0
  58. package/agent-spine-plugin/src/lib/acceptance.js +294 -0
  59. package/agent-spine-plugin/src/lib/attention.js +755 -0
  60. package/agent-spine-plugin/src/lib/audit.js +308 -0
  61. package/agent-spine-plugin/src/lib/authentication.js +513 -0
  62. package/agent-spine-plugin/src/lib/briefing.js +305 -0
  63. package/agent-spine-plugin/src/lib/catalog.js +142 -0
  64. package/agent-spine-plugin/src/lib/channel-runtime.js +665 -0
  65. package/agent-spine-plugin/src/lib/context.js +154 -0
  66. package/agent-spine-plugin/src/lib/continuity.js +333 -0
  67. package/agent-spine-plugin/src/lib/coordination.js +577 -0
  68. package/agent-spine-plugin/src/lib/documents.js +199 -0
  69. package/agent-spine-plugin/src/lib/feed-transport.js +497 -0
  70. package/agent-spine-plugin/src/lib/filesystem-retry.js +27 -0
  71. package/agent-spine-plugin/src/lib/gateway-runtime.js +760 -0
  72. package/agent-spine-plugin/src/lib/graph.js +284 -0
  73. package/agent-spine-plugin/src/lib/https-transport.js +392 -0
  74. package/agent-spine-plugin/src/lib/indexed-memory-offline.js +40 -0
  75. package/agent-spine-plugin/src/lib/indexed-memory.js +281 -0
  76. package/agent-spine-plugin/src/lib/learning.js +645 -0
  77. package/agent-spine-plugin/src/lib/object-transport.js +206 -0
  78. package/agent-spine-plugin/src/lib/paths.js +60 -0
  79. package/agent-spine-plugin/src/lib/peer-transport.js +283 -0
  80. package/agent-spine-plugin/src/lib/persona-runtime.js +503 -0
  81. package/agent-spine-plugin/src/lib/runtime.js +13 -0
  82. package/agent-spine-plugin/src/lib/selfstarter.js +819 -0
  83. package/agent-spine-plugin/src/lib/sharing.js +969 -0
  84. package/agent-spine-plugin/src/lib/source-roots.js +451 -0
  85. package/agent-spine-plugin/src/lib/sqlite-transport.js +501 -0
  86. package/agent-spine-plugin/src/lib/telegram-adapter.js +119 -0
  87. package/agent-spine-plugin/src/lib/voice-runtime.js +39 -0
  88. package/agent-spine-plugin/src/mcp.js +526 -0
  89. package/agent-spine-plugin/src/version.js +1 -0
  90. package/agent-spine-plugin/src/worker.js +178 -0
  91. package/bin/identity-context-policy.cjs +51 -1
  92. package/bin/launcher-runtime.js +9 -1
  93. package/bin/plugin-bootstrap.js +68 -27
  94. package/bin/turn-thinking-policy.cjs +5 -4
  95. package/bin/turn-tool-performance-policy.cjs +15 -2
  96. package/blun.mjs +96 -31
  97. package/package.json +2 -1
@@ -0,0 +1,950 @@
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, configureLearning, deleteLearning, evaluateLearning,
12
+ learningContext, loadLearning, proposeLearning, reviewLearning, rollbackLearning
13
+ } from "./lib/learning.js";
14
+ import { configureContinuity, loadContinuity, purgeContinuity } from "./lib/continuity.js";
15
+ import {
16
+ checkDelegation, createTask, deleteTask, grantDelegation, loadDelegationPolicy,
17
+ revokeDelegation, taskContext, updateTask
18
+ } from "./lib/coordination.js";
19
+ import {
20
+ cancelJob, deleteJob, grantExecution, loadExecutionPolicy, registerJob,
21
+ revokeExecution, selfstarterContext
22
+ } from "./lib/selfstarter.js";
23
+ import {
24
+ channelRuntimeContext, grantChannelBinding, loadChannelPolicy, revokeChannelBinding
25
+ } from "./lib/channel-runtime.js";
26
+ import { personaContext, syncPersonaRosterFromEnvironment } from "./lib/persona-runtime.js";
27
+ import { assignGoal, gatewayContext, setGatewayControl } from "./lib/gateway-runtime.js";
28
+ import {
29
+ configureSharing, deleteShared, initDirectoryAdapter, publishLearning, pullShared,
30
+ reviewShared, rollbackShared, sharedContext, sharedInbox
31
+ } from "./lib/sharing.js";
32
+ import {
33
+ generateSigningIdentity, listSigningIdentities, revokeTrustedSigner,
34
+ trustedSignerContext, trustSigner
35
+ } from "./lib/authentication.js";
36
+ import { exportHttpsSnapshot, pullHttpsSnapshot } from "./lib/https-transport.js";
37
+ import { publishHttpsSnapshot } from "./lib/object-transport.js";
38
+ import { loadHttpsFeedState, publishHttpsFeed, pullHttpsFeed } from "./lib/feed-transport.js";
39
+ import { pullPeerCommand, servePeerOnce } from "./lib/peer-transport.js";
40
+ import {
41
+ initSqliteAdapter, inspectSqliteAdapter, publishSqliteSnapshot, pullSqliteSnapshot
42
+ } from "./lib/sqlite-transport.js";
43
+ import {
44
+ annotateDocument, linkDocuments, linkEntities,
45
+ relationshipContext, upsertEntity
46
+ } from "./lib/graph.js";
47
+ import { checkHosts } from "../scripts/check-hosts.js";
48
+ import { renderAcceptanceReport, runVisibleAcceptance } from "./lib/acceptance.js";
49
+ import {
50
+ bindSourceRoot, inspectSourceRegistry, purgeSourceBinding, resolveHostSourceCatalog,
51
+ rollbackSourceBinding
52
+ } from "./lib/source-roots.js";
53
+ import { VERSION } from "./version.js";
54
+ import { isMainModule } from "./lib/runtime.js";
55
+ import { scanIndexedMemoryOrphans } from "./lib/indexed-memory-offline.js";
56
+
57
+ function parse(argv) {
58
+ const [command = "help", ...rest] = argv;
59
+ const flags = {};
60
+ const positional = [];
61
+ for (let index = 0; index < rest.length; index += 1) {
62
+ const token = rest[index];
63
+ if (!token.startsWith("--")) {
64
+ positional.push(token);
65
+ continue;
66
+ }
67
+ const [name, inline] = token.slice(2).split("=", 2);
68
+ if (inline !== undefined) flags[name] = inline;
69
+ else if (rest[index + 1] && !rest[index + 1].startsWith("--")) flags[name] = rest[++index];
70
+ else flags[name] = true;
71
+ }
72
+ return { command, flags, positional };
73
+ }
74
+
75
+ function output(value, json = false) {
76
+ if (json) {
77
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
78
+ return;
79
+ }
80
+ if (typeof value === "string") process.stdout.write(`${value}\n`);
81
+ else process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
82
+ }
83
+
84
+ function booleanFlag(value, fallback = false) {
85
+ if (value === undefined) return fallback;
86
+ if (value === true || value === "true" || value === "1") return true;
87
+ if (value === false || value === "false" || value === "0") return false;
88
+ throw new Error(`expected boolean flag, received: ${value}`);
89
+ }
90
+
91
+ function help() {
92
+ return `AgentSpine ${VERSION}
93
+
94
+ Usage:
95
+ agentspine scan [root] [--json]
96
+ agentspine context [root] [--cwd path] [--host codex|claude|generic] [--max-bytes n] [--json]
97
+ 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]
98
+ agentspine read <relative-path> [--root path] [--offset n] [--length n] [--json]
99
+ agentspine verify [root] [--json]
100
+ agentspine link <from.md> <to.md> --relation related [--reason text] [--confidence 0.8]
101
+ agentspine annotate <path.md> --layer soul [--reason text] [--confidence 0.8]
102
+ agentspine entity <id> --kind person [--name text] [--privacy private]
103
+ agentspine relate <from> <to> --relation works-with [--privacy private]
104
+ agentspine relationships <entity-id> [--group id] [--include-private] [--json]
105
+ agentspine attention [root] [--group id] [--include-private] [--focus-active] [--mark-presented]
106
+ agentspine attention-events [root] [--include-history]
107
+ agentspine attention-add [id] --kind promise --summary text [--entity id] [--group id] [--due date]
108
+ agentspine attention-resolve <id> [--status completed|dismissed|open]
109
+ agentspine attention-touch <entity-id> [--kind interaction] [--at date]
110
+ agentspine attention-delete <signal-id>
111
+ agentspine attention-event-delete <event-id>
112
+ agentspine attention-purge <entity-id>
113
+ agentspine attention-config [root] [--enabled true|false] [--quiet-start 22 --quiet-end 7 --utc-offset 120]
114
+ agentspine learn-propose [id] --kind preference --claim text --evidence text
115
+ agentspine learn-evidence <id> --summary text [--type interaction] [--source path.md]
116
+ agentspine learn-review <id> --decision accept|reject --reason text [--confirmed-by-user]
117
+ agentspine learn-context [root] [--group id] [--include-private] [--kind preference,goal]
118
+ agentspine learn-evaluate [root]
119
+ agentspine learn-rollback <id> --reason text
120
+ agentspine learn-delete <id>
121
+ agentspine learn-config [root] [--auto-promote true|false] [--min-confidence 0.85]
122
+ agentspine continuity-config [root] [--enabled true|false] [--entity id] [--project id] [--confirm-local-opt-in]
123
+ agentspine continuity-status [root]
124
+ agentspine continuity-purge <entity-id> [--root path] --confirm-local-purge
125
+ agentspine source-status --host claude|codex [--cwd path]
126
+ agentspine doctor --host claude [--cwd path] [--offline-memory-orphans]
127
+ agentspine source-bind <state-root> --host all|claude|codex --scope state-user --project path --host-home path --confirm-local-binding
128
+ agentspine source-rollback <binding-id> --confirm-local-binding
129
+ agentspine source-purge <binding-id> --confirm-local-binding
130
+ agentspine delegation-grant <actor-id> --actions assign,manage --targets agent:id --reason text [--confirm-local-policy]
131
+ agentspine delegation-revoke <grant-id> --reason text [--confirm-local-policy]
132
+ agentspine delegation-check <actor-id> --action assign [--target agent:id]
133
+ agentspine delegation-policy [root]
134
+ agentspine task-create [id] --actor id --title text [--assignee id] [--kind task|open-thread|handoff] [--privacy private|shared|group]
135
+ agentspine task-update <id> --actor id [--status in-progress] [--assignee id|--unassign]
136
+ agentspine tasks [root] [--assignee id] [--project id] [--include-private] [--group id]
137
+ agentspine task-delete <id> [--confirm-local-policy]
138
+ 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
139
+ agentspine execution-revoke <grant-id> --reason text --confirm-local-execution
140
+ agentspine execution-policy [root]
141
+ agentspine job-register <job-id> --grant grant:id [--max-retries 3] [--lease-seconds 120] --confirm-local-execution
142
+ agentspine jobs [root] [--actor id] [--project id] [--task id] [--include-terminal]
143
+ agentspine job-cancel <job-id> --reason text --confirm-local-execution
144
+ agentspine job-delete <job-id> --confirm-local-execution
145
+ 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
146
+ agentspine channel-revoke <binding-id> --reason text --confirm-local-channel
147
+ agentspine channel-policy [root]
148
+ agentspine channel-events [root] [--agent id] [--project id] [--group id] [--provider telegram] [--include-terminal]
149
+ agentspine persona-sync [root] --roster absolute-path --confirm-local-persona
150
+ agentspine personas [root] [--persona id] [--group id] [--include-inactive]
151
+ agentspine goal-assign <goal-id> --agent id --owner id --project id --success text --next-step text [--group id] [--deadline date] --confirm-local-goal
152
+ agentspine gateway-control [root] [--enabled true|false] [--kill-switch true|false] --confirm-local-gateway
153
+ agentspine gateway-status [root] [--agent id]
154
+ agentspine share-keygen <signer-id> [--public-out signer.json] [--rotate] [--confirm-local-share]
155
+ agentspine share-signers [root]
156
+ agentspine share-trust <signer.json> [--root path] [--confirm-local-share]
157
+ agentspine share-trust-revoke <key-id> --reason text [--root path] [--confirm-local-share]
158
+ agentspine share-trust-list [root] [--include-revoked]
159
+ agentspine share-init <directory> --scope team:id [--adapter adapter:id] [--signer signer:id] [--confirm-local-share]
160
+ agentspine share-publish <directory> --learning id [--id shared:id] [--signer signer:id] [--supersedes shared:id] [--confirm-local-share]
161
+ agentspine share-pull <directory> [--root path] [--require-authenticated]
162
+ agentspine share-snapshot-export <directory> --out snapshot.json [--id snapshot:id] [--confirm-local-share]
163
+ 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
164
+ agentspine share-https-pull <https-url> [--token-env VARIABLE] [--timeout-ms 10000] [--allow-private-network --confirm-local-share]
165
+ 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
166
+ agentspine share-feed-pull --base https://store.example/spine --feed team:id [--root path] [--token-env VARIABLE] [--allow-private-network --confirm-local-share]
167
+ agentspine share-feed-state [root]
168
+ agentspine share-peer-serve <directory> --root path --signer signer:id [--timeout-ms 10000] --confirm-local-share
169
+ agentspine share-peer-pull --root path --command-json '["ssh","host","agentspine",…]' [--timeout-ms 10000] [--max-bytes n] --confirm-local-share
170
+ agentspine share-sqlite-init <directory> --database path --confirm-local-share
171
+ agentspine share-sqlite-publish <directory> --database path [--id snapshot:id] --confirm-local-share
172
+ agentspine share-sqlite-inspect --database path [--root path]
173
+ agentspine share-sqlite-pull --database path [--root path]
174
+ agentspine share-inbox [root] [--status pending|accepted|rejected|superseded|rolled-back]
175
+ agentspine share-review <id> --decision accept|reject --reason text [--confirmed-by-user]
176
+ agentspine share-context [root] [--scope team:id] [--group group:id] [--kind preference,goal]
177
+ agentspine share-rollback <id> --reason text
178
+ agentspine share-delete <id> [--confirm-local-share]
179
+ agentspine share-config [root] --max-items 12
180
+ agentspine audit [root] [--json]
181
+ agentspine acceptance [--json]
182
+ agentspine doctor [--json]
183
+ agentspine mcp
184
+
185
+ AgentSpine reads existing Markdown in place. It never rewrites source documents.`;
186
+ }
187
+
188
+ export async function run(argv = process.argv.slice(2)) {
189
+ const { command, flags, positional } = parse(argv);
190
+ const json = Boolean(flags.json);
191
+ if (command === "help" || command === "--help" || command === "-h") return output(help());
192
+ if (command === "version" || command === "--version" || command === "-v") return output(VERSION);
193
+
194
+ if (command === "acceptance") {
195
+ if (positional.length || Object.keys(flags).some((name) => name !== "json")) {
196
+ throw new Error("acceptance supports only --json");
197
+ }
198
+ const report = await runVisibleAcceptance();
199
+ if (json) return output(report, true);
200
+ process.stdout.write(renderAcceptanceReport(report));
201
+ return;
202
+ }
203
+
204
+ if (command === "scan") {
205
+ const result = await scanAndSave(positional[0] || process.cwd());
206
+ return output(json ? result : {
207
+ root: result.catalog.root,
208
+ catalog: result.catalogPath,
209
+ documents: result.catalog.summary.total,
210
+ protected: result.catalog.summary.protected,
211
+ layers: result.catalog.summary.byLayer
212
+ }, json);
213
+ }
214
+
215
+ if (command === "context") {
216
+ const root = positional[0] || process.cwd();
217
+ const result = await resolveContext({
218
+ root,
219
+ cwd: flags.cwd || root,
220
+ host: flags.host || "generic",
221
+ maxBytes: Number(flags["max-bytes"] || 65536),
222
+ includeContent: flags["no-content"] !== true
223
+ });
224
+ return output(result, json);
225
+ }
226
+
227
+ if (command === "briefing") {
228
+ const root = positional[0] || process.cwd();
229
+ return output(await sessionBriefing({
230
+ root, cwd: flags.cwd || root, host: flags.host || "generic",
231
+ entityId: flags.entity || null, groupId: flags.group || null,
232
+ projectId: flags.project || null, currentTaskId: flags["current-task"] || null,
233
+ includePrivate: booleanFlag(flags["include-private"]),
234
+ focusActive: !booleanFlag(flags["allow-attention"]),
235
+ includeSourceContent: !booleanFlag(flags["no-source-content"]),
236
+ maxBytes: Number(flags["max-bytes"] ?? 16384)
237
+ }), json);
238
+ }
239
+
240
+ if (command === "read") {
241
+ const result = await readDocument({
242
+ root: flags.root || process.cwd(),
243
+ path: positional[0],
244
+ offset: Number(flags.offset || 0),
245
+ length: Number(flags.length || 65536)
246
+ });
247
+ return output(json ? result : result.content, json);
248
+ }
249
+
250
+ if (command === "verify") {
251
+ const result = await verifyCatalog(positional[0] || process.cwd());
252
+ output(result, json);
253
+ if (!result.ok) process.exitCode = 1;
254
+ return;
255
+ }
256
+
257
+ if (command === "link") {
258
+ const result = await linkDocuments({
259
+ root: flags.root || process.cwd(), from: positional[0], to: positional[1],
260
+ relation: flags.relation || "related", reason: flags.reason || "",
261
+ confidence: Number(flags.confidence ?? 0.5)
262
+ });
263
+ return output(result, json);
264
+ }
265
+
266
+ if (command === "annotate") {
267
+ const result = await annotateDocument({
268
+ root: flags.root || process.cwd(), path: positional[0], layer: flags.layer,
269
+ reason: flags.reason || "", confidence: Number(flags.confidence ?? 0.5)
270
+ });
271
+ return output(result, json);
272
+ }
273
+
274
+ if (command === "entity") {
275
+ const result = await upsertEntity({
276
+ root: flags.root || process.cwd(), id: positional[0], kind: flags.kind,
277
+ displayName: flags.name || "", confidence: Number(flags.confidence ?? 0.5),
278
+ privacy: flags.privacy || "private"
279
+ });
280
+ return output(result, json);
281
+ }
282
+
283
+ if (command === "relate") {
284
+ const result = await linkEntities({
285
+ root: flags.root || process.cwd(), from: positional[0], to: positional[1],
286
+ relation: flags.relation || "related", reason: flags.reason || "",
287
+ confidence: Number(flags.confidence ?? 0.5), privacy: flags.privacy || "private"
288
+ });
289
+ return output(result, json);
290
+ }
291
+
292
+ if (command === "relationships") {
293
+ return output(await relationshipContext({
294
+ root: flags.root || process.cwd(), entityId: positional[0],
295
+ includePrivate: Boolean(flags["include-private"]), groupId: flags.group || null
296
+ }), json);
297
+ }
298
+
299
+ if (command === "attention") {
300
+ return output(await attentionContext({
301
+ root: positional[0] || process.cwd(),
302
+ includePrivate: booleanFlag(flags["include-private"]),
303
+ focusActive: booleanFlag(flags["focus-active"]),
304
+ markPresented: booleanFlag(flags["mark-presented"]),
305
+ entityId: flags.entity || null,
306
+ groupId: flags.group || null,
307
+ projectId: flags.project || null,
308
+ currentTaskId: flags.task || null,
309
+ maxItems: flags["max-items"] === undefined ? null : Number(flags["max-items"])
310
+ }), json);
311
+ }
312
+
313
+ if (command === "attention-events") {
314
+ const { attention, attentionPath } = await loadAttention(positional[0] || process.cwd());
315
+ return output({
316
+ schema: "agentspine.attention-events/v1", attentionPath,
317
+ events: attention.events,
318
+ receipts: attention.receipts,
319
+ history: booleanFlag(flags["include-history"]) ? attention.history.filter((item) => item.kind === "attention-event") : [],
320
+ authority: "context-only"
321
+ }, json);
322
+ }
323
+
324
+ if (command === "attention-add") {
325
+ return output(await upsertAttention({
326
+ root: flags.root || process.cwd(), id: positional[0], kind: flags.kind,
327
+ summary: flags.summary, entityId: flags.entity || null, dueAt: flags.due || null,
328
+ priority: Number(flags.priority ?? 50), privacy: flags.privacy || "private",
329
+ groupId: flags.group || null,
330
+ sourceDocument: flags.source || null, confidence: Number(flags.confidence ?? 0.5)
331
+ }), json);
332
+ }
333
+
334
+ if (command === "attention-resolve") {
335
+ return output(await resolveAttention({
336
+ root: flags.root || process.cwd(), id: positional[0], status: flags.status || "completed"
337
+ }), json);
338
+ }
339
+
340
+ if (command === "attention-touch") {
341
+ return output(await recordActivity({
342
+ root: flags.root || process.cwd(), entityId: positional[0], kind: flags.kind || "interaction",
343
+ at: flags.at || new Date(), privacy: flags.privacy || "private", groupId: flags.group || null
344
+ }), json);
345
+ }
346
+
347
+ if (command === "attention-delete") {
348
+ return output(await deleteAttention({ root: flags.root || process.cwd(), signalId: positional[0] }), json);
349
+ }
350
+
351
+ if (command === "attention-event-delete") {
352
+ return output(await deleteAttention({ root: flags.root || process.cwd(), eventId: positional[0] }), json);
353
+ }
354
+
355
+ if (command === "attention-purge") {
356
+ return output(await deleteAttention({ root: flags.root || process.cwd(), entityId: positional[0] }), json);
357
+ }
358
+
359
+ if (command === "attention-config") {
360
+ const root = positional[0] || process.cwd();
361
+ const config = {};
362
+ if (flags.enabled !== undefined) config.enabled = booleanFlag(flags.enabled);
363
+ if (flags["min-interval-hours"] !== undefined) config.minIntervalHours = Number(flags["min-interval-hours"]);
364
+ if (flags["silence-days"] !== undefined) config.entitySilenceDays = Number(flags["silence-days"]);
365
+ if (flags["heartbeat-stale-minutes"] !== undefined) config.heartbeatStaleMinutes = Number(flags["heartbeat-stale-minutes"]);
366
+ if (flags["max-items"] !== undefined) config.maxItems = Number(flags["max-items"]);
367
+ if (flags["quiet-off"] !== undefined) config.quietHours = null;
368
+ else if (flags["quiet-start"] !== undefined || flags["quiet-end"] !== undefined) {
369
+ config.quietHours = {
370
+ start: Number(flags["quiet-start"]), end: Number(flags["quiet-end"]),
371
+ utcOffsetMinutes: Number(flags["utc-offset"] ?? 0)
372
+ };
373
+ }
374
+ if (!Object.keys(config).length) return output((await loadAttention(root)).attention.config, json);
375
+ return output(await configureAttention({ root, config }), json);
376
+ }
377
+
378
+ if (command === "learn-propose") {
379
+ return output(await proposeLearning({
380
+ root: flags.root || process.cwd(), id: positional[0], kind: flags.kind,
381
+ claim: flags.claim, subjectId: flags.subject || null, privacy: flags.privacy || "private",
382
+ groupId: flags.group || null, supersedesId: flags.supersedes || null,
383
+ evidence: {
384
+ id: flags["evidence-id"], type: flags["evidence-type"] || "user-statement",
385
+ summary: flags.evidence, sourceDocument: flags.source || null,
386
+ confidence: Number(flags.confidence ?? 0.5), observedAt: flags.at
387
+ }
388
+ }), json);
389
+ }
390
+
391
+ if (command === "learn-evidence") {
392
+ return output(await addLearningEvidence({
393
+ root: flags.root || process.cwd(), id: positional[0],
394
+ evidence: {
395
+ id: flags["evidence-id"], type: flags.type || "interaction", summary: flags.summary,
396
+ sourceDocument: flags.source || null, confidence: Number(flags.confidence ?? 0.5), observedAt: flags.at
397
+ }
398
+ }), json);
399
+ }
400
+
401
+ if (command === "learn-review") {
402
+ return output(await reviewLearning({
403
+ root: flags.root || process.cwd(), id: positional[0], decision: flags.decision,
404
+ reason: flags.reason, confirmedByUser: booleanFlag(flags["confirmed-by-user"])
405
+ }), json);
406
+ }
407
+
408
+ if (command === "learn-context") {
409
+ return output(await learningContext({
410
+ root: positional[0] || process.cwd(), includePrivate: booleanFlag(flags["include-private"]),
411
+ groupId: flags.group || null,
412
+ kinds: flags.kind ? String(flags.kind).split(",").filter(Boolean) : null,
413
+ subjectIds: flags.subject ? String(flags.subject).split(",").filter(Boolean) : null,
414
+ maxItems: flags["max-items"] === undefined ? null : Number(flags["max-items"])
415
+ }), json);
416
+ }
417
+
418
+ if (command === "learn-evaluate") {
419
+ return output(await evaluateLearning({ root: positional[0] || process.cwd() }), json);
420
+ }
421
+
422
+ if (command === "learn-rollback") {
423
+ return output(await rollbackLearning({
424
+ root: flags.root || process.cwd(), id: positional[0], reason: flags.reason
425
+ }), json);
426
+ }
427
+
428
+ if (command === "learn-delete") {
429
+ return output(await deleteLearning({ root: flags.root || process.cwd(), id: positional[0] }), json);
430
+ }
431
+
432
+ if (command === "learn-config") {
433
+ const root = positional[0] || process.cwd();
434
+ const config = {};
435
+ if (flags["auto-promote"] !== undefined) config.autoPromote = booleanFlag(flags["auto-promote"]);
436
+ if (flags["min-confidence"] !== undefined) config.minConfidence = Number(flags["min-confidence"]);
437
+ if (flags["min-evidence"] !== undefined) config.minEvidence = Number(flags["min-evidence"]);
438
+ if (flags["max-items"] !== undefined) config.maxContextItems = Number(flags["max-items"]);
439
+ if (!Object.keys(config).length) return output((await loadLearning(root)).learning.config, json);
440
+ return output(await configureLearning({ root, config }), json);
441
+ }
442
+
443
+ if (command === "continuity-config") {
444
+ const root = positional[0] || process.cwd();
445
+ const config = {};
446
+ if (flags.enabled !== undefined) config.enabled = booleanFlag(flags.enabled);
447
+ if (flags.entity !== undefined) config.defaultEntityId = flags.entity === "none" ? null : flags.entity;
448
+ if (flags.project !== undefined) config.defaultProjectId = flags.project === "none" ? null : flags.project;
449
+ if (flags["min-confidence"] !== undefined) config.minConfidence = Number(flags["min-confidence"]);
450
+ if (flags["min-directness"] !== undefined) config.minDirectness = Number(flags["min-directness"]);
451
+ if (flags["min-evidence"] !== undefined) config.minEvidence = Number(flags["min-evidence"]);
452
+ if (flags["max-prompt-bytes"] !== undefined) config.maxPromptBytes = Number(flags["max-prompt-bytes"]);
453
+ if (flags["max-briefing-bytes"] !== undefined) config.maxBriefingBytes = Number(flags["max-briefing-bytes"]);
454
+ if (!Object.keys(config).length) return output((await loadContinuity(root)).continuity.config, json);
455
+ return output(await configureContinuity({
456
+ root, config,
457
+ confirmation: booleanFlag(flags["confirm-local-opt-in"]) ? "local-user-opt-in" : null
458
+ }), json);
459
+ }
460
+
461
+ if (command === "continuity-status") {
462
+ const { continuity, continuityPath } = await loadContinuity(positional[0] || process.cwd());
463
+ return output({
464
+ schema: continuity.schema, config: continuity.config,
465
+ signals: continuity.signals.length, history: continuity.history.length,
466
+ continuityPath, authority: "context-only"
467
+ }, json);
468
+ }
469
+
470
+ if (command === "continuity-purge") {
471
+ return output(await purgeContinuity({
472
+ root: flags.root || process.cwd(), subjectId: positional[0],
473
+ confirmation: booleanFlag(flags["confirm-local-purge"]) ? "local-user-confirmed" : null
474
+ }), json);
475
+ }
476
+
477
+ if (command === "source-status") {
478
+ const host = flags.host || process.env.AGENTSPINE_HOST;
479
+ if (!["claude", "codex"].includes(host)) throw new Error("source-status requires --host claude or --host codex");
480
+ const resolved = await resolveHostSourceCatalog({ host, cwd: flags.cwd || process.cwd() });
481
+ const registry = await inspectSourceRegistry();
482
+ return output({ diagnostics: resolved.diagnostics, sources: resolved.catalog.documents.map((item) => ({
483
+ id: item.relativePath, scope: item.sourceScope, binding: item.sourceBinding, sha256: item.sha256,
484
+ bytes: item.bytes, authority: item.authority
485
+ })), bindings: registry.registry.bindings, authority: "context-only" }, json);
486
+ }
487
+
488
+ if (command === "source-bind") {
489
+ return output(await bindSourceRoot({
490
+ host: flags.host, hostHome: flags["host-home"] || process.env.CLAUDE_CONFIG_DIR || process.env.CODEX_HOME || process.cwd(),
491
+ projectRoot: flags.project || process.cwd(), sourceRoot: positional[0], scope: flags.scope || "state-user",
492
+ confirmation: booleanFlag(flags["confirm-local-binding"]) ? "local-user-confirmed" : null
493
+ }), json);
494
+ }
495
+
496
+ if (command === "source-rollback") {
497
+ return output(await rollbackSourceBinding({ id: positional[0],
498
+ confirmation: booleanFlag(flags["confirm-local-binding"]) ? "local-user-confirmed" : null }), json);
499
+ }
500
+
501
+ if (command === "source-purge") {
502
+ return output(await purgeSourceBinding({ id: positional[0],
503
+ confirmation: booleanFlag(flags["confirm-local-binding"]) ? "local-user-confirmed" : null }), json);
504
+ }
505
+
506
+ if (command === "delegation-grant") {
507
+ return output(await grantDelegation({
508
+ root: flags.root || process.cwd(), actorId: positional[0],
509
+ id: flags.id, actions: String(flags.actions || "").split(",").filter(Boolean),
510
+ targetIds: String(flags.targets || "*").split(",").filter(Boolean), reason: flags.reason,
511
+ confirmation: booleanFlag(flags["confirm-local-policy"]) ? "local-owner-confirmed" : null
512
+ }), json);
513
+ }
514
+
515
+ if (command === "delegation-revoke") {
516
+ return output(await revokeDelegation({
517
+ root: flags.root || process.cwd(), id: positional[0], reason: flags.reason,
518
+ confirmation: booleanFlag(flags["confirm-local-policy"]) ? "local-owner-confirmed" : null
519
+ }), json);
520
+ }
521
+
522
+ if (command === "delegation-check") {
523
+ return output(await checkDelegation({
524
+ root: flags.root || process.cwd(), actorId: positional[0], action: flags.action,
525
+ targetId: flags.target || null
526
+ }), json);
527
+ }
528
+
529
+ if (command === "delegation-policy") {
530
+ return output((await loadDelegationPolicy(positional[0] || process.cwd())).policy, json);
531
+ }
532
+
533
+ if (command === "task-create") {
534
+ return output(await createTask({
535
+ root: flags.root || process.cwd(), id: positional[0], actorId: flags.actor,
536
+ assigneeId: flags.assignee || null, kind: flags.kind || "task", title: flags.title,
537
+ summary: flags.summary || null, projectId: flags.project || null,
538
+ privacy: flags.privacy || "private", groupId: flags.group || null,
539
+ priority: Number(flags.priority ?? 50), dueAt: flags.due || null
540
+ }), json);
541
+ }
542
+
543
+ if (command === "task-update") {
544
+ const patch = {};
545
+ if (flags.status !== undefined) patch.status = flags.status;
546
+ if (flags.assignee !== undefined) patch.assigneeId = flags.assignee;
547
+ if (flags.unassign !== undefined) patch.assigneeId = null;
548
+ if (flags.title !== undefined) patch.title = flags.title;
549
+ if (flags.summary !== undefined) patch.summary = flags.summary;
550
+ if (flags.priority !== undefined) patch.priority = Number(flags.priority);
551
+ if (flags.due !== undefined) patch.dueAt = flags.due;
552
+ if (flags["clear-due"] !== undefined) patch.dueAt = null;
553
+ if (flags.note !== undefined) patch.note = flags.note;
554
+ return output(await updateTask({
555
+ root: flags.root || process.cwd(), id: positional[0], actorId: flags.actor, patch
556
+ }), json);
557
+ }
558
+
559
+ if (command === "tasks") {
560
+ return output(await taskContext({
561
+ root: positional[0] || process.cwd(), includePrivate: booleanFlag(flags["include-private"]),
562
+ groupId: flags.group || null, actorId: flags.actor || null, assigneeId: flags.assignee || null,
563
+ projectId: flags.project || null, includeClosed: booleanFlag(flags.closed),
564
+ maxItems: Number(flags["max-items"] ?? 20)
565
+ }), json);
566
+ }
567
+
568
+ if (command === "task-delete") {
569
+ return output(await deleteTask({
570
+ root: flags.root || process.cwd(), id: positional[0],
571
+ confirmation: booleanFlag(flags["confirm-local-policy"]) ? "local-owner-confirmed" : null
572
+ }), json);
573
+ }
574
+
575
+ if (command === "execution-grant") {
576
+ return output(await grantExecution({
577
+ root: flags.root || process.cwd(), id: flags.id, jobId: positional[0], actorId: flags.actor,
578
+ taskId: flags.task, targetId: flags.target, projectId: flags.project, groupId: flags.group || null,
579
+ host: flags.host, capabilities: String(flags.capabilities || "").split(",").filter(Boolean),
580
+ reason: flags.reason, expiresAt: flags.expires || null,
581
+ confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
582
+ }), json);
583
+ }
584
+
585
+ if (command === "execution-revoke") {
586
+ return output(await revokeExecution({
587
+ root: flags.root || process.cwd(), id: positional[0], reason: flags.reason,
588
+ confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
589
+ }), json);
590
+ }
591
+
592
+ if (command === "execution-policy") {
593
+ return output((await loadExecutionPolicy(positional[0] || process.cwd())).policy, json);
594
+ }
595
+
596
+ if (command === "job-register") {
597
+ return output(await registerJob({
598
+ root: flags.root || process.cwd(), id: positional[0], grantId: flags.grant,
599
+ maxRetries: Number(flags["max-retries"] ?? 3), leaseSeconds: Number(flags["lease-seconds"] ?? 120),
600
+ baseRetrySeconds: Number(flags["retry-seconds"] ?? 5),
601
+ confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
602
+ }), json);
603
+ }
604
+
605
+ if (command === "jobs") {
606
+ return output(await selfstarterContext({
607
+ root: positional[0] || process.cwd(), actorId: flags.actor || null,
608
+ projectId: flags.project || null, taskId: flags.task || null,
609
+ includeTerminal: booleanFlag(flags["include-terminal"])
610
+ }), json);
611
+ }
612
+
613
+ if (command === "job-cancel") {
614
+ return output(await cancelJob({
615
+ root: flags.root || process.cwd(), id: positional[0], reason: flags.reason,
616
+ confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
617
+ }), json);
618
+ }
619
+
620
+ if (command === "job-delete") {
621
+ return output(await deleteJob({
622
+ root: flags.root || process.cwd(), id: positional[0],
623
+ confirmation: booleanFlag(flags["confirm-local-execution"]) ? "local-owner-confirmed" : null
624
+ }), json);
625
+ }
626
+
627
+ if (command === "channel-bind") {
628
+ return output(await grantChannelBinding({
629
+ root: flags.root || process.cwd(), id: positional[0], provider: flags.provider,
630
+ tenantId: flags.tenant, accountId: flags.account, chatId: flags.chat,
631
+ threadId: flags.thread || null,
632
+ senderIds: String(flags.senders || "").split(",").filter(Boolean),
633
+ agentId: flags.agent, projectId: flags.project, groupId: flags.group || null,
634
+ sessionKey: flags.session, secretEnv: flags["secret-env"], outboundSecretEnv: flags["outbound-secret-env"] || null,
635
+ capabilities: String(flags.capabilities || "receive,reply").split(",").filter(Boolean),
636
+ confirmation: booleanFlag(flags["confirm-local-channel"]) ? "local-owner-confirmed" : null
637
+ }), json);
638
+ }
639
+
640
+ if (command === "channel-revoke") {
641
+ return output(await revokeChannelBinding({
642
+ root: flags.root || process.cwd(), id: positional[0], reason: flags.reason,
643
+ confirmation: booleanFlag(flags["confirm-local-channel"]) ? "local-owner-confirmed" : null
644
+ }), json);
645
+ }
646
+
647
+ if (command === "channel-policy") {
648
+ return output((await loadChannelPolicy(positional[0] || process.cwd())).policy, json);
649
+ }
650
+
651
+ if (command === "channel-events") {
652
+ return output(await channelRuntimeContext({
653
+ root: positional[0] || process.cwd(), agentId: flags.agent || null,
654
+ projectId: flags.project || null,
655
+ ...(flags.group !== undefined ? { groupId: flags.group || null } : {}),
656
+ provider: flags.provider || null,
657
+ includeTerminal: booleanFlag(flags["include-terminal"]),
658
+ maxItems: Number(flags["max-items"] ?? 20)
659
+ }), json);
660
+ }
661
+
662
+ if (command === "persona-sync") {
663
+ if (!booleanFlag(flags["confirm-local-persona"])) throw new Error("persona synchronization requires --confirm-local-persona");
664
+ return output(await syncPersonaRosterFromEnvironment({
665
+ root: positional[0] || process.cwd(), env: { ...process.env, AGENTSPINE_PERSONA_ROSTER_FILE: flags.roster }
666
+ }), json);
667
+ }
668
+
669
+ if (command === "personas") {
670
+ return output(await personaContext({
671
+ root: positional[0] || process.cwd(), personaId: flags.persona || null,
672
+ ...(flags.group !== undefined ? { groupId: flags.group || null } : {}),
673
+ includeInactive: booleanFlag(flags["include-inactive"])
674
+ }), json);
675
+ }
676
+
677
+ if (command === "goal-assign") {
678
+ return output(await assignGoal({
679
+ root: flags.root || process.cwd(), goalId: positional[0], agentId: flags.agent,
680
+ ownerSubjectId: flags.owner, projectId: flags.project, groupId: flags.group || null,
681
+ priority: Number(flags.priority ?? 70), successCriterion: flags.success,
682
+ nextSafeStep: flags["next-step"], deadline: flags.deadline || null,
683
+ confirmation: booleanFlag(flags["confirm-local-goal"]) ? "local-owner-confirmed" : null
684
+ }), json);
685
+ }
686
+
687
+ if (command === "gateway-control") {
688
+ return output(await setGatewayControl({
689
+ root: positional[0] || process.cwd(),
690
+ ...(flags.enabled !== undefined ? { enabled: booleanFlag(flags.enabled) } : {}),
691
+ ...(flags["kill-switch"] !== undefined ? { killSwitch: booleanFlag(flags["kill-switch"]) } : {}),
692
+ confirmation: booleanFlag(flags["confirm-local-gateway"]) ? "local-owner-confirmed" : null
693
+ }), json);
694
+ }
695
+
696
+ if (command === "gateway-status") {
697
+ return output(await gatewayContext({ root: positional[0] || process.cwd(), agentId: flags.agent || null }), json);
698
+ }
699
+
700
+ if (command === "share-init") {
701
+ return output(await initDirectoryAdapter({
702
+ root: flags.root || process.cwd(), directory: positional[0], scopeId: flags.scope,
703
+ adapterId: flags.adapter, signerId: flags.signer || null,
704
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
705
+ }), json);
706
+ }
707
+
708
+ if (command === "share-keygen") {
709
+ return output(await generateSigningIdentity({
710
+ root: flags.root || process.cwd(), signerId: positional[0], rotate: booleanFlag(flags.rotate),
711
+ publicOut: flags["public-out"] || null,
712
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
713
+ }), json);
714
+ }
715
+
716
+ if (command === "share-signers") {
717
+ return output(await listSigningIdentities({ root: positional[0] || process.cwd() }), json);
718
+ }
719
+
720
+ if (command === "share-trust") {
721
+ return output(await trustSigner({
722
+ root: flags.root || process.cwd(), publicIdentityPath: positional[0],
723
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
724
+ }), json);
725
+ }
726
+
727
+ if (command === "share-trust-revoke") {
728
+ return output(await revokeTrustedSigner({
729
+ root: flags.root || process.cwd(), keyId: positional[0], reason: flags.reason,
730
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
731
+ }), json);
732
+ }
733
+
734
+ if (command === "share-trust-list") {
735
+ return output(await trustedSignerContext({
736
+ root: positional[0] || process.cwd(), includeRevoked: booleanFlag(flags["include-revoked"])
737
+ }), json);
738
+ }
739
+
740
+ if (command === "share-publish") {
741
+ return output(await publishLearning({
742
+ root: flags.root || process.cwd(), directory: positional[0], learningId: flags.learning,
743
+ eventId: flags.id, supersedesEventId: flags.supersedes || null, signerId: flags.signer || null,
744
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
745
+ }), json);
746
+ }
747
+
748
+ if (command === "share-pull") {
749
+ return output(await pullShared({
750
+ root: flags.root || process.cwd(), directory: positional[0],
751
+ requireAuthenticated: booleanFlag(flags["require-authenticated"])
752
+ }), json);
753
+ }
754
+
755
+ if (command === "share-snapshot-export") {
756
+ return output(await exportHttpsSnapshot({
757
+ root: flags.root || process.cwd(), directory: positional[0], output: flags.out,
758
+ snapshotId: flags.id,
759
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
760
+ }), json);
761
+ }
762
+
763
+ if (command === "share-https-publish") {
764
+ return output(await publishHttpsSnapshot({
765
+ root: flags.root || process.cwd(), directory: positional[0], baseUrl: flags.base,
766
+ snapshotId: flags.id, tokenEnv: flags["token-env"] || null,
767
+ timeoutMs: Number(flags["timeout-ms"] ?? 10000),
768
+ allowPrivateNetwork: booleanFlag(flags["allow-private-network"]),
769
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
770
+ }), json);
771
+ }
772
+
773
+ if (command === "share-https-pull") {
774
+ return output(await pullHttpsSnapshot({
775
+ root: flags.root || process.cwd(), url: positional[0], tokenEnv: flags["token-env"] || null,
776
+ timeoutMs: Number(flags["timeout-ms"] ?? 10000),
777
+ allowPrivateNetwork: booleanFlag(flags["allow-private-network"]),
778
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
779
+ }), json);
780
+ }
781
+
782
+ if (command === "share-feed-publish") {
783
+ return output(await publishHttpsFeed({
784
+ root: flags.root || process.cwd(), directory: positional[0], baseUrl: flags.base,
785
+ feedId: flags.feed, signerId: flags.signer, snapshotId: flags.id,
786
+ tokenEnv: flags["token-env"] || null, timeoutMs: Number(flags["timeout-ms"] ?? 10000),
787
+ allowPrivateNetwork: booleanFlag(flags["allow-private-network"]),
788
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
789
+ }), json);
790
+ }
791
+
792
+ if (command === "share-feed-pull") {
793
+ return output(await pullHttpsFeed({
794
+ root: flags.root || process.cwd(), baseUrl: flags.base, feedId: flags.feed,
795
+ tokenEnv: flags["token-env"] || null, timeoutMs: Number(flags["timeout-ms"] ?? 10000),
796
+ allowPrivateNetwork: booleanFlag(flags["allow-private-network"]),
797
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
798
+ }), json);
799
+ }
800
+
801
+ if (command === "share-feed-state") {
802
+ return output(await loadHttpsFeedState(positional[0] || process.cwd()), json);
803
+ }
804
+
805
+ if (command === "share-peer-serve") {
806
+ await servePeerOnce({
807
+ root: flags.root || process.cwd(), directory: positional[0], signerId: flags.signer,
808
+ input: process.stdin, output: process.stdout,
809
+ timeoutMs: Number(flags["timeout-ms"] ?? 10000),
810
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
811
+ });
812
+ return;
813
+ }
814
+
815
+ if (command === "share-peer-pull") {
816
+ return output(await pullPeerCommand({
817
+ root: flags.root || process.cwd(), commandJson: flags["command-json"],
818
+ timeoutMs: Number(flags["timeout-ms"] ?? 10000),
819
+ maxBytes: Number(flags["max-bytes"] ?? 22 * 1024 * 1024),
820
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
821
+ }), json);
822
+ }
823
+
824
+ if (command === "share-sqlite-init") {
825
+ return output(await initSqliteAdapter({
826
+ root: flags.root || process.cwd(), directory: positional[0], database: flags.database,
827
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
828
+ }), json);
829
+ }
830
+
831
+ if (command === "share-sqlite-publish") {
832
+ return output(await publishSqliteSnapshot({
833
+ root: flags.root || process.cwd(), directory: positional[0], database: flags.database,
834
+ snapshotId: flags.id,
835
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
836
+ }), json);
837
+ }
838
+
839
+ if (command === "share-sqlite-inspect") {
840
+ return output(await inspectSqliteAdapter({
841
+ root: flags.root || process.cwd(), database: flags.database
842
+ }), json);
843
+ }
844
+
845
+ if (command === "share-sqlite-pull") {
846
+ return output(await pullSqliteSnapshot({
847
+ root: flags.root || process.cwd(), database: flags.database
848
+ }), json);
849
+ }
850
+
851
+ if (command === "share-inbox") {
852
+ return output(await sharedInbox({ root: positional[0] || process.cwd(), status: flags.status || "pending" }), json);
853
+ }
854
+
855
+ if (command === "share-review") {
856
+ return output(await reviewShared({
857
+ root: flags.root || process.cwd(), id: positional[0], decision: flags.decision,
858
+ reason: flags.reason, confirmedByUser: booleanFlag(flags["confirmed-by-user"])
859
+ }), json);
860
+ }
861
+
862
+ if (command === "share-context") {
863
+ return output(await sharedContext({
864
+ root: positional[0] || process.cwd(), scopeId: flags.scope || null, groupId: flags.group || null,
865
+ includePrivate: booleanFlag(flags["include-private"]),
866
+ kinds: flags.kind ? String(flags.kind).split(",").filter(Boolean) : null,
867
+ subjectIds: flags.subject ? String(flags.subject).split(",").filter(Boolean) : null,
868
+ maxItems: flags["max-items"] === undefined ? null : Number(flags["max-items"])
869
+ }), json);
870
+ }
871
+
872
+ if (command === "share-rollback") {
873
+ return output(await rollbackShared({
874
+ root: flags.root || process.cwd(), id: positional[0], reason: flags.reason
875
+ }), json);
876
+ }
877
+
878
+ if (command === "share-delete") {
879
+ return output(await deleteShared({
880
+ root: flags.root || process.cwd(), id: positional[0],
881
+ confirmation: booleanFlag(flags["confirm-local-share"]) ? "local-share-confirmed" : null
882
+ }), json);
883
+ }
884
+
885
+ if (command === "share-config") {
886
+ return output(await configureSharing({
887
+ root: positional[0] || process.cwd(), maxContextItems: Number(flags["max-items"])
888
+ }), json);
889
+ }
890
+
891
+ if (command === "doctor") {
892
+ let hostIntegration;
893
+ try {
894
+ hostIntegration = await checkHosts(fileURLToPath(new URL("..", import.meta.url)));
895
+ } catch (error) {
896
+ hostIntegration = { ok: false, error: error.message };
897
+ }
898
+ let sourceResolution = null;
899
+ let orphanScan = null;
900
+ const sourceHost = flags.host || process.env.AGENTSPINE_HOST;
901
+ if (["claude", "codex"].includes(sourceHost)) {
902
+ try {
903
+ const resolved = await resolveHostSourceCatalog({ host: sourceHost, cwd: flags.cwd || process.cwd() });
904
+ sourceResolution = resolved.diagnostics;
905
+ if (booleanFlag(flags["offline-memory-orphans"])) {
906
+ if (sourceHost !== "claude" || !resolved.memoryRoot) throw new Error("offline memory orphan scan requires a resolved Claude project-memory root");
907
+ orphanScan = await scanIndexedMemoryOrphans(resolved.memoryRoot);
908
+ }
909
+ }
910
+ catch (error) { sourceResolution = { status: "failed-closed", reason: error.message }; }
911
+ }
912
+ const result = {
913
+ ok: Number(process.versions.node.split(".")[0]) >= 20 && hostIntegration.ok
914
+ && (!sourceResolution || sourceResolution.status === "loaded"),
915
+ version: VERSION,
916
+ node: process.versions.node,
917
+ platform: process.platform,
918
+ preservationMode: "read-only-source-overlay",
919
+ stateDirectory: process.env.AGENTSPINE_STATE_DIR || "platform-default",
920
+ hostIntegration,
921
+ sourceResolution,
922
+ orphanScan
923
+ };
924
+ output(result, json);
925
+ if (!result.ok) process.exitCode = 1;
926
+ return;
927
+ }
928
+
929
+ if (command === "audit") {
930
+ const result = await runAudit(positional[0] || process.cwd(), { host: flags.host || process.env.AGENTSPINE_HOST || null });
931
+ output(result, json);
932
+ if (!result.ok) process.exitCode = 1;
933
+ return;
934
+ }
935
+
936
+ if (command === "mcp") {
937
+ const { startMcpServer } = await import("./mcp.js");
938
+ startMcpServer();
939
+ return;
940
+ }
941
+
942
+ throw new Error(`Unknown command: ${command}`);
943
+ }
944
+
945
+ if (isMainModule(import.meta.url)) {
946
+ run().catch((error) => {
947
+ process.stderr.write(`AgentSpine: ${error.message}\n`);
948
+ process.exitCode = 1;
949
+ });
950
+ }