@agile-vibe-coding/avc 0.1.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/cli/agent-loader.js +21 -0
  2. package/cli/agents/agent-selector.md +152 -0
  3. package/cli/agents/architecture-recommender.md +418 -0
  4. package/cli/agents/code-implementer.md +117 -0
  5. package/cli/agents/code-validator.md +80 -0
  6. package/cli/agents/context-reviewer-epic.md +101 -0
  7. package/cli/agents/context-reviewer-story.md +92 -0
  8. package/cli/agents/context-writer-epic.md +145 -0
  9. package/cli/agents/context-writer-story.md +111 -0
  10. package/cli/agents/database-deep-dive.md +470 -0
  11. package/cli/agents/database-recommender.md +634 -0
  12. package/cli/agents/doc-distributor.md +176 -0
  13. package/cli/agents/doc-writer-epic.md +42 -0
  14. package/cli/agents/doc-writer-story.md +43 -0
  15. package/cli/agents/documentation-updater.md +203 -0
  16. package/cli/agents/duplicate-detector.md +110 -0
  17. package/cli/agents/epic-story-decomposer.md +559 -0
  18. package/cli/agents/feature-context-generator.md +91 -0
  19. package/cli/agents/gap-checker-epic.md +52 -0
  20. package/cli/agents/impact-checker-story.md +51 -0
  21. package/cli/agents/migration-guide-generator.md +305 -0
  22. package/cli/agents/mission-scope-generator.md +143 -0
  23. package/cli/agents/mission-scope-validator.md +146 -0
  24. package/cli/agents/project-context-extractor.md +122 -0
  25. package/cli/agents/project-documentation-creator.json +226 -0
  26. package/cli/agents/project-documentation-creator.md +595 -0
  27. package/cli/agents/question-prefiller.md +269 -0
  28. package/cli/agents/refiner-epic.md +39 -0
  29. package/cli/agents/refiner-story.md +42 -0
  30. package/cli/agents/scaffolding-generator.md +99 -0
  31. package/cli/agents/seed-validator.md +71 -0
  32. package/cli/agents/story-doc-enricher.md +133 -0
  33. package/cli/agents/story-scope-reviewer.md +147 -0
  34. package/cli/agents/story-splitter.md +83 -0
  35. package/cli/agents/suggestion-business-analyst.md +88 -0
  36. package/cli/agents/suggestion-deployment-architect.md +263 -0
  37. package/cli/agents/suggestion-product-manager.md +129 -0
  38. package/cli/agents/suggestion-security-specialist.md +156 -0
  39. package/cli/agents/suggestion-technical-architect.md +269 -0
  40. package/cli/agents/suggestion-ux-researcher.md +93 -0
  41. package/cli/agents/task-subtask-decomposer.md +188 -0
  42. package/cli/agents/validator-documentation.json +183 -0
  43. package/cli/agents/validator-documentation.md +455 -0
  44. package/cli/agents/validator-selector.md +211 -0
  45. package/cli/ansi-colors.js +21 -0
  46. package/cli/api-reference-tool.js +368 -0
  47. package/cli/build-docs.js +29 -8
  48. package/cli/ceremony-history.js +369 -0
  49. package/cli/checks/catalog.json +76 -0
  50. package/cli/checks/code/quality.json +26 -0
  51. package/cli/checks/code/testing.json +14 -0
  52. package/cli/checks/code/traceability.json +26 -0
  53. package/cli/checks/cross-refs/epic.json +171 -0
  54. package/cli/checks/cross-refs/story.json +149 -0
  55. package/cli/checks/epic/api.json +114 -0
  56. package/cli/checks/epic/backend.json +126 -0
  57. package/cli/checks/epic/cloud.json +126 -0
  58. package/cli/checks/epic/data.json +102 -0
  59. package/cli/checks/epic/database.json +114 -0
  60. package/cli/checks/epic/developer.json +182 -0
  61. package/cli/checks/epic/devops.json +174 -0
  62. package/cli/checks/epic/frontend.json +162 -0
  63. package/cli/checks/epic/mobile.json +102 -0
  64. package/cli/checks/epic/qa.json +90 -0
  65. package/cli/checks/epic/security.json +184 -0
  66. package/cli/checks/epic/solution-architect.json +192 -0
  67. package/cli/checks/epic/test-architect.json +90 -0
  68. package/cli/checks/epic/ui.json +102 -0
  69. package/cli/checks/epic/ux.json +90 -0
  70. package/cli/checks/fixes/epic-fix-template.md +10 -0
  71. package/cli/checks/fixes/story-fix-template.md +10 -0
  72. package/cli/checks/story/api.json +186 -0
  73. package/cli/checks/story/backend.json +102 -0
  74. package/cli/checks/story/cloud.json +102 -0
  75. package/cli/checks/story/data.json +210 -0
  76. package/cli/checks/story/database.json +102 -0
  77. package/cli/checks/story/developer.json +168 -0
  78. package/cli/checks/story/devops.json +102 -0
  79. package/cli/checks/story/frontend.json +174 -0
  80. package/cli/checks/story/mobile.json +102 -0
  81. package/cli/checks/story/qa.json +210 -0
  82. package/cli/checks/story/security.json +198 -0
  83. package/cli/checks/story/solution-architect.json +230 -0
  84. package/cli/checks/story/test-architect.json +210 -0
  85. package/cli/checks/story/ui.json +102 -0
  86. package/cli/checks/story/ux.json +102 -0
  87. package/cli/coding-order.js +401 -0
  88. package/cli/command-logger.js +49 -12
  89. package/cli/components/static-output.js +63 -0
  90. package/cli/console-output-manager.js +94 -0
  91. package/cli/dependency-checker.js +72 -0
  92. package/cli/docs-sync.js +306 -0
  93. package/cli/epic-story-validator.js +659 -0
  94. package/cli/evaluation-prompts.js +1008 -0
  95. package/cli/execution-context.js +195 -0
  96. package/cli/generate-summary-table.js +340 -0
  97. package/cli/init-model-config.js +704 -0
  98. package/cli/init.js +1737 -278
  99. package/cli/kanban-server-manager.js +227 -0
  100. package/cli/llm-claude.js +150 -1
  101. package/cli/llm-gemini.js +109 -0
  102. package/cli/llm-local.js +493 -0
  103. package/cli/llm-mock.js +233 -0
  104. package/cli/llm-openai.js +454 -0
  105. package/cli/llm-provider.js +379 -3
  106. package/cli/llm-token-limits.js +211 -0
  107. package/cli/llm-verifier.js +662 -0
  108. package/cli/llm-xiaomi.js +143 -0
  109. package/cli/message-constants.js +49 -0
  110. package/cli/message-manager.js +334 -0
  111. package/cli/message-types.js +96 -0
  112. package/cli/messaging-api.js +291 -0
  113. package/cli/micro-check-fixer.js +335 -0
  114. package/cli/micro-check-runner.js +449 -0
  115. package/cli/micro-check-scorer.js +148 -0
  116. package/cli/micro-check-validator.js +538 -0
  117. package/cli/model-pricing.js +192 -0
  118. package/cli/model-query-engine.js +468 -0
  119. package/cli/model-recommendation-analyzer.js +495 -0
  120. package/cli/model-selector.js +270 -0
  121. package/cli/output-buffer.js +107 -0
  122. package/cli/process-manager.js +73 -2
  123. package/cli/prompt-logger.js +57 -0
  124. package/cli/repl-ink.js +4625 -1094
  125. package/cli/repl-old.js +3 -4
  126. package/cli/seed-processor.js +962 -0
  127. package/cli/sprint-planning-processor.js +4162 -0
  128. package/cli/template-processor.js +2149 -105
  129. package/cli/templates/project.md +25 -8
  130. package/cli/templates/vitepress-config.mts.template +5 -4
  131. package/cli/token-tracker.js +547 -0
  132. package/cli/tools/generate-story-validators.js +317 -0
  133. package/cli/tools/generate-validators.js +669 -0
  134. package/cli/update-checker.js +19 -17
  135. package/cli/update-notifier.js +4 -4
  136. package/cli/validation-router.js +667 -0
  137. package/cli/verification-tracker.js +563 -0
  138. package/cli/worktree-runner.js +654 -0
  139. package/kanban/README.md +386 -0
  140. package/kanban/client/README.md +205 -0
  141. package/kanban/client/components.json +20 -0
  142. package/kanban/client/dist/assets/index-D_KC5EQT.css +1 -0
  143. package/kanban/client/dist/assets/index-DjY5zqW7.js +351 -0
  144. package/kanban/client/dist/index.html +16 -0
  145. package/kanban/client/dist/vite.svg +1 -0
  146. package/kanban/client/index.html +15 -0
  147. package/kanban/client/package-lock.json +9442 -0
  148. package/kanban/client/package.json +44 -0
  149. package/kanban/client/postcss.config.js +6 -0
  150. package/kanban/client/public/vite.svg +1 -0
  151. package/kanban/client/src/App.jsx +651 -0
  152. package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
  153. package/kanban/client/src/components/ceremony/AskArchPopup.jsx +420 -0
  154. package/kanban/client/src/components/ceremony/AskModelPopup.jsx +629 -0
  155. package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +1133 -0
  156. package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
  157. package/kanban/client/src/components/ceremony/ProviderSwitcherButton.jsx +290 -0
  158. package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +686 -0
  159. package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +838 -0
  160. package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
  161. package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +136 -0
  162. package/kanban/client/src/components/ceremony/steps/DatabaseStep.jsx +202 -0
  163. package/kanban/client/src/components/ceremony/steps/DeploymentStep.jsx +123 -0
  164. package/kanban/client/src/components/ceremony/steps/MissionStep.jsx +106 -0
  165. package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +329 -0
  166. package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +249 -0
  167. package/kanban/client/src/components/kanban/CardDetailModal.jsx +646 -0
  168. package/kanban/client/src/components/kanban/EpicSection.jsx +146 -0
  169. package/kanban/client/src/components/kanban/FilterToolbar.jsx +222 -0
  170. package/kanban/client/src/components/kanban/GroupingSelector.jsx +63 -0
  171. package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
  172. package/kanban/client/src/components/kanban/KanbanCard.jsx +147 -0
  173. package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
  174. package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +784 -0
  175. package/kanban/client/src/components/kanban/RunButton.jsx +162 -0
  176. package/kanban/client/src/components/kanban/SeedButton.jsx +176 -0
  177. package/kanban/client/src/components/layout/LoadingScreen.jsx +82 -0
  178. package/kanban/client/src/components/process/ProcessMonitorBar.jsx +80 -0
  179. package/kanban/client/src/components/settings/AgentEditorPopup.jsx +171 -0
  180. package/kanban/client/src/components/settings/AgentsTab.jsx +381 -0
  181. package/kanban/client/src/components/settings/ApiKeysTab.jsx +142 -0
  182. package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +105 -0
  183. package/kanban/client/src/components/settings/CheckEditorPopup.jsx +507 -0
  184. package/kanban/client/src/components/settings/CostThresholdsTab.jsx +95 -0
  185. package/kanban/client/src/components/settings/ModelPricingTab.jsx +269 -0
  186. package/kanban/client/src/components/settings/OpenAIAuthSection.jsx +412 -0
  187. package/kanban/client/src/components/settings/ServersTab.jsx +121 -0
  188. package/kanban/client/src/components/settings/SettingsModal.jsx +84 -0
  189. package/kanban/client/src/components/stats/CostModal.jsx +384 -0
  190. package/kanban/client/src/components/ui/badge.jsx +27 -0
  191. package/kanban/client/src/components/ui/dialog.jsx +121 -0
  192. package/kanban/client/src/components/ui/tabs.jsx +85 -0
  193. package/kanban/client/src/hooks/__tests__/useGrouping.test.js +232 -0
  194. package/kanban/client/src/hooks/useGrouping.js +177 -0
  195. package/kanban/client/src/hooks/useWebSocket.js +120 -0
  196. package/kanban/client/src/lib/__tests__/api.test.js +196 -0
  197. package/kanban/client/src/lib/__tests__/status-grouping.test.js +94 -0
  198. package/kanban/client/src/lib/api.js +515 -0
  199. package/kanban/client/src/lib/status-grouping.js +154 -0
  200. package/kanban/client/src/lib/utils.js +11 -0
  201. package/kanban/client/src/main.jsx +10 -0
  202. package/kanban/client/src/store/__tests__/kanbanStore.test.js +164 -0
  203. package/kanban/client/src/store/ceremonyStore.js +172 -0
  204. package/kanban/client/src/store/filterStore.js +201 -0
  205. package/kanban/client/src/store/kanbanStore.js +123 -0
  206. package/kanban/client/src/store/processStore.js +65 -0
  207. package/kanban/client/src/store/sprintPlanningStore.js +33 -0
  208. package/kanban/client/src/styles/globals.css +59 -0
  209. package/kanban/client/tailwind.config.js +77 -0
  210. package/kanban/client/vite.config.js +28 -0
  211. package/kanban/client/vitest.config.js +28 -0
  212. package/kanban/dev-start.sh +47 -0
  213. package/kanban/package.json +12 -0
  214. package/kanban/server/index.js +537 -0
  215. package/kanban/server/routes/ceremony.js +454 -0
  216. package/kanban/server/routes/costs.js +163 -0
  217. package/kanban/server/routes/openai-oauth.js +366 -0
  218. package/kanban/server/routes/processes.js +50 -0
  219. package/kanban/server/routes/settings.js +736 -0
  220. package/kanban/server/routes/websocket.js +281 -0
  221. package/kanban/server/routes/work-items.js +487 -0
  222. package/kanban/server/services/CeremonyService.js +1441 -0
  223. package/kanban/server/services/FileSystemScanner.js +95 -0
  224. package/kanban/server/services/FileWatcher.js +144 -0
  225. package/kanban/server/services/HierarchyBuilder.js +196 -0
  226. package/kanban/server/services/ProcessRegistry.js +122 -0
  227. package/kanban/server/services/TaskRunnerService.js +261 -0
  228. package/kanban/server/services/WorkItemReader.js +123 -0
  229. package/kanban/server/services/WorkItemRefineService.js +510 -0
  230. package/kanban/server/start.js +49 -0
  231. package/kanban/server/utils/kanban-logger.js +132 -0
  232. package/kanban/server/utils/markdown.js +91 -0
  233. package/kanban/server/utils/status-grouping.js +107 -0
  234. package/kanban/server/workers/run-task-worker.js +121 -0
  235. package/kanban/server/workers/seed-worker.js +94 -0
  236. package/kanban/server/workers/sponsor-call-worker.js +92 -0
  237. package/kanban/server/workers/sprint-planning-worker.js +212 -0
  238. package/package.json +19 -7
  239. package/cli/agents/documentation.md +0 -302
@@ -0,0 +1,91 @@
1
+ import { marked } from 'marked';
2
+
3
+ /**
4
+ * Configure marked with safe defaults
5
+ */
6
+ marked.setOptions({
7
+ gfm: true, // GitHub Flavored Markdown
8
+ breaks: true, // Line breaks as <br>
9
+ headerIds: true, // Add IDs to headers
10
+ mangle: false, // Don't mangle email addresses
11
+ });
12
+
13
+ /**
14
+ * Render markdown to HTML
15
+ * @param {string} markdown - Markdown content
16
+ * @returns {string} HTML content
17
+ */
18
+ export function renderMarkdown(markdown) {
19
+ if (!markdown || typeof markdown !== 'string') {
20
+ return '';
21
+ }
22
+
23
+ try {
24
+ return marked.parse(markdown);
25
+ } catch (error) {
26
+ console.error('Error rendering markdown:', error);
27
+ return `<p>Error rendering markdown: ${error.message}</p>`;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Sanitize markdown content (remove potentially dangerous content)
33
+ * Note: For MVP, we're assuming trusted content from .avc/project/
34
+ * In production, consider using DOMPurify or similar
35
+ * @param {string} html - HTML content
36
+ * @returns {string} Sanitized HTML
37
+ */
38
+ export function sanitizeHtml(html) {
39
+ // For now, just return as-is since content is from trusted source
40
+ // TODO: Add proper sanitization if exposing to untrusted users
41
+ return html;
42
+ }
43
+
44
+ /**
45
+ * Extract the description (first paragraph after H1) from a doc.md string.
46
+ * @param {string} markdown
47
+ * @returns {string}
48
+ */
49
+ export function extractDescriptionFromDoc(markdown) {
50
+ if (!markdown) return '';
51
+ const lines = markdown.split('\n');
52
+ let pastH1 = false;
53
+ const descLines = [];
54
+ for (const line of lines) {
55
+ if (!pastH1) {
56
+ if (line.startsWith('# ')) pastH1 = true;
57
+ continue;
58
+ }
59
+ if (line.startsWith('#') || line.startsWith('---')) break;
60
+ if (line.trim() === '' && descLines.length > 0) break;
61
+ if (line.trim()) descLines.push(line.trim());
62
+ }
63
+ return descLines.join(' ');
64
+ }
65
+
66
+ /**
67
+ * Replace the first paragraph after H1 with newDescription.
68
+ * Preserves all content below the first paragraph unchanged.
69
+ * @param {string} markdown
70
+ * @param {string} newDescription
71
+ * @returns {string}
72
+ */
73
+ export function updateDescriptionInDoc(markdown, newDescription) {
74
+ if (!markdown) return `\n\n${newDescription}\n`;
75
+ const lines = markdown.split('\n');
76
+ let h1Idx = -1, paraStart = -1, paraEnd = -1;
77
+ for (let i = 0; i < lines.length; i++) {
78
+ if (h1Idx === -1 && lines[i].startsWith('# ')) { h1Idx = i; continue; }
79
+ if (h1Idx !== -1 && paraStart === -1 && lines[i].trim()) { paraStart = i; continue; }
80
+ if (paraStart !== -1 && (!lines[i].trim() || lines[i].startsWith('#') || lines[i] === '---')) {
81
+ paraEnd = i; break;
82
+ }
83
+ }
84
+ if (paraStart === -1) {
85
+ lines.splice(h1Idx + 1, 0, '', newDescription);
86
+ return lines.join('\n');
87
+ }
88
+ if (paraEnd === -1) paraEnd = lines.length;
89
+ lines.splice(paraStart, paraEnd - paraStart, newDescription);
90
+ return lines.join('\n');
91
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Status Column Grouping Logic
3
+ * Maps AVC work item statuses to logical kanban columns
4
+ */
5
+
6
+ /**
7
+ * Maps status values to their display column
8
+ * Key: Column name
9
+ * Value: Array of status values that belong in that column
10
+ */
11
+ export const STATUS_COLUMN_MAPPING = {
12
+ Backlog: ['planned', 'pending'],
13
+ Ready: ['ready'],
14
+ 'In Progress': ['implementing', 'feedback'],
15
+ Review: ['implemented', 'testing'],
16
+ Done: ['completed'],
17
+ };
18
+
19
+ /**
20
+ * Metadata for each status value
21
+ * Used for badges, colors, icons within grouped columns
22
+ */
23
+ export const STATUS_METADATA = {
24
+ planned: { color: 'gray', icon: '📋', label: 'Planned' },
25
+ pending: { color: 'slate', icon: '⏸️', label: 'Pending' },
26
+ ready: { color: 'blue', icon: '🚀', label: 'Ready' },
27
+ implementing: { color: 'yellow', icon: '⚙️', label: 'Implementing' },
28
+ feedback: { color: 'amber', icon: '💬', label: 'Feedback' },
29
+ implemented: { color: 'purple', icon: '✅', label: 'Implemented' },
30
+ testing: { color: 'violet', icon: '🧪', label: 'Testing' },
31
+ completed: { color: 'green', icon: '🎉', label: 'Completed' },
32
+ blocked: { color: 'red', icon: '🚫', label: 'Blocked' },
33
+ };
34
+
35
+ /**
36
+ * Canonical order for columns on the board
37
+ */
38
+ export const COLUMN_ORDER = ['Backlog', 'Ready', 'In Progress', 'Review', 'Done'];
39
+
40
+ /**
41
+ * Get the column name for a given status
42
+ * @param {string} status - Work item status
43
+ * @returns {string|null} Column name or null if not found
44
+ */
45
+ export function getColumnForStatus(status) {
46
+ for (const [column, statuses] of Object.entries(STATUS_COLUMN_MAPPING)) {
47
+ if (statuses.includes(status)) {
48
+ return column;
49
+ }
50
+ }
51
+ return null;
52
+ }
53
+
54
+ /**
55
+ * Get metadata for a status
56
+ * @param {string} status - Work item status
57
+ * @returns {object|null} Status metadata or null if not found
58
+ */
59
+ export function getStatusMetadata(status) {
60
+ return STATUS_METADATA[status] || null;
61
+ }
62
+
63
+ /**
64
+ * Group work items by column
65
+ * @param {Array} workItems - Array of work items
66
+ * @returns {object} Object with column names as keys, arrays of work items as values
67
+ */
68
+ export function groupItemsByColumn(workItems) {
69
+ const grouped = {};
70
+
71
+ // Initialize all columns as empty arrays
72
+ COLUMN_ORDER.forEach((column) => {
73
+ grouped[column] = [];
74
+ });
75
+
76
+ // Group items by their column
77
+ workItems.forEach((item) => {
78
+ const column = getColumnForStatus(item.status);
79
+ if (column && grouped[column]) {
80
+ grouped[column].push(item);
81
+ }
82
+ });
83
+
84
+ return grouped;
85
+ }
86
+
87
+ /**
88
+ * Get statistics for a column
89
+ * @param {Array} workItems - Work items in the column
90
+ * @returns {object} Statistics object with total count and breakdown by status
91
+ */
92
+ export function getColumnStats(workItems) {
93
+ const stats = {
94
+ total: workItems.length,
95
+ byStatus: {},
96
+ };
97
+
98
+ workItems.forEach((item) => {
99
+ const status = item.status;
100
+ if (!stats.byStatus[status]) {
101
+ stats.byStatus[status] = 0;
102
+ }
103
+ stats.byStatus[status]++;
104
+ });
105
+
106
+ return stats;
107
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Run Task Worker
3
+ * Forked by TaskRunnerService.runTask().
4
+ * Creates a git worktree, implements code via LLM, runs tests, commits, merges.
5
+ *
6
+ * Parent → Worker:
7
+ * { type: 'init', taskId }
8
+ * { type: 'cancel' }
9
+ *
10
+ * Worker → Parent:
11
+ * { type: 'progress', message }
12
+ * { type: 'complete', result: { success, taskId } }
13
+ * { type: 'cancelled' }
14
+ * { type: 'error', error }
15
+ */
16
+
17
+ import { CommandLogger } from '../../../cli/command-logger.js';
18
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
19
+ import { join } from 'path';
20
+
21
+ let _cancelled = false;
22
+
23
+ process.on('disconnect', () => {
24
+ _cancelled = true;
25
+ setTimeout(() => process.exit(1), 5000).unref();
26
+ });
27
+
28
+ process.on('message', async (msg) => {
29
+ if (msg.type === 'init') {
30
+ run(msg.taskId);
31
+ } else if (msg.type === 'cancel') {
32
+ _cancelled = true;
33
+ }
34
+ });
35
+
36
+ /**
37
+ * Update work.json status on disk.
38
+ */
39
+ function updateStatus(taskId, status) {
40
+ const projectRoot = process.cwd();
41
+ // Find the work.json by walking the hierarchy
42
+ // Task ID: context-0001-0001-0001 → path: .avc/project/context-0001/context-0001-0001/context-0001-0001-0001/work.json
43
+ const idParts = taskId.replace('context-', '').split('-');
44
+ let dir = join(projectRoot, '.avc', 'project');
45
+ let current = 'context';
46
+ for (const part of idParts) {
47
+ current += `-${part}`;
48
+ dir = join(dir, current);
49
+ }
50
+ const workJsonPath = join(dir, 'work.json');
51
+
52
+ if (existsSync(workJsonPath)) {
53
+ try {
54
+ const workJson = JSON.parse(readFileSync(workJsonPath, 'utf8'));
55
+ workJson.status = status;
56
+ workJson.updated = new Date().toISOString();
57
+ writeFileSync(workJsonPath, JSON.stringify(workJson, null, 2), 'utf8');
58
+ } catch {}
59
+ }
60
+ }
61
+
62
+ async function run(taskId) {
63
+ const logger = new CommandLogger('run-task', process.cwd());
64
+ logger.start();
65
+
66
+ try {
67
+ const { WorktreeRunner } = await import('../../../cli/worktree-runner.js');
68
+
69
+ // Set status to implementing
70
+ updateStatus(taskId, 'implementing');
71
+ try { process.send({ type: 'progress', message: `Starting implementation of ${taskId}` }); } catch {}
72
+
73
+ const runner = new WorktreeRunner(taskId, process.cwd());
74
+
75
+ const progressCallback = (message) => {
76
+ if (_cancelled) return;
77
+ try { process.send({ type: 'progress', message }); } catch {}
78
+ };
79
+
80
+ const cancelledCheck = () => _cancelled;
81
+
82
+ const result = await runner.execute(progressCallback, cancelledCheck);
83
+
84
+ if (_cancelled) {
85
+ updateStatus(taskId, 'planned'); // Reset on cancel
86
+ try { process.send({ type: 'cancelled' }); } catch {}
87
+ } else if (result.success) {
88
+ updateStatus(taskId, 'completed');
89
+ try {
90
+ process.send({
91
+ type: 'complete',
92
+ result: { success: true, taskId },
93
+ });
94
+ } catch {}
95
+ } else {
96
+ updateStatus(taskId, 'failed');
97
+ try {
98
+ process.send({
99
+ type: 'error',
100
+ error: { message: result.error || 'Implementation failed' },
101
+ });
102
+ } catch {}
103
+ }
104
+ } catch (error) {
105
+ if (error.message === 'CANCELLED') {
106
+ updateStatus(taskId, 'planned');
107
+ try { process.send({ type: 'cancelled' }); } catch {}
108
+ } else {
109
+ updateStatus(taskId, 'failed');
110
+ try {
111
+ process.send({
112
+ type: 'error',
113
+ error: { message: error.message, stack: error.stack },
114
+ });
115
+ } catch {}
116
+ }
117
+ } finally {
118
+ logger.stop();
119
+ setTimeout(() => process.exit(0), 500).unref();
120
+ }
121
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Seed Worker
3
+ * Forked by TaskRunnerService.runSeed().
4
+ * Communicates with parent via IPC (process.send / process.on('message')).
5
+ *
6
+ * Parent → Worker:
7
+ * { type: 'init', storyId }
8
+ * { type: 'cancel' }
9
+ *
10
+ * Worker → Parent:
11
+ * { type: 'progress', message }
12
+ * { type: 'item-written', itemId, itemType }
13
+ * { type: 'complete', result: { taskCount, subtaskCount, taskIds } }
14
+ * { type: 'cancelled' }
15
+ * { type: 'error', error }
16
+ */
17
+
18
+ import { CommandLogger } from '../../../cli/command-logger.js';
19
+
20
+ let _cancelled = false;
21
+
22
+ process.on('disconnect', () => {
23
+ _cancelled = true;
24
+ setTimeout(() => process.exit(1), 5000).unref();
25
+ });
26
+
27
+ process.on('message', async (msg) => {
28
+ if (msg.type === 'init') {
29
+ run(msg.storyId);
30
+ } else if (msg.type === 'cancel') {
31
+ _cancelled = true;
32
+ }
33
+ });
34
+
35
+ async function run(storyId) {
36
+ const logger = new CommandLogger('seed', process.cwd());
37
+ logger.start();
38
+
39
+ try {
40
+ // Dynamic import to avoid loading CLI modules at fork time
41
+ const { SeedProcessor } = await import('../../../cli/seed-processor.js');
42
+
43
+ const processor = new SeedProcessor(storyId);
44
+
45
+ const progressCallback = (message) => {
46
+ if (_cancelled) return;
47
+ try { process.send({ type: 'progress', message }); } catch {}
48
+ };
49
+
50
+ const itemWrittenCallback = ({ itemId, itemType }) => {
51
+ if (_cancelled) return;
52
+ try { process.send({ type: 'item-written', itemId, itemType }); } catch {}
53
+ };
54
+
55
+ const cancelledCheck = () => _cancelled;
56
+
57
+ const result = await processor.executeWithCallback(
58
+ progressCallback,
59
+ itemWrittenCallback,
60
+ cancelledCheck
61
+ );
62
+
63
+ if (_cancelled) {
64
+ try { process.send({ type: 'cancelled' }); } catch {}
65
+ } else {
66
+ try {
67
+ process.send({
68
+ type: 'complete',
69
+ result: {
70
+ storyId,
71
+ taskCount: result.taskCount,
72
+ subtaskCount: result.subtaskCount,
73
+ taskIds: result.taskIds,
74
+ },
75
+ });
76
+ } catch {}
77
+ }
78
+ } catch (error) {
79
+ if (error.message === 'CEREMONY_CANCELLED') {
80
+ try { process.send({ type: 'cancelled' }); } catch {}
81
+ } else {
82
+ try {
83
+ process.send({
84
+ type: 'error',
85
+ error: { message: error.message, stack: error.stack },
86
+ });
87
+ } catch {}
88
+ }
89
+ } finally {
90
+ logger.stop();
91
+ // Give parent time to receive the final message
92
+ setTimeout(() => process.exit(0), 500).unref();
93
+ }
94
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Sponsor Call Worker
3
+ * Forked by CeremonyService.runSponsorCallInProcess().
4
+ * Same IPC protocol as sprint-planning-worker.js.
5
+ * Receives `requirements` in the init message.
6
+ */
7
+
8
+ import { ProjectInitiator } from '../../../cli/init.js';
9
+ import { CommandLogger } from '../../../cli/command-logger.js';
10
+
11
+ let _paused = false;
12
+ let _cancelled = false;
13
+ let _requirements = null;
14
+ let _costThreshold = null;
15
+ let _waitingCostLimit = false;
16
+
17
+ // Parent server stopped — exit rather than running as an orphan.
18
+ process.on('disconnect', () => {
19
+ _cancelled = true;
20
+ // Give the current LLM call up to 5s to finish, then hard-exit.
21
+ setTimeout(() => process.exit(1), 5000).unref();
22
+ });
23
+
24
+ process.on('message', async (msg) => {
25
+ if (msg.type === 'init') {
26
+ _requirements = msg.requirements;
27
+ _costThreshold = msg.costThreshold ?? null;
28
+ run();
29
+ } else if (msg.type === 'pause') {
30
+ _paused = true;
31
+ process.send({ type: 'paused' });
32
+ } else if (msg.type === 'resume') {
33
+ _paused = false;
34
+ process.send({ type: 'resumed' });
35
+ } else if (msg.type === 'cancel') {
36
+ _cancelled = true;
37
+ } else if (msg.type === 'cost-limit-continue') {
38
+ _waitingCostLimit = false;
39
+ }
40
+ });
41
+
42
+ async function run() {
43
+ const logger = new CommandLogger('sponsor-call', process.cwd());
44
+ logger.start();
45
+ try {
46
+ const initiator = new ProjectInitiator();
47
+
48
+ const progressCallback = async (msg, substep, meta) => {
49
+ if (_cancelled) throw new Error('CEREMONY_CANCELLED');
50
+ while (_paused) {
51
+ await new Promise(r => setTimeout(r, 200));
52
+ if (_cancelled) throw new Error('CEREMONY_CANCELLED');
53
+ }
54
+ if (msg) process.send({ type: 'progress', message: msg });
55
+ if (substep) process.send({ type: 'substep', substep, meta: meta || {} });
56
+ if (meta?.detail) process.send({ type: 'detail', detail: meta.detail });
57
+ };
58
+
59
+ const costLimitReachedCallback = async (cost) => {
60
+ _waitingCostLimit = true;
61
+ process.send({ type: 'cost-limit', cost, threshold: _costThreshold });
62
+ while (_waitingCostLimit) {
63
+ await new Promise(r => setTimeout(r, 200));
64
+ if (_cancelled) throw new Error('CEREMONY_CANCELLED');
65
+ }
66
+ };
67
+
68
+ const result = await initiator.sponsorCallWithAnswers(_requirements, progressCallback, {
69
+ costThreshold: _costThreshold,
70
+ costLimitReachedCallback,
71
+ });
72
+ logger.stop();
73
+ // sponsorCallWithAnswers returns { error: true, message } on validation failure instead of throwing
74
+ if (result?.error === true) {
75
+ process.send({ type: 'error', error: result.message || 'Ceremony failed' });
76
+ } else {
77
+ process.send({ type: 'complete', result });
78
+ }
79
+ process.exit(0);
80
+ } catch (err) {
81
+ logger.stop();
82
+ const msg = err.message === 'CEREMONY_CANCELLED'
83
+ ? { type: 'cancelled' }
84
+ : { type: 'error', error: err.message };
85
+ // Guard against ERR_IPC_CHANNEL_CLOSED if the parent already disconnected.
86
+ try {
87
+ process.send(msg, () => process.exit(0));
88
+ } catch (_) {
89
+ process.exit(0);
90
+ }
91
+ }
92
+ }