@evolve.labs/devflow 0.8.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 (106) hide show
  1. package/.claude/commands/agents/architect.md +1162 -0
  2. package/.claude/commands/agents/architect.meta.yaml +124 -0
  3. package/.claude/commands/agents/builder.md +1432 -0
  4. package/.claude/commands/agents/builder.meta.yaml +117 -0
  5. package/.claude/commands/agents/chronicler.md +633 -0
  6. package/.claude/commands/agents/chronicler.meta.yaml +217 -0
  7. package/.claude/commands/agents/guardian.md +456 -0
  8. package/.claude/commands/agents/guardian.meta.yaml +127 -0
  9. package/.claude/commands/agents/strategist.md +483 -0
  10. package/.claude/commands/agents/strategist.meta.yaml +158 -0
  11. package/.claude/commands/agents/system-designer.md +1137 -0
  12. package/.claude/commands/agents/system-designer.meta.yaml +156 -0
  13. package/.claude/commands/devflow-help.md +93 -0
  14. package/.claude/commands/devflow-status.md +60 -0
  15. package/.claude/commands/quick/create-adr.md +82 -0
  16. package/.claude/commands/quick/new-feature.md +57 -0
  17. package/.claude/commands/quick/security-check.md +54 -0
  18. package/.claude/commands/quick/system-design.md +58 -0
  19. package/.claude_project +52 -0
  20. package/.devflow/agents/architect.meta.yaml +122 -0
  21. package/.devflow/agents/builder.meta.yaml +116 -0
  22. package/.devflow/agents/chronicler.meta.yaml +222 -0
  23. package/.devflow/agents/guardian.meta.yaml +127 -0
  24. package/.devflow/agents/strategist.meta.yaml +158 -0
  25. package/.devflow/agents/system-designer.meta.yaml +265 -0
  26. package/.devflow/project.yaml +242 -0
  27. package/.gitignore-template +84 -0
  28. package/LICENSE +21 -0
  29. package/README.md +249 -0
  30. package/bin/devflow.js +54 -0
  31. package/lib/autopilot.js +235 -0
  32. package/lib/autopilotConstants.js +213 -0
  33. package/lib/constants.js +95 -0
  34. package/lib/init.js +200 -0
  35. package/lib/update.js +181 -0
  36. package/lib/utils.js +157 -0
  37. package/lib/web.js +119 -0
  38. package/package.json +57 -0
  39. package/web/CHANGELOG.md +192 -0
  40. package/web/README.md +156 -0
  41. package/web/app/api/autopilot/execute/route.ts +102 -0
  42. package/web/app/api/autopilot/terminal-execute/route.ts +124 -0
  43. package/web/app/api/files/route.ts +280 -0
  44. package/web/app/api/files/tree/route.ts +160 -0
  45. package/web/app/api/git/route.ts +201 -0
  46. package/web/app/api/health/route.ts +94 -0
  47. package/web/app/api/project/open/route.ts +134 -0
  48. package/web/app/api/search/route.ts +247 -0
  49. package/web/app/api/specs/route.ts +405 -0
  50. package/web/app/api/terminal/route.ts +222 -0
  51. package/web/app/globals.css +160 -0
  52. package/web/app/ide/layout.tsx +43 -0
  53. package/web/app/ide/page.tsx +216 -0
  54. package/web/app/layout.tsx +34 -0
  55. package/web/app/page.tsx +303 -0
  56. package/web/components/agents/AgentIcons.tsx +281 -0
  57. package/web/components/autopilot/AutopilotConfigModal.tsx +245 -0
  58. package/web/components/autopilot/AutopilotPanel.tsx +299 -0
  59. package/web/components/dashboard/DashboardPanel.tsx +393 -0
  60. package/web/components/editor/Breadcrumbs.tsx +134 -0
  61. package/web/components/editor/EditorPanel.tsx +120 -0
  62. package/web/components/editor/EditorTabs.tsx +229 -0
  63. package/web/components/editor/MarkdownPreview.tsx +154 -0
  64. package/web/components/editor/MermaidDiagram.tsx +113 -0
  65. package/web/components/editor/MonacoEditor.tsx +177 -0
  66. package/web/components/editor/TabContextMenu.tsx +207 -0
  67. package/web/components/git/GitPanel.tsx +534 -0
  68. package/web/components/layout/Shell.tsx +15 -0
  69. package/web/components/layout/StatusBar.tsx +100 -0
  70. package/web/components/modals/CommandPalette.tsx +393 -0
  71. package/web/components/modals/GlobalSearch.tsx +348 -0
  72. package/web/components/modals/QuickOpen.tsx +241 -0
  73. package/web/components/modals/RecentFiles.tsx +208 -0
  74. package/web/components/projects/ProjectSelector.tsx +147 -0
  75. package/web/components/settings/SettingItem.tsx +150 -0
  76. package/web/components/settings/SettingsPanel.tsx +323 -0
  77. package/web/components/specs/SpecsPanel.tsx +1091 -0
  78. package/web/components/terminal/TerminalPanel.tsx +683 -0
  79. package/web/components/ui/ContextMenu.tsx +182 -0
  80. package/web/components/ui/LoadingSpinner.tsx +66 -0
  81. package/web/components/ui/ResizeHandle.tsx +110 -0
  82. package/web/components/ui/Skeleton.tsx +108 -0
  83. package/web/components/ui/SkipLinks.tsx +37 -0
  84. package/web/components/ui/Toaster.tsx +57 -0
  85. package/web/hooks/useFocusTrap.ts +141 -0
  86. package/web/hooks/useKeyboardShortcuts.ts +169 -0
  87. package/web/hooks/useListNavigation.ts +237 -0
  88. package/web/lib/autopilotConstants.ts +213 -0
  89. package/web/lib/constants/agents.ts +67 -0
  90. package/web/lib/git.ts +339 -0
  91. package/web/lib/ptyManager.ts +191 -0
  92. package/web/lib/specsParser.ts +299 -0
  93. package/web/lib/stores/autopilotStore.ts +288 -0
  94. package/web/lib/stores/fileStore.ts +550 -0
  95. package/web/lib/stores/gitStore.ts +386 -0
  96. package/web/lib/stores/projectStore.ts +196 -0
  97. package/web/lib/stores/settingsStore.ts +126 -0
  98. package/web/lib/stores/specsStore.ts +297 -0
  99. package/web/lib/stores/uiStore.ts +175 -0
  100. package/web/lib/types/index.ts +177 -0
  101. package/web/lib/utils.ts +98 -0
  102. package/web/next.config.js +50 -0
  103. package/web/package.json +54 -0
  104. package/web/postcss.config.js +6 -0
  105. package/web/tailwind.config.ts +68 -0
  106. package/web/tsconfig.json +41 -0
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Shared autopilot constants for CLI usage.
3
+ * Mirrors web/lib/autopilotConstants.ts for Node.js CLI.
4
+ */
5
+
6
+ const VALID_AGENTS = [
7
+ 'strategist', 'architect', 'system-designer', 'builder', 'guardian', 'chronicler',
8
+ ];
9
+
10
+ const AGENT_SKILLS = {
11
+ strategist: '/agents:strategist',
12
+ architect: '/agents:architect',
13
+ 'system-designer': '/agents:system-designer',
14
+ builder: '/agents:builder',
15
+ guardian: '/agents:guardian',
16
+ chronicler: '/agents:chronicler',
17
+ };
18
+
19
+ const AGENT_PROMPTS = {
20
+ strategist: `Analise a spec e refine os requisitos:
21
+ {spec_content}
22
+
23
+ 1. Identificar requisitos implícitos
24
+ 2. Listar acceptance criteria
25
+ 3. Dependências e riscos
26
+ 4. Estimar complexidade`,
27
+
28
+ architect: `Defina a arquitetura com base na spec:
29
+ {spec_content}
30
+
31
+ Contexto anterior: {previous_output}
32
+
33
+ 1. Arquitetura da solução
34
+ 2. Padrões e tecnologias
35
+ 3. Componentes necessários
36
+ 4. Decisões importantes`,
37
+
38
+ 'system-designer': `Projete o system design com base na spec:
39
+ {spec_content}
40
+
41
+ Contexto anterior: {previous_output}
42
+
43
+ 1. Back-of-the-envelope estimation
44
+ 2. High-level design
45
+ 3. Data model e storage
46
+ 4. Scalability e reliability`,
47
+
48
+ builder: `Implemente a solução conforme spec e design:
49
+ {spec_content}
50
+
51
+ Contexto anterior: {previous_output}
52
+
53
+ 1. Criar/modificar arquivos necessários
54
+ 2. Implementar lógica principal
55
+ 3. Tratamento de erros`,
56
+
57
+ guardian: `Revise o código implementado:
58
+ {spec_content}
59
+
60
+ Implementação: {previous_output}
61
+
62
+ 1. Segurança
63
+ 2. Performance
64
+ 3. Edge cases
65
+ 4. Melhorias necessárias`,
66
+
67
+ chronicler: `Documente as mudanças realizadas:
68
+ {spec_content}
69
+
70
+ Implementação: {previous_output}
71
+
72
+ 1. Resumir o que foi implementado
73
+ 2. Arquivos criados/modificados
74
+ 3. Atualizar tasks na spec`,
75
+ };
76
+
77
+ const AGENT_TIMEOUTS = {
78
+ strategist: 300,
79
+ architect: 600,
80
+ 'system-designer': 600,
81
+ builder: 1200,
82
+ guardian: 600,
83
+ chronicler: 300,
84
+ };
85
+
86
+ const DEFAULT_PHASES = [
87
+ { id: 'strategist', name: 'Planning' },
88
+ { id: 'architect', name: 'Design' },
89
+ { id: 'system-designer', name: 'System Design' },
90
+ { id: 'builder', name: 'Implementation' },
91
+ { id: 'guardian', name: 'Validation' },
92
+ { id: 'chronicler', name: 'Documentation' },
93
+ ];
94
+
95
+ const TASK_TRACKING_AGENTS = ['builder', 'guardian', 'chronicler'];
96
+
97
+ /**
98
+ * Load the full agent definition from .claude/commands/agents/{agent}.md
99
+ * Returns the file content or null if not found.
100
+ */
101
+ function loadAgentDefinitionSync(projectPath, agent) {
102
+ const path = require('node:path');
103
+ const fs = require('node:fs');
104
+ const filePath = path.join(projectPath, '.claude', 'commands', 'agents', `${agent}.md`);
105
+ try {
106
+ return fs.readFileSync(filePath, 'utf-8');
107
+ } catch {
108
+ return null;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Build the full prompt for a given agent phase.
114
+ * If agentDefinition is provided, uses it as context instead of the slash command.
115
+ */
116
+ function buildPrompt(agent, specContent, previousOutputs, agentDefinition) {
117
+ const prompt = AGENT_PROMPTS[agent]
118
+ .replace('{spec_content}', specContent)
119
+ .replace('{previous_output}', (previousOutputs || []).join('\n---\n') || 'N/A');
120
+
121
+ // Use full agent definition if available, otherwise fallback to skill command
122
+ const agentContext = agentDefinition || AGENT_SKILLS[agent];
123
+
124
+ return `${agentContext}\n\n---\n\n${prompt}`;
125
+ }
126
+
127
+ /**
128
+ * Extract unchecked task titles from markdown content.
129
+ */
130
+ function extractUncheckedTasks(content) {
131
+ const tasks = [];
132
+ const regex = /^\s*[-*]\s*\[ \]\s*(?:\[[^\]]+\]\s*)?(.+)$/gm;
133
+ let match;
134
+ while ((match = regex.exec(content)) !== null) {
135
+ tasks.push(match[1].trim());
136
+ }
137
+ return tasks;
138
+ }
139
+
140
+ /**
141
+ * Check if the agent output mentions a task as completed.
142
+ */
143
+ function isTaskMentionedAsCompleted(taskTitle, output) {
144
+ const lower = output.toLowerCase();
145
+ const taskLower = taskTitle.toLowerCase();
146
+
147
+ if (!lower.includes(taskLower)) return false;
148
+
149
+ if (taskLower.length < 10) {
150
+ const completionKeywords = [
151
+ 'completed', 'done', 'implemented', 'finished', 'created',
152
+ 'added', 'fixed', 'resolved', 'built', 'configured',
153
+ '✅', '✓', '[x]', 'complete',
154
+ ];
155
+ const idx = lower.indexOf(taskLower);
156
+ const context = lower.slice(Math.max(0, idx - 200), idx + taskLower.length + 200);
157
+ return completionKeywords.some(kw => context.includes(kw));
158
+ }
159
+
160
+ return true;
161
+ }
162
+
163
+ /**
164
+ * Auto-detect completed tasks and update the spec file.
165
+ */
166
+ async function autoUpdateSpecTasks(specFilePath, agentOutput) {
167
+ const fs = require('node:fs').promises;
168
+ const completedTasks = [];
169
+
170
+ try {
171
+ const content = await fs.readFile(specFilePath, 'utf-8');
172
+ const uncheckedTasks = extractUncheckedTasks(content);
173
+
174
+ if (uncheckedTasks.length === 0) return [];
175
+
176
+ let updatedContent = content;
177
+ for (const taskTitle of uncheckedTasks) {
178
+ if (isTaskMentionedAsCompleted(taskTitle, agentOutput)) {
179
+ const escapedTitle = taskTitle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
180
+ const taskRegex = new RegExp(
181
+ `^(\\s*[-*]\\s*)\\[ \\](\\s*(?:\\[[^\\]]+\\]\\s*)?)${escapedTitle}`,
182
+ 'gm'
183
+ );
184
+ updatedContent = updatedContent.replace(taskRegex, (match, prefix, middle) => {
185
+ completedTasks.push(taskTitle);
186
+ return `${prefix}[x]${middle}${taskTitle}`;
187
+ });
188
+ }
189
+ }
190
+
191
+ if (completedTasks.length > 0) {
192
+ await fs.writeFile(specFilePath, updatedContent, 'utf-8');
193
+ }
194
+ } catch (error) {
195
+ console.error('Error auto-updating spec tasks:', error.message);
196
+ }
197
+
198
+ return completedTasks;
199
+ }
200
+
201
+ module.exports = {
202
+ VALID_AGENTS,
203
+ AGENT_SKILLS,
204
+ AGENT_PROMPTS,
205
+ AGENT_TIMEOUTS,
206
+ DEFAULT_PHASES,
207
+ TASK_TRACKING_AGENTS,
208
+ loadAgentDefinitionSync,
209
+ buildPrompt,
210
+ extractUncheckedTasks,
211
+ isTaskMentionedAsCompleted,
212
+ autoUpdateSpecTasks,
213
+ };
@@ -0,0 +1,95 @@
1
+ const path = require('node:path');
2
+
3
+ const VERSION = '0.8.0';
4
+
5
+ // Root of the installed npm package (where source files live)
6
+ const PACKAGE_ROOT = path.resolve(__dirname, '..');
7
+
8
+ // Directories to create in all install modes
9
+ const BASE_DIRS = [
10
+ '.claude/commands',
11
+ '.devflow/agents',
12
+ '.devflow/memory',
13
+ '.devflow/sessions',
14
+ 'docs/snapshots',
15
+ 'docs/system-design/sdd',
16
+ 'docs/system-design/rfc',
17
+ 'docs/system-design/capacity',
18
+ 'docs/system-design/trade-offs',
19
+ ];
20
+
21
+ // Additional directories for default/full modes
22
+ const DOCS_DIRS = [
23
+ 'docs/planning/stories',
24
+ 'docs/architecture/diagrams',
25
+ 'docs/decisions',
26
+ 'docs/security',
27
+ 'docs/performance',
28
+ 'docs/api',
29
+ 'docs/migration',
30
+ ];
31
+
32
+ // Directories that should have .gitkeep files
33
+ const GITKEEP_DIRS = [
34
+ 'docs/planning',
35
+ 'docs/planning/stories',
36
+ 'docs/architecture',
37
+ 'docs/architecture/diagrams',
38
+ 'docs/decisions',
39
+ 'docs/security',
40
+ 'docs/performance',
41
+ 'docs/api',
42
+ 'docs/migration',
43
+ 'docs/snapshots',
44
+ 'docs/system-design/sdd',
45
+ 'docs/system-design/rfc',
46
+ 'docs/system-design/capacity',
47
+ 'docs/system-design/trade-offs',
48
+ '.devflow/sessions',
49
+ ];
50
+
51
+ // Files and directories to copy during init
52
+ const AGENTS_COPY = [
53
+ { src: '.claude/commands/agents', dest: '.claude/commands/agents', type: 'dir' },
54
+ { src: '.claude/commands/quick', dest: '.claude/commands/quick', type: 'dir' },
55
+ { src: '.claude/commands/devflow-help.md', dest: '.claude/commands/devflow-help.md', type: 'file' },
56
+ { src: '.claude/commands/devflow-status.md', dest: '.claude/commands/devflow-status.md', type: 'file' },
57
+ { src: '.claude_project', dest: '.claude_project', type: 'file' },
58
+ { src: '.devflow/project.yaml', dest: '.devflow/project.yaml', type: 'file' },
59
+ { src: '.devflow/agents', dest: '.devflow/agents', type: 'dir' },
60
+ ];
61
+
62
+ // Doc files to copy in default/full modes
63
+ const DOC_FILES = [
64
+ 'docs/decisions/000-template.md',
65
+ ];
66
+
67
+ // Web IDE source directories to copy (excludes node_modules, .next, etc.)
68
+ const WEB_COPY_DIRS = [
69
+ 'web/app',
70
+ 'web/components',
71
+ 'web/hooks',
72
+ 'web/lib',
73
+ ];
74
+
75
+ const WEB_COPY_FILES = [
76
+ 'web/package.json',
77
+ 'web/next.config.js',
78
+ 'web/tsconfig.json',
79
+ 'web/tailwind.config.ts',
80
+ 'web/postcss.config.js',
81
+ 'web/README.md',
82
+ 'web/CHANGELOG.md',
83
+ ];
84
+
85
+ module.exports = {
86
+ VERSION,
87
+ PACKAGE_ROOT,
88
+ BASE_DIRS,
89
+ DOCS_DIRS,
90
+ GITKEEP_DIRS,
91
+ AGENTS_COPY,
92
+ DOC_FILES,
93
+ WEB_COPY_DIRS,
94
+ WEB_COPY_FILES,
95
+ };
package/lib/init.js ADDED
@@ -0,0 +1,200 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+ const {
4
+ VERSION,
5
+ PACKAGE_ROOT,
6
+ BASE_DIRS,
7
+ DOCS_DIRS,
8
+ GITKEEP_DIRS,
9
+ AGENTS_COPY,
10
+ DOC_FILES,
11
+ WEB_COPY_DIRS,
12
+ WEB_COPY_FILES,
13
+ } = require('./constants');
14
+ const {
15
+ printHeader,
16
+ success,
17
+ error,
18
+ warn,
19
+ info,
20
+ printSuccess,
21
+ confirm,
22
+ checkDependencies,
23
+ copyDir,
24
+ copyFile,
25
+ ensureDirs,
26
+ createGitkeep,
27
+ pathExists,
28
+ resolveTarget,
29
+ } = require('./utils');
30
+
31
+ async function initCommand(targetArg, options) {
32
+ printHeader(VERSION);
33
+
34
+ const targetDir = resolveTarget(targetArg);
35
+
36
+ // 1. Check dependencies
37
+ if (!options.skipDeps) {
38
+ checkDependencies();
39
+ }
40
+
41
+ // 2. Validate/create target directory
42
+ if (!(await pathExists(targetDir))) {
43
+ info(`Directory does not exist: ${targetDir}`);
44
+ const shouldCreate = options.force || await confirm('Create this directory?');
45
+ if (!shouldCreate) {
46
+ error('Installation cancelled.');
47
+ process.exit(1);
48
+ }
49
+ await fs.promises.mkdir(targetDir, { recursive: true });
50
+ success(`Directory created: ${targetDir}`);
51
+ }
52
+
53
+ info(`Installing DevFlow in: ${targetDir}`);
54
+ console.log();
55
+
56
+ // 3. Check for existing installation
57
+ if (await pathExists(path.join(targetDir, '.devflow'))) {
58
+ warn('.devflow/ already exists in target directory!');
59
+ if (!options.force) {
60
+ const shouldOverwrite = await confirm('Overwrite existing installation?');
61
+ if (!shouldOverwrite) {
62
+ error('Installation cancelled.');
63
+ process.exit(1);
64
+ }
65
+ }
66
+ console.log();
67
+ }
68
+
69
+ // 4. Determine install mode
70
+ let mode = 'default';
71
+ if (options.agentsOnly) mode = 'agents-only';
72
+ if (options.full) mode = 'full';
73
+
74
+ const modeLabels = {
75
+ 'agents-only': 'Agents only (minimal)',
76
+ 'default': 'Agents + documentation structure',
77
+ 'full': 'Full installation',
78
+ };
79
+ info(`Install mode: ${modeLabels[mode]}`);
80
+ console.log();
81
+
82
+ // 5. Create base directory structure
83
+ await ensureDirs(targetDir, BASE_DIRS);
84
+
85
+ // 6. Copy agent files and core config
86
+ for (const item of AGENTS_COPY) {
87
+ const src = path.join(PACKAGE_ROOT, item.src);
88
+ const dest = path.join(targetDir, item.dest);
89
+ try {
90
+ if (item.type === 'dir') {
91
+ await copyDir(src, dest);
92
+ } else {
93
+ await copyFile(src, dest);
94
+ }
95
+ } catch (err) {
96
+ warn(`Could not copy ${item.src}: ${err.message}`);
97
+ }
98
+ }
99
+
100
+ success('Agents installed (.claude/commands/agents/)');
101
+ success('Quick commands installed (.claude/commands/quick/)');
102
+ success('.claude_project orchestration rules installed');
103
+ success('.devflow/ structure created');
104
+
105
+ // 7. Default/full: create docs structure with gitkeep files
106
+ if (mode === 'default' || mode === 'full') {
107
+ await ensureDirs(targetDir, DOCS_DIRS);
108
+ await createGitkeep(targetDir, GITKEEP_DIRS);
109
+
110
+ // Copy doc template files
111
+ for (const file of DOC_FILES) {
112
+ const src = path.join(PACKAGE_ROOT, file);
113
+ const dest = path.join(targetDir, file);
114
+ try {
115
+ if (!(await pathExists(dest))) {
116
+ await copyFile(src, dest);
117
+ }
118
+ } catch { /* skip if source missing */ }
119
+ }
120
+
121
+ success('Documentation structure created (docs/)');
122
+ }
123
+
124
+ // 8. Full: copy .gitignore
125
+ if (mode === 'full') {
126
+ const gitignoreSrc = path.join(PACKAGE_ROOT, '.gitignore-template');
127
+ const gitignoreDest = path.join(targetDir, '.gitignore');
128
+
129
+ if (await pathExists(gitignoreSrc)) {
130
+ if (await pathExists(gitignoreDest)) {
131
+ warn('.gitignore already exists - appending DevFlow entries');
132
+ const existing = await fs.promises.readFile(gitignoreDest, 'utf8');
133
+ if (!existing.includes('# DevFlow')) {
134
+ const devflowIgnore = await fs.promises.readFile(gitignoreSrc, 'utf8');
135
+ await fs.promises.appendFile(
136
+ gitignoreDest,
137
+ `\n\n# --- DevFlow entries ---\n${devflowIgnore}`
138
+ );
139
+ }
140
+ success('.gitignore updated with DevFlow entries');
141
+ } else {
142
+ await copyFile(gitignoreSrc, gitignoreDest);
143
+ success('.gitignore created');
144
+ }
145
+ }
146
+ }
147
+
148
+ // 9. Optional: copy Web IDE
149
+ if (options.web) {
150
+ info('Installing Web IDE source files...');
151
+
152
+ // Copy web directories
153
+ for (const dir of WEB_COPY_DIRS) {
154
+ const src = path.join(PACKAGE_ROOT, dir);
155
+ const dest = path.join(targetDir, dir);
156
+ try {
157
+ await copyDir(src, dest);
158
+ } catch (err) {
159
+ warn(`Could not copy ${dir}: ${err.message}`);
160
+ }
161
+ }
162
+
163
+ // Copy web files
164
+ for (const file of WEB_COPY_FILES) {
165
+ const src = path.join(PACKAGE_ROOT, file);
166
+ const dest = path.join(targetDir, file);
167
+ try {
168
+ await copyFile(src, dest);
169
+ } catch { /* skip if source missing */ }
170
+ }
171
+
172
+ success('Web IDE source files installed (web/)');
173
+ }
174
+
175
+ // 10. Success output
176
+ printSuccess(VERSION);
177
+ info('Next steps:');
178
+ console.log();
179
+ console.log('1. Open the project in Claude Code:');
180
+ console.log(` cd ${targetDir}`);
181
+ console.log(' claude');
182
+ console.log();
183
+ console.log('2. In Claude Code, test an agent:');
184
+ console.log(' /agents:strategist Hello! Introduce yourself');
185
+ console.log();
186
+ console.log('3. Create your first feature:');
187
+ console.log(' /agents:strategist I want to create [your feature]');
188
+
189
+ if (options.web) {
190
+ console.log();
191
+ console.log('4. Start the Web IDE:');
192
+ console.log(` cd ${path.join(targetDir, 'web')}`);
193
+ console.log(' npm install');
194
+ console.log(' npm run dev');
195
+ console.log(' # Open http://localhost:3000');
196
+ }
197
+ console.log();
198
+ }
199
+
200
+ module.exports = { initCommand };
package/lib/update.js ADDED
@@ -0,0 +1,181 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+ const {
4
+ VERSION,
5
+ PACKAGE_ROOT,
6
+ BASE_DIRS,
7
+ DOCS_DIRS,
8
+ GITKEEP_DIRS,
9
+ } = require('./constants');
10
+ const {
11
+ printHeader,
12
+ success,
13
+ error,
14
+ warn,
15
+ info,
16
+ printUpdateSuccess,
17
+ confirm,
18
+ copyDir,
19
+ copyFile,
20
+ ensureDirs,
21
+ createGitkeep,
22
+ pathExists,
23
+ resolveTarget,
24
+ } = require('./utils');
25
+
26
+ async function updateCommand(targetArg, options) {
27
+ printHeader(VERSION);
28
+
29
+ const targetDir = resolveTarget(targetArg);
30
+
31
+ // 1. Verify DevFlow is installed
32
+ const hasAgents = await pathExists(path.join(targetDir, '.claude', 'commands', 'agents'));
33
+ const hasDevflow = await pathExists(path.join(targetDir, '.devflow'));
34
+
35
+ if (!hasAgents && !hasDevflow) {
36
+ error(`DevFlow not found in: ${targetDir}`);
37
+ info('Use "devflow init" to install for the first time.');
38
+ process.exit(1);
39
+ }
40
+
41
+ // 2. Read installed version
42
+ let installedVersion = 'unknown';
43
+ const projectYamlPath = path.join(targetDir, '.devflow', 'project.yaml');
44
+ try {
45
+ const content = await fs.promises.readFile(projectYamlPath, 'utf8');
46
+ const match = content.match(/^\s*version:\s*"?([^"\s]+)"?/m);
47
+ if (match) installedVersion = match[1];
48
+ } catch { /* file may not exist */ }
49
+
50
+ info(`Project: ${targetDir}`);
51
+ info(`Installed version: ${installedVersion}`);
52
+ info(`New version: ${VERSION}`);
53
+ console.log();
54
+
55
+ // 3. Check if already up to date
56
+ if (installedVersion === VERSION) {
57
+ success('DevFlow is already up to date!');
58
+ return;
59
+ }
60
+
61
+ // 4. Confirm update
62
+ if (!options.force) {
63
+ warn('The update will overwrite agent files.');
64
+ warn('Customizations in .claude/commands/agents/ will be lost.');
65
+ console.log();
66
+ const shouldContinue = await confirm('Continue with update?');
67
+ if (!shouldContinue) {
68
+ warn('Update cancelled.');
69
+ return;
70
+ }
71
+ }
72
+
73
+ console.log();
74
+ info('Updating DevFlow...');
75
+ console.log();
76
+
77
+ // 5. Backup existing agents
78
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
79
+ const backupDir = path.join(targetDir, '.devflow', `backup-${timestamp}`);
80
+ await fs.promises.mkdir(backupDir, { recursive: true });
81
+
82
+ try {
83
+ await fs.promises.cp(
84
+ path.join(targetDir, '.claude', 'commands', 'agents'),
85
+ path.join(backupDir, 'agents'),
86
+ { recursive: true }
87
+ );
88
+ } catch { /* may not exist */ }
89
+
90
+ try {
91
+ await fs.promises.copyFile(
92
+ projectYamlPath,
93
+ path.join(backupDir, 'project.yaml')
94
+ );
95
+ } catch { /* may not exist */ }
96
+
97
+ success(`Backup created: .devflow/${path.basename(backupDir)}/`);
98
+
99
+ // 6. Update agents
100
+ console.log(' \u2192 Updating agents...');
101
+ await copyDir(
102
+ path.join(PACKAGE_ROOT, '.claude', 'commands', 'agents'),
103
+ path.join(targetDir, '.claude', 'commands', 'agents')
104
+ );
105
+ success('Agents updated (.claude/commands/agents/)');
106
+
107
+ // 7. Update quick commands
108
+ console.log(' \u2192 Updating quick commands...');
109
+ await copyDir(
110
+ path.join(PACKAGE_ROOT, '.claude', 'commands', 'quick'),
111
+ path.join(targetDir, '.claude', 'commands', 'quick')
112
+ );
113
+ success('Quick commands updated');
114
+
115
+ // 8. Update help & status commands
116
+ console.log(' \u2192 Updating help & status commands...');
117
+ for (const file of ['devflow-help.md', 'devflow-status.md']) {
118
+ try {
119
+ await copyFile(
120
+ path.join(PACKAGE_ROOT, '.claude', 'commands', file),
121
+ path.join(targetDir, '.claude', 'commands', file)
122
+ );
123
+ } catch { /* skip if missing */ }
124
+ }
125
+
126
+ // 9. Update .claude_project
127
+ console.log(' \u2192 Updating .claude_project...');
128
+ await copyFile(
129
+ path.join(PACKAGE_ROOT, '.claude_project'),
130
+ path.join(targetDir, '.claude_project')
131
+ );
132
+ success('.claude_project updated');
133
+
134
+ // 10. Update .devflow/agents/ meta.yaml files
135
+ console.log(' \u2192 Updating .devflow/agents/ meta.yaml files...');
136
+ await copyDir(
137
+ path.join(PACKAGE_ROOT, '.devflow', 'agents'),
138
+ path.join(targetDir, '.devflow', 'agents')
139
+ );
140
+ success('.devflow/agents/ updated');
141
+
142
+ // 11. Update project.yaml version
143
+ console.log(' \u2192 Updating project.yaml version...');
144
+ try {
145
+ let content = await fs.promises.readFile(projectYamlPath, 'utf8');
146
+ content = content.replace(
147
+ /version:\s*"?[^"\s]+"?/,
148
+ `version: "${VERSION}"`
149
+ );
150
+ await fs.promises.writeFile(projectYamlPath, content, 'utf8');
151
+ success(`project.yaml version updated to ${VERSION}`);
152
+ } catch {
153
+ await copyFile(
154
+ path.join(PACKAGE_ROOT, '.devflow', 'project.yaml'),
155
+ projectYamlPath
156
+ );
157
+ success('project.yaml created');
158
+ }
159
+
160
+ // 12. Ensure directory structure is complete
161
+ console.log(' \u2192 Verifying directory structure...');
162
+ await ensureDirs(targetDir, [...BASE_DIRS, ...DOCS_DIRS]);
163
+ await createGitkeep(targetDir, GITKEEP_DIRS);
164
+ success('Directory structure verified');
165
+
166
+ // 13. Success output
167
+ printUpdateSuccess(VERSION);
168
+ info('What was updated:');
169
+ console.log(' \u2022 Agents in .claude/commands/agents/');
170
+ console.log(' \u2022 Quick commands in .claude/commands/quick/');
171
+ console.log(' \u2022 .claude_project orchestration rules');
172
+ console.log(' \u2022 .devflow/agents/ meta.yaml files');
173
+ console.log(' \u2022 Directory structure');
174
+ console.log();
175
+ info(`Backup saved in: .devflow/${path.basename(backupDir)}/`);
176
+ console.log();
177
+ info('Tip: Use /agents:strategist to start a new feature');
178
+ console.log();
179
+ }
180
+
181
+ module.exports = { updateCommand };