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,221 @@
1
+ import { mkdirSync, writeFileSync, copyFileSync, existsSync, readFileSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import yaml from 'js-yaml';
5
+ import { IDE_CONFIGS } from '../config/ide-configs.js';
6
+ import { generateClaudeMCPConfig } from '../config/mcp-configs.js';
7
+ import { generateSessionYaml, generateConfigYaml, generateClaudeMd } from './templates.js';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+
11
+ // When published to npm: framework files are bundled at packages/chati-dev/framework/
12
+ // When developing locally in monorepo: fallback to monorepo root chati.dev/
13
+ const BUNDLED_SOURCE = join(__dirname, '..', '..', 'framework');
14
+ const MONOREPO_SOURCE = join(__dirname, '..', '..', '..', '..', 'chati.dev');
15
+ const FRAMEWORK_SOURCE = existsSync(BUNDLED_SOURCE) ? BUNDLED_SOURCE : MONOREPO_SOURCE;
16
+
17
+ /**
18
+ * Install chati.dev framework into target directory
19
+ */
20
+ export async function installFramework(config) {
21
+ const { targetDir, projectType, language, selectedIDEs, selectedMCPs, projectName, version } = config;
22
+
23
+ // 1. Create .chati/ session directory
24
+ createDir(join(targetDir, '.chati'));
25
+ writeFileSync(
26
+ join(targetDir, '.chati', 'session.yaml'),
27
+ generateSessionYaml({ projectName, projectType, language, selectedIDEs, selectedMCPs }),
28
+ 'utf-8'
29
+ );
30
+
31
+ // 2. Create chati.dev/ framework directory (copy from source)
32
+ const frameworkDir = join(targetDir, 'chati.dev');
33
+ createDir(frameworkDir);
34
+
35
+ // Copy framework structure
36
+ const frameworkDirs = [
37
+ 'orchestrator',
38
+ 'agents/clarity', 'agents/quality', 'agents/build', 'agents/deploy',
39
+ 'templates', 'workflows', 'quality-gates',
40
+ 'schemas', 'frameworks', 'intelligence', 'patterns',
41
+ 'i18n', 'migrations',
42
+ 'artifacts/0-WU', 'artifacts/1-Brief', 'artifacts/2-PRD',
43
+ 'artifacts/3-Architecture', 'artifacts/4-UX', 'artifacts/5-Phases',
44
+ 'artifacts/6-Tasks', 'artifacts/7-QA-Planning', 'artifacts/8-Validation',
45
+ 'artifacts/handoffs', 'artifacts/decisions',
46
+ ];
47
+
48
+ for (const dir of frameworkDirs) {
49
+ createDir(join(frameworkDir, dir));
50
+ }
51
+
52
+ // Copy framework files from source
53
+ copyFrameworkFiles(frameworkDir);
54
+
55
+ // Write config.yaml
56
+ writeFileSync(
57
+ join(frameworkDir, 'config.yaml'),
58
+ generateConfigYaml({ version, projectType, language, selectedIDEs }),
59
+ 'utf-8'
60
+ );
61
+
62
+ // 3. Configure IDEs
63
+ for (const ideKey of selectedIDEs) {
64
+ await configureIDE(targetDir, ideKey, selectedMCPs);
65
+ }
66
+
67
+ // 4. Create/update CLAUDE.md at root
68
+ writeFileSync(
69
+ join(targetDir, 'CLAUDE.md'),
70
+ generateClaudeMd({ projectName, projectType, language }),
71
+ 'utf-8'
72
+ );
73
+ }
74
+
75
+ /**
76
+ * Copy framework files from the chati.dev source directory
77
+ */
78
+ function copyFrameworkFiles(destDir) {
79
+ if (!existsSync(FRAMEWORK_SOURCE)) return;
80
+
81
+ const filesToCopy = [
82
+ 'constitution.md',
83
+ 'orchestrator/chati.md',
84
+ // CLARITY agents
85
+ 'agents/clarity/greenfield-wu.md',
86
+ 'agents/clarity/brownfield-wu.md',
87
+ 'agents/clarity/brief.md',
88
+ 'agents/clarity/detail.md',
89
+ 'agents/clarity/architect.md',
90
+ 'agents/clarity/ux.md',
91
+ 'agents/clarity/phases.md',
92
+ 'agents/clarity/tasks.md',
93
+ // Quality agents
94
+ 'agents/quality/qa-planning.md',
95
+ 'agents/quality/qa-implementation.md',
96
+ // BUILD + DEPLOY agents
97
+ 'agents/build/dev.md',
98
+ 'agents/deploy/devops.md',
99
+ // Templates
100
+ 'templates/prd-tmpl.yaml',
101
+ 'templates/brownfield-prd-tmpl.yaml',
102
+ 'templates/fullstack-architecture-tmpl.yaml',
103
+ 'templates/task-tmpl.yaml',
104
+ 'templates/qa-gate-tmpl.yaml',
105
+ // Workflows
106
+ 'workflows/greenfield-fullstack.yaml',
107
+ 'workflows/brownfield-fullstack.yaml',
108
+ 'workflows/brownfield-discovery.yaml',
109
+ 'workflows/brownfield-service.yaml',
110
+ 'workflows/brownfield-ui.yaml',
111
+ // Quality gates
112
+ 'quality-gates/planning-gate.md',
113
+ 'quality-gates/implementation-gate.md',
114
+ // Schemas
115
+ 'schemas/session.schema.json',
116
+ 'schemas/config.schema.json',
117
+ 'schemas/task.schema.json',
118
+ // Frameworks
119
+ 'frameworks/quality-dimensions.yaml',
120
+ 'frameworks/decision-heuristics.yaml',
121
+ // Intelligence
122
+ 'intelligence/gotchas.yaml',
123
+ 'intelligence/patterns.yaml',
124
+ 'intelligence/confidence.yaml',
125
+ // Patterns
126
+ 'patterns/elicitation.md',
127
+ // i18n
128
+ 'i18n/en.yaml',
129
+ 'i18n/pt.yaml',
130
+ 'i18n/es.yaml',
131
+ 'i18n/fr.yaml',
132
+ // Migrations
133
+ 'migrations/v1.0-to-v1.1.yaml',
134
+ ];
135
+
136
+ for (const file of filesToCopy) {
137
+ const src = join(FRAMEWORK_SOURCE, file);
138
+ const dest = join(destDir, file);
139
+
140
+ if (existsSync(src)) {
141
+ createDir(dirname(dest));
142
+ copyFileSync(src, dest);
143
+ }
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Configure a specific IDE
149
+ */
150
+ async function configureIDE(targetDir, ideKey, selectedMCPs) {
151
+ const config = IDE_CONFIGS[ideKey];
152
+ if (!config) return;
153
+
154
+ // Create config directory
155
+ createDir(join(targetDir, config.configPath));
156
+
157
+ if (ideKey === 'claude-code') {
158
+ // Thin router
159
+ const routerContent = `---
160
+ # chati.dev Thin Router
161
+ # This file delegates to the full orchestrator at chati.dev/orchestrator/chati.md
162
+ # DO NOT add logic here -- pure delegation only
163
+ ---
164
+
165
+ Read and execute the orchestrator at \`chati.dev/orchestrator/chati.md\`.
166
+
167
+ Pass through all context: session (.chati/session.yaml), handoffs (chati.dev/artifacts/handoffs/),
168
+ artifacts (chati.dev/artifacts/), constitution (chati.dev/constitution.md).
169
+
170
+ This is a thin router. All logic lives in the orchestrator.
171
+ `;
172
+ writeFileSync(join(targetDir, '.claude', 'commands', 'chati.md'), routerContent, 'utf-8');
173
+
174
+ // MCP config
175
+ if (selectedMCPs.length > 0) {
176
+ const mcpConfig = generateClaudeMCPConfig(selectedMCPs);
177
+ writeFileSync(
178
+ join(targetDir, '.claude', 'mcp.json'),
179
+ JSON.stringify(mcpConfig, null, 2) + '\n',
180
+ 'utf-8'
181
+ );
182
+ }
183
+ } else {
184
+ // For other IDEs, create a rules file pointing to chati.dev/
185
+ const rulesContent = `# chati.dev Framework Rules
186
+ # This file configures ${config.name} to work with chati.dev
187
+
188
+ ## Framework Location
189
+ All framework content is in the \`chati.dev/\` directory.
190
+
191
+ ## Session State
192
+ Runtime session state is in \`.chati/session.yaml\` (IDE-agnostic).
193
+
194
+ ## Getting Started
195
+ The orchestrator is at \`chati.dev/orchestrator/chati.md\`.
196
+ Read it to understand routing, session management, and agent activation.
197
+
198
+ ## Constitution
199
+ Governance rules are in \`chati.dev/constitution.md\` (10 Articles).
200
+
201
+ ## Agents
202
+ - CLARITY: chati.dev/agents/clarity/ (8 agents)
203
+ - Quality: chati.dev/agents/quality/ (2 agents)
204
+ - BUILD: chati.dev/agents/build/ (1 agent)
205
+ - DEPLOY: chati.dev/agents/deploy/ (1 agent)
206
+ `;
207
+ if (config.rulesFile) {
208
+ createDir(dirname(join(targetDir, config.rulesFile)));
209
+ writeFileSync(join(targetDir, config.rulesFile), rulesContent, 'utf-8');
210
+ }
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Recursively create directory if it doesn't exist
216
+ */
217
+ function createDir(dir) {
218
+ if (!existsSync(dir)) {
219
+ mkdirSync(dir, { recursive: true });
220
+ }
221
+ }
@@ -0,0 +1,97 @@
1
+ import yaml from 'js-yaml';
2
+
3
+ /**
4
+ * Generate session.yaml content
5
+ */
6
+ export function generateSessionYaml(config) {
7
+ const { projectName, projectType, language, selectedIDEs, selectedMCPs } = config;
8
+
9
+ const session = {
10
+ project: {
11
+ name: projectName,
12
+ type: projectType,
13
+ state: 'clarity',
14
+ },
15
+ execution_mode: 'interactive',
16
+ current_agent: '',
17
+ language: language,
18
+ ides: selectedIDEs,
19
+ mcps: selectedMCPs,
20
+ user_level: 'auto',
21
+ user_level_confidence: 0.0,
22
+ agents: {},
23
+ backlog: [],
24
+ last_handoff: '',
25
+ deviations: [],
26
+ };
27
+
28
+ // Initialize all 12 agent statuses
29
+ const agentNames = [
30
+ 'greenfield-wu', 'brownfield-wu', 'brief', 'detail',
31
+ 'architect', 'ux', 'phases', 'tasks',
32
+ 'qa-planning', 'dev', 'qa-implementation', 'devops',
33
+ ];
34
+
35
+ for (const agent of agentNames) {
36
+ session.agents[agent] = {
37
+ status: 'pending',
38
+ score: 0,
39
+ criteria_count: 0,
40
+ completed_at: null,
41
+ };
42
+ }
43
+
44
+ return yaml.dump(session, { lineWidth: -1, quotingType: '"', forceQuotes: false });
45
+ }
46
+
47
+ /**
48
+ * Generate config.yaml content
49
+ */
50
+ export function generateConfigYaml(config) {
51
+ const { version, projectType, language, selectedIDEs } = config;
52
+
53
+ const configData = {
54
+ version: version,
55
+ installed_at: new Date().toISOString(),
56
+ updated_at: new Date().toISOString(),
57
+ installer_version: version,
58
+ project_type: projectType,
59
+ language: language,
60
+ ides: selectedIDEs,
61
+ };
62
+
63
+ return yaml.dump(configData, { lineWidth: -1, quotingType: '"', forceQuotes: false });
64
+ }
65
+
66
+ /**
67
+ * Generate CLAUDE.md content
68
+ */
69
+ export function generateClaudeMd(config) {
70
+ const { projectName, projectType, language } = config;
71
+
72
+ return `# ${projectName}
73
+
74
+ ## Project Context
75
+ - **Type**: ${projectType === 'greenfield' ? 'Greenfield (new project)' : 'Brownfield (existing project)'}
76
+ - **State**: CLARITY (planning phase)
77
+ - **Language**: ${language}
78
+ - **Current Agent**: None (ready to start)
79
+
80
+ ## Quick Start
81
+ Type \`/chati\` to activate the orchestrator. It will guide you through the entire process.
82
+
83
+ ## Key Files
84
+ - **Session**: \`.chati/session.yaml\` (runtime state)
85
+ - **Constitution**: \`chati.dev/constitution.md\` (governance)
86
+ - **Orchestrator**: \`chati.dev/orchestrator/chati.md\` (entry point)
87
+
88
+ ## Pipeline
89
+ CLARITY (planning) -> BUILD (implementation) -> VALIDATE -> DEPLOY
90
+
91
+ ## Recent Decisions
92
+ _No decisions yet. Start with /chati._
93
+
94
+ ---
95
+ _Auto-updated by chati.dev agents (Protocol 5.4)_
96
+ `;
97
+ }
@@ -0,0 +1,114 @@
1
+ import { existsSync, readFileSync, readdirSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ /**
5
+ * Validate chati.dev installation
6
+ * Checks all 13 agents, constitution, session, schemas, etc.
7
+ */
8
+ export async function validateInstallation(targetDir) {
9
+ const results = {
10
+ agents: { pass: false, details: [] },
11
+ constitution: { pass: false, details: [] },
12
+ session: { pass: false, details: [] },
13
+ schemas: { pass: false, details: [] },
14
+ workflows: { pass: false, details: [] },
15
+ templates: { pass: false, details: [] },
16
+ total: 0,
17
+ passed: 0,
18
+ };
19
+
20
+ // Check all 13 agents (orchestrator + 12 specialized)
21
+ const agentFiles = [
22
+ 'orchestrator/chati.md',
23
+ 'agents/clarity/greenfield-wu.md',
24
+ 'agents/clarity/brownfield-wu.md',
25
+ 'agents/clarity/brief.md',
26
+ 'agents/clarity/detail.md',
27
+ 'agents/clarity/architect.md',
28
+ 'agents/clarity/ux.md',
29
+ 'agents/clarity/phases.md',
30
+ 'agents/clarity/tasks.md',
31
+ 'agents/quality/qa-planning.md',
32
+ 'agents/quality/qa-implementation.md',
33
+ 'agents/build/dev.md',
34
+ 'agents/deploy/devops.md',
35
+ ];
36
+
37
+ let agentCount = 0;
38
+ for (const file of agentFiles) {
39
+ const filePath = join(targetDir, 'chati.dev', file);
40
+ if (existsSync(filePath)) {
41
+ const content = readFileSync(filePath, 'utf-8');
42
+ // Check that agent implements protocols (look for key protocol references)
43
+ const hasProtocols = content.includes('Protocol') || content.includes('protocol');
44
+ agentCount++;
45
+ results.agents.details.push({ file, exists: true, hasProtocols });
46
+ } else {
47
+ results.agents.details.push({ file, exists: false, hasProtocols: false });
48
+ }
49
+ }
50
+ results.agents.pass = agentCount === 13;
51
+ results.total += 1;
52
+ if (results.agents.pass) results.passed += 1;
53
+
54
+ // Check constitution
55
+ const constitutionPath = join(targetDir, 'chati.dev', 'constitution.md');
56
+ if (existsSync(constitutionPath)) {
57
+ const content = readFileSync(constitutionPath, 'utf-8');
58
+ const articleCount = (content.match(/^## Article/gm) || []).length;
59
+ results.constitution.pass = articleCount >= 10;
60
+ results.constitution.details.push({ articleCount });
61
+ }
62
+ results.total += 1;
63
+ if (results.constitution.pass) results.passed += 1;
64
+
65
+ // Check session.yaml
66
+ const sessionPath = join(targetDir, '.chati', 'session.yaml');
67
+ results.session.pass = existsSync(sessionPath);
68
+ results.total += 1;
69
+ if (results.session.pass) results.passed += 1;
70
+
71
+ // Check schemas
72
+ const schemaFiles = ['session.schema.json', 'config.schema.json', 'task.schema.json'];
73
+ let schemaCount = 0;
74
+ for (const file of schemaFiles) {
75
+ if (existsSync(join(targetDir, 'chati.dev', 'schemas', file))) {
76
+ schemaCount++;
77
+ }
78
+ }
79
+ results.schemas.pass = schemaCount === 3;
80
+ results.total += 1;
81
+ if (results.schemas.pass) results.passed += 1;
82
+
83
+ // Check workflows
84
+ const workflowFiles = [
85
+ 'greenfield-fullstack.yaml', 'brownfield-fullstack.yaml',
86
+ 'brownfield-discovery.yaml', 'brownfield-service.yaml', 'brownfield-ui.yaml',
87
+ ];
88
+ let workflowCount = 0;
89
+ for (const file of workflowFiles) {
90
+ if (existsSync(join(targetDir, 'chati.dev', 'workflows', file))) {
91
+ workflowCount++;
92
+ }
93
+ }
94
+ results.workflows.pass = workflowCount === 5;
95
+ results.total += 1;
96
+ if (results.workflows.pass) results.passed += 1;
97
+
98
+ // Check templates
99
+ const templateFiles = [
100
+ 'prd-tmpl.yaml', 'brownfield-prd-tmpl.yaml',
101
+ 'fullstack-architecture-tmpl.yaml', 'task-tmpl.yaml', 'qa-gate-tmpl.yaml',
102
+ ];
103
+ let templateCount = 0;
104
+ for (const file of templateFiles) {
105
+ if (existsSync(join(targetDir, 'chati.dev', 'templates', file))) {
106
+ templateCount++;
107
+ }
108
+ }
109
+ results.templates.pass = templateCount === 5;
110
+ results.total += 1;
111
+ if (results.templates.pass) results.passed += 1;
112
+
113
+ return results;
114
+ }
@@ -0,0 +1,107 @@
1
+ import { existsSync, mkdirSync, cpSync, rmSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ /**
5
+ * Create backup of chati.dev/ directory before upgrade
6
+ * Backup location: chati.dev/.backup-v{version}/
7
+ */
8
+ export function createBackup(targetDir, currentVersion) {
9
+ const frameworkDir = join(targetDir, 'chati.dev');
10
+ const backupDir = join(frameworkDir, `.backup-v${currentVersion}`);
11
+
12
+ if (existsSync(backupDir)) {
13
+ // Remove old backup of same version
14
+ rmSync(backupDir, { recursive: true, force: true });
15
+ }
16
+
17
+ mkdirSync(backupDir, { recursive: true });
18
+
19
+ // Backup framework files (NOT artifacts or intelligence)
20
+ const dirsToBackup = [
21
+ 'orchestrator',
22
+ 'agents',
23
+ 'templates',
24
+ 'workflows',
25
+ 'quality-gates',
26
+ 'schemas',
27
+ 'frameworks',
28
+ 'i18n',
29
+ 'patterns',
30
+ 'migrations',
31
+ ];
32
+
33
+ const filesToBackup = [
34
+ 'constitution.md',
35
+ 'config.yaml',
36
+ ];
37
+
38
+ for (const dir of dirsToBackup) {
39
+ const src = join(frameworkDir, dir);
40
+ const dest = join(backupDir, dir);
41
+ if (existsSync(src)) {
42
+ cpSync(src, dest, { recursive: true });
43
+ }
44
+ }
45
+
46
+ for (const file of filesToBackup) {
47
+ const src = join(frameworkDir, file);
48
+ const dest = join(backupDir, file);
49
+ if (existsSync(src)) {
50
+ cpSync(src, dest);
51
+ }
52
+ }
53
+
54
+ return backupDir;
55
+ }
56
+
57
+ /**
58
+ * Restore from backup (rollback)
59
+ */
60
+ export function restoreFromBackup(targetDir, version) {
61
+ const frameworkDir = join(targetDir, 'chati.dev');
62
+ const backupDir = join(frameworkDir, `.backup-v${version}`);
63
+
64
+ if (!existsSync(backupDir)) {
65
+ throw new Error(`Backup not found: ${backupDir}`);
66
+ }
67
+
68
+ // Restore backed-up directories
69
+ const dirsToRestore = [
70
+ 'orchestrator',
71
+ 'agents',
72
+ 'templates',
73
+ 'workflows',
74
+ 'quality-gates',
75
+ 'schemas',
76
+ 'frameworks',
77
+ 'i18n',
78
+ 'patterns',
79
+ 'migrations',
80
+ ];
81
+
82
+ const filesToRestore = [
83
+ 'constitution.md',
84
+ 'config.yaml',
85
+ ];
86
+
87
+ for (const dir of dirsToRestore) {
88
+ const src = join(backupDir, dir);
89
+ const dest = join(frameworkDir, dir);
90
+ if (existsSync(src)) {
91
+ if (existsSync(dest)) {
92
+ rmSync(dest, { recursive: true, force: true });
93
+ }
94
+ cpSync(src, dest, { recursive: true });
95
+ }
96
+ }
97
+
98
+ for (const file of filesToRestore) {
99
+ const src = join(backupDir, file);
100
+ const dest = join(frameworkDir, file);
101
+ if (existsSync(src)) {
102
+ cpSync(src, dest);
103
+ }
104
+ }
105
+
106
+ return true;
107
+ }
@@ -0,0 +1,105 @@
1
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import yaml from 'js-yaml';
4
+ import semver from 'semver';
5
+
6
+ /**
7
+ * Read current installed version from config.yaml
8
+ */
9
+ export function getCurrentVersion(targetDir) {
10
+ const configPath = join(targetDir, 'chati.dev', 'config.yaml');
11
+ if (!existsSync(configPath)) return null;
12
+
13
+ try {
14
+ const config = yaml.load(readFileSync(configPath, 'utf-8'));
15
+ return config?.version || null;
16
+ } catch {
17
+ return null;
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Check for available updates
23
+ * In v1, this compares against the CLI package version
24
+ * Future versions will query npm registry or GitHub releases
25
+ */
26
+ export async function checkForUpdate(targetDir, cliVersion) {
27
+ const currentVersion = getCurrentVersion(targetDir);
28
+
29
+ if (!currentVersion) {
30
+ return {
31
+ hasUpdate: false,
32
+ error: 'Could not read current version from chati.dev/config.yaml',
33
+ };
34
+ }
35
+
36
+ if (!semver.valid(currentVersion)) {
37
+ return {
38
+ hasUpdate: false,
39
+ error: `Invalid version in config.yaml: ${currentVersion}`,
40
+ };
41
+ }
42
+
43
+ const latestVersion = cliVersion; // In v1, latest = CLI version
44
+
45
+ if (semver.gt(latestVersion, currentVersion)) {
46
+ return {
47
+ hasUpdate: true,
48
+ currentVersion,
49
+ latestVersion,
50
+ changes: getChangelog(currentVersion, latestVersion),
51
+ };
52
+ }
53
+
54
+ return {
55
+ hasUpdate: false,
56
+ currentVersion,
57
+ latestVersion,
58
+ message: 'You are on the latest version.',
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Get changelog between versions
64
+ * In v1, returns placeholder. Future: read from CHANGELOG.md or API
65
+ */
66
+ function getChangelog(fromVersion, toVersion) {
67
+ return [
68
+ `Changes from v${fromVersion} to v${toVersion}:`,
69
+ ' Check release notes for details.',
70
+ ];
71
+ }
72
+
73
+ /**
74
+ * Update config.yaml with the new version after successful upgrade
75
+ */
76
+ export function updateConfigVersion(targetDir, newVersion) {
77
+ const configPath = join(targetDir, 'chati.dev', 'config.yaml');
78
+ if (!existsSync(configPath)) return false;
79
+
80
+ try {
81
+ const config = yaml.load(readFileSync(configPath, 'utf-8'));
82
+ config.version = newVersion;
83
+ writeFileSync(configPath, yaml.dump(config, { lineWidth: -1 }), 'utf-8');
84
+ return true;
85
+ } catch {
86
+ return false;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Compare two versions
92
+ */
93
+ export function compareVersions(current, target) {
94
+ if (!semver.valid(current) || !semver.valid(target)) {
95
+ return { valid: false, error: 'Invalid version format' };
96
+ }
97
+
98
+ return {
99
+ valid: true,
100
+ isUpgrade: semver.gt(target, current),
101
+ isDowngrade: semver.lt(target, current),
102
+ isSame: semver.eq(target, current),
103
+ diff: semver.diff(target, current), // 'major', 'minor', 'patch'
104
+ };
105
+ }