codymaster 4.6.0 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/README.md +80 -30
  3. package/dist/browse-server.js +251 -0
  4. package/dist/cli/command-registry.js +26 -0
  5. package/dist/cli/commands/agent.js +120 -0
  6. package/dist/cli/commands/dashboard.js +93 -0
  7. package/dist/cli/commands/design-studio.js +111 -0
  8. package/dist/cli/commands/distro.js +25 -0
  9. package/dist/cli/commands/engineering.js +488 -0
  10. package/dist/cli/commands/project.js +324 -0
  11. package/dist/cli/commands/skill-chain.js +269 -0
  12. package/dist/cli/commands/system.js +89 -0
  13. package/dist/cli/commands/task.js +254 -0
  14. package/dist/cli/update-check.js +83 -0
  15. package/dist/cm-config.js +110 -0
  16. package/dist/cm-suggest.js +77 -0
  17. package/dist/distro-validate.js +54 -0
  18. package/dist/guardian-core.js +74 -0
  19. package/dist/index.js +36 -2759
  20. package/dist/mcp-context-server.js +60 -1
  21. package/dist/mcp-skills-tools.js +81 -0
  22. package/dist/retro-summary.js +70 -0
  23. package/dist/second-opinion-providers.js +79 -0
  24. package/dist/sprint-pipeline.js +228 -0
  25. package/dist/storage-backend.js +5 -60
  26. package/dist/utils/cli-utils.js +76 -0
  27. package/dist/utils/skill-utils.js +32 -0
  28. package/install.sh +274 -50
  29. package/package.json +16 -5
  30. package/scripts/build-skills.mjs +51 -0
  31. package/scripts/gate-0-repo-hygiene.js +75 -0
  32. package/scripts/postinstall.js +55 -0
  33. package/scripts/security-scan.js +1 -1
  34. package/scripts/validate-skills.mjs +42 -0
  35. package/scripts/viking-demo.ts +105 -0
  36. package/skills/CLAUDE.md +2 -2
  37. package/skills/cm-ads-tracker/SKILL.md +3 -6
  38. package/skills/cm-browse/SKILL.md +28 -0
  39. package/skills/cm-conductor-worktrees/SKILL.md +24 -0
  40. package/skills/cm-content-factory/SKILL.md +1 -1
  41. package/skills/cm-content-factory/landing/docs/content/changelog.md +36 -0
  42. package/skills/cm-content-factory/landing/docs/content/deployment.md +46 -0
  43. package/skills/cm-content-factory/landing/docs/content/execution-flow.md +67 -0
  44. package/skills/cm-content-factory/landing/docs/content/openspace.md +27 -0
  45. package/skills/cm-content-factory/landing/docs/content/openviking.md +33 -0
  46. package/skills/cm-content-factory/landing/docs/content/use-cases.md +26 -0
  47. package/skills/cm-content-factory/landing/docs/content/v5-intro.md +28 -0
  48. package/skills/cm-content-factory/landing/docs/index.html +240 -0
  49. package/skills/cm-content-factory/landing/index.html +99 -99
  50. package/skills/cm-content-factory/landing/script.js +42 -0
  51. package/skills/cm-content-factory/landing/translations.js +400 -400
  52. package/skills/cm-design-studio/SKILL.md +30 -0
  53. package/skills/cm-ecosystem-roadmap/SKILL.md +11 -0
  54. package/skills/cm-engineering-meta/SKILL.md +69 -0
  55. package/skills/cm-growth-hacking/SKILL.md +1 -12
  56. package/skills/cm-guardian-runtime/SKILL.md +22 -0
  57. package/skills/cm-mcp-engineering/SKILL.md +18 -0
  58. package/skills/cm-notebooklm/SKILL.md +1 -17
  59. package/skills/cm-post-deploy-canary/SKILL.md +18 -0
  60. package/skills/cm-qa-visual-cli/SKILL.md +18 -0
  61. package/skills/cm-retro-cli/SKILL.md +19 -0
  62. package/skills/cm-second-opinion-cli/SKILL.md +19 -0
  63. package/skills/cm-secret-shield/SKILL.md +2 -2
  64. package/skills/cm-sprint-bus/SKILL.md +29 -0
  65. package/skills/cm-tdd/SKILL.md +61 -74
  66. package/skills/profiles/README.md +21 -0
  67. package/skills/profiles/core.txt +23 -0
  68. package/skills/profiles/design.txt +6 -0
  69. package/skills/profiles/full.txt +58 -0
  70. package/skills/profiles/growth.txt +10 -0
  71. package/skills/profiles/knowledge.txt +7 -0
  72. package/scripts/test-gemini.js +0 -13
  73. package/skills/cm-frappe-agent/SKILL.md +0 -134
  74. package/skills/cm-frappe-agent/agents/doctype-architect.md +0 -596
  75. package/skills/cm-frappe-agent/agents/erpnext-customizer.md +0 -643
  76. package/skills/cm-frappe-agent/agents/frappe-backend.md +0 -814
  77. package/skills/cm-frappe-agent/agents/frappe-custom-frontend.md +0 -557
  78. package/skills/cm-frappe-agent/agents/frappe-debugger.md +0 -625
  79. package/skills/cm-frappe-agent/agents/frappe-fixer.md +0 -275
  80. package/skills/cm-frappe-agent/agents/frappe-frontend.md +0 -660
  81. package/skills/cm-frappe-agent/agents/frappe-installer.md +0 -158
  82. package/skills/cm-frappe-agent/agents/frappe-performance.md +0 -307
  83. package/skills/cm-frappe-agent/agents/frappe-planner.md +0 -419
  84. package/skills/cm-frappe-agent/agents/frappe-remote-ops.md +0 -153
  85. package/skills/cm-frappe-agent/agents/github-workflow.md +0 -286
  86. package/skills/cm-frappe-agent/commands/frappe-app.md +0 -351
  87. package/skills/cm-frappe-agent/commands/frappe-backend.md +0 -162
  88. package/skills/cm-frappe-agent/commands/frappe-bench.md +0 -254
  89. package/skills/cm-frappe-agent/commands/frappe-debug.md +0 -263
  90. package/skills/cm-frappe-agent/commands/frappe-doctype-create.md +0 -272
  91. package/skills/cm-frappe-agent/commands/frappe-doctype-field.md +0 -310
  92. package/skills/cm-frappe-agent/commands/frappe-erpnext.md +0 -210
  93. package/skills/cm-frappe-agent/commands/frappe-fix.md +0 -59
  94. package/skills/cm-frappe-agent/commands/frappe-frontend.md +0 -210
  95. package/skills/cm-frappe-agent/commands/frappe-fullstack.md +0 -243
  96. package/skills/cm-frappe-agent/commands/frappe-github.md +0 -57
  97. package/skills/cm-frappe-agent/commands/frappe-install.md +0 -52
  98. package/skills/cm-frappe-agent/commands/frappe-plan.md +0 -442
  99. package/skills/cm-frappe-agent/commands/frappe-remote.md +0 -58
  100. package/skills/cm-frappe-agent/commands/frappe-test.md +0 -356
  101. package/skills/cm-frappe-agent/docs/README.md +0 -51
  102. package/skills/cm-frappe-agent/docs/agents-catalog.md +0 -113
  103. package/skills/cm-frappe-agent/docs/architecture.md +0 -149
  104. package/skills/cm-frappe-agent/docs/commands-catalog.md +0 -82
  105. package/skills/cm-frappe-agent/docs/resources-catalog.md +0 -66
  106. package/skills/cm-frappe-agent/docs/sitemap-urls.txt +0 -52
  107. package/skills/cm-frappe-agent/docs/sitemap.md +0 -81
  108. package/skills/cm-frappe-agent/docs/sop/user-guide.md +0 -178
  109. package/skills/cm-frappe-agent/docs/sop/vibe-coding-guide.md +0 -122
  110. package/skills/cm-frappe-agent/resources/7-layer-architecture.md +0 -985
  111. package/skills/cm-frappe-agent/resources/bench_commands.md +0 -73
  112. package/skills/cm-frappe-agent/resources/code-patterns-guide.md +0 -948
  113. package/skills/cm-frappe-agent/resources/common_pitfalls.md +0 -266
  114. package/skills/cm-frappe-agent/resources/doctype-registry.md +0 -158
  115. package/skills/cm-frappe-agent/resources/installation-guide.md +0 -289
  116. package/skills/cm-frappe-agent/resources/rest-api-patterns.md +0 -182
  117. package/skills/cm-frappe-agent/resources/scaffold_checklist.md +0 -82
  118. package/skills/cm-frappe-agent/resources/upgrade_patterns.md +0 -113
  119. package/skills/cm-frappe-agent/resources/web-form-patterns.md +0 -252
  120. package/skills/cm-frappe-agent/skills/bench-commands/SKILL.md +0 -621
  121. package/skills/cm-frappe-agent/skills/client-scripts/SKILL.md +0 -642
  122. package/skills/cm-frappe-agent/skills/doctype-patterns/SKILL.md +0 -576
  123. package/skills/cm-frappe-agent/skills/frappe-api/SKILL.md +0 -740
  124. package/skills/cm-frappe-agent/skills/remote-operations/SKILL.md +0 -47
  125. package/skills/cm-frappe-agent/skills/server-scripts/SKILL.md +0 -608
  126. package/skills/cm-frappe-agent/skills/web-forms/SKILL.md +0 -46
  127. package/skills/frappe-app-builder.zip +0 -0
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * CodyMaster MCP Context Server
5
5
  *
6
- * Exposes 7 tools over JSON-RPC 2.0 / stdio (Content-Length framing):
6
+ * Exposes 13 tools over JSON-RPC 2.0 / stdio (Content-Length framing):
7
7
  * cm_query — FTS5 search across learnings + decisions
8
8
  * cm_resolve — resolve a cm:// URI at L0/L1/L2
9
9
  * cm_bus_read — read context bus state
@@ -11,6 +11,7 @@
11
11
  * cm_budget_check — check token budget for a category
12
12
  * cm_memory_decay — TTL cleanup for learnings
13
13
  * cm_index_refresh — regenerate L0 indexes
14
+ * cm_plan / cm_review / cm_qa / cm_deploy / cm_search / cm_memory_query — engineering kit bridge
14
15
  *
15
16
  * Usage (stdio MCP):
16
17
  * node dist/mcp-context-server.js --project /path/to/project
@@ -44,6 +45,7 @@ const uri_resolver_1 = require("./uri-resolver");
44
45
  const context_bus_1 = require("./context-bus");
45
46
  const token_budget_1 = require("./token-budget");
46
47
  const l0_indexer_1 = require("./l0-indexer");
48
+ const mcp_skills_tools_1 = require("./mcp-skills-tools");
47
49
  // ─── Config ──────────────────────────────────────────────────────────────────
48
50
  const SERVER_NAME = 'cm-context';
49
51
  const SERVER_VERSION = '1.0.0';
@@ -295,6 +297,51 @@ const TOOLS = [
295
297
  },
296
298
  },
297
299
  },
300
+ {
301
+ name: 'cm_plan',
302
+ description: 'Sprint + context bus snapshot: pipeline state, next skill hint, artifact paths.',
303
+ inputSchema: { type: 'object', properties: {} },
304
+ },
305
+ {
306
+ name: 'cm_review',
307
+ description: 'Read sprint review artifact preview if present; points to cm-code-review workflow.',
308
+ inputSchema: { type: 'object', properties: {} },
309
+ },
310
+ {
311
+ name: 'cm_qa',
312
+ description: 'QA hints: browse daemon, visual QA CLI, quality gates.',
313
+ inputSchema: { type: 'object', properties: {} },
314
+ },
315
+ {
316
+ name: 'cm_deploy',
317
+ description: 'Deploy workflow hints (cm-safe-deploy, canary).',
318
+ inputSchema: { type: 'object', properties: {} },
319
+ },
320
+ {
321
+ name: 'cm_search',
322
+ description: 'Search learnings + decisions (same backing store as cm_query).',
323
+ inputSchema: {
324
+ type: 'object',
325
+ properties: {
326
+ query: { type: 'string' },
327
+ scope: { type: 'string', enum: ['learnings', 'decisions', 'all'] },
328
+ limit: { type: 'number' },
329
+ },
330
+ required: ['query'],
331
+ },
332
+ },
333
+ {
334
+ name: 'cm_memory_query',
335
+ description: 'Alias-style memory search across learnings and decisions.',
336
+ inputSchema: {
337
+ type: 'object',
338
+ properties: {
339
+ query: { type: 'string' },
340
+ limit: { type: 'number' },
341
+ },
342
+ required: ['query'],
343
+ },
344
+ },
298
345
  ];
299
346
  // ─── MCP stdio protocol (JSON-RPC 2.0, Content-Length framing) ───────────────
300
347
  function sendMessage(msg) {
@@ -344,6 +391,18 @@ function handleRequest(msg) {
344
391
  result = cmMemoryDecay(a);
345
392
  else if (name === 'cm_index_refresh')
346
393
  result = cmIndexRefresh(a);
394
+ else if (name === 'cm_plan')
395
+ result = (0, mcp_skills_tools_1.cmPlanTool)(PROJECT_PATH);
396
+ else if (name === 'cm_review')
397
+ result = (0, mcp_skills_tools_1.cmReviewTool)(PROJECT_PATH);
398
+ else if (name === 'cm_qa')
399
+ result = (0, mcp_skills_tools_1.cmQaTool)(PROJECT_PATH);
400
+ else if (name === 'cm_deploy')
401
+ result = (0, mcp_skills_tools_1.cmDeployTool)(PROJECT_PATH);
402
+ else if (name === 'cm_search')
403
+ result = (0, mcp_skills_tools_1.cmSearchTool)(PROJECT_PATH, a);
404
+ else if (name === 'cm_memory_query')
405
+ result = (0, mcp_skills_tools_1.cmMemoryQueryTool)(PROJECT_PATH, a);
347
406
  else
348
407
  throw new Error(`Unknown tool: ${name}`);
349
408
  respond(id, {
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ /**
3
+ * Extra MCP tool handlers: plan/review/qa/deploy/search — bridge to sprint + memory.
4
+ */
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.cmPlanTool = cmPlanTool;
10
+ exports.cmReviewTool = cmReviewTool;
11
+ exports.cmQaTool = cmQaTool;
12
+ exports.cmDeployTool = cmDeployTool;
13
+ exports.cmSearchTool = cmSearchTool;
14
+ exports.cmMemoryQueryTool = cmMemoryQueryTool;
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const path_1 = __importDefault(require("path"));
17
+ const context_bus_1 = require("./context-bus");
18
+ const sprint_pipeline_1 = require("./sprint-pipeline");
19
+ const context_db_1 = require("./context-db");
20
+ function cmPlanTool(projectPath) {
21
+ const sprint = (0, sprint_pipeline_1.readSprintState)(projectPath);
22
+ const bus = (0, context_bus_1.readBus)(projectPath);
23
+ const preview = (0, sprint_pipeline_1.sprintArtifactPreviewFromDisk)(projectPath);
24
+ return {
25
+ sprint_active: !!sprint,
26
+ sprint: sprint !== null && sprint !== void 0 ? sprint : null,
27
+ context_bus: bus,
28
+ default_pipeline: sprint_pipeline_1.SPRINT_STEPS,
29
+ next_skill_hint: sprint
30
+ ? sprint.current_index >= sprint.pipeline.length
31
+ ? '(sprint complete — run cm-retro)'
32
+ : (0, sprint_pipeline_1.skillMappingForStep)(sprint.pipeline[sprint.current_index])
33
+ : 'cm-planning',
34
+ artifact_paths: preview.artifacts,
35
+ };
36
+ }
37
+ function cmReviewTool(projectPath) {
38
+ const artDir = path_1.default.join(projectPath, '.cm', 'sprint', 'artifacts', 'review.md');
39
+ let review = '';
40
+ if (fs_1.default.existsSync(artDir))
41
+ review = fs_1.default.readFileSync(artDir, 'utf8');
42
+ return {
43
+ review_artifact: artDir,
44
+ has_content: review.length > 0,
45
+ preview: review.slice(0, 4000),
46
+ hint: 'Use cm-code-review skill for full checklist; paste diff + requirements.',
47
+ };
48
+ }
49
+ function cmQaTool(projectPath) {
50
+ return {
51
+ browse_daemon: 'Run: cm browse start --token <secret> then POST /session/start',
52
+ visual: 'cm qa-visual --url http://localhost:3000',
53
+ gates: ['cm-quality-gate', 'cm-test-gate'],
54
+ };
55
+ }
56
+ function cmDeployTool(projectPath) {
57
+ return {
58
+ hint: 'Use cm-safe-deploy skill; after ship run cm canary --url <prod>',
59
+ project: projectPath,
60
+ };
61
+ }
62
+ function cmSearchTool(projectPath, args) {
63
+ const { query, scope = 'all', limit = 10 } = args;
64
+ const dbPath = (0, context_db_1.getDbPath)(projectPath);
65
+ (0, context_db_1.openDb)(dbPath);
66
+ const results = [];
67
+ if (scope === 'all' || scope === 'learnings') {
68
+ for (const l of (0, context_db_1.queryLearnings)(dbPath, query, undefined, limit)) {
69
+ results.push(Object.assign({ type: 'learning' }, l));
70
+ }
71
+ }
72
+ if (scope === 'all' || scope === 'decisions') {
73
+ for (const d of (0, context_db_1.queryDecisions)(dbPath, query, limit)) {
74
+ results.push(Object.assign({ type: 'decision' }, d));
75
+ }
76
+ }
77
+ return { query, scope, count: results.length, results };
78
+ }
79
+ function cmMemoryQueryTool(projectPath, args) {
80
+ return cmSearchTool(projectPath, Object.assign(Object.assign({}, args), { scope: 'all' }));
81
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ /**
3
+ * Aggregate `.cm/operational-learnings.jsonl` for `cm retro summary`.
4
+ */
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.loadRetroEntries = loadRetroEntries;
10
+ exports.filterSince = filterSince;
11
+ exports.countByTool = countByTool;
12
+ exports.formatRetroMarkdown = formatRetroMarkdown;
13
+ exports.formatRetroJson = formatRetroJson;
14
+ const fs_1 = __importDefault(require("fs"));
15
+ function loadRetroEntries(filePath) {
16
+ if (!fs_1.default.existsSync(filePath))
17
+ return [];
18
+ const out = [];
19
+ for (const line of fs_1.default.readFileSync(filePath, 'utf8').split('\n')) {
20
+ const t = line.trim();
21
+ if (!t)
22
+ continue;
23
+ try {
24
+ const o = JSON.parse(t);
25
+ if (typeof o.ts === 'string' && typeof o.note === 'string') {
26
+ out.push({
27
+ ts: o.ts,
28
+ tool: typeof o.tool === 'string' ? o.tool : 'unknown',
29
+ note: o.note,
30
+ });
31
+ }
32
+ }
33
+ catch (_a) {
34
+ /* skip malformed line */
35
+ }
36
+ }
37
+ return out;
38
+ }
39
+ function filterSince(entries, sinceIso) {
40
+ const t0 = new Date(sinceIso).getTime();
41
+ if (Number.isNaN(t0))
42
+ return entries;
43
+ return entries.filter((e) => new Date(e.ts).getTime() >= t0);
44
+ }
45
+ function countByTool(entries) {
46
+ const m = {};
47
+ for (const e of entries) {
48
+ m[e.tool] = (m[e.tool] || 0) + 1;
49
+ }
50
+ return m;
51
+ }
52
+ function formatRetroMarkdown(entries, byTool) {
53
+ const lines = ['# Retro summary', '', `**Total entries:** ${entries.length}`, ''];
54
+ lines.push('## By tool');
55
+ for (const [tool, n] of Object.entries(byTool).sort((a, b) => b[1] - a[1])) {
56
+ lines.push(`- **${tool}:** ${n}`);
57
+ }
58
+ lines.push('', '## Entries (chronological)');
59
+ for (const e of entries.sort((a, b) => a.ts.localeCompare(b.ts))) {
60
+ lines.push(`- \`${e.ts}\` [${e.tool}] ${e.note}`);
61
+ }
62
+ return lines.join('\n');
63
+ }
64
+ function formatRetroJson(entries, byTool) {
65
+ return JSON.stringify({
66
+ total: entries.length,
67
+ by_tool: byTool,
68
+ entries: entries.sort((a, b) => a.ts.localeCompare(b.ts)),
69
+ }, null, 2);
70
+ }
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ /**
3
+ * Redaction + secondary-model review for `cm second-opinion`.
4
+ */
5
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
6
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
7
+ return new (P || (P = Promise))(function (resolve, reject) {
8
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
9
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
10
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
11
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
12
+ });
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.redactDiffForReview = redactDiffForReview;
16
+ exports.reviewWithOpenAI = reviewWithOpenAI;
17
+ exports.reviewWithAnthropic = reviewWithAnthropic;
18
+ const SYSTEM = 'You are a senior reviewer. List risks, bugs, and missing tests. Be concise. Do not restate the entire diff.';
19
+ function redactDiffForReview(text, maxLen = 120000) {
20
+ let t = text.slice(0, maxLen);
21
+ t = t.replace(/^(\s*(?:#\s*)?(?:API_KEY|API_SECRET|SECRET|PASSWORD|ACCESS_TOKEN|AUTH_TOKEN|BEARER|Authorization)\s*[:=]\s*)\S+.*$/gim, '$1[REDACTED]');
22
+ t = t.replace(/\b(sk-[a-zA-Z0-9]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}|ghp_[A-Za-z0-9]{36,}|gho_[A-Za-z0-9]{36,}|AKIA[0-9A-Z]{16})\b/g, '[REDACTED_TOKEN]');
23
+ return t;
24
+ }
25
+ function reviewWithOpenAI(diffText) {
26
+ return __awaiter(this, void 0, void 0, function* () {
27
+ var _a, _b, _c, _d;
28
+ const key = process.env.OPENAI_API_KEY;
29
+ if (!key)
30
+ throw new Error('OPENAI_API_KEY is not set');
31
+ const model = process.env.CM_SECOND_OPINION_MODEL || 'gpt-4o-mini';
32
+ const res = yield fetch('https://api.openai.com/v1/chat/completions', {
33
+ method: 'POST',
34
+ headers: {
35
+ 'Content-Type': 'application/json',
36
+ Authorization: `Bearer ${key}`,
37
+ },
38
+ body: JSON.stringify({
39
+ model,
40
+ messages: [
41
+ { role: 'system', content: SYSTEM },
42
+ { role: 'user', content: `Review this diff:\n\n${diffText}` },
43
+ ],
44
+ }),
45
+ });
46
+ if (!res.ok)
47
+ throw new Error(yield res.text());
48
+ const data = (yield res.json());
49
+ return (_d = (_c = (_b = (_a = data.choices) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.message) === null || _c === void 0 ? void 0 : _c.content) !== null && _d !== void 0 ? _d : '';
50
+ });
51
+ }
52
+ function reviewWithAnthropic(diffText) {
53
+ return __awaiter(this, void 0, void 0, function* () {
54
+ var _a, _b;
55
+ const key = process.env.ANTHROPIC_API_KEY;
56
+ if (!key)
57
+ throw new Error('ANTHROPIC_API_KEY is not set');
58
+ const model = process.env.CM_ANTHROPIC_MODEL || 'claude-3-5-haiku-20241022';
59
+ const res = yield fetch('https://api.anthropic.com/v1/messages', {
60
+ method: 'POST',
61
+ headers: {
62
+ 'Content-Type': 'application/json',
63
+ 'x-api-key': key,
64
+ 'anthropic-version': '2023-06-01',
65
+ },
66
+ body: JSON.stringify({
67
+ model,
68
+ max_tokens: 4096,
69
+ system: SYSTEM,
70
+ messages: [{ role: 'user', content: `Review this diff:\n\n${diffText}` }],
71
+ }),
72
+ });
73
+ if (!res.ok)
74
+ throw new Error(yield res.text());
75
+ const data = (yield res.json());
76
+ const block = (_a = data.content) === null || _a === void 0 ? void 0 : _a.find((c) => c.type === 'text');
77
+ return (_b = block === null || block === void 0 ? void 0 : block.text) !== null && _b !== void 0 ? _b : '';
78
+ });
79
+ }
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ /**
3
+ * Opinionated sprint pipeline + Context Bus files under `.cm/sprint/`.
4
+ * Complements root `context-bus.json` with step artifacts (ADR 002).
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.SPRINT_STEPS = void 0;
11
+ exports.readSprintState = readSprintState;
12
+ exports.writeSprintState = writeSprintState;
13
+ exports.initSprint = initSprint;
14
+ exports.completeSprintStep = completeSprintStep;
15
+ exports.skipSprintStep = skipSprintStep;
16
+ exports.resetSprint = resetSprint;
17
+ exports.sprintDryRun = sprintDryRun;
18
+ exports.sprintArtifactPreviewFromDisk = sprintArtifactPreviewFromDisk;
19
+ exports.skillMappingForStep = skillMappingForStep;
20
+ const fs_1 = __importDefault(require("fs"));
21
+ const path_1 = __importDefault(require("path"));
22
+ exports.SPRINT_STEPS = [
23
+ 'brainstorm',
24
+ 'plan',
25
+ 'design',
26
+ 'tdd',
27
+ 'build',
28
+ 'review',
29
+ 'qa',
30
+ 'security',
31
+ 'ship',
32
+ 'monitor',
33
+ 'retro',
34
+ ];
35
+ function sprintDir(projectPath) {
36
+ return path_1.default.join(projectPath, '.cm', 'sprint');
37
+ }
38
+ function statePath(projectPath) {
39
+ return path_1.default.join(sprintDir(projectPath), 'state.json');
40
+ }
41
+ function ensureSprintDir(projectPath) {
42
+ const d = sprintDir(projectPath);
43
+ const art = path_1.default.join(d, 'artifacts');
44
+ if (!fs_1.default.existsSync(art))
45
+ fs_1.default.mkdirSync(art, { recursive: true });
46
+ }
47
+ function normalizeSprintState(raw) {
48
+ var _a;
49
+ return Object.assign(Object.assign({}, raw), { version: raw.version === 2 ? 2 : 1, skipped: (_a = raw.skipped) !== null && _a !== void 0 ? _a : [] });
50
+ }
51
+ function readSprintState(projectPath) {
52
+ const p = statePath(projectPath);
53
+ if (!fs_1.default.existsSync(p))
54
+ return null;
55
+ try {
56
+ const raw = JSON.parse(fs_1.default.readFileSync(p, 'utf8'));
57
+ return normalizeSprintState(raw);
58
+ }
59
+ catch (_a) {
60
+ return null;
61
+ }
62
+ }
63
+ function writeSprintState(projectPath, state) {
64
+ ensureSprintDir(projectPath);
65
+ fs_1.default.writeFileSync(statePath(projectPath), JSON.stringify(state, null, 2), 'utf8');
66
+ }
67
+ function initSprint(projectPath, fromStep) {
68
+ ensureSprintDir(projectPath);
69
+ const now = new Date().toISOString();
70
+ let pipeline = [...exports.SPRINT_STEPS];
71
+ let startIdx = 0;
72
+ if (fromStep) {
73
+ const i = pipeline.indexOf(fromStep);
74
+ if (i >= 0)
75
+ startIdx = i;
76
+ }
77
+ const state = {
78
+ version: 2,
79
+ pipeline,
80
+ current_index: startIdx,
81
+ completed: [],
82
+ skipped: [],
83
+ started_at: now,
84
+ updated_at: now,
85
+ artifacts_dir: path_1.default.join(sprintDir(projectPath), 'artifacts'),
86
+ };
87
+ writeSprintState(projectPath, state);
88
+ appendEvent(projectPath, { type: 'init', from: fromStep !== null && fromStep !== void 0 ? fromStep : null, at: now });
89
+ return state;
90
+ }
91
+ function completeSprintStep(projectPath, step, artifactBody) {
92
+ let state = readSprintState(projectPath);
93
+ if (!state)
94
+ state = initSprint(projectPath);
95
+ if (state.current_index >= state.pipeline.length) {
96
+ throw new Error('Sprint pipeline already finished');
97
+ }
98
+ const expected = state.pipeline[state.current_index];
99
+ if (expected !== step) {
100
+ throw new Error(`Expected step "${expected}", got "${step}"`);
101
+ }
102
+ const artFile = path_1.default.join(state.artifacts_dir, `${step}.md`);
103
+ fs_1.default.writeFileSync(artFile, artifactBody, 'utf8');
104
+ state.completed.push(step);
105
+ state.current_index = Math.min(state.current_index + 1, state.pipeline.length);
106
+ state.updated_at = new Date().toISOString();
107
+ state.version = 2;
108
+ writeSprintState(projectPath, state);
109
+ appendEvent(projectPath, { type: 'complete', step, at: state.updated_at });
110
+ return state;
111
+ }
112
+ const SKIP_STUB = (step, at) => `# ${step}\n\n_Skipped via \`cm sprint skip\` at ${at}._\n`;
113
+ function skipSprintStep(projectPath, step) {
114
+ let state = readSprintState(projectPath);
115
+ if (!state)
116
+ state = initSprint(projectPath);
117
+ if (state.current_index >= state.pipeline.length) {
118
+ throw new Error('Sprint pipeline already finished');
119
+ }
120
+ const expected = state.pipeline[state.current_index];
121
+ if (expected !== step) {
122
+ throw new Error(`Expected step "${expected}", got "${step}"`);
123
+ }
124
+ const at = new Date().toISOString();
125
+ const artFile = path_1.default.join(state.artifacts_dir, `${step}.md`);
126
+ fs_1.default.writeFileSync(artFile, SKIP_STUB(step, at), 'utf8');
127
+ state.skipped.push(step);
128
+ state.current_index = Math.min(state.current_index + 1, state.pipeline.length);
129
+ state.updated_at = at;
130
+ state.version = 2;
131
+ writeSprintState(projectPath, state);
132
+ appendEvent(projectPath, { type: 'skip', step, at });
133
+ return state;
134
+ }
135
+ function backupDirName() {
136
+ return new Date().toISOString().replace(/:/g, '-');
137
+ }
138
+ /** Remove sprint state; optional backup under `.cm/sprint/backup/<timestamp>/`. */
139
+ function resetSprint(projectPath, options) {
140
+ const backup = (options === null || options === void 0 ? void 0 : options.backup) !== false;
141
+ const sd = sprintDir(projectPath);
142
+ const st = statePath(projectPath);
143
+ const ev = eventsPath(projectPath);
144
+ const art = path_1.default.join(sd, 'artifacts');
145
+ const hasState = fs_1.default.existsSync(st);
146
+ let hasEvents = false;
147
+ if (fs_1.default.existsSync(ev)) {
148
+ try {
149
+ hasEvents = fs_1.default.statSync(ev).size > 0;
150
+ }
151
+ catch (_a) {
152
+ hasEvents = false;
153
+ }
154
+ }
155
+ let hasArtifacts = false;
156
+ if (fs_1.default.existsSync(art)) {
157
+ try {
158
+ hasArtifacts = fs_1.default.readdirSync(art).length > 0;
159
+ }
160
+ catch (_b) {
161
+ hasArtifacts = false;
162
+ }
163
+ }
164
+ if (!hasState && !hasEvents && !hasArtifacts) {
165
+ return { ok: false, reason: 'no_sprint_data' };
166
+ }
167
+ let backupPath;
168
+ if (backup) {
169
+ const stamp = backupDirName();
170
+ backupPath = path_1.default.join(sd, 'backup', stamp);
171
+ fs_1.default.mkdirSync(backupPath, { recursive: true });
172
+ if (hasState)
173
+ fs_1.default.copyFileSync(st, path_1.default.join(backupPath, 'state.json'));
174
+ if (fs_1.default.existsSync(ev))
175
+ fs_1.default.copyFileSync(ev, path_1.default.join(backupPath, 'events.jsonl'));
176
+ if (fs_1.default.existsSync(art)) {
177
+ const destArt = path_1.default.join(backupPath, 'artifacts');
178
+ fs_1.default.cpSync(art, destArt, { recursive: true });
179
+ }
180
+ }
181
+ fs_1.default.rmSync(st, { force: true });
182
+ fs_1.default.rmSync(ev, { force: true });
183
+ fs_1.default.rmSync(art, { recursive: true, force: true });
184
+ fs_1.default.mkdirSync(art, { recursive: true });
185
+ return { ok: true, backupDir: backupPath };
186
+ }
187
+ function sprintDryRun(projectPath) {
188
+ var _a;
189
+ const state = (_a = readSprintState(projectPath)) !== null && _a !== void 0 ? _a : initSprint(projectPath);
190
+ return sprintArtifactPreview(state);
191
+ }
192
+ /** Read-only preview without creating files (for MCP / status). */
193
+ function sprintArtifactPreviewFromDisk(projectPath) {
194
+ const state = readSprintState(projectPath);
195
+ if (!state) {
196
+ const base = path_1.default.join(projectPath, '.cm', 'sprint', 'artifacts');
197
+ const artifacts = exports.SPRINT_STEPS.map((s) => path_1.default.join(base, `${s}.md`));
198
+ return { steps: [...exports.SPRINT_STEPS], artifacts };
199
+ }
200
+ return sprintArtifactPreview(state);
201
+ }
202
+ function sprintArtifactPreview(state) {
203
+ const artifacts = state.pipeline.map((s) => path_1.default.join(state.artifacts_dir, `${s}.md`));
204
+ return { steps: [...state.pipeline], artifacts };
205
+ }
206
+ function eventsPath(projectPath) {
207
+ return path_1.default.join(sprintDir(projectPath), 'events.jsonl');
208
+ }
209
+ function appendEvent(projectPath, rec) {
210
+ ensureSprintDir(projectPath);
211
+ fs_1.default.appendFileSync(eventsPath(projectPath), JSON.stringify(rec) + '\n', 'utf8');
212
+ }
213
+ function skillMappingForStep(step) {
214
+ const map = {
215
+ brainstorm: 'cm-brainstorm-idea',
216
+ plan: 'cm-planning',
217
+ design: 'cm-ui-preview / cm-design-system',
218
+ tdd: 'cm-tdd',
219
+ build: 'cm-execution',
220
+ review: 'cm-code-review',
221
+ qa: 'cm-quality-gate / cm-test-gate',
222
+ security: 'cm-secret-shield / cm-security-gate',
223
+ ship: 'cm-safe-deploy',
224
+ monitor: 'cm-canary (post-deploy)',
225
+ retro: 'cm-retro',
226
+ };
227
+ return map[step];
228
+ }
@@ -1,16 +1,12 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.VikingBackend = exports.SqliteBackend = void 0;
7
4
  exports.getBackend = getBackend;
8
- const fs_1 = __importDefault(require("fs"));
9
- const path_1 = __importDefault(require("path"));
10
5
  const context_db_1 = require("./context-db");
11
6
  const viking_backend_1 = require("./backends/viking-backend");
12
7
  Object.defineProperty(exports, "VikingBackend", { enumerable: true, get: function () { return viking_backend_1.VikingBackend; } });
13
8
  const viking_http_client_1 = require("./backends/viking-http-client");
9
+ const cm_config_1 = require("./cm-config");
14
10
  // ─── SqliteBackend ────────────────────────────────────────────────────────────
15
11
  /**
16
12
  * Default backend — thin wrapper around context-db.ts (better-sqlite3 + FTS5).
@@ -39,62 +35,11 @@ class SqliteBackend {
39
35
  getSkillOutputs(sessionId) { return (0, context_db_1.getSkillOutputs)(this.dbPath, sessionId); }
40
36
  }
41
37
  exports.SqliteBackend = SqliteBackend;
42
- /**
43
- * Minimal YAML parser — reads `storage.backend` and `storage.viking.*` keys.
44
- * Avoids adding a js-yaml dependency for a handful of config fields.
45
- *
46
- * Supported format:
47
- * storage:
48
- * backend: viking
49
- * viking:
50
- * host: localhost
51
- * port: 1933
52
- * workspace: codymaster
53
- * timeout: 60000
54
- */
55
- function loadStorageConfig(projectPath) {
56
- var _a;
57
- const configPath = path_1.default.join(projectPath, '.cm', 'config.yaml');
58
- if (!fs_1.default.existsSync(configPath))
59
- return {};
60
- try {
61
- const raw = fs_1.default.readFileSync(configPath, 'utf-8');
62
- // Extract storage.backend
63
- const backendMatch = raw.match(/^storage:\s*\n(?:[ \t]+\S[^\n]*\n)*?[ \t]+backend:\s*(\S+)/m);
64
- const backend = (_a = backendMatch === null || backendMatch === void 0 ? void 0 : backendMatch[1]) === null || _a === void 0 ? void 0 : _a.trim();
65
- // Extract storage.viking.* keys
66
- const vikingBlock = raw.match(/[ \t]+viking:\s*\n((?:[ \t]{4,}[^\n]+\n?)*)/m);
67
- let viking;
68
- if (vikingBlock === null || vikingBlock === void 0 ? void 0 : vikingBlock[1]) {
69
- viking = {};
70
- for (const line of vikingBlock[1].split('\n')) {
71
- const kv = line.match(/[ \t]+(\w+):\s*(\S+)/);
72
- if (!kv)
73
- continue;
74
- const [, key, val] = kv;
75
- if (key === 'host')
76
- viking.host = val;
77
- if (key === 'workspace')
78
- viking.workspace = val;
79
- if (key === 'port')
80
- viking.port = parseInt(val, 10);
81
- if (key === 'timeout')
82
- viking.timeout = parseInt(val, 10);
83
- }
84
- }
85
- if (!backend)
86
- return {};
87
- return { storage: Object.assign({ backend }, (viking ? { viking } : {})) };
88
- }
89
- catch (_b) {
90
- return {};
91
- }
92
- }
93
38
  // ─── Factory ─────────────────────────────────────────────────────────────────
94
39
  /**
95
40
  * Returns the configured StorageBackend for the given project.
96
41
  *
97
- * Reads `.cm/config.yaml → storage.backend` (default: `sqlite`).
42
+ * Reads `.cm/config.yaml → storage.backend` via `loadCmConfig` (default: `sqlite`).
98
43
  * For `viking` backend, reads `storage.viking.*` for connection config.
99
44
  *
100
45
  * Usage:
@@ -104,11 +49,11 @@ function loadStorageConfig(projectPath) {
104
49
  */
105
50
  function getBackend(projectPath) {
106
51
  var _a, _b, _c;
107
- const config = loadStorageConfig(projectPath);
108
- const engine = (_b = (_a = config === null || config === void 0 ? void 0 : config.storage) === null || _a === void 0 ? void 0 : _a.backend) !== null && _b !== void 0 ? _b : 'sqlite';
52
+ const cfg = (0, cm_config_1.loadCmConfig)(projectPath);
53
+ const engine = ((_b = (_a = cfg.storage) === null || _a === void 0 ? void 0 : _a.backend) !== null && _b !== void 0 ? _b : 'sqlite').toLowerCase();
109
54
  switch (engine) {
110
55
  case 'viking': {
111
- const vikingConfig = Object.assign(Object.assign({}, viking_http_client_1.DEFAULT_VIKING_CONFIG), (_c = config === null || config === void 0 ? void 0 : config.storage) === null || _c === void 0 ? void 0 : _c.viking);
56
+ const vikingConfig = Object.assign(Object.assign({}, viking_http_client_1.DEFAULT_VIKING_CONFIG), (_c = cfg.storage) === null || _c === void 0 ? void 0 : _c.viking);
112
57
  return new viking_backend_1.VikingBackend(vikingConfig);
113
58
  }
114
59
  case 'sqlite':