cclaw-cli 0.42.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -4
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +17 -1
- package/dist/config.d.ts +63 -2
- package/dist/config.js +182 -8
- package/dist/content/harness-playbooks.js +9 -4
- package/dist/content/harnesses-doc.js +5 -0
- package/dist/content/hooks.d.ts +1 -0
- package/dist/content/hooks.js +46 -6
- package/dist/content/next-command.js +6 -3
- package/dist/content/observe.d.ts +1 -0
- package/dist/content/observe.js +101 -2
- package/dist/content/protocols.js +14 -9
- package/dist/content/skills.js +3 -0
- package/dist/content/stages/design.js +1 -0
- package/dist/content/stages/plan.js +3 -2
- package/dist/content/stages/scope.js +2 -1
- package/dist/content/stages/tdd.js +1 -8
- package/dist/install.d.ts +5 -6
- package/dist/install.js +35 -13
- package/dist/internal/advance-stage.d.ts +7 -0
- package/dist/internal/advance-stage.js +425 -0
- package/dist/types.d.ts +23 -2
- package/package.json +1 -1
package/dist/content/observe.js
CHANGED
|
@@ -154,6 +154,7 @@ exit 0
|
|
|
154
154
|
`;
|
|
155
155
|
}
|
|
156
156
|
export function workflowGuardScript(options = {}) {
|
|
157
|
+
const workflowGuardMode = options.workflowGuardMode === "strict" ? "strict" : "advisory";
|
|
157
158
|
const tddEnforcementMode = options.tddEnforcementMode === "strict" ? "strict" : "advisory";
|
|
158
159
|
const tddTestGlobs = options.tddTestGlobs && options.tddTestGlobs.length > 0
|
|
159
160
|
? options.tddTestGlobs.join(",")
|
|
@@ -162,7 +163,7 @@ export function workflowGuardScript(options = {}) {
|
|
|
162
163
|
# cclaw workflow guard hook — generated by cclaw sync
|
|
163
164
|
# Enforces stage-aware command discipline and recent flow-state read hygiene.
|
|
164
165
|
set -uo pipefail
|
|
165
|
-
WORKFLOW_GUARD_MODE="\${CCLAW_WORKFLOW_GUARD_MODE
|
|
166
|
+
WORKFLOW_GUARD_MODE="\${CCLAW_WORKFLOW_GUARD_MODE:-${workflowGuardMode}}"
|
|
166
167
|
MAX_FLOW_READ_AGE_SEC="\${CCLAW_WORKFLOW_GUARD_MAX_AGE_SEC:-1800}"
|
|
167
168
|
TDD_ENFORCEMENT_MODE="${tddEnforcementMode}"
|
|
168
169
|
TDD_TEST_GLOBS="${tddTestGlobs}"
|
|
@@ -342,6 +343,81 @@ is_cclaw_cli_payload() {
|
|
|
342
343
|
printf '%s' "$1" | grep -Eq '(cclaw |npx cclaw |/cc-|/cc[^[:alnum:]_-])'
|
|
343
344
|
}
|
|
344
345
|
|
|
346
|
+
extract_flow_state_after_json() {
|
|
347
|
+
if command -v jq >/dev/null 2>&1; then
|
|
348
|
+
printf '%s' "$INPUT" | jq -r '
|
|
349
|
+
.tool_input?.content //
|
|
350
|
+
.input?.content //
|
|
351
|
+
.arguments?.content //
|
|
352
|
+
.params?.content //
|
|
353
|
+
.payload?.content //
|
|
354
|
+
.content //
|
|
355
|
+
.input?.new_string //
|
|
356
|
+
.tool_input?.new_string //
|
|
357
|
+
""
|
|
358
|
+
' 2>/dev/null || echo ""
|
|
359
|
+
return 0
|
|
360
|
+
fi
|
|
361
|
+
|
|
362
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
363
|
+
INPUT_JSON="$INPUT" python3 - <<'PY'
|
|
364
|
+
import json
|
|
365
|
+
import os
|
|
366
|
+
|
|
367
|
+
try:
|
|
368
|
+
payload = json.loads(os.environ.get("INPUT_JSON", "{}"))
|
|
369
|
+
except Exception:
|
|
370
|
+
payload = {}
|
|
371
|
+
|
|
372
|
+
def pick(value):
|
|
373
|
+
if not isinstance(value, dict):
|
|
374
|
+
return ""
|
|
375
|
+
for key in ("tool_input", "input", "arguments", "params", "payload"):
|
|
376
|
+
nested = value.get(key)
|
|
377
|
+
if isinstance(nested, dict):
|
|
378
|
+
content = nested.get("content")
|
|
379
|
+
if isinstance(content, str) and content.strip():
|
|
380
|
+
return content
|
|
381
|
+
new_string = nested.get("new_string")
|
|
382
|
+
if isinstance(new_string, str) and new_string.strip():
|
|
383
|
+
return new_string
|
|
384
|
+
content = value.get("content")
|
|
385
|
+
if isinstance(content, str) and content.strip():
|
|
386
|
+
return content
|
|
387
|
+
return ""
|
|
388
|
+
|
|
389
|
+
print(pick(payload))
|
|
390
|
+
PY
|
|
391
|
+
return 0
|
|
392
|
+
fi
|
|
393
|
+
|
|
394
|
+
printf ''
|
|
395
|
+
return 0
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
verify_flow_state_candidate() {
|
|
399
|
+
local candidate_json="$1"
|
|
400
|
+
[ -n "$candidate_json" ] || return 1
|
|
401
|
+
local tmp_file="$STATE_DIR/.flow-state-candidate.$$.$RANDOM.json"
|
|
402
|
+
printf '%s' "$candidate_json" > "$tmp_file" 2>/dev/null || {
|
|
403
|
+
rm -f "$tmp_file" 2>/dev/null || true
|
|
404
|
+
return 1
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
local verify_cmd=(npx -y cclaw-cli internal verify-flow-state-diff --after-file="$tmp_file" --quiet)
|
|
408
|
+
if command -v cclaw >/dev/null 2>&1; then
|
|
409
|
+
verify_cmd=(cclaw internal verify-flow-state-diff --after-file="$tmp_file" --quiet)
|
|
410
|
+
fi
|
|
411
|
+
|
|
412
|
+
if "\${verify_cmd[@]}" >/dev/null 2>&1; then
|
|
413
|
+
rm -f "$tmp_file" 2>/dev/null || true
|
|
414
|
+
return 0
|
|
415
|
+
fi
|
|
416
|
+
|
|
417
|
+
rm -f "$tmp_file" 2>/dev/null || true
|
|
418
|
+
return 1
|
|
419
|
+
}
|
|
420
|
+
|
|
345
421
|
is_preimplementation_stage() {
|
|
346
422
|
case "$1" in
|
|
347
423
|
brainstorm|scope|design|spec|plan) return 0 ;;
|
|
@@ -480,6 +556,22 @@ if [ -n "$TARGET_STAGE" ] && [ "$CURRENT_STAGE" != "none" ]; then
|
|
|
480
556
|
fi
|
|
481
557
|
fi
|
|
482
558
|
|
|
559
|
+
if is_mutating_tool "$TOOL_LOWER" && printf '%s' "$PAYLOAD_LOWER" | grep -Eq '\.cclaw/state/flow-state\.json'; then
|
|
560
|
+
if [ -n "$REASONS" ]; then
|
|
561
|
+
REASONS="$REASONS,direct_flow_state_edit"
|
|
562
|
+
else
|
|
563
|
+
REASONS="direct_flow_state_edit"
|
|
564
|
+
fi
|
|
565
|
+
FLOW_STATE_AFTER_JSON=$(extract_flow_state_after_json)
|
|
566
|
+
if [ -n "$FLOW_STATE_AFTER_JSON" ]; then
|
|
567
|
+
if ! verify_flow_state_candidate "$FLOW_STATE_AFTER_JSON"; then
|
|
568
|
+
REASONS="$REASONS,flow_state_edit_failed_internal_validation"
|
|
569
|
+
fi
|
|
570
|
+
else
|
|
571
|
+
REASONS="$REASONS,flow_state_edit_without_serialized_content"
|
|
572
|
+
fi
|
|
573
|
+
fi
|
|
574
|
+
|
|
483
575
|
if is_preimplementation_stage "$CURRENT_STAGE" && is_mutating_tool "$TOOL_LOWER"; then
|
|
484
576
|
if ! printf '%s' "$PAYLOAD_LOWER" | grep -Eq '\.cclaw/'; then
|
|
485
577
|
if [ -n "$REASONS" ]; then
|
|
@@ -567,7 +659,11 @@ PY
|
|
|
567
659
|
fi
|
|
568
660
|
|
|
569
661
|
if [ -n "$REASONS" ]; then
|
|
570
|
-
|
|
662
|
+
if printf '%s' "$REASONS" | grep -Eq 'direct_flow_state_edit'; then
|
|
663
|
+
NOTE="Cclaw workflow guard: direct flow-state edit bypasses the canonical stage-complete helper (\${REASONS}). Prefer: bash ${RUNTIME_ROOT}/hooks/stage-complete.sh <stage>. In strict mode this is blocked."
|
|
664
|
+
else
|
|
665
|
+
NOTE="Cclaw workflow guard: detected potential flow violation (\${REASONS}). Re-read ${RUNTIME_ROOT}/state/flow-state.json, avoid source edits before tdd stage, and enforce RED -> GREEN -> REFACTOR discipline inside tdd."
|
|
666
|
+
fi
|
|
571
667
|
if command -v jq >/dev/null 2>&1; then
|
|
572
668
|
ENTRY=$(jq -n -c \
|
|
573
669
|
--arg ts "$TS" \
|
|
@@ -1826,6 +1922,9 @@ export function codexHooksJsonWithObservation() {
|
|
|
1826
1922
|
hooks: [{
|
|
1827
1923
|
type: "command",
|
|
1828
1924
|
command: `bash ${RUNTIME_ROOT}/hooks/prompt-guard.sh`
|
|
1925
|
+
}, {
|
|
1926
|
+
type: "command",
|
|
1927
|
+
command: "bash -lc 'if command -v cclaw >/dev/null 2>&1; then cclaw internal verify-current-state --quiet >/dev/null || true; else npx -y cclaw-cli internal verify-current-state --quiet >/dev/null || true; fi'"
|
|
1829
1928
|
}]
|
|
1830
1929
|
}],
|
|
1831
1930
|
PreToolUse: [{
|
|
@@ -99,16 +99,21 @@ Shared closeout sequence applied by every stage skill.
|
|
|
99
99
|
## Required order
|
|
100
100
|
|
|
101
101
|
1. Verify mandatory delegations are completed or explicitly waived.
|
|
102
|
-
2.
|
|
103
|
-
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
2. Persist stage artifact under \`.cclaw/artifacts/\`.
|
|
103
|
+
3. Use the canonical helper:
|
|
104
|
+
- \`bash .cclaw/hooks/stage-complete.sh <stage>\`
|
|
105
|
+
- helper responsibilities: validate mandatory delegations, validate
|
|
106
|
+
current-stage gate evidence/artifact lint, update
|
|
107
|
+
\`stageGateCatalog\` + \`guardEvidence\`, and advance \`currentStage\`.
|
|
108
|
+
4. Legacy fallback (only when helper is unavailable): manually edit
|
|
109
|
+
\`.cclaw/state/flow-state.json\` to mark passed gates, clear resolved
|
|
110
|
+
blocked gates, and update \`guardEvidence\`. This path is legacy and
|
|
111
|
+
intentionally noisy in workflow guards.
|
|
112
|
+
5. Run \`npx cclaw doctor\` and resolve failures.
|
|
113
|
+
6. **Capture through-flow learnings** — see the policy below. Knowledge
|
|
109
114
|
accrues continuously across stages, not just at retro.
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
7. Notify user with stage completion and next action (\`/cc-next\`).
|
|
116
|
+
8. Stop; do not auto-run the next stage unless user asks.
|
|
112
117
|
|
|
113
118
|
## Through-flow knowledge capture
|
|
114
119
|
|
package/dist/content/skills.js
CHANGED
|
@@ -222,6 +222,9 @@ function completionParametersBlock(schema) {
|
|
|
222
222
|
- \`gates\`: ${gateList}
|
|
223
223
|
- \`artifact\`: \`${RUNTIME_ROOT}/artifacts/${schema.artifactFile}\`
|
|
224
224
|
- \`mandatory delegations\`: ${mandatory}
|
|
225
|
+
- \`completion helper\`: \`bash .cclaw/hooks/stage-complete.sh ${schema.stage}\`
|
|
226
|
+
- Record mandatory delegation completion/waiver in \`${RUNTIME_ROOT}/state/delegation-log.json\` with rationale as needed.
|
|
227
|
+
- Use the completion helper instead of raw \`flow-state.json\` edits (legacy direct edits trigger workflow-guard warnings or strict-mode blocks).
|
|
225
228
|
|
|
226
229
|
Apply shared completion logic from:
|
|
227
230
|
\`${COMPLETION_PROTOCOL_PATH}\`
|
|
@@ -42,6 +42,7 @@ export const DESIGN = {
|
|
|
42
42
|
"If a section has no issues, say 'No issues found' and move on.",
|
|
43
43
|
"Do not skip failure-mode mapping.",
|
|
44
44
|
"For design baseline approval: present the full baseline. **STOP.** Do NOT proceed until user explicitly approves the design.",
|
|
45
|
+
"**STOP BEFORE ADVANCE.** Mandatory delegation `planner` must be marked completed or explicitly waived in `.cclaw/state/delegation-log.json`. Then close the stage via `bash .cclaw/hooks/stage-complete.sh design` (do not hand-edit `.cclaw/state/flow-state.json`).",
|
|
45
46
|
"Take a firm position on every recommendation. Do NOT hedge with 'it depends' or 'you could do either'. State your opinion, then justify it.",
|
|
46
47
|
"Use pushback patterns for weak framing: if the user says 'it's just a small change', respond with 'small changes to shared interfaces have outsized blast radius — let's map it'. If 'we'll refactor later', respond with 'later never comes — show me the refactor ticket or do it now'.",
|
|
47
48
|
"When the user's proposed architecture is suboptimal, say so directly. Offer the alternative with concrete trade-offs, do not bury criticism in praise.",
|
|
@@ -29,7 +29,7 @@ export const PLAN = {
|
|
|
29
29
|
"Map scope Locked Decisions — every D-XX from scope is referenced by at least one plan task (or explicitly marked deferred with reason).",
|
|
30
30
|
"Run anti-placeholder + anti-scope-reduction scans — block `TODO/TBD/...` and phrasing like `v1`, `for now`, `later` for locked boundaries.",
|
|
31
31
|
"Define checkpoints — mark points where progress should be validated before continuing.",
|
|
32
|
-
"WAIT_FOR_CONFIRM — write plan artifact and explicitly pause. **STOP.** Do NOT proceed until user confirms. Then
|
|
32
|
+
"WAIT_FOR_CONFIRM — write plan artifact and explicitly pause. **STOP.** Do NOT proceed until user confirms. Then close the stage with `bash .cclaw/hooks/stage-complete.sh plan` and tell user to run `/cc-next`."
|
|
33
33
|
],
|
|
34
34
|
interactionProtocol: [
|
|
35
35
|
"Plan in read-only mode relative to implementation.",
|
|
@@ -38,7 +38,8 @@ export const PLAN = {
|
|
|
38
38
|
"Attach verification step to every task.",
|
|
39
39
|
"Preserve locked scope boundaries: no silent scope reduction language in task rows.",
|
|
40
40
|
"Enforce WAIT_FOR_CONFIRM: present the plan summary with options (A) Approve / (B) Revise / (C) Reject.",
|
|
41
|
-
"**STOP.** Do NOT proceed until user explicitly approves.
|
|
41
|
+
"**STOP.** Do NOT proceed until user explicitly approves.",
|
|
42
|
+
"**STOP BEFORE ADVANCE.** Mandatory delegation `planner` must be marked completed or explicitly waived in `.cclaw/state/delegation-log.json`. Then close the stage via `bash .cclaw/hooks/stage-complete.sh plan` and tell the user to run `/cc-next`."
|
|
42
43
|
],
|
|
43
44
|
process: [
|
|
44
45
|
"Build dependency graph and ordered slices.",
|
|
@@ -41,7 +41,8 @@ export const SCOPE = {
|
|
|
41
41
|
"Record explicit in-scope and out-of-scope contract.",
|
|
42
42
|
"Once the user accepts or rejects a recommendation, commit fully. Do not re-argue.",
|
|
43
43
|
"Produce a clean scope summary after all issues are resolved.",
|
|
44
|
-
"**STOP.** Wait for explicit user approval of scope contract before advancing to design."
|
|
44
|
+
"**STOP.** Wait for explicit user approval of scope contract before advancing to design.",
|
|
45
|
+
"**STOP BEFORE ADVANCE.** Mandatory delegation `planner` must be marked completed or explicitly waived in `.cclaw/state/delegation-log.json`. Then close the stage via `bash .cclaw/hooks/stage-complete.sh scope` (do not hand-edit `.cclaw/state/flow-state.json`)."
|
|
45
46
|
],
|
|
46
47
|
process: [
|
|
47
48
|
"Run premise challenge and existing-solution leverage check.",
|
|
@@ -92,18 +92,11 @@ export const TDD = {
|
|
|
92
92
|
],
|
|
93
93
|
commonRationalizations: [
|
|
94
94
|
"Writing code before failing test",
|
|
95
|
-
"Asserting implementation details instead of behavior",
|
|
96
|
-
"Big-bang implementation across multiple slices",
|
|
97
95
|
"Partial test runs presented as GREEN",
|
|
98
96
|
"Skipping evidence capture",
|
|
99
97
|
"Undocumented refactor changes",
|
|
100
|
-
"Adding features beyond what RED tests require",
|
|
101
|
-
"No failing test output (RED missing)",
|
|
102
|
-
"Implementation edits appear before RED evidence",
|
|
103
98
|
"No full-suite GREEN evidence",
|
|
104
|
-
"
|
|
105
|
-
"Multiple tasks implemented in one pass without justification",
|
|
106
|
-
"Files changed outside current slice scope"
|
|
99
|
+
"Multiple tasks implemented in one pass without justification"
|
|
107
100
|
],
|
|
108
101
|
policyNeedles: ["RED", "GREEN", "REFACTOR", "failing test", "full test suite", "acceptance criteria", "traceable to plan slice"],
|
|
109
102
|
artifactFile: "06-tdd.md",
|
package/dist/install.d.ts
CHANGED
|
@@ -9,13 +9,12 @@ export declare function syncCclaw(projectRoot: string): Promise<void>;
|
|
|
9
9
|
/**
|
|
10
10
|
* Refresh generated files in `.cclaw/` without touching user-authored
|
|
11
11
|
* artifacts, state, or custom config keys. Only the `version` + `flowVersion`
|
|
12
|
-
* stamps are rewritten so the on-disk config reflects the installed CLI
|
|
13
|
-
* `promptGuardMode`, `tddEnforcement`, `gitHookGuards`, `languageRulePacks`,
|
|
14
|
-
* `trackHeuristics`, and `sliceReview` are preserved verbatim from the
|
|
15
|
-
* existing config.
|
|
12
|
+
* stamps are rewritten so the on-disk config reflects the installed CLI.
|
|
16
13
|
*
|
|
17
|
-
*
|
|
18
|
-
* (
|
|
14
|
+
* Shape preservation: if the user previously hand-authored advanced keys
|
|
15
|
+
* (e.g. `tddTestGlobs`, `trackHeuristics`, `sliceReview`), those stay in the
|
|
16
|
+
* yaml. If their existing config is minimal, the upgrade keeps it minimal —
|
|
17
|
+
* advanced knobs are never silently added.
|
|
19
18
|
*/
|
|
20
19
|
export declare function upgradeCclaw(projectRoot: string): Promise<void>;
|
|
21
20
|
export declare function uninstallCclaw(projectRoot: string): Promise<void>;
|
package/dist/install.js
CHANGED
|
@@ -3,7 +3,7 @@ import fs from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
import { CCLAW_VERSION, COMMAND_FILE_ORDER, FLOW_VERSION, REQUIRED_DIRS, RUNTIME_ROOT } from "./constants.js";
|
|
6
|
-
import { writeConfig, createDefaultConfig, readConfig, configPath } from "./config.js";
|
|
6
|
+
import { writeConfig, createDefaultConfig, readConfig, configPath, detectLanguageRulePacks, detectAdvancedKeys } from "./config.js";
|
|
7
7
|
import { commandContract } from "./content/contracts.js";
|
|
8
8
|
import { contextModeFiles, createInitialContextModeState } from "./content/contexts.js";
|
|
9
9
|
import { learnSkillMarkdown, learnCommandContract } from "./content/learnings.js";
|
|
@@ -23,7 +23,7 @@ import { archiveCommandContract, archiveCommandSkillMarkdown } from "./content/a
|
|
|
23
23
|
import { rewindCommandContract, rewindCommandSkillMarkdown } from "./content/rewind-command.js";
|
|
24
24
|
import { subagentDrivenDevSkill, parallelAgentsSkill } from "./content/subagents.js";
|
|
25
25
|
import { sessionHooksSkillMarkdown } from "./content/session-hooks.js";
|
|
26
|
-
import { sessionStartScript, stopCheckpointScript, preCompactScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
|
|
26
|
+
import { sessionStartScript, stopCheckpointScript, stageCompleteScript, preCompactScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
|
|
27
27
|
import { contextMonitorScript, promptGuardScript, workflowGuardScript } from "./content/observe.js";
|
|
28
28
|
import { META_SKILL_NAME, usingCclawSkillMarkdown } from "./content/meta-skill.js";
|
|
29
29
|
import { decisionProtocolMarkdown, completionProtocolMarkdown, ethosProtocolMarkdown } from "./content/protocols.js";
|
|
@@ -616,11 +616,13 @@ async function writeHooks(projectRoot, config) {
|
|
|
616
616
|
await ensureDir(hooksDir);
|
|
617
617
|
await writeFileSafe(path.join(hooksDir, "session-start.sh"), sessionStartScript());
|
|
618
618
|
await writeFileSafe(path.join(hooksDir, "stop-checkpoint.sh"), stopCheckpointScript());
|
|
619
|
+
await writeFileSafe(path.join(hooksDir, "stage-complete.sh"), stageCompleteScript());
|
|
619
620
|
await writeFileSafe(path.join(hooksDir, "pre-compact.sh"), preCompactScript());
|
|
620
621
|
await writeFileSafe(path.join(hooksDir, "prompt-guard.sh"), promptGuardScript({
|
|
621
622
|
strictMode: config.promptGuardMode === "strict"
|
|
622
623
|
}));
|
|
623
624
|
await writeFileSafe(path.join(hooksDir, "workflow-guard.sh"), workflowGuardScript({
|
|
625
|
+
workflowGuardMode: config.strictness ?? "advisory",
|
|
624
626
|
tddEnforcementMode: config.tddEnforcement ?? "advisory",
|
|
625
627
|
tddTestGlobs: config.tddTestGlobs
|
|
626
628
|
}));
|
|
@@ -631,6 +633,7 @@ async function writeHooks(projectRoot, config) {
|
|
|
631
633
|
for (const script of [
|
|
632
634
|
"session-start.sh",
|
|
633
635
|
"stop-checkpoint.sh",
|
|
636
|
+
"stage-complete.sh",
|
|
634
637
|
"pre-compact.sh",
|
|
635
638
|
"prompt-guard.sh",
|
|
636
639
|
"workflow-guard.sh",
|
|
@@ -1156,13 +1159,24 @@ async function materializeRuntime(projectRoot, config, forceStateReset) {
|
|
|
1156
1159
|
await ensureGitignore(projectRoot);
|
|
1157
1160
|
}
|
|
1158
1161
|
export async function initCclaw(options) {
|
|
1159
|
-
const
|
|
1160
|
-
|
|
1162
|
+
const baseConfig = createDefaultConfig(options.harnesses, options.track);
|
|
1163
|
+
// Best-effort auto-detect: a Node project gets `typescript`, a Go module
|
|
1164
|
+
// gets `go`, etc. Skipped entirely when the project root has no manifests.
|
|
1165
|
+
const detectedPacks = await detectLanguageRulePacks(options.projectRoot);
|
|
1166
|
+
const config = {
|
|
1167
|
+
...baseConfig,
|
|
1168
|
+
languageRulePacks: detectedPacks
|
|
1169
|
+
};
|
|
1170
|
+
// Write a minimal `config.yaml` — advanced knobs live in docs/config.md
|
|
1171
|
+
// and only appear in the on-disk file when the user sets them explicitly
|
|
1172
|
+
// or a non-default value was detected (e.g. languageRulePacks).
|
|
1173
|
+
await writeConfig(options.projectRoot, config, { mode: "minimal" });
|
|
1161
1174
|
await materializeRuntime(options.projectRoot, config, true);
|
|
1162
1175
|
}
|
|
1163
1176
|
export async function syncCclaw(projectRoot) {
|
|
1177
|
+
const configExists = await exists(configPath(projectRoot));
|
|
1164
1178
|
const config = await readConfig(projectRoot);
|
|
1165
|
-
if (!
|
|
1179
|
+
if (!configExists) {
|
|
1166
1180
|
await writeConfig(projectRoot, createDefaultConfig(config.harnesses));
|
|
1167
1181
|
}
|
|
1168
1182
|
await materializeRuntime(projectRoot, config, false);
|
|
@@ -1170,22 +1184,25 @@ export async function syncCclaw(projectRoot) {
|
|
|
1170
1184
|
/**
|
|
1171
1185
|
* Refresh generated files in `.cclaw/` without touching user-authored
|
|
1172
1186
|
* artifacts, state, or custom config keys. Only the `version` + `flowVersion`
|
|
1173
|
-
* stamps are rewritten so the on-disk config reflects the installed CLI
|
|
1174
|
-
* `promptGuardMode`, `tddEnforcement`, `gitHookGuards`, `languageRulePacks`,
|
|
1175
|
-
* `trackHeuristics`, and `sliceReview` are preserved verbatim from the
|
|
1176
|
-
* existing config.
|
|
1187
|
+
* stamps are rewritten so the on-disk config reflects the installed CLI.
|
|
1177
1188
|
*
|
|
1178
|
-
*
|
|
1179
|
-
* (
|
|
1189
|
+
* Shape preservation: if the user previously hand-authored advanced keys
|
|
1190
|
+
* (e.g. `tddTestGlobs`, `trackHeuristics`, `sliceReview`), those stay in the
|
|
1191
|
+
* yaml. If their existing config is minimal, the upgrade keeps it minimal —
|
|
1192
|
+
* advanced knobs are never silently added.
|
|
1180
1193
|
*/
|
|
1181
1194
|
export async function upgradeCclaw(projectRoot) {
|
|
1195
|
+
const advancedKeysPresent = await detectAdvancedKeys(projectRoot);
|
|
1182
1196
|
const existing = await readConfig(projectRoot);
|
|
1183
1197
|
const upgraded = {
|
|
1184
1198
|
...existing,
|
|
1185
1199
|
version: CCLAW_VERSION,
|
|
1186
1200
|
flowVersion: FLOW_VERSION
|
|
1187
1201
|
};
|
|
1188
|
-
await writeConfig(projectRoot, upgraded
|
|
1202
|
+
await writeConfig(projectRoot, upgraded, {
|
|
1203
|
+
mode: "minimal",
|
|
1204
|
+
advancedKeysPresent
|
|
1205
|
+
});
|
|
1189
1206
|
await materializeRuntime(projectRoot, upgraded, false);
|
|
1190
1207
|
}
|
|
1191
1208
|
function stripManagedHookCommands(value) {
|
|
@@ -1246,7 +1263,12 @@ function stripManagedHookCommands(value) {
|
|
|
1246
1263
|
}
|
|
1247
1264
|
function isManagedRuntimeHookCommand(command) {
|
|
1248
1265
|
const normalized = command.trim().replace(/\s+/gu, " ");
|
|
1249
|
-
|
|
1266
|
+
if (/(^|\s)(?:bash\s+)?(?:\.\/)?\.cclaw\/hooks\/(?:session-start|stop-checkpoint|pre-compact|prompt-guard|workflow-guard|context-monitor)\.sh(?:\s|$)/u.test(normalized)) {
|
|
1267
|
+
return true;
|
|
1268
|
+
}
|
|
1269
|
+
// Codex UserPromptSubmit non-blocking state nudge:
|
|
1270
|
+
// bash -lc '... cclaw internal verify-current-state --quiet ...'
|
|
1271
|
+
return /internal verify-current-state --quiet/u.test(normalized);
|
|
1250
1272
|
}
|
|
1251
1273
|
async function removeManagedHookEntries(hookFilePath) {
|
|
1252
1274
|
if (!(await exists(hookFilePath)))
|