@wazir-dev/cli 1.2.0 → 1.4.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.
Files changed (161) hide show
  1. package/CHANGELOG.md +54 -44
  2. package/README.md +13 -13
  3. package/assets/demo.cast +47 -0
  4. package/assets/demo.gif +0 -0
  5. package/docs/anti-patterns/AP-23-skipping-enabled-workflows.md +28 -0
  6. package/docs/anti-patterns/AP-24-clarifier-deciding-scope.md +34 -0
  7. package/docs/concepts/architecture.md +1 -1
  8. package/docs/concepts/why-wazir.md +1 -1
  9. package/docs/readmes/INDEX.md +1 -1
  10. package/docs/readmes/features/expertise/README.md +1 -1
  11. package/docs/readmes/features/hooks/pre-compact-summary.md +1 -1
  12. package/docs/reference/hooks.md +1 -0
  13. package/docs/reference/launch-checklist.md +3 -3
  14. package/docs/reference/review-loop-pattern.md +3 -2
  15. package/docs/reference/skill-tiers.md +2 -2
  16. package/docs/research/2026-03-20-agents/a18fb002157904af5.txt +187 -0
  17. package/docs/research/2026-03-20-agents/a1d0ac79ac2f11e6f.txt +2 -0
  18. package/docs/research/2026-03-20-agents/a324079de037abd7c.txt +198 -0
  19. package/docs/research/2026-03-20-agents/a357586bccfafb0e5.txt +256 -0
  20. package/docs/research/2026-03-20-agents/a4365394e4d753105.txt +137 -0
  21. package/docs/research/2026-03-20-agents/a492af28bc52d3613.txt +136 -0
  22. package/docs/research/2026-03-20-agents/a4984db0b6a8eee07.txt +124 -0
  23. package/docs/research/2026-03-20-agents/a5b30e59d34bbb062.txt +214 -0
  24. package/docs/research/2026-03-20-agents/a5cf7829dab911586.txt +165 -0
  25. package/docs/research/2026-03-20-agents/a607157c30dd97c9e.txt +96 -0
  26. package/docs/research/2026-03-20-agents/a60b68b1e19d1e16b.txt +115 -0
  27. package/docs/research/2026-03-20-agents/a722af01c5594aba0.txt +166 -0
  28. package/docs/research/2026-03-20-agents/a787bdc516faa5829.txt +181 -0
  29. package/docs/research/2026-03-20-agents/a7c46d1bba1056ed2.txt +132 -0
  30. package/docs/research/2026-03-20-agents/a7e5abbab2b281a0d.txt +100 -0
  31. package/docs/research/2026-03-20-agents/a8dbadc66cd0d7d5a.txt +95 -0
  32. package/docs/research/2026-03-20-agents/a904d9f45d6b86a6d.txt +75 -0
  33. package/docs/research/2026-03-20-agents/a927659a942ee7f60.txt +102 -0
  34. package/docs/research/2026-03-20-agents/a962cb569191f7583.txt +125 -0
  35. package/docs/research/2026-03-20-agents/aab6decea538aac41.txt +148 -0
  36. package/docs/research/2026-03-20-agents/abd58b853dd938a1b.txt +295 -0
  37. package/docs/research/2026-03-20-agents/ac009da573eff7f65.txt +100 -0
  38. package/docs/research/2026-03-20-agents/ac1bc783364405e5f.txt +190 -0
  39. package/docs/research/2026-03-20-agents/aca5e2b57fde152a0.txt +132 -0
  40. package/docs/research/2026-03-20-agents/ad849b8c0a7e95b8b.txt +176 -0
  41. package/docs/research/2026-03-20-agents/adc2b12a4da32c962.txt +258 -0
  42. package/docs/research/2026-03-20-agents/af97caaaa9a80e4cb.txt +146 -0
  43. package/docs/research/2026-03-20-agents/afc5faceee368b3ca.txt +111 -0
  44. package/docs/research/2026-03-20-agents/afdb282d866e3c1e4.txt +164 -0
  45. package/docs/research/2026-03-20-agents/afe9d1f61c02b1e8d.txt +299 -0
  46. package/docs/research/2026-03-20-agents/b4hmkwril.txt +1856 -0
  47. package/docs/research/2026-03-20-agents/b80ptk89g.txt +1856 -0
  48. package/docs/research/2026-03-20-agents/bf54s1jss.txt +1150 -0
  49. package/docs/research/2026-03-20-agents/bhd6kq2kx.txt +1856 -0
  50. package/docs/research/2026-03-20-agents/bmb2fodyr.txt +988 -0
  51. package/docs/research/2026-03-20-agents/bmmsrij8i.txt +826 -0
  52. package/docs/research/2026-03-20-agents/bn4t2ywpu.txt +2175 -0
  53. package/docs/research/2026-03-20-agents/bu22t9f1z.txt +0 -0
  54. package/docs/research/2026-03-20-agents/bwvl98v2p.txt +738 -0
  55. package/docs/research/2026-03-20-agents/psych-a3697a7fd06eb64fd.txt +135 -0
  56. package/docs/research/2026-03-20-agents/psych-a37776fabc870feae.txt +123 -0
  57. package/docs/research/2026-03-20-agents/psych-a5b1fe05c0589efaf.txt +2 -0
  58. package/docs/research/2026-03-20-agents/psych-a95c15b1f29424435.txt +76 -0
  59. package/docs/research/2026-03-20-agents/psych-a9c26f4d9172dde7c.txt +2 -0
  60. package/docs/research/2026-03-20-agents/psych-aa19c69f0ca2c5ad3.txt +2 -0
  61. package/docs/research/2026-03-20-agents/psych-aa4e4cb70e1be5ecb.txt +95 -0
  62. package/docs/research/2026-03-20-agents/psych-ab5b302f26a554663.txt +102 -0
  63. package/docs/research/2026-03-20-deep-research-complete.md +101 -0
  64. package/docs/research/2026-03-20-deep-research-status.md +38 -0
  65. package/docs/research/2026-03-20-enforcement-research.md +107 -0
  66. package/expertise/antipatterns/process/ai-coding-antipatterns.md +117 -0
  67. package/expertise/composition-map.yaml +27 -8
  68. package/expertise/digests/reviewer/ai-coding-digest.md +83 -0
  69. package/expertise/digests/reviewer/architectural-thinking-digest.md +63 -0
  70. package/expertise/digests/reviewer/architecture-antipatterns-digest.md +49 -0
  71. package/expertise/digests/reviewer/code-smells-digest.md +53 -0
  72. package/expertise/digests/reviewer/coupling-cohesion-digest.md +54 -0
  73. package/expertise/digests/reviewer/ddd-digest.md +60 -0
  74. package/expertise/digests/reviewer/dependency-risk-digest.md +40 -0
  75. package/expertise/digests/reviewer/error-handling-digest.md +55 -0
  76. package/expertise/digests/reviewer/review-methodology-digest.md +49 -0
  77. package/exports/hosts/claude/.claude/commands/learn.md +61 -8
  78. package/exports/hosts/claude/.claude/commands/plan-review.md +3 -1
  79. package/exports/hosts/claude/.claude/commands/verify.md +30 -1
  80. package/exports/hosts/claude/.claude/settings.json +7 -6
  81. package/exports/hosts/claude/export.manifest.json +8 -5
  82. package/exports/hosts/claude/host-package.json +3 -0
  83. package/exports/hosts/codex/export.manifest.json +8 -5
  84. package/exports/hosts/codex/host-package.json +3 -0
  85. package/exports/hosts/cursor/.cursor/hooks.json +6 -6
  86. package/exports/hosts/cursor/export.manifest.json +8 -5
  87. package/exports/hosts/cursor/host-package.json +3 -0
  88. package/exports/hosts/gemini/export.manifest.json +8 -5
  89. package/exports/hosts/gemini/host-package.json +3 -0
  90. package/hooks/definitions/pretooluse_dispatcher.yaml +26 -0
  91. package/hooks/definitions/pretooluse_pipeline_guard.yaml +22 -0
  92. package/hooks/definitions/stop_pipeline_gate.yaml +22 -0
  93. package/hooks/hooks.json +7 -6
  94. package/hooks/pretooluse-dispatcher +84 -0
  95. package/hooks/pretooluse-pipeline-guard +9 -0
  96. package/hooks/stop-pipeline-gate +9 -0
  97. package/llms-full.txt +48 -18
  98. package/package.json +2 -3
  99. package/schemas/decision.schema.json +15 -0
  100. package/schemas/hook.schema.json +4 -1
  101. package/schemas/phase-report.schema.json +9 -0
  102. package/skills/TEMPLATE-3-ZONE.md +160 -0
  103. package/skills/brainstorming/SKILL.md +137 -21
  104. package/skills/clarifier/SKILL.md +364 -53
  105. package/skills/claude-cli/SKILL.md +91 -12
  106. package/skills/codex-cli/SKILL.md +91 -12
  107. package/skills/debugging/SKILL.md +133 -38
  108. package/skills/design/SKILL.md +173 -37
  109. package/skills/dispatching-parallel-agents/SKILL.md +129 -31
  110. package/skills/executing-plans/SKILL.md +113 -25
  111. package/skills/executor/SKILL.md +252 -21
  112. package/skills/finishing-a-development-branch/SKILL.md +107 -18
  113. package/skills/gemini-cli/SKILL.md +91 -12
  114. package/skills/humanize/SKILL.md +92 -13
  115. package/skills/init-pipeline/SKILL.md +90 -18
  116. package/skills/prepare-next/SKILL.md +93 -24
  117. package/skills/receiving-code-review/SKILL.md +90 -16
  118. package/skills/requesting-code-review/SKILL.md +100 -24
  119. package/skills/requesting-code-review/code-reviewer.md +29 -17
  120. package/skills/reviewer/SKILL.md +270 -57
  121. package/skills/run-audit/SKILL.md +92 -15
  122. package/skills/scan-project/SKILL.md +93 -14
  123. package/skills/self-audit/SKILL.md +133 -39
  124. package/skills/skill-research/SKILL.md +275 -0
  125. package/skills/subagent-driven-development/SKILL.md +129 -30
  126. package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +30 -2
  127. package/skills/subagent-driven-development/implementer-prompt.md +40 -27
  128. package/skills/subagent-driven-development/spec-reviewer-prompt.md +25 -12
  129. package/skills/tdd/SKILL.md +125 -20
  130. package/skills/using-git-worktrees/SKILL.md +118 -28
  131. package/skills/using-skills/SKILL.md +116 -29
  132. package/skills/verification/SKILL.md +160 -17
  133. package/skills/wazir/SKILL.md +750 -120
  134. package/skills/writing-plans/SKILL.md +134 -28
  135. package/skills/writing-skills/SKILL.md +91 -13
  136. package/skills/writing-skills/anthropic-best-practices.md +104 -64
  137. package/skills/writing-skills/persuasion-principles.md +100 -34
  138. package/tooling/src/capture/command.js +46 -2
  139. package/tooling/src/capture/decision.js +40 -0
  140. package/tooling/src/capture/store.js +33 -0
  141. package/tooling/src/capture/user-input.js +66 -0
  142. package/tooling/src/checks/security-sensitivity.js +69 -0
  143. package/tooling/src/cli.js +28 -26
  144. package/tooling/src/config/depth-table.js +60 -0
  145. package/tooling/src/export/compiler.js +7 -8
  146. package/tooling/src/guards/guardrail-functions.js +131 -0
  147. package/tooling/src/guards/phase-prerequisite-guard.js +97 -3
  148. package/tooling/src/hooks/pretooluse-dispatcher.js +300 -0
  149. package/tooling/src/hooks/pretooluse-pipeline-guard.js +141 -0
  150. package/tooling/src/hooks/stop-pipeline-gate.js +92 -0
  151. package/tooling/src/init/auto-detect.js +0 -2
  152. package/tooling/src/init/command.js +3 -95
  153. package/tooling/src/learn/pipeline.js +177 -0
  154. package/tooling/src/state/db.js +251 -2
  155. package/tooling/src/state/pipeline-state.js +262 -0
  156. package/tooling/src/status/command.js +6 -1
  157. package/tooling/src/verify/proof-collector.js +299 -0
  158. package/wazir.manifest.yaml +3 -0
  159. package/workflows/learn.md +61 -8
  160. package/workflows/plan-review.md +3 -1
  161. package/workflows/verify.md +30 -1
@@ -2,49 +2,115 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- LLMs respond to the same persuasion principles as humans. Understanding this psychology helps you design more effective skills - not to manipulate, but to ensure critical practices are followed even under pressure.
5
+ LLMs exhibit statistical compliance biases that can be leveraged to improve instruction following. This is not psychology applied to machines it is empirical prompt engineering grounded in attention mechanics, training distribution effects, and measured compliance rates.
6
6
 
7
- **Research foundation:** Meincke et al. (2025) tested 7 persuasion principles with N=28,000 AI conversations. Persuasion techniques more than doubled compliance rates (33% -> 72%, p < .001).
7
+ **Research foundation:** Meincke et al. (2025) tested 7 persuasion principles with N=28,000 AI conversations. Commitment priming approached 100% compliance. Positive directive framing consistently outperformed negative framing. Authority framing lifted compliance by ~40pp.
8
8
 
9
- ## The Seven Principles
9
+ ## Principles Ranked by Evidence Strength
10
10
 
11
- ### 1. Authority
12
- - Imperative language: "YOU MUST", "Never", "Always"
13
- - Non-negotiable framing: "No exceptions"
14
- - Eliminates decision fatigue and rationalization
11
+ ### Tier 1: Strong Evidence, Large Effect
15
12
 
16
- ### 2. Commitment
17
- - Require announcements: "Announce skill usage"
18
- - Force explicit choices: "Choose A, B, or C"
19
- - Use tracking: TodoWrite for checklists
13
+ **1. Commitment Priming (highest impact)**
14
+ - Have the model announce its plan before executing
15
+ - Autoregressive consistency: once the model generates "I will do X", it is statistically more likely to do X
16
+ - Implementation: "Before executing, state which steps you will perform"
17
+ - Measured: near-100% compliance after self-commitment in Meincke et al.
20
18
 
21
- ### 3. Scarcity
22
- - Time-bound requirements: "Before proceeding"
23
- - Sequential dependencies: "Immediately after X"
24
- - Prevents procrastination
19
+ **2. Positive Directive Framing**
20
+ - "Always do X" consistently outperforms "Never do Y"
21
+ - Token generation selects what to produce, not what to avoid
22
+ - Negative instructions ("do NOT mention X") can paradoxically increase mentions
23
+ - Use negative framing ONLY for critical guardrails with a positive alternative: "Do NOT skip review. Instead, run review quickly."
25
24
 
26
- ### 4. Social Proof
27
- - Universal patterns: "Every time", "Always"
28
- - Failure modes: "X without Y = failure"
29
- - Establishes norms
25
+ **3. Structural Isolation (XML Tags)**
26
+ - Claude is fine-tuned to attend to XML tag boundaries
27
+ - Tags create attention-weight spikes and trust boundaries
28
+ - Use `<rules>`, `<instructions>`, `<output_format>` for hard boundaries
29
+ - Hybrid XML+markdown is optimal: XML for structure, markdown for formatting within sections
30
30
 
31
- ### 5. Unity
32
- - Collaborative language: "our codebase", "we're colleagues"
33
- - Shared goals: "we both want quality"
31
+ **4. Positional Privilege (Primacy + Recency)**
32
+ - First ~500 tokens: ~95% compliance (primacy zone)
33
+ - Last ~500 tokens: ~85% compliance (recency zone)
34
+ - Middle of long context: ~65-75% compliance (lost in the middle)
35
+ - Critical rules go at beginning AND end. Never only in the middle.
34
36
 
35
- ### 6. Reciprocity
36
- - Obligation to return favors
37
- - "I'll give you full context, you give me honest assessment"
37
+ ### Tier 2: Strong Evidence, Moderate Effect
38
38
 
39
- ### 7. Liking
40
- - Avoid in discipline-enforcing skills
41
- - Use sparingly for collaborative skills
39
+ **5. Authority / Role Assignment**
40
+ - "You are a senior security auditor responsible for..." activates domain-specific patterns
41
+ - +40pp lift in Meincke et al.
42
+ - Expert personas produce more accurate, more disciplined output
43
+
44
+ **6. Consequence Framing**
45
+ - "Skipping this step causes silent regressions that waste hours of debugging"
46
+ - Provides reasoning context for why compliance matters
47
+ - More effective than abstract rules ("always follow the process")
48
+
49
+ **7. Implementation Intentions (IF-THEN rules)**
50
+ - "IF user says skip → THEN say 'Running it quickly' and execute"
51
+ - Pre-decides the response — no judgment call needed at runtime
52
+ - d=0.65 across 94 psychology studies (Gollwitzer). Maps directly to LLM prompt design.
53
+ - Single most actionable technique for skill authors
54
+
55
+ **8. Redundant Reinforcement**
56
+ - State the rule, show an example, reference it in the output format, add a constraint tag
57
+ - Multiple encoding paths survive when any single one fails
58
+ - Paraphrased repetition (2-3x) outperforms verbatim repetition
59
+
60
+ ### Tier 3: Context-Dependent Effect
61
+
62
+ **9. Social Proof**
63
+ - "Standard practice is..." or "All production systems follow this pattern"
64
+ - Effective when baseline compliance is already moderate (+6pp)
65
+
66
+ **10. Urgency / Scarcity**
67
+ - "This must be done correctly the first time; there is no retry"
68
+ - Increases both compliance and output variance — use sparingly
69
+
70
+ **11. Moral / Ethical Framing**
71
+ - "Omitting this would produce misleading output"
72
+ - Effective for Claude specifically due to Constitutional AI training
73
+ - Frame positively (good outcome of compliance) not negatively
74
+
75
+ ## Anti-Patterns
76
+
77
+ | Pattern | Problem |
78
+ |---------|---------|
79
+ | Negative instructions without alternatives | "Don't do X" fails — model must activate X to evaluate constraint |
80
+ | Instruction overload (>12 constraints) | Steep compliance drop after ~12 accumulated constraints |
81
+ | Threats without specifics | "You will be punished" increases variance without improving median |
82
+ | Reciprocity framing | "I helped you, now help me" — weakest principle, only +11pp |
83
+ | Relying solely on alignment | 80% of enterprises reported injection incidents. Structural defenses needed. |
42
84
 
43
85
  ## Principle Combinations by Skill Type
44
86
 
45
- | Skill Type | Use | Avoid |
46
- |------------|-----|-------|
47
- | Discipline-enforcing | Authority + Commitment + Social Proof | Liking, Reciprocity |
48
- | Guidance/technique | Moderate Authority + Unity | Heavy authority |
49
- | Collaborative | Unity + Commitment | Authority, Liking |
50
- | Reference | Clarity only | All persuasion |
87
+ | Skill Type | Primary Techniques | Avoid |
88
+ |------------|-------------------|-------|
89
+ | Discipline-enforcing (TDD, verification) | Commitment + Implementation Intentions + Positional Privilege + Authority | Liking, Reciprocity |
90
+ | Process-governing (clarifier, executor) | Commitment + Consequence Framing + Structural Isolation | Heavy emotional framing |
91
+ | Collaborative (brainstorming, design) | Moderate Authority + Implementation Intentions | Over-constraining creative steps |
92
+ | Reference (docs, guides) | Structural Isolation + Positional Privilege | All persuasion — clarity only |
93
+
94
+ ## The 3-Zone Architecture
95
+
96
+ Apply these principles through the 3-zone skill layout:
97
+
98
+ - **Zone 1 (Primacy):** Identity + Iron Laws + Priority Stack — leverages positional privilege + authority + commitment
99
+ - **Zone 2 (Process):** IF-THEN rules + decision tables + gate functions — leverages implementation intentions + structural isolation
100
+ - **Zone 3 (Recency):** Restated laws + Red Flags + meta-instruction — leverages recency + redundant reinforcement + consequence framing
101
+
102
+ ## Temporal Testing Advisory
103
+
104
+ Prompt engineering techniques lose effectiveness as models improve. Re-test skill compliance every major model version. Include a "last verified" date on persuasion-dependent skills.
105
+
106
+ **Last verified:** Claude Opus 4.6, March 2026
107
+
108
+ ## Sources
109
+
110
+ - Meincke et al. (2025). "Call Me A Jerk: Persuading AI to Comply" (N=28,000, SSRN)
111
+ - Liu et al. (2024). "Lost in the Middle" (TACL, arXiv:2307.03172)
112
+ - Wallace et al. (2024). "The Instruction Hierarchy" (OpenAI, arXiv:2404.13208)
113
+ - Gollwitzer (1999). Implementation Intentions (d=0.65, 94 studies meta-analysis)
114
+ - EmotionPrompt (2023). Emotional framing effects (arXiv:2307.11760)
115
+ - Zhou et al. (2023). IFEval benchmark (arXiv:2311.07911)
116
+ - Anthropic (2024). Claude Model Spec — instruction hierarchy documentation
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
 
4
4
  import { parseCommandOptions, parsePositiveInteger } from '../command-options.js';
5
5
  import { readYamlFile } from '../loaders.js';
6
+ import { validateRunCompletion } from '../guards/phase-prerequisite-guard.js';
6
7
  import { findProjectRoot } from '../project-root.js';
7
8
  import { resolveStateRoot } from '../state-root.js';
8
9
  import {
@@ -18,6 +19,7 @@ import {
18
19
  } from './store.js';
19
20
  import { readRunConfig, getPhaseLoopCap } from './run-config.js';
20
21
  import { readUsage, generateReport, initUsage, recordCaptureSavings, recordPhaseUsage } from './usage.js';
22
+ import { appendDecision } from './decision.js';
21
23
  import { evaluateLoopCapGuard } from '../guards/loop-cap-guard.js';
22
24
  import { evaluatePhasePrerequisiteGuard } from '../guards/phase-prerequisite-guard.js';
23
25
 
@@ -57,7 +59,7 @@ function resolveCaptureContext(parsed, context = {}) {
57
59
  const projectRoot = findProjectRoot(context.cwd ?? process.cwd());
58
60
  const manifest = readYamlFile(path.join(projectRoot, 'wazir.manifest.yaml'));
59
61
  const { options } = parseCommandOptions(parsed.args, {
60
- boolean: ['json'],
62
+ boolean: ['json', 'complete'],
61
63
  string: [
62
64
  'run',
63
65
  'phase',
@@ -72,6 +74,8 @@ function resolveCaptureContext(parsed, context = {}) {
72
74
  'command',
73
75
  'exit-code',
74
76
  'task-id',
77
+ 'decision',
78
+ 'reason',
75
79
  ],
76
80
  });
77
81
  const stateRoot = resolveStateRoot(projectRoot, manifest, {
@@ -326,6 +330,21 @@ function handleSummary(parsed, context = {}) {
326
330
 
327
331
  const runPaths = getRunPaths(stateRoot, options.run);
328
332
  const status = readStatus(runPaths);
333
+
334
+ // Enforce workflow completion before allowing summary to finalize
335
+ if (options.complete) {
336
+ const projectRoot = findProjectRoot();
337
+ const manifestPath = path.join(projectRoot, 'wazir.manifest.yaml');
338
+ const result = validateRunCompletion(runPaths.runRoot, manifestPath);
339
+ if (!result.complete) {
340
+ const msg = `Run incomplete: ${result.missing.length} workflow(s) not finished: ${result.missing.join(', ')}`;
341
+ if (options.json) {
342
+ return { exitCode: 1, stdout: JSON.stringify({ run_id: options.run, complete: false, missing_workflows: result.missing, error: msg }, null, 2) + '\n' };
343
+ }
344
+ return { exitCode: 1, stderr: msg + '\n' };
345
+ }
346
+ }
347
+
329
348
  const eventName = options.event ?? 'pre_compact_summary';
330
349
  const summaryContent = readInput();
331
350
  const summaryPath = writeSummary(runPaths, summaryContent);
@@ -372,6 +391,29 @@ function handleUsage(parsed, context = {}) {
372
391
  };
373
392
  }
374
393
 
394
+ function handleDecision(parsed, context = {}) {
395
+ const { stateRoot, options } = resolveCaptureContext(parsed, context);
396
+
397
+ requireOption(options, 'run', 'Usage: wazir capture decision --run <id> --phase <phase> --decision "<text>" --reason "<text>" [--task-id <id>] [--state-root <path>] [--json]');
398
+ requireOption(options, 'phase', 'Usage: wazir capture decision --run <id> --phase <phase> --decision "<text>" --reason "<text>" [--task-id <id>] [--state-root <path>] [--json]');
399
+ requireOption(options, 'decision', 'Usage: wazir capture decision --run <id> --phase <phase> --decision "<text>" --reason "<text>" [--task-id <id>] [--state-root <path>] [--json]');
400
+ requireOption(options, 'reason', 'Usage: wazir capture decision --run <id> --phase <phase> --decision "<text>" --reason "<text>" [--task-id <id>] [--state-root <path>] [--json]');
401
+
402
+ const runPaths = getRunPaths(stateRoot, options.run);
403
+ appendDecision(runPaths, {
404
+ phase: options.phase,
405
+ decision: options.decision,
406
+ reason: options.reason,
407
+ task_id: options.taskId,
408
+ });
409
+
410
+ return formatResult({
411
+ run_id: options.run,
412
+ event: 'decision',
413
+ decisions_path: runPaths.decisionsPath,
414
+ }, { json: options.json });
415
+ }
416
+
375
417
  function handleLoopCheck(parsed, context = {}) {
376
418
  const { stateRoot, options } = resolveCaptureContext(parsed, context);
377
419
 
@@ -470,10 +512,12 @@ export function runCaptureCommand(parsed, context = {}) {
470
512
  return handleUsage(parsed, context);
471
513
  case 'loop-check':
472
514
  return handleLoopCheck(parsed, context);
515
+ case 'decision':
516
+ return handleDecision(parsed, context);
473
517
  default:
474
518
  return {
475
519
  exitCode: 1,
476
- stderr: 'Usage: wazir capture <init|event|route|output|summary|usage|loop-check> ...\n',
520
+ stderr: 'Usage: wazir capture <init|event|route|output|summary|usage|loop-check|decision> ...\n',
477
521
  };
478
522
  }
479
523
  } catch (error) {
@@ -0,0 +1,40 @@
1
+ import fs from 'node:fs';
2
+
3
+ /**
4
+ * Append a decision entry to the run's NDJSON log.
5
+ *
6
+ * @param {object} runPaths - Run paths object (must include decisionsPath)
7
+ * @param {object} entry - { phase, decision, reason, task_id? }
8
+ */
9
+ export function appendDecision(runPaths, { phase, decision, reason, task_id }) {
10
+ const record = {
11
+ timestamp: new Date().toISOString(),
12
+ phase: phase ?? 'unknown',
13
+ decision: decision ?? '',
14
+ reason: reason ?? '',
15
+ };
16
+ if (task_id) {
17
+ record.task_id = task_id;
18
+ }
19
+ fs.appendFileSync(runPaths.decisionsPath, JSON.stringify(record) + '\n');
20
+ return runPaths.decisionsPath;
21
+ }
22
+
23
+ /**
24
+ * Read all entries from a run's decisions log.
25
+ *
26
+ * @param {object} runPaths - Run paths object (must include decisionsPath)
27
+ * @returns {Array<object>}
28
+ */
29
+ export function readDecisions(runPaths) {
30
+ if (!fs.existsSync(runPaths.decisionsPath)) return [];
31
+
32
+ return fs.readFileSync(runPaths.decisionsPath, 'utf8')
33
+ .split('\n')
34
+ .filter(line => line.trim())
35
+ .map(line => {
36
+ try { return JSON.parse(line); }
37
+ catch { return null; }
38
+ })
39
+ .filter(Boolean);
40
+ }
@@ -19,6 +19,7 @@ export function getRunPaths(stateRoot, runId) {
19
19
  capturesDir,
20
20
  statusPath: path.join(runRoot, 'status.json'),
21
21
  eventsPath: path.join(runRoot, 'events.ndjson'),
22
+ decisionsPath: path.join(runRoot, 'decisions.ndjson'),
22
23
  summaryPath: path.join(runRoot, 'summary.md'),
23
24
  usagePath: path.join(runRoot, 'usage.json'),
24
25
  };
@@ -116,6 +117,38 @@ export function readPhaseExitEvents(runPaths) {
116
117
  return completedPhases;
117
118
  }
118
119
 
120
+ /**
121
+ * Read phase exit events with full two-level detail (parent_phase + workflow).
122
+ */
123
+ export function readPhaseExitEventsDetailed(runPaths) {
124
+ if (!fs.existsSync(runPaths.eventsPath)) {
125
+ return [];
126
+ }
127
+
128
+ const content = fs.readFileSync(runPaths.eventsPath, 'utf8');
129
+ const events = [];
130
+
131
+ for (const line of content.split('\n')) {
132
+ const trimmed = line.trim();
133
+ if (!trimmed) continue;
134
+ try {
135
+ const event = JSON.parse(trimmed);
136
+ if (event.event === 'phase_exit' && event.phase) {
137
+ events.push({
138
+ phase: event.phase,
139
+ parent_phase: event.parent_phase ?? event.phase,
140
+ workflow: event.workflow ?? event.phase,
141
+ status: event.status,
142
+ });
143
+ }
144
+ } catch {
145
+ // Skip malformed lines
146
+ }
147
+ }
148
+
149
+ return events;
150
+ }
151
+
119
152
  export function writeSummary(runPaths, content) {
120
153
  ensureRunDirectories(runPaths);
121
154
  fs.writeFileSync(runPaths.summaryPath, content);
@@ -0,0 +1,66 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ /**
5
+ * Append a user input entry to the run's NDJSON log.
6
+ *
7
+ * @param {string} runDir - Absolute path to the run directory
8
+ * @param {object} entry - { phase, type, content, context }
9
+ * type: 'instruction' | 'approval' | 'correction' | 'rejection' | 'redirect'
10
+ */
11
+ export function captureUserInput(runDir, { phase, type, content, context }) {
12
+ const logPath = path.join(runDir, 'user-input-log.ndjson');
13
+ const record = {
14
+ timestamp: new Date().toISOString(),
15
+ phase: phase ?? 'unknown',
16
+ type: type ?? 'instruction',
17
+ content: content ?? '',
18
+ context: context ?? '',
19
+ };
20
+ fs.appendFileSync(logPath, JSON.stringify(record) + '\n');
21
+ return logPath;
22
+ }
23
+
24
+ /**
25
+ * Read all entries from a run's user input log.
26
+ */
27
+ export function readUserInputLog(runDir) {
28
+ const logPath = path.join(runDir, 'user-input-log.ndjson');
29
+ if (!fs.existsSync(logPath)) return [];
30
+
31
+ return fs.readFileSync(logPath, 'utf8')
32
+ .split('\n')
33
+ .filter(line => line.trim())
34
+ .map(line => {
35
+ try { return JSON.parse(line); }
36
+ catch { return null; }
37
+ })
38
+ .filter(Boolean);
39
+ }
40
+
41
+ /**
42
+ * Prune old user-input-log.ndjson files, keeping the most recent `keep` runs.
43
+ *
44
+ * @param {string} stateRoot - Absolute path to the state root (e.g. ~/.wazir/projects/foo)
45
+ * @param {number} keep - Number of recent runs to keep (default 10)
46
+ */
47
+ export function pruneOldInputLogs(stateRoot, keep = 10) {
48
+ const runsDir = path.join(stateRoot, 'runs');
49
+ if (!fs.existsSync(runsDir)) return { pruned: 0 };
50
+
51
+ const entries = fs.readdirSync(runsDir)
52
+ .filter(name => name.startsWith('run-') && fs.statSync(path.join(runsDir, name)).isDirectory())
53
+ .sort()
54
+ .reverse();
55
+
56
+ let pruned = 0;
57
+ for (let i = keep; i < entries.length; i++) {
58
+ const logPath = path.join(runsDir, entries[i], 'user-input-log.ndjson');
59
+ if (fs.existsSync(logPath)) {
60
+ fs.unlinkSync(logPath);
61
+ pruned++;
62
+ }
63
+ }
64
+
65
+ return { pruned };
66
+ }
@@ -0,0 +1,69 @@
1
+ const SECURITY_PATTERNS = [
2
+ 'auth', 'password', 'passwd', 'token', 'query', 'sql',
3
+ 'fetch', 'upload', 'secret', 'env', 'api[._-]?key',
4
+ 'session', 'cookie', 'cors', 'csrf', 'jwt', 'oauth',
5
+ 'encrypt', 'decrypt', 'hash', 'salt', 'credential',
6
+ 'private[._-]?key', 'access[._-]?token', 'refresh[._-]?token',
7
+ ];
8
+
9
+ const PATTERN_REGEX = new RegExp(
10
+ `\\b(${SECURITY_PATTERNS.join('|')})\\b`,
11
+ 'gi'
12
+ );
13
+
14
+ /**
15
+ * Scan diff text for security-sensitive patterns.
16
+ * Returns which patterns were found and in which files.
17
+ */
18
+ export function detectSecurityPatterns(diffText) {
19
+ if (!diffText || typeof diffText !== 'string') {
20
+ return { triggered: false, patterns: [], files: [] };
21
+ }
22
+
23
+ const matchedPatterns = new Set();
24
+ const matchedFiles = new Set();
25
+ let currentFile = null;
26
+
27
+ for (const line of diffText.split('\n')) {
28
+ // Track current file from diff headers
29
+ const fileMatch = line.match(/^(?:diff --git a\/\S+ b\/|[+]{3} b\/)(.+)/);
30
+ if (fileMatch) {
31
+ currentFile = fileMatch[1];
32
+ continue;
33
+ }
34
+
35
+ // Only scan added/modified lines (starting with +, not +++)
36
+ if (!line.startsWith('+') || line.startsWith('+++')) continue;
37
+
38
+ const lineMatches = line.match(PATTERN_REGEX);
39
+ if (lineMatches) {
40
+ for (const m of lineMatches) {
41
+ matchedPatterns.add(m.toLowerCase());
42
+ }
43
+ if (currentFile) {
44
+ matchedFiles.add(currentFile);
45
+ }
46
+ }
47
+ }
48
+
49
+ const patterns = [...matchedPatterns].sort();
50
+ const files = [...matchedFiles].sort();
51
+
52
+ return {
53
+ triggered: patterns.length > 0,
54
+ patterns,
55
+ files,
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Security review dimensions to add when patterns are detected.
61
+ */
62
+ export const SECURITY_REVIEW_DIMENSIONS = [
63
+ 'Injection (SQL, command, template, header)',
64
+ 'Authentication bypass',
65
+ 'Data exposure (PII, secrets, tokens in logs/responses)',
66
+ 'CSRF / SSRF',
67
+ 'XSS (stored, reflected, DOM)',
68
+ 'Secrets leakage (hardcoded keys, env vars in client code)',
69
+ ];
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // Suppress Node.js ExperimentalWarning for built-in SQLite (node:sqlite).
4
+ // Must run before any module that transitively imports node:sqlite loads,
5
+ // so command handlers are lazy-imported below instead of using static imports.
6
+ const _originalEmit = process.emit;
7
+ process.emit = function (event, ...args) {
8
+ if (event === 'warning' && args[0]?.name === 'ExperimentalWarning' &&
9
+ args[0]?.message?.includes('SQLite')) {
10
+ return false;
11
+ }
12
+ return _originalEmit.apply(this, [event, ...args]);
13
+ };
14
+
3
15
  import fs from 'node:fs';
4
16
  import { fileURLToPath } from 'node:url';
5
17
 
6
- import { runCaptureCommand } from './capture/command.js';
7
- import { runValidateCommand } from './commands/validate.js';
8
- import { runDoctorCommand } from './doctor/command.js';
9
- import { runExportCommand as runGeneratedExportCommand } from './export/command.js';
10
- import { runIndexCommand } from './index/command.js';
11
- import { runInitCommand } from './init/command.js';
12
- import { runRecallCommand } from './recall/command.js';
13
- import { runReportCommand } from './reports/command.js';
14
- import { runStateCommand } from './state/command.js';
15
- import { runStatsCommand } from './commands/stats.js';
16
- import { runStatusCommand } from './status/command.js';
17
-
18
18
  const COMMAND_FAMILIES = [
19
19
  'export',
20
20
  'validate',
@@ -29,18 +29,19 @@ const COMMAND_FAMILIES = [
29
29
  'capture'
30
30
  ];
31
31
 
32
- const COMMAND_HANDLERS = {
33
- export: runGeneratedExportCommand,
34
- validate: runValidateCommand,
35
- doctor: runDoctorCommand,
36
- index: runIndexCommand,
37
- init: runInitCommand,
38
- recall: runRecallCommand,
39
- report: runReportCommand,
40
- state: runStateCommand,
41
- status: runStatusCommand,
42
- stats: runStatsCommand,
43
- capture: runCaptureCommand,
32
+ // Lazy-load command handlers so the warning filter is active before node:sqlite loads
33
+ const COMMAND_LOADERS = {
34
+ export: () => import('./export/command.js').then(m => m.runExportCommand),
35
+ validate: () => import('./commands/validate.js').then(m => m.runValidateCommand),
36
+ doctor: () => import('./doctor/command.js').then(m => m.runDoctorCommand),
37
+ index: () => import('./index/command.js').then(m => m.runIndexCommand),
38
+ init: () => import('./init/command.js').then(m => m.runInitCommand),
39
+ recall: () => import('./recall/command.js').then(m => m.runRecallCommand),
40
+ report: () => import('./reports/command.js').then(m => m.runReportCommand),
41
+ state: () => import('./state/command.js').then(m => m.runStateCommand),
42
+ status: () => import('./status/command.js').then(m => m.runStatusCommand),
43
+ stats: () => import('./commands/stats.js').then(m => m.runStatsCommand),
44
+ capture: () => import('./capture/command.js').then(m => m.runCaptureCommand),
44
45
  };
45
46
 
46
47
  export function parseArgs(argv) {
@@ -88,9 +89,9 @@ export async function main(argv = process.argv.slice(2)) {
88
89
  return 1;
89
90
  }
90
91
 
91
- const handler = COMMAND_HANDLERS[parsed.command];
92
+ const loader = COMMAND_LOADERS[parsed.command];
92
93
 
93
- if (!handler) {
94
+ if (!loader) {
94
95
  console.error(`wazir ${parsed.command} is not implemented yet`);
95
96
  return 2;
96
97
  }
@@ -98,6 +99,7 @@ export async function main(argv = process.argv.slice(2)) {
98
99
  let result;
99
100
 
100
101
  try {
102
+ const handler = await loader();
101
103
  result = await handler(parsed);
102
104
  } catch (error) {
103
105
  console.error(error.message);
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Canonical depth parameter table.
3
+ *
4
+ * Single source of truth for all depth-dependent behavior across the pipeline.
5
+ * Skills reference these values conceptually; hooks and tooling import directly.
6
+ */
7
+
8
+ export const DEPTH_LEVELS = new Set(['quick', 'standard', 'deep']);
9
+
10
+ export const DEPTH_TABLE = {
11
+ quick: {
12
+ review_passes: 3,
13
+ loop_cap: 5,
14
+ heartbeat_max_silence_s: 180,
15
+ research_intensity: 'minimal',
16
+ challenge_intensity: 'surface',
17
+ spec_hardening_passes: 1,
18
+ design_review_passes: 1,
19
+ time_estimate_label: '~15-30 min',
20
+ },
21
+ standard: {
22
+ review_passes: 5,
23
+ loop_cap: 10,
24
+ heartbeat_max_silence_s: 120,
25
+ research_intensity: 'balanced',
26
+ challenge_intensity: 'balanced',
27
+ spec_hardening_passes: 3,
28
+ design_review_passes: 3,
29
+ time_estimate_label: '~45-90 min',
30
+ },
31
+ deep: {
32
+ review_passes: 7,
33
+ loop_cap: 15,
34
+ heartbeat_max_silence_s: 90,
35
+ research_intensity: 'thorough',
36
+ challenge_intensity: 'adversarial',
37
+ spec_hardening_passes: 5,
38
+ design_review_passes: 5,
39
+ time_estimate_label: '~2-3 hrs',
40
+ },
41
+ };
42
+
43
+ /**
44
+ * Get a specific depth parameter value.
45
+ *
46
+ * @param {string} depth — 'quick' | 'standard' | 'deep' (defaults to 'standard')
47
+ * @param {string} param — parameter name from the depth table
48
+ * @returns {*} the parameter value
49
+ */
50
+ export function getDepthParam(depth, param) {
51
+ const level = depth ?? 'standard';
52
+ if (!DEPTH_LEVELS.has(level)) {
53
+ throw new Error(`Unknown depth level: "${level}". Valid levels: ${[...DEPTH_LEVELS].join(', ')}`);
54
+ }
55
+ const entry = DEPTH_TABLE[level];
56
+ if (!(param in entry)) {
57
+ throw new Error(`Unknown depth parameter: "${param}". Valid params: ${Object.keys(entry).join(', ')}`);
58
+ }
59
+ return entry[param];
60
+ }
@@ -91,8 +91,7 @@ function renderCommonInstructions(host, manifest) {
91
91
  const DEFAULT_CLAUDE_HOOKS = {
92
92
  hooks: {
93
93
  PreToolUse: [
94
- { matcher: 'Write|Edit', hooks: [{ type: 'command', command: './hooks/protected-path-write-guard' }] },
95
- { matcher: 'Bash', hooks: [{ type: 'command', command: './hooks/context-mode-router' }] },
94
+ { matcher: 'Write|Edit|Bash', hooks: [{ type: 'command', command: './hooks/pretooluse-dispatcher' }] },
96
95
  ],
97
96
  SessionStart: [
98
97
  { hooks: [{ type: 'command', command: './hooks/loop-cap-guard' }] },
@@ -115,21 +114,21 @@ function renderCursorHooks() {
115
114
  return JSON.stringify({
116
115
  hooks: [
117
116
  {
118
- name: 'protected-path-write-guard',
119
- command: './hooks/protected-path-write-guard',
117
+ name: 'pretooluse-dispatcher',
118
+ command: './hooks/pretooluse-dispatcher',
120
119
  },
121
120
  {
122
121
  name: 'loop-cap-guard',
123
122
  command: './hooks/loop-cap-guard',
124
123
  },
125
- {
126
- name: 'context-mode-router',
127
- command: './hooks/context-mode-router',
128
- },
129
124
  {
130
125
  name: 'session-start',
131
126
  command: './hooks/session-start',
132
127
  },
128
+ {
129
+ name: 'stop-pipeline-gate',
130
+ command: './hooks/stop-pipeline-gate',
131
+ },
133
132
  ],
134
133
  }, null, 2);
135
134
  }