cclaw-cli 0.5.3 → 0.5.5

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.
@@ -1,12 +1,9 @@
1
1
  /**
2
- * Tool observation system captures PreToolUse/PostToolUse events
3
- * to .cclaw/observations.jsonl for continuous learning.
2
+ * Hook helper scripts and harness hook JSON generators.
4
3
  *
5
- * observe.sh: reads hook JSON from stdin, extracts tool name + truncated I/O,
6
- * appends a JSONL line to .cclaw/observations.jsonl.
7
- *
8
- * summarize-observations.sh: run at session stop, reads recent observations,
9
- * identifies patterns, and appends new learnings to .cclaw/learnings.jsonl.
4
+ * This module still provides prompt/workflow/context guard scripts and
5
+ * cross-harness hook wiring. Observation pipeline scripts are retained only
6
+ * for backward compatibility and are not wired by default runtime generation.
10
7
  */
11
8
  import { RUNTIME_ROOT } from "../constants.js";
12
9
  import { RUNTIME_SHELL_DETECT_ROOT } from "./hooks.js";
@@ -39,7 +36,7 @@ INPUT=$(cat 2>/dev/null || echo '{}')
39
36
  TOOL="unknown"
40
37
  PAYLOAD=""
41
38
  if command -v jq >/dev/null 2>&1; then
42
- TOOL=$(printf '%s' "$INPUT" | jq -r '.tool_name // .tool // "unknown"' 2>/dev/null || echo "unknown")
39
+ TOOL=$(printf '%s' "$INPUT" | jq -r '.tool_name // .tool // .toolName // .name // .id // .command // .tool.name // .tool.id // .input.tool_name // .input.tool // .input.toolName // .input.name // .input.id // .input.command // .input.tool.name // .input.tool.id // "unknown"' 2>/dev/null || echo "unknown")
43
40
  PAYLOAD=$(printf '%s' "$INPUT" | jq -r '.tool_input // .input // .arguments // .params // .payload // {} | tostring' 2>/dev/null || echo "")
44
41
  elif command -v python3 >/dev/null 2>&1; then
45
42
  TOOL=$(INPUT_JSON="$INPUT" python3 - <<'PY'
@@ -50,8 +47,40 @@ try:
50
47
  value = json.loads(os.environ.get("INPUT_JSON", "{}"))
51
48
  except Exception:
52
49
  value = {}
53
- tool = value.get("tool_name") or value.get("tool") or "unknown"
54
- print(tool if isinstance(tool, str) else "unknown")
50
+
51
+ def pick_tool(payload):
52
+ if not isinstance(payload, dict):
53
+ return "unknown"
54
+ candidates = [
55
+ payload.get("tool_name"),
56
+ payload.get("tool"),
57
+ payload.get("toolName"),
58
+ payload.get("name"),
59
+ payload.get("id"),
60
+ payload.get("command")
61
+ ]
62
+ top_tool = payload.get("tool")
63
+ if isinstance(top_tool, dict):
64
+ candidates.extend([top_tool.get("name"), top_tool.get("id")])
65
+ nested = payload.get("input")
66
+ if isinstance(nested, dict):
67
+ candidates.extend([
68
+ nested.get("tool_name"),
69
+ nested.get("tool"),
70
+ nested.get("toolName"),
71
+ nested.get("name"),
72
+ nested.get("id"),
73
+ nested.get("command")
74
+ ])
75
+ nested_tool = nested.get("tool")
76
+ if isinstance(nested_tool, dict):
77
+ candidates.extend([nested_tool.get("name"), nested_tool.get("id")])
78
+ for candidate in candidates:
79
+ if isinstance(candidate, str) and candidate.strip():
80
+ return candidate.strip()
81
+ return "unknown"
82
+
83
+ print(pick_tool(value))
55
84
  PY
56
85
  )
57
86
  PAYLOAD=$(printf '%s' "$INPUT")
@@ -70,7 +99,7 @@ REASONS=""
70
99
 
71
100
  case "$TOOL_LOWER" in
72
101
  write|edit|multiedit|delete|applypatch|runcommand|shell|terminal|execcommand)
73
- if printf '%s' "$PAYLOAD_LOWER" | grep -Eq '\.cclaw/(state|artifacts|hooks|skills|commands|agents|runs|learnings)'; then
102
+ if printf '%s' "$PAYLOAD_LOWER" | grep -Eq '\.cclaw/(state|artifacts|hooks|skills|commands|agents|runs|knowledge)'; then
74
103
  REASONS="write_to_cclaw_runtime"
75
104
  fi
76
105
  ;;
@@ -146,7 +175,7 @@ INPUT=$(cat 2>/dev/null || echo '{}')
146
175
  TOOL="unknown"
147
176
  PAYLOAD=""
148
177
  if command -v jq >/dev/null 2>&1; then
149
- TOOL=$(printf '%s' "$INPUT" | jq -r '.tool_name // .tool // "unknown"' 2>/dev/null || echo "unknown")
178
+ TOOL=$(printf '%s' "$INPUT" | jq -r '.tool_name // .tool // .toolName // .name // .id // .command // .tool.name // .tool.id // .input.tool_name // .input.tool // .input.toolName // .input.name // .input.id // .input.command // .input.tool.name // .input.tool.id // "unknown"' 2>/dev/null || echo "unknown")
150
179
  PAYLOAD=$(printf '%s' "$INPUT" | jq -r '.tool_input // .input // .arguments // .params // .payload // {} | tostring' 2>/dev/null || echo "")
151
180
  elif command -v python3 >/dev/null 2>&1; then
152
181
  TOOL=$(INPUT_JSON="$INPUT" python3 - <<'PY'
@@ -156,8 +185,40 @@ try:
156
185
  value = json.loads(os.environ.get("INPUT_JSON", "{}"))
157
186
  except Exception:
158
187
  value = {}
159
- tool = value.get("tool_name") or value.get("tool") or "unknown"
160
- print(tool if isinstance(tool, str) else "unknown")
188
+
189
+ def pick_tool(payload):
190
+ if not isinstance(payload, dict):
191
+ return "unknown"
192
+ candidates = [
193
+ payload.get("tool_name"),
194
+ payload.get("tool"),
195
+ payload.get("toolName"),
196
+ payload.get("name"),
197
+ payload.get("id"),
198
+ payload.get("command")
199
+ ]
200
+ top_tool = payload.get("tool")
201
+ if isinstance(top_tool, dict):
202
+ candidates.extend([top_tool.get("name"), top_tool.get("id")])
203
+ nested = payload.get("input")
204
+ if isinstance(nested, dict):
205
+ candidates.extend([
206
+ nested.get("tool_name"),
207
+ nested.get("tool"),
208
+ nested.get("toolName"),
209
+ nested.get("name"),
210
+ nested.get("id"),
211
+ nested.get("command")
212
+ ])
213
+ nested_tool = nested.get("tool")
214
+ if isinstance(nested_tool, dict):
215
+ candidates.extend([nested_tool.get("name"), nested_tool.get("id")])
216
+ for candidate in candidates:
217
+ if isinstance(candidate, str) and candidate.strip():
218
+ return candidate.strip()
219
+ return "unknown"
220
+
221
+ print(pick_tool(value))
161
222
  PY
162
223
  )
163
224
  PAYLOAD=$(printf '%s' "$INPUT")
@@ -527,7 +588,7 @@ INPUT=$(cat 2>/dev/null || echo '{}')
527
588
  TOOL="unknown"
528
589
  PAYLOAD=""
529
590
  if command -v jq >/dev/null 2>&1; then
530
- TOOL=$(echo "$INPUT" | jq -r '.tool_name // .tool // "unknown"' 2>/dev/null || echo "unknown")
591
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // .tool // .toolName // .name // .id // .command // .tool.name // .tool.id // .input.tool_name // .input.tool // .input.toolName // .input.name // .input.id // .input.command // .input.tool.name // .input.tool.id // "unknown"' 2>/dev/null || echo "unknown")
531
592
  if [ "$PHASE" = "pre" ]; then
532
593
  PAYLOAD=$(echo "$INPUT" | jq -r --arg max "$MAX_LEN" '.tool_input // .input // {} | tostring | .[0:($max|tonumber)]' 2>/dev/null || echo "")
533
594
  else
@@ -1531,9 +1592,6 @@ export function claudeHooksJsonWithObservation() {
1531
1592
  }, {
1532
1593
  type: "command",
1533
1594
  command: `bash ${RUNTIME_ROOT}/hooks/workflow-guard.sh`
1534
- }, {
1535
- type: "command",
1536
- command: `bash ${RUNTIME_ROOT}/hooks/observe.sh pre`
1537
1595
  }]
1538
1596
  }],
1539
1597
  PostToolUse: [{
@@ -1541,24 +1599,14 @@ export function claudeHooksJsonWithObservation() {
1541
1599
  hooks: [{
1542
1600
  type: "command",
1543
1601
  command: `bash ${RUNTIME_ROOT}/hooks/context-monitor.sh`
1544
- }, {
1545
- type: "command",
1546
- command: `bash ${RUNTIME_ROOT}/hooks/observe.sh post`
1547
1602
  }]
1548
1603
  }],
1549
1604
  Stop: [{
1550
- hooks: [
1551
- {
1552
- type: "command",
1553
- command: `bash ${RUNTIME_ROOT}/hooks/summarize-observations.sh`,
1554
- timeout: 15
1555
- },
1556
- {
1605
+ hooks: [{
1557
1606
  type: "command",
1558
1607
  command: `bash ${RUNTIME_ROOT}/hooks/stop-checkpoint.sh`,
1559
1608
  timeout: 10
1560
- }
1561
- ]
1609
+ }]
1562
1610
  }]
1563
1611
  }
1564
1612
  }, null, 2);
@@ -1586,21 +1634,12 @@ export function cursorHooksJsonWithObservation() {
1586
1634
  }, {
1587
1635
  matcher: "*",
1588
1636
  command: `bash ${RUNTIME_ROOT}/hooks/workflow-guard.sh`
1589
- }, {
1590
- matcher: "*",
1591
- command: `bash ${RUNTIME_ROOT}/hooks/observe.sh pre`
1592
1637
  }],
1593
1638
  postToolUse: [{
1594
1639
  matcher: "*",
1595
1640
  command: `bash ${RUNTIME_ROOT}/hooks/context-monitor.sh`
1596
- }, {
1597
- matcher: "*",
1598
- command: `bash ${RUNTIME_ROOT}/hooks/observe.sh post`
1599
1641
  }],
1600
- stop: [
1601
- { command: `bash ${RUNTIME_ROOT}/hooks/summarize-observations.sh`, timeout: 15 },
1602
- { command: `bash ${RUNTIME_ROOT}/hooks/stop-checkpoint.sh`, timeout: 10 }
1603
- ]
1642
+ stop: [{ command: `bash ${RUNTIME_ROOT}/hooks/stop-checkpoint.sh`, timeout: 10 }]
1604
1643
  }
1605
1644
  }, null, 2);
1606
1645
  }
@@ -1624,9 +1663,6 @@ export function codexHooksJsonWithObservation() {
1624
1663
  }, {
1625
1664
  type: "command",
1626
1665
  command: `bash ${RUNTIME_ROOT}/hooks/workflow-guard.sh`
1627
- }, {
1628
- type: "command",
1629
- command: `bash ${RUNTIME_ROOT}/hooks/observe.sh pre`
1630
1666
  }]
1631
1667
  }],
1632
1668
  PostToolUse: [{
@@ -1634,24 +1670,14 @@ export function codexHooksJsonWithObservation() {
1634
1670
  hooks: [{
1635
1671
  type: "command",
1636
1672
  command: `bash ${RUNTIME_ROOT}/hooks/context-monitor.sh`
1637
- }, {
1638
- type: "command",
1639
- command: `bash ${RUNTIME_ROOT}/hooks/observe.sh post`
1640
1673
  }]
1641
1674
  }],
1642
1675
  Stop: [{
1643
- hooks: [
1644
- {
1645
- type: "command",
1646
- command: `bash ${RUNTIME_ROOT}/hooks/summarize-observations.sh`,
1647
- timeout: 15
1648
- },
1649
- {
1676
+ hooks: [{
1650
1677
  type: "command",
1651
1678
  command: `bash ${RUNTIME_ROOT}/hooks/stop-checkpoint.sh`,
1652
1679
  timeout: 10
1653
- }
1654
- ]
1680
+ }]
1655
1681
  }]
1656
1682
  }
1657
1683
  }, null, 2);
@@ -26,7 +26,7 @@ These are prompt-discipline guidelines that complement the real hooks cclaw gene
26
26
  When a new session begins in any harness:
27
27
 
28
28
  1. **Read flow state:** Load \`.cclaw/state/flow-state.json\` to find the current stage and completed stages.
29
- 2. **Load learnings:** Run \`tail -n 20 .cclaw/learnings.jsonl 2>/dev/null\` and surface the top 3 highest-confidence entries (see learnings skill for dedup/decay).
29
+ 2. **Load knowledge:** Review recent entries from \`.cclaw/knowledge.md\` and surface the most relevant rules/patterns.
30
30
  3. **Check for in-progress work:** If the last stage is incomplete, remind the user and offer to resume.
31
31
  4. **Load suggestion memory:** Read \`.cclaw/state/suggestion-memory.json\` and honor \`enabled=false\` opt-out.
32
32
  5. **Read AGENTS.md:** The cclaw block contains routing and rules — follow them.
@@ -35,7 +35,7 @@ When a new session begins in any harness:
35
35
 
36
36
  \`\`\`
37
37
  Cclaw flow state: [current stage] ([N] of 9 stages completed)
38
- Top learnings: [key1] (confidence N), [key2] (confidence N), [key3] (confidence N)
38
+ Knowledge highlights: [rule/pattern 1], [rule/pattern 2], [rule/pattern 3]
39
39
  Next action: /cc-[stage] to continue, or describe what you'd like to do.
40
40
  \`\`\`
41
41
 
@@ -45,7 +45,7 @@ Before ending a session or when context is full:
45
45
 
46
46
  1. **Verify no pending changes:** All modified files must be either committed or explicitly reverted.
47
47
  2. **Update flow state:** Mark the current stage as its actual status (DONE / DONE_WITH_CONCERNS / BLOCKED).
48
- 3. **Write learnings:** If any insight from this session would save 5+ minutes next time, append to \`.cclaw/learnings.jsonl\`.
48
+ 3. **Write knowledge:** If any non-obvious reusable insight appears, append to \`.cclaw/knowledge.md\` with type \`rule\`, \`pattern\`, or \`lesson\`.
49
49
  4. **Create checkpoint:** Write a brief status note to the current artifact or as a comment in flow-state.json.
50
50
 
51
51
  ### Stop conditions (agent must halt and report)
@@ -61,7 +61,7 @@ When resuming work after a break:
61
61
 
62
62
  1. Re-read \`.cclaw/state/flow-state.json\` (may have changed externally).
63
63
  2. Re-read the current stage's artifact to verify it matches your last checkpoint.
64
- 3. Re-load top learnings.
64
+ 3. Re-load recent knowledge entries.
65
65
  4. Continue from the last incomplete step — do not restart the stage.
66
66
 
67
67
  ## Proactive Suggestion Memory
@@ -121,16 +121,16 @@ When approaching context limits:
121
121
  - Ending a session with modified but uncommitted files
122
122
  - No flow state update after completing work
123
123
  - Restarting a stage from scratch instead of resuming from checkpoint
124
- - Ignoring learnings from prior sessions
124
+ - Ignoring knowledge from prior sessions
125
125
  `;
126
126
  }
127
127
  export function sessionHooksAgentsMdBlock() {
128
128
  return `### Session Guidelines
129
129
 
130
130
  Session boundary behavior (real hooks inject context automatically; guidelines cover manual steps):
131
- - **Start:** Hooks inject flow state + top learnings. Check for in-progress work, show status.
132
- - **Stop:** Hooks remind about checkpoint. Verify no pending changes, update flow state, write learnings.
133
- - **Resume:** Re-read state, verify artifact, re-load learnings, continue from last step.
131
+ - **Start:** Hooks inject flow state + knowledge snapshot. Check for in-progress work, show status.
132
+ - **Stop:** Hooks remind about checkpoint. Verify no pending changes, update flow state, append useful knowledge.
133
+ - **Resume:** Re-read state, verify artifact, re-load knowledge, continue from last step.
134
134
 
135
135
  Skill: \`.cclaw/skills/session/SKILL.md\`
136
136
  `;
@@ -2,10 +2,6 @@ import { RUNTIME_ROOT } from "../constants.js";
2
2
  import { stageExamples } from "./examples.js";
3
3
  import { selfImprovementBlock } from "./learnings.js";
4
4
  import { QUESTION_FORMAT_SPEC, ERROR_BUDGET_SPEC, stageAutoSubagentDispatch, stageSchema } from "./stage-schema.js";
5
- function artifactFileName(artifactPath) {
6
- const parts = artifactPath.split("/");
7
- return parts[parts.length - 1] ?? artifactPath;
8
- }
9
5
  function rationalizationTable(stage) {
10
6
  const schema = stageSchema(stage);
11
7
  return `| Rationalization | Reality |
@@ -75,20 +71,17 @@ function contextLoadingBlock(stage) {
75
71
  const trace = stageSchema(stage).crossStageTrace;
76
72
  const readLines = trace.readsFrom.length > 0
77
73
  ? trace.readsFrom
78
- .map((value) => {
79
- const fileName = artifactFileName(value);
80
- return `- Canonical: \`.cclaw/runs/<activeRunId>/artifacts/${fileName}\` (fallback: \`${value}\`)`;
81
- })
74
+ .map((value) => `- \`${value}\``)
82
75
  .join("\n")
83
76
  : "- (first stage — no upstream artifacts)";
84
77
  return `## Context Loading
85
78
 
86
79
  Before starting stage execution:
87
- 1. Read \`.cclaw/state/flow-state.json\` and capture \`activeRunId\`.
88
- 2. Resolve canonical run artifact root: \`.cclaw/runs/<activeRunId>/artifacts/\`.
80
+ 1. Read \`.cclaw/state/flow-state.json\`.
81
+ 2. Resolve active artifact root: \`.cclaw/artifacts/\`.
89
82
  3. Load upstream artifacts required by this stage:
90
83
  ${readLines}
91
- 4. If canonical run artifact is missing, fallback to the \`.cclaw/artifacts/\` mirror and record that fallback.
84
+ 4. Read \`.cclaw/knowledge.md\` and apply relevant entries before making decisions.
92
85
  `;
93
86
  }
94
87
  function whenNotToUseBlock(stage) {
@@ -113,7 +106,7 @@ function autoSubagentDispatchBlock(stage) {
113
106
  .join("\n");
114
107
  const mandatory = stageSchema(stage).mandatoryDelegations;
115
108
  const mandatoryList = mandatory.length > 0 ? mandatory.map((a) => `\`${a}\``).join(", ") : "(none — only proactive dispatch applies)";
116
- const delegationLogRel = `${RUNTIME_ROOT}/runs/<activeRunId>/delegation-log.json`;
109
+ const delegationLogRel = `${RUNTIME_ROOT}/state/delegation-log.json`;
117
110
  return `## Automatic Subagent Dispatch
118
111
 
119
112
  Machine-only work should be delegated to specialist agents automatically according to the matrix below.
@@ -174,7 +167,7 @@ When all required gates are satisfied and the artifact is written:
174
167
  1. **Update \`${RUNTIME_ROOT}/state/flow-state.json\`:**
175
168
  ${stateUpdate}
176
169
  - For each passed gate, add an entry to \`guardEvidence\`: \`"<gate_id>": "<artifact path or excerpt proving the gate>"\`. Do NOT leave \`guardEvidence\` empty.
177
- 2. **Persist artifact** at \`${RUNTIME_ROOT}/artifacts/${schema.artifactFile}\`. Do NOT manually copy into run directories; cclaw sync/runtime keeps \`${RUNTIME_ROOT}/runs/<activeRunId>/artifacts/\` aligned.
170
+ 2. **Persist artifact** at \`${RUNTIME_ROOT}/artifacts/${schema.artifactFile}\`. Do NOT manually copy into \`${RUNTIME_ROOT}/runs/\`; archival is handled by \`cclaw archive\`.
178
171
  ${nextAction}
179
172
 
180
173
  **STOP.** Do not load the next stage skill yourself. The user will run \`/cc-next\` when ready (same session or new session).
@@ -231,7 +224,7 @@ function progressiveDisclosureBlock(stage) {
231
224
  - Primary stage procedure (this file): \`.cclaw/skills/${schema.skillFolder}/SKILL.md\`
232
225
  - Orchestrator contract (gate language and handoff): \`.cclaw/commands/${stage}.md\`
233
226
  - Artifact structure baseline: \`.cclaw/templates/${schema.artifactFile}\`
234
- - Runtime state truth source: \`.cclaw/state/flow-state.json\` + \`.cclaw/runs/<activeRunId>/\`
227
+ - Runtime state truth source: \`.cclaw/state/flow-state.json\` + \`.cclaw/artifacts/\` + \`.cclaw/knowledge.md\`
235
228
 
236
229
  ### See also
237
230
  - Meta routing and activation rules: \`.cclaw/skills/using-cclaw/SKILL.md\`
@@ -287,7 +280,7 @@ function quickStartBlock(stage) {
287
280
 
288
281
  > **Even if you read nothing else, do these 3 things:**
289
282
  > 1. Obey the HARD-GATE below — violating it invalidates the entire stage.
290
- > 2. Complete every checklist step in order and write the artifact to \`.cclaw/artifacts/${schema.artifactFile}\`. Run snapshots in \`.cclaw/runs/<activeRunId>/artifacts/\` are synchronized by cclaw runtime.
283
+ > 2. Complete every checklist step in order and write the artifact to \`.cclaw/artifacts/${schema.artifactFile}\`.
291
284
  > 3. Do not claim completion without satisfying gates: ${topGates}${schema.requiredGates.length > 3 ? ` (+${schema.requiredGates.length - 3} more)` : ""}.
292
285
  >
293
286
  > **After this stage:** update \`flow-state.json\` and tell the user to run \`/cc-next\`.
@@ -385,7 +378,7 @@ ${progressiveDisclosureBlock(stage)}
385
378
  ${selfImprovementBlock(stage)}
386
379
  ## Handoff
387
380
  - Next command: \`/cc-next\` (loads whatever stage is current in flow-state)
388
- - Required artifact: \`.cclaw/artifacts/${schema.artifactFile}\` (run snapshot at \`.cclaw/runs/<activeRunId>/artifacts/${schema.artifactFile}\`, synchronized by cclaw runtime)
381
+ - Required artifact: \`.cclaw/artifacts/${schema.artifactFile}\`
389
382
  - Stage stays blocked if any required gate is unsatisfied
390
383
  `;
391
384
  }