chati-dev 1.0.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 (63) hide show
  1. package/assets/logo.txt +6 -0
  2. package/bin/chati.js +175 -0
  3. package/framework/agents/build/dev.md +342 -0
  4. package/framework/agents/clarity/architect.md +263 -0
  5. package/framework/agents/clarity/brief.md +277 -0
  6. package/framework/agents/clarity/brownfield-wu.md +288 -0
  7. package/framework/agents/clarity/detail.md +274 -0
  8. package/framework/agents/clarity/greenfield-wu.md +231 -0
  9. package/framework/agents/clarity/phases.md +272 -0
  10. package/framework/agents/clarity/tasks.md +279 -0
  11. package/framework/agents/clarity/ux.md +293 -0
  12. package/framework/agents/deploy/devops.md +321 -0
  13. package/framework/agents/quality/qa-implementation.md +310 -0
  14. package/framework/agents/quality/qa-planning.md +289 -0
  15. package/framework/config.yaml +8 -0
  16. package/framework/constitution.md +238 -0
  17. package/framework/frameworks/decision-heuristics.yaml +64 -0
  18. package/framework/frameworks/quality-dimensions.yaml +59 -0
  19. package/framework/i18n/en.yaml +78 -0
  20. package/framework/i18n/es.yaml +78 -0
  21. package/framework/i18n/fr.yaml +78 -0
  22. package/framework/i18n/pt.yaml +78 -0
  23. package/framework/intelligence/confidence.yaml +42 -0
  24. package/framework/intelligence/gotchas.yaml +51 -0
  25. package/framework/intelligence/patterns.yaml +32 -0
  26. package/framework/migrations/v1.0-to-v1.1.yaml +48 -0
  27. package/framework/orchestrator/chati.md +333 -0
  28. package/framework/patterns/elicitation.md +137 -0
  29. package/framework/quality-gates/implementation-gate.md +64 -0
  30. package/framework/quality-gates/planning-gate.md +52 -0
  31. package/framework/schemas/config.schema.json +42 -0
  32. package/framework/schemas/session.schema.json +103 -0
  33. package/framework/schemas/task.schema.json +71 -0
  34. package/framework/templates/brownfield-prd-tmpl.yaml +103 -0
  35. package/framework/templates/fullstack-architecture-tmpl.yaml +101 -0
  36. package/framework/templates/prd-tmpl.yaml +94 -0
  37. package/framework/templates/qa-gate-tmpl.yaml +96 -0
  38. package/framework/templates/task-tmpl.yaml +85 -0
  39. package/framework/workflows/brownfield-discovery.yaml +75 -0
  40. package/framework/workflows/brownfield-fullstack.yaml +104 -0
  41. package/framework/workflows/brownfield-service.yaml +81 -0
  42. package/framework/workflows/brownfield-ui.yaml +87 -0
  43. package/framework/workflows/greenfield-fullstack.yaml +108 -0
  44. package/package.json +60 -0
  45. package/scripts/bundle-framework.js +58 -0
  46. package/src/config/ide-configs.js +80 -0
  47. package/src/config/mcp-configs.js +136 -0
  48. package/src/dashboard/data-reader.js +99 -0
  49. package/src/dashboard/layout.js +161 -0
  50. package/src/dashboard/renderer.js +104 -0
  51. package/src/installer/core.js +221 -0
  52. package/src/installer/templates.js +97 -0
  53. package/src/installer/validator.js +114 -0
  54. package/src/upgrade/backup.js +107 -0
  55. package/src/upgrade/checker.js +105 -0
  56. package/src/upgrade/migrator.js +171 -0
  57. package/src/utils/colors.js +18 -0
  58. package/src/utils/detector.js +51 -0
  59. package/src/utils/logger.js +41 -0
  60. package/src/wizard/feedback.js +76 -0
  61. package/src/wizard/i18n.js +168 -0
  62. package/src/wizard/index.js +107 -0
  63. package/src/wizard/questions.js +169 -0
@@ -0,0 +1,171 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
2
+ import { join } from 'path';
3
+ import yaml from 'js-yaml';
4
+ import semver from 'semver';
5
+
6
+ /**
7
+ * Find applicable migrations between two versions
8
+ */
9
+ export function findMigrations(targetDir, fromVersion, toVersion) {
10
+ const migrationsDir = join(targetDir, 'chati.dev', 'migrations');
11
+ if (!existsSync(migrationsDir)) return [];
12
+
13
+ const files = readdirSync(migrationsDir)
14
+ .filter(f => f.endsWith('.yaml'))
15
+ .sort();
16
+
17
+ const applicable = [];
18
+
19
+ for (const file of files) {
20
+ try {
21
+ const content = yaml.load(readFileSync(join(migrationsDir, file), 'utf-8'));
22
+ const migration = content?.migration;
23
+
24
+ if (!migration?.from || !migration?.to) continue;
25
+
26
+ // Check if this migration is in the upgrade path
27
+ if (
28
+ semver.gte(migration.from, fromVersion) &&
29
+ semver.lte(migration.to, toVersion)
30
+ ) {
31
+ applicable.push({
32
+ file,
33
+ from: migration.from,
34
+ to: migration.to,
35
+ description: migration.description || '',
36
+ breaking: migration.breaking || false,
37
+ steps: content.steps || [],
38
+ rollback: content.rollback || [],
39
+ });
40
+ }
41
+ } catch {
42
+ // Skip invalid migration files
43
+ }
44
+ }
45
+
46
+ // Sort by target version
47
+ applicable.sort((a, b) => semver.compare(a.to, b.to));
48
+
49
+ return applicable;
50
+ }
51
+
52
+ /**
53
+ * Execute a single migration step
54
+ */
55
+ function executeMigrationStep(targetDir, step) {
56
+ switch (step.type) {
57
+ case 'create_directory': {
58
+ const dir = join(targetDir, step.path);
59
+ if (!existsSync(dir) || !step.idempotent) {
60
+ mkdirSync(dir, { recursive: true });
61
+ }
62
+ break;
63
+ }
64
+
65
+ case 'create_file': {
66
+ const filePath = join(targetDir, step.path);
67
+ if (!existsSync(filePath) || !step.skip_if_exists) {
68
+ mkdirSync(join(targetDir, step.path, '..'), { recursive: true });
69
+ writeFileSync(filePath, step.content || '', 'utf-8');
70
+ }
71
+ break;
72
+ }
73
+
74
+ case 'update_yaml': {
75
+ const filePath = join(targetDir, step.path);
76
+ if (!existsSync(filePath)) break;
77
+
78
+ const content = yaml.load(readFileSync(filePath, 'utf-8')) || {};
79
+
80
+ if (step.operation === 'add_field') {
81
+ const keys = step.field.split('.');
82
+ let obj = content;
83
+ for (let i = 0; i < keys.length - 1; i++) {
84
+ if (!obj[keys[i]]) obj[keys[i]] = {};
85
+ obj = obj[keys[i]];
86
+ }
87
+ const lastKey = keys[keys.length - 1];
88
+ if (!step.skip_if_exists || !(lastKey in obj)) {
89
+ obj[lastKey] = step.default;
90
+ }
91
+ } else if (step.operation === 'set') {
92
+ const keys = step.field.split('.');
93
+ let obj = content;
94
+ for (let i = 0; i < keys.length - 1; i++) {
95
+ if (!obj[keys[i]]) obj[keys[i]] = {};
96
+ obj = obj[keys[i]];
97
+ }
98
+ obj[keys[keys.length - 1]] = step.value;
99
+ }
100
+
101
+ writeFileSync(filePath, yaml.dump(content, { lineWidth: -1 }), 'utf-8');
102
+ break;
103
+ }
104
+
105
+ case 'delete_directory': {
106
+ const dir = join(targetDir, step.path);
107
+ if (existsSync(dir)) {
108
+ if (step.only_if_empty) {
109
+ const files = readdirSync(dir);
110
+ if (files.length === 0) {
111
+ rmSync(dir, { recursive: true });
112
+ }
113
+ } else {
114
+ rmSync(dir, { recursive: true });
115
+ }
116
+ }
117
+ break;
118
+ }
119
+
120
+ default:
121
+ // Unknown step type - skip
122
+ break;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Run all applicable migrations sequentially
128
+ */
129
+ export async function runMigrations(targetDir, fromVersion, toVersion) {
130
+ const migrations = findMigrations(targetDir, fromVersion, toVersion);
131
+
132
+ if (migrations.length === 0) {
133
+ return { success: true, migrationsRun: 0 };
134
+ }
135
+
136
+ const results = [];
137
+
138
+ for (const migration of migrations) {
139
+ try {
140
+ for (const step of migration.steps) {
141
+ executeMigrationStep(targetDir, step);
142
+ }
143
+ results.push({ file: migration.file, success: true });
144
+ } catch (err) {
145
+ results.push({ file: migration.file, success: false, error: err.message });
146
+
147
+ // Attempt rollback for this migration
148
+ try {
149
+ for (const step of migration.rollback) {
150
+ executeMigrationStep(targetDir, step);
151
+ }
152
+ } catch {
153
+ // Rollback failed - return error
154
+ }
155
+
156
+ return {
157
+ success: false,
158
+ migrationsRun: results.filter(r => r.success).length,
159
+ failedAt: migration.file,
160
+ error: err.message,
161
+ results,
162
+ };
163
+ }
164
+ }
165
+
166
+ return {
167
+ success: true,
168
+ migrationsRun: migrations.length,
169
+ results,
170
+ };
171
+ }
@@ -0,0 +1,18 @@
1
+ import chalk from 'chalk';
2
+
3
+ // chati.dev brand color: steel blue
4
+ export const brand = chalk.hex('#6BA4DC');
5
+ export const dim = chalk.dim;
6
+ export const bold = chalk.bold;
7
+ export const green = chalk.green;
8
+ export const red = chalk.red;
9
+ export const yellow = chalk.yellow;
10
+ export const cyan = chalk.cyan;
11
+ export const gray = chalk.gray;
12
+ export const white = chalk.white;
13
+
14
+ export const success = chalk.green;
15
+ export const error = chalk.red;
16
+ export const warning = chalk.yellow;
17
+ export const info = chalk.hex('#6BA4DC');
18
+ export const muted = chalk.dim;
@@ -0,0 +1,51 @@
1
+ import { existsSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ /**
5
+ * Auto-detect project type (greenfield vs brownfield)
6
+ * Brownfield signals: package.json, src/, .git/, etc.
7
+ */
8
+ export function detectProjectType(targetDir) {
9
+ const signals = {
10
+ packageJson: existsSync(join(targetDir, 'package.json')),
11
+ srcDir: existsSync(join(targetDir, 'src')),
12
+ gitDir: existsSync(join(targetDir, '.git')),
13
+ composerJson: existsSync(join(targetDir, 'composer.json')),
14
+ requirementsTxt: existsSync(join(targetDir, 'requirements.txt')),
15
+ goMod: existsSync(join(targetDir, 'go.mod')),
16
+ cargoToml: existsSync(join(targetDir, 'Cargo.toml')),
17
+ pomXml: existsSync(join(targetDir, 'pom.xml')),
18
+ };
19
+
20
+ const brownfieldSignals = Object.values(signals).filter(Boolean).length;
21
+
22
+ return {
23
+ suggestion: brownfieldSignals >= 2 ? 'brownfield' : 'greenfield',
24
+ signals,
25
+ confidence: brownfieldSignals >= 2 ? 'high' : brownfieldSignals === 1 ? 'medium' : 'low',
26
+ };
27
+ }
28
+
29
+ /**
30
+ * Detect currently installed IDEs by checking common paths/commands
31
+ */
32
+ export function detectInstalledIDEs() {
33
+ const detected = [];
34
+
35
+ // Claude Code - check if claude CLI exists
36
+ if (existsSync('/usr/local/bin/claude') || existsSync(join(process.env.HOME || '', '.claude'))) {
37
+ detected.push('claude-code');
38
+ }
39
+
40
+ // VS Code - check .vscode or code command
41
+ if (existsSync(join(process.env.HOME || '', '.vscode'))) {
42
+ detected.push('vscode');
43
+ }
44
+
45
+ // Cursor
46
+ if (existsSync(join(process.env.HOME || '', '.cursor'))) {
47
+ detected.push('cursor');
48
+ }
49
+
50
+ return detected;
51
+ }
@@ -0,0 +1,41 @@
1
+ import { success, error, warning, info, muted, brand, dim } from './colors.js';
2
+
3
+ export function logStep(message) {
4
+ console.log(` ${success('✓')} ${message}`);
5
+ }
6
+
7
+ export function logError(message) {
8
+ console.log(` ${error('✗')} ${message}`);
9
+ }
10
+
11
+ export function logWarning(message) {
12
+ console.log(` ${warning('⚠')} ${message}`);
13
+ }
14
+
15
+ export function logInfo(message) {
16
+ console.log(` ${info('ℹ')} ${message}`);
17
+ }
18
+
19
+ export function logMuted(message) {
20
+ console.log(` ${muted(message)}`);
21
+ }
22
+
23
+ export function logBanner(logoText, version) {
24
+ const lines = logoText.trim().split('\n');
25
+ for (const line of lines) {
26
+ console.log(brand(line));
27
+ }
28
+ console.log(brand(`chati.dev v${version}`));
29
+ console.log(dim('AI-Powered Multi-Agent Development Framework'));
30
+ console.log(dim('═'.repeat(55)));
31
+ console.log();
32
+ }
33
+
34
+ export function logSection(title) {
35
+ console.log();
36
+ console.log(` ${brand('──')} ${title} ${brand('─'.repeat(Math.max(0, 45 - title.length)))}`);
37
+ }
38
+
39
+ export function logResult(label, value) {
40
+ console.log(` ${muted(label + ':')} ${value}`);
41
+ }
@@ -0,0 +1,76 @@
1
+ import ora from 'ora';
2
+ import { brand, success, dim } from '../utils/colors.js';
3
+
4
+ /**
5
+ * Create a spinner with chati.dev branding
6
+ */
7
+ export function createSpinner(text) {
8
+ return ora({
9
+ text,
10
+ color: 'cyan',
11
+ spinner: 'dots',
12
+ });
13
+ }
14
+
15
+ /**
16
+ * Show installation progress step
17
+ */
18
+ export function showStep(message) {
19
+ console.log(` ${success('✓')} ${message}`);
20
+ }
21
+
22
+ /**
23
+ * Show validation step
24
+ */
25
+ export function showValidation(message) {
26
+ console.log(` ${success('✓')} ${message}`);
27
+ }
28
+
29
+ /**
30
+ * Show warning
31
+ */
32
+ export function showWarning(message) {
33
+ console.log(` ${brand('⚠')} ${message}`);
34
+ }
35
+
36
+ /**
37
+ * Show error
38
+ */
39
+ export function showError(message) {
40
+ console.log(` ${'✗'} ${message}`);
41
+ }
42
+
43
+ /**
44
+ * Show quick start guide
45
+ */
46
+ export function showQuickStart(title, steps) {
47
+ console.log();
48
+ console.log(`${brand(title)}:`);
49
+ steps.forEach((step, i) => {
50
+ console.log(` ${dim(`${i + 1}.`)} ${step}`);
51
+ });
52
+ console.log();
53
+ console.log(`${dim('Documentation:')} chati.dev/constitution.md`);
54
+ console.log(`${dim('Session:')} .chati/session.yaml`);
55
+ }
56
+
57
+ /**
58
+ * Show summary table
59
+ */
60
+ export function showSummary(data) {
61
+ console.log();
62
+ for (const [label, value] of Object.entries(data)) {
63
+ const paddedLabel = label.padEnd(12);
64
+ console.log(` ${dim(paddedLabel)} ${value}`);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Show checklist of items to install
70
+ */
71
+ export function showChecklist(items) {
72
+ console.log();
73
+ for (const item of items) {
74
+ console.log(` ${success('✓')} ${item}`);
75
+ }
76
+ }
@@ -0,0 +1,168 @@
1
+ import { readFileSync, existsSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import yaml from 'js-yaml';
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+
8
+ // Bundled fallback strings (English) for when i18n files aren't available
9
+ const FALLBACK_EN = {
10
+ installer: {
11
+ welcome: 'Welcome to chati.dev',
12
+ select_language: 'Select your language:',
13
+ project_type: 'What type of project are you setting up?',
14
+ greenfield: 'Greenfield (new project from scratch)',
15
+ brownfield: 'Brownfield (existing project integration)',
16
+ detected_brownfield: 'Detected package.json and src/ in current directory',
17
+ suggestion_brownfield: 'Suggestion: Brownfield',
18
+ select_ides: 'Select IDE(s) for chati.dev configuration:',
19
+ select_mcps: 'Select MCPs to install (project-level):',
20
+ confirmation_title: 'Installation Summary',
21
+ project_label: 'Project',
22
+ language_label: 'Language',
23
+ ides_label: 'IDEs',
24
+ mcps_label: 'MCPs',
25
+ will_install: 'Will install:',
26
+ agents_count: '13 agent definitions (CLARITY, BUILD, DEPLOY phases)',
27
+ workflows_count: '5 workflow blueprints',
28
+ templates_count: '5 templates (PRD, Brownfield PRD, Architecture, Task, QA Gate)',
29
+ constitution: 'Constitution (10 Articles + Preamble)',
30
+ session_mgmt: 'Session management system',
31
+ quality_gates: 'Quality gates (4-tier validation)',
32
+ proceed: 'Proceed with installation?',
33
+ installing: 'Installing chati.dev...',
34
+ created_chati: 'Created .chati/ session directory',
35
+ created_framework: 'Created chati.dev/ framework directory (agents, templates, workflows)',
36
+ created_commands: 'Created .claude/commands/ (thin router)',
37
+ installed_constitution: 'Installed Constitution (Articles I-X)',
38
+ created_session: 'Created session.yaml schema',
39
+ created_claude_md: 'Created CLAUDE.md',
40
+ configured_mcps: 'Configured MCPs:',
41
+ validating: 'Validating installation...',
42
+ agents_valid: 'All 13 agents implement 8 protocols',
43
+ handoff_ok: 'Handoff protocol: OK',
44
+ validation_ok: 'Self-validation criteria: OK',
45
+ constitution_ok: 'Constitution: 10 articles verified',
46
+ session_ok: 'Session schema: valid',
47
+ success: 'chati.dev installed successfully!',
48
+ quick_start_title: 'Quick Start',
49
+ quick_start_1: 'Open your IDE',
50
+ quick_start_2: 'Type: /chati',
51
+ quick_start_3: 'The orchestrator will guide you through the process',
52
+ },
53
+ agents: {
54
+ starting: 'Starting agent: {agent}',
55
+ completed: 'Agent {agent} completed with score {score}%',
56
+ refining: 'Refining artifacts for consistency...',
57
+ running_validations: 'Running additional validations...',
58
+ blocked: 'Blocker detected: {blocker}',
59
+ },
60
+ options: {
61
+ continue: 'Continue with {agent} (Recommended)',
62
+ review: 'Review last result',
63
+ status: 'View full status',
64
+ enter_prompt: 'Enter the number or describe what you want to do:',
65
+ },
66
+ status: {
67
+ project: 'Project',
68
+ type: 'Type',
69
+ phase: 'Phase',
70
+ mode: 'Mode',
71
+ language: 'Language',
72
+ ide: 'IDE',
73
+ current_agent: 'Current Agent',
74
+ last_handoff: 'Last Handoff',
75
+ backlog_items: 'Backlog Items',
76
+ high_priority: 'high priority',
77
+ },
78
+ errors: {
79
+ session_corrupted: 'Session file appears corrupted. Attempting recovery...',
80
+ handoff_missing: 'Handoff not found. Using session.yaml + CLAUDE.md as fallback.',
81
+ agent_failed: 'Agent failed after 3 attempts. Escalating to user.',
82
+ mcp_required: "Required MCP '{mcp}' is not configured. Installation instructions:",
83
+ mcp_optional: "Optional MCP '{mcp}' not configured. Skipping related functionality.",
84
+ },
85
+ };
86
+
87
+ let currentStrings = FALLBACK_EN;
88
+ let currentLanguage = 'en';
89
+
90
+ /**
91
+ * Load i18n strings for a given language
92
+ * Tries: 1) installed chati.dev/i18n/ in target project, 2) bundled assets, 3) fallback
93
+ */
94
+ export function loadLanguage(lang, targetDir = null) {
95
+ currentLanguage = lang;
96
+
97
+ if (lang === 'en') {
98
+ currentStrings = FALLBACK_EN;
99
+ return currentStrings;
100
+ }
101
+
102
+ // Try to load from installed project i18n
103
+ const paths = [
104
+ targetDir && join(targetDir, 'chati.dev', 'i18n', `${lang}.yaml`),
105
+ join(__dirname, '..', '..', 'assets', 'i18n', `${lang}.yaml`),
106
+ ].filter(Boolean);
107
+
108
+ for (const filePath of paths) {
109
+ if (existsSync(filePath)) {
110
+ try {
111
+ const content = readFileSync(filePath, 'utf-8');
112
+ const parsed = yaml.load(content);
113
+ currentStrings = parsed;
114
+ return currentStrings;
115
+ } catch {
116
+ // Fall through to next path
117
+ }
118
+ }
119
+ }
120
+
121
+ // Fallback to English
122
+ currentStrings = FALLBACK_EN;
123
+ return currentStrings;
124
+ }
125
+
126
+ /**
127
+ * Get a translated string by dot-notation key
128
+ * Supports interpolation: t('agents.completed', { agent: 'Brief', score: 97 })
129
+ */
130
+ export function t(key, vars = {}) {
131
+ const keys = key.split('.');
132
+ let value = currentStrings;
133
+
134
+ for (const k of keys) {
135
+ if (value && typeof value === 'object' && k in value) {
136
+ value = value[k];
137
+ } else {
138
+ // Fallback to English
139
+ value = FALLBACK_EN;
140
+ for (const fk of keys) {
141
+ if (value && typeof value === 'object' && fk in value) {
142
+ value = value[fk];
143
+ } else {
144
+ return key; // Return key itself if not found
145
+ }
146
+ }
147
+ break;
148
+ }
149
+ }
150
+
151
+ if (typeof value !== 'string') return key;
152
+
153
+ // Interpolate variables
154
+ return value.replace(/\{(\w+)\}/g, (_, varName) => {
155
+ return vars[varName] !== undefined ? String(vars[varName]) : `{${varName}}`;
156
+ });
157
+ }
158
+
159
+ export function getCurrentLanguage() {
160
+ return currentLanguage;
161
+ }
162
+
163
+ export const SUPPORTED_LANGUAGES = [
164
+ { value: 'en', label: 'English' },
165
+ { value: 'pt', label: 'Portugues' },
166
+ { value: 'es', label: 'Espanol' },
167
+ { value: 'fr', label: 'Francais' },
168
+ ];
@@ -0,0 +1,107 @@
1
+ import * as p from '@clack/prompts';
2
+ import { readFileSync } from 'fs';
3
+ import { join, dirname, basename } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { logBanner } from '../utils/logger.js';
6
+ import { stepLanguage, stepProjectType, stepIDEs, stepMCPs, stepConfirmation } from './questions.js';
7
+ import { createSpinner, showStep, showValidation, showQuickStart } from './feedback.js';
8
+ import { installFramework } from '../installer/core.js';
9
+ import { validateInstallation } from '../installer/validator.js';
10
+ import { t } from './i18n.js';
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const VERSION = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf-8')).version;
14
+
15
+ /**
16
+ * Run the 6-step installer wizard
17
+ */
18
+ export async function runWizard(targetDir, options = {}) {
19
+ // Load ASCII logo
20
+ let logoText;
21
+ try {
22
+ logoText = readFileSync(join(__dirname, '..', '..', 'assets', 'logo.txt'), 'utf-8');
23
+ } catch {
24
+ logoText = 'chati.dev';
25
+ }
26
+
27
+ // Step 1: Logo + Language Selection (in English)
28
+ logBanner(logoText, VERSION);
29
+
30
+ p.intro('Setting up chati.dev');
31
+
32
+ const language = options.language || await stepLanguage();
33
+
34
+ // Step 2: Project Type
35
+ const projectType = options.projectType || await stepProjectType(targetDir);
36
+
37
+ // Step 3: IDE Selection
38
+ const selectedIDEs = options.ides || await stepIDEs();
39
+
40
+ // Step 4: MCP Selection
41
+ const selectedMCPs = options.mcps || await stepMCPs(projectType);
42
+
43
+ // Step 5: Confirmation
44
+ const projectName = basename(targetDir);
45
+ const config = {
46
+ projectName,
47
+ projectType,
48
+ language,
49
+ selectedIDEs,
50
+ selectedMCPs,
51
+ targetDir,
52
+ version: VERSION,
53
+ };
54
+
55
+ await stepConfirmation(config);
56
+
57
+ // Step 6: Installation + Validation
58
+ console.log();
59
+ const installSpinner = createSpinner(t('installer.installing'));
60
+ installSpinner.start();
61
+
62
+ try {
63
+ await installFramework(config);
64
+ installSpinner.stop();
65
+
66
+ showStep(t('installer.created_chati'));
67
+ showStep(t('installer.created_framework'));
68
+ showStep(t('installer.created_commands'));
69
+ showStep(t('installer.installed_constitution'));
70
+ showStep(t('installer.created_session'));
71
+ showStep(t('installer.created_claude_md'));
72
+
73
+ if (selectedMCPs.length > 0) {
74
+ showStep(`${t('installer.configured_mcps')} ${selectedMCPs.join(', ')}`);
75
+ }
76
+
77
+ // Validation
78
+ console.log();
79
+ const validateSpinner = createSpinner(t('installer.validating'));
80
+ validateSpinner.start();
81
+
82
+ const validation = await validateInstallation(targetDir);
83
+ validateSpinner.stop();
84
+
85
+ showValidation(t('installer.agents_valid'));
86
+ showValidation(t('installer.handoff_ok'));
87
+ showValidation(t('installer.validation_ok'));
88
+ showValidation(t('installer.constitution_ok'));
89
+ showValidation(t('installer.session_ok'));
90
+
91
+ console.log();
92
+ p.outro(t('installer.success'));
93
+
94
+ const primaryIDE = selectedIDEs[0] || 'your IDE';
95
+ showQuickStart(t('installer.quick_start_title'), [
96
+ `${t('installer.quick_start_1')} (${primaryIDE})`,
97
+ t('installer.quick_start_2'),
98
+ t('installer.quick_start_3'),
99
+ ]);
100
+
101
+ return { success: true, config, validation };
102
+ } catch (err) {
103
+ installSpinner.stop();
104
+ p.cancel(`Installation failed: ${err.message}`);
105
+ return { success: false, error: err.message };
106
+ }
107
+ }