codymaster 4.1.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 (193) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +285 -0
  3. package/adapters/antigravity.js +15 -0
  4. package/adapters/claude-code.js +17 -0
  5. package/adapters/cursor.js +16 -0
  6. package/commands/bootstrap.md +49 -0
  7. package/commands/build.md +48 -0
  8. package/commands/content.md +48 -0
  9. package/commands/continuity.md +60 -0
  10. package/commands/debug.md +51 -0
  11. package/commands/demo.md +96 -0
  12. package/commands/deploy.md +51 -0
  13. package/commands/plan.md +42 -0
  14. package/commands/review.md +55 -0
  15. package/commands/track.md +46 -0
  16. package/commands/ux.md +46 -0
  17. package/dist/agent-dispatch.js +161 -0
  18. package/dist/chains/builtin.js +85 -0
  19. package/dist/continuity.js +385 -0
  20. package/dist/dashboard.js +926 -0
  21. package/dist/data.js +122 -0
  22. package/dist/index.js +2434 -0
  23. package/dist/judge.js +252 -0
  24. package/dist/parallel-dispatch.js +359 -0
  25. package/dist/parallel-quality.js +172 -0
  26. package/dist/skill-chain.js +258 -0
  27. package/install.sh +513 -0
  28. package/package.json +79 -0
  29. package/skills/.content-factory-state.json +132 -0
  30. package/skills/.git 2/logs/refs/heads/main +1 -0
  31. package/skills/.git 2/logs/refs/remotes/origin/main +1 -0
  32. package/skills/.git 2/objects/02/fb0956734b5f8ba3f918b7defd04a89cfe0076 +0 -0
  33. package/skills/.git 2/objects/08/1e129d75dc6feac6c02037272e6bd1a04e3324 +0 -0
  34. package/skills/.git 2/objects/0c/5393416f3c5e01c9a655a802bff0dd52f76f0a +0 -0
  35. package/skills/.git 2/objects/10/0b9be46978a946a77188f68be725098a122001 +0 -0
  36. package/skills/.git 2/objects/10/cf041167fc9843610eb3d90259ef3396315fdc +0 -0
  37. package/skills/.git 2/objects/12/5e19538dd6e1338ffe74f6c4c165b00435bf48 +0 -0
  38. package/skills/.git 2/objects/16/a9b9d0088d5c1347628b45a2620b479d8ad57c +0 -0
  39. package/skills/.git 2/objects/17/8c2a9ef93c33ae4eec9d58e82321f9229843a1 +0 -0
  40. package/skills/.git 2/objects/25/397ae41d09104d763bdcac2695209d85cdea89 +0 -0
  41. package/skills/.git 2/objects/2f/a836b7947f2d458e1f639788bf4bb0983a3305 +0 -0
  42. package/skills/.git 2/objects/3a/baaaf0a1c0909c0828335791557125fba911e0 +0 -0
  43. package/skills/.git 2/objects/42/2924221b81f5ce3c4e4daac9a64a24f9b01f9a +0 -0
  44. package/skills/.git 2/objects/42/ec0ce707447dc11446a34c9995fb8533801731 +0 -0
  45. package/skills/.git 2/objects/46/e43ce92866d56ce74b1d750db307cfe6154a15 +0 -0
  46. package/skills/.git 2/objects/48/5e41b633c63f55b8277bcc59f44f67681f671a +0 -0
  47. package/skills/.git 2/objects/49/49c596a3a89fa240642acd95dd3258e261eb09 +0 -0
  48. package/skills/.git 2/objects/50/9d42d8412ef8eaf7f7e138476bac2e4d10ce60 +0 -0
  49. package/skills/.git 2/objects/55/0c8c389d981b463ef849aeb792d8be3ccb6ec8 +0 -0
  50. package/skills/.git 2/objects/5d/82d3b18410cdda3ace3677436f0cb599dbe2d2 +0 -0
  51. package/skills/.git 2/objects/60/0617c58e871a38b33bf29e282d132bb3c381ad +0 -0
  52. package/skills/.git 2/objects/6a/8369a99c687b7245c92ffaf0e0f0dab9014504 +0 -0
  53. package/skills/.git 2/objects/79/bea435d40ab531c1aaf6be0432c6a5b7aaed21 +0 -0
  54. package/skills/.git 2/objects/7e/5ebd79251c2f14e4aceb86c74b6b6daae6b500 +0 -0
  55. package/skills/.git 2/objects/81/98a822a60178d6d5023ddb3e222cddf048742e +0 -0
  56. package/skills/.git 2/objects/86/0a0e1943dfe53411d2e499a1f16f46a96ef758 +0 -0
  57. package/skills/.git 2/objects/86/971fb55fdc081fdbae52376f0f13e57a4e9b04 +0 -0
  58. package/skills/.git 2/objects/88/b89dd609a0a03f8d4fe8bfde20d5b8fc1d326d +0 -0
  59. package/skills/.git 2/objects/90/8737edb6b7809e32cc01590b4e08ba42a9d40d +0 -0
  60. package/skills/.git 2/objects/93/d5a8a9a7d4fb7f11491cb596a6880528725118 +0 -0
  61. package/skills/.git 2/objects/98/46a2ab81d0c3b3eb00ef88fc56989aa7e9f316 +0 -0
  62. package/skills/.git 2/objects/9b/d8dd1e49cf274eaf9c555f3ab39dce7af5715e +0 -0
  63. package/skills/.git 2/objects/a1/13329fb0cec96ae78b222d33a24c3b5bc7fa1f +0 -0
  64. package/skills/.git 2/objects/a9/e6effe626e8a3aea3a8fc3364b492191c6e7d0 +0 -0
  65. package/skills/.git 2/objects/ad/6de7e48d9782cca9353d1ff0aa1aab7fe1df85 +0 -0
  66. package/skills/.git 2/objects/af/54ae316f771ff692e299ffcd8bf2f06b413b59 +0 -0
  67. package/skills/.git 2/objects/b0/4cb8b0b00dad633e731c1472161419e738d674 +0 -0
  68. package/skills/.git 2/objects/b3/094abb0b9ed46419b269e4a4e36a459690e3b0 +0 -0
  69. package/skills/.git 2/objects/b9/435c5d4baac2cfc5c83009ddd27b46b60db5f1 +0 -0
  70. package/skills/.git 2/objects/ba/5da17dbaec5ec2dcfdfd126aead518d1171d5c +0 -0
  71. package/skills/.git 2/objects/c0/bf58703aa258ba5dd63083bebaec8f223d844c +0 -0
  72. package/skills/.git 2/objects/c4/701a34edf1fc1bad58ccc57bd03f9426acb59a +0 -0
  73. package/skills/.git 2/objects/c7/5ccce9a4e5cc74d9b3174550cf6d993ca43638 +0 -0
  74. package/skills/.git 2/objects/c7/710d59b5a35b0f1f0a0399386643a0bd94c929 +0 -0
  75. package/skills/.git 2/objects/d1/fe58237112e953e5fec52da22cf38e08be3df9 +5 -0
  76. package/skills/.git 2/objects/d2/2bbe9fd2f74c95bc5583e803f5e435f1e2cd86 +0 -0
  77. package/skills/.git 2/objects/d7/e72852ea2bff74581dbf247d400120086229f4 +0 -0
  78. package/skills/.git 2/objects/d8/d4c3b5553e4fd72807e1d4b49ef07d9ef3ac35 +0 -0
  79. package/skills/.git 2/objects/dc/75050c2876f6a02ae2a53a3c886f395b622977 +0 -0
  80. package/skills/.git 2/objects/ee/e8546f95acec500187c08a28a8b9ee02db0dec +0 -0
  81. package/skills/.git 2/objects/ef/263c059208b416c2146434f10cb2b9fabcba16 +0 -0
  82. package/skills/.git 2/objects/f3/ae597e84d9a59b88acd21c99bde2eaf686d785 +0 -0
  83. package/skills/.git 2/objects/f3/f6f5673c821d3d8e76fa267a9e882e7a5387ea +0 -0
  84. package/skills/.git 2/objects/f9/6e6d0ad02624dd11d5848594d056caef7a5e8b +0 -0
  85. package/skills/.git 2/objects/ff/278988fc1edf0db3abcf18de795f4cc0b4f3e1 +0 -0
  86. package/skills/.git 2/refs/heads/main +1 -0
  87. package/skills/.git 2/refs/remotes/origin/main +1 -0
  88. package/skills/.pytest_cache 2/v/cache/nodeids +76 -0
  89. package/skills/.pytest_cache 2/v/cache/stepwise +1 -0
  90. package/skills/_shared/helpers.md +123 -0
  91. package/skills/_shared/outputs-convention.md +24 -0
  92. package/skills/cm-ads-tracker/SKILL.md +109 -0
  93. package/skills/cm-ads-tracker/evals/evals.json +55 -0
  94. package/skills/cm-ads-tracker/references/gtm-architecture.md +321 -0
  95. package/skills/cm-ads-tracker/references/industry-events.md +294 -0
  96. package/skills/cm-ads-tracker/references/platforms-api.md +238 -0
  97. package/skills/cm-ads-tracker/templates/capi-payload.md +79 -0
  98. package/skills/cm-ads-tracker/templates/datalayer-push.js +104 -0
  99. package/skills/cm-ads-tracker/templates/gtm-variables.js +56 -0
  100. package/skills/cm-brainstorm-idea/SKILL.md +423 -0
  101. package/skills/cm-code-review/SKILL.md +151 -0
  102. package/skills/cm-content-factory/SKILL.md +416 -0
  103. package/skills/cm-continuity/SKILL.md +399 -0
  104. package/skills/cm-dashboard/SKILL.md +533 -0
  105. package/skills/cm-dashboard/ui/app.js +1270 -0
  106. package/skills/cm-dashboard/ui/index.html +206 -0
  107. package/skills/cm-dashboard/ui/style.css +440 -0
  108. package/skills/cm-debugging/SKILL.md +412 -0
  109. package/skills/cm-deep-search/SKILL.md +242 -0
  110. package/skills/cm-design-system/SKILL.md +97 -0
  111. package/skills/cm-design-system/resources/halo-modern.md +40 -0
  112. package/skills/cm-design-system/resources/lunaris-advanced.md +40 -0
  113. package/skills/cm-design-system/resources/nitro-enterprise.md +39 -0
  114. package/skills/cm-design-system/resources/shadcn-default.md +37 -0
  115. package/skills/cm-dockit/README.md +100 -0
  116. package/skills/cm-dockit/SKILL.md +302 -0
  117. package/skills/cm-dockit/index.html +443 -0
  118. package/skills/cm-dockit/package-lock.json +1850 -0
  119. package/skills/cm-dockit/package.json +14 -0
  120. package/skills/cm-dockit/prompts/analysis.md +34 -0
  121. package/skills/cm-dockit/prompts/api-reference.md +24 -0
  122. package/skills/cm-dockit/prompts/architecture.md +21 -0
  123. package/skills/cm-dockit/prompts/data-flow.md +20 -0
  124. package/skills/cm-dockit/prompts/database.md +21 -0
  125. package/skills/cm-dockit/prompts/deployment.md +22 -0
  126. package/skills/cm-dockit/prompts/flows.md +21 -0
  127. package/skills/cm-dockit/prompts/jtbd.md +20 -0
  128. package/skills/cm-dockit/prompts/personas.md +24 -0
  129. package/skills/cm-dockit/prompts/sop-modules.md +40 -0
  130. package/skills/cm-dockit/scripts/doc-gen.sh +121 -0
  131. package/skills/cm-dockit/scripts/dockit-dashboard.sh +142 -0
  132. package/skills/cm-dockit/scripts/dockit-runner.sh +607 -0
  133. package/skills/cm-dockit/scripts/dockit-task.sh +166 -0
  134. package/skills/cm-dockit/skills/analyze-codebase.md +174 -0
  135. package/skills/cm-dockit/skills/api-reference.md +237 -0
  136. package/skills/cm-dockit/skills/changelog-guide.md +195 -0
  137. package/skills/cm-dockit/skills/content-guidelines.md +190 -0
  138. package/skills/cm-dockit/skills/sop-guide.md +184 -0
  139. package/skills/cm-dockit/skills/tech-docs.md +287 -0
  140. package/skills/cm-dockit/templates/markdown/structure.md +60 -0
  141. package/skills/cm-dockit/templates/vitepress-premium/.vitepress/config.mts +110 -0
  142. package/skills/cm-dockit/templates/vitepress-premium/.vitepress/theme/custom.css +189 -0
  143. package/skills/cm-dockit/templates/vitepress-premium/.vitepress/theme/index.ts +4 -0
  144. package/skills/cm-dockit/templates/vitepress-premium/package.json +19 -0
  145. package/skills/cm-dockit/templates/vitepress-premium/tests/frontend.test.ts +45 -0
  146. package/skills/cm-dockit/tests/runner.test.ts +66 -0
  147. package/skills/cm-dockit/workflows/export-markdown.md +82 -0
  148. package/skills/cm-dockit/workflows/generate-docs.md +68 -0
  149. package/skills/cm-dockit/workflows/setup-vitepress.md +181 -0
  150. package/skills/cm-example/SKILL.md +26 -0
  151. package/skills/cm-execution/SKILL.md +268 -0
  152. package/skills/cm-git-worktrees/SKILL.md +164 -0
  153. package/skills/cm-how-it-work/SKILL.md +189 -0
  154. package/skills/cm-identity-guard/SKILL.md +412 -0
  155. package/skills/cm-jtbd/SKILL.md +98 -0
  156. package/skills/cm-planning/SKILL.md +130 -0
  157. package/skills/cm-project-bootstrap/SKILL.md +161 -0
  158. package/skills/cm-project-bootstrap/templates/AGENTS.md +42 -0
  159. package/skills/cm-project-bootstrap/templates/frontend-safety.test.js +51 -0
  160. package/skills/cm-project-bootstrap/templates/i18n-sync.test.js +38 -0
  161. package/skills/cm-project-bootstrap/templates/pr-template.md +12 -0
  162. package/skills/cm-project-bootstrap/templates/project-identity.json +29 -0
  163. package/skills/cm-project-bootstrap/templates/vitest.config.js +10 -0
  164. package/skills/cm-quality-gate/SKILL.md +218 -0
  165. package/skills/cm-readit/SKILL.md +289 -0
  166. package/skills/cm-readit/audio-player.md +206 -0
  167. package/skills/cm-readit/examples/blog-reader.js +352 -0
  168. package/skills/cm-readit/examples/voice-cro.js +390 -0
  169. package/skills/cm-readit/tts-engine.md +262 -0
  170. package/skills/cm-readit/ui-patterns.md +362 -0
  171. package/skills/cm-readit/voice-cro.md +223 -0
  172. package/skills/cm-safe-deploy/SKILL.md +120 -0
  173. package/skills/cm-safe-deploy/templates/deploy.sh +89 -0
  174. package/skills/cm-safe-i18n/SKILL.md +473 -0
  175. package/skills/cm-secret-shield/SKILL.md +580 -0
  176. package/skills/cm-skill-chain/SKILL.md +78 -0
  177. package/skills/cm-skill-index/SKILL.md +318 -0
  178. package/skills/cm-skill-mastery/SKILL.md +169 -0
  179. package/skills/cm-start/SKILL.md +65 -0
  180. package/skills/cm-status/SKILL.md +12 -0
  181. package/skills/cm-tdd/SKILL.md +370 -0
  182. package/skills/cm-terminal/SKILL.md +177 -0
  183. package/skills/cm-test-gate/SKILL.md +242 -0
  184. package/skills/cm-ui-preview/SKILL.md +291 -0
  185. package/skills/cm-ux-master/DESIGN_STANDARD_TEMPLATE.md +54 -0
  186. package/skills/cm-ux-master/SKILL.md +114 -0
  187. package/skills/cro-methodology/SKILL.md +98 -0
  188. package/skills/cro-methodology/references/COPYWRITING.md +178 -0
  189. package/skills/cro-methodology/references/OBJECTIONS.md +135 -0
  190. package/skills/cro-methodology/references/PERSUASION.md +158 -0
  191. package/skills/cro-methodology/references/RESEARCH.md +220 -0
  192. package/skills/cro-methodology/references/funnel-analysis.md +365 -0
  193. package/skills/cro-methodology/references/testing-methodology.md +330 -0
@@ -0,0 +1,926 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.launchDashboard = launchDashboard;
7
+ const express_1 = __importDefault(require("express"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const crypto_1 = __importDefault(require("crypto"));
12
+ const data_1 = require("./data");
13
+ const agent_dispatch_1 = require("./agent-dispatch");
14
+ const continuity_1 = require("./continuity");
15
+ const judge_1 = require("./judge");
16
+ const skill_chain_1 = require("./skill-chain");
17
+ // ─── Dashboard Server ───────────────────────────────────────────────────────
18
+ function launchDashboard(port = data_1.DEFAULT_PORT, silent = false) {
19
+ const app = (0, express_1.default)();
20
+ app.use(express_1.default.json());
21
+ const publicDir = path_1.default.join(__dirname, '..', 'public', 'dashboard');
22
+ app.use(express_1.default.static(publicDir));
23
+ // ─── Project API ────────────────────────────────────────────────────────
24
+ app.get('/api/projects', (_req, res) => {
25
+ const data = (0, data_1.loadData)();
26
+ const enriched = data.projects.map(p => {
27
+ const pt = data.tasks.filter(t => t.projectId === p.id);
28
+ return Object.assign(Object.assign({}, p), { taskCount: pt.length, doneCount: pt.filter(t => t.column === 'done').length, activeAgents: [...new Set(pt.map(t => t.agent).filter(Boolean))] });
29
+ });
30
+ res.json(enriched);
31
+ });
32
+ app.post('/api/projects', (req, res) => {
33
+ const data = (0, data_1.loadData)();
34
+ const { name, path: pp, agents } = req.body;
35
+ if (!name || typeof name !== 'string') {
36
+ res.status(400).json({ error: 'Project name is required' });
37
+ return;
38
+ }
39
+ const project = { id: crypto_1.default.randomUUID(), name: name.trim(), path: (pp || '').trim(), agents: Array.isArray(agents) ? agents : [], createdAt: new Date().toISOString() };
40
+ data.projects.push(project);
41
+ (0, data_1.logActivity)(data, 'project_created', `Project "${project.name}" created`, project.id);
42
+ (0, data_1.saveData)(data);
43
+ res.status(201).json(project);
44
+ });
45
+ app.put('/api/projects/:id', (req, res) => {
46
+ const data = (0, data_1.loadData)();
47
+ const idx = data.projects.findIndex(p => p.id === req.params.id);
48
+ if (idx === -1) {
49
+ res.status(404).json({ error: 'Project not found' });
50
+ return;
51
+ }
52
+ const { name, path: pp, agents } = req.body;
53
+ if (name)
54
+ data.projects[idx].name = String(name).trim();
55
+ if (pp !== undefined)
56
+ data.projects[idx].path = String(pp).trim();
57
+ if (Array.isArray(agents))
58
+ data.projects[idx].agents = agents;
59
+ (0, data_1.saveData)(data);
60
+ res.json(data.projects[idx]);
61
+ });
62
+ app.delete('/api/projects/:id', (req, res) => {
63
+ const data = (0, data_1.loadData)();
64
+ const idx = data.projects.findIndex(p => p.id === req.params.id);
65
+ if (idx === -1) {
66
+ res.status(404).json({ error: 'Project not found' });
67
+ return;
68
+ }
69
+ const name = data.projects[idx].name;
70
+ data.projects.splice(idx, 1);
71
+ data.tasks = data.tasks.filter(t => t.projectId !== req.params.id);
72
+ (0, data_1.logActivity)(data, 'project_deleted', `Project "${name}" deleted`, req.params.id);
73
+ (0, data_1.saveData)(data);
74
+ res.status(204).send();
75
+ });
76
+ // ─── Task API ───────────────────────────────────────────────────────────
77
+ app.get('/api/tasks', (req, res) => {
78
+ const data = (0, data_1.loadData)();
79
+ let tasks = data.tasks;
80
+ if (req.query.projectId)
81
+ tasks = tasks.filter(t => t.projectId === req.query.projectId);
82
+ res.json(tasks);
83
+ });
84
+ app.post('/api/tasks', (req, res) => {
85
+ const data = (0, data_1.loadData)();
86
+ const { title, description, column, priority, projectId, agent, skill } = req.body;
87
+ if (!title || typeof title !== 'string') {
88
+ res.status(400).json({ error: 'Title is required' });
89
+ return;
90
+ }
91
+ let rpid = projectId;
92
+ if (!rpid && data.projects.length > 0)
93
+ rpid = data.projects[0].id;
94
+ else if (!rpid) {
95
+ const dp = { id: crypto_1.default.randomUUID(), name: 'Default Project', path: process.cwd(), agents: agent ? [agent] : [], createdAt: new Date().toISOString() };
96
+ data.projects.push(dp);
97
+ rpid = dp.id;
98
+ }
99
+ const vc = ['backlog', 'in-progress', 'review', 'done'];
100
+ const tc = vc.includes(column) ? column : 'backlog';
101
+ const vp = ['low', 'medium', 'high', 'urgent'];
102
+ const tp = vp.includes(priority) ? priority : 'medium';
103
+ const ct = data.tasks.filter(t => t.column === tc && t.projectId === rpid);
104
+ const mo = ct.length > 0 ? Math.max(...ct.map(t => t.order)) : -1;
105
+ const now = new Date().toISOString();
106
+ const task = { id: crypto_1.default.randomUUID(), projectId: rpid, title: title.trim(), description: (description || '').trim(), column: tc, order: mo + 1, priority: tp, agent: (agent || '').trim(), skill: (skill || '').trim(), createdAt: now, updatedAt: now };
107
+ data.tasks.push(task);
108
+ if (agent) {
109
+ const project = data.projects.find(p => p.id === rpid);
110
+ if (project && !project.agents.includes(agent))
111
+ project.agents.push(agent);
112
+ }
113
+ (0, data_1.logActivity)(data, 'task_created', `Task "${task.title}" created`, rpid, agent || '', { taskId: task.id, column: tc });
114
+ (0, data_1.saveData)(data);
115
+ res.status(201).json(task);
116
+ });
117
+ app.post('/api/tasks/sync', (req, res) => {
118
+ const data = (0, data_1.loadData)();
119
+ const { projectId, projectName, projectPath, agent, skill, tasks: incoming } = req.body;
120
+ if (!Array.isArray(incoming) || incoming.length === 0) {
121
+ res.status(400).json({ error: 'tasks array required' });
122
+ return;
123
+ }
124
+ let project = projectId ? data.projects.find(p => p.id === projectId) : data.projects.find(p => p.name === projectName || p.path === projectPath);
125
+ if (!project) {
126
+ project = { id: crypto_1.default.randomUUID(), name: projectName || 'Untitled', path: projectPath || '', agents: agent ? [agent] : [], createdAt: new Date().toISOString() };
127
+ data.projects.push(project);
128
+ }
129
+ if (agent && !project.agents.includes(agent))
130
+ project.agents.push(agent);
131
+ const now = new Date().toISOString();
132
+ const created = [];
133
+ for (const inc of incoming) {
134
+ const col = inc.column || 'backlog';
135
+ const ct = data.tasks.filter(t => t.column === col && t.projectId === project.id);
136
+ const mo = ct.length > 0 ? Math.max(...ct.map(t => t.order)) : -1;
137
+ const task = { id: crypto_1.default.randomUUID(), projectId: project.id, title: String(inc.title || '').trim(), description: String(inc.description || '').trim(), column: col, order: mo + 1, priority: inc.priority || 'medium', agent: agent || '', skill: skill || '', createdAt: now, updatedAt: now };
138
+ data.tasks.push(task);
139
+ created.push(task);
140
+ }
141
+ (0, data_1.logActivity)(data, 'task_created', `Synced ${created.length} tasks`, project.id, agent || '', { count: created.length });
142
+ (0, data_1.saveData)(data);
143
+ res.status(201).json({ project, tasks: created });
144
+ });
145
+ // ─── Auto-Sync (Conversation Lifecycle) ─────────────────────────────
146
+ // Agents and webhooks call this to report conversation status.
147
+ // Upserts by conversationId — creates task if missing, transitions if status changes.
148
+ app.post('/api/tasks/auto-sync', (req, res) => {
149
+ const data = (0, data_1.loadData)();
150
+ const { conversationId, title, status, agent, skill, projectId, projectName, priority } = req.body;
151
+ if (!conversationId || !title) {
152
+ res.status(400).json({ error: 'conversationId and title are required' });
153
+ return;
154
+ }
155
+ // Map status to column
156
+ const STATUS_TO_COLUMN = {
157
+ 'backlog': 'backlog',
158
+ 'pending': 'backlog',
159
+ 'todo': 'backlog',
160
+ 'started': 'in-progress',
161
+ 'active': 'in-progress',
162
+ 'in-progress': 'in-progress',
163
+ 'idle': 'in-progress',
164
+ 'review': 'review',
165
+ 'completed': 'done',
166
+ 'done': 'done',
167
+ 'cancelled': 'done',
168
+ };
169
+ const column = STATUS_TO_COLUMN[status || 'active'] || 'in-progress';
170
+ // Find project — try by ID, then by name, then use first project
171
+ let project = projectId
172
+ ? data.projects.find(p => p.id === projectId)
173
+ : projectName
174
+ ? data.projects.find(p => p.name.toLowerCase() === projectName.toLowerCase())
175
+ : data.projects[0];
176
+ if (!project) {
177
+ project = {
178
+ id: crypto_1.default.randomUUID(),
179
+ name: projectName || 'Default',
180
+ path: '',
181
+ agents: agent ? [agent] : [],
182
+ createdAt: new Date().toISOString(),
183
+ };
184
+ data.projects.push(project);
185
+ }
186
+ if (agent && !project.agents.includes(agent))
187
+ project.agents.push(agent);
188
+ // Find existing task by conversationId metadata or exact title match
189
+ let taskIdx = data.tasks.findIndex(t => t.conversationId === conversationId ||
190
+ (t.title === title && t.projectId === project.id && t.column !== 'done'));
191
+ const now = new Date().toISOString();
192
+ if (taskIdx >= 0) {
193
+ // Update existing task
194
+ const task = data.tasks[taskIdx];
195
+ const oldCol = task.column;
196
+ if (oldCol !== column) {
197
+ task.column = column;
198
+ task.updatedAt = now;
199
+ task.stuckSince = undefined;
200
+ // Reorder
201
+ const targetTasks = data.tasks.filter(t => t.column === column && t.id !== task.id && t.projectId === task.projectId).sort((a, b) => a.order - b.order);
202
+ task.order = targetTasks.length;
203
+ data.tasks.filter(t => t.column === oldCol && t.projectId === task.projectId).sort((a, b) => a.order - b.order).forEach((t, i) => { t.order = i; });
204
+ const actType = column === 'done' ? 'task_done' : 'task_transitioned';
205
+ (0, data_1.logActivity)(data, actType, `Auto-sync: "${task.title}" ${oldCol} → ${column}`, task.projectId, agent || task.agent, { from: oldCol, to: column, conversationId });
206
+ }
207
+ (0, data_1.saveData)(data);
208
+ res.json({ action: 'updated', task: data.tasks[taskIdx] });
209
+ }
210
+ else {
211
+ // Create new task
212
+ const colTasks = data.tasks.filter(t => t.column === column && t.projectId === project.id);
213
+ const maxOrder = colTasks.length > 0 ? Math.max(...colTasks.map(t => t.order)) : -1;
214
+ const task = {
215
+ id: crypto_1.default.randomUUID(),
216
+ projectId: project.id,
217
+ title: title.trim(),
218
+ description: `Auto-synced from conversation ${conversationId}`,
219
+ column,
220
+ order: maxOrder + 1,
221
+ priority: (['low', 'medium', 'high', 'urgent'].includes(priority) ? priority : 'medium'),
222
+ agent: (agent || '').trim(),
223
+ skill: (skill || '').trim(),
224
+ createdAt: now,
225
+ updatedAt: now,
226
+ conversationId,
227
+ };
228
+ data.tasks.push(task);
229
+ (0, data_1.logActivity)(data, 'task_created', `Auto-sync: "${task.title}" created in ${column}`, project.id, agent || '', { conversationId });
230
+ (0, data_1.saveData)(data);
231
+ res.status(201).json({ action: 'created', task });
232
+ }
233
+ });
234
+ // ─── Auto-Cleanup (Archive stale done tasks) ────────────────────────
235
+ app.post('/api/tasks/auto-cleanup', (req, res) => {
236
+ const data = (0, data_1.loadData)();
237
+ const { maxAgeHours = 72, projectId } = req.body;
238
+ const cutoff = new Date(Date.now() - maxAgeHours * 60 * 60 * 1000).toISOString();
239
+ const before = data.tasks.length;
240
+ data.tasks = data.tasks.filter(t => {
241
+ if (t.column !== 'done')
242
+ return true;
243
+ if (projectId && t.projectId !== projectId)
244
+ return true;
245
+ return t.updatedAt > cutoff;
246
+ });
247
+ const removed = before - data.tasks.length;
248
+ if (removed > 0) {
249
+ (0, data_1.logActivity)(data, 'task_deleted', `Auto-cleanup: archived ${removed} done tasks older than ${maxAgeHours}h`, projectId || '', '', { removed, maxAgeHours });
250
+ (0, data_1.saveData)(data);
251
+ }
252
+ res.json({ removed, remaining: data.tasks.length });
253
+ });
254
+ app.put('/api/tasks/:id', (req, res) => {
255
+ const data = (0, data_1.loadData)();
256
+ const idx = data.tasks.findIndex(t => t.id === req.params.id);
257
+ if (idx === -1) {
258
+ res.status(404).json({ error: 'Task not found' });
259
+ return;
260
+ }
261
+ const { title, description, priority, agent, skill } = req.body;
262
+ if (title !== undefined)
263
+ data.tasks[idx].title = String(title).trim();
264
+ if (description !== undefined)
265
+ data.tasks[idx].description = String(description).trim();
266
+ if (agent !== undefined)
267
+ data.tasks[idx].agent = String(agent).trim();
268
+ if (skill !== undefined)
269
+ data.tasks[idx].skill = String(skill).trim();
270
+ const vp = ['low', 'medium', 'high', 'urgent'];
271
+ if (priority && vp.includes(priority))
272
+ data.tasks[idx].priority = priority;
273
+ data.tasks[idx].updatedAt = new Date().toISOString();
274
+ (0, data_1.logActivity)(data, 'task_updated', `Task "${data.tasks[idx].title}" updated`, data.tasks[idx].projectId, agent || '');
275
+ (0, data_1.saveData)(data);
276
+ res.json(data.tasks[idx]);
277
+ });
278
+ app.put('/api/tasks/:id/move', (req, res) => {
279
+ const data = (0, data_1.loadData)();
280
+ const idx = data.tasks.findIndex(t => t.id === req.params.id);
281
+ if (idx === -1) {
282
+ res.status(404).json({ error: 'Task not found' });
283
+ return;
284
+ }
285
+ const { column, order } = req.body;
286
+ const vc = ['backlog', 'in-progress', 'review', 'done'];
287
+ if (!column || !vc.includes(column)) {
288
+ res.status(400).json({ error: 'Valid column required' });
289
+ return;
290
+ }
291
+ const task = data.tasks[idx];
292
+ const oldCol = task.column;
293
+ const newO = typeof order === 'number' ? order : 0;
294
+ task.column = column;
295
+ task.order = newO;
296
+ task.updatedAt = new Date().toISOString();
297
+ const tt = data.tasks.filter(t => t.column === column && t.id !== task.id && t.projectId === task.projectId).sort((a, b) => a.order - b.order);
298
+ tt.splice(newO, 0, task);
299
+ tt.forEach((t, i) => { t.order = i; });
300
+ if (oldCol !== column) {
301
+ data.tasks.filter(t => t.column === oldCol && t.projectId === task.projectId).sort((a, b) => a.order - b.order).forEach((t, i) => { t.order = i; });
302
+ }
303
+ const actType = column === 'done' ? 'task_done' : 'task_moved';
304
+ (0, data_1.logActivity)(data, actType, `Task "${task.title}" moved: ${oldCol} → ${column}`, task.projectId, task.agent, { from: oldCol, to: column });
305
+ (0, data_1.saveData)(data);
306
+ res.json(task);
307
+ });
308
+ // ─── Valid Transition Map ─────────────────────────────────────────────
309
+ const VALID_TRANSITIONS = {
310
+ 'backlog': ['in-progress'],
311
+ 'in-progress': ['review', 'done', 'backlog'],
312
+ 'review': ['done', 'in-progress'],
313
+ 'done': ['backlog'],
314
+ };
315
+ app.post('/api/tasks/:id/transition', (req, res) => {
316
+ const data = (0, data_1.loadData)();
317
+ const idx = data.tasks.findIndex(t => t.id === req.params.id);
318
+ if (idx === -1) {
319
+ res.status(404).json({ error: 'Task not found' });
320
+ return;
321
+ }
322
+ const { column, reason } = req.body;
323
+ const vc = ['backlog', 'in-progress', 'review', 'done'];
324
+ if (!column || !vc.includes(column)) {
325
+ res.status(400).json({ error: 'Valid column required' });
326
+ return;
327
+ }
328
+ const task = data.tasks[idx];
329
+ const oldCol = task.column;
330
+ // Validate transition
331
+ const allowed = VALID_TRANSITIONS[oldCol] || [];
332
+ if (oldCol !== column && !allowed.includes(column)) {
333
+ res.status(400).json({
334
+ error: `Invalid transition: ${oldCol} → ${column}. Allowed: ${allowed.join(', ')}`,
335
+ errorCode: 'INVALID_TRANSITION',
336
+ allowed,
337
+ });
338
+ return;
339
+ }
340
+ if (oldCol === column) {
341
+ res.json(task);
342
+ return;
343
+ } // No-op
344
+ // Execute transition
345
+ const now = new Date().toISOString();
346
+ task.column = column;
347
+ task.updatedAt = now;
348
+ task.stuckSince = undefined; // Reset stuck tracker
349
+ // Reorder in target column
350
+ const targetTasks = data.tasks.filter(t => t.column === column && t.id !== task.id && t.projectId === task.projectId).sort((a, b) => a.order - b.order);
351
+ task.order = targetTasks.length;
352
+ // Reorder old column
353
+ data.tasks.filter(t => t.column === oldCol && t.projectId === task.projectId).sort((a, b) => a.order - b.order).forEach((t, i) => { t.order = i; });
354
+ const actType = column === 'done' ? 'task_done' : 'task_transitioned';
355
+ const msg = reason
356
+ ? `Task "${task.title}" transitioned: ${oldCol} → ${column} — ${reason}`
357
+ : `Task "${task.title}" transitioned: ${oldCol} → ${column}`;
358
+ (0, data_1.logActivity)(data, actType, msg, task.projectId, task.agent, { from: oldCol, to: column, reason: reason || '' });
359
+ (0, data_1.saveData)(data);
360
+ res.json(task);
361
+ });
362
+ app.post('/api/tasks/bulk-transition', (req, res) => {
363
+ const data = (0, data_1.loadData)();
364
+ const { taskIds, column, reason } = req.body;
365
+ if (!Array.isArray(taskIds) || taskIds.length === 0) {
366
+ res.status(400).json({ error: 'taskIds array required' });
367
+ return;
368
+ }
369
+ const vc = ['backlog', 'in-progress', 'review', 'done'];
370
+ if (!column || !vc.includes(column)) {
371
+ res.status(400).json({ error: 'Valid column required' });
372
+ return;
373
+ }
374
+ const results = [];
375
+ const now = new Date().toISOString();
376
+ for (const tid of taskIds) {
377
+ const idx = data.tasks.findIndex(t => t.id === tid);
378
+ if (idx === -1) {
379
+ results.push({ id: tid, success: false, error: 'Not found' });
380
+ continue;
381
+ }
382
+ const task = data.tasks[idx];
383
+ const oldCol = task.column;
384
+ const allowed = VALID_TRANSITIONS[oldCol] || [];
385
+ if (oldCol !== column && !allowed.includes(column)) {
386
+ results.push({ id: tid, success: false, error: `Invalid: ${oldCol} → ${column}` });
387
+ continue;
388
+ }
389
+ if (oldCol === column) {
390
+ results.push({ id: tid, success: true });
391
+ continue;
392
+ }
393
+ task.column = column;
394
+ task.updatedAt = now;
395
+ task.stuckSince = undefined;
396
+ const targetTasks = data.tasks.filter(t => t.column === column && t.id !== task.id && t.projectId === task.projectId).sort((a, b) => a.order - b.order);
397
+ task.order = targetTasks.length;
398
+ const actType = column === 'done' ? 'task_done' : 'task_transitioned';
399
+ (0, data_1.logActivity)(data, actType, `Task "${task.title}" bulk-transitioned: ${oldCol} → ${column}`, task.projectId, task.agent, { from: oldCol, to: column, reason: reason || '', bulk: true });
400
+ results.push({ id: tid, success: true });
401
+ }
402
+ // Reorder all affected columns
403
+ const affectedCols = new Set(results.filter(r => r.success).map(r => { var _a; return (_a = data.tasks.find(t => t.id === r.id)) === null || _a === void 0 ? void 0 : _a.column; }).filter(Boolean));
404
+ for (const col of affectedCols) {
405
+ data.tasks.filter(t => t.column === col).sort((a, b) => a.order - b.order).forEach((t, i) => { t.order = i; });
406
+ }
407
+ (0, data_1.saveData)(data);
408
+ const successCount = results.filter(r => r.success).length;
409
+ res.json({ results, summary: `${successCount}/${taskIds.length} tasks transitioned to ${column}` });
410
+ });
411
+ app.get('/api/tasks/stuck', (req, res) => {
412
+ const data = (0, data_1.loadData)();
413
+ const thresholdMs = parseInt(String(req.query.threshold)) || 30 * 60 * 1000; // default 30 min
414
+ const now = Date.now();
415
+ let tasks = data.tasks.filter(t => t.column === 'in-progress');
416
+ if (req.query.projectId)
417
+ tasks = tasks.filter(t => t.projectId === req.query.projectId);
418
+ const stuck = tasks.filter(t => {
419
+ const elapsed = now - new Date(t.updatedAt).getTime();
420
+ return elapsed > thresholdMs;
421
+ }).map(t => (Object.assign(Object.assign({}, t), { stuckMinutes: Math.round((now - new Date(t.updatedAt).getTime()) / 60000) })));
422
+ res.json(stuck);
423
+ });
424
+ app.delete('/api/tasks/:id', (req, res) => {
425
+ const data = (0, data_1.loadData)();
426
+ const idx = data.tasks.findIndex(t => t.id === req.params.id);
427
+ if (idx === -1) {
428
+ res.status(404).json({ error: 'Task not found' });
429
+ return;
430
+ }
431
+ const [removed] = data.tasks.splice(idx, 1);
432
+ data.tasks.filter(t => t.column === removed.column && t.projectId === removed.projectId).sort((a, b) => a.order - b.order).forEach((t, i) => { t.order = i; });
433
+ (0, data_1.logActivity)(data, 'task_deleted', `Task "${removed.title}" deleted`, removed.projectId, removed.agent);
434
+ (0, data_1.saveData)(data);
435
+ res.status(204).send();
436
+ });
437
+ // ─── Task Dispatch API ──────────────────────────────────────────────────
438
+ app.post('/api/tasks/:id/dispatch', (req, res) => {
439
+ const data = (0, data_1.loadData)();
440
+ const task = data.tasks.find(t => t.id === req.params.id);
441
+ if (!task) {
442
+ res.status(404).json({ error: 'Task not found' });
443
+ return;
444
+ }
445
+ const project = data.projects.find(p => p.id === task.projectId);
446
+ const force = req.query.force === 'true';
447
+ // Validate before dispatch
448
+ const validationError = (0, agent_dispatch_1.validateDispatch)(task, project, force);
449
+ if (validationError) {
450
+ const statusCode = validationError.errorCode === 'ALREADY_DISPATCHED' ? 409
451
+ : validationError.errorCode === 'WRITE_ERROR' ? 500 : 400;
452
+ res.status(statusCode).json({ error: validationError.error, errorCode: validationError.errorCode });
453
+ return;
454
+ }
455
+ // Dispatch
456
+ const result = (0, agent_dispatch_1.dispatchTaskToAgent)(task, project, force);
457
+ if (result.success) {
458
+ // Update task dispatch status
459
+ task.dispatchStatus = 'dispatched';
460
+ task.dispatchedAt = new Date().toISOString();
461
+ task.dispatchError = undefined;
462
+ task.updatedAt = task.dispatchedAt;
463
+ (0, data_1.logActivity)(data, 'task_dispatched', `Task "${task.title}" dispatched to ${task.agent}`, task.projectId, task.agent, {
464
+ taskId: task.id, filePath: result.filePath, skill: task.skill, force,
465
+ });
466
+ (0, data_1.saveData)(data);
467
+ res.json({ success: true, task, filePath: result.filePath, prompt: result.prompt, cliCommand: result.cliCommand });
468
+ }
469
+ else {
470
+ // Mark as failed
471
+ task.dispatchStatus = 'failed';
472
+ task.dispatchError = result.error;
473
+ task.updatedAt = new Date().toISOString();
474
+ (0, data_1.saveData)(data);
475
+ res.status(500).json({ error: result.error, errorCode: result.errorCode });
476
+ }
477
+ });
478
+ // ─── Activity API ──────────────────────────────────────────────────────
479
+ app.get('/api/activities', (req, res) => {
480
+ const data = (0, data_1.loadData)();
481
+ let activities = data.activities;
482
+ if (req.query.projectId)
483
+ activities = activities.filter(a => a.projectId === req.query.projectId);
484
+ const limit = parseInt(String(req.query.limit)) || 50;
485
+ res.json(activities.slice(0, limit));
486
+ });
487
+ // ─── Deployment API ────────────────────────────────────────────────────
488
+ app.get('/api/deployments', (req, res) => {
489
+ const data = (0, data_1.loadData)();
490
+ let deps = data.deployments;
491
+ if (req.query.projectId)
492
+ deps = deps.filter(d => d.projectId === req.query.projectId);
493
+ res.json(deps);
494
+ });
495
+ app.post('/api/deployments', (req, res) => {
496
+ const data = (0, data_1.loadData)();
497
+ const { projectId, env, commit, branch, agent, message } = req.body;
498
+ if (!projectId || !env) {
499
+ res.status(400).json({ error: 'projectId and env required' });
500
+ return;
501
+ }
502
+ const validEnvs = ['staging', 'production'];
503
+ if (!validEnvs.includes(env)) {
504
+ res.status(400).json({ error: 'env must be staging or production' });
505
+ return;
506
+ }
507
+ const now = new Date().toISOString();
508
+ const dep = {
509
+ id: crypto_1.default.randomUUID(), projectId, env, status: 'success',
510
+ commit: commit || '', branch: branch || 'main',
511
+ agent: agent || '', message: message || `Deploy to ${env}`,
512
+ startedAt: now, finishedAt: now,
513
+ };
514
+ data.deployments.unshift(dep);
515
+ (0, data_1.logActivity)(data, env === 'staging' ? 'deploy_staging' : 'deploy_production', `Deployed to ${env}: ${dep.message}`, projectId, agent || '', { deploymentId: dep.id, commit, branch });
516
+ (0, data_1.saveData)(data);
517
+ res.status(201).json(dep);
518
+ });
519
+ app.put('/api/deployments/:id/status', (req, res) => {
520
+ const data = (0, data_1.loadData)();
521
+ const dep = data.deployments.find(d => d.id === req.params.id);
522
+ if (!dep) {
523
+ res.status(404).json({ error: 'Deployment not found' });
524
+ return;
525
+ }
526
+ const { status } = req.body;
527
+ const vs = ['pending', 'running', 'success', 'failed', 'rolled_back'];
528
+ if (!vs.includes(status)) {
529
+ res.status(400).json({ error: 'Invalid status' });
530
+ return;
531
+ }
532
+ dep.status = status;
533
+ dep.finishedAt = new Date().toISOString();
534
+ if (status === 'failed')
535
+ (0, data_1.logActivity)(data, 'deploy_failed', `Deploy to ${dep.env} failed`, dep.projectId, dep.agent, { deploymentId: dep.id });
536
+ (0, data_1.saveData)(data);
537
+ res.json(dep);
538
+ });
539
+ app.post('/api/deployments/:id/rollback', (req, res) => {
540
+ const data = (0, data_1.loadData)();
541
+ const dep = data.deployments.find(d => d.id === req.params.id);
542
+ if (!dep) {
543
+ res.status(404).json({ error: 'Deployment not found' });
544
+ return;
545
+ }
546
+ dep.status = 'rolled_back';
547
+ const now = new Date().toISOString();
548
+ const rollback = {
549
+ id: crypto_1.default.randomUUID(), projectId: dep.projectId, env: dep.env, status: 'success',
550
+ commit: '', branch: dep.branch, agent: req.body.agent || '', message: `Rollback of deploy ${dep.id.substring(0, 8)}`,
551
+ startedAt: now, finishedAt: now, rollbackOf: dep.id,
552
+ };
553
+ data.deployments.unshift(rollback);
554
+ (0, data_1.logActivity)(data, 'rollback', `Rolled back ${dep.env} deploy: ${dep.message}`, dep.projectId, req.body.agent || '', { originalDeployId: dep.id, rollbackId: rollback.id });
555
+ (0, data_1.saveData)(data);
556
+ res.status(201).json(rollback);
557
+ });
558
+ // ─── Changelog API ─────────────────────────────────────────────────────
559
+ app.get('/api/changelog', (req, res) => {
560
+ const data = (0, data_1.loadData)();
561
+ let entries = data.changelog;
562
+ if (req.query.projectId)
563
+ entries = entries.filter(c => c.projectId === req.query.projectId);
564
+ res.json(entries);
565
+ });
566
+ app.post('/api/changelog', (req, res) => {
567
+ const data = (0, data_1.loadData)();
568
+ const { projectId, version, title, changes, deploymentId, agent } = req.body;
569
+ if (!version || !title) {
570
+ res.status(400).json({ error: 'version and title required' });
571
+ return;
572
+ }
573
+ const entry = {
574
+ id: crypto_1.default.randomUUID(), projectId: projectId || '', version, title,
575
+ changes: Array.isArray(changes) ? changes : [], deploymentId: deploymentId || '',
576
+ agent: agent || '', createdAt: new Date().toISOString(),
577
+ };
578
+ data.changelog.unshift(entry);
579
+ (0, data_1.logActivity)(data, 'changelog_added', `Changelog v${version}: ${title}`, projectId || '', agent || '', { changelogId: entry.id });
580
+ (0, data_1.saveData)(data);
581
+ res.status(201).json(entry);
582
+ });
583
+ // ─── Continuity / Working Memory API ───────────────────────────────────
584
+ app.get('/api/continuity', (req, res) => {
585
+ const data = (0, data_1.loadData)();
586
+ const results = {};
587
+ for (const project of data.projects) {
588
+ if (project.path && (0, continuity_1.hasCmDir)(project.path)) {
589
+ results[project.id] = (0, continuity_1.getContinuityStatus)(project.path);
590
+ }
591
+ }
592
+ res.json(results);
593
+ });
594
+ app.get('/api/continuity/:projectId', (req, res) => {
595
+ const data = (0, data_1.loadData)();
596
+ const project = data.projects.find(p => p.id === req.params.projectId);
597
+ if (!project || !project.path) {
598
+ res.status(404).json({ error: 'Project not found or no path' });
599
+ return;
600
+ }
601
+ if (!(0, continuity_1.hasCmDir)(project.path)) {
602
+ res.status(404).json({ error: 'Working memory not initialized. Run: cm continuity init' });
603
+ return;
604
+ }
605
+ const status = (0, continuity_1.getContinuityStatus)(project.path);
606
+ const state = (0, continuity_1.readContinuityState)(project.path);
607
+ res.json({ status, state });
608
+ });
609
+ app.post('/api/continuity/:projectId', (req, res) => {
610
+ const data = (0, data_1.loadData)();
611
+ const project = data.projects.find(p => p.id === req.params.projectId);
612
+ if (!project || !project.path) {
613
+ res.status(404).json({ error: 'Project not found' });
614
+ return;
615
+ }
616
+ if (!(0, continuity_1.hasCmDir)(project.path))
617
+ (0, continuity_1.ensureCmDir)(project.path);
618
+ const state = req.body;
619
+ (0, continuity_1.writeContinuityMd)(project.path, state);
620
+ res.json({ success: true, state });
621
+ });
622
+ app.get('/api/learnings/:projectId', (req, res) => {
623
+ const data = (0, data_1.loadData)();
624
+ const project = data.projects.find(p => p.id === req.params.projectId);
625
+ if (!project || !project.path) {
626
+ res.status(404).json({ error: 'Project not found' });
627
+ return;
628
+ }
629
+ const learnings = (0, continuity_1.hasCmDir)(project.path) ? (0, continuity_1.getLearnings)(project.path) : [];
630
+ res.json(learnings);
631
+ });
632
+ app.post('/api/learnings/:projectId', (req, res) => {
633
+ const data = (0, data_1.loadData)();
634
+ const project = data.projects.find(p => p.id === req.params.projectId);
635
+ if (!project || !project.path) {
636
+ res.status(404).json({ error: 'Project not found' });
637
+ return;
638
+ }
639
+ if (!(0, continuity_1.hasCmDir)(project.path))
640
+ (0, continuity_1.ensureCmDir)(project.path);
641
+ const { whatFailed, whyFailed, howToPrevent, agent, taskId } = req.body;
642
+ if (!whatFailed) {
643
+ res.status(400).json({ error: 'whatFailed is required' });
644
+ return;
645
+ }
646
+ const learning = (0, continuity_1.addLearning)(project.path, {
647
+ whatFailed, whyFailed: whyFailed || '', howToPrevent: howToPrevent || '',
648
+ timestamp: new Date().toISOString(), agent: agent || '', taskId: taskId || '',
649
+ });
650
+ res.status(201).json(learning);
651
+ });
652
+ app.delete('/api/learnings/:projectId/:learningId', (req, res) => {
653
+ const data = (0, data_1.loadData)();
654
+ const project = data.projects.find(p => p.id === req.params.projectId);
655
+ if (!project || !project.path) {
656
+ res.status(404).json({ error: 'Project not found' });
657
+ return;
658
+ }
659
+ if (!(0, continuity_1.hasCmDir)(project.path)) {
660
+ res.status(404).json({ error: 'No .cm/ directory' });
661
+ return;
662
+ }
663
+ const success = (0, continuity_1.deleteLearning)(project.path, req.params.learningId);
664
+ if (!success) {
665
+ res.status(404).json({ error: 'Learning not found' });
666
+ return;
667
+ }
668
+ (0, data_1.logActivity)(data, 'learning_deleted', `Learning deleted`, project.id);
669
+ (0, data_1.saveData)(data);
670
+ res.status(204).send();
671
+ });
672
+ app.get('/api/decisions/:projectId', (req, res) => {
673
+ const data = (0, data_1.loadData)();
674
+ const project = data.projects.find(p => p.id === req.params.projectId);
675
+ if (!project || !project.path) {
676
+ res.status(404).json({ error: 'Project not found' });
677
+ return;
678
+ }
679
+ const decisions = (0, continuity_1.hasCmDir)(project.path) ? (0, continuity_1.getDecisions)(project.path) : [];
680
+ res.json(decisions);
681
+ });
682
+ app.post('/api/decisions/:projectId', (req, res) => {
683
+ const data = (0, data_1.loadData)();
684
+ const project = data.projects.find(p => p.id === req.params.projectId);
685
+ if (!project || !project.path) {
686
+ res.status(404).json({ error: 'Project not found' });
687
+ return;
688
+ }
689
+ if (!(0, continuity_1.hasCmDir)(project.path))
690
+ (0, continuity_1.ensureCmDir)(project.path);
691
+ const { decision, rationale, agent } = req.body;
692
+ if (!decision) {
693
+ res.status(400).json({ error: 'decision is required' });
694
+ return;
695
+ }
696
+ const entry = (0, continuity_1.addDecision)(project.path, {
697
+ decision, rationale: rationale || '', timestamp: new Date().toISOString(), agent: agent || '',
698
+ });
699
+ res.status(201).json(entry);
700
+ });
701
+ app.delete('/api/decisions/:projectId/:decisionId', (req, res) => {
702
+ const data = (0, data_1.loadData)();
703
+ const project = data.projects.find(p => p.id === req.params.projectId);
704
+ if (!project || !project.path) {
705
+ res.status(404).json({ error: 'Project not found' });
706
+ return;
707
+ }
708
+ if (!(0, continuity_1.hasCmDir)(project.path)) {
709
+ res.status(404).json({ error: 'No .cm/ directory' });
710
+ return;
711
+ }
712
+ const success = (0, continuity_1.deleteDecision)(project.path, req.params.decisionId);
713
+ if (!success) {
714
+ res.status(404).json({ error: 'Decision not found' });
715
+ return;
716
+ }
717
+ (0, data_1.logActivity)(data, 'decision_deleted', `Decision deleted`, project.id);
718
+ (0, data_1.saveData)(data);
719
+ res.status(204).send();
720
+ });
721
+ app.post('/api/continuity/:projectId/init', (req, res) => {
722
+ const data = (0, data_1.loadData)();
723
+ const project = data.projects.find(p => p.id === req.params.projectId);
724
+ if (!project || !project.path) {
725
+ res.status(404).json({ error: 'Project not found' });
726
+ return;
727
+ }
728
+ (0, continuity_1.ensureCmDir)(project.path);
729
+ const status = (0, continuity_1.getContinuityStatus)(project.path);
730
+ res.json({ success: true, status });
731
+ });
732
+ // ─── Judge Agent API ──────────────────────────────────────────────────
733
+ app.get('/api/judge', (req, res) => {
734
+ const data = (0, data_1.loadData)();
735
+ let tasks = data.tasks;
736
+ if (req.query.projectId) {
737
+ tasks = tasks.filter(t => t.projectId === req.query.projectId);
738
+ }
739
+ // Collect learnings from all projects
740
+ let allLearnings = [];
741
+ for (const project of data.projects) {
742
+ if (project.path && (0, continuity_1.hasCmDir)(project.path)) {
743
+ allLearnings = allLearnings.concat((0, continuity_1.getLearnings)(project.path));
744
+ }
745
+ }
746
+ const decisions = (0, judge_1.evaluateAllTasks)(tasks, allLearnings);
747
+ const result = {};
748
+ for (const [taskId, decision] of decisions) {
749
+ result[taskId] = decision;
750
+ }
751
+ res.json(result);
752
+ });
753
+ // ─── Transition Suggestion API (must be before :taskId) ────────────────
754
+ app.get('/api/judge/suggestions', (req, res) => {
755
+ const data = (0, data_1.loadData)();
756
+ let tasks = data.tasks;
757
+ if (req.query.projectId)
758
+ tasks = tasks.filter(t => t.projectId === req.query.projectId);
759
+ const suggestions = (0, judge_1.suggestTransitions)(tasks);
760
+ res.json(suggestions);
761
+ });
762
+ app.get('/api/judge/:taskId', (req, res) => {
763
+ const data = (0, data_1.loadData)();
764
+ const task = data.tasks.find(t => t.id === req.params.taskId);
765
+ if (!task) {
766
+ res.status(404).json({ error: 'Task not found' });
767
+ return;
768
+ }
769
+ const project = data.projects.find(p => p.id === task.projectId);
770
+ let learnings = [];
771
+ if ((project === null || project === void 0 ? void 0 : project.path) && (0, continuity_1.hasCmDir)(project.path)) {
772
+ learnings = (0, continuity_1.getLearnings)(project.path);
773
+ }
774
+ const decision = (0, judge_1.evaluateTaskState)(task, data.tasks, learnings);
775
+ res.json(Object.assign({ task: task.id }, decision));
776
+ });
777
+ // ─── Agent Suggestion API ─────────────────────────────────────────────
778
+ app.get('/api/agents/suggest', (req, res) => {
779
+ const skill = String(req.query.skill || '');
780
+ if (!skill) {
781
+ res.json({ agents: (0, judge_1.suggestAgentsForSkill)('cm-execution'), domain: 'orchestration' });
782
+ return;
783
+ }
784
+ const domain = (0, judge_1.getSkillDomain)(skill);
785
+ const agents = (0, judge_1.suggestAgentsForSkill)(skill);
786
+ res.json({ skill, domain, agents });
787
+ });
788
+ app.get('/api/agents/suggest/:taskId', (req, res) => {
789
+ const data = (0, data_1.loadData)();
790
+ const task = data.tasks.find(t => t.id === req.params.taskId);
791
+ if (!task) {
792
+ res.status(404).json({ error: 'Task not found' });
793
+ return;
794
+ }
795
+ const suggestions = (0, judge_1.suggestAgentsForTask)(task);
796
+ res.json({ taskId: task.id, skill: task.skill, suggestions });
797
+ });
798
+ // ─── Chain API ────────────────────────────────────────────────────────────
799
+ app.get('/api/chains', (_req, res) => {
800
+ res.json((0, skill_chain_1.listChains)());
801
+ });
802
+ app.get('/api/chains/:id', (req, res) => {
803
+ const chain = (0, skill_chain_1.findChain)(req.params.id);
804
+ if (!chain) {
805
+ res.status(404).json({ error: 'Chain not found' });
806
+ return;
807
+ }
808
+ res.json(chain);
809
+ });
810
+ app.get('/api/chain-executions', (req, res) => {
811
+ const data = (0, data_1.loadData)();
812
+ let execs = data.chainExecutions;
813
+ if (req.query.status)
814
+ execs = execs.filter(e => e.status === req.query.status);
815
+ if (req.query.projectId)
816
+ execs = execs.filter(e => e.projectId === req.query.projectId);
817
+ res.json(execs);
818
+ });
819
+ app.get('/api/chain-executions/:id', (req, res) => {
820
+ const data = (0, data_1.loadData)();
821
+ const exec = data.chainExecutions.find(e => e.id === req.params.id);
822
+ if (!exec) {
823
+ res.status(404).json({ error: 'Chain execution not found' });
824
+ return;
825
+ }
826
+ res.json(exec);
827
+ });
828
+ app.post('/api/chain-executions', (req, res) => {
829
+ const { chainId, taskTitle, projectId, agent } = req.body;
830
+ if (!chainId || !taskTitle) {
831
+ res.status(400).json({ error: 'chainId and taskTitle required' });
832
+ return;
833
+ }
834
+ const chain = (0, skill_chain_1.findChain)(chainId);
835
+ if (!chain) {
836
+ res.status(404).json({ error: 'Chain not found' });
837
+ return;
838
+ }
839
+ const data = (0, data_1.loadData)();
840
+ const pid = projectId || (data.projects.length > 0 ? data.projects[0].id : undefined);
841
+ if (!pid) {
842
+ res.status(400).json({ error: 'No project available' });
843
+ return;
844
+ }
845
+ const execution = (0, skill_chain_1.createChainExecution)(chain, pid, taskTitle, agent || 'antigravity');
846
+ data.chainExecutions.push(execution);
847
+ (0, data_1.logActivity)(data, 'chain_started', `Chain "${chain.name}" started: "${taskTitle}"`, pid, agent || '', { chainId, executionId: execution.id });
848
+ (0, data_1.saveData)(data);
849
+ res.status(201).json(execution);
850
+ });
851
+ app.put('/api/chain-executions/:id/advance', (req, res) => {
852
+ const data = (0, data_1.loadData)();
853
+ const exec = data.chainExecutions.find(e => e.id === req.params.id);
854
+ if (!exec) {
855
+ res.status(404).json({ error: 'Chain execution not found' });
856
+ return;
857
+ }
858
+ if (exec.status !== 'running') {
859
+ res.status(400).json({ error: `Chain is ${exec.status}` });
860
+ return;
861
+ }
862
+ const result = (0, skill_chain_1.advanceChain)(exec, req.body.output);
863
+ const actType = result.completed ? 'chain_completed' : 'chain_step_completed';
864
+ (0, data_1.logActivity)(data, actType, result.completed ? `Chain completed: "${exec.taskTitle}"` : `Chain step advanced: ${result.nextSkill}`, exec.projectId, exec.agent, { executionId: exec.id });
865
+ (0, data_1.saveData)(data);
866
+ res.json(Object.assign(Object.assign({}, result), { execution: exec }));
867
+ });
868
+ app.put('/api/chain-executions/:id/skip', (req, res) => {
869
+ const data = (0, data_1.loadData)();
870
+ const exec = data.chainExecutions.find(e => e.id === req.params.id);
871
+ if (!exec) {
872
+ res.status(404).json({ error: 'Chain execution not found' });
873
+ return;
874
+ }
875
+ if (exec.status !== 'running') {
876
+ res.status(400).json({ error: `Chain is ${exec.status}` });
877
+ return;
878
+ }
879
+ const result = (0, skill_chain_1.skipChainStep)(exec, req.body.reason);
880
+ (0, data_1.saveData)(data);
881
+ res.json(Object.assign(Object.assign({}, result), { execution: exec }));
882
+ });
883
+ app.put('/api/chain-executions/:id/abort', (req, res) => {
884
+ const data = (0, data_1.loadData)();
885
+ const exec = data.chainExecutions.find(e => e.id === req.params.id);
886
+ if (!exec) {
887
+ res.status(404).json({ error: 'Chain execution not found' });
888
+ return;
889
+ }
890
+ (0, skill_chain_1.abortChain)(exec, req.body.reason);
891
+ (0, data_1.logActivity)(data, 'chain_aborted', `Chain aborted: "${exec.taskTitle}"`, exec.projectId, exec.agent, { executionId: exec.id });
892
+ (0, data_1.saveData)(data);
893
+ res.json(exec);
894
+ });
895
+ app.get('/api/chains/suggest/:title', (req, res) => {
896
+ const chain = (0, skill_chain_1.matchChain)(req.params.title);
897
+ res.json(chain || { match: false });
898
+ });
899
+ // ─── Fallback ──────────────────────────────────────────────────────────
900
+ app.get('/{*path}', (_req, res) => {
901
+ res.sendFile(path_1.default.join(publicDir, 'index.html'));
902
+ });
903
+ // ─── Start Server ─────────────────────────────────────────────────────
904
+ const server = app.listen(port, () => {
905
+ try {
906
+ fs_1.default.writeFileSync(data_1.PID_FILE, String(process.pid));
907
+ }
908
+ catch (_a) { }
909
+ if (!silent) {
910
+ console.log(chalk_1.default.cyan(`\n🚀 Mission Control at http://codymaster.localhost:${port}`));
911
+ console.log(chalk_1.default.gray(` Data: ${data_1.DATA_FILE}`));
912
+ console.log(chalk_1.default.gray(` Press Ctrl+C to stop.\n`));
913
+ }
914
+ else {
915
+ // Silent auto-start: just a subtle hint
916
+ console.log(chalk_1.default.gray(` 📊 Dashboard auto-started → http://codymaster.localhost:${port}`));
917
+ }
918
+ });
919
+ const cleanup = () => { try {
920
+ fs_1.default.unlinkSync(data_1.PID_FILE);
921
+ }
922
+ catch (_a) { } };
923
+ process.on('SIGINT', () => { cleanup(); process.exit(0); });
924
+ process.on('SIGTERM', () => { cleanup(); process.exit(0); });
925
+ return server;
926
+ }