@mthanhlm/autodev 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/PUBLISH.md +1 -1
  2. package/README.md +21 -6
  3. package/agents/autodev-codebase-domain.md +16 -0
  4. package/agents/autodev-codebase-quality.md +16 -0
  5. package/agents/autodev-codebase-runtime.md +16 -0
  6. package/agents/autodev-codebase-structure.md +16 -0
  7. package/agents/autodev-review-integration.md +21 -0
  8. package/agents/autodev-review-polish.md +21 -0
  9. package/agents/autodev-review-quality.md +21 -0
  10. package/agents/autodev-review-security.md +21 -0
  11. package/autodev/bin/autodev-tools.cjs +406 -90
  12. package/autodev/templates/codebase/domain.md +13 -0
  13. package/autodev/templates/codebase/quality.md +13 -0
  14. package/autodev/templates/codebase/runtime.md +13 -0
  15. package/autodev/templates/codebase/structure.md +13 -0
  16. package/autodev/templates/codebase/summary.md +13 -0
  17. package/autodev/templates/config.json +6 -1
  18. package/autodev/templates/plan.md +4 -0
  19. package/autodev/templates/project-state.md +11 -0
  20. package/autodev/templates/project.md +3 -0
  21. package/autodev/templates/requirements.md +2 -0
  22. package/autodev/templates/review.md +24 -0
  23. package/autodev/templates/roadmap.md +3 -3
  24. package/autodev/templates/state.md +5 -4
  25. package/autodev/templates/summary.md +3 -1
  26. package/autodev/templates/track-state.md +12 -0
  27. package/autodev/templates/track.md +17 -0
  28. package/autodev/templates/uat.md +5 -2
  29. package/autodev/workflows/autodev.md +62 -0
  30. package/autodev/workflows/cleanup.md +45 -0
  31. package/autodev/workflows/execute-phase.md +15 -6
  32. package/autodev/workflows/explore-codebase.md +57 -0
  33. package/autodev/workflows/help.md +45 -12
  34. package/autodev/workflows/new-project.md +41 -14
  35. package/autodev/workflows/plan-phase.md +19 -8
  36. package/autodev/workflows/progress.md +3 -1
  37. package/autodev/workflows/review-phase.md +59 -0
  38. package/autodev/workflows/verify-work.md +17 -7
  39. package/bin/install.js +61 -16
  40. package/commands/autodev/cleanup.md +23 -0
  41. package/commands/autodev/execute-phase.md +2 -2
  42. package/commands/autodev/explore-codebase.md +33 -0
  43. package/commands/autodev/help.md +1 -1
  44. package/commands/autodev/index.md +35 -0
  45. package/commands/autodev/new-project.md +6 -4
  46. package/commands/autodev/plan-phase.md +2 -2
  47. package/commands/autodev/progress.md +1 -1
  48. package/commands/autodev/review-phase.md +29 -0
  49. package/commands/autodev/verify-work.md +2 -2
  50. package/hooks/autodev-session-state.sh +1 -1
  51. package/package.json +5 -2
@@ -4,8 +4,13 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
6
  const DEFAULT_CONFIG = {
7
+ project: {
8
+ type: null
9
+ },
7
10
  workflow: {
8
- research: false
11
+ research: false,
12
+ review_after_execute: true,
13
+ codebase_parallel_agents: 4
9
14
  },
10
15
  execution: {
11
16
  parallel: false
@@ -24,10 +29,26 @@ const DEFAULT_CONFIG = {
24
29
  }
25
30
  };
26
31
 
32
+ const CODEBASE_FILES = ['structure.md', 'domain.md', 'runtime.md', 'quality.md', 'summary.md'];
33
+
27
34
  function autodevDir(cwd) {
28
35
  return path.join(cwd, '.autodev');
29
36
  }
30
37
 
38
+ function rootPaths(cwd) {
39
+ const root = autodevDir(cwd);
40
+ return {
41
+ root,
42
+ config: path.join(root, 'config.json'),
43
+ project: path.join(root, 'PROJECT.md'),
44
+ state: path.join(root, 'STATE.md'),
45
+ activeTrack: path.join(root, 'ACTIVE_TRACK'),
46
+ codebaseDir: path.join(root, 'codebase'),
47
+ codebaseSummary: path.join(root, 'codebase', 'summary.md'),
48
+ tracksDir: path.join(root, 'tracks')
49
+ };
50
+ }
51
+
31
52
  function readText(filePath) {
32
53
  try {
33
54
  return fs.readFileSync(filePath, 'utf8');
@@ -44,98 +65,226 @@ function readJson(filePath, fallback = null) {
44
65
  }
45
66
  }
46
67
 
68
+ function fileExists(filePath) {
69
+ try {
70
+ return fs.existsSync(filePath);
71
+ } catch {
72
+ return false;
73
+ }
74
+ }
75
+
47
76
  function slugify(value) {
48
77
  return String(value)
49
78
  .toLowerCase()
50
79
  .replace(/[^a-z0-9]+/g, '-')
51
80
  .replace(/^-+|-+$/g, '')
52
- .replace(/-+/g, '-') || 'phase';
81
+ .replace(/-+/g, '-') || 'track';
53
82
  }
54
83
 
55
84
  function padPhase(number) {
56
85
  return String(number).padStart(2, '0');
57
86
  }
58
87
 
88
+ function detectExistingCodebase(cwd) {
89
+ const knownFiles = [
90
+ 'package.json',
91
+ 'package-lock.json',
92
+ 'pnpm-lock.yaml',
93
+ 'yarn.lock',
94
+ 'tsconfig.json',
95
+ 'jsconfig.json',
96
+ 'pyproject.toml',
97
+ 'requirements.txt',
98
+ 'Pipfile',
99
+ 'Cargo.toml',
100
+ 'go.mod',
101
+ 'pom.xml',
102
+ 'build.gradle',
103
+ 'Dockerfile'
104
+ ];
105
+ const knownDirs = [
106
+ 'src',
107
+ 'app',
108
+ 'lib',
109
+ 'server',
110
+ 'client',
111
+ 'backend',
112
+ 'frontend',
113
+ 'components',
114
+ 'pages',
115
+ 'api',
116
+ 'tests',
117
+ '__tests__'
118
+ ];
119
+
120
+ if (knownFiles.some(name => fileExists(path.join(cwd, name)))) {
121
+ return true;
122
+ }
123
+
124
+ if (knownDirs.some(name => fileExists(path.join(cwd, name)))) {
125
+ return true;
126
+ }
127
+
128
+ try {
129
+ const entries = fs.readdirSync(cwd, { withFileTypes: true });
130
+ return entries.some(entry => {
131
+ if (entry.name === '.autodev' || entry.name === '.claude' || entry.name.startsWith('.')) {
132
+ return false;
133
+ }
134
+ return entry.isDirectory() || /\.(js|ts|tsx|jsx|py|go|rs|java|rb|php|cs|cpp|c|swift|kt|sql|json|yaml|yml|md)$/.test(entry.name);
135
+ });
136
+ } catch {
137
+ return false;
138
+ }
139
+ }
140
+
141
+ function mergeSection(defaultValue, actualValue) {
142
+ if (defaultValue && typeof defaultValue === 'object' && !Array.isArray(defaultValue)) {
143
+ return {
144
+ ...defaultValue,
145
+ ...((actualValue && typeof actualValue === 'object' && !Array.isArray(actualValue)) ? actualValue : {})
146
+ };
147
+ }
148
+ return actualValue !== undefined ? actualValue : defaultValue;
149
+ }
150
+
151
+ function loadConfig(cwd) {
152
+ const config = readJson(rootPaths(cwd).config, {}) || {};
153
+ return {
154
+ project: mergeSection(DEFAULT_CONFIG.project, config.project),
155
+ workflow: mergeSection(DEFAULT_CONFIG.workflow, config.workflow),
156
+ execution: mergeSection(DEFAULT_CONFIG.execution, config.execution),
157
+ git: mergeSection(DEFAULT_CONFIG.git, config.git),
158
+ hooks: mergeSection(DEFAULT_CONFIG.hooks, config.hooks)
159
+ };
160
+ }
161
+
162
+ function readActiveTrack(cwd) {
163
+ const value = readText(rootPaths(cwd).activeTrack);
164
+ return value ? value.trim() || null : null;
165
+ }
166
+
167
+ function trackDir(cwd, slug) {
168
+ return path.join(rootPaths(cwd).tracksDir, slug);
169
+ }
170
+
171
+ function trackPaths(cwd, slug = readActiveTrack(cwd)) {
172
+ if (!slug) {
173
+ return null;
174
+ }
175
+
176
+ const dir = trackDir(cwd, slug);
177
+ return {
178
+ slug,
179
+ dir,
180
+ track: path.join(dir, 'TRACK.md'),
181
+ requirements: path.join(dir, 'REQUIREMENTS.md'),
182
+ roadmap: path.join(dir, 'ROADMAP.md'),
183
+ state: path.join(dir, 'STATE.md'),
184
+ phasesDir: path.join(dir, 'phases')
185
+ };
186
+ }
187
+
188
+ function listTracks(cwd) {
189
+ const tracksDir = rootPaths(cwd).tracksDir;
190
+ if (!fileExists(tracksDir)) {
191
+ return [];
192
+ }
193
+
194
+ return fs.readdirSync(tracksDir, { withFileTypes: true })
195
+ .filter(entry => entry.isDirectory())
196
+ .map(entry => {
197
+ const paths = trackPaths(cwd, entry.name);
198
+ const trackName = (() => {
199
+ const content = readText(paths.track);
200
+ const match = content && content.match(/^#\s+Track:\s*(.+)$/m);
201
+ return match ? match[1].trim() : entry.name;
202
+ })();
203
+
204
+ return {
205
+ slug: entry.name,
206
+ name: trackName,
207
+ paths,
208
+ active: readActiveTrack(cwd) === entry.name
209
+ };
210
+ })
211
+ .sort((left, right) => left.slug.localeCompare(right.slug));
212
+ }
213
+
59
214
  function parseRoadmap(content) {
60
215
  const phases = [];
61
216
  if (!content) {
62
217
  return phases;
63
218
  }
64
219
 
65
- const matcher = /^##\s+Phase\s+(\d+)\s*:\s*(.+)$/gm;
220
+ const matcher = /^##\s+Phase\s+(\d+)(?:\s+\[([a-z-]+)\])?\s*:\s*(.+)$/gm;
66
221
  let match;
67
222
  while ((match = matcher.exec(content)) !== null) {
68
223
  phases.push({
69
224
  number: Number(match[1]),
70
- name: match[2].trim()
225
+ type: match[2] || 'feature',
226
+ name: match[3].trim()
71
227
  });
72
228
  }
73
229
  return phases;
74
230
  }
75
231
 
76
- function phasePaths(cwd, phase) {
77
- const dirName = `${padPhase(phase.number)}-${slugify(phase.name)}`;
78
- const dir = path.join(autodevDir(cwd), 'phases', dirName);
232
+ function phasePaths(cwd, slug, phase) {
233
+ const track = trackPaths(cwd, slug);
234
+ if (!track) {
235
+ return null;
236
+ }
237
+
79
238
  const prefix = padPhase(phase.number);
239
+ const dirName = `${prefix}-${phase.type}-${slugify(phase.name)}`;
240
+ const dir = path.join(track.phasesDir, dirName);
80
241
  return {
81
242
  ...phase,
82
243
  padded: prefix,
83
244
  dir,
84
245
  planPath: path.join(dir, `${prefix}-PLAN.md`),
85
246
  summaryPath: path.join(dir, `${prefix}-SUMMARY.md`),
247
+ reviewPath: path.join(dir, `${prefix}-REVIEW.md`),
86
248
  uatPath: path.join(dir, `${prefix}-UAT.md`)
87
249
  };
88
250
  }
89
251
 
90
- function loadConfig(cwd) {
91
- const config = readJson(path.join(autodevDir(cwd), 'config.json'), {});
92
- return {
93
- workflow: {
94
- ...DEFAULT_CONFIG.workflow,
95
- ...(config.workflow || {})
96
- },
97
- execution: {
98
- ...DEFAULT_CONFIG.execution,
99
- ...(config.execution || {})
100
- },
101
- git: {
102
- ...DEFAULT_CONFIG.git,
103
- ...(config.git || {})
104
- },
105
- hooks: {
106
- ...DEFAULT_CONFIG.hooks,
107
- ...(config.hooks || {})
108
- }
109
- };
110
- }
252
+ function listPhases(cwd, slug = readActiveTrack(cwd)) {
253
+ const track = trackPaths(cwd, slug);
254
+ if (!track || !fileExists(track.roadmap)) {
255
+ return [];
256
+ }
111
257
 
112
- function listPhases(cwd) {
113
- const roadmap = readText(path.join(autodevDir(cwd), 'ROADMAP.md'));
114
- return parseRoadmap(roadmap).map(phase => {
115
- const details = phasePaths(cwd, phase);
116
- const planExists = fs.existsSync(details.planPath);
117
- const summaryExists = fs.existsSync(details.summaryPath);
118
- const uatExists = fs.existsSync(details.uatPath);
258
+ return parseRoadmap(readText(track.roadmap)).map(phase => {
259
+ const details = phasePaths(cwd, slug, phase);
260
+ const planExists = fileExists(details.planPath);
261
+ const summaryExists = fileExists(details.summaryPath);
262
+ const reviewExists = fileExists(details.reviewPath);
263
+ const uatExists = fileExists(details.uatPath);
119
264
  const status = uatExists
120
265
  ? 'verified'
121
- : summaryExists
122
- ? 'executed'
123
- : planExists
124
- ? 'planned'
125
- : 'pending';
266
+ : reviewExists
267
+ ? 'reviewed'
268
+ : summaryExists
269
+ ? 'executed'
270
+ : planExists
271
+ ? 'planned'
272
+ : 'pending';
126
273
 
127
274
  return {
128
275
  ...details,
276
+ trackSlug: slug,
129
277
  planExists,
130
278
  summaryExists,
279
+ reviewExists,
131
280
  uatExists,
132
281
  status
133
282
  };
134
283
  });
135
284
  }
136
285
 
137
- function resolvePhase(cwd, requestedPhase, mode) {
138
- const phases = listPhases(cwd);
286
+ function resolvePhase(cwd, slug, requestedPhase, mode) {
287
+ const phases = listPhases(cwd, slug);
139
288
  if (phases.length === 0) {
140
289
  return null;
141
290
  }
@@ -153,93 +302,204 @@ function resolvePhase(cwd, requestedPhase, mode) {
153
302
  return phases.find(phase => phase.planExists && !phase.summaryExists) || null;
154
303
  }
155
304
 
305
+ if (mode === 'review') {
306
+ return phases.find(phase => phase.summaryExists && !phase.reviewExists) || null;
307
+ }
308
+
156
309
  if (mode === 'verify') {
157
- return phases.find(phase => phase.summaryExists && !phase.uatExists)
158
- || [...phases].reverse().find(phase => phase.summaryExists)
310
+ return phases.find(phase => phase.reviewExists && !phase.uatExists)
311
+ || [...phases].reverse().find(phase => phase.reviewExists)
159
312
  || null;
160
313
  }
161
314
 
162
315
  return phases[0];
163
316
  }
164
317
 
165
- function nextCommand(progress) {
166
- if (!progress.initialized) {
167
- return '/autodev-new-project';
318
+ function buildRoute(cwd) {
319
+ const paths = rootPaths(cwd);
320
+ const config = loadConfig(cwd);
321
+ const existingCodebase = detectExistingCodebase(cwd);
322
+ const projectType = config.project.type || (existingCodebase ? 'brownfield' : 'greenfield');
323
+ const projectExists = fileExists(paths.project);
324
+ const codebaseMapExists = fileExists(paths.codebaseSummary);
325
+ const activeTrack = readActiveTrack(cwd);
326
+ const tracks = listTracks(cwd);
327
+
328
+ if (!projectExists) {
329
+ return {
330
+ kind: 'init_project',
331
+ command: '/autodev-new-project',
332
+ reason: existingCodebase ? 'existing_code_detected_without_project_state' : 'project_state_missing',
333
+ projectType
334
+ };
335
+ }
336
+
337
+ if (projectType === 'brownfield' && !codebaseMapExists) {
338
+ return {
339
+ kind: 'explore_codebase',
340
+ command: '/autodev-explore-codebase',
341
+ reason: 'brownfield_project_needs_codebase_map',
342
+ projectType
343
+ };
344
+ }
345
+
346
+ if (!activeTrack) {
347
+ return {
348
+ kind: 'track_select',
349
+ command: '/autodev',
350
+ reason: tracks.length > 0 ? 'no_active_track_selected' : 'no_tracks_created',
351
+ projectType
352
+ };
353
+ }
354
+
355
+ const track = trackPaths(cwd, activeTrack);
356
+ if (!track || !fileExists(track.track) || !fileExists(track.roadmap) || !fileExists(track.state)) {
357
+ return {
358
+ kind: 'track_setup',
359
+ command: '/autodev',
360
+ reason: 'active_track_missing_required_artifacts',
361
+ projectType,
362
+ trackSlug: activeTrack
363
+ };
364
+ }
365
+
366
+ const phases = listPhases(cwd, activeTrack);
367
+ if (phases.length === 0) {
368
+ return {
369
+ kind: 'track_setup',
370
+ command: '/autodev',
371
+ reason: 'active_track_has_no_phases',
372
+ projectType,
373
+ trackSlug: activeTrack
374
+ };
375
+ }
376
+
377
+ const nextReview = phases.find(phase => phase.summaryExists && !phase.reviewExists);
378
+ if (nextReview && config.workflow.review_after_execute !== false) {
379
+ return {
380
+ kind: 'review_phase',
381
+ command: `/autodev-review-phase ${nextReview.number}`,
382
+ reason: 'phase_executed_but_not_reviewed',
383
+ projectType,
384
+ trackSlug: activeTrack,
385
+ phaseNumber: nextReview.number
386
+ };
168
387
  }
169
388
 
170
- const nextVerify = progress.phases.find(phase => phase.summaryExists && !phase.uatExists);
389
+ const nextVerify = phases.find(phase => phase.reviewExists && !phase.uatExists);
171
390
  if (nextVerify) {
172
- return `/autodev-verify-work ${nextVerify.number}`;
391
+ return {
392
+ kind: 'verify_phase',
393
+ command: `/autodev-verify-work ${nextVerify.number}`,
394
+ reason: 'phase_reviewed_but_not_verified',
395
+ projectType,
396
+ trackSlug: activeTrack,
397
+ phaseNumber: nextVerify.number
398
+ };
173
399
  }
174
400
 
175
- const nextExecute = progress.phases.find(phase => phase.planExists && !phase.summaryExists);
401
+ const nextExecute = phases.find(phase => phase.planExists && !phase.summaryExists);
176
402
  if (nextExecute) {
177
- return `/autodev-execute-phase ${nextExecute.number}`;
403
+ return {
404
+ kind: 'execute_phase',
405
+ command: `/autodev-execute-phase ${nextExecute.number}`,
406
+ reason: 'phase_planned_but_not_executed',
407
+ projectType,
408
+ trackSlug: activeTrack,
409
+ phaseNumber: nextExecute.number
410
+ };
178
411
  }
179
412
 
180
- const nextPlan = progress.phases.find(phase => !phase.planExists);
413
+ const nextPlan = phases.find(phase => !phase.planExists);
181
414
  if (nextPlan) {
182
- return `/autodev-plan-phase ${nextPlan.number}`;
415
+ return {
416
+ kind: 'plan_phase',
417
+ command: `/autodev-plan-phase ${nextPlan.number}`,
418
+ reason: 'phase_not_planned',
419
+ projectType,
420
+ trackSlug: activeTrack,
421
+ phaseNumber: nextPlan.number
422
+ };
183
423
  }
184
424
 
185
- return '/autodev-progress';
425
+ return {
426
+ kind: 'track_complete',
427
+ command: '/autodev-cleanup',
428
+ reason: 'active_track_fully_verified',
429
+ projectType,
430
+ trackSlug: activeTrack
431
+ };
186
432
  }
187
433
 
188
434
  function buildProgress(cwd) {
189
- const root = autodevDir(cwd);
190
- const initialized = fs.existsSync(root);
191
- if (!initialized) {
192
- return {
193
- initialized: false,
194
- phases: [],
195
- counts: {
196
- total: 0,
197
- planned: 0,
198
- executed: 0,
199
- verified: 0
200
- },
201
- nextCommand: '/autodev-new-project'
202
- };
203
- }
435
+ const paths = rootPaths(cwd);
436
+ const initialized = fileExists(paths.project);
437
+ const existingCodebase = detectExistingCodebase(cwd);
438
+ const config = loadConfig(cwd);
439
+ const projectType = config.project.type || (existingCodebase ? 'brownfield' : 'greenfield');
440
+ const activeTrack = readActiveTrack(cwd);
441
+ const tracks = listTracks(cwd);
442
+ const phases = activeTrack ? listPhases(cwd, activeTrack) : [];
443
+ const codebaseMapExists = fileExists(paths.codebaseSummary);
444
+ const route = buildRoute(cwd);
204
445
 
205
- const phases = listPhases(cwd);
206
446
  return {
207
- initialized: true,
447
+ initialized,
448
+ projectType,
449
+ existingCodebase,
450
+ codebaseMapExists,
451
+ activeTrack,
452
+ tracks,
208
453
  phases,
209
454
  counts: {
455
+ trackCount: tracks.length,
210
456
  total: phases.length,
211
457
  planned: phases.filter(phase => phase.planExists).length,
212
458
  executed: phases.filter(phase => phase.summaryExists).length,
459
+ reviewed: phases.filter(phase => phase.reviewExists).length,
213
460
  verified: phases.filter(phase => phase.uatExists).length
214
461
  },
215
- nextCommand: nextCommand({ initialized: true, phases })
462
+ route,
463
+ nextCommand: route.command
216
464
  };
217
465
  }
218
466
 
219
467
  function renderProgressTable(progress) {
220
468
  if (!progress.initialized) {
221
- return 'No .autodev project found.\nNext: /autodev-new-project';
469
+ return 'No .autodev project found.\nNext: /autodev';
222
470
  }
223
471
 
224
472
  const lines = [
225
473
  'Autodev Progress',
226
474
  '',
475
+ `Project Type: ${progress.projectType}`,
476
+ `Codebase Map: ${progress.codebaseMapExists ? 'ready' : 'missing'}`,
477
+ `Tracks: ${progress.counts.trackCount}`,
478
+ `Active Track: ${progress.activeTrack || 'none'}`,
227
479
  `Phases: ${progress.counts.total}`,
228
480
  `Planned: ${progress.counts.planned}`,
229
481
  `Executed: ${progress.counts.executed}`,
482
+ `Reviewed: ${progress.counts.reviewed}`,
230
483
  `Verified: ${progress.counts.verified}`,
231
- `Next: ${progress.nextCommand}`,
484
+ `Next: ${progress.route.command}`,
232
485
  '',
233
- '| Phase | Name | Plan | Summary | UAT | Status |',
234
- '| --- | --- | --- | --- | --- | --- |'
486
+ '| Phase | Type | Name | Plan | Summary | Review | UAT | Status |',
487
+ '| --- | --- | --- | --- | --- | --- | --- | --- |'
235
488
  ];
236
489
 
237
490
  for (const phase of progress.phases) {
238
491
  lines.push(
239
- `| ${phase.number} | ${phase.name} | ${phase.planExists ? 'yes' : 'no'} | ${phase.summaryExists ? 'yes' : 'no'} | ${phase.uatExists ? 'yes' : 'no'} | ${phase.status} |`
492
+ `| ${phase.number} | ${phase.type} | ${phase.name} | ${phase.planExists ? 'yes' : 'no'} | ${phase.summaryExists ? 'yes' : 'no'} | ${phase.reviewExists ? 'yes' : 'no'} | ${phase.uatExists ? 'yes' : 'no'} | ${phase.status} |`
240
493
  );
241
494
  }
242
495
 
496
+ if (progress.tracks.length > 0) {
497
+ lines.push('', '| Track | Active | Name |', '| --- | --- | --- |');
498
+ for (const track of progress.tracks) {
499
+ lines.push(`| ${track.slug} | ${track.active ? 'yes' : 'no'} | ${track.name} |`);
500
+ }
501
+ }
502
+
243
503
  return lines.join('\n');
244
504
  }
245
505
 
@@ -249,30 +509,67 @@ function dotGet(object, key) {
249
509
  ), object);
250
510
  }
251
511
 
512
+ function codebasePaths(cwd) {
513
+ const codebaseDir = rootPaths(cwd).codebaseDir;
514
+ return Object.fromEntries(CODEBASE_FILES.map(name => [name.replace(/\.md$/, ''), path.join(codebaseDir, name)]));
515
+ }
516
+
252
517
  function initPayload(cwd, mode, requestedPhase) {
253
- const root = autodevDir(cwd);
518
+ const paths = rootPaths(cwd);
254
519
  const config = loadConfig(cwd);
255
- const phase = mode === 'new-project' ? null : resolvePhase(cwd, requestedPhase, mode);
520
+ const route = buildRoute(cwd);
521
+ const existingCodebase = detectExistingCodebase(cwd);
522
+ const projectType = config.project.type || (existingCodebase ? 'brownfield' : 'greenfield');
523
+ const activeTrack = readActiveTrack(cwd);
524
+ const tracks = listTracks(cwd);
525
+ const track = trackPaths(cwd, activeTrack);
526
+ const phaseMode = mode === 'review-phase' ? 'review' : mode === 'verify-work' ? 'verify' : mode === 'execute-phase' ? 'execute' : mode === 'plan-phase' ? 'plan' : null;
527
+ const phase = phaseMode && activeTrack ? resolvePhase(cwd, activeTrack, requestedPhase, phaseMode) : null;
528
+ const codebase = codebasePaths(cwd);
529
+
256
530
  return {
257
531
  cwd,
258
- autodev_exists: fs.existsSync(root),
259
- config_path: path.join(root, 'config.json'),
260
- project_path: path.join(root, 'PROJECT.md'),
261
- requirements_path: path.join(root, 'REQUIREMENTS.md'),
262
- roadmap_path: path.join(root, 'ROADMAP.md'),
263
- state_path: path.join(root, 'STATE.md'),
264
- research_enabled: config.workflow.research,
265
- git_mode: config.git.mode,
532
+ autodev_exists: fileExists(paths.root),
533
+ project_exists: fileExists(paths.project),
534
+ existing_code_detected: existingCodebase,
535
+ project_type: projectType,
536
+ config_path: paths.config,
537
+ project_path: paths.project,
538
+ project_state_path: paths.state,
539
+ active_track_path: paths.activeTrack,
540
+ active_track: activeTrack,
541
+ track_count: tracks.length,
542
+ tracks: tracks.map(trackItem => ({
543
+ slug: trackItem.slug,
544
+ name: trackItem.name,
545
+ active: trackItem.active,
546
+ path: trackItem.paths.dir
547
+ })),
548
+ codebase_dir: paths.codebaseDir,
549
+ codebase_map_exists: fileExists(paths.codebaseSummary),
550
+ codebase_paths: codebase,
551
+ track_path: track ? track.dir : null,
552
+ track_doc_path: track ? track.track : null,
553
+ requirements_path: track ? track.requirements : null,
554
+ roadmap_path: track ? track.roadmap : null,
555
+ track_state_path: track ? track.state : null,
556
+ phases_dir: track ? track.phasesDir : null,
557
+ route,
266
558
  phase_found: Boolean(phase),
267
559
  phase_number: phase ? phase.number : null,
560
+ phase_type: phase ? phase.type : null,
268
561
  phase_name: phase ? phase.name : null,
269
562
  phase_dir: phase ? phase.dir : null,
270
563
  plan_path: phase ? phase.planPath : null,
271
564
  summary_path: phase ? phase.summaryPath : null,
565
+ review_path: phase ? phase.reviewPath : null,
272
566
  uat_path: phase ? phase.uatPath : null,
273
567
  plan_exists: phase ? phase.planExists : false,
274
568
  summary_exists: phase ? phase.summaryExists : false,
275
- uat_exists: phase ? phase.uatExists : false
569
+ review_exists: phase ? phase.reviewExists : false,
570
+ uat_exists: phase ? phase.uatExists : false,
571
+ workflow: config.workflow,
572
+ git_mode: config.git.mode
276
573
  };
277
574
  }
278
575
 
@@ -309,7 +606,7 @@ function main() {
309
606
  process.exit(1);
310
607
  }
311
608
  const value = dotGet(loadConfig(cwd), key);
312
- if (typeof value === 'object') {
609
+ if (typeof value === 'object' && value !== null) {
313
610
  printJson(value);
314
611
  } else if (value !== undefined) {
315
612
  process.stdout.write(`${String(value)}\n`);
@@ -317,6 +614,17 @@ function main() {
317
614
  return;
318
615
  }
319
616
 
617
+ if (command === 'route') {
618
+ const mode = rest[0] || 'json';
619
+ const route = buildRoute(cwd);
620
+ if (mode === 'json') {
621
+ printJson(route);
622
+ return;
623
+ }
624
+ process.stdout.write(`${route.command}\n`);
625
+ return;
626
+ }
627
+
320
628
  if (command === 'init') {
321
629
  const mode = rest[0];
322
630
  const phase = rest[1];
@@ -339,8 +647,16 @@ if (require.main === module) {
339
647
  module.exports = {
340
648
  autodevDir,
341
649
  buildProgress,
650
+ buildRoute,
651
+ detectExistingCodebase,
652
+ initPayload,
653
+ listPhases,
654
+ listTracks,
342
655
  loadConfig,
343
656
  parseRoadmap,
657
+ readActiveTrack,
344
658
  renderProgressTable,
345
- resolvePhase
659
+ resolvePhase,
660
+ rootPaths,
661
+ trackPaths
346
662
  };
@@ -0,0 +1,13 @@
1
+ # Codebase Domain
2
+
3
+ ## Main Product Areas
4
+ - [Domain area]
5
+
6
+ ## Key User Or System Flows
7
+ - [Flow]
8
+
9
+ ## Data And State Boundaries
10
+ - [Where important state lives]
11
+
12
+ ## Active Track Impact
13
+ - [How the current track intersects the domain]
@@ -0,0 +1,13 @@
1
+ # Codebase Quality
2
+
3
+ ## Tests
4
+ - [Existing test setup and obvious gaps]
5
+
6
+ ## Conventions
7
+ - [Naming, file organization, style patterns]
8
+
9
+ ## Risks
10
+ - [Security, stability, or maintainability concerns]
11
+
12
+ ## Tech Debt
13
+ - [Important debt likely to affect the active track]