@veewo/claw 0.1.93 → 0.1.95
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 +2 -0
- package/dist/bin.js +13 -1
- package/dist/bin.js.map +1 -1
- package/dist/cli.js +130 -68
- package/dist/cli.js.map +1 -1
- package/dist/search-entry.d.ts +1 -0
- package/dist/search-entry.js +120 -0
- package/dist/search-entry.js.map +1 -0
- package/package.json +3 -3
- package/dist/knowledge-git-commit.d.ts +0 -18
- package/dist/knowledge-git-commit.js +0 -127
- package/dist/knowledge-git-commit.js.map +0 -1
package/README.md
CHANGED
|
@@ -35,6 +35,8 @@ claw plan create "Ephemeral harness" --scope session --goal "Use plan and Goal w
|
|
|
35
35
|
|
|
36
36
|
`claw plan create` uses explicit `--template` first, otherwise the project's configured `defaultPlanTemplate`, and finally falls back to the built-in `default` template. You can select a template explicitly with `claw plan create "<title>" --template <name>` or `claw plan create --template <name> --title "<title>"`.
|
|
37
37
|
|
|
38
|
+
New project tasks are grouped under `.claw/tasks/YYYY-MM-DD/`. On the first `claw context` call of each local calendar day, claw performs a lock-protected maintenance pass: it clears `.claw/runtime/tmp/` (and removes legacy `.claw/tmp/`), moves date-scoped task folders from before yesterday into the archive even when old plans have no `completedAt`, archives legacy flat tasks by `plan.updatedAt` (falling back to the plan file timestamp), applies `maxTasksToKeep` to archived tasks, removes bindings to plans no longer under active tasks, and sweeps expired session workflows. This is lazy maintenance, not a background scheduler.
|
|
39
|
+
|
|
38
40
|
`--scope session` stores the workflow in a user-level directory keyed by the platform session id, so it works without a project `.claw` directory and recovers across cwd changes. It preserves plan/task/subplan/Goal behavior while disabling project knowledge capture, memory/GitNexus refresh, and project retention. Use `claw session clean` for the current session or `claw session clean --expired` for the seven-day TTL sweep.
|
|
39
41
|
|
|
40
42
|
Projects can add reusable templates directly under `.claw/templates` with `.json`, `.js`, `.mjs`, or `.cjs` files. Put `defaultPlanTemplate` in `.claw/project.json` for a shared team default, or in `.claw/project-override.json` for a local personal override.
|
package/dist/bin.js
CHANGED
|
@@ -9,11 +9,23 @@ process.emitWarning = ((warning, ...args) => {
|
|
|
9
9
|
});
|
|
10
10
|
void main();
|
|
11
11
|
async function main() {
|
|
12
|
-
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
if (!await shouldLoadCliForInvocation(args, process.cwd(), process.env)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (isSearchInvocation(args)) {
|
|
17
|
+
const { runSearchEntry } = await import("./search-entry.js");
|
|
18
|
+
await runSearchEntry(args.slice(1));
|
|
13
19
|
return;
|
|
14
20
|
}
|
|
15
21
|
await import("./cli.js");
|
|
16
22
|
}
|
|
23
|
+
function isSearchInvocation(args) {
|
|
24
|
+
return args[0] === "search"
|
|
25
|
+
&& args[1] !== "help"
|
|
26
|
+
&& !args.includes("--help")
|
|
27
|
+
&& !args.includes("-h");
|
|
28
|
+
}
|
|
17
29
|
function shouldSuppressSqliteExperimentalWarning(warning, args) {
|
|
18
30
|
const message = typeof warning === "string"
|
|
19
31
|
? warning
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAE3E,MAAM,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAE9D,OAAO,CAAC,WAAW,GAAG,CAAC,CAAC,OAAgB,EAAE,GAAG,IAAe,EAAE,EAAE;IAC9D,IAAI,uCAAuC,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;QAC3D,OAAO;IACT,CAAC;IACD,OAAO,mBAAmB,CAAC,OAAgB,EAAE,GAAI,IAAW,CAAC,CAAC;AAChE,CAAC,CAA+B,CAAC;AAEjC,KAAK,IAAI,EAAE,CAAC;AAEZ,KAAK,UAAU,IAAI;IACjB,
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAE3E,MAAM,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAE9D,OAAO,CAAC,WAAW,GAAG,CAAC,CAAC,OAAgB,EAAE,GAAG,IAAe,EAAE,EAAE;IAC9D,IAAI,uCAAuC,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;QAC3D,OAAO;IACT,CAAC;IACD,OAAO,mBAAmB,CAAC,OAAgB,EAAE,GAAI,IAAW,CAAC,CAAC;AAChE,CAAC,CAA+B,CAAC;AAEjC,KAAK,IAAI,EAAE,CAAC;AAEZ,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,CAAC,MAAM,0BAA0B,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACxE,OAAO;IACT,CAAC;IACD,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;QAC7D,MAAM,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,OAAO;IACT,CAAC;IACD,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAc;IACxC,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ;WACtB,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM;WAClB,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;WACxB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,uCAAuC,CAAC,OAAgB,EAAE,IAAe;IAChF,MAAM,OAAO,GACX,OAAO,OAAO,KAAK,QAAQ;QACzB,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,OAAO,YAAY,KAAK;YACxB,CAAC,CAAC,OAAO,CAAC,OAAO;YACjB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxB,MAAM,IAAI,GACR,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ;QACzB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACT,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC;YAC3D,CAAC,CAAC,MAAM,CAAE,IAAI,CAAC,CAAC,CAAwB,CAAC,IAAI,IAAI,EAAE,CAAC;YACpD,CAAC,CAAC,OAAO,YAAY,KAAK;gBACxB,CAAC,CAAC,OAAO,CAAC,IAAI;gBACd,CAAC,CAAC,EAAE,CAAC;IAEb,OAAO,IAAI,KAAK,qBAAqB,IAAI,OAAO,CAAC,QAAQ,CAAC,mCAAmC,CAAC,CAAC;AACjG,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -5,21 +5,20 @@ 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, DEFAULT_MAX_TASKS_TO_KEEP, checkProjectProtocol, ClawError, buildPlanWorkflowGuidance, buildMemoryIndex, buildSessionStartDefaultPrompt, buildSessionStartRecoveredPrompt, editPlan, ensureProjectProtocol, enforceTaskRetention, ingestTruth, initProject, getTemplateTaskDoneChoices, resolvePlanTemplateFile, resolveProjectContext, resolveWorkflowProjectContext, resolveSessionWorkflowContext, deleteSessionWorkflow, sweepExpiredSessionWorkflows, resolveSessionBoundPlan, resolveContext, resolveSeedPlanTemplate,
|
|
8
|
+
import { buildDirectWorkflowGuidance, DEFAULT_MAX_TASKS_TO_KEEP, checkProjectProtocol, ClawError, buildPlanWorkflowGuidance, buildMemoryIndex, buildSessionStartDefaultPrompt, buildSessionStartRecoveredPrompt, editPlan, ensureProjectProtocol, enforceTaskRetention, findTaskDirectory, runDailyMaintenance, ingestTruth, initProject, getTemplateTaskDoneChoices, resolvePlanTemplateFile, resolveProjectContext, resolveWorkflowProjectContext, resolveSessionWorkflowContext, deleteSessionWorkflow, sweepExpiredSessionWorkflows, resolveSessionBoundPlan, resolveContext, resolveSeedPlanTemplate, searchMemoryAsync, warmProjectMemoryEmbedding, showPlan, createSubplan, switchTask, tryCaptureKnowledgeStop, claimKnowledgeFinalizationJob, changedKnowledgeMarkdownPaths, listRetryableKnowledgeFinalizationJobs, governChangedKnowledgeMarkdown, normalizeTruthMarkdownEncoding, recordKnowledgeFinalizationResult, snapshotKnowledgeMarkdown, 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, extractTaskDoneConclusions, } from "./codex-transcript.js";
|
|
12
12
|
import { consumeBufferedHookInput } from "./knowledge-hook-preflight.js";
|
|
13
13
|
import { resolveInvocationHost, withoutInvocationHost } from "./invocation-host.js";
|
|
14
14
|
import { runOpencodeKnowledgeWriter } from "./opencode-runner.js";
|
|
15
|
-
import { captureKnowledgeGitState, commitKnowledgeDocumentation } from "./knowledge-git-commit.js";
|
|
16
15
|
const CLI_VERSION = readCliVersion();
|
|
17
16
|
const TOP_LEVEL_COMMANDS = [
|
|
18
17
|
{ name: "init [options]", summary: "Initialize and normalize the .claw project surface." },
|
|
19
18
|
{ name: "context [--task <name>]", summary: "Resolve project context, auto-initializing or correcting .claw state." },
|
|
20
19
|
{ name: "session clean [--expired]", summary: "Remove current or expired session workflow state." },
|
|
21
20
|
{ name: "check", summary: "Check and auto-correct .claw project protocol fields." },
|
|
22
|
-
{ name: "plan <subcommand> [options]", summary: "Plan lifecycle: create, start, edit, remove, wait, resume, show, done." },
|
|
21
|
+
{ name: "plan <subcommand> [options]", summary: "Plan lifecycle: create, start, edit, remove, wait, resume, sync, show, done." },
|
|
23
22
|
{ name: "codex driver", summary: "Return the versioned code-mode driver used by the Codex adapter." },
|
|
24
23
|
{ name: "template <subcommand> [options]", summary: "Plan template helpers such as validation." },
|
|
25
24
|
{ name: "task <subcommand> [options]", summary: "Task lifecycle helpers inside an existing plan." },
|
|
@@ -93,6 +92,10 @@ const COMMAND_HELP = {
|
|
|
93
92
|
{ flag: "--summary <text>", detail: "Set the plan summary." },
|
|
94
93
|
{ flag: "--rule <text>", detail: "Append a rule (repeatable)." },
|
|
95
94
|
{ flag: "--key-decision <text>", detail: "Append a key decision (repeatable)." },
|
|
95
|
+
{ flag: "--retrospective <text>", detail: "Set the retrospective summary; required before --status end.completed." },
|
|
96
|
+
{ flag: "--what-worked <text>", detail: "Append a retrospective success (repeatable)." },
|
|
97
|
+
{ flag: "--issue <text>", detail: "Append a retrospective issue (repeatable)." },
|
|
98
|
+
{ flag: "--follow-up <text>", detail: "Append a retrospective follow-up (repeatable)." },
|
|
96
99
|
{ flag: "--reference <path>", detail: "Add a reference; follow it immediately with --why (repeatable)." },
|
|
97
100
|
{ flag: "--why <text>", detail: "Explain the immediately preceding --reference." },
|
|
98
101
|
{ flag: "--task-name <name>", detail: "Advanced: override the session-bound task scope." },
|
|
@@ -131,6 +134,11 @@ const COMMAND_HELP = {
|
|
|
131
134
|
{ flag: "--plan-file <relative-path>", detail: "Advanced: override the session-bound plan file." },
|
|
132
135
|
],
|
|
133
136
|
},
|
|
137
|
+
sync: {
|
|
138
|
+
usage: ["{script} plan sync"],
|
|
139
|
+
description: "Resynchronize a recovered active Codex plan with host progress and Goal Mode without mutating the plan.",
|
|
140
|
+
summary: "Resync a recovered active Codex plan.",
|
|
141
|
+
},
|
|
134
142
|
start: {
|
|
135
143
|
usage: ["{script} plan start --requirements <text> --add-task <title> [--detail <text>] [options]"],
|
|
136
144
|
description: "Atomically apply refined plan content, append outcome tasks, and execute the current template task's plan-start guidance in one serialized mutation.",
|
|
@@ -161,8 +169,8 @@ const COMMAND_HELP = {
|
|
|
161
169
|
},
|
|
162
170
|
done: {
|
|
163
171
|
usage: ["{script} plan done --retrospective <text> [options]"],
|
|
164
|
-
description: "
|
|
165
|
-
summary: "
|
|
172
|
+
description: "Shortcut for applying closeout fields and --status end.completed in one ordered plan edit; it retains the task for at least one hour, sweeps older completed tasks into the archive, and queues the async completion refresh.",
|
|
173
|
+
summary: "Shortcut for completing a plan with a retrospective and queueing completion refresh.",
|
|
166
174
|
options: [
|
|
167
175
|
{ flag: "--retrospective <text>", detail: "Retrospective summary (required)." },
|
|
168
176
|
{ flag: "--key-decision <text>", detail: "Append a durable key decision when one exists (repeatable)." },
|
|
@@ -443,7 +451,7 @@ async function main() {
|
|
|
443
451
|
}));
|
|
444
452
|
return;
|
|
445
453
|
case "search":
|
|
446
|
-
runSearch(args);
|
|
454
|
+
await runSearch(args);
|
|
447
455
|
return;
|
|
448
456
|
case "direct":
|
|
449
457
|
runDirect(args, effectiveHost);
|
|
@@ -541,15 +549,21 @@ async function runPlan(args, effectiveHost) {
|
|
|
541
549
|
if (operations.length === 0) {
|
|
542
550
|
throw new ClawError("PROJECT_CONFIG_INVALID", "plan edit requires at least one plan field or --status.");
|
|
543
551
|
}
|
|
552
|
+
const ownerSessionKey = resolveOwnerSessionKey() ?? undefined;
|
|
553
|
+
const entersEndState = requestsPlanEndState(operations);
|
|
554
|
+
const queuePlanEndFinalization = entersEndState
|
|
555
|
+
? preparePlanEndFinalization(process.cwd(), ownerSessionKey)
|
|
556
|
+
: undefined;
|
|
544
557
|
const result = await editPlan({
|
|
545
558
|
cwd: process.cwd(),
|
|
546
559
|
...target,
|
|
547
560
|
operations,
|
|
548
561
|
commandSource: "plan.edit",
|
|
549
562
|
host: effectiveHost,
|
|
550
|
-
ownerSessionKey
|
|
563
|
+
ownerSessionKey,
|
|
551
564
|
});
|
|
552
|
-
|
|
565
|
+
const completionRefresh = queuePlanEndFinalization?.(result.taskName);
|
|
566
|
+
printJson(compactPlanCommandResult("plan.edit", result, effectiveHost, completionRefresh));
|
|
553
567
|
if (result.operationChain?.status === "partial")
|
|
554
568
|
process.exitCode = 1;
|
|
555
569
|
return;
|
|
@@ -578,6 +592,9 @@ async function runPlan(args, effectiveHost) {
|
|
|
578
592
|
case "resume":
|
|
579
593
|
await runPlanStatusAlias(args, "process.active", "plan.resume", effectiveHost);
|
|
580
594
|
return;
|
|
595
|
+
case "sync":
|
|
596
|
+
await runPlanSync(args, effectiveHost);
|
|
597
|
+
return;
|
|
581
598
|
case "start": {
|
|
582
599
|
const updates = readPlanFieldUpdates(args);
|
|
583
600
|
const appendTasks = readExplicitAddedTasks(args);
|
|
@@ -614,26 +631,16 @@ async function runPlan(args, effectiveHost) {
|
|
|
614
631
|
const target = readPlanMutationTarget(args);
|
|
615
632
|
assertNoRemainingArgs(args, "plan done");
|
|
616
633
|
const ownerSessionKey = resolveOwnerSessionKey() ?? undefined;
|
|
617
|
-
const
|
|
618
|
-
const gitNexusPreflightAnalyzed = workflowProject.scope === "project"
|
|
619
|
-
? ensureGitNexusReadyForPlanDone(process.cwd())
|
|
620
|
-
: false;
|
|
634
|
+
const queuePlanEndFinalization = preparePlanEndFinalization(process.cwd(), ownerSessionKey);
|
|
621
635
|
const result = await editPlan({
|
|
622
636
|
cwd: process.cwd(),
|
|
623
637
|
...target,
|
|
624
|
-
updates,
|
|
625
|
-
planStatus: "end.completed",
|
|
638
|
+
operations: planCompletionOperations(updates),
|
|
626
639
|
commandSource: "plan.done",
|
|
627
640
|
host: effectiveHost,
|
|
628
641
|
ownerSessionKey,
|
|
629
642
|
});
|
|
630
|
-
const completionRefresh =
|
|
631
|
-
? queueCompletionRefresh({
|
|
632
|
-
cwd: process.cwd(),
|
|
633
|
-
taskName: result.taskName,
|
|
634
|
-
skipGitNexusRefresh: gitNexusPreflightAnalyzed,
|
|
635
|
-
})
|
|
636
|
-
: undefined;
|
|
643
|
+
const completionRefresh = queuePlanEndFinalization?.(result.taskName);
|
|
637
644
|
printJson(compactPlanCommandResult("plan.done", result, effectiveHost, completionRefresh));
|
|
638
645
|
return;
|
|
639
646
|
}
|
|
@@ -671,7 +678,28 @@ async function runPlanStatusAlias(args, planStatus, command, effectiveHost) {
|
|
|
671
678
|
ownerSessionKey: resolveOwnerSessionKey() ?? undefined,
|
|
672
679
|
});
|
|
673
680
|
assertNoRemainingArgs(args, command);
|
|
674
|
-
printJson(compactPlanCommandResult(command, result, effectiveHost));
|
|
681
|
+
printJson(compactPlanCommandResult(command, result, effectiveHost, undefined, true));
|
|
682
|
+
}
|
|
683
|
+
async function runPlanSync(args, effectiveHost) {
|
|
684
|
+
const ownerSessionKey = resolveOwnerSessionKey() ?? undefined;
|
|
685
|
+
const result = showPlan({ cwd: process.cwd(), ...readPlanMutationTarget(args), ownerSessionKey });
|
|
686
|
+
assertNoRemainingArgs(args, "plan sync");
|
|
687
|
+
if (result.plan.status !== "process.active") {
|
|
688
|
+
printJson({ ok: true, command: "plan.sync", planPath: result.planPath, planStatus: result.plan.status });
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
const project = resolveWorkflowProjectContext(process.cwd(), ownerSessionKey);
|
|
692
|
+
const workflowGuidance = await buildPlanWorkflowGuidance({
|
|
693
|
+
taskName: result.taskName,
|
|
694
|
+
planFile: result.planFile,
|
|
695
|
+
plan: result.plan,
|
|
696
|
+
projectRoot: project.projectRoot,
|
|
697
|
+
projectConfig: project.projectConfig,
|
|
698
|
+
previousStatus: "process.wait",
|
|
699
|
+
host: effectiveHost,
|
|
700
|
+
recoveryResync: true,
|
|
701
|
+
});
|
|
702
|
+
printJson(compactPlanCommandResult("plan.sync", { ...result, planStatus: result.plan.status, workflowGuidance }, effectiveHost, undefined, true));
|
|
675
703
|
}
|
|
676
704
|
async function runTemplate(args) {
|
|
677
705
|
const subcommand = args.shift();
|
|
@@ -790,7 +818,7 @@ async function runTask(args, effectiveHost) {
|
|
|
790
818
|
throw new ClawError("PROJECT_CONFIG_INVALID", `Unknown task subcommand "${subcommand ?? ""}".`);
|
|
791
819
|
}
|
|
792
820
|
}
|
|
793
|
-
function runSearch(args) {
|
|
821
|
+
async function runSearch(args) {
|
|
794
822
|
const subcommand = args[0];
|
|
795
823
|
if (subcommand === "index") {
|
|
796
824
|
args.shift();
|
|
@@ -815,7 +843,7 @@ function runSearch(args) {
|
|
|
815
843
|
printJson({
|
|
816
844
|
ok: true,
|
|
817
845
|
command: "search",
|
|
818
|
-
...
|
|
846
|
+
...await searchMemoryAsync({
|
|
819
847
|
cwd: process.cwd(),
|
|
820
848
|
limit: readOptionalNumber(args, "--limit"),
|
|
821
849
|
query: readRequiredSearchQuery(args),
|
|
@@ -844,6 +872,7 @@ async function runContextCommand(args, cwd = process.cwd(), ownerSessionKey = re
|
|
|
844
872
|
let fixedPaths = [];
|
|
845
873
|
const sessionProject = resolveSessionWorkflowContext(ownerSessionKey ?? undefined);
|
|
846
874
|
if (sessionProject) {
|
|
875
|
+
const maintenance = runDailyMaintenance(sessionProject, { excludeSessionKey: ownerSessionKey ?? undefined, includeProject: false });
|
|
847
876
|
const activeWorkflow = !taskName && ownerSessionKey
|
|
848
877
|
? await tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey, effectiveHost)
|
|
849
878
|
: null;
|
|
@@ -854,6 +883,7 @@ async function runContextCommand(args, cwd = process.cwd(), ownerSessionKey = re
|
|
|
854
883
|
return {
|
|
855
884
|
project: sessionProject,
|
|
856
885
|
...(activeWorkflow ? { activeWorkflow } : {}),
|
|
886
|
+
...(maintenance.ran ? { maintenance } : {}),
|
|
857
887
|
...(codexRuntimeError ? { error: codexRuntimeError } : {}),
|
|
858
888
|
};
|
|
859
889
|
}
|
|
@@ -869,6 +899,7 @@ async function runContextCommand(args, cwd = process.cwd(), ownerSessionKey = re
|
|
|
869
899
|
initProject({ cwd, version: CLI_VERSION });
|
|
870
900
|
initialized = true;
|
|
871
901
|
}
|
|
902
|
+
const maintenance = runDailyMaintenance(resolveProjectContext(cwd), { excludeSessionKey: ownerSessionKey ?? undefined });
|
|
872
903
|
let resolved = resolveContext(cwd, taskName);
|
|
873
904
|
const versionSync = syncProjectVersionWithCli(cwd, resolved.project);
|
|
874
905
|
if (versionSync.projectVersionUpdated) {
|
|
@@ -888,6 +919,7 @@ async function runContextCommand(args, cwd = process.cwd(), ownerSessionKey = re
|
|
|
888
919
|
launchProjectEmbeddingWarmup(resolved.project);
|
|
889
920
|
return {
|
|
890
921
|
...resolved,
|
|
922
|
+
...(maintenance.ran ? { maintenance } : {}),
|
|
891
923
|
...(activeWorkflow ? { activeWorkflow } : {}),
|
|
892
924
|
...(codexRuntimeError ? { error: codexRuntimeError } : {}),
|
|
893
925
|
protocolCheck: checkProjectProtocol(cwd),
|
|
@@ -1221,10 +1253,6 @@ async function runInternalKnowledgeFinalize(args) {
|
|
|
1221
1253
|
const project = resolveProjectContext(running.projectRoot);
|
|
1222
1254
|
const useBuiltInAutomation = usesBuiltInKnowledgeWriter(running);
|
|
1223
1255
|
const knowledgeBefore = snapshotKnowledgeMarkdown(project.truthDir);
|
|
1224
|
-
const autoCommitKnowledge = project.projectConfig?.autoCommitKnowledge !== false;
|
|
1225
|
-
const knowledgeGitState = autoCommitKnowledge
|
|
1226
|
-
? captureKnowledgeGitState(running.projectRoot, project.truthDir)
|
|
1227
|
-
: null;
|
|
1228
1256
|
const writerRun = await runKnowledgeWriterForJob(running);
|
|
1229
1257
|
const knowledgeGovernance = useBuiltInAutomation
|
|
1230
1258
|
? governChangedKnowledgeMarkdown({
|
|
@@ -1268,14 +1296,6 @@ async function runInternalKnowledgeFinalize(args) {
|
|
|
1268
1296
|
...(knowledgeGovernance ? { knowledgeGovernance } : {}),
|
|
1269
1297
|
truthEncoding,
|
|
1270
1298
|
});
|
|
1271
|
-
if (autoCommitKnowledge) {
|
|
1272
|
-
commitKnowledgeDocumentation({
|
|
1273
|
-
state: knowledgeGitState,
|
|
1274
|
-
truthDir: project.truthDir,
|
|
1275
|
-
changedPaths: changedKnowledgePaths,
|
|
1276
|
-
message: running.taskName,
|
|
1277
|
-
});
|
|
1278
|
-
}
|
|
1279
1299
|
}
|
|
1280
1300
|
catch (error) {
|
|
1281
1301
|
const failed = {
|
|
@@ -1477,7 +1497,7 @@ async function runSessionStartHook(effectiveHost) {
|
|
|
1477
1497
|
}
|
|
1478
1498
|
}
|
|
1479
1499
|
}
|
|
1480
|
-
const additionalContext = buildSessionStartAdditionalContext(context, hookCwd);
|
|
1500
|
+
const additionalContext = buildSessionStartAdditionalContext(context, hookCwd, effectiveHost);
|
|
1481
1501
|
if (!additionalContext) {
|
|
1482
1502
|
return;
|
|
1483
1503
|
}
|
|
@@ -1557,14 +1577,18 @@ function safeResolveTempDir() {
|
|
|
1557
1577
|
return null;
|
|
1558
1578
|
}
|
|
1559
1579
|
}
|
|
1560
|
-
function buildSessionStartAdditionalContext(context, sessionCwd) {
|
|
1580
|
+
function buildSessionStartAdditionalContext(context, sessionCwd, effectiveHost) {
|
|
1561
1581
|
const versionSyncPrompt = buildVersionSyncPrompt(context);
|
|
1562
1582
|
const searchGuidance = buildContextSearchGuidance(context);
|
|
1563
1583
|
const runtimeErrorPrompt = buildCodexRuntimeErrorPrompt(context);
|
|
1564
1584
|
const activeWorkflow = context.activeWorkflow;
|
|
1565
1585
|
if (activeWorkflow) {
|
|
1566
1586
|
const prompt = buildRecoveredWorkflowAdditionalContext(activeWorkflow, versionSyncPrompt);
|
|
1567
|
-
const
|
|
1587
|
+
const recoverySyncPrompt = effectiveHost === "codex" && activeWorkflow.planStatus === "process.active"
|
|
1588
|
+
? "Before continuing, run `claw plan sync` once through the fixed Codex driver to restore host progress and Goal Mode."
|
|
1589
|
+
: "";
|
|
1590
|
+
const promptWithSync = recoverySyncPrompt ? `${prompt}\n${recoverySyncPrompt}` : prompt;
|
|
1591
|
+
const promptWithSearch = searchGuidance ? `${promptWithSync}\n${searchGuidance}` : promptWithSync;
|
|
1568
1592
|
return runtimeErrorPrompt ? `${runtimeErrorPrompt}\n\n${promptWithSearch}` : promptWithSearch;
|
|
1569
1593
|
}
|
|
1570
1594
|
const project = context.project;
|
|
@@ -1731,18 +1755,15 @@ async function tryResolveActiveWorkflowSnapshot(cwd, ownerSessionKey, effectiveH
|
|
|
1731
1755
|
return null;
|
|
1732
1756
|
}
|
|
1733
1757
|
try {
|
|
1734
|
-
const
|
|
1735
|
-
|
|
1736
|
-
const taskName = segments.shift();
|
|
1737
|
-
const planFile = segments.join("/");
|
|
1738
|
-
if (!taskName || !planFile) {
|
|
1758
|
+
const target = parseTaskPlanPath(project, planPath);
|
|
1759
|
+
if (!target) {
|
|
1739
1760
|
unbindSession(project, ownerSessionKey);
|
|
1740
1761
|
return null;
|
|
1741
1762
|
}
|
|
1742
1763
|
const result = showPlan({
|
|
1743
1764
|
cwd,
|
|
1744
|
-
taskName,
|
|
1745
|
-
planFile,
|
|
1765
|
+
taskName: target.taskName,
|
|
1766
|
+
planFile: target.planFile,
|
|
1746
1767
|
ownerSessionKey,
|
|
1747
1768
|
});
|
|
1748
1769
|
if (result.plan.status.startsWith("end.")) {
|
|
@@ -1859,16 +1880,15 @@ function readJson(filePath) {
|
|
|
1859
1880
|
function stripBom(content) {
|
|
1860
1881
|
return content.charCodeAt(0) === 0xfeff ? content.slice(1) : content;
|
|
1861
1882
|
}
|
|
1862
|
-
function compactPlanCommandResult(command, result, effectiveHost, completionRefresh) {
|
|
1883
|
+
function compactPlanCommandResult(command, result, effectiveHost, completionRefresh, forceProjectionSync = false) {
|
|
1863
1884
|
const archivedPlanPath = completionRefresh?.taskRetention.archivedCurrentTask?.taskName === result.taskName &&
|
|
1864
1885
|
completionRefresh.taskRetention.archivedCurrentTask.archivedPlanPath
|
|
1865
1886
|
? completionRefresh.taskRetention.archivedCurrentTask.archivedPlanPath
|
|
1866
1887
|
: undefined;
|
|
1867
1888
|
const resolvedPlanPath = archivedPlanPath ?? result.planPath;
|
|
1868
1889
|
const codexResult = effectiveHost === "codex";
|
|
1869
|
-
const hostActions = codexResult ? buildHostActions(result) : [];
|
|
1890
|
+
const hostActions = codexResult ? buildHostActions(result, { forceProjectionSync, actionIdPrefix: command === "plan.sync" ? `plan.sync:${createHash("sha256").update(result.planPath).digest("hex").slice(0, 16)}` : undefined }) : [];
|
|
1870
1891
|
const nextsteps = codexResult
|
|
1871
|
-
&& command === "plan.done"
|
|
1872
1892
|
&& result.planStatus === "end.completed"
|
|
1873
1893
|
&& result.workflowGuidance.goalTool?.tool === "update_goal"
|
|
1874
1894
|
&& result.workflowGuidance.goalTool.status === "complete"
|
|
@@ -1878,7 +1898,7 @@ function compactPlanCommandResult(command, result, effectiveHost, completionRefr
|
|
|
1878
1898
|
const includePlan = Boolean((command === "plan.create" || command === "subplan.create")
|
|
1879
1899
|
&& result.plan
|
|
1880
1900
|
&& (!codexResult || result.workflowGuidance.stage === "discussion"));
|
|
1881
|
-
const achievement =
|
|
1901
|
+
const achievement = result.planStatus === "end.completed" && result.plan
|
|
1882
1902
|
? {
|
|
1883
1903
|
status: result.planStatus,
|
|
1884
1904
|
title: result.plan.title,
|
|
@@ -1902,7 +1922,7 @@ function compactPlanCommandResult(command, result, effectiveHost, completionRefr
|
|
|
1902
1922
|
...(!codexResult && result.changedTaskIds?.length ? { changedTaskIds: result.changedTaskIds } : {}),
|
|
1903
1923
|
...(!codexResult && result.appendedTaskIds?.length ? { appendedTaskIds: result.appendedTaskIds } : {}),
|
|
1904
1924
|
...(codexResult ? { stage: result.workflowGuidance.stage } : {}),
|
|
1905
|
-
...(!codexResult ||
|
|
1925
|
+
...(!codexResult || result.planStatus === "end.completed"
|
|
1906
1926
|
? { nextsteps }
|
|
1907
1927
|
: {}),
|
|
1908
1928
|
...(result.workflowGuidance.nextTask ? { nextTask: result.workflowGuidance.nextTask } : {}),
|
|
@@ -1937,9 +1957,10 @@ function compactPlanCommandResult(command, result, effectiveHost, completionRefr
|
|
|
1937
1957
|
...(!codexResult || !includePlan ? { planSummary } : {}),
|
|
1938
1958
|
};
|
|
1939
1959
|
}
|
|
1940
|
-
function buildHostActions(result) {
|
|
1960
|
+
function buildHostActions(result, options = {}) {
|
|
1941
1961
|
const latestEvent = result.events?.at(-1);
|
|
1942
|
-
|
|
1962
|
+
const actionIdPrefix = options.actionIdPrefix ?? latestEvent?.mutationId;
|
|
1963
|
+
if (!actionIdPrefix) {
|
|
1943
1964
|
return [];
|
|
1944
1965
|
}
|
|
1945
1966
|
const actions = [];
|
|
@@ -1950,18 +1971,20 @@ function buildHostActions(result) {
|
|
|
1950
1971
|
if (isSubplanGoalHandoff && goalTool?.tool === "update_goal") {
|
|
1951
1972
|
actions.push({
|
|
1952
1973
|
schemaVersion: 1,
|
|
1953
|
-
id: `${
|
|
1974
|
+
id: `${actionIdPrefix}:update_goal`,
|
|
1954
1975
|
tool: "update_goal",
|
|
1955
1976
|
input: {
|
|
1956
1977
|
status: "complete",
|
|
1957
1978
|
},
|
|
1958
1979
|
});
|
|
1959
1980
|
}
|
|
1960
|
-
if (result.plan
|
|
1981
|
+
if (result.plan
|
|
1982
|
+
&& result.plan.tasks.length > 0
|
|
1983
|
+
&& (options.forceProjectionSync || codexPlanProjectionChanged(result.previousPlan, result.plan, result.planStatus))) {
|
|
1961
1984
|
const plan = buildCodexPlanProjection(result.plan, result.planStatus);
|
|
1962
1985
|
actions.push({
|
|
1963
1986
|
schemaVersion: 1,
|
|
1964
|
-
id: `${
|
|
1987
|
+
id: `${actionIdPrefix}:update_plan`,
|
|
1965
1988
|
tool: "update_plan",
|
|
1966
1989
|
input: {
|
|
1967
1990
|
explanation: result.workflowGuidance.summary,
|
|
@@ -1973,7 +1996,7 @@ function buildHostActions(result) {
|
|
|
1973
1996
|
if (goalTool.tool === "create_goal") {
|
|
1974
1997
|
actions.push({
|
|
1975
1998
|
schemaVersion: 1,
|
|
1976
|
-
id: `${
|
|
1999
|
+
id: `${actionIdPrefix}:create_goal`,
|
|
1977
2000
|
tool: "create_goal",
|
|
1978
2001
|
input: {
|
|
1979
2002
|
objective: goalTool.objective,
|
|
@@ -1986,7 +2009,7 @@ function buildHostActions(result) {
|
|
|
1986
2009
|
: goalTool.status;
|
|
1987
2010
|
actions.push({
|
|
1988
2011
|
schemaVersion: 1,
|
|
1989
|
-
id: `${
|
|
2012
|
+
id: `${actionIdPrefix}:update_goal`,
|
|
1990
2013
|
tool: "update_goal",
|
|
1991
2014
|
input: {
|
|
1992
2015
|
status: codexStatus,
|
|
@@ -2003,12 +2026,13 @@ function codexPlanProjectionChanged(previousPlan, plan, planStatus) {
|
|
|
2003
2026
|
!== JSON.stringify(buildCodexPlanProjection(plan, planStatus));
|
|
2004
2027
|
}
|
|
2005
2028
|
function buildCodexPlanProjection(plan, planStatus) {
|
|
2006
|
-
|
|
2029
|
+
const activeTask = plan.tasks.find((task) => task.status === "in_progress" || task.status === "subagent_running") ?? (planStatus === "process.active"
|
|
2030
|
+
? plan.tasks.find((task) => task.status !== "done")
|
|
2031
|
+
: undefined);
|
|
2007
2032
|
return plan.tasks.map((task) => {
|
|
2008
2033
|
let status = task.status === "done" ? "completed" : "pending";
|
|
2009
|
-
if (
|
|
2034
|
+
if (task.id === activeTask?.id) {
|
|
2010
2035
|
status = "in_progress";
|
|
2011
|
-
assignedInProgress = true;
|
|
2012
2036
|
}
|
|
2013
2037
|
return { step: task.title, status };
|
|
2014
2038
|
});
|
|
@@ -2070,6 +2094,18 @@ function readOrderedPlanEditOperations(args) {
|
|
|
2070
2094
|
case "--key-decision":
|
|
2071
2095
|
operations.push({ type: "plan.update", updates: { keyDecisions: [readChainValue(args, flag)] } });
|
|
2072
2096
|
break;
|
|
2097
|
+
case "--retrospective":
|
|
2098
|
+
operations.push({ type: "plan.update", updates: { retrospectiveSummary: readChainValue(args, flag) } });
|
|
2099
|
+
break;
|
|
2100
|
+
case "--what-worked":
|
|
2101
|
+
operations.push({ type: "plan.update", updates: { whatWorked: [readChainValue(args, flag)] } });
|
|
2102
|
+
break;
|
|
2103
|
+
case "--issue":
|
|
2104
|
+
operations.push({ type: "plan.update", updates: { issues: [readChainValue(args, flag)] } });
|
|
2105
|
+
break;
|
|
2106
|
+
case "--follow-up":
|
|
2107
|
+
operations.push({ type: "plan.update", updates: { followUps: [readChainValue(args, flag)] } });
|
|
2108
|
+
break;
|
|
2073
2109
|
case "--reference": {
|
|
2074
2110
|
const path = readChainValue(args, flag);
|
|
2075
2111
|
const whyFlag = args.shift();
|
|
@@ -2088,6 +2124,15 @@ function readOrderedPlanEditOperations(args) {
|
|
|
2088
2124
|
}
|
|
2089
2125
|
return operations;
|
|
2090
2126
|
}
|
|
2127
|
+
function requestsPlanEndState(operations) {
|
|
2128
|
+
return operations.some((operation) => operation.type === "plan.status" && operation.status.startsWith("end."));
|
|
2129
|
+
}
|
|
2130
|
+
function planCompletionOperations(updates) {
|
|
2131
|
+
return [
|
|
2132
|
+
{ type: "plan.update", updates },
|
|
2133
|
+
{ type: "plan.status", status: "end.completed" },
|
|
2134
|
+
];
|
|
2135
|
+
}
|
|
2091
2136
|
function readOrderedTaskAddOperations(args) {
|
|
2092
2137
|
const operations = [];
|
|
2093
2138
|
while (args.length > 0) {
|
|
@@ -2222,16 +2267,25 @@ function readPlanMutationTarget(args) {
|
|
|
2222
2267
|
if (!boundPlanPath) {
|
|
2223
2268
|
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.");
|
|
2224
2269
|
}
|
|
2225
|
-
const
|
|
2226
|
-
|
|
2227
|
-
if (segments.length < 2 || relativePlanPath.startsWith("..") || path.isAbsolute(relativePlanPath)) {
|
|
2270
|
+
const target = parseTaskPlanPath(project, boundPlanPath);
|
|
2271
|
+
if (!target) {
|
|
2228
2272
|
throw new ClawError("PROJECT_CONFIG_INVALID", `Invalid session-bound plan path: ${boundPlanPath}`);
|
|
2229
2273
|
}
|
|
2230
2274
|
return {
|
|
2231
|
-
taskName:
|
|
2232
|
-
planFile: explicitPlanFile ??
|
|
2275
|
+
taskName: target.taskName,
|
|
2276
|
+
planFile: explicitPlanFile ?? target.planFile,
|
|
2233
2277
|
};
|
|
2234
2278
|
}
|
|
2279
|
+
function parseTaskPlanPath(project, planPath) {
|
|
2280
|
+
const relativePlanPath = path.relative(project.tasksDir, planPath);
|
|
2281
|
+
if (relativePlanPath.startsWith("..") || path.isAbsolute(relativePlanPath))
|
|
2282
|
+
return null;
|
|
2283
|
+
const segments = relativePlanPath.split(path.sep).filter(Boolean);
|
|
2284
|
+
const dated = /^\d{4}-\d{2}-\d{2}$/.test(segments[0] ?? "");
|
|
2285
|
+
const taskName = dated ? segments[1] : segments[0];
|
|
2286
|
+
const planFile = (dated ? segments.slice(2) : segments.slice(1)).join(path.sep);
|
|
2287
|
+
return taskName && planFile ? { taskName, planFile } : null;
|
|
2288
|
+
}
|
|
2235
2289
|
function readExplicitAddedTasks(args) {
|
|
2236
2290
|
return readGroupedValues(args, "--add-task", "--detail", false).map((entry) => ({
|
|
2237
2291
|
title: entry.value,
|
|
@@ -2274,6 +2328,13 @@ function readRepeatedIntegerFlag(args, flag) {
|
|
|
2274
2328
|
function failMissingNumericFlag(flag) {
|
|
2275
2329
|
throw new ClawError("PROJECT_CONFIG_INVALID", `Missing required flag ${flag}.`, { flag });
|
|
2276
2330
|
}
|
|
2331
|
+
function preparePlanEndFinalization(cwd, ownerSessionKey) {
|
|
2332
|
+
const workflowProject = resolveWorkflowProjectContext(cwd, ownerSessionKey);
|
|
2333
|
+
if (workflowProject.scope !== "project")
|
|
2334
|
+
return undefined;
|
|
2335
|
+
const skipGitNexusRefresh = ensureGitNexusReadyForPlanFinalization(cwd);
|
|
2336
|
+
return (taskName) => queueCompletionRefresh({ cwd, taskName, skipGitNexusRefresh });
|
|
2337
|
+
}
|
|
2277
2338
|
function queueCompletionRefresh(input) {
|
|
2278
2339
|
const project = resolveProjectContext(input.cwd);
|
|
2279
2340
|
const includeTaskRetention = input.includeTaskRetention ?? true;
|
|
@@ -2283,6 +2344,7 @@ function queueCompletionRefresh(input) {
|
|
|
2283
2344
|
: {
|
|
2284
2345
|
enabled: false,
|
|
2285
2346
|
maxTasksToKeep: project.projectConfig?.maxTasksToKeep ?? DEFAULT_MAX_TASKS_TO_KEEP,
|
|
2347
|
+
archivedTasks: [],
|
|
2286
2348
|
prunedArchivedTasks: [],
|
|
2287
2349
|
};
|
|
2288
2350
|
const startedAt = new Date().toISOString();
|
|
@@ -2639,7 +2701,7 @@ function computeCompletionDirtyHash(cwd, taskName, operations) {
|
|
|
2639
2701
|
const roots = [
|
|
2640
2702
|
path.join(project.clawDir, "memory.md"),
|
|
2641
2703
|
path.join(project.clawDir, "truth"),
|
|
2642
|
-
path.join(project.tasksDir, taskName),
|
|
2704
|
+
findTaskDirectory(project, taskName) ?? path.join(project.tasksDir, taskName),
|
|
2643
2705
|
];
|
|
2644
2706
|
const files = roots.flatMap((root) => listCompletionFingerprintFiles(root)).sort();
|
|
2645
2707
|
for (const filePath of files) {
|
|
@@ -2753,7 +2815,7 @@ function isWindowsAccessViolation(result) {
|
|
|
2753
2815
|
&& result.status !== null
|
|
2754
2816
|
&& (result.status >>> 0) === 0xc0000005;
|
|
2755
2817
|
}
|
|
2756
|
-
function
|
|
2818
|
+
function ensureGitNexusReadyForPlanFinalization(cwd) {
|
|
2757
2819
|
const project = resolveProjectContext(cwd);
|
|
2758
2820
|
if (project.projectConfig?.gitnexus !== true) {
|
|
2759
2821
|
return false;
|