@wazir-dev/cli 1.1.0 → 1.2.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 (124) hide show
  1. package/CHANGELOG.md +73 -4
  2. package/README.md +6 -6
  3. package/docs/concepts/architecture.md +1 -1
  4. package/docs/concepts/roles-and-workflows.md +2 -0
  5. package/docs/concepts/why-wazir.md +59 -0
  6. package/docs/decisions/2026-03-19-deferred-items.md +564 -0
  7. package/docs/decisions/2026-03-19-enhancement-decisions.md +300 -0
  8. package/docs/readmes/INDEX.md +21 -5
  9. package/docs/readmes/features/expertise/README.md +2 -2
  10. package/docs/readmes/features/exports/README.md +2 -2
  11. package/docs/readmes/features/schemas/README.md +3 -0
  12. package/docs/readmes/features/skills/README.md +17 -0
  13. package/docs/readmes/features/skills/clarifier.md +5 -0
  14. package/docs/readmes/features/skills/claude-cli.md +5 -0
  15. package/docs/readmes/features/skills/codex-cli.md +5 -0
  16. package/docs/readmes/features/skills/dispatching-parallel-agents.md +5 -0
  17. package/docs/readmes/features/skills/executing-plans.md +5 -0
  18. package/docs/readmes/features/skills/executor.md +5 -0
  19. package/docs/readmes/features/skills/finishing-a-development-branch.md +5 -0
  20. package/docs/readmes/features/skills/gemini-cli.md +5 -0
  21. package/docs/readmes/features/skills/humanize.md +5 -0
  22. package/docs/readmes/features/skills/init-pipeline.md +5 -0
  23. package/docs/readmes/features/skills/receiving-code-review.md +5 -0
  24. package/docs/readmes/features/skills/requesting-code-review.md +5 -0
  25. package/docs/readmes/features/skills/reviewer.md +5 -0
  26. package/docs/readmes/features/skills/subagent-driven-development.md +5 -0
  27. package/docs/readmes/features/skills/using-git-worktrees.md +5 -0
  28. package/docs/readmes/features/skills/wazir.md +5 -0
  29. package/docs/readmes/features/skills/writing-skills.md +5 -0
  30. package/docs/readmes/features/workflows/prepare-next.md +1 -1
  31. package/docs/reference/configuration-reference.md +47 -6
  32. package/docs/reference/launch-checklist.md +4 -4
  33. package/docs/reference/review-loop-pattern.md +117 -8
  34. package/docs/reference/roles-reference.md +1 -0
  35. package/docs/reference/skill-tiers.md +147 -0
  36. package/docs/reference/tooling-cli.md +3 -1
  37. package/docs/truth-claims.yaml +12 -0
  38. package/expertise/antipatterns/process/ai-coding-antipatterns.md +97 -1
  39. package/exports/hosts/claude/.claude/settings.json +9 -0
  40. package/exports/hosts/claude/CLAUDE.md +1 -1
  41. package/exports/hosts/claude/export.manifest.json +4 -2
  42. package/exports/hosts/claude/host-package.json +3 -1
  43. package/exports/hosts/codex/AGENTS.md +1 -1
  44. package/exports/hosts/codex/export.manifest.json +4 -2
  45. package/exports/hosts/codex/host-package.json +3 -1
  46. package/exports/hosts/cursor/.cursor/hooks.json +4 -0
  47. package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +1 -1
  48. package/exports/hosts/cursor/export.manifest.json +4 -2
  49. package/exports/hosts/cursor/host-package.json +3 -1
  50. package/exports/hosts/gemini/GEMINI.md +1 -1
  51. package/exports/hosts/gemini/export.manifest.json +4 -2
  52. package/exports/hosts/gemini/host-package.json +3 -1
  53. package/hooks/context-mode-router +191 -0
  54. package/hooks/definitions/context_mode_router.yaml +19 -0
  55. package/hooks/hooks.json +31 -6
  56. package/hooks/protected-path-write-guard +8 -0
  57. package/hooks/routing-matrix.json +45 -0
  58. package/hooks/session-start +62 -1
  59. package/llms-full.txt +905 -132
  60. package/package.json +2 -3
  61. package/schemas/hook.schema.json +2 -1
  62. package/schemas/phase-report.schema.json +80 -0
  63. package/schemas/usage.schema.json +25 -1
  64. package/schemas/wazir-manifest.schema.json +19 -0
  65. package/skills/brainstorming/SKILL.md +18 -155
  66. package/skills/clarifier/SKILL.md +122 -98
  67. package/skills/claude-cli/SKILL.md +320 -0
  68. package/skills/codex-cli/SKILL.md +260 -0
  69. package/skills/debugging/SKILL.md +13 -0
  70. package/skills/design/SKILL.md +13 -0
  71. package/skills/dispatching-parallel-agents/SKILL.md +13 -0
  72. package/skills/executing-plans/SKILL.md +13 -0
  73. package/skills/executor/SKILL.md +72 -19
  74. package/skills/finishing-a-development-branch/SKILL.md +13 -0
  75. package/skills/gemini-cli/SKILL.md +260 -0
  76. package/skills/humanize/SKILL.md +13 -0
  77. package/skills/init-pipeline/SKILL.md +73 -164
  78. package/skills/prepare-next/SKILL.md +81 -10
  79. package/skills/receiving-code-review/SKILL.md +13 -0
  80. package/skills/requesting-code-review/SKILL.md +13 -0
  81. package/skills/reviewer/SKILL.md +287 -15
  82. package/skills/run-audit/SKILL.md +13 -0
  83. package/skills/scan-project/SKILL.md +13 -0
  84. package/skills/self-audit/SKILL.md +197 -16
  85. package/skills/subagent-driven-development/SKILL.md +13 -0
  86. package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +2 -0
  87. package/skills/subagent-driven-development/implementer-prompt.md +8 -0
  88. package/skills/subagent-driven-development/spec-reviewer-prompt.md +7 -0
  89. package/skills/tdd/SKILL.md +13 -0
  90. package/skills/using-git-worktrees/SKILL.md +13 -0
  91. package/skills/using-skills/SKILL.md +13 -0
  92. package/skills/verification/SKILL.md +13 -0
  93. package/skills/wazir/SKILL.md +194 -377
  94. package/skills/writing-plans/SKILL.md +14 -1
  95. package/skills/writing-skills/SKILL.md +13 -0
  96. package/templates/artifacts/implementation-plan.md +3 -0
  97. package/templates/artifacts/tasks-template.md +133 -0
  98. package/templates/examples/phase-report.example.json +48 -0
  99. package/tooling/src/adapters/composition-engine.js +256 -0
  100. package/tooling/src/adapters/model-router.js +84 -0
  101. package/tooling/src/capture/command.js +24 -1
  102. package/tooling/src/capture/run-config.js +3 -1
  103. package/tooling/src/capture/store.js +24 -0
  104. package/tooling/src/capture/usage.js +106 -0
  105. package/tooling/src/checks/ac-matrix.js +256 -0
  106. package/tooling/src/checks/command-registry.js +12 -0
  107. package/tooling/src/checks/docs-truth.js +1 -1
  108. package/tooling/src/checks/skills.js +111 -0
  109. package/tooling/src/cli.js +9 -0
  110. package/tooling/src/commands/stats.js +161 -0
  111. package/tooling/src/commands/validate.js +5 -1
  112. package/tooling/src/export/compiler.js +33 -37
  113. package/tooling/src/gating/agent.js +145 -0
  114. package/tooling/src/guards/phase-prerequisite-guard.js +127 -0
  115. package/tooling/src/hooks/routing-logic.js +69 -0
  116. package/tooling/src/init/auto-detect.js +260 -0
  117. package/tooling/src/init/command.js +95 -135
  118. package/tooling/src/input/scanner.js +46 -0
  119. package/tooling/src/reports/command.js +103 -0
  120. package/tooling/src/reports/phase-report.js +323 -0
  121. package/tooling/src/state/command.js +160 -0
  122. package/tooling/src/state/db.js +287 -0
  123. package/tooling/src/status/command.js +53 -1
  124. package/wazir.manifest.yaml +26 -14
@@ -0,0 +1,260 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+
5
+ /**
6
+ * Detect which AI host CLI is running this process.
7
+ * Checks environment variables, process ancestry, and known file markers.
8
+ *
9
+ * @returns {{ host: string, confidence: string, signals: string[] }}
10
+ */
11
+ export function detectHost() {
12
+ const signals = [];
13
+
14
+ // Claude Code detection
15
+ if (process.env.CLAUDE_CODE || process.env.CLAUDE_CODE_ENTRYPOINT) {
16
+ signals.push('CLAUDE_CODE env var');
17
+ return { host: 'claude', confidence: 'high', signals };
18
+ }
19
+ if (fs.existsSync(path.join(os.homedir(), '.claude'))) {
20
+ signals.push('~/.claude directory exists');
21
+ }
22
+
23
+ // Codex detection
24
+ if (process.env.CODEX_CLI || process.env.OPENAI_API_KEY) {
25
+ signals.push('CODEX_CLI or OPENAI_API_KEY env var');
26
+ }
27
+ if (process.env.CODEX_SANDBOX_MODE) {
28
+ signals.push('CODEX_SANDBOX_MODE env var');
29
+ return { host: 'codex', confidence: 'high', signals };
30
+ }
31
+
32
+ // Gemini detection
33
+ if (process.env.GEMINI_API_KEY || process.env.GOOGLE_AI_API_KEY) {
34
+ signals.push('Gemini API key env var');
35
+ }
36
+ if (process.env.GEMINI_CLI) {
37
+ signals.push('GEMINI_CLI env var');
38
+ return { host: 'gemini', confidence: 'high', signals };
39
+ }
40
+
41
+ // Cursor detection
42
+ if (process.env.CURSOR_SESSION || process.env.CURSOR_TRACE_ID) {
43
+ signals.push('Cursor session env var');
44
+ return { host: 'cursor', confidence: 'high', signals };
45
+ }
46
+
47
+ // Fallback: check for marker files
48
+ const cwd = process.cwd();
49
+ if (fs.existsSync(path.join(cwd, '.claude', 'settings.json'))) {
50
+ signals.push('.claude/settings.json exists in project');
51
+ return { host: 'claude', confidence: 'medium', signals };
52
+ }
53
+ if (fs.existsSync(path.join(cwd, 'AGENTS.md'))) {
54
+ signals.push('AGENTS.md exists (Codex marker)');
55
+ return { host: 'codex', confidence: 'medium', signals };
56
+ }
57
+ if (fs.existsSync(path.join(cwd, 'GEMINI.md'))) {
58
+ signals.push('GEMINI.md exists (Gemini marker)');
59
+ return { host: 'gemini', confidence: 'medium', signals };
60
+ }
61
+ if (fs.existsSync(path.join(cwd, '.cursorrules'))) {
62
+ signals.push('.cursorrules exists (Cursor marker)');
63
+ return { host: 'cursor', confidence: 'medium', signals };
64
+ }
65
+
66
+ // Default: assume Claude Code (most common)
67
+ if (signals.includes('~/.claude directory exists')) {
68
+ return { host: 'claude', confidence: 'low', signals };
69
+ }
70
+
71
+ return { host: 'claude', confidence: 'low', signals: ['no host detected, defaulting to claude'] };
72
+ }
73
+
74
+ /**
75
+ * Auto-detect project stack from package files, config, and structure.
76
+ *
77
+ * @param {string} projectRoot
78
+ * @returns {{ language: string, framework: string|null, stack: string[] }}
79
+ */
80
+ export function detectProjectStack(projectRoot) {
81
+ const stack = [];
82
+ let language = 'unknown';
83
+ let framework = null;
84
+
85
+ // Node.js / JavaScript
86
+ if (fs.existsSync(path.join(projectRoot, 'package.json'))) {
87
+ language = 'javascript';
88
+ stack.push('node');
89
+
90
+ try {
91
+ const pkg = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'));
92
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
93
+
94
+ if (allDeps.next) { framework = 'nextjs'; stack.push('next'); }
95
+ else if (allDeps.react) { framework = 'react'; stack.push('react'); }
96
+ else if (allDeps.vue) { framework = 'vue'; stack.push('vue'); }
97
+ else if (allDeps.angular || allDeps['@angular/core']) { framework = 'angular'; stack.push('angular'); }
98
+ else if (allDeps.express || allDeps.fastify || allDeps.koa) { framework = 'node-api'; stack.push('node-api'); }
99
+
100
+ if (allDeps.typescript) { language = 'typescript'; stack.push('typescript'); }
101
+ } catch { /* ignore parse errors */ }
102
+ }
103
+
104
+ // Python
105
+ if (fs.existsSync(path.join(projectRoot, 'pyproject.toml')) ||
106
+ fs.existsSync(path.join(projectRoot, 'requirements.txt')) ||
107
+ fs.existsSync(path.join(projectRoot, 'setup.py'))) {
108
+ language = 'python';
109
+ stack.push('python');
110
+
111
+ if (fs.existsSync(path.join(projectRoot, 'manage.py'))) { framework = 'django'; stack.push('django'); }
112
+ }
113
+
114
+ // Go
115
+ if (fs.existsSync(path.join(projectRoot, 'go.mod'))) {
116
+ language = 'go';
117
+ stack.push('go');
118
+ }
119
+
120
+ // Rust
121
+ if (fs.existsSync(path.join(projectRoot, 'Cargo.toml'))) {
122
+ language = 'rust';
123
+ stack.push('rust');
124
+ }
125
+
126
+ // Flutter / Dart
127
+ if (fs.existsSync(path.join(projectRoot, 'pubspec.yaml'))) {
128
+ language = 'dart';
129
+ framework = 'flutter';
130
+ stack.push('dart', 'flutter');
131
+ }
132
+
133
+ // Java
134
+ if (fs.existsSync(path.join(projectRoot, 'pom.xml')) ||
135
+ fs.existsSync(path.join(projectRoot, 'build.gradle'))) {
136
+ language = 'java';
137
+ stack.push('java');
138
+ }
139
+
140
+ return { language, framework, stack };
141
+ }
142
+
143
+ /**
144
+ * Infer intent from request text using keyword matching.
145
+ *
146
+ * @param {string} requestText
147
+ * @returns {string} One of: bugfix, refactor, docs, spike, feature
148
+ */
149
+ export function inferIntent(requestText) {
150
+ if (!requestText) return 'feature';
151
+ const lower = requestText.toLowerCase();
152
+
153
+ const patterns = [
154
+ { keywords: ['fix', 'bug', 'broken', 'crash', 'error', 'issue', 'wrong'], intent: 'bugfix' },
155
+ { keywords: ['refactor', 'clean', 'restructure', 'reorganize', 'rename', 'simplify'], intent: 'refactor' },
156
+ { keywords: ['doc', 'document', 'readme', 'guide', 'explain'], intent: 'docs' },
157
+ { keywords: ['research', 'spike', 'explore', 'investigate', 'prototype'], intent: 'spike' },
158
+ ];
159
+
160
+ for (const { keywords, intent } of patterns) {
161
+ if (keywords.some((kw) => lower.includes(kw))) return intent;
162
+ }
163
+
164
+ return 'feature';
165
+ }
166
+
167
+ /**
168
+ * Parse inline depth modifiers from request text.
169
+ *
170
+ * @param {string} requestText
171
+ * @returns {{ depth: string, cleanedText: string }}
172
+ */
173
+ export function parseDepthModifier(requestText) {
174
+ if (!requestText) return { depth: 'standard', cleanedText: '' };
175
+
176
+ const match = requestText.match(/^\s*(quick|deep)\s+/i);
177
+ if (match) {
178
+ return {
179
+ depth: match[1].toLowerCase(),
180
+ cleanedText: requestText.slice(match[0].length),
181
+ };
182
+ }
183
+
184
+ return { depth: 'standard', cleanedText: requestText };
185
+ }
186
+
187
+ /**
188
+ * Run zero-config auto-initialization.
189
+ * Creates .wazir directories, detects host, scans project, writes config.
190
+ * No interactive prompts — everything is inferred.
191
+ *
192
+ * @param {string} projectRoot
193
+ * @param {object} [opts]
194
+ * @param {object} [opts.context] - Runtime context (availableTools, etc.)
195
+ * @param {boolean} [opts.force] - Force reinitialize even if config exists
196
+ * @returns {{ config: object, host: object, stack: object, filesCreated: string[] }}
197
+ */
198
+ export function autoInit(projectRoot, opts = {}) {
199
+ const wazirDir = path.join(projectRoot, '.wazir');
200
+ const configPath = path.join(wazirDir, 'state', 'config.json');
201
+
202
+ // If already initialized and not forced, return existing config
203
+ if (fs.existsSync(configPath) && !opts.force) {
204
+ const existing = JSON.parse(fs.readFileSync(configPath, 'utf8'));
205
+ return {
206
+ config: existing,
207
+ host: detectHost(),
208
+ stack: detectProjectStack(projectRoot),
209
+ filesCreated: [],
210
+ alreadyInitialized: true,
211
+ };
212
+ }
213
+
214
+ // Create directories
215
+ for (const dir of ['input', 'state', 'runs']) {
216
+ fs.mkdirSync(path.join(wazirDir, dir), { recursive: true });
217
+ }
218
+
219
+ const host = detectHost();
220
+ const stack = detectProjectStack(projectRoot);
221
+
222
+ // Detect context-mode MCP
223
+ const contextMode = { enabled: false, has_execute_file: false };
224
+ if (opts.context?.availableTools) {
225
+ const prefix = 'mcp__plugin_context-mode_context-mode__';
226
+ const hasExecute = opts.context.availableTools.includes(`${prefix}execute`);
227
+ const hasFetchAndIndex = opts.context.availableTools.includes(`${prefix}fetch_and_index`);
228
+ const hasSearch = opts.context.availableTools.includes(`${prefix}search`);
229
+ const hasExecuteFile = opts.context.availableTools.includes(`${prefix}execute_file`);
230
+ if (hasExecute && hasFetchAndIndex && hasSearch) {
231
+ contextMode.enabled = true;
232
+ contextMode.has_execute_file = hasExecuteFile;
233
+ }
234
+ } else {
235
+ const pluginDir = path.join(os.homedir(), '.claude', 'plugins', 'cache', 'context-mode');
236
+ if (fs.existsSync(pluginDir)) {
237
+ contextMode.enabled = true;
238
+ contextMode.has_execute_file = true;
239
+ }
240
+ }
241
+
242
+ // Sensible defaults — no questions
243
+ const config = {
244
+ model_mode: 'claude-only',
245
+ default_depth: 'standard',
246
+ default_intent: 'feature',
247
+ team_mode: 'sequential',
248
+ parallel_backend: 'none',
249
+ context_mode: contextMode,
250
+ detected_host: host.host,
251
+ detected_stack: stack,
252
+ auto_initialized: true,
253
+ };
254
+
255
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
256
+
257
+ const filesCreated = ['.wazir/input/', '.wazir/state/', '.wazir/runs/', '.wazir/state/config.json'];
258
+
259
+ return { config, host, stack, filesCreated, alreadyInitialized: false };
260
+ }
@@ -1,117 +1,136 @@
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|--interactive|--force]
8
+ *
9
+ * Default: --auto (zero-config, no prompts, infer everything)
10
+ * --interactive: legacy mode with @inquirer/prompts (may fail in non-TTY)
11
+ * --force: reinitialize even if already initialized
12
+ */
6
13
  export async function runInitCommand(parsed, context = {}) {
7
14
  const cwd = context.cwd ?? process.cwd();
8
15
  const wazirDir = path.join(cwd, '.wazir');
9
16
  const configPath = path.join(wazirDir, 'state', 'config.json');
17
+ const isForce = parsed.args.includes('--force');
18
+ const isInteractive = parsed.args.includes('--interactive');
10
19
 
11
- if (fs.existsSync(configPath) && !parsed.args.includes('--force')) {
20
+ // Already initialized check
21
+ if (fs.existsSync(configPath) && !isForce) {
12
22
  return {
13
23
  exitCode: 1,
14
24
  stderr: 'Pipeline already initialized. Use --force to reinitialize.\n',
15
25
  };
16
26
  }
17
27
 
28
+ // Interactive mode — legacy prompts (may fail in non-TTY environments like Claude Code)
29
+ if (isInteractive) {
30
+ return runInteractiveInit(parsed, context);
31
+ }
32
+
33
+ // Default: auto mode — zero-config
34
+ try {
35
+ const result = autoInit(cwd, { context, force: isForce });
36
+
37
+ if (result.alreadyInitialized && !isForce) {
38
+ return {
39
+ exitCode: 0,
40
+ stdout: `Already initialized. Host: ${result.host.host}, Stack: ${result.stack.language}\n`,
41
+ };
42
+ }
43
+
44
+ // Auto-export for detected host
45
+ let exportNote = '';
46
+ try {
47
+ const { buildHostExports } = await import('../export/compiler.js');
48
+ buildHostExports(cwd);
49
+ exportNote = ` Exports: generated for ${result.host.host}\n`;
50
+ } catch {
51
+ exportNote = ' Exports: skipped (run `wazir export build` manually)\n';
52
+ }
53
+
54
+ const lines = [
55
+ '',
56
+ 'Wazir initialized (zero-config).',
57
+ '',
58
+ ` Host: ${result.host.host} (${result.host.confidence} confidence)`,
59
+ ` Stack: ${result.stack.language}${result.stack.framework ? ` / ${result.stack.framework}` : ''}`,
60
+ ` Mode: ${result.config.model_mode}`,
61
+ ` Depth: ${result.config.default_depth}`,
62
+ exportNote,
63
+ 'Files created:',
64
+ ...result.filesCreated.map((f) => ` - ${f}`),
65
+ '',
66
+ 'Next: /wazir <what you want to build>',
67
+ '',
68
+ 'Power users: `wazir init --interactive` for manual config.',
69
+ 'Override: `wazir config set model_mode multi-tool`',
70
+ '',
71
+ ];
72
+
73
+ return { exitCode: 0, stdout: lines.join('\n') };
74
+ } catch (error) {
75
+ return { exitCode: 1, stderr: `Auto-init failed: ${error.message}\n` };
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Legacy interactive init with @inquirer/prompts.
81
+ * Kept for power users who want manual control.
82
+ * Will fail in non-TTY environments (Claude Code Bash tool).
83
+ */
84
+ async function runInteractiveInit(parsed, context = {}) {
85
+ const cwd = context.cwd ?? process.cwd();
86
+ const wazirDir = path.join(cwd, '.wazir');
87
+ const configPath = path.join(wazirDir, 'state', 'config.json');
88
+
18
89
  try {
19
- // Create directories
90
+ const { select } = await import('@inquirer/prompts');
91
+
20
92
  for (const dir of ['input', 'state', 'runs']) {
21
93
  fs.mkdirSync(path.join(wazirDir, dir), { recursive: true });
22
94
  }
23
95
 
24
- // Pipeline mode
25
96
  const modelMode = await select({
26
97
  message: 'How should Wazir run in this project?',
27
98
  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' },
99
+ { name: 'Single model (Recommended)', value: 'claude-only' },
100
+ { name: 'Multi-model (Haiku/Sonnet/Opus routing)', value: 'multi-model' },
101
+ { name: 'Multi-tool (current model + external reviewers)', value: 'multi-tool' },
31
102
  ],
32
103
  default: 'claude-only',
33
104
  });
34
105
 
35
- // Multi-tool tools (conditional)
36
106
  let multiToolTools = [];
37
107
  if (modelMode === 'multi-tool') {
38
108
  const toolChoice = await select({
39
- message: 'Which external tools should Wazir use for reviews?',
109
+ message: 'Which external tools for reviews?',
40
110
  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' },
111
+ { name: 'Codex', value: 'codex' },
112
+ { name: 'Gemini', value: 'gemini' },
113
+ { name: 'Both', value: 'both' },
44
114
  ],
45
115
  });
46
116
  multiToolTools = toolChoice === 'both' ? ['codex', 'gemini'] : [toolChoice];
47
117
  }
48
118
 
49
- // Codex model (conditional)
50
119
  let codexModel = null;
51
120
  if (multiToolTools.includes('codex')) {
52
121
  codexModel = await select({
53
- message: 'Which Codex model should Wazir use?',
122
+ message: 'Codex model?',
54
123
  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' },
124
+ { name: 'gpt-5.3-codex-spark (Recommended)', value: 'gpt-5.3-codex-spark' },
125
+ { name: 'gpt-5.4', value: 'gpt-5.4' },
57
126
  ],
58
127
  default: 'gpt-5.3-codex-spark',
59
128
  });
60
129
  }
61
130
 
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';
131
+ const host = detectHost();
132
+ const stack = detectProjectStack(cwd);
89
133
 
90
- const depthAllows = defaultDepth === 'standard' || defaultDepth === 'deep';
91
- const intentAllows = defaultIntent === 'feature' || defaultIntent === 'refactor';
92
-
93
- if (depthAllows && intentAllows) {
94
- const useTeams = await select({
95
- message: 'Would you like to use Agent Teams for parallel execution?',
96
- choices: [
97
- { name: 'No (Recommended) — sequential, predictable, lower cost', value: 'sequential' },
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
134
  const config = {
116
135
  model_mode: modelMode,
117
136
  ...(modelMode === 'multi-tool' && {
@@ -120,77 +139,18 @@ export async function runInitCommand(parsed, context = {}) {
120
139
  ...(codexModel && { codex: { model: codexModel } }),
121
140
  },
122
141
  }),
123
- default_depth: defaultDepth,
124
- default_intent: defaultIntent,
125
- team_mode: teamMode,
126
- parallel_backend: parallelBackend,
142
+ default_depth: 'standard',
143
+ default_intent: 'feature',
144
+ team_mode: 'sequential',
145
+ parallel_backend: 'none',
146
+ detected_host: host.host,
147
+ detected_stack: stack,
127
148
  };
128
149
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
129
150
 
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');
150
- }
151
-
152
- if (multiToolTools.includes('gemini')) {
153
- const content = [
154
- '# Wazir Pipeline',
155
- '',
156
- 'Agent protocols are at `~/.claude/agents/` (global).',
157
- '',
158
- '## Running the Pipeline',
159
- '1. Clarifier: read and follow `~/.claude/agents/clarifier.md` — tasks are in `.wazir/input/`',
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');
169
- }
170
-
171
- const lines = [
172
- '',
173
- '\u2705 Pipeline initialized!',
174
- '',
175
- ` Mode: ${modelMode}`,
176
- ` Depth: ${defaultDepth}`,
177
- ` Intent: ${defaultIntent}`,
178
- ` Teams: ${teamMode}`,
179
- '',
180
- 'Files created:',
181
- ...filesCreated.map((f) => ` - ${f}`),
182
- '',
183
- 'You can now use:',
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',
188
- '',
189
- ];
190
-
191
151
  return {
192
152
  exitCode: 0,
193
- stdout: lines.join('\n'),
153
+ stdout: `\nInitialized (${modelMode}). Host: ${host.host}. Next: /wazir <request>\n`,
194
154
  };
195
155
  } catch (error) {
196
156
  if (error.name === 'ExitPromptError') {
@@ -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
+ }