chati-dev 3.0.0 → 3.0.1

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 (65) hide show
  1. package/README.md +95 -24
  2. package/bin/chati.js +1 -1
  3. package/framework/agents/plan/phases.md +2 -2
  4. package/framework/config.yaml +2 -2
  5. package/framework/constitution.md +4 -4
  6. package/framework/context/governance.md +16 -3
  7. package/framework/context/root.md +3 -3
  8. package/framework/data/entity-registry.yaml +1 -1
  9. package/framework/domains/agents/detail.yaml +1 -1
  10. package/framework/domains/agents/phases.yaml +2 -2
  11. package/framework/domains/agents/tasks.yaml +2 -2
  12. package/framework/domains/constitution.yaml +2 -2
  13. package/framework/hooks/read-protection.js +1 -0
  14. package/framework/i18n/en.yaml +11 -4
  15. package/framework/i18n/es.yaml +11 -4
  16. package/framework/i18n/fr.yaml +11 -4
  17. package/framework/i18n/pt.yaml +11 -4
  18. package/framework/intelligence/context-engine.md +2 -2
  19. package/framework/intelligence/decision-engine.md +1 -1
  20. package/framework/patterns/elicitation-library.yaml +2 -2
  21. package/framework/tasks/brownfield-wu-dependency-scan.md +1 -1
  22. package/framework/tasks/brownfield-wu-migration-plan.md +4 -4
  23. package/framework/tasks/brownfield-wu-risk-assess.md +2 -2
  24. package/framework/tasks/detail-expand-prd.md +2 -2
  25. package/framework/tasks/greenfield-wu-report.md +1 -1
  26. package/framework/tasks/orchestrator-handoff.md +2 -2
  27. package/framework/tasks/orchestrator-resume.md +1 -1
  28. package/framework/tasks/qa-impl-performance-test.md +2 -2
  29. package/framework/tasks/qa-impl-sast-scan.md +6 -6
  30. package/framework/tasks/qa-planning-consolidate.md +1 -1
  31. package/framework/tasks/qa-planning-coverage-plan.md +9 -9
  32. package/framework/tasks/qa-planning-gate-define.md +6 -6
  33. package/framework/tasks/qa-planning-risk-matrix.md +1 -1
  34. package/framework/tasks/tasks-consolidate.md +5 -5
  35. package/package.json +1 -1
  36. package/scripts/changelog-generator.js +2 -2
  37. package/scripts/ide-sync.js +9 -4
  38. package/scripts/pr-review.js +5 -5
  39. package/scripts/semantic-lint.js +3 -3
  40. package/src/autonomy/build-state.js +1 -1
  41. package/src/autonomy/execution-profile.js +1 -1
  42. package/src/config/context-file-generator.js +20 -26
  43. package/src/config/ide-configs.js +6 -6
  44. package/src/gates/g2-qa-planning.js +1 -1
  45. package/src/gates/g4-qa-implementation.js +1 -1
  46. package/src/health/engine.js +7 -12
  47. package/src/installer/core.js +54 -16
  48. package/src/installer/templates.js +131 -2
  49. package/src/installer/validator.js +5 -3
  50. package/src/intelligence/registry-manager.js +2 -2
  51. package/src/preview/detector.js +1 -1
  52. package/src/terminal/adapters/claude-adapter.js +3 -13
  53. package/src/terminal/adapters/codex-adapter.js +3 -13
  54. package/src/terminal/adapters/copilot-adapter.js +3 -13
  55. package/src/terminal/adapters/gemini-adapter.js +3 -17
  56. package/src/terminal/cli-registry.js +20 -48
  57. package/src/terminal/prompt-builder.js +4 -18
  58. package/src/terminal/run-agent.js +2 -1
  59. package/src/terminal/spawner.js +27 -7
  60. package/src/terminal/wave-analyzer.js +1 -1
  61. package/src/utils/config-parser.js +97 -0
  62. package/src/wizard/feedback.js +1 -1
  63. package/src/wizard/i18n.js +11 -4
  64. package/src/wizard/index.js +22 -10
  65. package/src/wizard/questions.js +50 -2
@@ -6,8 +6,9 @@
6
6
  * Constitution Article XIV — Framework Registry Governance.
7
7
  */
8
8
 
9
- import { existsSync, readFileSync, readdirSync } from 'fs';
9
+ import { existsSync, readFileSync } from 'fs';
10
10
  import { join } from 'path';
11
+ import { isCommandAvailable, parseProviderConfig } from '../utils/config-parser.js';
11
12
 
12
13
  // ---------------------------------------------------------------------------
13
14
  // Check Result Types
@@ -50,19 +51,13 @@ export async function checkCliAvailability(projectDir) {
50
51
  return { name: 'cli-availability', status: 'warn', message: 'No config.yaml found', duration: Date.now() - start };
51
52
  }
52
53
 
53
- const raw = readFileSync(configPath, 'utf-8');
54
- const providers = ['claude', 'gemini', 'codex', 'copilot'];
54
+ const { enabled } = parseProviderConfig(projectDir);
55
55
  const missing = [];
56
56
 
57
- for (const provider of providers) {
58
- const enabledMatch = raw.match(new RegExp(`${provider}:[\\s\\S]*?enabled:\\s*true`, 'm'));
59
- if (enabledMatch) {
60
- const { execSync } = await import('child_process');
61
- try {
62
- execSync(`which ${provider}`, { stdio: 'ignore' });
63
- } catch {
64
- missing.push(provider);
65
- }
57
+ for (const provider of enabled) {
58
+ const available = await isCommandAvailable(provider);
59
+ if (!available) {
60
+ missing.push(provider);
66
61
  }
67
62
  }
68
63
 
@@ -3,7 +3,8 @@ import { join, dirname } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { IDE_CONFIGS } from '../config/ide-configs.js';
5
5
  import { generateClaudeMCPConfig } from '../config/mcp-configs.js';
6
- import { generateSessionYaml, generateConfigYaml, generateClaudeMd, generateClaudeLocalMd } from './templates.js';
6
+ import { generateSessionYaml, generateConfigYaml, generateClaudeMd, generateClaudeLocalMd, generateGeminiRouter, generateCopilotAgent } from './templates.js';
7
+ import { generateContextFiles } from '../config/context-file-generator.js';
7
8
  import { verifyManifest } from './manifest.js';
8
9
 
9
10
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -18,7 +19,7 @@ const FRAMEWORK_SOURCE = existsSync(BUNDLED_SOURCE) ? BUNDLED_SOURCE : MONOREPO_
18
19
  * Install Chati.dev framework into target directory
19
20
  */
20
21
  export async function installFramework(config) {
21
- const { targetDir, projectType, language, selectedIDEs, selectedMCPs, projectName, version } = config;
22
+ const { targetDir, projectType, language, selectedIDEs, selectedMCPs, projectName, version, llmProvider } = config;
22
23
 
23
24
  // 0. Verify framework signature (supply chain protection)
24
25
  const manifestPath = join(FRAMEWORK_SOURCE, 'manifest.json');
@@ -38,7 +39,7 @@ export async function installFramework(config) {
38
39
  createDir(join(targetDir, '.chati'));
39
40
  writeFileSync(
40
41
  join(targetDir, '.chati', 'session.yaml'),
41
- generateSessionYaml({ projectName, projectType, language, selectedIDEs, selectedMCPs }),
42
+ generateSessionYaml({ projectName, projectType, language, selectedIDEs, selectedMCPs, llmProvider }),
42
43
  'utf-8'
43
44
  );
44
45
 
@@ -91,7 +92,7 @@ export async function installFramework(config) {
91
92
  // Write config.yaml
92
93
  writeFileSync(
93
94
  join(frameworkDir, 'config.yaml'),
94
- generateConfigYaml({ version, projectType, language, selectedIDEs }),
95
+ generateConfigYaml({ version, projectType, language, selectedIDEs, llmProvider }),
95
96
  'utf-8'
96
97
  );
97
98
 
@@ -101,13 +102,13 @@ export async function installFramework(config) {
101
102
  }
102
103
 
103
104
  // 4. Copy context files to .claude/rules/chati/ (auto-loaded by Claude Code)
104
- const rulesDir = join(targetDir, '.claude', 'rules', 'chati');
105
- createDir(rulesDir);
106
105
  const contextFiles = ['root.md', 'governance.md', 'protocols.md', 'quality.md'];
106
+ const claudeRulesDir = join(targetDir, '.claude', 'rules', 'chati');
107
+ createDir(claudeRulesDir);
107
108
  for (const file of contextFiles) {
108
109
  const src = join(FRAMEWORK_SOURCE, 'context', file);
109
110
  if (existsSync(src)) {
110
- copyFileSync(src, join(rulesDir, file));
111
+ copyFileSync(src, join(claudeRulesDir, file));
111
112
  }
112
113
  }
113
114
 
@@ -124,6 +125,14 @@ export async function installFramework(config) {
124
125
  generateClaudeLocalMd(),
125
126
  'utf-8'
126
127
  );
128
+
129
+ // 7. Generate context files for non-Claude providers (GEMINI.md, AGENTS.md)
130
+ // Copilot CLI natively reads AGENTS.md + CLAUDE.md + GEMINI.md — no separate COPILOT.md needed.
131
+ const hasNonClaudeProvider = (llmProvider && llmProvider !== 'claude') ||
132
+ selectedIDEs.some(ide => ['gemini-cli', 'github-copilot'].includes(ide));
133
+ if (hasNonClaudeProvider) {
134
+ generateContextFiles(targetDir);
135
+ }
127
136
  }
128
137
 
129
138
  /**
@@ -157,12 +166,14 @@ function copyFrameworkFiles(destDir) {
157
166
  'templates/fullstack-architecture-tmpl.yaml',
158
167
  'templates/task-tmpl.yaml',
159
168
  'templates/qa-gate-tmpl.yaml',
169
+ 'templates/quick-brief-tmpl.yaml',
160
170
  // Workflows
161
171
  'workflows/greenfield-fullstack.yaml',
162
172
  'workflows/brownfield-fullstack.yaml',
163
173
  'workflows/brownfield-discovery.yaml',
164
174
  'workflows/brownfield-service.yaml',
165
175
  'workflows/brownfield-ui.yaml',
176
+ 'workflows/quick-flow.yaml',
166
177
  // Quality gates
167
178
  'quality-gates/planning-gate.md',
168
179
  'quality-gates/implementation-gate.md',
@@ -191,6 +202,7 @@ function copyFrameworkFiles(destDir) {
191
202
  'hooks/session-digest.js',
192
203
  'hooks/model-governance.js',
193
204
  'hooks/settings.json',
205
+ 'hooks/read-protection.js',
194
206
  // Domains (PRISM Context Engine)
195
207
  'domains/constitution.yaml',
196
208
  'domains/global.yaml',
@@ -212,6 +224,7 @@ function copyFrameworkFiles(destDir) {
212
224
  'domains/workflows/brownfield-discovery.yaml',
213
225
  'domains/workflows/brownfield-service.yaml',
214
226
  'domains/workflows/brownfield-ui.yaml',
227
+ 'domains/workflows/quick-flow.yaml',
215
228
  // i18n
216
229
  'i18n/en.yaml',
217
230
  'i18n/pt.yaml',
@@ -294,10 +307,32 @@ Pass through all context: session state, handoffs, artifacts, and user input.
294
307
  'utf-8'
295
308
  );
296
309
  }
310
+ } else if (ideKey === 'gemini-cli') {
311
+ // Gemini CLI: TOML command file (native format for /chati command)
312
+ writeFileSync(join(targetDir, '.gemini', 'commands', 'chati.toml'), generateGeminiRouter(), 'utf-8');
313
+ } else if (ideKey === 'github-copilot') {
314
+ // GitHub Copilot: agent file (.github/agents/chati.md) for @chati invocation
315
+ writeFileSync(join(targetDir, '.github', 'agents', 'chati.md'), generateCopilotAgent(), 'utf-8');
316
+
317
+ // Copilot instructions file (auto-loaded by Copilot CLI)
318
+ createDir(dirname(join(targetDir, config.rulesFile)));
319
+ writeFileSync(join(targetDir, config.rulesFile), generateProviderInstructions(config.name), 'utf-8');
297
320
  } else {
298
- // For other IDEs, create a rules file pointing to chati.dev/
299
- const rulesContent = `# Chati.dev System Rules
300
- # This file configures ${config.name} to work with Chati.dev
321
+ // VS Code, Cursor, AntiGravity generic rules file
322
+ if (config.rulesFile) {
323
+ createDir(dirname(join(targetDir, config.rulesFile)));
324
+ writeFileSync(join(targetDir, config.rulesFile), generateProviderInstructions(config.name), 'utf-8');
325
+ }
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Generate provider-agnostic instructions file content.
331
+ * Used for non-Claude IDEs (.github/copilot-instructions.md, .vscode/chati/rules.md, etc.)
332
+ */
333
+ function generateProviderInstructions(providerName) {
334
+ return `# Chati.dev System Rules
335
+ # This file configures ${providerName} to work with Chati.dev
301
336
 
302
337
  ## System Location
303
338
  All system content is in the \`chati.dev/\` directory.
@@ -310,7 +345,15 @@ The orchestrator is at \`chati.dev/orchestrator/chati.md\`.
310
345
  Read it to understand routing, session management, and agent activation.
311
346
 
312
347
  ## Constitution
313
- Governance rules are in \`chati.dev/constitution.md\` (17 Articles).
348
+ Governance rules are in \`chati.dev/constitution.md\` (19 Articles).
349
+
350
+ ## Pipeline
351
+ \`\`\`
352
+ DISCOVER: WU -> Brief
353
+ PLAN: Detail -> Architect -> UX -> Phases -> Tasks -> QA-Planning
354
+ BUILD: Dev -> QA-Implementation
355
+ DEPLOY: DevOps
356
+ \`\`\`
314
357
 
315
358
  ## Agents
316
359
  - DISCOVER: chati.dev/agents/discover/ (3 agents)
@@ -319,11 +362,6 @@ Governance rules are in \`chati.dev/constitution.md\` (17 Articles).
319
362
  - BUILD: chati.dev/agents/build/ (1 agent)
320
363
  - DEPLOY: chati.dev/agents/deploy/ (1 agent)
321
364
  `;
322
- if (config.rulesFile) {
323
- createDir(dirname(join(targetDir, config.rulesFile)));
324
- writeFileSync(join(targetDir, config.rulesFile), rulesContent, 'utf-8');
325
- }
326
- }
327
365
  }
328
366
 
329
367
  /**
@@ -4,7 +4,7 @@ import yaml from 'js-yaml';
4
4
  * Generate session.yaml content
5
5
  */
6
6
  export function generateSessionYaml(config) {
7
- const { projectName, projectType, language, selectedIDEs, selectedMCPs } = config;
7
+ const { projectName, projectType, language, selectedIDEs, selectedMCPs, llmProvider } = config;
8
8
 
9
9
  const session = {
10
10
  project: {
@@ -13,16 +13,19 @@ export function generateSessionYaml(config) {
13
13
  state: 'discover',
14
14
  },
15
15
  execution_mode: 'interactive',
16
+ execution_profile: 'guided',
16
17
  current_agent: '',
17
18
  language: language,
18
19
  ides: selectedIDEs,
19
20
  mcps: selectedMCPs,
21
+ providers_enabled: [llmProvider || 'claude'],
20
22
  user_level: 'auto',
21
23
  user_level_confidence: 0.0,
22
24
  agents: {},
23
25
  backlog: [],
24
26
  last_handoff: '',
25
27
  deviations: [],
28
+ profile_transitions: [],
26
29
  };
27
30
 
28
31
  // Initialize all 12 agent statuses
@@ -44,11 +47,56 @@ export function generateSessionYaml(config) {
44
47
  return yaml.dump(session, { lineWidth: -1, quotingType: '"', forceQuotes: false });
45
48
  }
46
49
 
50
+ /**
51
+ * Optimal model assignments per provider.
52
+ * Deep reasoning agents (architect, qa, dev, detail, brownfield-wu) get the top model.
53
+ * Lightweight agents (brief, phases, ux, greenfield-wu, devops, orchestrator) get the fast model.
54
+ */
55
+ const PROVIDER_MODEL_MAPS = {
56
+ claude: {
57
+ deep: 'opus', light: 'sonnet', minimal: 'haiku',
58
+ agents: {
59
+ orchestrator: 'sonnet', 'greenfield-wu': 'haiku', 'brownfield-wu': 'opus',
60
+ brief: 'sonnet', detail: 'opus', architect: 'opus', ux: 'sonnet',
61
+ phases: 'sonnet', tasks: 'sonnet', 'qa-planning': 'opus',
62
+ dev: 'opus', 'qa-implementation': 'opus', devops: 'sonnet',
63
+ },
64
+ },
65
+ gemini: {
66
+ deep: 'pro', light: 'flash', minimal: 'flash',
67
+ agents: {
68
+ orchestrator: 'flash', 'greenfield-wu': 'flash', 'brownfield-wu': 'pro',
69
+ brief: 'flash', detail: 'pro', architect: 'pro', ux: 'flash',
70
+ phases: 'flash', tasks: 'flash', 'qa-planning': 'pro',
71
+ dev: 'pro', 'qa-implementation': 'pro', devops: 'flash',
72
+ },
73
+ },
74
+ codex: {
75
+ deep: 'codex', light: 'mini', minimal: 'mini',
76
+ agents: {
77
+ orchestrator: 'mini', 'greenfield-wu': 'mini', 'brownfield-wu': 'codex',
78
+ brief: 'mini', detail: 'codex', architect: 'codex', ux: 'mini',
79
+ phases: 'mini', tasks: 'mini', 'qa-planning': 'codex',
80
+ dev: 'codex', 'qa-implementation': 'codex', devops: 'mini',
81
+ },
82
+ },
83
+ copilot: {
84
+ deep: 'claude-sonnet', light: 'claude-sonnet', minimal: 'gpt-5',
85
+ agents: {
86
+ orchestrator: 'claude-sonnet', 'greenfield-wu': 'gpt-5', 'brownfield-wu': 'claude-sonnet',
87
+ brief: 'claude-sonnet', detail: 'claude-sonnet', architect: 'claude-sonnet', ux: 'claude-sonnet',
88
+ phases: 'claude-sonnet', tasks: 'claude-sonnet', 'qa-planning': 'claude-sonnet',
89
+ dev: 'claude-sonnet', 'qa-implementation': 'claude-sonnet', devops: 'claude-sonnet',
90
+ },
91
+ },
92
+ };
93
+
47
94
  /**
48
95
  * Generate config.yaml content
49
96
  */
50
97
  export function generateConfigYaml(config) {
51
- const { version, projectType, language, selectedIDEs } = config;
98
+ const { version, projectType, language, selectedIDEs, llmProvider } = config;
99
+ const provider = llmProvider || 'claude';
52
100
 
53
101
  const configData = {
54
102
  version: version,
@@ -58,8 +106,25 @@ export function generateConfigYaml(config) {
58
106
  project_type: projectType,
59
107
  language: language,
60
108
  ides: selectedIDEs,
109
+ providers: {
110
+ claude: { enabled: true, primary: provider === 'claude' },
111
+ gemini: { enabled: provider === 'gemini' || selectedIDEs.includes('gemini-cli'), model_default: 'pro', primary: provider === 'gemini' },
112
+ codex: { enabled: provider === 'codex', model_default: 'codex', primary: provider === 'codex' },
113
+ copilot: { enabled: provider === 'copilot' || selectedIDEs.includes('github-copilot'), primary: provider === 'copilot' },
114
+ },
61
115
  };
62
116
 
117
+ // Add agent_overrides with optimal model map for the selected provider
118
+ if (provider !== 'claude') {
119
+ const modelMap = PROVIDER_MODEL_MAPS[provider];
120
+ if (modelMap) {
121
+ configData.agent_overrides = {};
122
+ for (const [agent, model] of Object.entries(modelMap.agents)) {
123
+ configData.agent_overrides[agent] = { provider, model };
124
+ }
125
+ }
126
+ }
127
+
63
128
  return yaml.dump(configData, { lineWidth: -1, quotingType: '"', forceQuotes: false });
64
129
  }
65
130
 
@@ -81,6 +146,70 @@ Type \`/chati\` to start.
81
146
  `;
82
147
  }
83
148
 
149
+ /**
150
+ * Generate Gemini CLI TOML command (.gemini/commands/chati.toml)
151
+ *
152
+ * Gemini CLI custom commands use TOML format with `description` and `prompt` fields.
153
+ * Supports {{args}} for user arguments and @{file} for file injection.
154
+ */
155
+ export function generateGeminiRouter() {
156
+ return `description = "Activate Chati.dev orchestrator"
157
+ prompt = """
158
+ CRITICAL — Language Override:
159
+ Read \`.chati/session.yaml\` field \`language\` BEFORE anything else.
160
+ ALL responses MUST be in this language (en, pt, es, fr).
161
+ If session.yaml does not exist or has no language field, default to English.
162
+
163
+ Read and execute the full orchestrator at \`chati.dev/orchestrator/chati.md\`.
164
+
165
+ Context to load:
166
+ - \`.chati/session.yaml\` (session state — includes language)
167
+ - \`chati.dev/artifacts/handoffs/\` (latest handoff)
168
+ - \`chati.dev/config.yaml\` (version info)
169
+
170
+ User input: {{args}}
171
+ """
172
+ `;
173
+ }
174
+
175
+ /**
176
+ * Generate GitHub Copilot agent file (.github/agents/chati.md)
177
+ *
178
+ * Copilot CLI uses .github/agents/*.md for custom agents.
179
+ * The user invokes with @chati in the Copilot CLI.
180
+ */
181
+ export function generateCopilotAgent() {
182
+ return `# Chati.dev Orchestrator
183
+
184
+ You are the Chati.dev orchestrator agent for GitHub Copilot CLI.
185
+
186
+ ## CRITICAL — Language Override
187
+
188
+ Read \`.chati/session.yaml\` field \`language\` BEFORE anything else.
189
+ ALL responses MUST be in this language. This overrides any global setting.
190
+
191
+ | Value | Language |
192
+ |-------|----------|
193
+ | \`en\` | English |
194
+ | \`pt\` | Portugues |
195
+ | \`es\` | Espanol |
196
+ | \`fr\` | Francais |
197
+
198
+ If session.yaml does not exist or has no language field, default to English.
199
+
200
+ ## Load
201
+
202
+ Read and execute the full orchestrator at \`chati.dev/orchestrator/chati.md\`.
203
+
204
+ Pass through all context: session state, handoffs, artifacts, and user input.
205
+
206
+ **Context to pass:**
207
+ - \`.chati/session.yaml\` (session state — includes language)
208
+ - \`chati.dev/artifacts/handoffs/\` (latest handoff)
209
+ - \`chati.dev/config.yaml\` (version info)
210
+ `;
211
+ }
212
+
84
213
  /**
85
214
  * Generate CLAUDE.local.md content (runtime state — auto-gitignored, never committed)
86
215
  */
@@ -63,7 +63,7 @@ export async function validateInstallation(targetDir) {
63
63
  if (existsSync(constitutionPath)) {
64
64
  const content = readFileSync(constitutionPath, 'utf-8');
65
65
  const articleCount = (content.match(/^## Article/gm) || []).length;
66
- results.constitution.pass = articleCount >= 17;
66
+ results.constitution.pass = articleCount >= 19;
67
67
  results.constitution.details.push({ articleCount });
68
68
  }
69
69
  results.total += 1;
@@ -94,6 +94,7 @@ export async function validateInstallation(targetDir) {
94
94
  const workflowFiles = [
95
95
  'greenfield-fullstack.yaml', 'brownfield-fullstack.yaml',
96
96
  'brownfield-discovery.yaml', 'brownfield-service.yaml', 'brownfield-ui.yaml',
97
+ 'quick-flow.yaml',
97
98
  ];
98
99
  let workflowCount = 0;
99
100
  for (const file of workflowFiles) {
@@ -101,7 +102,7 @@ export async function validateInstallation(targetDir) {
101
102
  workflowCount++;
102
103
  }
103
104
  }
104
- results.workflows.pass = workflowCount === 5;
105
+ results.workflows.pass = workflowCount === 6;
105
106
  results.total += 1;
106
107
  if (results.workflows.pass) results.passed += 1;
107
108
 
@@ -109,6 +110,7 @@ export async function validateInstallation(targetDir) {
109
110
  const templateFiles = [
110
111
  'prd-tmpl.yaml', 'brownfield-prd-tmpl.yaml',
111
112
  'fullstack-architecture-tmpl.yaml', 'task-tmpl.yaml', 'qa-gate-tmpl.yaml',
113
+ 'quick-brief-tmpl.yaml',
112
114
  ];
113
115
  let templateCount = 0;
114
116
  for (const file of templateFiles) {
@@ -116,7 +118,7 @@ export async function validateInstallation(targetDir) {
116
118
  templateCount++;
117
119
  }
118
120
  }
119
- results.templates.pass = templateCount === 5;
121
+ results.templates.pass = templateCount === 6;
120
122
  results.total += 1;
121
123
  if (results.templates.pass) results.passed += 1;
122
124
 
@@ -131,8 +131,8 @@ export function runHealthCheck(targetDir) {
131
131
  if (existsSync(constitutionPath)) {
132
132
  const content = readFileSync(constitutionPath, 'utf-8');
133
133
  const articleCount = (content.match(/^## Article/gm) || []).length;
134
- checks.constitution.pass = articleCount >= 17;
135
- checks.constitution.details = `${articleCount}/17 articles`;
134
+ checks.constitution.pass = articleCount >= 19;
135
+ checks.constitution.details = `${articleCount}/19 articles`;
136
136
  } else {
137
137
  checks.constitution.details = 'Not found';
138
138
  }
@@ -191,7 +191,7 @@ export function detectProjectKind(projectDir) {
191
191
  * @returns {{ command: string, args: string[], description: string, framework: string, defaultPort: number }}
192
192
  */
193
193
  function resolveFrameworkCommand(projectDir, detector) {
194
- const { framework, defaultPort, kind } = detector;
194
+ const { framework, defaultPort } = detector;
195
195
 
196
196
  // Django has its own command
197
197
  if (framework === 'django') {
@@ -18,26 +18,16 @@
18
18
  * @returns {{ command: string, args: string[], stdinPrompt: string|null }}
19
19
  */
20
20
  export function buildCommand(config, provider) {
21
- const args = ['--print', '--dangerously-skip-permissions'];
21
+ const args = [...provider.baseArgs];
22
22
 
23
23
  if (config.model) {
24
24
  const resolvedModel = provider.modelMap[config.model] || config.model;
25
- args.push('--model', resolvedModel);
25
+ args.push(provider.modelFlag, resolvedModel);
26
26
  }
27
27
 
28
28
  return {
29
- command: 'claude',
29
+ command: provider.command,
30
30
  args,
31
31
  stdinPrompt: config.prompt || null,
32
32
  };
33
33
  }
34
-
35
- /**
36
- * Build environment variables specific to Claude Code.
37
- *
38
- * @param {SpawnConfig} config
39
- * @returns {Record<string, string>}
40
- */
41
- export function buildEnv(config) {
42
- return {};
43
- }
@@ -13,29 +13,19 @@
13
13
  * @returns {{ command: string, args: string[], stdinPrompt: string|null }}
14
14
  */
15
15
  export function buildCommand(config, provider) {
16
- const args = ['exec'];
16
+ const args = [...provider.baseArgs];
17
17
 
18
18
  if (config.model) {
19
19
  const resolvedModel = provider.modelMap[config.model] || config.model;
20
- args.push('-m', resolvedModel);
20
+ args.push(provider.modelFlag, resolvedModel);
21
21
  }
22
22
 
23
23
  // Codex exec reads prompt from stdin when `-` is passed
24
24
  args.push('-');
25
25
 
26
26
  return {
27
- command: 'codex',
27
+ command: provider.command,
28
28
  args,
29
29
  stdinPrompt: config.prompt || null,
30
30
  };
31
31
  }
32
-
33
- /**
34
- * Build environment variables specific to Codex CLI.
35
- *
36
- * @param {import('../spawner.js').SpawnConfig} config
37
- * @returns {Record<string, string>}
38
- */
39
- export function buildEnv(config) {
40
- return {};
41
- }
@@ -13,26 +13,16 @@
13
13
  * @returns {{ command: string, args: string[], stdinPrompt: string|null }}
14
14
  */
15
15
  export function buildCommand(config, provider) {
16
- const args = ['-p'];
16
+ const args = [...provider.baseArgs];
17
17
 
18
18
  if (config.model) {
19
19
  const resolvedModel = provider.modelMap[config.model] || config.model;
20
- args.push('--model', resolvedModel);
20
+ args.push(provider.modelFlag, resolvedModel);
21
21
  }
22
22
 
23
23
  return {
24
- command: 'copilot',
24
+ command: provider.command,
25
25
  args,
26
26
  stdinPrompt: config.prompt || null,
27
27
  };
28
28
  }
29
-
30
- /**
31
- * Build environment variables specific to Copilot CLI.
32
- *
33
- * @param {import('../spawner.js').SpawnConfig} config
34
- * @returns {Record<string, string>}
35
- */
36
- export function buildEnv(config) {
37
- return {};
38
- }
@@ -13,30 +13,16 @@
13
13
  * @returns {{ command: string, args: string[], stdinPrompt: string|null }}
14
14
  */
15
15
  export function buildCommand(config, provider) {
16
- const args = [];
16
+ const args = [...provider.baseArgs];
17
17
 
18
18
  if (config.model) {
19
19
  const resolvedModel = provider.modelMap[config.model] || config.model;
20
- args.push('--model', resolvedModel);
20
+ args.push(provider.modelFlag, resolvedModel);
21
21
  }
22
22
 
23
- // Gemini CLI uses --prompt for non-interactive mode
24
- // When stdin is piped, Gemini reads from stdin automatically
25
- args.push('--prompt');
26
-
27
23
  return {
28
- command: 'gemini',
24
+ command: provider.command,
29
25
  args,
30
26
  stdinPrompt: config.prompt || null,
31
27
  };
32
28
  }
33
-
34
- /**
35
- * Build environment variables specific to Gemini CLI.
36
- *
37
- * @param {import('../spawner.js').SpawnConfig} config
38
- * @returns {Record<string, string>}
39
- */
40
- export function buildEnv(config) {
41
- return {};
42
- }