@veewo/claw 0.1.78 → 0.1.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/bin.js +8 -2
- package/dist/bin.js.map +1 -1
- package/dist/cli.js +177 -83
- package/dist/cli.js.map +1 -1
- package/dist/invocation-host.d.ts +6 -0
- package/dist/invocation-host.js +29 -0
- package/dist/invocation-host.js.map +1 -0
- package/dist/knowledge-hook-preflight.d.ts +3 -0
- package/dist/knowledge-hook-preflight.js +112 -0
- package/dist/knowledge-hook-preflight.js.map +1 -0
- package/dist/knowledge-writer-result.d.ts +11 -0
- package/dist/knowledge-writer-result.js +50 -0
- package/dist/knowledge-writer-result.js.map +1 -0
- package/dist/opencode-runner.js +2 -1
- package/dist/opencode-runner.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -5,15 +5,18 @@ import path from "node:path";
|
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
7
|
import { spawn, spawnSync } from "node:child_process";
|
|
8
|
-
import { buildDirectWorkflowGuidance, checkProjectProtocol, ClawError, buildPlanWorkflowGuidance, buildMemoryIndex, buildSessionStartDefaultPrompt, buildSessionStartRecoveredPrompt, editPlan, ensureProjectProtocol, enforceTaskRetention, ingestTruth, initProject, getTemplateTaskDoneChoices, resolvePlanTemplateFile, resolveProjectContext, resolveSessionBoundPlan, resolveContext, resolveSeedPlanTemplate, searchMemory, showPlan, createSubplan, switchTask, tryCaptureKnowledgeStop, claimKnowledgeFinalizationJob, listRetryableKnowledgeFinalizationJobs, normalizeTruthMarkdownEncoding, tryCleanupKnowledgeFinalizationReport, writeKnowledgeFinalizationJob, unbindSession, writePlan, } from "@veewo/claw-core";
|
|
8
|
+
import { buildDirectWorkflowGuidance, checkProjectProtocol, ClawError, buildPlanWorkflowGuidance, buildMemoryIndex, buildSessionStartDefaultPrompt, buildSessionStartRecoveredPrompt, editPlan, ensureProjectProtocol, enforceTaskRetention, ingestTruth, initProject, getTemplateTaskDoneChoices, resolvePlanTemplateFile, resolveProjectContext, resolveWorkflowProjectContext, resolveSessionWorkflowContext, deleteSessionWorkflow, sweepExpiredSessionWorkflows, resolveSessionBoundPlan, resolveContext, resolveSeedPlanTemplate, searchMemory, showPlan, createSubplan, switchTask, tryCaptureKnowledgeStop, claimKnowledgeFinalizationJob, listRetryableKnowledgeFinalizationJobs, normalizeTruthMarkdownEncoding, tryCleanupKnowledgeFinalizationReport, writeKnowledgeFinalizationJob, unbindSession, writePlan, } from "@veewo/claw-core";
|
|
9
9
|
import { buildCodexDriverEnvelope } from "./codex-driver.js";
|
|
10
10
|
import { checkCodexRuntime, resolveCodexSdkEntryPath } from "./codex-runtime.js";
|
|
11
11
|
import { extractLatestFinalAssistantMessage } from "./codex-transcript.js";
|
|
12
|
+
import { consumeBufferedHookInput } from "./knowledge-hook-preflight.js";
|
|
13
|
+
import { resolveInvocationHost, withoutInvocationHost } from "./invocation-host.js";
|
|
12
14
|
import { runOpencodeKnowledgeWriter } from "./opencode-runner.js";
|
|
13
15
|
const CLI_VERSION = readCliVersion();
|
|
14
16
|
const TOP_LEVEL_COMMANDS = [
|
|
15
17
|
{ name: "init [options]", summary: "Initialize and normalize the .claw project surface." },
|
|
16
18
|
{ name: "context [--task <name>]", summary: "Resolve project context, auto-initializing or correcting .claw state." },
|
|
19
|
+
{ name: "session clean [--expired]", summary: "Remove current or expired session workflow state." },
|
|
17
20
|
{ name: "check", summary: "Check and auto-correct .claw project protocol fields." },
|
|
18
21
|
{ name: "plan <subcommand> [options]", summary: "Plan lifecycle: create, start, edit, remove, wait, resume, show, done." },
|
|
19
22
|
{ name: "codex driver", summary: "Return the versioned code-mode driver used by the Codex adapter." },
|
|
@@ -34,7 +37,7 @@ const COMMAND_HELP = {
|
|
|
34
37
|
{ flag: "--name <project-name>", detail: "Human-readable project name." },
|
|
35
38
|
{ flag: "--context-path <file>", detail: "Extra context path to track (repeatable)." },
|
|
36
39
|
{ flag: "--ext-path <path>", detail: "External doc path to index (repeatable)." },
|
|
37
|
-
{ flag: "--external-writer-skill <skill>", detail: "Skill id for the combined knowledge
|
|
40
|
+
{ flag: "--external-writer-skill <skill>", detail: "Skill id override for the combined knowledge-writer pass." },
|
|
38
41
|
{ flag: "--planning true|false", detail: "Enable planning-aware default template behavior (default true)." },
|
|
39
42
|
{ flag: "--external-planning-skill <skill>", detail: "Skill id for an external planning skill." },
|
|
40
43
|
{ flag: "--gitnexus true|false", detail: "Enable GitNexus integration (default false)." },
|
|
@@ -49,6 +52,10 @@ const COMMAND_HELP = {
|
|
|
49
52
|
{ flag: "--task <name>", detail: "Resolve context scoped to a specific task." },
|
|
50
53
|
],
|
|
51
54
|
},
|
|
55
|
+
session: {
|
|
56
|
+
usage: ["{script} session clean", "{script} session clean --expired"],
|
|
57
|
+
description: "Clean ephemeral session-scoped workflow state without touching a project .claw directory.",
|
|
58
|
+
},
|
|
52
59
|
check: {
|
|
53
60
|
usage: ["{script} check"],
|
|
54
61
|
description: "Check the .claw project protocol and auto-correct any missing or malformed fields in project.json. Returns issues found and the paths that were fixed.",
|
|
@@ -59,14 +66,15 @@ const COMMAND_HELP = {
|
|
|
59
66
|
subcommands: {
|
|
60
67
|
create: {
|
|
61
68
|
usage: [
|
|
62
|
-
"{script} plan create \"<title>\" [--goal <text>]",
|
|
63
|
-
"{script} plan create --title <text> [--goal <text>] [--template <name>]",
|
|
69
|
+
"{script} plan create \"<title>\" [--goal <text>] [--scope session]",
|
|
70
|
+
"{script} plan create --title <text> [--goal <text>] [--template <name>] [--scope session]",
|
|
64
71
|
],
|
|
65
72
|
description: "Create the task scope and initial plan from a template. Uses explicit `--template` first, otherwise the project's configured `defaultPlanTemplate`, and finally falls back to the built-in `default`; planning-enabled projects start in process.discussing with the default planning bridge tasks, while planning-disabled projects start directly in process.active with one executable task.",
|
|
66
73
|
summary: "Create the task scope and initial plan.",
|
|
67
74
|
options: [
|
|
68
75
|
{ flag: "--title <text>", detail: "Task title (required unless a positional title is given)." },
|
|
69
76
|
{ flag: "--goal <text>", detail: "Optional goal text." },
|
|
77
|
+
{ flag: "--scope session", detail: "Use ephemeral per-session storage and disable project knowledge side effects." },
|
|
70
78
|
{ flag: "--template <name>", detail: "Optional plan template name. Overrides the project's configured default template." },
|
|
71
79
|
],
|
|
72
80
|
},
|
|
@@ -336,12 +344,13 @@ const COMMAND_HELP = {
|
|
|
336
344
|
async function main() {
|
|
337
345
|
const args = process.argv.slice(2);
|
|
338
346
|
const explicitHost = readOptionalFlag(args, "--host");
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
347
|
+
let effectiveHost;
|
|
348
|
+
try {
|
|
349
|
+
effectiveHost = resolveInvocationHost(explicitHost, process.env.CLAW_HOST);
|
|
350
|
+
}
|
|
351
|
+
catch (error) {
|
|
352
|
+
handleError(error);
|
|
353
|
+
return;
|
|
345
354
|
}
|
|
346
355
|
const command = args.shift();
|
|
347
356
|
if (command === "--help" || command === "-h") {
|
|
@@ -383,7 +392,10 @@ async function main() {
|
|
|
383
392
|
printJson(initProject(initInput));
|
|
384
393
|
return;
|
|
385
394
|
case "context":
|
|
386
|
-
printJson(buildPublicContextOutput(await runContextCommand(args)));
|
|
395
|
+
printJson(buildPublicContextOutput(await runContextCommand(args, process.cwd(), resolveOwnerSessionKey(), effectiveHost)));
|
|
396
|
+
return;
|
|
397
|
+
case "session":
|
|
398
|
+
runSession(args);
|
|
387
399
|
return;
|
|
388
400
|
case "check":
|
|
389
401
|
const checkResult = ensureProjectProtocol(process.cwd());
|
|
@@ -398,7 +410,7 @@ async function main() {
|
|
|
398
410
|
});
|
|
399
411
|
return;
|
|
400
412
|
case "plan":
|
|
401
|
-
await runPlan(args);
|
|
413
|
+
await runPlan(args, effectiveHost);
|
|
402
414
|
return;
|
|
403
415
|
case "codex":
|
|
404
416
|
runCodex(args);
|
|
@@ -407,10 +419,10 @@ async function main() {
|
|
|
407
419
|
await runTemplate(args);
|
|
408
420
|
return;
|
|
409
421
|
case "task":
|
|
410
|
-
await runTask(args);
|
|
422
|
+
await runTask(args, effectiveHost);
|
|
411
423
|
return;
|
|
412
424
|
case "subplan":
|
|
413
|
-
await runSubplan(args);
|
|
425
|
+
await runSubplan(args, effectiveHost);
|
|
414
426
|
return;
|
|
415
427
|
case "switch-task":
|
|
416
428
|
printJson(switchTask({
|
|
@@ -426,13 +438,13 @@ async function main() {
|
|
|
426
438
|
runSearch(args);
|
|
427
439
|
return;
|
|
428
440
|
case "direct":
|
|
429
|
-
runDirect(args);
|
|
441
|
+
runDirect(args, effectiveHost);
|
|
430
442
|
return;
|
|
431
443
|
case "truth":
|
|
432
444
|
runTruth(args);
|
|
433
445
|
return;
|
|
434
446
|
case "hook":
|
|
435
|
-
await runHook(args);
|
|
447
|
+
await runHook(args, effectiveHost);
|
|
436
448
|
return;
|
|
437
449
|
case "help":
|
|
438
450
|
printHelp(args);
|
|
@@ -460,13 +472,36 @@ function runCodex(args) {
|
|
|
460
472
|
assertNoRemainingArgs(args, "codex driver");
|
|
461
473
|
printJson(buildCodexDriverEnvelope(CLI_VERSION));
|
|
462
474
|
}
|
|
463
|
-
|
|
475
|
+
function runSession(args) {
|
|
476
|
+
const subcommand = args.shift();
|
|
477
|
+
if (subcommand !== "clean") {
|
|
478
|
+
throw new ClawError("PROJECT_CONFIG_INVALID", `Unknown session subcommand "${subcommand ?? ""}".`);
|
|
479
|
+
}
|
|
480
|
+
const expired = readBooleanFlag(args, "--expired");
|
|
481
|
+
assertNoRemainingArgs(args, "session clean");
|
|
482
|
+
if (expired) {
|
|
483
|
+
const removed = sweepExpiredSessionWorkflows();
|
|
484
|
+
printJson({ ok: true, command: "session.clean", expired: true, removedCount: removed.length, removed });
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const ownerSessionKey = resolveOwnerSessionKey();
|
|
488
|
+
if (!ownerSessionKey) {
|
|
489
|
+
throw new ClawError("PROJECT_CONFIG_INVALID", "session clean requires a platform session id.");
|
|
490
|
+
}
|
|
491
|
+
printJson({
|
|
492
|
+
ok: true,
|
|
493
|
+
command: "session.clean",
|
|
494
|
+
removed: deleteSessionWorkflow(ownerSessionKey),
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
async function runPlan(args, effectiveHost) {
|
|
464
498
|
const subcommand = args.shift();
|
|
465
499
|
switch (subcommand) {
|
|
466
500
|
case "create":
|
|
467
501
|
rejectFlags(args, ["--task", "--plan", "--content", "--status", "--parent-task-id", "--description"]);
|
|
468
502
|
const explicitTitle = readOptionalFlag(args, "--title");
|
|
469
503
|
const explicitTemplate = readOptionalFlag(args, "--template");
|
|
504
|
+
const scope = readWorkflowScope(args);
|
|
470
505
|
const title = explicitTitle ?? readOptionalPositionalArg(args);
|
|
471
506
|
const templateName = explicitTemplate;
|
|
472
507
|
if (!title) {
|
|
@@ -474,14 +509,15 @@ async function runPlan(args) {
|
|
|
474
509
|
}
|
|
475
510
|
const result = await writePlan({
|
|
476
511
|
cwd: process.cwd(),
|
|
512
|
+
scope,
|
|
477
513
|
templateName,
|
|
478
514
|
title,
|
|
479
515
|
goalText: readOptionalFlag(args, "--goal"),
|
|
480
516
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
481
|
-
host:
|
|
517
|
+
host: effectiveHost,
|
|
482
518
|
});
|
|
483
519
|
assertNoRemainingArgs(args, "plan create");
|
|
484
|
-
printJson(compactPlanCommandResult("plan.create", result));
|
|
520
|
+
printJson(compactPlanCommandResult("plan.create", result, effectiveHost));
|
|
485
521
|
return;
|
|
486
522
|
case "edit": {
|
|
487
523
|
const target = readPlanMutationTarget(args);
|
|
@@ -494,10 +530,10 @@ async function runPlan(args) {
|
|
|
494
530
|
...target,
|
|
495
531
|
operations,
|
|
496
532
|
commandSource: "plan.edit",
|
|
497
|
-
host:
|
|
533
|
+
host: effectiveHost,
|
|
498
534
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
499
535
|
});
|
|
500
|
-
printJson(compactPlanCommandResult("plan.edit", result));
|
|
536
|
+
printJson(compactPlanCommandResult("plan.edit", result, effectiveHost));
|
|
501
537
|
if (result.operationChain?.status === "partial")
|
|
502
538
|
process.exitCode = 1;
|
|
503
539
|
return;
|
|
@@ -514,17 +550,17 @@ async function runPlan(args) {
|
|
|
514
550
|
...target,
|
|
515
551
|
updates,
|
|
516
552
|
commandSource: "plan.edit",
|
|
517
|
-
host:
|
|
553
|
+
host: effectiveHost,
|
|
518
554
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
519
555
|
});
|
|
520
|
-
printJson(compactPlanCommandResult("plan.remove", result));
|
|
556
|
+
printJson(compactPlanCommandResult("plan.remove", result, effectiveHost));
|
|
521
557
|
return;
|
|
522
558
|
}
|
|
523
559
|
case "wait":
|
|
524
|
-
await runPlanStatusAlias(args, "process.wait", "plan.wait");
|
|
560
|
+
await runPlanStatusAlias(args, "process.wait", "plan.wait", effectiveHost);
|
|
525
561
|
return;
|
|
526
562
|
case "resume":
|
|
527
|
-
await runPlanStatusAlias(args, "process.active", "plan.resume");
|
|
563
|
+
await runPlanStatusAlias(args, "process.active", "plan.resume", effectiveHost);
|
|
528
564
|
return;
|
|
529
565
|
case "start": {
|
|
530
566
|
const updates = readPlanFieldUpdates(args);
|
|
@@ -542,10 +578,10 @@ async function runPlan(args) {
|
|
|
542
578
|
planStatus: "process.active",
|
|
543
579
|
completeLifecycleBridge: true,
|
|
544
580
|
commandSource: "plan.start",
|
|
545
|
-
host:
|
|
581
|
+
host: effectiveHost,
|
|
546
582
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
547
583
|
});
|
|
548
|
-
printJson(compactPlanCommandResult("plan.start", result));
|
|
584
|
+
printJson(compactPlanCommandResult("plan.start", result, effectiveHost));
|
|
549
585
|
return;
|
|
550
586
|
}
|
|
551
587
|
case "done": {
|
|
@@ -562,22 +598,28 @@ async function runPlan(args) {
|
|
|
562
598
|
};
|
|
563
599
|
const target = readPlanMutationTarget(args);
|
|
564
600
|
assertNoRemainingArgs(args, "plan done");
|
|
565
|
-
const
|
|
601
|
+
const ownerSessionKey = resolveOwnerSessionKey() ?? undefined;
|
|
602
|
+
const workflowProject = resolveWorkflowProjectContext(process.cwd(), ownerSessionKey);
|
|
603
|
+
const gitNexusPreflightAnalyzed = workflowProject.scope === "project"
|
|
604
|
+
? ensureGitNexusReadyForPlanDone(process.cwd())
|
|
605
|
+
: false;
|
|
566
606
|
const result = await editPlan({
|
|
567
607
|
cwd: process.cwd(),
|
|
568
608
|
...target,
|
|
569
609
|
updates,
|
|
570
610
|
planStatus: "end.completed",
|
|
571
611
|
commandSource: "plan.done",
|
|
572
|
-
host:
|
|
573
|
-
ownerSessionKey
|
|
574
|
-
});
|
|
575
|
-
const completionRefresh = queueCompletionRefresh({
|
|
576
|
-
cwd: process.cwd(),
|
|
577
|
-
taskName: result.taskName,
|
|
578
|
-
skipGitNexusRefresh: gitNexusPreflightAnalyzed,
|
|
612
|
+
host: effectiveHost,
|
|
613
|
+
ownerSessionKey,
|
|
579
614
|
});
|
|
580
|
-
|
|
615
|
+
const completionRefresh = workflowProject.scope === "project"
|
|
616
|
+
? queueCompletionRefresh({
|
|
617
|
+
cwd: process.cwd(),
|
|
618
|
+
taskName: result.taskName,
|
|
619
|
+
skipGitNexusRefresh: gitNexusPreflightAnalyzed,
|
|
620
|
+
})
|
|
621
|
+
: undefined;
|
|
622
|
+
printJson(compactPlanCommandResult("plan.done", result, effectiveHost, completionRefresh));
|
|
581
623
|
return;
|
|
582
624
|
}
|
|
583
625
|
case "show": {
|
|
@@ -586,6 +628,7 @@ async function runPlan(args) {
|
|
|
586
628
|
const result = showPlan({
|
|
587
629
|
cwd: process.cwd(),
|
|
588
630
|
...target,
|
|
631
|
+
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
589
632
|
});
|
|
590
633
|
printJson({
|
|
591
634
|
ok: true,
|
|
@@ -603,17 +646,17 @@ async function runPlan(args) {
|
|
|
603
646
|
throw new ClawError("PROJECT_CONFIG_INVALID", `Unknown plan subcommand "${subcommand ?? ""}".`);
|
|
604
647
|
}
|
|
605
648
|
}
|
|
606
|
-
async function runPlanStatusAlias(args, planStatus, command) {
|
|
649
|
+
async function runPlanStatusAlias(args, planStatus, command, effectiveHost) {
|
|
607
650
|
const result = await editPlan({
|
|
608
651
|
cwd: process.cwd(),
|
|
609
652
|
...readPlanMutationTarget(args),
|
|
610
653
|
planStatus,
|
|
611
654
|
commandSource: "plan.edit",
|
|
612
|
-
host:
|
|
655
|
+
host: effectiveHost,
|
|
613
656
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
614
657
|
});
|
|
615
658
|
assertNoRemainingArgs(args, command);
|
|
616
|
-
printJson(compactPlanCommandResult(command, result));
|
|
659
|
+
printJson(compactPlanCommandResult(command, result, effectiveHost));
|
|
617
660
|
}
|
|
618
661
|
async function runTemplate(args) {
|
|
619
662
|
const subcommand = args.shift();
|
|
@@ -643,6 +686,7 @@ async function runTemplate(args) {
|
|
|
643
686
|
command: "template.validate",
|
|
644
687
|
ok: true,
|
|
645
688
|
templateId: template.id,
|
|
689
|
+
...(template.scope ? { scope: template.scope } : {}),
|
|
646
690
|
source: template.source,
|
|
647
691
|
...(template.templatePath ? { templatePath: template.templatePath } : {}),
|
|
648
692
|
status: template.status,
|
|
@@ -660,7 +704,7 @@ async function runTemplate(args) {
|
|
|
660
704
|
});
|
|
661
705
|
}
|
|
662
706
|
}
|
|
663
|
-
async function runTask(args) {
|
|
707
|
+
async function runTask(args, effectiveHost) {
|
|
664
708
|
const subcommand = args.shift();
|
|
665
709
|
switch (subcommand) {
|
|
666
710
|
case "add": {
|
|
@@ -671,10 +715,10 @@ async function runTask(args) {
|
|
|
671
715
|
...target,
|
|
672
716
|
operations,
|
|
673
717
|
commandSource: "plan.edit",
|
|
674
|
-
host:
|
|
718
|
+
host: effectiveHost,
|
|
675
719
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
676
720
|
});
|
|
677
|
-
printJson(compactPlanCommandResult("task.add", result));
|
|
721
|
+
printJson(compactPlanCommandResult("task.add", result, effectiveHost));
|
|
678
722
|
if (result.operationChain?.status === "partial")
|
|
679
723
|
process.exitCode = 1;
|
|
680
724
|
return;
|
|
@@ -687,10 +731,10 @@ async function runTask(args) {
|
|
|
687
731
|
...target,
|
|
688
732
|
operations,
|
|
689
733
|
commandSource: "plan.edit",
|
|
690
|
-
host:
|
|
734
|
+
host: effectiveHost,
|
|
691
735
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
692
736
|
});
|
|
693
|
-
printJson(compactPlanCommandResult("task.edit", result));
|
|
737
|
+
printJson(compactPlanCommandResult("task.edit", result, effectiveHost));
|
|
694
738
|
if (result.operationChain?.status === "partial")
|
|
695
739
|
process.exitCode = 1;
|
|
696
740
|
return;
|
|
@@ -703,10 +747,10 @@ async function runTask(args) {
|
|
|
703
747
|
...target,
|
|
704
748
|
operations,
|
|
705
749
|
commandSource: "plan.edit",
|
|
706
|
-
host:
|
|
750
|
+
host: effectiveHost,
|
|
707
751
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
708
752
|
});
|
|
709
|
-
printJson(compactPlanCommandResult("task.remove", result));
|
|
753
|
+
printJson(compactPlanCommandResult("task.remove", result, effectiveHost));
|
|
710
754
|
if (result.operationChain?.status === "partial")
|
|
711
755
|
process.exitCode = 1;
|
|
712
756
|
return;
|
|
@@ -718,10 +762,10 @@ async function runTask(args) {
|
|
|
718
762
|
cwd: process.cwd(),
|
|
719
763
|
...target,
|
|
720
764
|
operations,
|
|
721
|
-
host:
|
|
765
|
+
host: effectiveHost,
|
|
722
766
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
723
767
|
});
|
|
724
|
-
printJson(compactPlanCommandResult("task.done", result));
|
|
768
|
+
printJson(compactPlanCommandResult("task.done", result, effectiveHost));
|
|
725
769
|
if (result.operationChain?.status === "partial")
|
|
726
770
|
process.exitCode = 1;
|
|
727
771
|
return;
|
|
@@ -763,7 +807,7 @@ function runSearch(args) {
|
|
|
763
807
|
}),
|
|
764
808
|
});
|
|
765
809
|
}
|
|
766
|
-
function runDirect(args) {
|
|
810
|
+
function runDirect(args, effectiveHost) {
|
|
767
811
|
assertNoRemainingArgs(args, "direct");
|
|
768
812
|
const completionRefresh = queueCompletionRefresh({
|
|
769
813
|
cwd: process.cwd(),
|
|
@@ -774,14 +818,29 @@ function runDirect(args) {
|
|
|
774
818
|
});
|
|
775
819
|
printJson(compactDirectCommandResult("direct", buildDirectWorkflowGuidance({
|
|
776
820
|
projectConfig: resolveProjectContext(process.cwd()).projectConfig,
|
|
777
|
-
host:
|
|
821
|
+
host: effectiveHost,
|
|
778
822
|
}), completionRefresh));
|
|
779
823
|
}
|
|
780
|
-
async function runContextCommand(args, cwd = process.cwd(), ownerSessionKey = resolveOwnerSessionKey()) {
|
|
824
|
+
async function runContextCommand(args, cwd = process.cwd(), ownerSessionKey = resolveOwnerSessionKey(), effectiveHost) {
|
|
781
825
|
const taskName = readOptionalFlag(args, "--task");
|
|
782
826
|
let initialized = false;
|
|
783
827
|
let corrected = false;
|
|
784
828
|
let fixedPaths = [];
|
|
829
|
+
const sessionProject = resolveSessionWorkflowContext(ownerSessionKey ?? undefined);
|
|
830
|
+
if (sessionProject) {
|
|
831
|
+
const activeWorkflow = !taskName && ownerSessionKey
|
|
832
|
+
? await tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey, effectiveHost)
|
|
833
|
+
: null;
|
|
834
|
+
const codexRuntime = effectiveHost === "codex" ? checkCodexRuntime() : null;
|
|
835
|
+
const codexRuntimeError = codexRuntime && !codexRuntime.ok
|
|
836
|
+
? buildCodexRuntimeError(codexRuntime.detail)
|
|
837
|
+
: null;
|
|
838
|
+
return {
|
|
839
|
+
project: sessionProject,
|
|
840
|
+
...(activeWorkflow ? { activeWorkflow } : {}),
|
|
841
|
+
...(codexRuntimeError ? { error: codexRuntimeError } : {}),
|
|
842
|
+
};
|
|
843
|
+
}
|
|
785
844
|
try {
|
|
786
845
|
const ensureResult = ensureProjectProtocol(cwd);
|
|
787
846
|
corrected = ensureResult.changed;
|
|
@@ -804,9 +863,9 @@ async function runContextCommand(args, cwd = process.cwd(), ownerSessionKey = re
|
|
|
804
863
|
resolved = resolveContext(cwd, taskName);
|
|
805
864
|
}
|
|
806
865
|
const activeWorkflow = !taskName && ownerSessionKey
|
|
807
|
-
? await tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey)
|
|
866
|
+
? await tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey, effectiveHost)
|
|
808
867
|
: null;
|
|
809
|
-
const codexRuntime =
|
|
868
|
+
const codexRuntime = effectiveHost === "codex" ? checkCodexRuntime() : null;
|
|
810
869
|
const codexRuntimeError = codexRuntime && !codexRuntime.ok
|
|
811
870
|
? buildCodexRuntimeError(codexRuntime.detail)
|
|
812
871
|
: null;
|
|
@@ -840,6 +899,7 @@ function buildPublicContextOutput(context) {
|
|
|
840
899
|
const output = {};
|
|
841
900
|
if (project) {
|
|
842
901
|
output.project = {
|
|
902
|
+
...(project.scope === "session" ? { scope: "session" } : {}),
|
|
843
903
|
projectRoot: project.projectRoot,
|
|
844
904
|
clawDir: project.clawDir,
|
|
845
905
|
projectId: project.projectId,
|
|
@@ -854,6 +914,12 @@ function buildPublicContextOutput(context) {
|
|
|
854
914
|
if (context.activeWorkflow !== undefined) {
|
|
855
915
|
output.activeWorkflow = context.activeWorkflow;
|
|
856
916
|
}
|
|
917
|
+
else {
|
|
918
|
+
output.session = {
|
|
919
|
+
boundPlan: false,
|
|
920
|
+
note: "No plan is bound to this session yet. Ask the user for the task scope, or run `claw plan create` when ready to start one.",
|
|
921
|
+
};
|
|
922
|
+
}
|
|
857
923
|
if (context.error !== undefined) {
|
|
858
924
|
output.error = context.error;
|
|
859
925
|
}
|
|
@@ -1011,17 +1077,17 @@ function runTruth(args) {
|
|
|
1011
1077
|
throw new ClawError("PROJECT_CONFIG_INVALID", `Unknown truth subcommand "${subcommand ?? ""}".`);
|
|
1012
1078
|
}
|
|
1013
1079
|
}
|
|
1014
|
-
async function runHook(args) {
|
|
1080
|
+
async function runHook(args, effectiveHost) {
|
|
1015
1081
|
const eventName = args.shift();
|
|
1016
1082
|
if (!eventName) {
|
|
1017
1083
|
throw new ClawError("PROJECT_CONFIG_INVALID", "claw hook requires an event name.");
|
|
1018
1084
|
}
|
|
1019
1085
|
if (eventName === "SessionStart" || eventName === "auto-claw") {
|
|
1020
|
-
await runSessionStartHook();
|
|
1086
|
+
await runSessionStartHook(effectiveHost);
|
|
1021
1087
|
return;
|
|
1022
1088
|
}
|
|
1023
1089
|
if (eventName === "Stop" || eventName === "auto-doc") {
|
|
1024
|
-
await runStopHook();
|
|
1090
|
+
await runStopHook(effectiveHost);
|
|
1025
1091
|
return;
|
|
1026
1092
|
}
|
|
1027
1093
|
const project = tryResolveHookProject(process.cwd());
|
|
@@ -1058,7 +1124,7 @@ async function runHook(args) {
|
|
|
1058
1124
|
logPath,
|
|
1059
1125
|
});
|
|
1060
1126
|
}
|
|
1061
|
-
async function runStopHook() {
|
|
1127
|
+
async function runStopHook(effectiveHost) {
|
|
1062
1128
|
if (process.env.CLAW_KNOWLEDGE_FINALIZER === "1") {
|
|
1063
1129
|
return;
|
|
1064
1130
|
}
|
|
@@ -1084,7 +1150,7 @@ async function runStopHook() {
|
|
|
1084
1150
|
sessionId,
|
|
1085
1151
|
turnId,
|
|
1086
1152
|
message,
|
|
1087
|
-
host:
|
|
1153
|
+
host: effectiveHost,
|
|
1088
1154
|
});
|
|
1089
1155
|
if (result.ok && result.jobPath && process.env.CLAW_KNOWLEDGE_FINALIZER_DISABLE_LAUNCH !== "1") {
|
|
1090
1156
|
launchKnowledgeFinalizationWorker(result.jobPath, project.projectRoot);
|
|
@@ -1101,8 +1167,8 @@ async function runInternalKnowledgeFinalize(args) {
|
|
|
1101
1167
|
return;
|
|
1102
1168
|
}
|
|
1103
1169
|
try {
|
|
1104
|
-
const run = await runKnowledgeWriterForJob(running);
|
|
1105
1170
|
const project = resolveProjectContext(running.projectRoot);
|
|
1171
|
+
const writerRun = await runKnowledgeWriterForJob(running);
|
|
1106
1172
|
const truthEncoding = normalizeTruthMarkdownEncoding(project);
|
|
1107
1173
|
queueCompletionRefresh({
|
|
1108
1174
|
cwd: running.projectRoot,
|
|
@@ -1116,8 +1182,8 @@ async function runInternalKnowledgeFinalize(args) {
|
|
|
1116
1182
|
...running,
|
|
1117
1183
|
status: "succeeded",
|
|
1118
1184
|
finishedAt: new Date().toISOString(),
|
|
1119
|
-
...(
|
|
1120
|
-
finalResponse:
|
|
1185
|
+
...(writerRun.threadId ? { sdkThreadId: writerRun.threadId } : {}),
|
|
1186
|
+
finalResponse: writerRun.finalResponse,
|
|
1121
1187
|
truthEncoding,
|
|
1122
1188
|
});
|
|
1123
1189
|
tryCleanupKnowledgeFinalizationReport(project, running.reportPath);
|
|
@@ -1152,6 +1218,9 @@ async function runKnowledgeWriterForJob(running) {
|
|
|
1152
1218
|
...(result.threadId ? { threadId: result.threadId } : {}),
|
|
1153
1219
|
};
|
|
1154
1220
|
}
|
|
1221
|
+
if (running.host !== undefined && running.host !== null && running.host !== "codex") {
|
|
1222
|
+
throw new Error(`Unsupported knowledge finalization job host "${String(running.host)}".`);
|
|
1223
|
+
}
|
|
1155
1224
|
return runCodexSdkWriter(running);
|
|
1156
1225
|
}
|
|
1157
1226
|
async function runCodexSdkWriter(running) {
|
|
@@ -1182,7 +1251,7 @@ async function runCodexSdkWriter(running) {
|
|
|
1182
1251
|
}
|
|
1183
1252
|
function knowledgeFinalizerEnvironment() {
|
|
1184
1253
|
const env = {};
|
|
1185
|
-
for (const [key, value] of Object.entries(
|
|
1254
|
+
for (const [key, value] of Object.entries(withoutInvocationHost())) {
|
|
1186
1255
|
if (value !== undefined) {
|
|
1187
1256
|
env[key] = value;
|
|
1188
1257
|
}
|
|
@@ -1194,10 +1263,11 @@ function buildKnowledgeWriterPrompt(job) {
|
|
|
1194
1263
|
const writerSkill = job.writer?.externalSkill?.trim() || "claw-kit:knowledge-writer";
|
|
1195
1264
|
return [
|
|
1196
1265
|
`Use the ${writerSkill} skill and follow it exactly.`,
|
|
1266
|
+
"Act as the knowledge-base steward: maintain Truth and ADR together, preserve one current owner, and reconcile related current claims before completion.",
|
|
1197
1267
|
`Completed plan: ${job.planPath}`,
|
|
1198
1268
|
`Turn report: ${job.reportPath}`,
|
|
1199
1269
|
`Finalization id: ${job.finalizeId}`,
|
|
1200
|
-
"Treat
|
|
1270
|
+
"Treat the completed plan and report as verified evidence. Do not edit either input, do not dispatch subagents, and do not repeat implementation or test verification.",
|
|
1201
1271
|
].join("\n");
|
|
1202
1272
|
}
|
|
1203
1273
|
function launchKnowledgeFinalizationWorker(jobPath, cwd) {
|
|
@@ -1214,7 +1284,7 @@ function launchKnowledgeFinalizationWorker(jobPath, cwd) {
|
|
|
1214
1284
|
stdio: "ignore",
|
|
1215
1285
|
windowsHide: true,
|
|
1216
1286
|
env: {
|
|
1217
|
-
...
|
|
1287
|
+
...withoutInvocationHost(),
|
|
1218
1288
|
CLAW_KNOWLEDGE_NODE: process.execPath,
|
|
1219
1289
|
CLAW_KNOWLEDGE_ENTRY: resolveCliEntryPath(),
|
|
1220
1290
|
CLAW_KNOWLEDGE_JOB: jobPath,
|
|
@@ -1226,22 +1296,29 @@ function launchKnowledgeFinalizationWorker(jobPath, cwd) {
|
|
|
1226
1296
|
}
|
|
1227
1297
|
return;
|
|
1228
1298
|
}
|
|
1229
|
-
const child = spawn(process.execPath, [resolveCliEntryPath(), "internal-knowledge-finalize", "--job", jobPath], { cwd, detached: true, stdio: "ignore", windowsHide: true });
|
|
1299
|
+
const child = spawn(process.execPath, [resolveCliEntryPath(), "internal-knowledge-finalize", "--job", jobPath], { cwd, detached: true, stdio: "ignore", windowsHide: true, env: withoutInvocationHost() });
|
|
1230
1300
|
child.unref();
|
|
1231
1301
|
}
|
|
1232
|
-
async function runSessionStartHook() {
|
|
1302
|
+
async function runSessionStartHook(effectiveHost) {
|
|
1233
1303
|
if (process.env.CLAW_KNOWLEDGE_FINALIZER === "1") {
|
|
1234
1304
|
return;
|
|
1235
1305
|
}
|
|
1236
1306
|
const payload = await readStdinJson();
|
|
1237
1307
|
const hookCwd = resolveHookCwd(payload);
|
|
1238
1308
|
const ownerSessionKey = resolveOwnerSessionKey(payload);
|
|
1239
|
-
if (!hookCwd
|
|
1309
|
+
if (!hookCwd) {
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
const sessionProject = resolveSessionWorkflowContext(ownerSessionKey ?? undefined);
|
|
1313
|
+
if (!containsClawDir(hookCwd) && !sessionProject) {
|
|
1240
1314
|
return;
|
|
1241
1315
|
}
|
|
1242
1316
|
try {
|
|
1243
|
-
const context = await runContextCommand([], hookCwd, ownerSessionKey);
|
|
1244
|
-
|
|
1317
|
+
const context = await runContextCommand([], hookCwd, ownerSessionKey, effectiveHost);
|
|
1318
|
+
const contextProject = asJsonRecord(context.project);
|
|
1319
|
+
if (contextProject?.scope !== "session"
|
|
1320
|
+
&& !context.error
|
|
1321
|
+
&& process.env.CLAW_KNOWLEDGE_FINALIZER_DISABLE_LAUNCH !== "1") {
|
|
1245
1322
|
const project = resolveProjectContext(hookCwd);
|
|
1246
1323
|
for (const jobPath of listRetryableKnowledgeFinalizationJobs(project)) {
|
|
1247
1324
|
try {
|
|
@@ -1499,8 +1576,8 @@ function summarizeRecoveredPlanContent(planContent) {
|
|
|
1499
1576
|
}
|
|
1500
1577
|
return lines.length > 0 ? lines : ["- plan content present in activeWorkflow.planContent JSON."];
|
|
1501
1578
|
}
|
|
1502
|
-
async function tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey) {
|
|
1503
|
-
const project =
|
|
1579
|
+
async function tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey, effectiveHost) {
|
|
1580
|
+
const project = resolveWorkflowProjectContext(cwd, ownerSessionKey);
|
|
1504
1581
|
const planPath = resolveSessionBoundPlan(project, ownerSessionKey);
|
|
1505
1582
|
if (!planPath) {
|
|
1506
1583
|
return null;
|
|
@@ -1518,6 +1595,7 @@ async function tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey) {
|
|
|
1518
1595
|
cwd,
|
|
1519
1596
|
taskName,
|
|
1520
1597
|
planFile,
|
|
1598
|
+
ownerSessionKey,
|
|
1521
1599
|
});
|
|
1522
1600
|
if (result.plan.status.startsWith("end.")) {
|
|
1523
1601
|
unbindSession(project, ownerSessionKey);
|
|
@@ -1536,6 +1614,7 @@ async function tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey) {
|
|
|
1536
1614
|
plan: result.plan,
|
|
1537
1615
|
projectRoot: project.projectRoot,
|
|
1538
1616
|
projectConfig: project.projectConfig,
|
|
1617
|
+
host: effectiveHost,
|
|
1539
1618
|
}),
|
|
1540
1619
|
};
|
|
1541
1620
|
}
|
|
@@ -1544,7 +1623,7 @@ async function tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey) {
|
|
|
1544
1623
|
return null;
|
|
1545
1624
|
}
|
|
1546
1625
|
}
|
|
1547
|
-
async function runSubplan(args) {
|
|
1626
|
+
async function runSubplan(args, effectiveHost) {
|
|
1548
1627
|
const subcommand = args.shift();
|
|
1549
1628
|
switch (subcommand) {
|
|
1550
1629
|
case "create": {
|
|
@@ -1554,9 +1633,10 @@ async function runSubplan(args) {
|
|
|
1554
1633
|
parentTaskId: readOptionalNumber(args, "--task-id") ?? failMissingNumericFlag("--task-id"),
|
|
1555
1634
|
templateName: readOptionalFlag(args, "--template") ?? undefined,
|
|
1556
1635
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
1636
|
+
host: effectiveHost,
|
|
1557
1637
|
});
|
|
1558
1638
|
assertNoRemainingArgs(args, "subplan create");
|
|
1559
|
-
printJson(compactPlanCommandResult("subplan.create", result));
|
|
1639
|
+
printJson(compactPlanCommandResult("subplan.create", result, effectiveHost));
|
|
1560
1640
|
return;
|
|
1561
1641
|
}
|
|
1562
1642
|
default:
|
|
@@ -1601,11 +1681,14 @@ function summarizeGoalMode(value) {
|
|
|
1601
1681
|
return value.recommendedObjective.trim();
|
|
1602
1682
|
}
|
|
1603
1683
|
async function readStdinJson() {
|
|
1684
|
+
const bufferedInput = consumeBufferedHookInput();
|
|
1604
1685
|
const chunks = [];
|
|
1605
|
-
|
|
1606
|
-
|
|
1686
|
+
if (bufferedInput === null) {
|
|
1687
|
+
for await (const chunk of process.stdin) {
|
|
1688
|
+
chunks.push(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
|
|
1689
|
+
}
|
|
1607
1690
|
}
|
|
1608
|
-
const raw = chunks.join("").trim();
|
|
1691
|
+
const raw = (bufferedInput ?? chunks.join("")).trim();
|
|
1609
1692
|
if (!raw) {
|
|
1610
1693
|
return null;
|
|
1611
1694
|
}
|
|
@@ -1622,14 +1705,14 @@ function readJson(filePath) {
|
|
|
1622
1705
|
function stripBom(content) {
|
|
1623
1706
|
return content.charCodeAt(0) === 0xfeff ? content.slice(1) : content;
|
|
1624
1707
|
}
|
|
1625
|
-
function compactPlanCommandResult(command, result, completionRefresh) {
|
|
1708
|
+
function compactPlanCommandResult(command, result, effectiveHost, completionRefresh) {
|
|
1626
1709
|
const archivedPlanPath = completionRefresh?.taskRetention.archivedCurrentTask?.taskName === result.taskName &&
|
|
1627
1710
|
completionRefresh.taskRetention.archivedCurrentTask.archivedPlanPath
|
|
1628
1711
|
? completionRefresh.taskRetention.archivedCurrentTask.archivedPlanPath
|
|
1629
1712
|
: undefined;
|
|
1630
1713
|
const resolvedPlanPath = archivedPlanPath ?? result.planPath;
|
|
1631
|
-
const
|
|
1632
|
-
const
|
|
1714
|
+
const codexResult = effectiveHost === "codex";
|
|
1715
|
+
const hostActions = codexResult ? buildHostActions(result) : [];
|
|
1633
1716
|
return {
|
|
1634
1717
|
ok: true,
|
|
1635
1718
|
command,
|
|
@@ -1637,8 +1720,6 @@ function compactPlanCommandResult(command, result, completionRefresh) {
|
|
|
1637
1720
|
...(archivedPlanPath ? { archivedPlanPath } : {}),
|
|
1638
1721
|
planStatus: result.planStatus,
|
|
1639
1722
|
...(!codexResult && result.previousPlanStatus ? { previousPlanStatus: result.previousPlanStatus } : {}),
|
|
1640
|
-
...(!codexResult && result.emittedEvents?.length ? { emittedEvents: result.emittedEvents } : {}),
|
|
1641
|
-
...(!codexResult && result.events?.length ? { events: result.events } : {}),
|
|
1642
1723
|
...(hostActions.length ? { hostActions } : {}),
|
|
1643
1724
|
...(!codexResult && result.changedTaskIds?.length ? { changedTaskIds: result.changedTaskIds } : {}),
|
|
1644
1725
|
...(!codexResult && result.appendedTaskIds?.length ? { appendedTaskIds: result.appendedTaskIds } : {}),
|
|
@@ -1944,7 +2025,7 @@ function readPlanMutationTarget(args) {
|
|
|
1944
2025
|
...(explicitPlanFile ? { planFile: explicitPlanFile } : {}),
|
|
1945
2026
|
};
|
|
1946
2027
|
}
|
|
1947
|
-
const project =
|
|
2028
|
+
const project = resolveWorkflowProjectContext(process.cwd(), resolveOwnerSessionKey() ?? undefined);
|
|
1948
2029
|
const boundPlanPath = resolveSessionBoundPlan(project, resolveOwnerSessionKey() ?? undefined);
|
|
1949
2030
|
if (!boundPlanPath) {
|
|
1950
2031
|
throw new ClawError("PROJECT_CONFIG_INVALID", "No plan is bound to the current session. Create or recover a plan first, or use --task-name and optional --plan-file as an advanced override.");
|
|
@@ -2098,7 +2179,7 @@ function launchCompletionRefreshWorker(input) {
|
|
|
2098
2179
|
stdio: "ignore",
|
|
2099
2180
|
windowsHide: true,
|
|
2100
2181
|
env: {
|
|
2101
|
-
...
|
|
2182
|
+
...withoutInvocationHost(),
|
|
2102
2183
|
CLAW_COMPLETION_NODE: process.execPath,
|
|
2103
2184
|
CLAW_COMPLETION_ENTRY: resolveCliEntryPath(),
|
|
2104
2185
|
CLAW_COMPLETION_CWD: input.cwd,
|
|
@@ -2134,6 +2215,7 @@ function launchCompletionRefreshWorker(input) {
|
|
|
2134
2215
|
detached: true,
|
|
2135
2216
|
stdio: "ignore",
|
|
2136
2217
|
windowsHide: true,
|
|
2218
|
+
env: withoutInvocationHost(),
|
|
2137
2219
|
});
|
|
2138
2220
|
child.unref();
|
|
2139
2221
|
}
|
|
@@ -2776,6 +2858,18 @@ function normalizeVersionString(value) {
|
|
|
2776
2858
|
const trimmed = value.trim();
|
|
2777
2859
|
return trimmed ? trimmed : null;
|
|
2778
2860
|
}
|
|
2861
|
+
function readWorkflowScope(args) {
|
|
2862
|
+
const value = readOptionalFlag(args, "--scope");
|
|
2863
|
+
if (value === undefined) {
|
|
2864
|
+
return undefined;
|
|
2865
|
+
}
|
|
2866
|
+
if (value === "session") {
|
|
2867
|
+
return "session";
|
|
2868
|
+
}
|
|
2869
|
+
throw new ClawError("PROJECT_CONFIG_INVALID", "--scope currently accepts only session; omit it for project scope.", {
|
|
2870
|
+
scope: value,
|
|
2871
|
+
});
|
|
2872
|
+
}
|
|
2779
2873
|
function readOptionalFlag(args, flag) {
|
|
2780
2874
|
const index = args.indexOf(flag);
|
|
2781
2875
|
if (index === -1) {
|