@kontextmind/kxm 0.7.119 → 0.7.121

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.
@@ -415,28 +415,31 @@ export function resolveKxmRunTarget(
415
415
  runtime: Runtime,
416
416
  workflow: string | undefined,
417
417
  forTask = false,
418
+ envelope: Record<string, unknown> = {},
419
+ allowDefaultWorkflow = false,
418
420
  ): { projectRoot: string; workflowId: string; configRevision: string; defaultHarness: string; prerequisites: KxmRunHandoff[] } | number {
419
421
  if (runtime.workspaceFlag !== undefined) {
420
422
  print(runtime.io, runtime.json, {
421
423
  ok: false,
422
424
  command: "run",
423
425
  error: "workspace_option_unsupported",
426
+ ...envelope,
424
427
  }, "kxm run discovers the authoritative project from the current directory; --workspace is not supported");
425
428
  return 2;
426
429
  }
427
- if (!workflow && !forTask) {
428
- print(runtime.io, runtime.json, { ok: false, command: "run", error: "workflow_required" }, "usage: kxm run <workflow> [prompt]");
430
+ if (!workflow && !forTask && !allowDefaultWorkflow) {
431
+ print(runtime.io, runtime.json, { ok: false, command: "run", error: "workflow_required", ...envelope }, "usage: kxm run <workflow> [prompt]");
429
432
  return 2;
430
433
  }
431
434
  const projectRoot = discoverKxmProjectRoot(runtime.cwd);
432
435
  if (!projectRoot) {
433
- print(runtime.io, runtime.json, { ok: false, command: "run", error: "project_required" }, "kxm run requires a KXM project (run kxm init first)");
436
+ print(runtime.io, runtime.json, { ok: false, command: "run", error: "project_required", ...envelope }, "kxm run requires a KXM project (run kxm init first)");
434
437
  return 1;
435
438
  }
436
439
  const bundle = loadKxmProject(projectRoot, {});
437
440
  const workflowId = workflow ?? String(bundle.project.value.defaultWorkflow ?? "default");
438
441
  if (!bundle.workflows.has(workflowId)) {
439
- print(runtime.io, runtime.json, { ok: false, command: "run", error: "run_workflow_unknown", workflow: workflowId }, `workflow ${workflowId} does not exist in this project`);
442
+ print(runtime.io, runtime.json, { ok: false, command: "run", error: "run_workflow_unknown", workflow: workflowId, ...envelope }, `workflow ${workflowId} does not exist in this project`);
440
443
  return 1;
441
444
  }
442
445
  const defaultHarness = String(bundle.project.value.defaultHarness ?? "pi");
@@ -451,9 +454,16 @@ export function resolveKxmRunTarget(
451
454
  return { projectRoot, workflowId, configRevision: bundle.configRevision, defaultHarness, prerequisites };
452
455
  }
453
456
 
454
- export async function cmdKxmRun(runtime: Runtime, workflow: string | undefined, promptParts: string[], forTask = false): Promise<number> {
457
+ export async function cmdKxmRun(
458
+ runtime: Runtime,
459
+ workflow: string | undefined,
460
+ promptParts: string[],
461
+ forTask = false,
462
+ call?: { brief?: string; prompt?: string; runIdOut?: { runId?: string }; allowDefaultWorkflow?: boolean },
463
+ ): Promise<number> {
464
+ const envelope = call?.brief !== undefined ? { brief: call.brief } : {};
455
465
  try {
456
- const target = resolveKxmRunTarget(runtime, workflow, forTask);
466
+ const target = resolveKxmRunTarget(runtime, workflow, forTask, envelope, call?.allowDefaultWorkflow === true);
457
467
  if (typeof target === "number") return target;
458
468
  const { projectRoot } = target;
459
469
  if (runtime.dryRun) {
@@ -461,21 +471,24 @@ export async function cmdKxmRun(runtime: Runtime, workflow: string | undefined,
461
471
  ok: true,
462
472
  command: "run",
463
473
  dryRun: true,
474
+ ...envelope,
464
475
  ...target,
465
476
  }, `run plan: workflow ${target.workflowId} at ${target.configRevision.slice(0, 19)}… (no run created)`);
466
477
  return 0;
467
478
  }
468
479
  const supervisor = await (kxmDriveCliSeams.ensureSupervisor ?? ensureKxmSupervisor)({ env: runtime.env });
469
- const prompt = promptParts.join(" ").trim();
480
+ const prompt = call?.prompt !== undefined ? call.prompt : promptParts.join(" ").trim();
470
481
  const acceptance = await (kxmDriveCliSeams.runtimeRequest ?? kxmRuntimeRequest)(supervisor, "POST", "/v1/runs", {
471
482
  projectRoot,
472
483
  workflowId: target.workflowId,
473
484
  prompt,
474
485
  });
475
486
  const run = acceptance.run as { runId: string; homeRuntimeId: string; status: string; configRevision: string };
487
+ if (call?.runIdOut) call.runIdOut.runId = run.runId;
476
488
  print(runtime.io, runtime.json, {
477
489
  ok: true,
478
490
  command: "run",
491
+ ...envelope,
479
492
  execution: {
480
493
  status: "not_started",
481
494
  mode: "live",
@@ -496,10 +509,10 @@ export async function cmdKxmRun(runtime: Runtime, workflow: string | undefined,
496
509
  return 0;
497
510
  } catch (error) {
498
511
  if (error instanceof KxmConfigError) {
499
- print(runtime.io, runtime.json, { ok: false, command: "run", error: "run_failed", issues: error.issues }, `run failed: ${error.message}`);
512
+ print(runtime.io, runtime.json, { ok: false, command: "run", error: "run_failed", issues: error.issues, ...envelope }, `run failed: ${error.message}`);
500
513
  return 1;
501
514
  }
502
- print(runtime.io, runtime.json, { ok: false, command: "run", error: "run_io_failed" }, "run failed because a local operation did not complete");
515
+ print(runtime.io, runtime.json, { ok: false, command: "run", error: "run_io_failed", ...envelope }, "run failed because a local operation did not complete");
503
516
  return 1;
504
517
  }
505
518
  }
@@ -104,7 +104,6 @@ import {
104
104
  cmdBackup,
105
105
  cmdRestore,
106
106
  cmdKxmTrust,
107
- cmdKxmRun,
108
107
  cmdKxmRunStatus,
109
108
  cmdKxmRunDrive,
110
109
  cmdKxmRunReceipt,
@@ -122,6 +121,18 @@ cmdBackup,
122
121
 
123
122
  import { cmdPluginInstall } from "./cli/plugins.ts";
124
123
 
124
+ import {
125
+ applyLane,
126
+ cmdKxmRunCli,
127
+ cmdLaneCreate,
128
+ cmdLaneDrop,
129
+ cmdLaneList,
130
+ cmdLaneRun,
131
+ cmdLaneStatus,
132
+ } from "./cli/lanes.ts";
133
+
134
+ import { cmdLand } from "./cli/land.ts";
135
+
125
136
  import {
126
137
  cmdStatus,
127
138
  cmdDash,
@@ -187,6 +198,8 @@ const USAGE_ERROR_CODES = new Set([
187
198
  */
188
199
  const DRY_RUN_COMMANDS: ReadonlySet<string> = new Set([
189
200
  "init", "backup", "restore", "run", "explain", "suggest", "update", "dash", "completion", "completion install",
201
+ "lane create", "lane list", "lane status", "lane drop", "lane run",
202
+ "land",
190
203
  "plugin install", "plugin",
191
204
  "runs status", "runs drive", "runs receipt", "runs cancel", "runs list",
192
205
  "tenant status", "models inventory-refresh", "harness list", "auth token",
@@ -435,26 +448,88 @@ function createProgram(ctx: CliContext, result: { code: number }): Command {
435
448
  result.code = await cmdRestore(runtimeFrom(ctx, this), manifest, options);
436
449
  });
437
450
 
451
+ const laneCmd = addGlobalOptions(program.command("lane").description("Manage one git worktree per unit of work"));
452
+ laneCmd.helpCommand("help", "Show lane help");
453
+ addGlobalOptions(laneCmd.command("create").description("Create a worktree lane from a base ref (default origin/main)"))
454
+ .argument("<unit>", "Lane unit name")
455
+ .option("--base <ref>", "Base ref to resolve before adding the worktree (default origin/main)")
456
+ .action(async function laneCreateAction(this: Command, unit: string, options: { base?: string }) {
457
+ result.code = await cmdLaneCreate(runtimeFrom(ctx, this), unit, options);
458
+ });
459
+ addGlobalOptions(laneCmd.command("list").description("List lanes with dirty, ahead, and behind counts"))
460
+ .action(async function laneListAction(this: Command) {
461
+ result.code = await cmdLaneList(runtimeFrom(ctx, this));
462
+ });
463
+ addGlobalOptions(laneCmd.command("status").description("Show one lane and the projected status of its last run"))
464
+ .argument("<unit>", "Lane unit name")
465
+ .action(async function laneStatusAction(this: Command, unit: string) {
466
+ result.code = await cmdLaneStatus(runtimeFrom(ctx, this), unit);
467
+ });
468
+ addGlobalOptions(laneCmd.command("drop").description("Remove a lane worktree and its record without deleting the branch"))
469
+ .argument("<unit>", "Lane unit name")
470
+ .option("--force", "Remove a dirty lane or one whose last run is still open")
471
+ .action(async function laneDropAction(this: Command, unit: string, options: { force?: boolean }) {
472
+ result.code = await cmdLaneDrop(runtimeFrom(ctx, this), unit, { force: options.force === true });
473
+ });
474
+ addGlobalOptions(laneCmd.command("run").description("Create the lane if needed, start a run from a brief, and drive it"))
475
+ .argument("<unit>", "Lane unit name")
476
+ .requiredOption("--brief <file>", "Read the run prompt from a file")
477
+ .option("--workflow <id>", "Workflow id (default: the project default)")
478
+ .option("--base <ref>", "Base ref when the lane does not exist yet (default origin/main)")
479
+ .option("--wait", "Wait until a drive receipt is recorded")
480
+ .option("--timeout-ms <n>", "Wait timeout in milliseconds (default 60000, max 600000)")
481
+ .action(async function laneRunAction(this: Command, unit: string, options: { brief?: string; workflow?: string; base?: string; wait?: boolean; timeoutMs?: string }) {
482
+ result.code = await cmdLaneRun(runtimeFrom(ctx, this), unit, {
483
+ ...(options.brief !== undefined ? { brief: options.brief } : {}),
484
+ ...(options.workflow !== undefined ? { workflow: options.workflow } : {}),
485
+ ...(options.base !== undefined ? { base: options.base } : {}),
486
+ wait: options.wait === true,
487
+ ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
488
+ });
489
+ });
490
+
491
+ addGlobalOptions(program.command("land").description("Land the current branch: verify, regenerate docs, push, pull request, rebase, unblock, squash-merge, release, and milestone"))
492
+ .option("--pr <n>", "Existing pull request number")
493
+ .option("--stage <name>", "Run one stage: verify, docs, push, pr, rebase, unblock, merge, release, or milestone")
494
+ .option("--body-file <path>", "Pull request body file used when creating a pull request")
495
+ .action(async function landAction(this: Command, options: { pr?: string; stage?: string; bodyFile?: string }) {
496
+ result.code = await cmdLand(runtimeFrom(ctx, this), {
497
+ ...(options.pr !== undefined ? { pr: options.pr } : {}),
498
+ ...(options.stage !== undefined ? { stage: options.stage } : {}),
499
+ ...(options.bodyFile !== undefined ? { bodyFile: options.bodyFile } : {}),
500
+ });
501
+ });
502
+
438
503
  addGlobalOptions(program.command("run").description("Create a KXM run without executing steps; follow its prerequisites, then kxm runs drive <runId> --wait")
439
504
  .argument("[workflow]", "Workflow id to run")
440
505
  .argument("[prompt...]", "Run prompt (events keep its hash; the full text is kept in a local 0600 sidecar file)")
441
- .action(async function runAction(this: Command, workflow: string | undefined, promptParts: string[]) {
442
- result.code = await cmdKxmRun(runtimeFrom(ctx, this), workflow, promptParts);
506
+ .option("--brief <file>", "Read the run prompt from a file instead of the positional prompt")
507
+ .option("--lane <unit>", "Discover the project from this lane's worktree")
508
+ .action(async function runAction(this: Command, workflow: string | undefined, promptParts: string[], options: { brief?: string; lane?: string }) {
509
+ result.code = await cmdKxmRunCli(runtimeFrom(ctx, this), workflow, promptParts, options);
443
510
  }));
444
511
  const runCmd = addGlobalOptions(program.command("runs").description("Inspect KXM runs"));
445
512
  runCmd.helpCommand("help", "Show runs help");
446
513
  addGlobalOptions(runCmd.command("status").description("Show the projected status of a run, including durable drive receipt state (open / receipt verified / unsettled / orphaned)"))
447
514
  .argument("<runId>", "Run id")
448
- .action(async function runStatusAction(this: Command, runId: string) {
449
- result.code = await cmdKxmRunStatus(runtimeFrom(ctx, this), runId);
515
+ .option("--lane <unit>", "Discover the project from this lane's worktree")
516
+ .action(async function runStatusAction(this: Command, runId: string, options: { lane?: string }) {
517
+ const runtime = applyLane(runtimeFrom(ctx, this), options.lane, "runs status");
518
+ result.code = typeof runtime === "number" ? runtime : await cmdKxmRunStatus(runtime, runId);
450
519
  });
451
520
  addGlobalOptions(runCmd.command("drive").description("Drive a run with live harness calls, or with the model-free simulation when --simulated is passed"))
452
521
  .argument("<runId>", "Run id")
453
522
  .option("--simulated", "Use the model-free simulation producer instead of a live model")
454
523
  .option("--wait", "Wait until a drive receipt is recorded; exits 0 only for a VERIFIED COMPLETED settlement")
455
524
  .option("--timeout-ms <n>", "Wait timeout in milliseconds (default 60000, max 600000)")
456
- .action(async function runDriveAction(this: Command, runId: string, options: { simulated?: boolean; wait?: boolean; timeoutMs?: string }) {
457
- result.code = await cmdKxmRunDrive(runtimeFrom(ctx, this), runId, options.simulated === true, {
525
+ .option("--lane <unit>", "Discover the project from this lane's worktree")
526
+ .action(async function runDriveAction(this: Command, runId: string, options: { simulated?: boolean; wait?: boolean; timeoutMs?: string; lane?: string }) {
527
+ const runtime = applyLane(runtimeFrom(ctx, this), options.lane, "runs drive");
528
+ if (typeof runtime === "number") {
529
+ result.code = runtime;
530
+ return;
531
+ }
532
+ result.code = await cmdKxmRunDrive(runtime, runId, options.simulated === true, {
458
533
  wait: options.wait === true,
459
534
  ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
460
535
  });
@@ -462,13 +537,17 @@ function createProgram(ctx: CliContext, result: { code: number }): Command {
462
537
  addGlobalOptions(runCmd.command("receipt").description("Print the newest drive receipt for a run"))
463
538
  .argument("<runId>", "Run id")
464
539
  .option("--all", "Print the capped receipt list for the run")
465
- .action(async function runReceiptAction(this: Command, runId: string, options: { all?: boolean }) {
466
- result.code = await cmdKxmRunReceipt(runtimeFrom(ctx, this), runId, { all: options.all === true });
540
+ .option("--lane <unit>", "Discover the project from this lane's worktree")
541
+ .action(async function runReceiptAction(this: Command, runId: string, options: { all?: boolean; lane?: string }) {
542
+ const runtime = applyLane(runtimeFrom(ctx, this), options.lane, "runs receipt");
543
+ result.code = typeof runtime === "number" ? runtime : await cmdKxmRunReceipt(runtime, runId, { all: options.all === true });
467
544
  });
468
545
  addGlobalOptions(runCmd.command("cancel").description("Durably request cancellation of a run"))
469
546
  .argument("<runId>", "Run id")
470
- .action(async function runCancelAction(this: Command, runId: string) {
471
- result.code = await cmdKxmRunCancel(runtimeFrom(ctx, this), runId);
547
+ .option("--lane <unit>", "Discover the project from this lane's worktree")
548
+ .action(async function runCancelAction(this: Command, runId: string, options: { lane?: string }) {
549
+ const runtime = applyLane(runtimeFrom(ctx, this), options.lane, "runs cancel");
550
+ result.code = typeof runtime === "number" ? runtime : await cmdKxmRunCancel(runtime, runId);
472
551
  });
473
552
  addGlobalOptions(runCmd.command("list").description("List recent runs for the current project"))
474
553
  .action(async function runListAction(this: Command) {
@@ -11,7 +11,7 @@ import { deliverInboxNotification } from "./inbox.ts";
11
11
  import type { HubEvent, MessageRecord } from "./protocol.ts";
12
12
  import { sessionTokenFixHint } from "./session-token-hint.ts";
13
13
 
14
- const VERSION = "0.7.119";
14
+ const VERSION = "0.7.121";
15
15
  const CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
16
16
  const inbox = new Map<string, MessageRecord>();
17
17
  const notifiedInbox = new Set<string>();
package/schemas/README.md CHANGED
@@ -13,6 +13,7 @@ Runtime-local records, and JSON events/results for the planned KXM contract.
13
13
  | `workflow.schema.json` | `kxm.workflow.v1` |
14
14
  | `template-provenance.schema.json` | `kxm.template-provenance.v1` |
15
15
  | `local-repository-bindings.schema.json` | `kxm.local-repository-bindings.v1` |
16
+ | `lanes.schema.json` | `kxm.lanes.v1` |
16
17
  | `init-operation.schema.json` | `kxm.init-operation.v1` |
17
18
  | `permission-diff.schema.json` | `kxm.permission-diff.v1` |
18
19
  | `run-event.schema.json` | `kxm.run-event.v1` |
@@ -0,0 +1,61 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.kxm.dev/lanes.schema.json",
4
+ "title": "KXM worktree lanes",
5
+ "type": "object",
6
+ "required": ["schema", "lanes"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "schema": {
10
+ "const": "kxm.lanes.v1"
11
+ },
12
+ "lanes": {
13
+ "type": "object",
14
+ "propertyNames": {
15
+ "type": "string",
16
+ "minLength": 1,
17
+ "maxLength": 64,
18
+ "pattern": "^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$"
19
+ },
20
+ "patternProperties": {
21
+ "^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$": {
22
+ "$ref": "#/$defs/lane"
23
+ }
24
+ },
25
+ "additionalProperties": false
26
+ }
27
+ },
28
+ "$defs": {
29
+ "lane": {
30
+ "type": "object",
31
+ "required": ["path", "branch", "baseRef", "baseSha", "createdAt"],
32
+ "additionalProperties": false,
33
+ "properties": {
34
+ "path": {
35
+ "type": "string",
36
+ "minLength": 1
37
+ },
38
+ "branch": {
39
+ "type": "string",
40
+ "minLength": 1
41
+ },
42
+ "baseRef": {
43
+ "type": "string",
44
+ "minLength": 1
45
+ },
46
+ "baseSha": {
47
+ "type": "string",
48
+ "pattern": "^[a-f0-9]{40,64}$"
49
+ },
50
+ "createdAt": {
51
+ "type": "string",
52
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\\.[0-9]{1,9})?Z$"
53
+ },
54
+ "lastRunId": {
55
+ "type": "string",
56
+ "minLength": 1
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,2 @@
1
+ export function unionChangelogUnreleased(ours: string, theirs: string): string;
2
+ export function unionLandedTracker(ours: string, theirs: string): string;