@veewo/claw 0.1.77 → 0.1.79
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 +72 -69
- package/dist/bin.js +8 -2
- package/dist/bin.js.map +1 -1
- package/dist/cli.js +229 -122
- package/dist/cli.js.map +1 -1
- package/dist/codex-driver.js +0 -1
- package/dist/codex-driver.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.d.ts +23 -0
- package/dist/opencode-runner.js +105 -0
- package/dist/opencode-runner.js.map +1 -0
- package/package.json +41 -41
package/dist/cli.js
CHANGED
|
@@ -5,14 +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";
|
|
14
|
+
import { runOpencodeKnowledgeWriter } from "./opencode-runner.js";
|
|
12
15
|
const CLI_VERSION = readCliVersion();
|
|
13
16
|
const TOP_LEVEL_COMMANDS = [
|
|
14
17
|
{ name: "init [options]", summary: "Initialize and normalize the .claw project surface." },
|
|
15
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." },
|
|
16
20
|
{ name: "check", summary: "Check and auto-correct .claw project protocol fields." },
|
|
17
21
|
{ name: "plan <subcommand> [options]", summary: "Plan lifecycle: create, start, edit, remove, wait, resume, show, done." },
|
|
18
22
|
{ name: "codex driver", summary: "Return the versioned code-mode driver used by the Codex adapter." },
|
|
@@ -33,7 +37,7 @@ const COMMAND_HELP = {
|
|
|
33
37
|
{ flag: "--name <project-name>", detail: "Human-readable project name." },
|
|
34
38
|
{ flag: "--context-path <file>", detail: "Extra context path to track (repeatable)." },
|
|
35
39
|
{ flag: "--ext-path <path>", detail: "External doc path to index (repeatable)." },
|
|
36
|
-
{ 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." },
|
|
37
41
|
{ flag: "--planning true|false", detail: "Enable planning-aware default template behavior (default true)." },
|
|
38
42
|
{ flag: "--external-planning-skill <skill>", detail: "Skill id for an external planning skill." },
|
|
39
43
|
{ flag: "--gitnexus true|false", detail: "Enable GitNexus integration (default false)." },
|
|
@@ -48,6 +52,10 @@ const COMMAND_HELP = {
|
|
|
48
52
|
{ flag: "--task <name>", detail: "Resolve context scoped to a specific task." },
|
|
49
53
|
],
|
|
50
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
|
+
},
|
|
51
59
|
check: {
|
|
52
60
|
usage: ["{script} check"],
|
|
53
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.",
|
|
@@ -58,14 +66,15 @@ const COMMAND_HELP = {
|
|
|
58
66
|
subcommands: {
|
|
59
67
|
create: {
|
|
60
68
|
usage: [
|
|
61
|
-
"{script} plan create \"<title>\" [--goal <text>]",
|
|
62
|
-
"{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]",
|
|
63
71
|
],
|
|
64
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.",
|
|
65
73
|
summary: "Create the task scope and initial plan.",
|
|
66
74
|
options: [
|
|
67
75
|
{ flag: "--title <text>", detail: "Task title (required unless a positional title is given)." },
|
|
68
76
|
{ flag: "--goal <text>", detail: "Optional goal text." },
|
|
77
|
+
{ flag: "--scope session", detail: "Use ephemeral per-session storage and disable project knowledge side effects." },
|
|
69
78
|
{ flag: "--template <name>", detail: "Optional plan template name. Overrides the project's configured default template." },
|
|
70
79
|
],
|
|
71
80
|
},
|
|
@@ -328,19 +337,20 @@ const COMMAND_HELP = {
|
|
|
328
337
|
},
|
|
329
338
|
"internal-knowledge-finalize": {
|
|
330
339
|
usage: ["{script} internal-knowledge-finalize --job <path>"],
|
|
331
|
-
description: "Internal: runs one queued knowledge deposition job through the Codex SDK.",
|
|
340
|
+
description: "Internal: runs one queued knowledge deposition job through the host-aware finalization runner (Codex SDK for codex host, opencode run for opencode host).",
|
|
332
341
|
options: [{ flag: "--job <path>", detail: "(required) Finalization job JSON path." }],
|
|
333
342
|
},
|
|
334
343
|
};
|
|
335
344
|
async function main() {
|
|
336
345
|
const args = process.argv.slice(2);
|
|
337
346
|
const explicitHost = readOptionalFlag(args, "--host");
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
347
|
+
let effectiveHost;
|
|
348
|
+
try {
|
|
349
|
+
effectiveHost = resolveInvocationHost(explicitHost, process.env.CLAW_HOST);
|
|
350
|
+
}
|
|
351
|
+
catch (error) {
|
|
352
|
+
handleError(error);
|
|
353
|
+
return;
|
|
344
354
|
}
|
|
345
355
|
const command = args.shift();
|
|
346
356
|
if (command === "--help" || command === "-h") {
|
|
@@ -382,7 +392,10 @@ async function main() {
|
|
|
382
392
|
printJson(initProject(initInput));
|
|
383
393
|
return;
|
|
384
394
|
case "context":
|
|
385
|
-
printJson(buildPublicContextOutput(await runContextCommand(args)));
|
|
395
|
+
printJson(buildPublicContextOutput(await runContextCommand(args, process.cwd(), resolveOwnerSessionKey(), effectiveHost)));
|
|
396
|
+
return;
|
|
397
|
+
case "session":
|
|
398
|
+
runSession(args);
|
|
386
399
|
return;
|
|
387
400
|
case "check":
|
|
388
401
|
const checkResult = ensureProjectProtocol(process.cwd());
|
|
@@ -397,7 +410,7 @@ async function main() {
|
|
|
397
410
|
});
|
|
398
411
|
return;
|
|
399
412
|
case "plan":
|
|
400
|
-
await runPlan(args);
|
|
413
|
+
await runPlan(args, effectiveHost);
|
|
401
414
|
return;
|
|
402
415
|
case "codex":
|
|
403
416
|
runCodex(args);
|
|
@@ -406,10 +419,10 @@ async function main() {
|
|
|
406
419
|
await runTemplate(args);
|
|
407
420
|
return;
|
|
408
421
|
case "task":
|
|
409
|
-
await runTask(args);
|
|
422
|
+
await runTask(args, effectiveHost);
|
|
410
423
|
return;
|
|
411
424
|
case "subplan":
|
|
412
|
-
await runSubplan(args);
|
|
425
|
+
await runSubplan(args, effectiveHost);
|
|
413
426
|
return;
|
|
414
427
|
case "switch-task":
|
|
415
428
|
printJson(switchTask({
|
|
@@ -425,13 +438,13 @@ async function main() {
|
|
|
425
438
|
runSearch(args);
|
|
426
439
|
return;
|
|
427
440
|
case "direct":
|
|
428
|
-
runDirect(args);
|
|
441
|
+
runDirect(args, effectiveHost);
|
|
429
442
|
return;
|
|
430
443
|
case "truth":
|
|
431
444
|
runTruth(args);
|
|
432
445
|
return;
|
|
433
446
|
case "hook":
|
|
434
|
-
await runHook(args);
|
|
447
|
+
await runHook(args, effectiveHost);
|
|
435
448
|
return;
|
|
436
449
|
case "help":
|
|
437
450
|
printHelp(args);
|
|
@@ -459,13 +472,36 @@ function runCodex(args) {
|
|
|
459
472
|
assertNoRemainingArgs(args, "codex driver");
|
|
460
473
|
printJson(buildCodexDriverEnvelope(CLI_VERSION));
|
|
461
474
|
}
|
|
462
|
-
|
|
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) {
|
|
463
498
|
const subcommand = args.shift();
|
|
464
499
|
switch (subcommand) {
|
|
465
500
|
case "create":
|
|
466
501
|
rejectFlags(args, ["--task", "--plan", "--content", "--status", "--parent-task-id", "--description"]);
|
|
467
502
|
const explicitTitle = readOptionalFlag(args, "--title");
|
|
468
503
|
const explicitTemplate = readOptionalFlag(args, "--template");
|
|
504
|
+
const scope = readWorkflowScope(args);
|
|
469
505
|
const title = explicitTitle ?? readOptionalPositionalArg(args);
|
|
470
506
|
const templateName = explicitTemplate;
|
|
471
507
|
if (!title) {
|
|
@@ -473,14 +509,15 @@ async function runPlan(args) {
|
|
|
473
509
|
}
|
|
474
510
|
const result = await writePlan({
|
|
475
511
|
cwd: process.cwd(),
|
|
512
|
+
scope,
|
|
476
513
|
templateName,
|
|
477
514
|
title,
|
|
478
515
|
goalText: readOptionalFlag(args, "--goal"),
|
|
479
516
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
480
|
-
host:
|
|
517
|
+
host: effectiveHost,
|
|
481
518
|
});
|
|
482
519
|
assertNoRemainingArgs(args, "plan create");
|
|
483
|
-
printJson(compactPlanCommandResult("plan.create", result));
|
|
520
|
+
printJson(compactPlanCommandResult("plan.create", result, effectiveHost));
|
|
484
521
|
return;
|
|
485
522
|
case "edit": {
|
|
486
523
|
const target = readPlanMutationTarget(args);
|
|
@@ -493,10 +530,10 @@ async function runPlan(args) {
|
|
|
493
530
|
...target,
|
|
494
531
|
operations,
|
|
495
532
|
commandSource: "plan.edit",
|
|
496
|
-
host:
|
|
533
|
+
host: effectiveHost,
|
|
497
534
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
498
535
|
});
|
|
499
|
-
printJson(compactPlanCommandResult("plan.edit", result));
|
|
536
|
+
printJson(compactPlanCommandResult("plan.edit", result, effectiveHost));
|
|
500
537
|
if (result.operationChain?.status === "partial")
|
|
501
538
|
process.exitCode = 1;
|
|
502
539
|
return;
|
|
@@ -513,17 +550,17 @@ async function runPlan(args) {
|
|
|
513
550
|
...target,
|
|
514
551
|
updates,
|
|
515
552
|
commandSource: "plan.edit",
|
|
516
|
-
host:
|
|
553
|
+
host: effectiveHost,
|
|
517
554
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
518
555
|
});
|
|
519
|
-
printJson(compactPlanCommandResult("plan.remove", result));
|
|
556
|
+
printJson(compactPlanCommandResult("plan.remove", result, effectiveHost));
|
|
520
557
|
return;
|
|
521
558
|
}
|
|
522
559
|
case "wait":
|
|
523
|
-
await runPlanStatusAlias(args, "process.wait", "plan.wait");
|
|
560
|
+
await runPlanStatusAlias(args, "process.wait", "plan.wait", effectiveHost);
|
|
524
561
|
return;
|
|
525
562
|
case "resume":
|
|
526
|
-
await runPlanStatusAlias(args, "process.active", "plan.resume");
|
|
563
|
+
await runPlanStatusAlias(args, "process.active", "plan.resume", effectiveHost);
|
|
527
564
|
return;
|
|
528
565
|
case "start": {
|
|
529
566
|
const updates = readPlanFieldUpdates(args);
|
|
@@ -541,10 +578,10 @@ async function runPlan(args) {
|
|
|
541
578
|
planStatus: "process.active",
|
|
542
579
|
completeLifecycleBridge: true,
|
|
543
580
|
commandSource: "plan.start",
|
|
544
|
-
host:
|
|
581
|
+
host: effectiveHost,
|
|
545
582
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
546
583
|
});
|
|
547
|
-
printJson(compactPlanCommandResult("plan.start", result));
|
|
584
|
+
printJson(compactPlanCommandResult("plan.start", result, effectiveHost));
|
|
548
585
|
return;
|
|
549
586
|
}
|
|
550
587
|
case "done": {
|
|
@@ -561,22 +598,28 @@ async function runPlan(args) {
|
|
|
561
598
|
};
|
|
562
599
|
const target = readPlanMutationTarget(args);
|
|
563
600
|
assertNoRemainingArgs(args, "plan done");
|
|
564
|
-
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;
|
|
565
606
|
const result = await editPlan({
|
|
566
607
|
cwd: process.cwd(),
|
|
567
608
|
...target,
|
|
568
609
|
updates,
|
|
569
610
|
planStatus: "end.completed",
|
|
570
611
|
commandSource: "plan.done",
|
|
571
|
-
host:
|
|
572
|
-
ownerSessionKey
|
|
573
|
-
});
|
|
574
|
-
const completionRefresh = queueCompletionRefresh({
|
|
575
|
-
cwd: process.cwd(),
|
|
576
|
-
taskName: result.taskName,
|
|
577
|
-
skipGitNexusRefresh: gitNexusPreflightAnalyzed,
|
|
612
|
+
host: effectiveHost,
|
|
613
|
+
ownerSessionKey,
|
|
578
614
|
});
|
|
579
|
-
|
|
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));
|
|
580
623
|
return;
|
|
581
624
|
}
|
|
582
625
|
case "show": {
|
|
@@ -585,6 +628,7 @@ async function runPlan(args) {
|
|
|
585
628
|
const result = showPlan({
|
|
586
629
|
cwd: process.cwd(),
|
|
587
630
|
...target,
|
|
631
|
+
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
588
632
|
});
|
|
589
633
|
printJson({
|
|
590
634
|
ok: true,
|
|
@@ -602,17 +646,17 @@ async function runPlan(args) {
|
|
|
602
646
|
throw new ClawError("PROJECT_CONFIG_INVALID", `Unknown plan subcommand "${subcommand ?? ""}".`);
|
|
603
647
|
}
|
|
604
648
|
}
|
|
605
|
-
async function runPlanStatusAlias(args, planStatus, command) {
|
|
649
|
+
async function runPlanStatusAlias(args, planStatus, command, effectiveHost) {
|
|
606
650
|
const result = await editPlan({
|
|
607
651
|
cwd: process.cwd(),
|
|
608
652
|
...readPlanMutationTarget(args),
|
|
609
653
|
planStatus,
|
|
610
654
|
commandSource: "plan.edit",
|
|
611
|
-
host:
|
|
655
|
+
host: effectiveHost,
|
|
612
656
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
613
657
|
});
|
|
614
658
|
assertNoRemainingArgs(args, command);
|
|
615
|
-
printJson(compactPlanCommandResult(command, result));
|
|
659
|
+
printJson(compactPlanCommandResult(command, result, effectiveHost));
|
|
616
660
|
}
|
|
617
661
|
async function runTemplate(args) {
|
|
618
662
|
const subcommand = args.shift();
|
|
@@ -642,6 +686,7 @@ async function runTemplate(args) {
|
|
|
642
686
|
command: "template.validate",
|
|
643
687
|
ok: true,
|
|
644
688
|
templateId: template.id,
|
|
689
|
+
...(template.scope ? { scope: template.scope } : {}),
|
|
645
690
|
source: template.source,
|
|
646
691
|
...(template.templatePath ? { templatePath: template.templatePath } : {}),
|
|
647
692
|
status: template.status,
|
|
@@ -659,7 +704,7 @@ async function runTemplate(args) {
|
|
|
659
704
|
});
|
|
660
705
|
}
|
|
661
706
|
}
|
|
662
|
-
async function runTask(args) {
|
|
707
|
+
async function runTask(args, effectiveHost) {
|
|
663
708
|
const subcommand = args.shift();
|
|
664
709
|
switch (subcommand) {
|
|
665
710
|
case "add": {
|
|
@@ -670,10 +715,10 @@ async function runTask(args) {
|
|
|
670
715
|
...target,
|
|
671
716
|
operations,
|
|
672
717
|
commandSource: "plan.edit",
|
|
673
|
-
host:
|
|
718
|
+
host: effectiveHost,
|
|
674
719
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
675
720
|
});
|
|
676
|
-
printJson(compactPlanCommandResult("task.add", result));
|
|
721
|
+
printJson(compactPlanCommandResult("task.add", result, effectiveHost));
|
|
677
722
|
if (result.operationChain?.status === "partial")
|
|
678
723
|
process.exitCode = 1;
|
|
679
724
|
return;
|
|
@@ -686,10 +731,10 @@ async function runTask(args) {
|
|
|
686
731
|
...target,
|
|
687
732
|
operations,
|
|
688
733
|
commandSource: "plan.edit",
|
|
689
|
-
host:
|
|
734
|
+
host: effectiveHost,
|
|
690
735
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
691
736
|
});
|
|
692
|
-
printJson(compactPlanCommandResult("task.edit", result));
|
|
737
|
+
printJson(compactPlanCommandResult("task.edit", result, effectiveHost));
|
|
693
738
|
if (result.operationChain?.status === "partial")
|
|
694
739
|
process.exitCode = 1;
|
|
695
740
|
return;
|
|
@@ -702,10 +747,10 @@ async function runTask(args) {
|
|
|
702
747
|
...target,
|
|
703
748
|
operations,
|
|
704
749
|
commandSource: "plan.edit",
|
|
705
|
-
host:
|
|
750
|
+
host: effectiveHost,
|
|
706
751
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
707
752
|
});
|
|
708
|
-
printJson(compactPlanCommandResult("task.remove", result));
|
|
753
|
+
printJson(compactPlanCommandResult("task.remove", result, effectiveHost));
|
|
709
754
|
if (result.operationChain?.status === "partial")
|
|
710
755
|
process.exitCode = 1;
|
|
711
756
|
return;
|
|
@@ -717,10 +762,10 @@ async function runTask(args) {
|
|
|
717
762
|
cwd: process.cwd(),
|
|
718
763
|
...target,
|
|
719
764
|
operations,
|
|
720
|
-
host:
|
|
765
|
+
host: effectiveHost,
|
|
721
766
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
722
767
|
});
|
|
723
|
-
printJson(compactPlanCommandResult("task.done", result));
|
|
768
|
+
printJson(compactPlanCommandResult("task.done", result, effectiveHost));
|
|
724
769
|
if (result.operationChain?.status === "partial")
|
|
725
770
|
process.exitCode = 1;
|
|
726
771
|
return;
|
|
@@ -762,7 +807,7 @@ function runSearch(args) {
|
|
|
762
807
|
}),
|
|
763
808
|
});
|
|
764
809
|
}
|
|
765
|
-
function runDirect(args) {
|
|
810
|
+
function runDirect(args, effectiveHost) {
|
|
766
811
|
assertNoRemainingArgs(args, "direct");
|
|
767
812
|
const completionRefresh = queueCompletionRefresh({
|
|
768
813
|
cwd: process.cwd(),
|
|
@@ -773,14 +818,29 @@ function runDirect(args) {
|
|
|
773
818
|
});
|
|
774
819
|
printJson(compactDirectCommandResult("direct", buildDirectWorkflowGuidance({
|
|
775
820
|
projectConfig: resolveProjectContext(process.cwd()).projectConfig,
|
|
776
|
-
host:
|
|
821
|
+
host: effectiveHost,
|
|
777
822
|
}), completionRefresh));
|
|
778
823
|
}
|
|
779
|
-
async function runContextCommand(args, cwd = process.cwd(), ownerSessionKey = resolveOwnerSessionKey()) {
|
|
824
|
+
async function runContextCommand(args, cwd = process.cwd(), ownerSessionKey = resolveOwnerSessionKey(), effectiveHost) {
|
|
780
825
|
const taskName = readOptionalFlag(args, "--task");
|
|
781
826
|
let initialized = false;
|
|
782
827
|
let corrected = false;
|
|
783
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
|
+
}
|
|
784
844
|
try {
|
|
785
845
|
const ensureResult = ensureProjectProtocol(cwd);
|
|
786
846
|
corrected = ensureResult.changed;
|
|
@@ -803,9 +863,9 @@ async function runContextCommand(args, cwd = process.cwd(), ownerSessionKey = re
|
|
|
803
863
|
resolved = resolveContext(cwd, taskName);
|
|
804
864
|
}
|
|
805
865
|
const activeWorkflow = !taskName && ownerSessionKey
|
|
806
|
-
? await tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey)
|
|
866
|
+
? await tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey, effectiveHost)
|
|
807
867
|
: null;
|
|
808
|
-
const codexRuntime =
|
|
868
|
+
const codexRuntime = effectiveHost === "codex" ? checkCodexRuntime() : null;
|
|
809
869
|
const codexRuntimeError = codexRuntime && !codexRuntime.ok
|
|
810
870
|
? buildCodexRuntimeError(codexRuntime.detail)
|
|
811
871
|
: null;
|
|
@@ -839,6 +899,7 @@ function buildPublicContextOutput(context) {
|
|
|
839
899
|
const output = {};
|
|
840
900
|
if (project) {
|
|
841
901
|
output.project = {
|
|
902
|
+
...(project.scope === "session" ? { scope: "session" } : {}),
|
|
842
903
|
projectRoot: project.projectRoot,
|
|
843
904
|
clawDir: project.clawDir,
|
|
844
905
|
projectId: project.projectId,
|
|
@@ -853,6 +914,12 @@ function buildPublicContextOutput(context) {
|
|
|
853
914
|
if (context.activeWorkflow !== undefined) {
|
|
854
915
|
output.activeWorkflow = context.activeWorkflow;
|
|
855
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
|
+
}
|
|
856
923
|
if (context.error !== undefined) {
|
|
857
924
|
output.error = context.error;
|
|
858
925
|
}
|
|
@@ -1010,17 +1077,17 @@ function runTruth(args) {
|
|
|
1010
1077
|
throw new ClawError("PROJECT_CONFIG_INVALID", `Unknown truth subcommand "${subcommand ?? ""}".`);
|
|
1011
1078
|
}
|
|
1012
1079
|
}
|
|
1013
|
-
async function runHook(args) {
|
|
1080
|
+
async function runHook(args, effectiveHost) {
|
|
1014
1081
|
const eventName = args.shift();
|
|
1015
1082
|
if (!eventName) {
|
|
1016
1083
|
throw new ClawError("PROJECT_CONFIG_INVALID", "claw hook requires an event name.");
|
|
1017
1084
|
}
|
|
1018
1085
|
if (eventName === "SessionStart" || eventName === "auto-claw") {
|
|
1019
|
-
await runSessionStartHook();
|
|
1086
|
+
await runSessionStartHook(effectiveHost);
|
|
1020
1087
|
return;
|
|
1021
1088
|
}
|
|
1022
1089
|
if (eventName === "Stop" || eventName === "auto-doc") {
|
|
1023
|
-
await runStopHook();
|
|
1090
|
+
await runStopHook(effectiveHost);
|
|
1024
1091
|
return;
|
|
1025
1092
|
}
|
|
1026
1093
|
const project = tryResolveHookProject(process.cwd());
|
|
@@ -1057,7 +1124,7 @@ async function runHook(args) {
|
|
|
1057
1124
|
logPath,
|
|
1058
1125
|
});
|
|
1059
1126
|
}
|
|
1060
|
-
async function runStopHook() {
|
|
1127
|
+
async function runStopHook(effectiveHost) {
|
|
1061
1128
|
if (process.env.CLAW_KNOWLEDGE_FINALIZER === "1") {
|
|
1062
1129
|
return;
|
|
1063
1130
|
}
|
|
@@ -1066,12 +1133,15 @@ async function runStopHook() {
|
|
|
1066
1133
|
const sessionId = resolveOwnerSessionKey(payload);
|
|
1067
1134
|
const turnId = readHookString(payload, "turn_id");
|
|
1068
1135
|
const transcriptPath = readHookString(payload, "transcript_path");
|
|
1069
|
-
|
|
1136
|
+
const payloadMessage = readHookString(payload, "message");
|
|
1137
|
+
if (!hookCwd || !sessionId || !turnId || !containsClawDir(hookCwd)) {
|
|
1070
1138
|
return;
|
|
1071
1139
|
}
|
|
1072
1140
|
try {
|
|
1073
1141
|
const project = resolveProjectContext(hookCwd);
|
|
1074
|
-
|
|
1142
|
+
// Codex passes a transcript_path; opencode and other hosts without a file
|
|
1143
|
+
// transcript pass the final assistant message inline. Either source is valid.
|
|
1144
|
+
const message = payloadMessage ?? (transcriptPath ? extractLatestFinalAssistantMessage(transcriptPath) : null);
|
|
1075
1145
|
if (!message) {
|
|
1076
1146
|
return;
|
|
1077
1147
|
}
|
|
@@ -1080,6 +1150,7 @@ async function runStopHook() {
|
|
|
1080
1150
|
sessionId,
|
|
1081
1151
|
turnId,
|
|
1082
1152
|
message,
|
|
1153
|
+
host: effectiveHost,
|
|
1083
1154
|
});
|
|
1084
1155
|
if (result.ok && result.jobPath && process.env.CLAW_KNOWLEDGE_FINALIZER_DISABLE_LAUNCH !== "1") {
|
|
1085
1156
|
launchKnowledgeFinalizationWorker(result.jobPath, project.projectRoot);
|
|
@@ -1096,27 +1167,8 @@ async function runInternalKnowledgeFinalize(args) {
|
|
|
1096
1167
|
return;
|
|
1097
1168
|
}
|
|
1098
1169
|
try {
|
|
1099
|
-
const sdk = await import(pathToFileURL(resolveCodexSdkEntryPath()).href);
|
|
1100
|
-
const Codex = sdk.Codex;
|
|
1101
|
-
const codex = new Codex({
|
|
1102
|
-
env: knowledgeFinalizerEnvironment(),
|
|
1103
|
-
...(process.env.CLAW_CODEX_PATH_OVERRIDE
|
|
1104
|
-
? { codexPathOverride: process.env.CLAW_CODEX_PATH_OVERRIDE }
|
|
1105
|
-
: {}),
|
|
1106
|
-
});
|
|
1107
|
-
const writer = running.writer ?? { model: null, reasoningEffort: "medium" };
|
|
1108
|
-
const thread = codex.startThread({
|
|
1109
|
-
workingDirectory: running.projectRoot,
|
|
1110
|
-
sandboxMode: "workspace-write",
|
|
1111
|
-
approvalPolicy: "never",
|
|
1112
|
-
networkAccessEnabled: false,
|
|
1113
|
-
...(writer.model ? { model: writer.model } : {}),
|
|
1114
|
-
...(writer.reasoningEffort
|
|
1115
|
-
? { modelReasoningEffort: writer.reasoningEffort }
|
|
1116
|
-
: {}),
|
|
1117
|
-
});
|
|
1118
|
-
const turn = await thread.run(buildKnowledgeWriterPrompt(running));
|
|
1119
1170
|
const project = resolveProjectContext(running.projectRoot);
|
|
1171
|
+
const writerRun = await runKnowledgeWriterForJob(running);
|
|
1120
1172
|
const truthEncoding = normalizeTruthMarkdownEncoding(project);
|
|
1121
1173
|
queueCompletionRefresh({
|
|
1122
1174
|
cwd: running.projectRoot,
|
|
@@ -1130,8 +1182,8 @@ async function runInternalKnowledgeFinalize(args) {
|
|
|
1130
1182
|
...running,
|
|
1131
1183
|
status: "succeeded",
|
|
1132
1184
|
finishedAt: new Date().toISOString(),
|
|
1133
|
-
...(
|
|
1134
|
-
finalResponse:
|
|
1185
|
+
...(writerRun.threadId ? { sdkThreadId: writerRun.threadId } : {}),
|
|
1186
|
+
finalResponse: writerRun.finalResponse,
|
|
1135
1187
|
truthEncoding,
|
|
1136
1188
|
});
|
|
1137
1189
|
tryCleanupKnowledgeFinalizationReport(project, running.reportPath);
|
|
@@ -1149,9 +1201,57 @@ async function runInternalKnowledgeFinalize(args) {
|
|
|
1149
1201
|
}
|
|
1150
1202
|
}
|
|
1151
1203
|
}
|
|
1204
|
+
/**
|
|
1205
|
+
* Pick the host-aware finalization runner. The opencode host never assumes a Codex SDK
|
|
1206
|
+
* runtime is installed and runs the writer through `opencode run`; the Codex host and
|
|
1207
|
+
* legacy jobs without a host field keep using the versioned Codex SDK runtime.
|
|
1208
|
+
*/
|
|
1209
|
+
async function runKnowledgeWriterForJob(running) {
|
|
1210
|
+
if (running.host === "opencode") {
|
|
1211
|
+
const result = runOpencodeKnowledgeWriter({
|
|
1212
|
+
prompt: buildKnowledgeWriterPrompt(running),
|
|
1213
|
+
projectRoot: running.projectRoot,
|
|
1214
|
+
writer: running.writer ?? null,
|
|
1215
|
+
});
|
|
1216
|
+
return {
|
|
1217
|
+
finalResponse: result.finalResponse,
|
|
1218
|
+
...(result.threadId ? { threadId: result.threadId } : {}),
|
|
1219
|
+
};
|
|
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
|
+
}
|
|
1224
|
+
return runCodexSdkWriter(running);
|
|
1225
|
+
}
|
|
1226
|
+
async function runCodexSdkWriter(running) {
|
|
1227
|
+
const sdk = await import(pathToFileURL(resolveCodexSdkEntryPath()).href);
|
|
1228
|
+
const Codex = sdk.Codex;
|
|
1229
|
+
const codex = new Codex({
|
|
1230
|
+
env: knowledgeFinalizerEnvironment(),
|
|
1231
|
+
...(process.env.CLAW_CODEX_PATH_OVERRIDE
|
|
1232
|
+
? { codexPathOverride: process.env.CLAW_CODEX_PATH_OVERRIDE }
|
|
1233
|
+
: {}),
|
|
1234
|
+
});
|
|
1235
|
+
const writer = running.writer ?? { model: null, reasoningEffort: "medium" };
|
|
1236
|
+
const thread = codex.startThread({
|
|
1237
|
+
workingDirectory: running.projectRoot,
|
|
1238
|
+
sandboxMode: "workspace-write",
|
|
1239
|
+
approvalPolicy: "never",
|
|
1240
|
+
networkAccessEnabled: false,
|
|
1241
|
+
...(writer.model ? { model: writer.model } : {}),
|
|
1242
|
+
...(writer.reasoningEffort
|
|
1243
|
+
? { modelReasoningEffort: writer.reasoningEffort }
|
|
1244
|
+
: {}),
|
|
1245
|
+
});
|
|
1246
|
+
const turn = await thread.run(buildKnowledgeWriterPrompt(running));
|
|
1247
|
+
return {
|
|
1248
|
+
finalResponse: turn.finalResponse,
|
|
1249
|
+
...(thread.id ? { threadId: thread.id } : {}),
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1152
1252
|
function knowledgeFinalizerEnvironment() {
|
|
1153
1253
|
const env = {};
|
|
1154
|
-
for (const [key, value] of Object.entries(
|
|
1254
|
+
for (const [key, value] of Object.entries(withoutInvocationHost())) {
|
|
1155
1255
|
if (value !== undefined) {
|
|
1156
1256
|
env[key] = value;
|
|
1157
1257
|
}
|
|
@@ -1163,10 +1263,11 @@ function buildKnowledgeWriterPrompt(job) {
|
|
|
1163
1263
|
const writerSkill = job.writer?.externalSkill?.trim() || "claw-kit:knowledge-writer";
|
|
1164
1264
|
return [
|
|
1165
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.",
|
|
1166
1267
|
`Completed plan: ${job.planPath}`,
|
|
1167
1268
|
`Turn report: ${job.reportPath}`,
|
|
1168
1269
|
`Finalization id: ${job.finalizeId}`,
|
|
1169
|
-
"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.",
|
|
1170
1271
|
].join("\n");
|
|
1171
1272
|
}
|
|
1172
1273
|
function launchKnowledgeFinalizationWorker(jobPath, cwd) {
|
|
@@ -1183,7 +1284,7 @@ function launchKnowledgeFinalizationWorker(jobPath, cwd) {
|
|
|
1183
1284
|
stdio: "ignore",
|
|
1184
1285
|
windowsHide: true,
|
|
1185
1286
|
env: {
|
|
1186
|
-
...
|
|
1287
|
+
...withoutInvocationHost(),
|
|
1187
1288
|
CLAW_KNOWLEDGE_NODE: process.execPath,
|
|
1188
1289
|
CLAW_KNOWLEDGE_ENTRY: resolveCliEntryPath(),
|
|
1189
1290
|
CLAW_KNOWLEDGE_JOB: jobPath,
|
|
@@ -1195,22 +1296,29 @@ function launchKnowledgeFinalizationWorker(jobPath, cwd) {
|
|
|
1195
1296
|
}
|
|
1196
1297
|
return;
|
|
1197
1298
|
}
|
|
1198
|
-
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() });
|
|
1199
1300
|
child.unref();
|
|
1200
1301
|
}
|
|
1201
|
-
async function runSessionStartHook() {
|
|
1302
|
+
async function runSessionStartHook(effectiveHost) {
|
|
1202
1303
|
if (process.env.CLAW_KNOWLEDGE_FINALIZER === "1") {
|
|
1203
1304
|
return;
|
|
1204
1305
|
}
|
|
1205
1306
|
const payload = await readStdinJson();
|
|
1206
1307
|
const hookCwd = resolveHookCwd(payload);
|
|
1207
1308
|
const ownerSessionKey = resolveOwnerSessionKey(payload);
|
|
1208
|
-
if (!hookCwd
|
|
1309
|
+
if (!hookCwd) {
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
const sessionProject = resolveSessionWorkflowContext(ownerSessionKey ?? undefined);
|
|
1313
|
+
if (!containsClawDir(hookCwd) && !sessionProject) {
|
|
1209
1314
|
return;
|
|
1210
1315
|
}
|
|
1211
1316
|
try {
|
|
1212
|
-
const context = await runContextCommand([], hookCwd, ownerSessionKey);
|
|
1213
|
-
|
|
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") {
|
|
1214
1322
|
const project = resolveProjectContext(hookCwd);
|
|
1215
1323
|
for (const jobPath of listRetryableKnowledgeFinalizationJobs(project)) {
|
|
1216
1324
|
try {
|
|
@@ -1349,7 +1457,6 @@ function buildRecoveredWorkflowAdditionalContext(activeWorkflow, versionSyncProm
|
|
|
1349
1457
|
const workflowGuidance = activeWorkflow.workflowGuidance;
|
|
1350
1458
|
const nextsteps = toStringList(workflowGuidance?.nextsteps);
|
|
1351
1459
|
const recommendedCommands = toStringList(workflowGuidance?.recommendedCommands);
|
|
1352
|
-
const delegateSubagents = toDelegateNames(workflowGuidance?.delegateSubagents);
|
|
1353
1460
|
const notes = typeof workflowGuidance?.notes === "string" ? workflowGuidance.notes.trim() : "";
|
|
1354
1461
|
const askUser = summarizeAskUser(workflowGuidance?.askUser);
|
|
1355
1462
|
const goalMode = summarizeGoalMode(workflowGuidance?.goalMode);
|
|
@@ -1361,7 +1468,6 @@ function buildRecoveredWorkflowAdditionalContext(activeWorkflow, versionSyncProm
|
|
|
1361
1468
|
planSummary,
|
|
1362
1469
|
nextsteps,
|
|
1363
1470
|
recommendedCommands,
|
|
1364
|
-
delegateSubagents,
|
|
1365
1471
|
notes,
|
|
1366
1472
|
askUser: askUser ?? "",
|
|
1367
1473
|
goalMode: goalMode ?? "",
|
|
@@ -1470,8 +1576,8 @@ function summarizeRecoveredPlanContent(planContent) {
|
|
|
1470
1576
|
}
|
|
1471
1577
|
return lines.length > 0 ? lines : ["- plan content present in activeWorkflow.planContent JSON."];
|
|
1472
1578
|
}
|
|
1473
|
-
async function tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey) {
|
|
1474
|
-
const project =
|
|
1579
|
+
async function tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey, effectiveHost) {
|
|
1580
|
+
const project = resolveWorkflowProjectContext(cwd, ownerSessionKey);
|
|
1475
1581
|
const planPath = resolveSessionBoundPlan(project, ownerSessionKey);
|
|
1476
1582
|
if (!planPath) {
|
|
1477
1583
|
return null;
|
|
@@ -1489,6 +1595,7 @@ async function tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey) {
|
|
|
1489
1595
|
cwd,
|
|
1490
1596
|
taskName,
|
|
1491
1597
|
planFile,
|
|
1598
|
+
ownerSessionKey,
|
|
1492
1599
|
});
|
|
1493
1600
|
if (result.plan.status.startsWith("end.")) {
|
|
1494
1601
|
unbindSession(project, ownerSessionKey);
|
|
@@ -1507,6 +1614,7 @@ async function tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey) {
|
|
|
1507
1614
|
plan: result.plan,
|
|
1508
1615
|
projectRoot: project.projectRoot,
|
|
1509
1616
|
projectConfig: project.projectConfig,
|
|
1617
|
+
host: effectiveHost,
|
|
1510
1618
|
}),
|
|
1511
1619
|
};
|
|
1512
1620
|
}
|
|
@@ -1515,7 +1623,7 @@ async function tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey) {
|
|
|
1515
1623
|
return null;
|
|
1516
1624
|
}
|
|
1517
1625
|
}
|
|
1518
|
-
async function runSubplan(args) {
|
|
1626
|
+
async function runSubplan(args, effectiveHost) {
|
|
1519
1627
|
const subcommand = args.shift();
|
|
1520
1628
|
switch (subcommand) {
|
|
1521
1629
|
case "create": {
|
|
@@ -1525,9 +1633,10 @@ async function runSubplan(args) {
|
|
|
1525
1633
|
parentTaskId: readOptionalNumber(args, "--task-id") ?? failMissingNumericFlag("--task-id"),
|
|
1526
1634
|
templateName: readOptionalFlag(args, "--template") ?? undefined,
|
|
1527
1635
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
1636
|
+
host: effectiveHost,
|
|
1528
1637
|
});
|
|
1529
1638
|
assertNoRemainingArgs(args, "subplan create");
|
|
1530
|
-
printJson(compactPlanCommandResult("subplan.create", result));
|
|
1639
|
+
printJson(compactPlanCommandResult("subplan.create", result, effectiveHost));
|
|
1531
1640
|
return;
|
|
1532
1641
|
}
|
|
1533
1642
|
default:
|
|
@@ -1559,16 +1668,6 @@ function toStringList(value) {
|
|
|
1559
1668
|
.map((item) => item.trim())
|
|
1560
1669
|
: [];
|
|
1561
1670
|
}
|
|
1562
|
-
function toDelegateNames(value) {
|
|
1563
|
-
if (!Array.isArray(value)) {
|
|
1564
|
-
return [];
|
|
1565
|
-
}
|
|
1566
|
-
return value
|
|
1567
|
-
.map((entry) => (entry && typeof entry === "object" && typeof entry.name === "string"
|
|
1568
|
-
? entry.name.trim()
|
|
1569
|
-
: ""))
|
|
1570
|
-
.filter((entry) => Boolean(entry));
|
|
1571
|
-
}
|
|
1572
1671
|
function summarizeAskUser(value) {
|
|
1573
1672
|
if (!value || typeof value.reason !== "string" || !value.reason.trim()) {
|
|
1574
1673
|
return null;
|
|
@@ -1582,11 +1681,14 @@ function summarizeGoalMode(value) {
|
|
|
1582
1681
|
return value.recommendedObjective.trim();
|
|
1583
1682
|
}
|
|
1584
1683
|
async function readStdinJson() {
|
|
1684
|
+
const bufferedInput = consumeBufferedHookInput();
|
|
1585
1685
|
const chunks = [];
|
|
1586
|
-
|
|
1587
|
-
|
|
1686
|
+
if (bufferedInput === null) {
|
|
1687
|
+
for await (const chunk of process.stdin) {
|
|
1688
|
+
chunks.push(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
|
|
1689
|
+
}
|
|
1588
1690
|
}
|
|
1589
|
-
const raw = chunks.join("").trim();
|
|
1691
|
+
const raw = (bufferedInput ?? chunks.join("")).trim();
|
|
1590
1692
|
if (!raw) {
|
|
1591
1693
|
return null;
|
|
1592
1694
|
}
|
|
@@ -1603,14 +1705,14 @@ function readJson(filePath) {
|
|
|
1603
1705
|
function stripBom(content) {
|
|
1604
1706
|
return content.charCodeAt(0) === 0xfeff ? content.slice(1) : content;
|
|
1605
1707
|
}
|
|
1606
|
-
function compactPlanCommandResult(command, result, completionRefresh) {
|
|
1708
|
+
function compactPlanCommandResult(command, result, effectiveHost, completionRefresh) {
|
|
1607
1709
|
const archivedPlanPath = completionRefresh?.taskRetention.archivedCurrentTask?.taskName === result.taskName &&
|
|
1608
1710
|
completionRefresh.taskRetention.archivedCurrentTask.archivedPlanPath
|
|
1609
1711
|
? completionRefresh.taskRetention.archivedCurrentTask.archivedPlanPath
|
|
1610
1712
|
: undefined;
|
|
1611
1713
|
const resolvedPlanPath = archivedPlanPath ?? result.planPath;
|
|
1612
|
-
const
|
|
1613
|
-
const
|
|
1714
|
+
const codexResult = effectiveHost === "codex";
|
|
1715
|
+
const hostActions = codexResult ? buildHostActions(result) : [];
|
|
1614
1716
|
return {
|
|
1615
1717
|
ok: true,
|
|
1616
1718
|
command,
|
|
@@ -1618,17 +1720,12 @@ function compactPlanCommandResult(command, result, completionRefresh) {
|
|
|
1618
1720
|
...(archivedPlanPath ? { archivedPlanPath } : {}),
|
|
1619
1721
|
planStatus: result.planStatus,
|
|
1620
1722
|
...(!codexResult && result.previousPlanStatus ? { previousPlanStatus: result.previousPlanStatus } : {}),
|
|
1621
|
-
...(!codexResult && result.emittedEvents?.length ? { emittedEvents: result.emittedEvents } : {}),
|
|
1622
|
-
...(!codexResult && result.events?.length ? { events: result.events } : {}),
|
|
1623
1723
|
...(hostActions.length ? { hostActions } : {}),
|
|
1624
1724
|
...(!codexResult && result.changedTaskIds?.length ? { changedTaskIds: result.changedTaskIds } : {}),
|
|
1625
1725
|
...(!codexResult && result.appendedTaskIds?.length ? { appendedTaskIds: result.appendedTaskIds } : {}),
|
|
1626
1726
|
...(codexResult ? { stage: result.workflowGuidance.stage } : {}),
|
|
1627
1727
|
...(!codexResult ? { nextsteps: result.workflowGuidance.nextsteps } : {}),
|
|
1628
1728
|
...(result.workflowGuidance.nextTask ? { nextTask: result.workflowGuidance.nextTask } : {}),
|
|
1629
|
-
...(result.workflowGuidance.delegateSubagents?.length
|
|
1630
|
-
? { delegateSubagents: result.workflowGuidance.delegateSubagents }
|
|
1631
|
-
: {}),
|
|
1632
1729
|
...(result.workflowGuidance.notes?.trim() && !codexResult
|
|
1633
1730
|
? { notes: result.workflowGuidance.notes }
|
|
1634
1731
|
: {}),
|
|
@@ -1730,9 +1827,6 @@ function compactDirectCommandResult(command, workflowGuidance, completionRefresh
|
|
|
1730
1827
|
command,
|
|
1731
1828
|
summary: workflowGuidance.summary,
|
|
1732
1829
|
nextsteps: workflowGuidance.nextsteps,
|
|
1733
|
-
...(workflowGuidance.delegateSubagents?.length
|
|
1734
|
-
? { delegateSubagents: workflowGuidance.delegateSubagents }
|
|
1735
|
-
: {}),
|
|
1736
1830
|
...(workflowGuidance.notes?.trim() ? { notes: workflowGuidance.notes } : {}),
|
|
1737
1831
|
...(workflowGuidance.recommendedCommands?.length
|
|
1738
1832
|
? { recommendedCommands: workflowGuidance.recommendedCommands }
|
|
@@ -1931,7 +2025,7 @@ function readPlanMutationTarget(args) {
|
|
|
1931
2025
|
...(explicitPlanFile ? { planFile: explicitPlanFile } : {}),
|
|
1932
2026
|
};
|
|
1933
2027
|
}
|
|
1934
|
-
const project =
|
|
2028
|
+
const project = resolveWorkflowProjectContext(process.cwd(), resolveOwnerSessionKey() ?? undefined);
|
|
1935
2029
|
const boundPlanPath = resolveSessionBoundPlan(project, resolveOwnerSessionKey() ?? undefined);
|
|
1936
2030
|
if (!boundPlanPath) {
|
|
1937
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.");
|
|
@@ -2085,7 +2179,7 @@ function launchCompletionRefreshWorker(input) {
|
|
|
2085
2179
|
stdio: "ignore",
|
|
2086
2180
|
windowsHide: true,
|
|
2087
2181
|
env: {
|
|
2088
|
-
...
|
|
2182
|
+
...withoutInvocationHost(),
|
|
2089
2183
|
CLAW_COMPLETION_NODE: process.execPath,
|
|
2090
2184
|
CLAW_COMPLETION_ENTRY: resolveCliEntryPath(),
|
|
2091
2185
|
CLAW_COMPLETION_CWD: input.cwd,
|
|
@@ -2121,6 +2215,7 @@ function launchCompletionRefreshWorker(input) {
|
|
|
2121
2215
|
detached: true,
|
|
2122
2216
|
stdio: "ignore",
|
|
2123
2217
|
windowsHide: true,
|
|
2218
|
+
env: withoutInvocationHost(),
|
|
2124
2219
|
});
|
|
2125
2220
|
child.unref();
|
|
2126
2221
|
}
|
|
@@ -2763,6 +2858,18 @@ function normalizeVersionString(value) {
|
|
|
2763
2858
|
const trimmed = value.trim();
|
|
2764
2859
|
return trimmed ? trimmed : null;
|
|
2765
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
|
+
}
|
|
2766
2873
|
function readOptionalFlag(args, flag) {
|
|
2767
2874
|
const index = args.indexOf(flag);
|
|
2768
2875
|
if (index === -1) {
|