ctxr-cli 0.34.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.md +25 -5
  2. package/dist/commands/plugin-build.d.ts +33 -0
  3. package/dist/commands/plugin-build.d.ts.map +1 -0
  4. package/dist/commands/plugin-build.js +51 -0
  5. package/dist/commands/plugin-build.js.map +1 -0
  6. package/dist/commands/publish-check.d.ts.map +1 -1
  7. package/dist/commands/publish-check.js +35 -4
  8. package/dist/commands/publish-check.js.map +1 -1
  9. package/dist/commands/serve.d.ts.map +1 -1
  10. package/dist/commands/serve.js +10 -2
  11. package/dist/commands/serve.js.map +1 -1
  12. package/dist/commands/session-start.d.ts.map +1 -1
  13. package/dist/commands/session-start.js +12 -38
  14. package/dist/commands/session-start.js.map +1 -1
  15. package/dist/core/fs/atomic.d.ts +3 -2
  16. package/dist/core/fs/atomic.d.ts.map +1 -1
  17. package/dist/core/fs/atomic.js +3 -2
  18. package/dist/core/fs/atomic.js.map +1 -1
  19. package/dist/core/mcp/cli-runner.d.ts +41 -0
  20. package/dist/core/mcp/cli-runner.d.ts.map +1 -0
  21. package/dist/core/mcp/cli-runner.js +81 -0
  22. package/dist/core/mcp/cli-runner.js.map +1 -0
  23. package/dist/core/mcp/jsonrpc.d.ts +13 -1
  24. package/dist/core/mcp/jsonrpc.d.ts.map +1 -1
  25. package/dist/core/mcp/jsonrpc.js +1 -1
  26. package/dist/core/mcp/jsonrpc.js.map +1 -1
  27. package/dist/core/mcp/member-sessions.d.ts +48 -0
  28. package/dist/core/mcp/member-sessions.d.ts.map +1 -0
  29. package/dist/core/mcp/member-sessions.js +85 -0
  30. package/dist/core/mcp/member-sessions.js.map +1 -0
  31. package/dist/core/mcp/ownership.d.ts +51 -0
  32. package/dist/core/mcp/ownership.d.ts.map +1 -0
  33. package/dist/core/mcp/ownership.js +101 -0
  34. package/dist/core/mcp/ownership.js.map +1 -0
  35. package/dist/core/mcp/paths.d.ts +48 -0
  36. package/dist/core/mcp/paths.d.ts.map +1 -0
  37. package/dist/core/mcp/paths.js +111 -0
  38. package/dist/core/mcp/paths.js.map +1 -0
  39. package/dist/core/mcp/sessions.d.ts +24 -46
  40. package/dist/core/mcp/sessions.d.ts.map +1 -1
  41. package/dist/core/mcp/sessions.js +31 -103
  42. package/dist/core/mcp/sessions.js.map +1 -1
  43. package/dist/core/mcp/tool-map.d.ts +94 -0
  44. package/dist/core/mcp/tool-map.d.ts.map +1 -0
  45. package/dist/core/mcp/tool-map.js +211 -0
  46. package/dist/core/mcp/tool-map.js.map +1 -0
  47. package/dist/core/mcp/tools/capture.d.ts +7 -2
  48. package/dist/core/mcp/tools/capture.d.ts.map +1 -1
  49. package/dist/core/mcp/tools/capture.js +69 -18
  50. package/dist/core/mcp/tools/capture.js.map +1 -1
  51. package/dist/core/mcp/tools/cli.d.ts +97 -0
  52. package/dist/core/mcp/tools/cli.d.ts.map +1 -0
  53. package/dist/core/mcp/tools/cli.js +271 -0
  54. package/dist/core/mcp/tools/cli.js.map +1 -0
  55. package/dist/core/mcp/tools/edit.d.ts +62 -28
  56. package/dist/core/mcp/tools/edit.d.ts.map +1 -1
  57. package/dist/core/mcp/tools/edit.js +182 -153
  58. package/dist/core/mcp/tools/edit.js.map +1 -1
  59. package/dist/core/mcp/tools/files.d.ts +37 -11
  60. package/dist/core/mcp/tools/files.d.ts.map +1 -1
  61. package/dist/core/mcp/tools/files.js +122 -100
  62. package/dist/core/mcp/tools/files.js.map +1 -1
  63. package/dist/core/mcp/tools/index.d.ts +12 -3
  64. package/dist/core/mcp/tools/index.d.ts.map +1 -1
  65. package/dist/core/mcp/tools/index.js +161 -58
  66. package/dist/core/mcp/tools/index.js.map +1 -1
  67. package/dist/core/mcp/tools/sessions.d.ts +145 -0
  68. package/dist/core/mcp/tools/sessions.d.ts.map +1 -0
  69. package/dist/core/mcp/tools/sessions.js +226 -0
  70. package/dist/core/mcp/tools/sessions.js.map +1 -0
  71. package/dist/core/plugin/build.d.ts +70 -0
  72. package/dist/core/plugin/build.d.ts.map +1 -0
  73. package/dist/core/plugin/build.js +159 -0
  74. package/dist/core/plugin/build.js.map +1 -0
  75. package/dist/core/plugin/routes.d.ts +35 -0
  76. package/dist/core/plugin/routes.d.ts.map +1 -0
  77. package/dist/core/plugin/routes.js +77 -0
  78. package/dist/core/plugin/routes.js.map +1 -0
  79. package/dist/core/plugin/zip.d.ts +17 -0
  80. package/dist/core/plugin/zip.d.ts.map +1 -0
  81. package/dist/core/plugin/zip.js +75 -0
  82. package/dist/core/plugin/zip.js.map +1 -0
  83. package/dist/core/session/start.d.ts +46 -0
  84. package/dist/core/session/start.d.ts.map +1 -0
  85. package/dist/core/session/start.js +33 -0
  86. package/dist/core/session/start.js.map +1 -0
  87. package/dist/core/skills.d.ts +20 -2
  88. package/dist/core/skills.d.ts.map +1 -1
  89. package/dist/core/skills.js +56 -3
  90. package/dist/core/skills.js.map +1 -1
  91. package/dist/run.d.ts +12 -0
  92. package/dist/run.d.ts.map +1 -1
  93. package/dist/run.js +81 -58
  94. package/dist/run.js.map +1 -1
  95. package/dist/version.d.ts +1 -1
  96. package/dist/version.js +1 -1
  97. package/package.json +1 -1
  98. package/templates/mcp/Delete.md +6 -0
  99. package/templates/mcp/Edit.md +6 -5
  100. package/templates/mcp/Glob.md +2 -2
  101. package/templates/mcp/Grep.md +2 -1
  102. package/templates/mcp/Move.md +7 -0
  103. package/templates/mcp/Read.md +4 -4
  104. package/templates/mcp/Write.md +4 -4
  105. package/templates/mcp/ctxr.md +7 -0
  106. package/templates/mcp/ctxr_write.md +8 -0
  107. package/templates/mcp/server-instructions.md +11 -3
  108. package/templates/mcp/session_discard.md +4 -0
  109. package/templates/mcp/session_list.md +7 -0
  110. package/templates/mcp/session_rebase.md +5 -0
  111. package/templates/mcp/session_start.md +7 -0
  112. package/templates/mcp/session_status.md +3 -0
  113. package/templates/mcp/session_submit.md +8 -0
  114. package/templates/plugin/README.md +13 -0
  115. package/templates/plugin/chatgpt-mcp.json +19 -0
  116. package/templates/plugin/chatgpt-plugin.json +13 -0
  117. package/templates/plugin/claude-mcp.json +8 -0
  118. package/templates/plugin/claude-plugin.json +6 -0
  119. package/templates/plugin/page.html +47 -0
  120. package/templates/skills/ctxr-publish.md +2 -1
  121. package/templates/skills/mcp/_preamble.md +44 -0
  122. package/dist/core/mcp/drafts.d.ts +0 -30
  123. package/dist/core/mcp/drafts.d.ts.map +0 -1
  124. package/dist/core/mcp/drafts.js +0 -52
  125. package/dist/core/mcp/drafts.js.map +0 -1
package/dist/run.js CHANGED
@@ -19,6 +19,7 @@ import * as publishCheckCommand from './commands/publish-check.js';
19
19
  import * as publishUrlsCommand from './commands/publish-urls.js';
20
20
  import * as publishNewCommand from './commands/publish-new.js';
21
21
  import * as publishStampCommand from './commands/publish-stamp.js';
22
+ import * as pluginBuildCommand from './commands/plugin-build.js';
22
23
  import * as reviewListCommand from './commands/review-list.js';
23
24
  import * as secondOpinionCommand from './commands/second-opinion.js';
24
25
  import * as sharedStoresStatusCommand from './commands/shared-stores-status.js';
@@ -168,12 +169,12 @@ function commandPathFor(program, argv) {
168
169
  function collect(value, previous) {
169
170
  return [...previous, value];
170
171
  }
171
- export async function run(argv, env) {
172
- if (isGlobalVersionRequest(argv)) {
173
- const jsonMode = argv.includes('--json');
174
- const runEnv = { ...env, noInput: env.noInput || argv.includes('--no-input') || jsonMode };
175
- return runCommand('version', runEnv, jsonMode, () => versionCommand.execute(runEnv));
176
- }
172
+ /**
173
+ * The command tree, built once per invocation. Exported so the MCP surface's
174
+ * tool/command alignment check (serve-the-session-lifecycle-to-chat-apps D9)
175
+ * reads the CLI's real command registry rather than a copy of it.
176
+ */
177
+ export function buildProgram(env) {
177
178
  const program = new Command();
178
179
  program
179
180
  .name('ctxr')
@@ -185,7 +186,7 @@ export async function run(argv, env) {
185
186
  .option('--root <path>', 'store root (overrides CONTEXTURE_STORE_ROOT)')
186
187
  .option('--json', 'emit machine-readable output')
187
188
  .option('--no-input', 'never prompt; fail loud instead of blocking');
188
- let result = ExitCode.Ok;
189
+ const state = { result: ExitCode.Ok };
189
190
  program
190
191
  .command('init')
191
192
  .description('create a new context store, or reconcile an existing one')
@@ -194,7 +195,7 @@ export async function run(argv, env) {
194
195
  .option('--harness <ids>', `comma-separated harness-generation adapters to target (${initCommand.SELECTABLE_HARNESS_IDS.join(', ')}), or "none"`)
195
196
  .action(async (cmdOpts, cmd) => {
196
197
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
197
- result = await runCommand('init', runEnv, jsonMode, () => initCommand.execute(runEnv, { root, profile: cmdOpts.profile, taxonomy: cmdOpts.taxonomy, harness: cmdOpts.harness }));
198
+ state.result = await runCommand('init', runEnv, jsonMode, () => initCommand.execute(runEnv, { root, profile: cmdOpts.profile, taxonomy: cmdOpts.taxonomy, harness: cmdOpts.harness }));
198
199
  });
199
200
  program
200
201
  .command('doctor')
@@ -202,7 +203,7 @@ export async function run(argv, env) {
202
203
  .option('--staged', 'check staged changes only (used by the pre-commit hook)')
203
204
  .action(async (cmdOpts, cmd) => {
204
205
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
205
- result = await runCommand('doctor', runEnv, jsonMode, async () => {
206
+ state.result = await runCommand('doctor', runEnv, jsonMode, async () => {
206
207
  const store = await openStore(runEnv, { root });
207
208
  return doctorCommand.execute(runEnv, store, { staged: cmdOpts.staged });
208
209
  });
@@ -215,7 +216,7 @@ export async function run(argv, env) {
215
216
  .requiredOption('--text <text>', 'the line to append')
216
217
  .action(async (notePath, cmdOpts, cmd) => {
217
218
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
218
- result = await runCommand('entry.append', runEnv, jsonMode, async () => {
219
+ state.result = await runCommand('entry.append', runEnv, jsonMode, async () => {
219
220
  const store = await openStore(runEnv, { root });
220
221
  return entryAppendCommand.execute(runEnv, store, { path: notePath, region: cmdOpts.region, text: cmdOpts.text });
221
222
  });
@@ -226,7 +227,7 @@ export async function run(argv, env) {
226
227
  .description('regenerate every catalog section, preserving authored glosses')
227
228
  .action(async (_cmdOpts, cmd) => {
228
229
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
229
- result = await runCommand('catalog.build', runEnv, jsonMode, async () => {
230
+ state.result = await runCommand('catalog.build', runEnv, jsonMode, async () => {
230
231
  const store = await openStore(runEnv, { root });
231
232
  return catalogBuildCommand.execute(store);
232
233
  });
@@ -237,7 +238,7 @@ export async function run(argv, env) {
237
238
  .option('--stale', 'also report entries whose note has changed since the gloss was confirmed')
238
239
  .action(async (cmdOpts, cmd) => {
239
240
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
240
- result = await runCommand('catalog.check', runEnv, jsonMode, async () => {
241
+ state.result = await runCommand('catalog.check', runEnv, jsonMode, async () => {
241
242
  const store = await openStore(runEnv, { root });
242
243
  return catalogCheckCommand.execute(store, { stale: cmdOpts.stale });
243
244
  });
@@ -248,7 +249,7 @@ export async function run(argv, env) {
248
249
  .requiredOption('--section <id>', 'the section id to print')
249
250
  .action(async (cmdOpts, cmd) => {
250
251
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
251
- result = await runCommand('catalog.show', runEnv, jsonMode, async () => {
252
+ state.result = await runCommand('catalog.show', runEnv, jsonMode, async () => {
252
253
  const store = await openStore(runEnv, { root });
253
254
  return catalogShowCommand.execute(store, { section: cmdOpts.section });
254
255
  });
@@ -267,7 +268,7 @@ export async function run(argv, env) {
267
268
  .option('--max-notes <n>', 'cap the returned set; truncation is reported, never silent', (v) => Number.parseInt(v, 10))
268
269
  .action(async (cmdOpts, cmd) => {
269
270
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
270
- result = await runCommand('context.gather', runEnv, jsonMode, async () => {
271
+ state.result = await runCommand('context.gather', runEnv, jsonMode, async () => {
271
272
  const store = await openStore(runEnv, { root });
272
273
  return contextGatherCommand.execute(store, {
273
274
  seed: cmdOpts.seed,
@@ -288,7 +289,7 @@ export async function run(argv, env) {
288
289
  .option('--emit-records', 'also emit a stable per-note record list {id, path, gloss, hash}')
289
290
  .action(async (cmdOpts, cmd) => {
290
291
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
291
- result = await runCommand('graph.build', runEnv, jsonMode, async () => {
292
+ state.result = await runCommand('graph.build', runEnv, jsonMode, async () => {
292
293
  const store = await openStore(runEnv, { root });
293
294
  return graphBuildCommand.execute(store, { emitRecords: cmdOpts.emitRecords });
294
295
  });
@@ -304,7 +305,7 @@ export async function run(argv, env) {
304
305
  const flags = { since: cmdOpts.since, until: cmdOpts.until, format: cmdOpts.format };
305
306
  // review-the-knowledge-diff (D9): `--format markdown` is the whole stdout — the
306
307
  // block rides as the human summary, which the reporter writes verbatim and alone.
307
- result = await runCommand('graph.diff', runEnv, jsonMode, async () => {
308
+ state.result = await runCommand('graph.diff', runEnv, jsonMode, async () => {
308
309
  graphDiffCommand.checkFlags(flags, jsonMode);
309
310
  const store = await openStore(runEnv, { root });
310
311
  return graphDiffCommand.execute(store, flags, jsonMode);
@@ -319,7 +320,7 @@ export async function run(argv, env) {
319
320
  .option('--type <name>', 'follow only edges of this type (a configured relation name, or link)')
320
321
  .action(async (node, cmdOpts, cmd) => {
321
322
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
322
- result = await runCommand('graph.query.neighbors', runEnv, jsonMode, async () => {
323
+ state.result = await runCommand('graph.query.neighbors', runEnv, jsonMode, async () => {
323
324
  const store = await openStore(runEnv, { root });
324
325
  return graphQueryCommand.executeNeighbors(store, {
325
326
  node,
@@ -334,7 +335,7 @@ export async function run(argv, env) {
334
335
  .description('shortest path between two nodes, if one exists')
335
336
  .action(async (from, to, _cmdOpts, cmd) => {
336
337
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
337
- result = await runCommand('graph.query.path', runEnv, jsonMode, async () => {
338
+ state.result = await runCommand('graph.query.path', runEnv, jsonMode, async () => {
338
339
  const store = await openStore(runEnv, { root });
339
340
  return graphQueryCommand.executePath(store, { from, to });
340
341
  });
@@ -344,7 +345,7 @@ export async function run(argv, env) {
344
345
  .description('the induced subgraph over the given node ids')
345
346
  .action(async (ids, _cmdOpts, cmd) => {
346
347
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
347
- result = await runCommand('graph.query.subgraph', runEnv, jsonMode, async () => {
348
+ state.result = await runCommand('graph.query.subgraph', runEnv, jsonMode, async () => {
348
349
  const store = await openStore(runEnv, { root });
349
350
  return graphQueryCommand.executeSubgraph(store, { ids });
350
351
  });
@@ -355,7 +356,7 @@ export async function run(argv, env) {
355
356
  .option('--top <n>', 'how many to list', (v) => Number.parseInt(v, 10), 10)
356
357
  .action(async (cmdOpts, cmd) => {
357
358
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
358
- result = await runCommand('graph.query.hubs', runEnv, jsonMode, async () => {
359
+ state.result = await runCommand('graph.query.hubs', runEnv, jsonMode, async () => {
359
360
  const store = await openStore(runEnv, { root });
360
361
  return graphQueryCommand.executeHubs(store, { top: cmdOpts.top });
361
362
  });
@@ -365,7 +366,7 @@ export async function run(argv, env) {
365
366
  .description('every positional cluster with its note count')
366
367
  .action(async (_cmdOpts, cmd) => {
367
368
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
368
- result = await runCommand('graph.query.clusters', runEnv, jsonMode, async () => {
369
+ state.result = await runCommand('graph.query.clusters', runEnv, jsonMode, async () => {
369
370
  const store = await openStore(runEnv, { root });
370
371
  return graphQueryCommand.executeClusters(store, {});
371
372
  });
@@ -376,7 +377,7 @@ export async function run(argv, env) {
376
377
  .option('--top <n>', 'how many to list (default: the configured bridge limit)', (v) => Number.parseInt(v, 10))
377
378
  .action(async (cmdOpts, cmd) => {
378
379
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
379
- result = await runCommand('graph.query.bridges', runEnv, jsonMode, async () => {
380
+ state.result = await runCommand('graph.query.bridges', runEnv, jsonMode, async () => {
380
381
  const store = await openStore(runEnv, { root });
381
382
  return graphQueryCommand.executeBridges(store, { top: cmdOpts.top });
382
383
  });
@@ -386,7 +387,7 @@ export async function run(argv, env) {
386
387
  .description('nodes with no links in or out')
387
388
  .action(async (_cmdOpts, cmd) => {
388
389
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
389
- result = await runCommand('graph.query.orphans', runEnv, jsonMode, async () => {
390
+ state.result = await runCommand('graph.query.orphans', runEnv, jsonMode, async () => {
390
391
  const store = await openStore(runEnv, { root });
391
392
  return graphQueryCommand.executeOrphans(store, {});
392
393
  });
@@ -399,7 +400,7 @@ export async function run(argv, env) {
399
400
  .requiredOption('--source-id <id>', 'a stable identifier for this specific source')
400
401
  .action(async (capturePath, cmdOpts, cmd) => {
401
402
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
402
- result = await runCommand('ingest', runEnv, jsonMode, async () => {
403
+ state.result = await runCommand('ingest', runEnv, jsonMode, async () => {
403
404
  const store = await openStore(runEnv, { root });
404
405
  return ingestCommand.execute(runEnv, store, {
405
406
  path: capturePath,
@@ -415,7 +416,7 @@ export async function run(argv, env) {
415
416
  .description('the canonicalized-content hash of a file')
416
417
  .action(async (notePath, _cmdOpts, cmd) => {
417
418
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
418
- result = await runCommand('source.hash', runEnv, jsonMode, async () => {
419
+ state.result = await runCommand('source.hash', runEnv, jsonMode, async () => {
419
420
  const store = await openStore(runEnv, { root });
420
421
  return sourceHashCommand.execute(runEnv, store, { path: notePath });
421
422
  });
@@ -426,7 +427,7 @@ export async function run(argv, env) {
426
427
  .requiredOption('--source-id <id>', "the candidate material's source identifier")
427
428
  .action(async (notePath, cmdOpts, cmd) => {
428
429
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
429
- result = await runCommand('source.check', runEnv, jsonMode, async () => {
430
+ state.result = await runCommand('source.check', runEnv, jsonMode, async () => {
430
431
  const store = await openStore(runEnv, { root });
431
432
  return sourceCheckCommand.execute(runEnv, store, { path: notePath, sourceId: cmdOpts.sourceId });
432
433
  });
@@ -438,7 +439,7 @@ export async function run(argv, env) {
438
439
  .option('--hash <hash>', 'the content hash to record (default: computed from the note\'s current body)')
439
440
  .action(async (notePath, cmdOpts, cmd) => {
440
441
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
441
- result = await runCommand('source.stamp', runEnv, jsonMode, async () => {
442
+ state.result = await runCommand('source.stamp', runEnv, jsonMode, async () => {
442
443
  const store = await openStore(runEnv, { root });
443
444
  return sourceStampCommand.execute(runEnv, store, { path: notePath, id: cmdOpts.id, hash: cmdOpts.hash });
444
445
  });
@@ -449,7 +450,7 @@ export async function run(argv, env) {
449
450
  .requiredOption('--id <id>', 'the alternative source identifier')
450
451
  .action(async (notePath, cmdOpts, cmd) => {
451
452
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
452
- result = await runCommand('source.add-alt', runEnv, jsonMode, async () => {
453
+ state.result = await runCommand('source.add-alt', runEnv, jsonMode, async () => {
453
454
  const store = await openStore(runEnv, { root });
454
455
  return sourceAddAltCommand.execute(runEnv, store, { path: notePath, id: cmdOpts.id });
455
456
  });
@@ -459,7 +460,7 @@ export async function run(argv, env) {
459
460
  .description('health observations that never fail the run: orphans, broken links, uningested inbox material, catalog gaps')
460
461
  .action(async (_cmdOpts, cmd) => {
461
462
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
462
- result = await runCommand('lint', runEnv, jsonMode, async () => {
463
+ state.result = await runCommand('lint', runEnv, jsonMode, async () => {
463
464
  const store = await openStore(runEnv, { root });
464
465
  return lintCommand.execute(runEnv, store);
465
466
  });
@@ -469,7 +470,7 @@ export async function run(argv, env) {
469
470
  .description('retire a note, or a folder of notes whole, to its same path beneath the archive destination via a single tracked rename, reporting every note that links to it')
470
471
  .action(async (notePath, _cmdOpts, cmd) => {
471
472
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
472
- result = await runCommand('archive', runEnv, jsonMode, async () => {
473
+ state.result = await runCommand('archive', runEnv, jsonMode, async () => {
473
474
  const store = await openStore(runEnv, { root });
474
475
  return archiveCommand.execute(runEnv, store, { path: notePath });
475
476
  });
@@ -480,7 +481,7 @@ export async function run(argv, env) {
480
481
  .description('enumerate candidate source notes (notes linking to <entity>) for a rollup')
481
482
  .action(async (entity, _cmdOpts, cmd) => {
482
483
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
483
- result = await runCommand('rollup.gather', runEnv, jsonMode, async () => {
484
+ state.result = await runCommand('rollup.gather', runEnv, jsonMode, async () => {
484
485
  const store = await openStore(runEnv, { root });
485
486
  return rollupGatherCommand.execute(runEnv, store, { entity });
486
487
  });
@@ -491,7 +492,7 @@ export async function run(argv, env) {
491
492
  .requiredOption('--content-file <path>', 'a file containing the rollup text to write')
492
493
  .action(async (entity, cmdOpts, cmd) => {
493
494
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
494
- result = await runCommand('rollup.write', runEnv, jsonMode, async () => {
495
+ state.result = await runCommand('rollup.write', runEnv, jsonMode, async () => {
495
496
  const store = await openStore(runEnv, { root });
496
497
  return rollupWriteCommand.execute(runEnv, store, { entity, contentFile: cmdOpts.contentFile });
497
498
  });
@@ -502,7 +503,7 @@ export async function run(argv, env) {
502
503
  .option('--for <entity>', 'check only this entity')
503
504
  .action(async (cmdOpts, cmd) => {
504
505
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
505
- result = await runCommand('rollup.stale', runEnv, jsonMode, async () => {
506
+ state.result = await runCommand('rollup.stale', runEnv, jsonMode, async () => {
506
507
  const store = await openStore(runEnv, { root });
507
508
  return rollupStaleCommand.execute(runEnv, store, { for: cmdOpts.for });
508
509
  });
@@ -516,7 +517,7 @@ export async function run(argv, env) {
516
517
  .option('--entity <name>', 'every note linking to this entity (same enumeration as rollup gather)')
517
518
  .action(async (cmdOpts, cmd) => {
518
519
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
519
- result = await runCommand('publish.gather', runEnv, jsonMode, async () => {
520
+ state.result = await runCommand('publish.gather', runEnv, jsonMode, async () => {
520
521
  const store = await openStore(runEnv, { root });
521
522
  return publishGatherCommand.execute(runEnv, store, {
522
523
  under: cmdOpts.under,
@@ -531,7 +532,7 @@ export async function run(argv, env) {
531
532
  .option('--disposable', 'scaffold a disposable page at disposable/<YYYY-MM-DD>-<slug>, dated today by the command (two-kinds-of-published-page)')
532
533
  .action(async (slug, cmdOpts, cmd) => {
533
534
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
534
- result = await runCommand('publish.new', runEnv, jsonMode, async () => {
535
+ state.result = await runCommand('publish.new', runEnv, jsonMode, async () => {
535
536
  const store = await openStore(runEnv, { root });
536
537
  return publishNewCommand.execute(store, { slug, disposable: cmdOpts.disposable === true });
537
538
  });
@@ -541,7 +542,7 @@ export async function run(argv, env) {
541
542
  .description("record the hash of the note a permanent page represents (its README's lead link) as a marker on that line — once; never overwrites a marker, and refuses a disposable page")
542
543
  .action(async (pagePath, _cmdOpts, cmd) => {
543
544
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
544
- result = await runCommand('publish.stamp', runEnv, jsonMode, async () => {
545
+ state.result = await runCommand('publish.stamp', runEnv, jsonMode, async () => {
545
546
  const store = await openStore(runEnv, { root });
546
547
  return publishStampCommand.execute(store, { path: pagePath });
547
548
  });
@@ -551,7 +552,7 @@ export async function run(argv, env) {
551
552
  .description("the mechanized structural checks a published page must pass (no external references, viewport meta, print rule, provenance, sibling README, script syntax; a disposable page's shape; a permanent page's represented note and its hash), and the page's address and lifespan")
552
553
  .action(async (pagePath, _cmdOpts, cmd) => {
553
554
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
554
- result = await runCommand('publish.check', runEnv, jsonMode, async () => {
555
+ state.result = await runCommand('publish.check', runEnv, jsonMode, async () => {
555
556
  const store = await openStore(runEnv, { root });
556
557
  return publishCheckCommand.execute(store, { path: pagePath });
557
558
  });
@@ -563,7 +564,7 @@ export async function run(argv, env) {
563
564
  .option('--since <ref>', 'the changes since this ref, as a pull request against it would show')
564
565
  .action(async (cmdOpts, cmd) => {
565
566
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
566
- result = await runCommand('publish.urls', runEnv, jsonMode, async () => {
567
+ state.result = await runCommand('publish.urls', runEnv, jsonMode, async () => {
567
568
  const store = await openStore(runEnv, { root });
568
569
  return publishUrlsCommand.execute(store, { staged: cmdOpts.staged === true, since: cmdOpts.since });
569
570
  });
@@ -574,7 +575,7 @@ export async function run(argv, env) {
574
575
  .description('(re)generate every configured harness-generation adapter\'s output')
575
576
  .action(async (_cmdOpts, cmd) => {
576
577
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
577
- result = await runCommand('adapters.generate', runEnv, jsonMode, async () => {
578
+ state.result = await runCommand('adapters.generate', runEnv, jsonMode, async () => {
578
579
  const store = await openStore(runEnv, { root });
579
580
  return adaptersGenerateCommand.execute(runEnv, store);
580
581
  });
@@ -586,7 +587,7 @@ export async function run(argv, env) {
586
587
  .option('--host <address>', 'address to bind — widening beyond the default exposes the server, with no requester filtering of its own, to whatever can reach that address', serveCommand.DEFAULT_HOST)
587
588
  .action(async (cmdOpts, cmd) => {
588
589
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
589
- result = await runCommand('serve', runEnv, jsonMode, async () => {
590
+ state.result = await runCommand('serve', runEnv, jsonMode, async () => {
590
591
  const store = await openStore(runEnv, { root });
591
592
  return serveCommand.execute(runEnv, store, { port: cmdOpts.port, host: cmdOpts.host });
592
593
  });
@@ -597,7 +598,7 @@ export async function run(argv, env) {
597
598
  .option('--check', 'resolve the latest published release and compare it against the installed one')
598
599
  .action(async (cmdOpts, cmd) => {
599
600
  const { runEnv, jsonMode } = deriveRunEnv(env, cmd);
600
- result = await runCommand('version', runEnv, jsonMode, () => versionCommand.execute(runEnv, { check: cmdOpts.check }));
601
+ state.result = await runCommand('version', runEnv, jsonMode, () => versionCommand.execute(runEnv, { check: cmdOpts.check }));
601
602
  });
602
603
  program
603
604
  .command('update')
@@ -605,7 +606,7 @@ export async function run(argv, env) {
605
606
  .option('--worktree', 'work in a new worktree on a branch named for this release, never in this checkout; commits nothing')
606
607
  .action(async (cmdOpts, cmd) => {
607
608
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
608
- result = await runCommand('update', runEnv, jsonMode, async () => {
609
+ state.result = await runCommand('update', runEnv, jsonMode, async () => {
609
610
  // The one command allowed past the schema gate on an older store (migrate-stores-on-update).
610
611
  const store = await openStoreMigrating(runEnv, { root });
611
612
  return cmdOpts.worktree
@@ -619,7 +620,7 @@ export async function run(argv, env) {
619
620
  .option('--portable', "verify the store's recorded commit in a disposable checkout, in a child process with no harness state reachable")
620
621
  .action(async (cmdOpts, cmd) => {
621
622
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
622
- result = await runCommand('verify', runEnv, jsonMode, async () => {
623
+ state.result = await runCommand('verify', runEnv, jsonMode, async () => {
623
624
  const store = await openStore(runEnv, { root });
624
625
  return verifyCommand.execute(runEnv, store, { portable: cmdOpts.portable });
625
626
  });
@@ -633,18 +634,31 @@ export async function run(argv, env) {
633
634
  .option('--session <session>', 'with --confirm: land every pull request of a session homed here (worktree name or branch), parts in mounted stores first, stopping at the first that does not land')
634
635
  .action(async (pr, cmdOpts, cmd) => {
635
636
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
636
- result = await runCommand('land', runEnv, jsonMode, async () => {
637
+ state.result = await runCommand('land', runEnv, jsonMode, async () => {
637
638
  const store = await openStore(runEnv, { root });
638
639
  return landCommand.execute(runEnv, store, { pr, pending: Boolean(cmdOpts.pending), confirm: Boolean(cmdOpts.confirm), session: cmdOpts.session });
639
640
  });
640
641
  });
642
+ // serve-the-session-lifecycle-to-chat-apps D7: the chat-app plugins, built from the MCP render of the owned skills.
643
+ const pluginCommand = program.command('plugin').description("build the store's Claude and ChatGPT plugins: its skills and its MCP server");
644
+ pluginCommand
645
+ .command('build')
646
+ .description('write the Claude and ChatGPT plugins, each as a directory and a zip archive, from the MCP render of the owned skills and serve.base_url')
647
+ .option('--out <dir>', 'where to write them (default: .contexture/cache/plugin/ under the store root)')
648
+ .action(async (cmdOpts, cmd) => {
649
+ const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
650
+ state.result = await runCommand('plugin.build', runEnv, jsonMode, async () => {
651
+ const store = await openStore(runEnv, { root });
652
+ return pluginBuildCommand.execute(runEnv, store, { out: cmdOpts.out });
653
+ });
654
+ });
641
655
  const reviewCommand = program.command('review').description('read the decisions people made on session pull requests from the approval page');
642
656
  reviewCommand
643
657
  .command('list')
644
658
  .description('report each open session pull request with its decision state: awaiting, approved, changes-requested, needs-reapproval or blocked')
645
659
  .action(async (_cmdOpts, cmd) => {
646
660
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
647
- result = await runCommand('review.list', runEnv, jsonMode, async () => {
661
+ state.result = await runCommand('review.list', runEnv, jsonMode, async () => {
648
662
  const store = await openStore(runEnv, { root });
649
663
  return reviewListCommand.execute(runEnv, store);
650
664
  });
@@ -660,7 +674,7 @@ export async function run(argv, env) {
660
674
  .option('--timeout <seconds>', 'per-CLI time limit in seconds (default 600)');
661
675
  commonSecondOpinionOptions(secondOpinionGroup.command('preflight').description('check each model CLI answers, at the tier the run will use, and still accepts the flags this command passes')).action(async (cmdOpts, cmd) => {
662
676
  const { runEnv, jsonMode } = deriveRunEnv(env, cmd);
663
- result = await runCommand('second-opinion.preflight', runEnv, jsonMode, () => secondOpinionCommand.executePreflight(runEnv, cmdOpts));
677
+ state.result = await runCommand('second-opinion.preflight', runEnv, jsonMode, () => secondOpinionCommand.executePreflight(runEnv, cmdOpts));
664
678
  });
665
679
  commonSecondOpinionOptions(secondOpinionGroup.command('critique').description('send a written plan to three critic roles, each on a different model family, and write anonymised letters and a manifest'))
666
680
  .option('--plan-file <path>', 'the plan to critique (default: read from stdin)')
@@ -668,7 +682,7 @@ export async function run(argv, env) {
668
682
  .option('--seed <n>', 'seed the letter assignment, for a reproducible mapping')
669
683
  .action(async (cmdOpts, cmd) => {
670
684
  const { runEnv, jsonMode } = deriveRunEnv(env, cmd);
671
- result = await runCommand('second-opinion.critique', runEnv, jsonMode, () => secondOpinionCommand.executeCritique(runEnv, cmdOpts));
685
+ state.result = await runCommand('second-opinion.critique', runEnv, jsonMode, () => secondOpinionCommand.executeCritique(runEnv, cmdOpts));
672
686
  });
673
687
  commonSecondOpinionOptions(secondOpinionGroup.command('poll').description('send one question to the same model families, optionally answered through two or three taste lenses'))
674
688
  .option('--prompt-file <path>', 'the question and its options (default: read from stdin)')
@@ -677,7 +691,7 @@ export async function run(argv, env) {
677
691
  .option('--no-wrap', 'send the question as written, without the answer scaffold')
678
692
  .action(async (cmdOpts, cmd) => {
679
693
  const { runEnv, jsonMode } = deriveRunEnv(env, cmd);
680
- result = await runCommand('second-opinion.poll', runEnv, jsonMode, () => secondOpinionCommand.executePoll(runEnv, cmdOpts));
694
+ state.result = await runCommand('second-opinion.poll', runEnv, jsonMode, () => secondOpinionCommand.executePoll(runEnv, cmdOpts));
681
695
  });
682
696
  const sharedStoresCommand = program.command('shared-stores').description('the stores this store mounts under shared/');
683
697
  sharedStoresCommand
@@ -685,7 +699,7 @@ export async function run(argv, env) {
685
699
  .description('clone each declared mount that is missing and fast-forward the others, leaving any with local work alone')
686
700
  .action(async (_cmdOpts, cmd) => {
687
701
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
688
- result = await runCommand('shared-stores.sync', runEnv, jsonMode, async () => {
702
+ state.result = await runCommand('shared-stores.sync', runEnv, jsonMode, async () => {
689
703
  const store = await openStore(runEnv, { root });
690
704
  return sharedStoresSyncCommand.execute(runEnv, store);
691
705
  });
@@ -695,7 +709,7 @@ export async function run(argv, env) {
695
709
  .description('report each declared mount: present, a store, branch, commit, uncommitted changes — no writes, no network')
696
710
  .action(async (_cmdOpts, cmd) => {
697
711
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
698
- result = await runCommand('shared-stores.status', runEnv, jsonMode, async () => {
712
+ state.result = await runCommand('shared-stores.status', runEnv, jsonMode, async () => {
699
713
  const store = await openStore(runEnv, { root });
700
714
  return sharedStoresStatusCommand.execute(runEnv, store);
701
715
  });
@@ -708,7 +722,7 @@ export async function run(argv, env) {
708
722
  .option('--joins <session>', 'run inside a mounted store: start a session that belongs to this live session of the store that mounts it (worktree name or branch)')
709
723
  .action(async (label, cmdOpts, cmd) => {
710
724
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
711
- result = await runCommand('session.start', runEnv, jsonMode, async () => {
725
+ state.result = await runCommand('session.start', runEnv, jsonMode, async () => {
712
726
  const store = await openStore(runEnv, { root });
713
727
  return sessionStartCommand.execute(runEnv, store, { label, ...(cmdOpts.joins === undefined ? {} : { joins: cmdOpts.joins }) });
714
728
  });
@@ -719,7 +733,7 @@ export async function run(argv, env) {
719
733
  .requiredOption('--proposal <path>', 'a YAML file of approved note items')
720
734
  .action(async (cmdOpts, cmd) => {
721
735
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
722
- result = await runCommand('session.capture', runEnv, jsonMode, async () => {
736
+ state.result = await runCommand('session.capture', runEnv, jsonMode, async () => {
723
737
  const store = await openStore(runEnv, { root });
724
738
  return sessionCaptureCommand.execute(store, cmdOpts);
725
739
  });
@@ -730,7 +744,7 @@ export async function run(argv, env) {
730
744
  .option('--review', "also read each session's open pull request and, when the store declares change_approval, its decision state from the forge")
731
745
  .action(async (cmdOpts, cmd) => {
732
746
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
733
- result = await runCommand('session.list', runEnv, jsonMode, async () => {
747
+ state.result = await runCommand('session.list', runEnv, jsonMode, async () => {
734
748
  const store = await openStore(runEnv, { root });
735
749
  return sessionListCommand.execute(runEnv, store, { review: Boolean(cmdOpts.review) });
736
750
  });
@@ -743,7 +757,7 @@ export async function run(argv, env) {
743
757
  .option('--diff', 'also print the unified diff of uncommitted work')
744
758
  .action(async (session, cmdOpts, cmd) => {
745
759
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
746
- result = await runCommand('session.status', runEnv, jsonMode, async () => {
760
+ state.result = await runCommand('session.status', runEnv, jsonMode, async () => {
747
761
  const store = await openStore(runEnv, { root });
748
762
  return sessionStatusCommand.execute(runEnv, store, { session, diff: Boolean(cmdOpts.diff) });
749
763
  });
@@ -759,7 +773,7 @@ export async function run(argv, env) {
759
773
  .option('--body-file <path>', 'read the pull request body from a file instead')
760
774
  .action(async (session, cmdOpts, cmd) => {
761
775
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
762
- result = await runCommand('session.submit', runEnv, jsonMode, async () => {
776
+ state.result = await runCommand('session.submit', runEnv, jsonMode, async () => {
763
777
  const store = await openStore(runEnv, { root });
764
778
  return sessionSubmitCommand.execute(runEnv, store, { session, ...cmdOpts });
765
779
  });
@@ -772,7 +786,7 @@ export async function run(argv, env) {
772
786
  .option('--abort', 'abort the rebase and restore the session as it was')
773
787
  .action(async (session, cmdOpts, cmd) => {
774
788
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
775
- result = await runCommand('session.rebase', runEnv, jsonMode, async () => {
789
+ state.result = await runCommand('session.rebase', runEnv, jsonMode, async () => {
776
790
  const store = await openStore(runEnv, { root });
777
791
  return sessionRebaseCommand.execute(runEnv, store, { session, continue: cmdOpts.continue, abort: cmdOpts.abort });
778
792
  });
@@ -785,11 +799,20 @@ export async function run(argv, env) {
785
799
  .option('--close-pull-request', 'also close the session\'s open pull request')
786
800
  .action(async (session, cmdOpts, cmd) => {
787
801
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
788
- result = await runCommand('session.discard', runEnv, jsonMode, async () => {
802
+ state.result = await runCommand('session.discard', runEnv, jsonMode, async () => {
789
803
  const store = await openStore(runEnv, { root });
790
804
  return sessionDiscardCommand.execute(runEnv, store, { session, force: cmdOpts.force, closePullRequest: cmdOpts.closePullRequest });
791
805
  });
792
806
  });
807
+ return { program, state };
808
+ }
809
+ export async function run(argv, env) {
810
+ if (isGlobalVersionRequest(argv)) {
811
+ const jsonMode = argv.includes('--json');
812
+ const runEnv = { ...env, noInput: env.noInput || argv.includes('--no-input') || jsonMode };
813
+ return runCommand('version', runEnv, jsonMode, () => versionCommand.execute(runEnv));
814
+ }
815
+ const { program, state } = buildProgram(env);
793
816
  try {
794
817
  await program.parseAsync(argv, { from: 'user' });
795
818
  }
@@ -827,6 +850,6 @@ export async function run(argv, env) {
827
850
  }
828
851
  throw err;
829
852
  }
830
- return result;
853
+ return state.result;
831
854
  }
832
855
  //# sourceMappingURL=run.js.map