codymaster 4.6.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/CHANGELOG.md +74 -8
  2. package/README.md +192 -95
  3. package/dist/advisory-handoff.js +89 -0
  4. package/dist/advisory-report.js +105 -0
  5. package/dist/browse-server.js +251 -0
  6. package/dist/cli/command-registry.js +34 -0
  7. package/dist/cli/commands/agent.js +120 -0
  8. package/dist/cli/commands/bench.js +69 -0
  9. package/dist/cli/commands/brain.js +108 -0
  10. package/dist/cli/commands/dashboard.js +93 -0
  11. package/dist/cli/commands/design-studio.js +111 -0
  12. package/dist/cli/commands/distro.js +25 -0
  13. package/dist/cli/commands/engineering.js +596 -0
  14. package/dist/cli/commands/evolve.js +123 -0
  15. package/dist/cli/commands/mcp-serve.js +104 -0
  16. package/dist/cli/commands/project.js +324 -0
  17. package/dist/cli/commands/skill-chain.js +269 -0
  18. package/dist/cli/commands/system.js +89 -0
  19. package/dist/cli/commands/task.js +254 -0
  20. package/dist/cli/update-check.js +83 -0
  21. package/dist/cm-config.js +92 -0
  22. package/dist/cm-suggest.js +77 -0
  23. package/dist/codybench/judges/automated.js +31 -0
  24. package/dist/codybench/runners/claude-code.js +32 -0
  25. package/dist/codybench/suites/memory-retention.js +85 -0
  26. package/dist/codybench/suites/tdd-regression.js +35 -0
  27. package/dist/codybench/suites/token-efficiency.js +55 -0
  28. package/dist/codybench/types.js +2 -0
  29. package/dist/context-db.js +157 -0
  30. package/dist/continuity.js +2 -6
  31. package/dist/distro-validate.js +54 -0
  32. package/dist/execution-analyzer.js +138 -0
  33. package/dist/guardian-core.js +74 -0
  34. package/dist/index.js +36 -2759
  35. package/dist/indexer/skills-lib.js +533 -0
  36. package/dist/indexer/skills-map.js +1374 -0
  37. package/dist/indexer/skills.js +16 -0
  38. package/dist/learning-promoter.js +246 -0
  39. package/dist/mcp-context-server.js +289 -1
  40. package/dist/mcp-skills-tools.js +81 -0
  41. package/dist/retro-summary.js +70 -0
  42. package/dist/second-opinion-providers.js +79 -0
  43. package/dist/skill-chain.js +63 -1
  44. package/dist/skill-evolver.js +456 -0
  45. package/dist/skill-execution-cache.js +254 -0
  46. package/dist/smart-brain-router.js +184 -0
  47. package/dist/sprint-pipeline.js +228 -0
  48. package/dist/storage-backend.js +14 -67
  49. package/dist/token-budget.js +88 -0
  50. package/dist/utils/cli-utils.js +76 -0
  51. package/dist/utils/skill-utils.js +32 -0
  52. package/package.json +17 -7
  53. package/scripts/build-skills.mjs +51 -0
  54. package/scripts/gate-0-repo-hygiene.js +75 -0
  55. package/scripts/postinstall.js +34 -28
  56. package/scripts/security-scan.js +1 -1
  57. package/scripts/validate-skills.mjs +42 -0
  58. package/skills/CLAUDE.md +2 -7
  59. package/skills/_shared/helpers.md +2 -8
  60. package/skills/cm-ads-tracker/SKILL.md +3 -6
  61. package/skills/cm-browse/SKILL.md +34 -0
  62. package/skills/cm-conductor-worktrees/SKILL.md +28 -0
  63. package/skills/cm-content-factory/SKILL.md +1 -1
  64. package/skills/cm-content-factory/landing/docs/content/changelog.md +36 -0
  65. package/skills/cm-content-factory/landing/docs/content/deployment.md +46 -0
  66. package/skills/cm-content-factory/landing/docs/content/execution-flow.md +67 -0
  67. package/skills/cm-content-factory/landing/docs/content/memory-system.md +38 -0
  68. package/skills/cm-content-factory/landing/docs/content/openspace.md +27 -0
  69. package/skills/cm-content-factory/landing/docs/content/use-cases.md +26 -0
  70. package/skills/cm-content-factory/landing/docs/content/v5-intro.md +28 -0
  71. package/skills/cm-content-factory/landing/docs/index.html +240 -0
  72. package/skills/cm-content-factory/landing/index.html +100 -100
  73. package/skills/cm-content-factory/landing/script.js +42 -0
  74. package/skills/cm-content-factory/landing/translations.js +400 -400
  75. package/skills/cm-continuity/SKILL.md +32 -33
  76. package/skills/cm-design-studio/SKILL.md +34 -0
  77. package/skills/cm-ecosystem-roadmap/SKILL.md +15 -0
  78. package/skills/cm-engineering-meta/SKILL.md +73 -0
  79. package/skills/cm-growth-hacking/SKILL.md +1 -12
  80. package/skills/cm-guardian-runtime/SKILL.md +26 -0
  81. package/skills/cm-mcp-engineering/SKILL.md +22 -0
  82. package/skills/cm-notebooklm/SKILL.md +1 -17
  83. package/skills/cm-post-deploy-canary/SKILL.md +22 -0
  84. package/skills/cm-project-bootstrap/SKILL.md +11 -0
  85. package/skills/cm-qa-visual-cli/SKILL.md +22 -0
  86. package/skills/cm-retro-cli/SKILL.md +23 -0
  87. package/skills/cm-second-opinion-cli/SKILL.md +23 -0
  88. package/skills/cm-secret-shield/SKILL.md +2 -2
  89. package/skills/cm-security-gate/SKILL.md +1 -0
  90. package/skills/cm-skill-chain/SKILL.md +25 -4
  91. package/skills/cm-skill-evolution/SKILL.md +83 -0
  92. package/skills/cm-skill-health/SKILL.md +83 -0
  93. package/skills/cm-skill-index/SKILL.md +11 -3
  94. package/skills/cm-skill-search/SKILL.md +49 -0
  95. package/skills/cm-skill-share/SKILL.md +58 -0
  96. package/skills/cm-sprint-bus/SKILL.md +33 -0
  97. package/skills/cm-start/SKILL.md +0 -10
  98. package/skills/cm-tdd/SKILL.md +59 -72
  99. package/skills/profiles/README.md +21 -0
  100. package/skills/profiles/core.txt +23 -0
  101. package/skills/profiles/design.txt +6 -0
  102. package/skills/profiles/full.txt +62 -0
  103. package/skills/profiles/growth.txt +10 -0
  104. package/skills/profiles/knowledge.txt +7 -0
  105. package/install.sh +0 -901
  106. package/scripts/test-gemini.js +0 -13
  107. package/skills/cm-frappe-agent/SKILL.md +0 -134
  108. package/skills/cm-frappe-agent/agents/doctype-architect.md +0 -596
  109. package/skills/cm-frappe-agent/agents/erpnext-customizer.md +0 -643
  110. package/skills/cm-frappe-agent/agents/frappe-backend.md +0 -814
  111. package/skills/cm-frappe-agent/agents/frappe-custom-frontend.md +0 -557
  112. package/skills/cm-frappe-agent/agents/frappe-debugger.md +0 -625
  113. package/skills/cm-frappe-agent/agents/frappe-fixer.md +0 -275
  114. package/skills/cm-frappe-agent/agents/frappe-frontend.md +0 -660
  115. package/skills/cm-frappe-agent/agents/frappe-installer.md +0 -158
  116. package/skills/cm-frappe-agent/agents/frappe-performance.md +0 -307
  117. package/skills/cm-frappe-agent/agents/frappe-planner.md +0 -419
  118. package/skills/cm-frappe-agent/agents/frappe-remote-ops.md +0 -153
  119. package/skills/cm-frappe-agent/agents/github-workflow.md +0 -286
  120. package/skills/cm-frappe-agent/commands/frappe-app.md +0 -351
  121. package/skills/cm-frappe-agent/commands/frappe-backend.md +0 -162
  122. package/skills/cm-frappe-agent/commands/frappe-bench.md +0 -254
  123. package/skills/cm-frappe-agent/commands/frappe-debug.md +0 -263
  124. package/skills/cm-frappe-agent/commands/frappe-doctype-create.md +0 -272
  125. package/skills/cm-frappe-agent/commands/frappe-doctype-field.md +0 -310
  126. package/skills/cm-frappe-agent/commands/frappe-erpnext.md +0 -210
  127. package/skills/cm-frappe-agent/commands/frappe-fix.md +0 -59
  128. package/skills/cm-frappe-agent/commands/frappe-frontend.md +0 -210
  129. package/skills/cm-frappe-agent/commands/frappe-fullstack.md +0 -243
  130. package/skills/cm-frappe-agent/commands/frappe-github.md +0 -57
  131. package/skills/cm-frappe-agent/commands/frappe-install.md +0 -52
  132. package/skills/cm-frappe-agent/commands/frappe-plan.md +0 -442
  133. package/skills/cm-frappe-agent/commands/frappe-remote.md +0 -58
  134. package/skills/cm-frappe-agent/commands/frappe-test.md +0 -356
  135. package/skills/cm-frappe-agent/docs/README.md +0 -51
  136. package/skills/cm-frappe-agent/docs/agents-catalog.md +0 -113
  137. package/skills/cm-frappe-agent/docs/architecture.md +0 -149
  138. package/skills/cm-frappe-agent/docs/commands-catalog.md +0 -82
  139. package/skills/cm-frappe-agent/docs/resources-catalog.md +0 -66
  140. package/skills/cm-frappe-agent/docs/sitemap-urls.txt +0 -52
  141. package/skills/cm-frappe-agent/docs/sitemap.md +0 -81
  142. package/skills/cm-frappe-agent/docs/sop/user-guide.md +0 -178
  143. package/skills/cm-frappe-agent/docs/sop/vibe-coding-guide.md +0 -122
  144. package/skills/cm-frappe-agent/resources/7-layer-architecture.md +0 -985
  145. package/skills/cm-frappe-agent/resources/bench_commands.md +0 -73
  146. package/skills/cm-frappe-agent/resources/code-patterns-guide.md +0 -948
  147. package/skills/cm-frappe-agent/resources/common_pitfalls.md +0 -266
  148. package/skills/cm-frappe-agent/resources/doctype-registry.md +0 -158
  149. package/skills/cm-frappe-agent/resources/installation-guide.md +0 -289
  150. package/skills/cm-frappe-agent/resources/rest-api-patterns.md +0 -182
  151. package/skills/cm-frappe-agent/resources/scaffold_checklist.md +0 -82
  152. package/skills/cm-frappe-agent/resources/upgrade_patterns.md +0 -113
  153. package/skills/cm-frappe-agent/resources/web-form-patterns.md +0 -252
  154. package/skills/cm-frappe-agent/skills/bench-commands/SKILL.md +0 -621
  155. package/skills/cm-frappe-agent/skills/client-scripts/SKILL.md +0 -642
  156. package/skills/cm-frappe-agent/skills/doctype-patterns/SKILL.md +0 -576
  157. package/skills/cm-frappe-agent/skills/frappe-api/SKILL.md +0 -740
  158. package/skills/cm-frappe-agent/skills/remote-operations/SKILL.md +0 -47
  159. package/skills/cm-frappe-agent/skills/server-scripts/SKILL.md +0 -608
  160. package/skills/cm-frappe-agent/skills/web-forms/SKILL.md +0 -46
  161. package/skills/frappe-app-builder.zip +0 -0
@@ -0,0 +1,596 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.registerEngineeringCommands = registerEngineeringCommands;
16
+ const fs_1 = __importDefault(require("fs"));
17
+ const path_1 = __importDefault(require("path"));
18
+ const child_process_1 = require("child_process");
19
+ const http_1 = __importDefault(require("http"));
20
+ const chalk_1 = __importDefault(require("chalk"));
21
+ const browse_server_1 = require("../../browse-server");
22
+ const guardian_core_1 = require("../../guardian-core");
23
+ const cm_config_1 = require("../../cm-config");
24
+ const second_opinion_providers_1 = require("../../second-opinion-providers");
25
+ const sprint_pipeline_1 = require("../../sprint-pipeline");
26
+ const retro_summary_1 = require("../../retro-summary");
27
+ const cm_suggest_1 = require("../../cm-suggest");
28
+ const storage_backend_1 = require("../../storage-backend");
29
+ const advisory_report_1 = require("../../advisory-report");
30
+ const advisory_handoff_1 = require("../../advisory-handoff");
31
+ function projectPath(opt) {
32
+ return path_1.default.resolve(opt || process.cwd());
33
+ }
34
+ function registerEngineeringCommands(program) {
35
+ const browse = program.command('browse').description('Playwright browse daemon (local QA / screenshots)');
36
+ browse
37
+ .command('start')
38
+ .option('-p, --port <n>', 'port (default: .cm/config.yaml browse.port or 17395)')
39
+ .option('-H, --host <h>', 'bind host (default: config or 127.0.0.1)')
40
+ .option('--token <t>', 'bearer token (or env CM_BROWSE_TOKEN or config browse.token)')
41
+ .option('--headed', 'headed browser', false)
42
+ .action((opts) => __awaiter(this, void 0, void 0, function* () {
43
+ var _a, _b, _c, _d, _e, _f, _g;
44
+ const root = process.cwd();
45
+ const cfg = (0, cm_config_1.loadCmConfig)(root);
46
+ const port = parseInt(String((_c = (_a = opts.port) !== null && _a !== void 0 ? _a : (_b = cfg.browse) === null || _b === void 0 ? void 0 : _b.port) !== null && _c !== void 0 ? _c : 17395), 10);
47
+ const host = String((_f = (_d = opts.host) !== null && _d !== void 0 ? _d : (_e = cfg.browse) === null || _e === void 0 ? void 0 : _e.host) !== null && _f !== void 0 ? _f : '127.0.0.1');
48
+ const token = opts.token ||
49
+ process.env.CM_BROWSE_TOKEN ||
50
+ ((_g = cfg.browse) === null || _g === void 0 ? void 0 : _g.token) ||
51
+ 'dev-token-change-me';
52
+ const daemon = new browse_server_1.BrowseDaemon({
53
+ host,
54
+ port,
55
+ token,
56
+ headless: !opts.headed,
57
+ });
58
+ yield daemon.listen();
59
+ console.log(chalk_1.default.green(`cm-browse listening http://${host}:${port}`));
60
+ console.log(chalk_1.default.dim(`Authorization: Bearer ${token.slice(0, 8)}…`));
61
+ console.log(chalk_1.default.dim('POST /session/start, /navigate, /refs/refresh, /click, /fill, GET /screenshot'));
62
+ process.on('SIGINT', () => __awaiter(this, void 0, void 0, function* () {
63
+ yield daemon.close();
64
+ process.exit(0);
65
+ }));
66
+ }));
67
+ const guardian = program.command('guardian').description('Runtime safety: destructive command patterns + path freeze');
68
+ guardian
69
+ .command('check')
70
+ .argument('<cmd...>', 'shell command to check')
71
+ .action((parts) => {
72
+ var _a;
73
+ const cmd = parts.join(' ');
74
+ const cfg = (0, cm_config_1.loadCmConfig)(process.cwd());
75
+ const extra = (_a = cfg.guardian) === null || _a === void 0 ? void 0 : _a.whitelist_prefixes;
76
+ const r = (0, guardian_core_1.checkShellCommand)(cmd, (extra === null || extra === void 0 ? void 0 : extra.length) ? { extraWhitelist: extra } : undefined);
77
+ if (!r.safe) {
78
+ console.error(chalk_1.default.red('BLOCKED:'), r.reason);
79
+ console.error(chalk_1.default.dim('Pattern:'), r.matchedPattern);
80
+ (0, guardian_core_1.appendGuardianLog)(process.cwd(), `BLOCKED: ${cmd}`);
81
+ process.exit(1);
82
+ }
83
+ console.log(chalk_1.default.green('OK'), chalk_1.default.dim(cmd));
84
+ });
85
+ guardian
86
+ .command('path-check')
87
+ .requiredOption('--file <f>', 'file path')
88
+ .option('--roots <r>', 'comma-separated roots (default: config guardian.freeze_roots or src,lib)')
89
+ .action((opts) => {
90
+ var _a, _b, _c;
91
+ const cwd = process.cwd();
92
+ const cfg = (0, cm_config_1.loadCmConfig)(cwd);
93
+ const rootsCsv = (_a = opts.roots) !== null && _a !== void 0 ? _a : (((_c = (_b = cfg.guardian) === null || _b === void 0 ? void 0 : _b.freeze_roots) === null || _c === void 0 ? void 0 : _c.length) ? cfg.guardian.freeze_roots.join(',') : 'src,lib');
94
+ const roots = (0, guardian_core_1.normalizeRoots)(cwd, String(rootsCsv)
95
+ .split(',')
96
+ .map((s) => s.trim())
97
+ .filter(Boolean));
98
+ const ok = (0, guardian_core_1.isPathUnderRoots)(opts.file, roots);
99
+ if (!ok) {
100
+ console.error(chalk_1.default.red('Path outside freeze roots:'), opts.file);
101
+ (0, guardian_core_1.appendGuardianLog)(cwd, `FREEZE_VIOLATION: ${opts.file}`);
102
+ process.exit(1);
103
+ }
104
+ console.log(chalk_1.default.green('OK'), opts.file);
105
+ });
106
+ const advisory = program
107
+ .command('advisory')
108
+ .description('Operator-facing execution analysis and skill quality reports');
109
+ advisory
110
+ .command('report')
111
+ .description('Show recent execution analyses with recommended actions')
112
+ .option('--project <dir>')
113
+ .option('--limit <n>', 'number of analyses to show', '10')
114
+ .action((opts) => {
115
+ var _a;
116
+ const root = projectPath(opts.project);
117
+ const backend = (0, storage_backend_1.getBackend)(root);
118
+ backend.initialize();
119
+ try {
120
+ const limit = Math.max(1, parseInt(String((_a = opts.limit) !== null && _a !== void 0 ? _a : '10'), 10) || 10);
121
+ console.log((0, advisory_report_1.formatAdvisoryReport)(backend, { limit }));
122
+ }
123
+ finally {
124
+ backend.close();
125
+ }
126
+ });
127
+ advisory
128
+ .command('metrics')
129
+ .description('Show aggregated skill metrics with quality weights')
130
+ .option('--project <dir>')
131
+ .option('--limit <n>', 'number of skills to show', '10')
132
+ .action((opts) => {
133
+ var _a;
134
+ const root = projectPath(opts.project);
135
+ const backend = (0, storage_backend_1.getBackend)(root);
136
+ backend.initialize();
137
+ try {
138
+ const limit = Math.max(1, parseInt(String((_a = opts.limit) !== null && _a !== void 0 ? _a : '10'), 10) || 10);
139
+ console.log((0, advisory_report_1.formatAdvisoryMetrics)(backend, { limit }));
140
+ }
141
+ finally {
142
+ backend.close();
143
+ }
144
+ });
145
+ advisory
146
+ .command('handoff')
147
+ .description('Build a structured advisory handoff for cm-skill-health or cm-skill-evolution')
148
+ .requiredOption('--for <consumer>', 'cm-skill-health | cm-skill-evolution')
149
+ .option('--analysis <id>', 'analysis id prefix (default: latest)')
150
+ .option('--skill <name>', 'override target skill')
151
+ .option('--format <f>', 'md | json', 'md')
152
+ .option('--project <dir>')
153
+ .action((opts) => {
154
+ var _a;
155
+ const consumer = String(opts.for);
156
+ if (consumer !== 'cm-skill-health' && consumer !== 'cm-skill-evolution') {
157
+ console.error(chalk_1.default.red('Invalid --for value. Use cm-skill-health or cm-skill-evolution.'));
158
+ process.exit(1);
159
+ }
160
+ const root = projectPath(opts.project);
161
+ const backend = (0, storage_backend_1.getBackend)(root);
162
+ backend.initialize();
163
+ try {
164
+ const handoff = (0, advisory_handoff_1.buildAdvisoryHandoff)(backend, {
165
+ consumer,
166
+ analysisId: opts.analysis,
167
+ skill: opts.skill,
168
+ });
169
+ const format = String((_a = opts.format) !== null && _a !== void 0 ? _a : 'md').toLowerCase();
170
+ if (format === 'json')
171
+ console.log(JSON.stringify(handoff, null, 2));
172
+ else
173
+ console.log((0, advisory_handoff_1.formatAdvisoryHandoffMarkdown)(handoff));
174
+ }
175
+ catch (error) {
176
+ console.error(chalk_1.default.red(error.message));
177
+ process.exit(1);
178
+ }
179
+ finally {
180
+ backend.close();
181
+ }
182
+ });
183
+ const sprint = program.command('sprint').description('Opinionated pipeline + .cm/sprint Context Bus');
184
+ sprint
185
+ .command('init')
186
+ .option('--project <dir>')
187
+ .option('--from <step>', `one of: ${sprint_pipeline_1.SPRINT_STEPS.join(',')}`)
188
+ .action((opts) => {
189
+ const root = projectPath(opts.project);
190
+ const from = opts.from;
191
+ if (from && !sprint_pipeline_1.SPRINT_STEPS.includes(from)) {
192
+ console.error(chalk_1.default.red('Invalid --from'));
193
+ process.exit(1);
194
+ }
195
+ const state = (0, sprint_pipeline_1.initSprint)(root, from);
196
+ console.log(chalk_1.default.green('Sprint initialized'));
197
+ console.log(chalk_1.default.dim(JSON.stringify(state, null, 2)));
198
+ });
199
+ sprint
200
+ .command('status')
201
+ .option('--project <dir>')
202
+ .action((opts) => {
203
+ const root = projectPath(opts.project);
204
+ const state = (0, sprint_pipeline_1.readSprintState)(root);
205
+ if (!state) {
206
+ console.log(chalk_1.default.yellow('No sprint state. Run: cm sprint init'));
207
+ return;
208
+ }
209
+ const next = state.current_index >= state.pipeline.length
210
+ ? '(done)'
211
+ : state.pipeline[state.current_index];
212
+ console.log(chalk_1.default.cyan('Current step:'), next);
213
+ if (typeof next === 'string' && next !== '(done)')
214
+ console.log(chalk_1.default.dim('Skill hint:'), (0, sprint_pipeline_1.skillMappingForStep)(next));
215
+ console.log(chalk_1.default.dim('Completed:'), state.completed.join(', ') || '(none)');
216
+ console.log(chalk_1.default.dim('Skipped:'), state.skipped.join(', ') || '(none)');
217
+ });
218
+ sprint
219
+ .command('complete')
220
+ .argument('<step>', 'step name')
221
+ .option('--project <dir>')
222
+ .option('-m, --message <text>', 'artifact markdown body', '')
223
+ .action((step, opts) => {
224
+ const root = projectPath(opts.project);
225
+ if (!sprint_pipeline_1.SPRINT_STEPS.includes(step)) {
226
+ console.error(chalk_1.default.red('Invalid step'));
227
+ process.exit(1);
228
+ }
229
+ const body = opts.message ||
230
+ `# ${step}\n\n_Completed via \`cm sprint complete\` — replace with real notes._\n`;
231
+ try {
232
+ const state = (0, sprint_pipeline_1.completeSprintStep)(root, step, body);
233
+ console.log(chalk_1.default.green('Step recorded:', step));
234
+ console.log(chalk_1.default.dim('Next index:', state.current_index));
235
+ }
236
+ catch (e) {
237
+ console.error(chalk_1.default.red(e.message));
238
+ process.exit(1);
239
+ }
240
+ });
241
+ sprint
242
+ .command('skip')
243
+ .argument('[step]', 'step name (default: current step)')
244
+ .option('--project <dir>')
245
+ .action((step, opts) => {
246
+ const root = projectPath(opts.project);
247
+ const state = (0, sprint_pipeline_1.readSprintState)(root);
248
+ if (!state) {
249
+ console.error(chalk_1.default.red('No sprint state. Run: cm sprint init'));
250
+ process.exit(1);
251
+ }
252
+ if (state.current_index >= state.pipeline.length) {
253
+ console.error(chalk_1.default.red('Sprint pipeline already finished'));
254
+ process.exit(1);
255
+ }
256
+ const current = state.pipeline[state.current_index];
257
+ const target = (step || current);
258
+ if (step && !sprint_pipeline_1.SPRINT_STEPS.includes(target)) {
259
+ console.error(chalk_1.default.red('Invalid step'));
260
+ process.exit(1);
261
+ }
262
+ try {
263
+ const next = (0, sprint_pipeline_1.skipSprintStep)(root, target);
264
+ console.log(chalk_1.default.green('Skipped step:', target));
265
+ console.log(chalk_1.default.dim('Next index:', next.current_index));
266
+ }
267
+ catch (e) {
268
+ console.error(chalk_1.default.red(e.message));
269
+ process.exit(1);
270
+ }
271
+ });
272
+ sprint
273
+ .command('reset')
274
+ .option('--project <dir>')
275
+ .option('--no-backup', 'do not copy sprint files to .cm/sprint/backup before clearing')
276
+ .action((opts) => {
277
+ const root = projectPath(opts.project);
278
+ const r = (0, sprint_pipeline_1.resetSprint)(root, { backup: opts.backup !== false });
279
+ if (!r.ok) {
280
+ console.log(chalk_1.default.yellow('Nothing to reset (no sprint data under .cm/sprint).'));
281
+ return;
282
+ }
283
+ if (r.backupDir)
284
+ console.log(chalk_1.default.dim('Backup:'), r.backupDir);
285
+ console.log(chalk_1.default.green('Sprint data cleared. Run: cm sprint init'));
286
+ });
287
+ sprint
288
+ .command('dry-run')
289
+ .option('--project <dir>')
290
+ .action((opts) => {
291
+ const root = projectPath(opts.project);
292
+ const d = (0, sprint_pipeline_1.sprintDryRun)(root);
293
+ console.log(chalk_1.default.cyan('Steps:'), d.steps.join(' → '));
294
+ d.artifacts.forEach((a) => console.log(chalk_1.default.dim(' -'), a));
295
+ });
296
+ program
297
+ .command('second-opinion')
298
+ .description('Send unified diff to a secondary model (redacts obvious secrets)')
299
+ .option('--file <f>', 'file containing diff text')
300
+ .option('--provider <p>', 'openai | anthropic', 'openai')
301
+ .action((opts) => __awaiter(this, void 0, void 0, function* () {
302
+ const provider = String(opts.provider || 'openai').toLowerCase();
303
+ const raw = opts.file ? fs_1.default.readFileSync(opts.file, 'utf8') : '';
304
+ const text = raw ? (0, second_opinion_providers_1.redactDiffForReview)(raw) : '';
305
+ if (!opts.file || !text.trim()) {
306
+ console.log(chalk_1.default.yellow('Pass --file <diff.txt>. Set OPENAI_API_KEY (openai) or ANTHROPIC_API_KEY (anthropic).'));
307
+ console.log(chalk_1.default.dim('Diff content is redacted for common secret patterns before sending.'));
308
+ return;
309
+ }
310
+ try {
311
+ let out;
312
+ if (provider === 'anthropic')
313
+ out = yield (0, second_opinion_providers_1.reviewWithAnthropic)(text);
314
+ else if (provider === 'openai')
315
+ out = yield (0, second_opinion_providers_1.reviewWithOpenAI)(text);
316
+ else {
317
+ console.error(chalk_1.default.red('Unknown --provider (use openai or anthropic)'));
318
+ process.exit(1);
319
+ }
320
+ console.log(out);
321
+ }
322
+ catch (e) {
323
+ console.error(chalk_1.default.red(e.message));
324
+ process.exit(1);
325
+ }
326
+ }));
327
+ program
328
+ .command('qa-visual')
329
+ .description('Hit cm-browse for screenshot + health (requires browse running)')
330
+ .requiredOption('--url <u>', 'page URL to navigate')
331
+ .option('--port <n>', 'browse daemon port (default: config browse.port or 17395)')
332
+ .option('--token <t>', 'or env CM_BROWSE_TOKEN or config browse.token')
333
+ .action((opts) => __awaiter(this, void 0, void 0, function* () {
334
+ var _a, _b, _c, _d;
335
+ const cfg = (0, cm_config_1.loadCmConfig)(process.cwd());
336
+ const token = opts.token || process.env.CM_BROWSE_TOKEN || ((_a = cfg.browse) === null || _a === void 0 ? void 0 : _a.token) || 'dev-token-change-me';
337
+ const port = parseInt(String((_d = (_b = opts.port) !== null && _b !== void 0 ? _b : (_c = cfg.browse) === null || _c === void 0 ? void 0 : _c.port) !== null && _d !== void 0 ? _d : 17395), 10);
338
+ const auth = `Bearer ${token}`;
339
+ yield browseRequest(port, '/session/start', 'POST', auth, { headless: true });
340
+ yield browseRequest(port, '/navigate', 'POST', auth, { url: opts.url });
341
+ yield browseRequest(port, '/refs/refresh', 'POST', auth, {});
342
+ const png = yield browseBuffer(port, '/screenshot', auth);
343
+ const out = path_1.default.join(process.cwd(), 'cm-qa-visual.png');
344
+ fs_1.default.writeFileSync(out, png);
345
+ console.log(chalk_1.default.green('Screenshot saved'), out);
346
+ }));
347
+ program
348
+ .command('canary')
349
+ .description('Post-deploy smoke: HTTP fetch + optional browse console; baseline compare')
350
+ .requiredOption('--url <u>', 'URL to fetch (http/https)')
351
+ .option('--browse-port <n>', 'if set, GET /console from browse (default: config canary.browse_port)')
352
+ .option('--token <t>', 'browse token (env CM_BROWSE_TOKEN or config)')
353
+ .option('--save-baseline', 'write .cm/canary-baseline.json after check')
354
+ .option('--compare-baseline', 'fail on HTTP regression or 2× latency vs baseline')
355
+ .action((opts) => __awaiter(this, void 0, void 0, function* () {
356
+ var _a, _b, _c, _d;
357
+ const root = process.cwd();
358
+ const cfg = (0, cm_config_1.loadCmConfig)(root);
359
+ const u = new URL(opts.url);
360
+ const { status, latency_ms } = yield httpProbeUrl(u.href);
361
+ if (status >= 400) {
362
+ console.error(chalk_1.default.red(`HTTP ${status}`), u.href);
363
+ process.exit(1);
364
+ }
365
+ if (opts.compareBaseline) {
366
+ const baselinePath = path_1.default.join(root, '.cm', 'canary-baseline.json');
367
+ if (!fs_1.default.existsSync(baselinePath)) {
368
+ console.error(chalk_1.default.red('No baseline file. Run once with --save-baseline'));
369
+ process.exit(1);
370
+ }
371
+ const prev = JSON.parse(fs_1.default.readFileSync(baselinePath, 'utf8'));
372
+ if (prev.http_status !== undefined &&
373
+ prev.http_status < 400 &&
374
+ status >= 400) {
375
+ console.error(chalk_1.default.red('HTTP regression vs baseline'));
376
+ process.exit(1);
377
+ }
378
+ if (typeof prev.latency_ms === 'number' &&
379
+ prev.latency_ms > 50 &&
380
+ latency_ms > prev.latency_ms * 2) {
381
+ console.error(chalk_1.default.red(`Latency regression: ${latency_ms}ms vs baseline ${prev.latency_ms}ms`));
382
+ process.exit(1);
383
+ }
384
+ console.log(chalk_1.default.dim('Baseline compare OK'));
385
+ }
386
+ if (opts.saveBaseline) {
387
+ const baselinePath = path_1.default.join(root, '.cm', 'canary-baseline.json');
388
+ fs_1.default.mkdirSync(path_1.default.dirname(baselinePath), { recursive: true });
389
+ fs_1.default.writeFileSync(baselinePath, JSON.stringify({
390
+ url: opts.url,
391
+ http_status: status,
392
+ latency_ms,
393
+ at: new Date().toISOString(),
394
+ }, null, 2), 'utf8');
395
+ console.log(chalk_1.default.dim('Wrote'), baselinePath);
396
+ }
397
+ console.log(chalk_1.default.green('HTTP OK'), u.href, chalk_1.default.dim(`${status} ${latency_ms}ms`));
398
+ const browsePort = (_a = opts.browsePort) !== null && _a !== void 0 ? _a : (((_b = cfg.canary) === null || _b === void 0 ? void 0 : _b.browse_port) != null ? String(cfg.canary.browse_port) : undefined);
399
+ if (browsePort) {
400
+ const token = opts.token ||
401
+ process.env.CM_BROWSE_TOKEN ||
402
+ ((_c = cfg.canary) === null || _c === void 0 ? void 0 : _c.token) ||
403
+ ((_d = cfg.browse) === null || _d === void 0 ? void 0 : _d.token) ||
404
+ 'dev-token-change-me';
405
+ const raw = yield browseRaw(parseInt(browsePort, 10), '/console', `Bearer ${token}`);
406
+ console.log(chalk_1.default.dim('Browse console (last messages):'), raw.slice(0, 500));
407
+ }
408
+ }));
409
+ const conductor = program.command('conductor').description('Git worktree helpers for parallel sprints');
410
+ conductor
411
+ .command('add')
412
+ .requiredOption('--at <dir>', 'new worktree directory')
413
+ .requiredOption('--branch <b>', 'branch name')
414
+ .option('--base <b>', 'start from branch', 'main')
415
+ .action((opts) => {
416
+ (0, child_process_1.execSync)(`git worktree add -b ${opts.branch} ${opts.at} ${opts.base}`, {
417
+ stdio: 'inherit',
418
+ cwd: process.cwd(),
419
+ });
420
+ console.log(chalk_1.default.green('Worktree created'));
421
+ });
422
+ conductor.command('list').action(() => {
423
+ (0, child_process_1.execSync)('git worktree list', { stdio: 'inherit', cwd: process.cwd() });
424
+ });
425
+ const retro = program
426
+ .command('retro')
427
+ .description('Append operational learning (.cm/operational-learnings.jsonl) or print summary');
428
+ retro
429
+ .command('summary')
430
+ .description('Aggregate JSONL by tool; optional --since filter')
431
+ .option('--project <dir>')
432
+ .option('--since <iso>', 'include entries on or after this ISO timestamp')
433
+ .option('--format <f>', 'json | md', 'md')
434
+ .action((opts) => {
435
+ const root = projectPath(opts.project);
436
+ const j = path_1.default.join(root, '.cm', 'operational-learnings.jsonl');
437
+ let entries = (0, retro_summary_1.loadRetroEntries)(j);
438
+ if (opts.since)
439
+ entries = (0, retro_summary_1.filterSince)(entries, opts.since);
440
+ const byTool = (0, retro_summary_1.countByTool)(entries);
441
+ const fmt = (opts.format || 'md').toLowerCase();
442
+ if (fmt === 'json')
443
+ console.log((0, retro_summary_1.formatRetroJson)(entries, byTool));
444
+ else
445
+ console.log((0, retro_summary_1.formatRetroMarkdown)(entries, byTool));
446
+ });
447
+ retro
448
+ .option('--note <text>', 'append entry')
449
+ .option('--tool <t>', 'tool label', 'cli')
450
+ .option('--project <dir>')
451
+ .option('--summary', 'print last 20 lines (legacy quick view)')
452
+ .action((opts) => {
453
+ const root = projectPath(opts.project);
454
+ const dir = path_1.default.join(root, '.cm');
455
+ if (!fs_1.default.existsSync(dir))
456
+ fs_1.default.mkdirSync(dir, { recursive: true });
457
+ const j = path_1.default.join(dir, 'operational-learnings.jsonl');
458
+ if (opts.summary) {
459
+ if (!fs_1.default.existsSync(j)) {
460
+ console.log(chalk_1.default.yellow('No entries yet'));
461
+ return;
462
+ }
463
+ const lines = fs_1.default.readFileSync(j, 'utf8').trim().split('\n').filter(Boolean).slice(-20);
464
+ for (const line of lines)
465
+ console.log(line);
466
+ return;
467
+ }
468
+ if (!opts.note) {
469
+ console.log(chalk_1.default.yellow('Pass --note "...", --summary, or: cm retro summary'));
470
+ return;
471
+ }
472
+ const rec = {
473
+ ts: new Date().toISOString(),
474
+ tool: opts.tool,
475
+ note: opts.note,
476
+ };
477
+ fs_1.default.appendFileSync(j, JSON.stringify(rec) + '\n', 'utf8');
478
+ console.log(chalk_1.default.green('Recorded'));
479
+ });
480
+ program
481
+ .command('suggest')
482
+ .description('Proactive skill hints from git status + sprint state')
483
+ .option('--project <dir>')
484
+ .action((opts) => {
485
+ const root = projectPath(opts.project);
486
+ const list = (0, cm_suggest_1.suggestFromContext)(root);
487
+ if (list.length === 0) {
488
+ console.log(chalk_1.default.yellow('No strong signals. Try cm-start or cm-planning for the next step.'));
489
+ return;
490
+ }
491
+ for (const s of list) {
492
+ console.log(chalk_1.default.cyan(s.skill));
493
+ console.log(chalk_1.default.dim(` ${s.reason}`));
494
+ }
495
+ });
496
+ const indexer = program.command('index').description('Project intelligence indexing');
497
+ indexer
498
+ .command('skills')
499
+ .description('Detect tech stack and build .cm/project-skills.md')
500
+ .option('--project <dir>')
501
+ .action((opts) => {
502
+ const root = projectPath(opts.project);
503
+ // Lazy load to avoid module compilation issues at boot if not used
504
+ const { generateProjectSkillsIndex } = require('../../indexer/skills');
505
+ const idx = generateProjectSkillsIndex(root);
506
+ const dotCm = path_1.default.join(root, '.cm');
507
+ if (!fs_1.default.existsSync(dotCm)) {
508
+ fs_1.default.mkdirSync(dotCm, { recursive: true });
509
+ }
510
+ const out = path_1.default.join(dotCm, 'project-skills.md');
511
+ const md = [
512
+ '# Local Project Skills Index',
513
+ '',
514
+ `Detected Technologies: **${idx.detectedTechnologies.join(', ') || 'None'}**`,
515
+ '',
516
+ '## Recommended Community Skills',
517
+ ...idx.recommendedSkills.map((s) => `- \`${s}\``),
518
+ '',
519
+ '> Autogenerated by `cm index skills`. Agents should run `npx skills add <skill>` if needed.'
520
+ ].join('\n');
521
+ fs_1.default.writeFileSync(out, md, 'utf-8');
522
+ console.log(chalk_1.default.green(`Indexed ${idx.detectedTechnologies.length} technologies and ${idx.recommendedSkills.length} skills to ${out}`));
523
+ });
524
+ }
525
+ function browseRequest(port, pathname, method, auth, body) {
526
+ return new Promise((resolve, reject) => {
527
+ const data = Buffer.from(JSON.stringify(body));
528
+ const req = http_1.default.request({
529
+ hostname: '127.0.0.1',
530
+ port,
531
+ path: pathname,
532
+ method,
533
+ headers: {
534
+ 'Content-Type': 'application/json',
535
+ 'Content-Length': data.length,
536
+ Authorization: auth,
537
+ },
538
+ }, (res) => {
539
+ res.resume();
540
+ if (res.statusCode && res.statusCode >= 400)
541
+ reject(new Error(`HTTP ${res.statusCode}`));
542
+ else
543
+ resolve();
544
+ });
545
+ req.on('error', reject);
546
+ req.write(data);
547
+ req.end();
548
+ });
549
+ }
550
+ function browseBuffer(port, pathname, auth) {
551
+ return new Promise((resolve, reject) => {
552
+ http_1.default.get({
553
+ hostname: '127.0.0.1',
554
+ port,
555
+ path: pathname,
556
+ headers: { Authorization: auth },
557
+ }, (res) => {
558
+ const chunks = [];
559
+ res.on('data', (c) => chunks.push(c));
560
+ res.on('end', () => {
561
+ if (res.statusCode && res.statusCode >= 400)
562
+ reject(new Error(`HTTP ${res.statusCode}`));
563
+ else
564
+ resolve(Buffer.concat(chunks));
565
+ });
566
+ }).on('error', reject);
567
+ });
568
+ }
569
+ function browseRaw(port, pathname, auth) {
570
+ return new Promise((resolve, reject) => {
571
+ http_1.default.get({
572
+ hostname: '127.0.0.1',
573
+ port,
574
+ path: pathname,
575
+ headers: { Authorization: auth },
576
+ }, (res) => {
577
+ let s = '';
578
+ res.on('data', (c) => (s += c));
579
+ res.on('end', () => {
580
+ if (res.statusCode && res.statusCode >= 400)
581
+ reject(new Error(`HTTP ${res.statusCode}`));
582
+ else
583
+ resolve(s);
584
+ });
585
+ }).on('error', reject);
586
+ });
587
+ }
588
+ function httpProbeUrl(url) {
589
+ return __awaiter(this, void 0, void 0, function* () {
590
+ const t0 = performance.now();
591
+ const res = yield fetch(url, { redirect: 'follow' });
592
+ yield res.arrayBuffer().catch(() => { });
593
+ const latency_ms = Math.round(performance.now() - t0);
594
+ return { status: res.status, latency_ms };
595
+ });
596
+ }