@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.
- package/CHANGELOG.md +54 -44
- package/README.md +13 -13
- package/assets/demo.cast +47 -0
- package/assets/demo.gif +0 -0
- package/docs/anti-patterns/AP-23-skipping-enabled-workflows.md +28 -0
- package/docs/anti-patterns/AP-24-clarifier-deciding-scope.md +34 -0
- package/docs/concepts/architecture.md +1 -1
- package/docs/concepts/why-wazir.md +1 -1
- package/docs/readmes/INDEX.md +1 -1
- package/docs/readmes/features/expertise/README.md +1 -1
- package/docs/readmes/features/hooks/pre-compact-summary.md +1 -1
- package/docs/reference/hooks.md +1 -0
- package/docs/reference/launch-checklist.md +3 -3
- package/docs/reference/review-loop-pattern.md +3 -2
- package/docs/reference/skill-tiers.md +2 -2
- package/docs/research/2026-03-20-agents/a18fb002157904af5.txt +187 -0
- package/docs/research/2026-03-20-agents/a1d0ac79ac2f11e6f.txt +2 -0
- package/docs/research/2026-03-20-agents/a324079de037abd7c.txt +198 -0
- package/docs/research/2026-03-20-agents/a357586bccfafb0e5.txt +256 -0
- package/docs/research/2026-03-20-agents/a4365394e4d753105.txt +137 -0
- package/docs/research/2026-03-20-agents/a492af28bc52d3613.txt +136 -0
- package/docs/research/2026-03-20-agents/a4984db0b6a8eee07.txt +124 -0
- package/docs/research/2026-03-20-agents/a5b30e59d34bbb062.txt +214 -0
- package/docs/research/2026-03-20-agents/a5cf7829dab911586.txt +165 -0
- package/docs/research/2026-03-20-agents/a607157c30dd97c9e.txt +96 -0
- package/docs/research/2026-03-20-agents/a60b68b1e19d1e16b.txt +115 -0
- package/docs/research/2026-03-20-agents/a722af01c5594aba0.txt +166 -0
- package/docs/research/2026-03-20-agents/a787bdc516faa5829.txt +181 -0
- package/docs/research/2026-03-20-agents/a7c46d1bba1056ed2.txt +132 -0
- package/docs/research/2026-03-20-agents/a7e5abbab2b281a0d.txt +100 -0
- package/docs/research/2026-03-20-agents/a8dbadc66cd0d7d5a.txt +95 -0
- package/docs/research/2026-03-20-agents/a904d9f45d6b86a6d.txt +75 -0
- package/docs/research/2026-03-20-agents/a927659a942ee7f60.txt +102 -0
- package/docs/research/2026-03-20-agents/a962cb569191f7583.txt +125 -0
- package/docs/research/2026-03-20-agents/aab6decea538aac41.txt +148 -0
- package/docs/research/2026-03-20-agents/abd58b853dd938a1b.txt +295 -0
- package/docs/research/2026-03-20-agents/ac009da573eff7f65.txt +100 -0
- package/docs/research/2026-03-20-agents/ac1bc783364405e5f.txt +190 -0
- package/docs/research/2026-03-20-agents/aca5e2b57fde152a0.txt +132 -0
- package/docs/research/2026-03-20-agents/ad849b8c0a7e95b8b.txt +176 -0
- package/docs/research/2026-03-20-agents/adc2b12a4da32c962.txt +258 -0
- package/docs/research/2026-03-20-agents/af97caaaa9a80e4cb.txt +146 -0
- package/docs/research/2026-03-20-agents/afc5faceee368b3ca.txt +111 -0
- package/docs/research/2026-03-20-agents/afdb282d866e3c1e4.txt +164 -0
- package/docs/research/2026-03-20-agents/afe9d1f61c02b1e8d.txt +299 -0
- package/docs/research/2026-03-20-agents/b4hmkwril.txt +1856 -0
- package/docs/research/2026-03-20-agents/b80ptk89g.txt +1856 -0
- package/docs/research/2026-03-20-agents/bf54s1jss.txt +1150 -0
- package/docs/research/2026-03-20-agents/bhd6kq2kx.txt +1856 -0
- package/docs/research/2026-03-20-agents/bmb2fodyr.txt +988 -0
- package/docs/research/2026-03-20-agents/bmmsrij8i.txt +826 -0
- package/docs/research/2026-03-20-agents/bn4t2ywpu.txt +2175 -0
- package/docs/research/2026-03-20-agents/bu22t9f1z.txt +0 -0
- package/docs/research/2026-03-20-agents/bwvl98v2p.txt +738 -0
- package/docs/research/2026-03-20-agents/psych-a3697a7fd06eb64fd.txt +135 -0
- package/docs/research/2026-03-20-agents/psych-a37776fabc870feae.txt +123 -0
- package/docs/research/2026-03-20-agents/psych-a5b1fe05c0589efaf.txt +2 -0
- package/docs/research/2026-03-20-agents/psych-a95c15b1f29424435.txt +76 -0
- package/docs/research/2026-03-20-agents/psych-a9c26f4d9172dde7c.txt +2 -0
- package/docs/research/2026-03-20-agents/psych-aa19c69f0ca2c5ad3.txt +2 -0
- package/docs/research/2026-03-20-agents/psych-aa4e4cb70e1be5ecb.txt +95 -0
- package/docs/research/2026-03-20-agents/psych-ab5b302f26a554663.txt +102 -0
- package/docs/research/2026-03-20-deep-research-complete.md +101 -0
- package/docs/research/2026-03-20-deep-research-status.md +38 -0
- package/docs/research/2026-03-20-enforcement-research.md +107 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +117 -0
- package/expertise/composition-map.yaml +27 -8
- package/expertise/digests/reviewer/ai-coding-digest.md +83 -0
- package/expertise/digests/reviewer/architectural-thinking-digest.md +63 -0
- package/expertise/digests/reviewer/architecture-antipatterns-digest.md +49 -0
- package/expertise/digests/reviewer/code-smells-digest.md +53 -0
- package/expertise/digests/reviewer/coupling-cohesion-digest.md +54 -0
- package/expertise/digests/reviewer/ddd-digest.md +60 -0
- package/expertise/digests/reviewer/dependency-risk-digest.md +40 -0
- package/expertise/digests/reviewer/error-handling-digest.md +55 -0
- package/expertise/digests/reviewer/review-methodology-digest.md +49 -0
- package/exports/hosts/claude/.claude/commands/learn.md +61 -8
- package/exports/hosts/claude/.claude/commands/plan-review.md +3 -1
- package/exports/hosts/claude/.claude/commands/verify.md +30 -1
- package/exports/hosts/claude/.claude/settings.json +7 -6
- package/exports/hosts/claude/export.manifest.json +8 -5
- package/exports/hosts/claude/host-package.json +3 -0
- package/exports/hosts/codex/export.manifest.json +8 -5
- package/exports/hosts/codex/host-package.json +3 -0
- package/exports/hosts/cursor/.cursor/hooks.json +6 -6
- package/exports/hosts/cursor/export.manifest.json +8 -5
- package/exports/hosts/cursor/host-package.json +3 -0
- package/exports/hosts/gemini/export.manifest.json +8 -5
- package/exports/hosts/gemini/host-package.json +3 -0
- package/hooks/definitions/pretooluse_dispatcher.yaml +26 -0
- package/hooks/definitions/pretooluse_pipeline_guard.yaml +22 -0
- package/hooks/definitions/stop_pipeline_gate.yaml +22 -0
- package/hooks/hooks.json +7 -6
- package/hooks/pretooluse-dispatcher +84 -0
- package/hooks/pretooluse-pipeline-guard +9 -0
- package/hooks/stop-pipeline-gate +9 -0
- package/llms-full.txt +48 -18
- package/package.json +2 -3
- package/schemas/decision.schema.json +15 -0
- package/schemas/hook.schema.json +4 -1
- package/schemas/phase-report.schema.json +9 -0
- package/skills/TEMPLATE-3-ZONE.md +160 -0
- package/skills/brainstorming/SKILL.md +137 -21
- package/skills/clarifier/SKILL.md +364 -53
- package/skills/claude-cli/SKILL.md +91 -12
- package/skills/codex-cli/SKILL.md +91 -12
- package/skills/debugging/SKILL.md +133 -38
- package/skills/design/SKILL.md +173 -37
- package/skills/dispatching-parallel-agents/SKILL.md +129 -31
- package/skills/executing-plans/SKILL.md +113 -25
- package/skills/executor/SKILL.md +252 -21
- package/skills/finishing-a-development-branch/SKILL.md +107 -18
- package/skills/gemini-cli/SKILL.md +91 -12
- package/skills/humanize/SKILL.md +92 -13
- package/skills/init-pipeline/SKILL.md +90 -18
- package/skills/prepare-next/SKILL.md +93 -24
- package/skills/receiving-code-review/SKILL.md +90 -16
- package/skills/requesting-code-review/SKILL.md +100 -24
- package/skills/requesting-code-review/code-reviewer.md +29 -17
- package/skills/reviewer/SKILL.md +270 -57
- package/skills/run-audit/SKILL.md +92 -15
- package/skills/scan-project/SKILL.md +93 -14
- package/skills/self-audit/SKILL.md +133 -39
- package/skills/skill-research/SKILL.md +275 -0
- package/skills/subagent-driven-development/SKILL.md +129 -30
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +30 -2
- package/skills/subagent-driven-development/implementer-prompt.md +40 -27
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +25 -12
- package/skills/tdd/SKILL.md +125 -20
- package/skills/using-git-worktrees/SKILL.md +118 -28
- package/skills/using-skills/SKILL.md +116 -29
- package/skills/verification/SKILL.md +160 -17
- package/skills/wazir/SKILL.md +750 -120
- package/skills/writing-plans/SKILL.md +134 -28
- package/skills/writing-skills/SKILL.md +91 -13
- package/skills/writing-skills/anthropic-best-practices.md +104 -64
- package/skills/writing-skills/persuasion-principles.md +100 -34
- package/tooling/src/capture/command.js +46 -2
- package/tooling/src/capture/decision.js +40 -0
- package/tooling/src/capture/store.js +33 -0
- package/tooling/src/capture/user-input.js +66 -0
- package/tooling/src/checks/security-sensitivity.js +69 -0
- package/tooling/src/cli.js +28 -26
- package/tooling/src/config/depth-table.js +60 -0
- package/tooling/src/export/compiler.js +7 -8
- package/tooling/src/guards/guardrail-functions.js +131 -0
- package/tooling/src/guards/phase-prerequisite-guard.js +97 -3
- package/tooling/src/hooks/pretooluse-dispatcher.js +300 -0
- package/tooling/src/hooks/pretooluse-pipeline-guard.js +141 -0
- package/tooling/src/hooks/stop-pipeline-gate.js +92 -0
- package/tooling/src/init/auto-detect.js +0 -2
- package/tooling/src/init/command.js +3 -95
- package/tooling/src/learn/pipeline.js +177 -0
- package/tooling/src/state/db.js +251 -2
- package/tooling/src/state/pipeline-state.js +262 -0
- package/tooling/src/status/command.js +6 -1
- package/tooling/src/verify/proof-collector.js +299 -0
- package/wazir.manifest.yaml +3 -0
- package/workflows/learn.md +61 -8
- package/workflows/plan-review.md +3 -1
- package/workflows/verify.md +30 -1
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
const WEB_FRAMEWORKS = ['next', 'vite', 'react-scripts', '@angular/cli', 'nuxt', 'astro', 'gatsby'];
|
|
6
|
+
const API_FRAMEWORKS = ['express', 'fastify', 'hono', 'koa', '@nestjs/core', '@hapi/hapi'];
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Detect whether a project produces runnable output and what type.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} projectRoot
|
|
12
|
+
* @returns {'web' | 'api' | 'cli' | 'library'}
|
|
13
|
+
*/
|
|
14
|
+
export function detectRunnableType(projectRoot) {
|
|
15
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
16
|
+
if (!fs.existsSync(pkgPath)) return 'library';
|
|
17
|
+
|
|
18
|
+
let pkg;
|
|
19
|
+
try {
|
|
20
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
21
|
+
} catch {
|
|
22
|
+
return 'library';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
26
|
+
|
|
27
|
+
if (WEB_FRAMEWORKS.some((fw) => fw in allDeps)) return 'web';
|
|
28
|
+
if (API_FRAMEWORKS.some((fw) => fw in allDeps)) return 'api';
|
|
29
|
+
if (pkg.bin) return 'cli';
|
|
30
|
+
|
|
31
|
+
return 'library';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Run a command safely using execFileSync (no shell injection).
|
|
36
|
+
*
|
|
37
|
+
* @param {string} cmd - The executable
|
|
38
|
+
* @param {string[]} args - Arguments array
|
|
39
|
+
* @param {string} cwd
|
|
40
|
+
* @returns {{ exit_code: number, stdout: string, stderr: string }}
|
|
41
|
+
*/
|
|
42
|
+
function runCommand(cmd, args, cwd) {
|
|
43
|
+
try {
|
|
44
|
+
const stdout = execFileSync(cmd, args, {
|
|
45
|
+
cwd,
|
|
46
|
+
encoding: 'utf8',
|
|
47
|
+
timeout: 60000,
|
|
48
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
49
|
+
});
|
|
50
|
+
return { exit_code: 0, stdout: stdout.trim(), stderr: '' };
|
|
51
|
+
} catch (err) {
|
|
52
|
+
return {
|
|
53
|
+
exit_code: err.status ?? 1,
|
|
54
|
+
stdout: (err.stdout ?? '').trim(),
|
|
55
|
+
stderr: (err.stderr ?? '').trim(),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Summarize command output to a short string.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} stdout
|
|
64
|
+
* @param {number} maxLen
|
|
65
|
+
* @returns {string}
|
|
66
|
+
*/
|
|
67
|
+
function summarize(stdout, maxLen = 200) {
|
|
68
|
+
if (!stdout) return '';
|
|
69
|
+
const lines = stdout.split('\n');
|
|
70
|
+
if (lines.length <= 5) return stdout.slice(0, maxLen);
|
|
71
|
+
return [...lines.slice(0, 3), `... (${lines.length} lines total)`, ...lines.slice(-2)]
|
|
72
|
+
.join('\n')
|
|
73
|
+
.slice(0, maxLen);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Check if a package.json has a specific script.
|
|
78
|
+
*
|
|
79
|
+
* @param {string} projectRoot
|
|
80
|
+
* @param {string} scriptName
|
|
81
|
+
* @returns {boolean}
|
|
82
|
+
*/
|
|
83
|
+
function hasScript(projectRoot, scriptName) {
|
|
84
|
+
try {
|
|
85
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'));
|
|
86
|
+
return !!(pkg.scripts && pkg.scripts[scriptName]);
|
|
87
|
+
} catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Check if a config file exists for a tool.
|
|
94
|
+
*
|
|
95
|
+
* @param {string} projectRoot
|
|
96
|
+
* @param {string[]} candidates
|
|
97
|
+
* @returns {boolean}
|
|
98
|
+
*/
|
|
99
|
+
function hasConfigFile(projectRoot, candidates) {
|
|
100
|
+
return candidates.some((f) => fs.existsSync(path.join(projectRoot, f)));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Collect library-type proof: tests, lint, format, type-check.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} projectRoot
|
|
107
|
+
* @returns {{ tool: string, command: string, exit_code: number, stdout_summary: string, passed: boolean }[]}
|
|
108
|
+
*/
|
|
109
|
+
function collectLibraryEvidence(projectRoot) {
|
|
110
|
+
const evidence = [];
|
|
111
|
+
|
|
112
|
+
// npm test
|
|
113
|
+
if (hasScript(projectRoot, 'test')) {
|
|
114
|
+
const result = runCommand('npm', ['test'], projectRoot);
|
|
115
|
+
evidence.push({
|
|
116
|
+
tool: 'npm test',
|
|
117
|
+
command: 'npm test',
|
|
118
|
+
exit_code: result.exit_code,
|
|
119
|
+
stdout_summary: summarize(result.stdout),
|
|
120
|
+
passed: result.exit_code === 0,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// TypeScript type check
|
|
125
|
+
if (
|
|
126
|
+
hasConfigFile(projectRoot, ['tsconfig.json']) ||
|
|
127
|
+
hasScript(projectRoot, 'typecheck')
|
|
128
|
+
) {
|
|
129
|
+
const cmd = hasScript(projectRoot, 'typecheck')
|
|
130
|
+
? ['npm', ['run', 'typecheck']]
|
|
131
|
+
: ['npx', ['tsc', '--noEmit']];
|
|
132
|
+
const result = runCommand(cmd[0], cmd[1], projectRoot);
|
|
133
|
+
evidence.push({
|
|
134
|
+
tool: 'tsc',
|
|
135
|
+
command: cmd[0] + ' ' + cmd[1].join(' '),
|
|
136
|
+
exit_code: result.exit_code,
|
|
137
|
+
stdout_summary: summarize(result.exit_code === 0 ? 'No type errors' : result.stdout || result.stderr),
|
|
138
|
+
passed: result.exit_code === 0,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ESLint
|
|
143
|
+
if (
|
|
144
|
+
hasConfigFile(projectRoot, ['.eslintrc', '.eslintrc.js', '.eslintrc.json', '.eslintrc.yml', 'eslint.config.js', 'eslint.config.mjs']) ||
|
|
145
|
+
hasScript(projectRoot, 'lint')
|
|
146
|
+
) {
|
|
147
|
+
const cmd = hasScript(projectRoot, 'lint')
|
|
148
|
+
? ['npm', ['run', 'lint']]
|
|
149
|
+
: ['npx', ['eslint', '.']];
|
|
150
|
+
const result = runCommand(cmd[0], cmd[1], projectRoot);
|
|
151
|
+
evidence.push({
|
|
152
|
+
tool: 'eslint',
|
|
153
|
+
command: cmd[0] + ' ' + cmd[1].join(' '),
|
|
154
|
+
exit_code: result.exit_code,
|
|
155
|
+
stdout_summary: summarize(result.exit_code === 0 ? 'No lint errors' : result.stdout || result.stderr),
|
|
156
|
+
passed: result.exit_code === 0,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Prettier
|
|
161
|
+
if (
|
|
162
|
+
hasConfigFile(projectRoot, ['.prettierrc', '.prettierrc.js', '.prettierrc.json', '.prettierrc.yml', 'prettier.config.js', 'prettier.config.mjs']) ||
|
|
163
|
+
hasScript(projectRoot, 'format:check')
|
|
164
|
+
) {
|
|
165
|
+
const cmd = hasScript(projectRoot, 'format:check')
|
|
166
|
+
? ['npm', ['run', 'format:check']]
|
|
167
|
+
: ['npx', ['prettier', '--check', '.']];
|
|
168
|
+
const result = runCommand(cmd[0], cmd[1], projectRoot);
|
|
169
|
+
evidence.push({
|
|
170
|
+
tool: 'prettier',
|
|
171
|
+
command: cmd[0] + ' ' + cmd[1].join(' '),
|
|
172
|
+
exit_code: result.exit_code,
|
|
173
|
+
stdout_summary: summarize(result.exit_code === 0 ? 'All files formatted' : result.stdout || result.stderr),
|
|
174
|
+
passed: result.exit_code === 0,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return evidence;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Collect web-type proof: build + library checks.
|
|
183
|
+
*
|
|
184
|
+
* @param {string} projectRoot
|
|
185
|
+
* @returns {{ tool: string, command: string, exit_code: number, stdout_summary: string, passed: boolean }[]}
|
|
186
|
+
*/
|
|
187
|
+
function collectWebEvidence(projectRoot) {
|
|
188
|
+
const evidence = [];
|
|
189
|
+
|
|
190
|
+
// Build
|
|
191
|
+
if (hasScript(projectRoot, 'build')) {
|
|
192
|
+
const result = runCommand('npm', ['run', 'build'], projectRoot);
|
|
193
|
+
evidence.push({
|
|
194
|
+
tool: 'build',
|
|
195
|
+
command: 'npm run build',
|
|
196
|
+
exit_code: result.exit_code,
|
|
197
|
+
stdout_summary: summarize(result.stdout),
|
|
198
|
+
passed: result.exit_code === 0,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Also run library checks (tests, lint, etc.)
|
|
203
|
+
evidence.push(...collectLibraryEvidence(projectRoot));
|
|
204
|
+
|
|
205
|
+
return evidence;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Collect API-type proof: library checks (server start/stop is complex, defer to manual).
|
|
210
|
+
*
|
|
211
|
+
* @param {string} projectRoot
|
|
212
|
+
* @returns {{ tool: string, command: string, exit_code: number, stdout_summary: string, passed: boolean }[]}
|
|
213
|
+
*/
|
|
214
|
+
function collectApiEvidence(projectRoot) {
|
|
215
|
+
return collectLibraryEvidence(projectRoot);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Collect CLI-type proof: --help output + library checks.
|
|
220
|
+
*
|
|
221
|
+
* @param {string} projectRoot
|
|
222
|
+
* @returns {{ tool: string, command: string, exit_code: number, stdout_summary: string, passed: boolean }[]}
|
|
223
|
+
*/
|
|
224
|
+
function collectCliEvidence(projectRoot) {
|
|
225
|
+
const evidence = [];
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'));
|
|
229
|
+
const binEntry = typeof pkg.bin === 'string' ? pkg.bin : Object.values(pkg.bin || {})[0];
|
|
230
|
+
if (binEntry) {
|
|
231
|
+
const binPath = path.join(projectRoot, binEntry);
|
|
232
|
+
if (fs.existsSync(binPath)) {
|
|
233
|
+
const result = runCommand('node', [binPath, '--help'], projectRoot);
|
|
234
|
+
evidence.push({
|
|
235
|
+
tool: 'cli --help',
|
|
236
|
+
command: `node ${binEntry} --help`,
|
|
237
|
+
exit_code: result.exit_code,
|
|
238
|
+
stdout_summary: summarize(result.stdout),
|
|
239
|
+
passed: result.exit_code === 0,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
} catch { /* ignore */ }
|
|
244
|
+
|
|
245
|
+
evidence.push(...collectLibraryEvidence(projectRoot));
|
|
246
|
+
|
|
247
|
+
return evidence;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Collect proof of implementation for a task.
|
|
252
|
+
*
|
|
253
|
+
* @param {{ id: string, title: string }} taskSpec
|
|
254
|
+
* @param {{ projectRoot: string, runId?: string, stateRoot?: string }} runConfig
|
|
255
|
+
* @returns {Promise<{ task_id: string, type: string, timestamp: string, evidence: object[], status: string, all_passed: boolean }>}
|
|
256
|
+
*/
|
|
257
|
+
export async function collectProof(taskSpec, runConfig) {
|
|
258
|
+
const { projectRoot } = runConfig;
|
|
259
|
+
const type = detectRunnableType(projectRoot);
|
|
260
|
+
|
|
261
|
+
let evidence;
|
|
262
|
+
switch (type) {
|
|
263
|
+
case 'web':
|
|
264
|
+
evidence = collectWebEvidence(projectRoot);
|
|
265
|
+
break;
|
|
266
|
+
case 'api':
|
|
267
|
+
evidence = collectApiEvidence(projectRoot);
|
|
268
|
+
break;
|
|
269
|
+
case 'cli':
|
|
270
|
+
evidence = collectCliEvidence(projectRoot);
|
|
271
|
+
break;
|
|
272
|
+
default:
|
|
273
|
+
evidence = collectLibraryEvidence(projectRoot);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const allPassed = evidence.length === 0 || evidence.every((e) => e.passed);
|
|
277
|
+
|
|
278
|
+
const result = {
|
|
279
|
+
task_id: taskSpec.id,
|
|
280
|
+
type,
|
|
281
|
+
timestamp: new Date().toISOString(),
|
|
282
|
+
evidence,
|
|
283
|
+
status: allPassed ? 'pass' : 'fail',
|
|
284
|
+
all_passed: allPassed,
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Save to artifacts if runId provided
|
|
288
|
+
if (runConfig.runId && runConfig.stateRoot) {
|
|
289
|
+
const artifactDir = path.join(runConfig.stateRoot, 'runs', runConfig.runId, 'artifacts');
|
|
290
|
+
if (fs.existsSync(artifactDir)) {
|
|
291
|
+
fs.writeFileSync(
|
|
292
|
+
path.join(artifactDir, `proof-${taskSpec.id}.json`),
|
|
293
|
+
JSON.stringify(result, null, 2) + '\n',
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return result;
|
|
299
|
+
}
|
package/wazir.manifest.yaml
CHANGED
package/workflows/learn.md
CHANGED
|
@@ -2,37 +2,90 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
Extract durable scoped learnings
|
|
5
|
+
Extract durable scoped learnings from the completed run using the 4-stage promotion pipeline.
|
|
6
6
|
|
|
7
7
|
## Phase entry
|
|
8
8
|
|
|
9
9
|
On entering this phase, run:
|
|
10
|
-
`wazir capture event --run <run-id> --event phase_enter --phase
|
|
10
|
+
`wazir capture event --run <run-id> --event phase_enter --phase learn --status in_progress`
|
|
11
11
|
|
|
12
12
|
## Inputs
|
|
13
13
|
|
|
14
14
|
- run artifacts
|
|
15
|
-
- review findings
|
|
15
|
+
- review findings (all passes, all tiers)
|
|
16
16
|
- verification proof
|
|
17
17
|
|
|
18
18
|
## Primary Role
|
|
19
19
|
|
|
20
20
|
- `learner`
|
|
21
21
|
|
|
22
|
+
## Pipeline Stages
|
|
23
|
+
|
|
24
|
+
The learning pipeline follows a 4-stage promotion model (Tally → Candidate → Promote → Active):
|
|
25
|
+
|
|
26
|
+
### Stage 1: TALLY (Automatic)
|
|
27
|
+
|
|
28
|
+
Every finding from the run is:
|
|
29
|
+
1. Canonicalized (file paths, line numbers, identifiers stripped)
|
|
30
|
+
2. Hashed for dedup
|
|
31
|
+
3. Clustered by semantic similarity in `finding_clusters` table
|
|
32
|
+
4. Category-tagged (from the reviewer's finding category)
|
|
33
|
+
|
|
34
|
+
Also:
|
|
35
|
+
- Read `.wazir/runs/<id>/decisions.ndjson` for recurring patterns
|
|
36
|
+
- Append summary to `memory/findings/cumulative-findings.md`
|
|
37
|
+
|
|
38
|
+
Implementation: `tooling/src/learn/pipeline.js` → `tallyFinding()`
|
|
39
|
+
|
|
40
|
+
### Stage 2: CANDIDATE (Automatic)
|
|
41
|
+
|
|
42
|
+
Clusters meeting the promotion threshold are flagged:
|
|
43
|
+
- **Occurrence threshold:** 3+ findings with the same canonical pattern
|
|
44
|
+
- **Run threshold:** Pattern must appear across 2+ distinct runs
|
|
45
|
+
- **Drift cap:** No promotion if active antipatterns count >= 30
|
|
46
|
+
|
|
47
|
+
Implementation: `tooling/src/learn/pipeline.js` → `identifyCandidates()` + `promoteToCandidates()`
|
|
48
|
+
|
|
49
|
+
### Stage 3: PROMOTE (Human Gate)
|
|
50
|
+
|
|
51
|
+
Candidates are proposed for user review:
|
|
52
|
+
- Written to `memory/learnings/proposed/<run-id>-<NNN>.md`
|
|
53
|
+
- User reviews and accepts/rejects via `/wazir audit learnings`
|
|
54
|
+
- Accepted candidates move to `status: accepted` in `antipattern_candidates` table
|
|
55
|
+
|
|
56
|
+
### Stage 4: ACTIVE (Automatic)
|
|
57
|
+
|
|
58
|
+
Accepted antipatterns are loaded into reviewer context for future runs:
|
|
59
|
+
- Injected as project-level learnings alongside expertise modules
|
|
60
|
+
- Hit-rate tracked: if an antipattern triggers in <5% of runs over 90 days, it's demoted
|
|
61
|
+
- Max 30 active project-level antipatterns (drift prevention)
|
|
62
|
+
|
|
63
|
+
## Drift Prevention
|
|
64
|
+
|
|
65
|
+
- **Cap:** 30 active project antipatterns maximum
|
|
66
|
+
- **TTL:** 90-day expiry on unreviewed candidates
|
|
67
|
+
- **Demotion:** Antipatterns with <5% hit rate over 90 days are auto-demoted
|
|
68
|
+
- **Consolidation:** When count exceeds 25, similar antipatterns are merged
|
|
69
|
+
|
|
22
70
|
## Outputs
|
|
23
71
|
|
|
24
|
-
-
|
|
25
|
-
-
|
|
72
|
+
- Tallied findings in `finding_clusters` table
|
|
73
|
+
- Promoted candidates in `antipattern_candidates` table
|
|
74
|
+
- Proposed learning artifacts in `memory/learnings/proposed/`
|
|
75
|
+
- Cumulative findings appended to `memory/findings/cumulative-findings.md`
|
|
26
76
|
|
|
27
77
|
## Approval Gate
|
|
28
78
|
|
|
29
|
-
-
|
|
79
|
+
- Accepted learnings require explicit user review and scope tags
|
|
80
|
+
- Learnings are NEVER auto-applied to future runs without user acceptance
|
|
30
81
|
|
|
31
82
|
## Phase exit
|
|
32
83
|
|
|
33
84
|
On completing this phase, run:
|
|
34
|
-
`wazir capture event --run <run-id> --event phase_exit --phase
|
|
85
|
+
`wazir capture event --run <run-id> --event phase_exit --phase learn --status completed`
|
|
35
86
|
|
|
36
87
|
## Failure Conditions
|
|
37
88
|
|
|
38
|
-
- auto-applied learning drift
|
|
89
|
+
- auto-applied learning drift (bypassing human gate)
|
|
90
|
+
- candidate count exceeds cap without consolidation
|
|
91
|
+
- stale candidates not expired
|
package/workflows/plan-review.md
CHANGED
|
@@ -39,7 +39,9 @@ On completing this phase, run:
|
|
|
39
39
|
|
|
40
40
|
## Loop Structure
|
|
41
41
|
|
|
42
|
-
Follows the review loop pattern in `docs/reference/review-loop-pattern.md` with plan dimensions. The planner role resolves findings. Pass count determined by depth. No extension.
|
|
42
|
+
Follows the review loop pattern in `docs/reference/review-loop-pattern.md` with 8 plan dimensions (including Input Coverage). The planner role resolves findings. Pass count determined by depth. No extension.
|
|
43
|
+
|
|
44
|
+
**Input Coverage dimension:** The reviewer reads the original input/briefing, counts distinct items, and compares against tasks in the plan. If `tasks_in_plan < items_in_input`, this is a HIGH finding listing the missing items. This prevents silent scope reduction where 21 input items become 5 tasks.
|
|
43
45
|
|
|
44
46
|
## Failure Conditions
|
|
45
47
|
|
package/workflows/verify.md
CHANGED
|
@@ -14,6 +14,16 @@ On entering this phase, run:
|
|
|
14
14
|
- changed files
|
|
15
15
|
- claimed outcomes
|
|
16
16
|
- acceptance criteria
|
|
17
|
+
- project type (detected via `detectRunnableType(projectRoot)` → web | api | cli | library, from `tooling/src/verify/proof-collector.js`)
|
|
18
|
+
|
|
19
|
+
## Process
|
|
20
|
+
|
|
21
|
+
1. **Detect project type:** Run `detectRunnableType(projectRoot)` to determine verification strategy
|
|
22
|
+
2. **Collect evidence:** Run `collectProof(taskSpec, runConfig)` with the detected type
|
|
23
|
+
3. **For runnable output (web/api/cli):** Run the application and capture runtime evidence (build output, screenshots, curl responses, CLI output)
|
|
24
|
+
4. **For non-runnable output (library/config/skills):** Run lint, format check, type check, and tests — all must pass
|
|
25
|
+
5. **Save evidence:** Write to `.wazir/runs/<id>/artifacts/proof-<task>.json`
|
|
26
|
+
6. **Validate:** Ensure every acceptance criterion has at least one evidence item mapped to it
|
|
17
27
|
|
|
18
28
|
## Primary Role
|
|
19
29
|
|
|
@@ -21,7 +31,11 @@ On entering this phase, run:
|
|
|
21
31
|
|
|
22
32
|
## Outputs
|
|
23
33
|
|
|
24
|
-
- verification proof artifact
|
|
34
|
+
- verification proof artifact (produced by `collectProof` from `tooling/src/verify/proof-collector.js`)
|
|
35
|
+
|
|
36
|
+
## Proof Collection
|
|
37
|
+
|
|
38
|
+
Use `detectRunnableType` to classify the project, then `collectProof` to gather evidence. The proof-collector runs type-appropriate commands (build, test, lint, type-check) using `execFileSync` and returns structured `{ type, evidence }`.
|
|
25
39
|
|
|
26
40
|
## Approval Gate
|
|
27
41
|
|
|
@@ -32,6 +46,19 @@ On entering this phase, run:
|
|
|
32
46
|
On completing this phase, run:
|
|
33
47
|
`wazir capture event --run <run-id> --event phase_exit --phase <phase-name> --status completed`
|
|
34
48
|
|
|
49
|
+
## Proof of Implementation
|
|
50
|
+
|
|
51
|
+
The verifier detects whether the project output is runnable and collects appropriate evidence:
|
|
52
|
+
|
|
53
|
+
| Project Type | Detection | Evidence Collected |
|
|
54
|
+
|-------------|-----------|-------------------|
|
|
55
|
+
| `web` | next/vite/react-scripts in deps | Build output, Playwright screenshot (if available), curl response |
|
|
56
|
+
| `api` | express/fastify/hono in deps | curl endpoint responses with status codes |
|
|
57
|
+
| `cli` | `bin` field in package.json | `--help` output, test run with sample args |
|
|
58
|
+
| `library` | default (no runnable markers) | npm test, tsc, eslint, prettier — all must pass |
|
|
59
|
+
|
|
60
|
+
Evidence is saved to `.wazir/runs/<id>/artifacts/proof-<task>.json` using `collectProof()` from `tooling/src/verify/proof-collector.js`.
|
|
61
|
+
|
|
35
62
|
## Relationship to Review Loops
|
|
36
63
|
|
|
37
64
|
Verification is invoked per-task during execution, not as a review loop. It produces deterministic proof, not adversarial findings.
|
|
@@ -39,3 +66,5 @@ Verification is invoked per-task during execution, not as a review loop. It prod
|
|
|
39
66
|
## Failure Conditions
|
|
40
67
|
|
|
41
68
|
- stale or partial verification
|
|
69
|
+
- proof-collector reports `status: "fail"` for any evidence item
|
|
70
|
+
- runnable type detected but no evidence collected
|