@wazir-dev/cli 1.1.0 → 1.3.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 +74 -10
- package/README.md +15 -15
- 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/roles-and-workflows.md +2 -0
- package/docs/concepts/why-wazir.md +59 -0
- package/docs/decisions/2026-03-19-deferred-items.md +564 -0
- package/docs/decisions/2026-03-19-enhancement-decisions.md +300 -0
- package/docs/readmes/INDEX.md +21 -5
- package/docs/readmes/features/expertise/README.md +2 -2
- package/docs/readmes/features/exports/README.md +2 -2
- package/docs/readmes/features/hooks/pre-compact-summary.md +1 -1
- package/docs/readmes/features/schemas/README.md +3 -0
- package/docs/readmes/features/skills/README.md +17 -0
- package/docs/readmes/features/skills/clarifier.md +5 -0
- package/docs/readmes/features/skills/claude-cli.md +5 -0
- package/docs/readmes/features/skills/codex-cli.md +5 -0
- package/docs/readmes/features/skills/dispatching-parallel-agents.md +5 -0
- package/docs/readmes/features/skills/executing-plans.md +5 -0
- package/docs/readmes/features/skills/executor.md +5 -0
- package/docs/readmes/features/skills/finishing-a-development-branch.md +5 -0
- package/docs/readmes/features/skills/gemini-cli.md +5 -0
- package/docs/readmes/features/skills/humanize.md +5 -0
- package/docs/readmes/features/skills/init-pipeline.md +5 -0
- package/docs/readmes/features/skills/receiving-code-review.md +5 -0
- package/docs/readmes/features/skills/requesting-code-review.md +5 -0
- package/docs/readmes/features/skills/reviewer.md +5 -0
- package/docs/readmes/features/skills/subagent-driven-development.md +5 -0
- package/docs/readmes/features/skills/using-git-worktrees.md +5 -0
- package/docs/readmes/features/skills/wazir.md +5 -0
- package/docs/readmes/features/skills/writing-skills.md +5 -0
- package/docs/readmes/features/workflows/prepare-next.md +1 -1
- package/docs/reference/configuration-reference.md +47 -6
- package/docs/reference/hooks.md +1 -0
- package/docs/reference/launch-checklist.md +4 -4
- package/docs/reference/review-loop-pattern.md +119 -9
- package/docs/reference/roles-reference.md +1 -0
- package/docs/reference/skill-tiers.md +147 -0
- package/docs/reference/tooling-cli.md +3 -1
- package/docs/truth-claims.yaml +12 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +214 -1
- 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 +9 -0
- package/exports/hosts/claude/CLAUDE.md +1 -1
- package/exports/hosts/claude/export.manifest.json +6 -4
- package/exports/hosts/claude/host-package.json +3 -1
- package/exports/hosts/codex/AGENTS.md +1 -1
- package/exports/hosts/codex/export.manifest.json +6 -4
- package/exports/hosts/codex/host-package.json +3 -1
- package/exports/hosts/cursor/.cursor/hooks.json +4 -0
- package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +1 -1
- package/exports/hosts/cursor/export.manifest.json +6 -4
- package/exports/hosts/cursor/host-package.json +3 -1
- package/exports/hosts/gemini/GEMINI.md +1 -1
- package/exports/hosts/gemini/export.manifest.json +6 -4
- package/exports/hosts/gemini/host-package.json +3 -1
- package/hooks/context-mode-router +191 -0
- package/hooks/definitions/context_mode_router.yaml +19 -0
- package/hooks/hooks.json +31 -6
- package/hooks/protected-path-write-guard +8 -0
- package/hooks/routing-matrix.json +45 -0
- package/hooks/session-start +62 -1
- package/llms-full.txt +937 -134
- package/package.json +2 -4
- package/schemas/hook.schema.json +2 -1
- package/schemas/phase-report.schema.json +89 -0
- package/schemas/usage.schema.json +25 -1
- package/schemas/wazir-manifest.schema.json +19 -0
- package/skills/brainstorming/SKILL.md +32 -157
- package/skills/clarifier/SKILL.md +289 -111
- package/skills/claude-cli/SKILL.md +320 -0
- package/skills/codex-cli/SKILL.md +260 -0
- package/skills/debugging/SKILL.md +13 -0
- package/skills/design/SKILL.md +13 -0
- package/skills/dispatching-parallel-agents/SKILL.md +13 -0
- package/skills/executing-plans/SKILL.md +13 -0
- package/skills/executor/SKILL.md +139 -19
- package/skills/finishing-a-development-branch/SKILL.md +13 -0
- package/skills/gemini-cli/SKILL.md +260 -0
- package/skills/humanize/SKILL.md +13 -0
- package/skills/init-pipeline/SKILL.md +72 -164
- package/skills/prepare-next/SKILL.md +81 -10
- package/skills/receiving-code-review/SKILL.md +13 -0
- package/skills/requesting-code-review/SKILL.md +13 -0
- package/skills/reviewer/SKILL.md +369 -24
- package/skills/run-audit/SKILL.md +13 -0
- package/skills/scan-project/SKILL.md +13 -0
- package/skills/self-audit/SKILL.md +217 -16
- package/skills/skill-research/SKILL.md +188 -0
- package/skills/subagent-driven-development/SKILL.md +13 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +2 -0
- package/skills/subagent-driven-development/implementer-prompt.md +8 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +7 -0
- package/skills/tdd/SKILL.md +13 -0
- package/skills/using-git-worktrees/SKILL.md +13 -0
- package/skills/using-skills/SKILL.md +13 -0
- package/skills/verification/SKILL.md +54 -3
- package/skills/wazir/SKILL.md +464 -381
- package/skills/writing-plans/SKILL.md +14 -1
- package/skills/writing-skills/SKILL.md +13 -0
- package/templates/artifacts/implementation-plan.md +3 -0
- package/templates/artifacts/tasks-template.md +133 -0
- package/templates/examples/phase-report.example.json +48 -0
- package/tooling/src/adapters/composition-engine.js +256 -0
- package/tooling/src/adapters/model-router.js +84 -0
- package/tooling/src/capture/command.js +41 -2
- package/tooling/src/capture/run-config.js +3 -1
- package/tooling/src/capture/store.js +56 -0
- package/tooling/src/capture/usage.js +106 -0
- package/tooling/src/capture/user-input.js +66 -0
- package/tooling/src/checks/ac-matrix.js +256 -0
- package/tooling/src/checks/command-registry.js +12 -0
- package/tooling/src/checks/docs-truth.js +1 -1
- package/tooling/src/checks/security-sensitivity.js +69 -0
- package/tooling/src/checks/skills.js +111 -0
- package/tooling/src/cli.js +31 -20
- package/tooling/src/commands/stats.js +161 -0
- package/tooling/src/commands/validate.js +5 -1
- package/tooling/src/export/compiler.js +33 -37
- package/tooling/src/gating/agent.js +145 -0
- package/tooling/src/guards/phase-prerequisite-guard.js +185 -0
- package/tooling/src/hooks/routing-logic.js +69 -0
- package/tooling/src/init/auto-detect.js +258 -0
- package/tooling/src/init/command.js +38 -170
- package/tooling/src/input/scanner.js +46 -0
- package/tooling/src/reports/command.js +103 -0
- package/tooling/src/reports/phase-report.js +323 -0
- package/tooling/src/state/command.js +160 -0
- package/tooling/src/state/db.js +287 -0
- package/tooling/src/status/command.js +58 -1
- package/tooling/src/verify/proof-collector.js +299 -0
- package/wazir.manifest.yaml +26 -14
- package/workflows/plan-review.md +3 -1
- package/workflows/verify.md +30 -1
|
@@ -1,201 +1,69 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
2
1
|
import fs from 'node:fs';
|
|
3
2
|
import path from 'node:path';
|
|
4
|
-
import { select } from '@inquirer/prompts';
|
|
5
3
|
|
|
4
|
+
import { autoInit, detectHost, detectProjectStack } from './auto-detect.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* wazir init [--auto|--force]
|
|
8
|
+
*
|
|
9
|
+
* Default: --auto (zero-config, no prompts, infer everything)
|
|
10
|
+
* --force: reinitialize even if already initialized
|
|
11
|
+
*/
|
|
6
12
|
export async function runInitCommand(parsed, context = {}) {
|
|
7
13
|
const cwd = context.cwd ?? process.cwd();
|
|
8
14
|
const wazirDir = path.join(cwd, '.wazir');
|
|
9
15
|
const configPath = path.join(wazirDir, 'state', 'config.json');
|
|
16
|
+
const isForce = parsed.args.includes('--force');
|
|
10
17
|
|
|
11
|
-
|
|
18
|
+
// Already initialized check
|
|
19
|
+
if (fs.existsSync(configPath) && !isForce) {
|
|
12
20
|
return {
|
|
13
21
|
exitCode: 1,
|
|
14
22
|
stderr: 'Pipeline already initialized. Use --force to reinitialize.\n',
|
|
15
23
|
};
|
|
16
24
|
}
|
|
17
25
|
|
|
26
|
+
// Auto mode — zero-config
|
|
18
27
|
try {
|
|
19
|
-
|
|
20
|
-
for (const dir of ['input', 'state', 'runs']) {
|
|
21
|
-
fs.mkdirSync(path.join(wazirDir, dir), { recursive: true });
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Pipeline mode
|
|
25
|
-
const modelMode = await select({
|
|
26
|
-
message: 'How should Wazir run in this project?',
|
|
27
|
-
choices: [
|
|
28
|
-
{ name: 'Single model (Recommended) — slash commands only', value: 'claude-only' },
|
|
29
|
-
{ name: 'Multi-model — routes by complexity (Haiku/Sonnet/Opus)', value: 'multi-model' },
|
|
30
|
-
{ name: 'Multi-tool — current model + external tools for reviews', value: 'multi-tool' },
|
|
31
|
-
],
|
|
32
|
-
default: 'claude-only',
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// Multi-tool tools (conditional)
|
|
36
|
-
let multiToolTools = [];
|
|
37
|
-
if (modelMode === 'multi-tool') {
|
|
38
|
-
const toolChoice = await select({
|
|
39
|
-
message: 'Which external tools should Wazir use for reviews?',
|
|
40
|
-
choices: [
|
|
41
|
-
{ name: 'Codex — Send reviews to OpenAI Codex', value: 'codex' },
|
|
42
|
-
{ name: 'Gemini — Send reviews to Google Gemini', value: 'gemini' },
|
|
43
|
-
{ name: 'Both — Use Codex and Gemini', value: 'both' },
|
|
44
|
-
],
|
|
45
|
-
});
|
|
46
|
-
multiToolTools = toolChoice === 'both' ? ['codex', 'gemini'] : [toolChoice];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Codex model (conditional)
|
|
50
|
-
let codexModel = null;
|
|
51
|
-
if (multiToolTools.includes('codex')) {
|
|
52
|
-
codexModel = await select({
|
|
53
|
-
message: 'Which Codex model should Wazir use?',
|
|
54
|
-
choices: [
|
|
55
|
-
{ name: 'gpt-5.3-codex-spark (Recommended) — fast, good for review loops', value: 'gpt-5.3-codex-spark' },
|
|
56
|
-
{ name: 'gpt-5.4 — slower, deeper analysis for complex reviews', value: 'gpt-5.4' },
|
|
57
|
-
],
|
|
58
|
-
default: 'gpt-5.3-codex-spark',
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Default depth
|
|
63
|
-
const defaultDepth = await select({
|
|
64
|
-
message: 'What default depth should runs use?',
|
|
65
|
-
choices: [
|
|
66
|
-
{ name: 'Quick — minimal research, single-pass review', value: 'quick' },
|
|
67
|
-
{ name: 'Standard (Recommended) — balanced research, multi-pass hardening', value: 'standard' },
|
|
68
|
-
{ name: 'Deep — extended research, strict review thresholds', value: 'deep' },
|
|
69
|
-
],
|
|
70
|
-
default: 'standard',
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Default intent
|
|
74
|
-
const defaultIntent = await select({
|
|
75
|
-
message: 'What kind of work does this project mostly involve?',
|
|
76
|
-
choices: [
|
|
77
|
-
{ name: 'Feature (Recommended) — new functionality or enhancement', value: 'feature' },
|
|
78
|
-
{ name: 'Bugfix — fix broken behavior', value: 'bugfix' },
|
|
79
|
-
{ name: 'Refactor — restructure without changing behavior', value: 'refactor' },
|
|
80
|
-
{ name: 'Docs — documentation only', value: 'docs' },
|
|
81
|
-
{ name: 'Spike — research and exploration', value: 'spike' },
|
|
82
|
-
],
|
|
83
|
-
default: 'feature',
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// Agent Teams (conditional)
|
|
87
|
-
let teamMode = 'sequential';
|
|
88
|
-
let parallelBackend = 'none';
|
|
89
|
-
|
|
90
|
-
const depthAllows = defaultDepth === 'standard' || defaultDepth === 'deep';
|
|
91
|
-
const intentAllows = defaultIntent === 'feature' || defaultIntent === 'refactor';
|
|
28
|
+
const result = autoInit(cwd, { context, force: isForce });
|
|
92
29
|
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
{ name: 'Yes — parallel teammates, faster but experimental (Opus only)', value: 'parallel' },
|
|
99
|
-
],
|
|
100
|
-
default: 'sequential',
|
|
101
|
-
});
|
|
102
|
-
teamMode = useTeams;
|
|
103
|
-
parallelBackend = useTeams === 'parallel' ? 'claude_teams' : 'none';
|
|
104
|
-
|
|
105
|
-
if (teamMode === 'parallel') {
|
|
106
|
-
try {
|
|
107
|
-
execFileSync('claude', ['config', 'set', 'env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS', '1'], { stdio: 'pipe' });
|
|
108
|
-
} catch {
|
|
109
|
-
// claude CLI not available — user will need to set it manually
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Write config
|
|
115
|
-
const config = {
|
|
116
|
-
model_mode: modelMode,
|
|
117
|
-
...(modelMode === 'multi-tool' && {
|
|
118
|
-
multi_tool: {
|
|
119
|
-
tools: multiToolTools,
|
|
120
|
-
...(codexModel && { codex: { model: codexModel } }),
|
|
121
|
-
},
|
|
122
|
-
}),
|
|
123
|
-
default_depth: defaultDepth,
|
|
124
|
-
default_intent: defaultIntent,
|
|
125
|
-
team_mode: teamMode,
|
|
126
|
-
parallel_backend: parallelBackend,
|
|
127
|
-
};
|
|
128
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
129
|
-
|
|
130
|
-
// Runtime-specific setup
|
|
131
|
-
const filesCreated = ['.wazir/input/', '.wazir/state/', '.wazir/runs/', '.wazir/state/config.json'];
|
|
132
|
-
|
|
133
|
-
if (multiToolTools.includes('codex')) {
|
|
134
|
-
const content = [
|
|
135
|
-
'# Wazir Pipeline',
|
|
136
|
-
'',
|
|
137
|
-
'Agent protocols are at `~/.claude/agents/` (global).',
|
|
138
|
-
'',
|
|
139
|
-
'## Running the Pipeline',
|
|
140
|
-
'1. Clarifier: read and follow `~/.claude/agents/clarifier.md` — tasks are in `.wazir/input/`',
|
|
141
|
-
'2. Orchestrator: read and follow `~/.claude/agents/orchestrator.md` — start from task 1',
|
|
142
|
-
'3. Opus Reviewer: read and follow `~/.claude/agents/opus-reviewer.md` — run all phases',
|
|
143
|
-
'',
|
|
144
|
-
'## Review Mode',
|
|
145
|
-
'This project uses Codex as a secondary reviewer. Review artifacts are in `.wazir/reviews/`.',
|
|
146
|
-
'',
|
|
147
|
-
].join('\n');
|
|
148
|
-
fs.writeFileSync(path.join(cwd, 'AGENTS.md'), content);
|
|
149
|
-
filesCreated.push('AGENTS.md');
|
|
30
|
+
if (result.alreadyInitialized && !isForce) {
|
|
31
|
+
return {
|
|
32
|
+
exitCode: 0,
|
|
33
|
+
stdout: `Already initialized. Host: ${result.host.host}, Stack: ${result.stack.language}\n`,
|
|
34
|
+
};
|
|
150
35
|
}
|
|
151
36
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
'2. Orchestrator: read and follow `~/.claude/agents/orchestrator.md` — start from task 1',
|
|
161
|
-
'3. Opus Reviewer: read and follow `~/.claude/agents/opus-reviewer.md` — run all phases',
|
|
162
|
-
'',
|
|
163
|
-
'## Review Mode',
|
|
164
|
-
'This project uses Gemini as a secondary reviewer. Review artifacts are in `.wazir/reviews/`.',
|
|
165
|
-
'',
|
|
166
|
-
].join('\n');
|
|
167
|
-
fs.writeFileSync(path.join(cwd, 'GEMINI.md'), content);
|
|
168
|
-
filesCreated.push('GEMINI.md');
|
|
37
|
+
// Auto-export for detected host
|
|
38
|
+
let exportNote = '';
|
|
39
|
+
try {
|
|
40
|
+
const { buildHostExports } = await import('../export/compiler.js');
|
|
41
|
+
buildHostExports(cwd);
|
|
42
|
+
exportNote = ` Exports: generated for ${result.host.host}\n`;
|
|
43
|
+
} catch {
|
|
44
|
+
exportNote = ' Exports: skipped (run `wazir export build` manually)\n';
|
|
169
45
|
}
|
|
170
46
|
|
|
171
47
|
const lines = [
|
|
172
48
|
'',
|
|
173
|
-
'
|
|
174
|
-
'',
|
|
175
|
-
` Mode: ${modelMode}`,
|
|
176
|
-
` Depth: ${defaultDepth}`,
|
|
177
|
-
` Intent: ${defaultIntent}`,
|
|
178
|
-
` Teams: ${teamMode}`,
|
|
49
|
+
'Wazir initialized (zero-config).',
|
|
179
50
|
'',
|
|
51
|
+
` Host: ${result.host.host} (${result.host.confidence} confidence)`,
|
|
52
|
+
` Stack: ${result.stack.language}${result.stack.framework ? ` / ${result.stack.framework}` : ''}`,
|
|
53
|
+
` Mode: ${result.config.model_mode}`,
|
|
54
|
+
` Depth: ${result.config.default_depth}`,
|
|
55
|
+
exportNote,
|
|
180
56
|
'Files created:',
|
|
181
|
-
...filesCreated.map((f) => ` - ${f}`),
|
|
57
|
+
...result.filesCreated.map((f) => ` - ${f}`),
|
|
58
|
+
'',
|
|
59
|
+
'Next: /wazir <what you want to build>',
|
|
182
60
|
'',
|
|
183
|
-
'
|
|
184
|
-
' /wazir <your request> \u2014 Run the full pipeline',
|
|
185
|
-
' /clarifier \u2014 Research, clarify, plan',
|
|
186
|
-
' /executor \u2014 Autonomous execution',
|
|
187
|
-
' /reviewer \u2014 Final review and scoring',
|
|
61
|
+
'Override: `wazir config set model_mode multi-tool`',
|
|
188
62
|
'',
|
|
189
63
|
];
|
|
190
64
|
|
|
191
|
-
return {
|
|
192
|
-
exitCode: 0,
|
|
193
|
-
stdout: lines.join('\n'),
|
|
194
|
-
};
|
|
65
|
+
return { exitCode: 0, stdout: lines.join('\n') };
|
|
195
66
|
} catch (error) {
|
|
196
|
-
|
|
197
|
-
return { exitCode: 130, stderr: '\nInit cancelled.\n' };
|
|
198
|
-
}
|
|
199
|
-
return { exitCode: 1, stderr: `${error.message}\n` };
|
|
67
|
+
return { exitCode: 1, stderr: `Auto-init failed: ${error.message}\n` };
|
|
200
68
|
}
|
|
201
69
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { readdirSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join, resolve, basename } from 'node:path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scan input directories for briefing materials.
|
|
6
|
+
* Globs input/*.md and .wazir/input/*.md (flat, not recursive).
|
|
7
|
+
* Excludes README.md.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} projectRoot - Absolute path to project root
|
|
10
|
+
* @returns {Array<{path: string, auto: boolean}>} Found files with auto flag
|
|
11
|
+
*/
|
|
12
|
+
export function scanInputDirectories(projectRoot) {
|
|
13
|
+
const root = resolve(projectRoot);
|
|
14
|
+
const dirs = [
|
|
15
|
+
join(root, 'input'),
|
|
16
|
+
join(root, '.wazir', 'input'),
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const results = [];
|
|
20
|
+
|
|
21
|
+
for (const dir of dirs) {
|
|
22
|
+
if (!existsSync(dir)) continue;
|
|
23
|
+
|
|
24
|
+
let entries;
|
|
25
|
+
try {
|
|
26
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
27
|
+
} catch {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
for (const entry of entries) {
|
|
32
|
+
if (!entry.isFile()) continue;
|
|
33
|
+
if (!entry.name.endsWith('.md')) continue;
|
|
34
|
+
if (basename(entry.name).toLowerCase() === 'readme.md') continue;
|
|
35
|
+
|
|
36
|
+
results.push({ path: join(dir, entry.name), auto: false });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Single file auto-use
|
|
41
|
+
if (results.length === 1) {
|
|
42
|
+
results[0].auto = true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return results;
|
|
46
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import { parseCommandOptions } from '../command-options.js';
|
|
4
|
+
import { readYamlFile } from '../loaders.js';
|
|
5
|
+
import { findProjectRoot } from '../project-root.js';
|
|
6
|
+
import { resolveStateRoot } from '../state-root.js';
|
|
7
|
+
import { collectPhaseMetrics, buildPhaseReport } from './phase-report.js';
|
|
8
|
+
|
|
9
|
+
const USAGE = 'Usage: wazir report phase --run <run-id> --phase <phase> [--base <branch>] [--json]';
|
|
10
|
+
|
|
11
|
+
function handlePhase(parsed, context = {}) {
|
|
12
|
+
const { options } = parseCommandOptions(parsed.args, {
|
|
13
|
+
boolean: ['json', 'help'],
|
|
14
|
+
string: ['run', 'phase', 'base', 'state-root'],
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
if (options.help) {
|
|
18
|
+
return {
|
|
19
|
+
exitCode: 0,
|
|
20
|
+
stdout: `${USAGE}\n\nGenerate a structured phase report with metrics from git, tests, and run state.\n`,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!options.run) {
|
|
25
|
+
return {
|
|
26
|
+
exitCode: 1,
|
|
27
|
+
stderr: `wazir report phase requires --run <id>\n${USAGE}\n`,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!options.phase) {
|
|
32
|
+
return {
|
|
33
|
+
exitCode: 1,
|
|
34
|
+
stderr: `wazir report phase requires --phase <phase>\n${USAGE}\n`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const projectRoot = findProjectRoot(context.cwd ?? process.cwd());
|
|
39
|
+
const manifest = readYamlFile(path.join(projectRoot, 'wazir.manifest.yaml'));
|
|
40
|
+
const stateRoot = resolveStateRoot(projectRoot, manifest, {
|
|
41
|
+
cwd: context.cwd ?? process.cwd(),
|
|
42
|
+
override: options.stateRoot,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const metrics = collectPhaseMetrics({
|
|
46
|
+
projectRoot,
|
|
47
|
+
stateRoot,
|
|
48
|
+
runId: options.run,
|
|
49
|
+
phase: options.phase,
|
|
50
|
+
baseBranch: options.base ?? 'main',
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const report = buildPhaseReport(metrics);
|
|
54
|
+
|
|
55
|
+
if (options.json) {
|
|
56
|
+
return {
|
|
57
|
+
exitCode: 0,
|
|
58
|
+
stdout: `${JSON.stringify(report, null, 2)}\n`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Human-readable text output
|
|
63
|
+
const lines = [
|
|
64
|
+
`Phase Report: ${report.phase} (run ${report.run_id})`,
|
|
65
|
+
`Generated: ${report.generated_at}`,
|
|
66
|
+
'',
|
|
67
|
+
'Tests:',
|
|
68
|
+
` total: ${report.metrics.tests.total}, passed: ${report.metrics.tests.passed}, failed: ${report.metrics.tests.failed}, skipped: ${report.metrics.tests.skipped}`,
|
|
69
|
+
'',
|
|
70
|
+
'Diff:',
|
|
71
|
+
` files changed: ${report.metrics.diff.files_changed}, insertions: ${report.metrics.diff.insertions}, deletions: ${report.metrics.diff.deletions}`,
|
|
72
|
+
'',
|
|
73
|
+
'Files:',
|
|
74
|
+
` added: ${report.metrics.files.added.length}, modified: ${report.metrics.files.modified.length}, deleted: ${report.metrics.files.deleted.length}`,
|
|
75
|
+
'',
|
|
76
|
+
`Artifacts: ${report.metrics.artifacts.length}`,
|
|
77
|
+
`Duration: ${report.metrics.duration_seconds !== null ? `${report.metrics.duration_seconds}s` : 'N/A'}`,
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
exitCode: 0,
|
|
82
|
+
stdout: `${lines.join('\n')}\n`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function runReportCommand(parsed, context = {}) {
|
|
87
|
+
try {
|
|
88
|
+
switch (parsed.subcommand) {
|
|
89
|
+
case 'phase':
|
|
90
|
+
return handlePhase(parsed, context);
|
|
91
|
+
default:
|
|
92
|
+
return {
|
|
93
|
+
exitCode: 1,
|
|
94
|
+
stderr: `${USAGE}\n`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
return {
|
|
99
|
+
exitCode: 1,
|
|
100
|
+
stderr: `${error.message}\n`,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|