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
@@ -0,0 +1,488 @@
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
+ function projectPath(opt) {
29
+ return path_1.default.resolve(opt || process.cwd());
30
+ }
31
+ function registerEngineeringCommands(program) {
32
+ const browse = program.command('browse').description('Playwright browse daemon (local QA / screenshots)');
33
+ browse
34
+ .command('start')
35
+ .option('-p, --port <n>', 'port (default: .cm/config.yaml browse.port or 17395)')
36
+ .option('-H, --host <h>', 'bind host (default: config or 127.0.0.1)')
37
+ .option('--token <t>', 'bearer token (or env CM_BROWSE_TOKEN or config browse.token)')
38
+ .option('--headed', 'headed browser', false)
39
+ .action((opts) => __awaiter(this, void 0, void 0, function* () {
40
+ var _a, _b, _c, _d, _e, _f, _g;
41
+ const root = process.cwd();
42
+ const cfg = (0, cm_config_1.loadCmConfig)(root);
43
+ 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);
44
+ 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');
45
+ const token = opts.token ||
46
+ process.env.CM_BROWSE_TOKEN ||
47
+ ((_g = cfg.browse) === null || _g === void 0 ? void 0 : _g.token) ||
48
+ 'dev-token-change-me';
49
+ const daemon = new browse_server_1.BrowseDaemon({
50
+ host,
51
+ port,
52
+ token,
53
+ headless: !opts.headed,
54
+ });
55
+ yield daemon.listen();
56
+ console.log(chalk_1.default.green(`cm-browse listening http://${host}:${port}`));
57
+ console.log(chalk_1.default.dim(`Authorization: Bearer ${token.slice(0, 8)}…`));
58
+ console.log(chalk_1.default.dim('POST /session/start, /navigate, /refs/refresh, /click, /fill, GET /screenshot'));
59
+ process.on('SIGINT', () => __awaiter(this, void 0, void 0, function* () {
60
+ yield daemon.close();
61
+ process.exit(0);
62
+ }));
63
+ }));
64
+ const guardian = program.command('guardian').description('Runtime safety: destructive command patterns + path freeze');
65
+ guardian
66
+ .command('check')
67
+ .argument('<cmd...>', 'shell command to check')
68
+ .action((parts) => {
69
+ var _a;
70
+ const cmd = parts.join(' ');
71
+ const cfg = (0, cm_config_1.loadCmConfig)(process.cwd());
72
+ const extra = (_a = cfg.guardian) === null || _a === void 0 ? void 0 : _a.whitelist_prefixes;
73
+ const r = (0, guardian_core_1.checkShellCommand)(cmd, (extra === null || extra === void 0 ? void 0 : extra.length) ? { extraWhitelist: extra } : undefined);
74
+ if (!r.safe) {
75
+ console.error(chalk_1.default.red('BLOCKED:'), r.reason);
76
+ console.error(chalk_1.default.dim('Pattern:'), r.matchedPattern);
77
+ (0, guardian_core_1.appendGuardianLog)(process.cwd(), `BLOCKED: ${cmd}`);
78
+ process.exit(1);
79
+ }
80
+ console.log(chalk_1.default.green('OK'), chalk_1.default.dim(cmd));
81
+ });
82
+ guardian
83
+ .command('path-check')
84
+ .requiredOption('--file <f>', 'file path')
85
+ .option('--roots <r>', 'comma-separated roots (default: config guardian.freeze_roots or src,lib)')
86
+ .action((opts) => {
87
+ var _a, _b, _c;
88
+ const cwd = process.cwd();
89
+ const cfg = (0, cm_config_1.loadCmConfig)(cwd);
90
+ 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');
91
+ const roots = (0, guardian_core_1.normalizeRoots)(cwd, String(rootsCsv)
92
+ .split(',')
93
+ .map((s) => s.trim())
94
+ .filter(Boolean));
95
+ const ok = (0, guardian_core_1.isPathUnderRoots)(opts.file, roots);
96
+ if (!ok) {
97
+ console.error(chalk_1.default.red('Path outside freeze roots:'), opts.file);
98
+ (0, guardian_core_1.appendGuardianLog)(cwd, `FREEZE_VIOLATION: ${opts.file}`);
99
+ process.exit(1);
100
+ }
101
+ console.log(chalk_1.default.green('OK'), opts.file);
102
+ });
103
+ const sprint = program.command('sprint').description('Opinionated pipeline + .cm/sprint Context Bus');
104
+ sprint
105
+ .command('init')
106
+ .option('--project <dir>')
107
+ .option('--from <step>', `one of: ${sprint_pipeline_1.SPRINT_STEPS.join(',')}`)
108
+ .action((opts) => {
109
+ const root = projectPath(opts.project);
110
+ const from = opts.from;
111
+ if (from && !sprint_pipeline_1.SPRINT_STEPS.includes(from)) {
112
+ console.error(chalk_1.default.red('Invalid --from'));
113
+ process.exit(1);
114
+ }
115
+ const state = (0, sprint_pipeline_1.initSprint)(root, from);
116
+ console.log(chalk_1.default.green('Sprint initialized'));
117
+ console.log(chalk_1.default.dim(JSON.stringify(state, null, 2)));
118
+ });
119
+ sprint
120
+ .command('status')
121
+ .option('--project <dir>')
122
+ .action((opts) => {
123
+ const root = projectPath(opts.project);
124
+ const state = (0, sprint_pipeline_1.readSprintState)(root);
125
+ if (!state) {
126
+ console.log(chalk_1.default.yellow('No sprint state. Run: cm sprint init'));
127
+ return;
128
+ }
129
+ const next = state.current_index >= state.pipeline.length
130
+ ? '(done)'
131
+ : state.pipeline[state.current_index];
132
+ console.log(chalk_1.default.cyan('Current step:'), next);
133
+ if (typeof next === 'string' && next !== '(done)')
134
+ console.log(chalk_1.default.dim('Skill hint:'), (0, sprint_pipeline_1.skillMappingForStep)(next));
135
+ console.log(chalk_1.default.dim('Completed:'), state.completed.join(', ') || '(none)');
136
+ console.log(chalk_1.default.dim('Skipped:'), state.skipped.join(', ') || '(none)');
137
+ });
138
+ sprint
139
+ .command('complete')
140
+ .argument('<step>', 'step name')
141
+ .option('--project <dir>')
142
+ .option('-m, --message <text>', 'artifact markdown body', '')
143
+ .action((step, opts) => {
144
+ const root = projectPath(opts.project);
145
+ if (!sprint_pipeline_1.SPRINT_STEPS.includes(step)) {
146
+ console.error(chalk_1.default.red('Invalid step'));
147
+ process.exit(1);
148
+ }
149
+ const body = opts.message ||
150
+ `# ${step}\n\n_Completed via \`cm sprint complete\` — replace with real notes._\n`;
151
+ try {
152
+ const state = (0, sprint_pipeline_1.completeSprintStep)(root, step, body);
153
+ console.log(chalk_1.default.green('Step recorded:', step));
154
+ console.log(chalk_1.default.dim('Next index:', state.current_index));
155
+ }
156
+ catch (e) {
157
+ console.error(chalk_1.default.red(e.message));
158
+ process.exit(1);
159
+ }
160
+ });
161
+ sprint
162
+ .command('skip')
163
+ .argument('[step]', 'step name (default: current step)')
164
+ .option('--project <dir>')
165
+ .action((step, opts) => {
166
+ const root = projectPath(opts.project);
167
+ const state = (0, sprint_pipeline_1.readSprintState)(root);
168
+ if (!state) {
169
+ console.error(chalk_1.default.red('No sprint state. Run: cm sprint init'));
170
+ process.exit(1);
171
+ }
172
+ if (state.current_index >= state.pipeline.length) {
173
+ console.error(chalk_1.default.red('Sprint pipeline already finished'));
174
+ process.exit(1);
175
+ }
176
+ const current = state.pipeline[state.current_index];
177
+ const target = (step || current);
178
+ if (step && !sprint_pipeline_1.SPRINT_STEPS.includes(target)) {
179
+ console.error(chalk_1.default.red('Invalid step'));
180
+ process.exit(1);
181
+ }
182
+ try {
183
+ const next = (0, sprint_pipeline_1.skipSprintStep)(root, target);
184
+ console.log(chalk_1.default.green('Skipped step:', target));
185
+ console.log(chalk_1.default.dim('Next index:', next.current_index));
186
+ }
187
+ catch (e) {
188
+ console.error(chalk_1.default.red(e.message));
189
+ process.exit(1);
190
+ }
191
+ });
192
+ sprint
193
+ .command('reset')
194
+ .option('--project <dir>')
195
+ .option('--no-backup', 'do not copy sprint files to .cm/sprint/backup before clearing')
196
+ .action((opts) => {
197
+ const root = projectPath(opts.project);
198
+ const r = (0, sprint_pipeline_1.resetSprint)(root, { backup: opts.backup !== false });
199
+ if (!r.ok) {
200
+ console.log(chalk_1.default.yellow('Nothing to reset (no sprint data under .cm/sprint).'));
201
+ return;
202
+ }
203
+ if (r.backupDir)
204
+ console.log(chalk_1.default.dim('Backup:'), r.backupDir);
205
+ console.log(chalk_1.default.green('Sprint data cleared. Run: cm sprint init'));
206
+ });
207
+ sprint
208
+ .command('dry-run')
209
+ .option('--project <dir>')
210
+ .action((opts) => {
211
+ const root = projectPath(opts.project);
212
+ const d = (0, sprint_pipeline_1.sprintDryRun)(root);
213
+ console.log(chalk_1.default.cyan('Steps:'), d.steps.join(' → '));
214
+ d.artifacts.forEach((a) => console.log(chalk_1.default.dim(' -'), a));
215
+ });
216
+ program
217
+ .command('second-opinion')
218
+ .description('Send unified diff to a secondary model (redacts obvious secrets)')
219
+ .option('--file <f>', 'file containing diff text')
220
+ .option('--provider <p>', 'openai | anthropic', 'openai')
221
+ .action((opts) => __awaiter(this, void 0, void 0, function* () {
222
+ const provider = String(opts.provider || 'openai').toLowerCase();
223
+ const raw = opts.file ? fs_1.default.readFileSync(opts.file, 'utf8') : '';
224
+ const text = raw ? (0, second_opinion_providers_1.redactDiffForReview)(raw) : '';
225
+ if (!opts.file || !text.trim()) {
226
+ console.log(chalk_1.default.yellow('Pass --file <diff.txt>. Set OPENAI_API_KEY (openai) or ANTHROPIC_API_KEY (anthropic).'));
227
+ console.log(chalk_1.default.dim('Diff content is redacted for common secret patterns before sending.'));
228
+ return;
229
+ }
230
+ try {
231
+ let out;
232
+ if (provider === 'anthropic')
233
+ out = yield (0, second_opinion_providers_1.reviewWithAnthropic)(text);
234
+ else if (provider === 'openai')
235
+ out = yield (0, second_opinion_providers_1.reviewWithOpenAI)(text);
236
+ else {
237
+ console.error(chalk_1.default.red('Unknown --provider (use openai or anthropic)'));
238
+ process.exit(1);
239
+ }
240
+ console.log(out);
241
+ }
242
+ catch (e) {
243
+ console.error(chalk_1.default.red(e.message));
244
+ process.exit(1);
245
+ }
246
+ }));
247
+ program
248
+ .command('qa-visual')
249
+ .description('Hit cm-browse for screenshot + health (requires browse running)')
250
+ .requiredOption('--url <u>', 'page URL to navigate')
251
+ .option('--port <n>', 'browse daemon port (default: config browse.port or 17395)')
252
+ .option('--token <t>', 'or env CM_BROWSE_TOKEN or config browse.token')
253
+ .action((opts) => __awaiter(this, void 0, void 0, function* () {
254
+ var _a, _b, _c, _d;
255
+ const cfg = (0, cm_config_1.loadCmConfig)(process.cwd());
256
+ const token = opts.token || process.env.CM_BROWSE_TOKEN || ((_a = cfg.browse) === null || _a === void 0 ? void 0 : _a.token) || 'dev-token-change-me';
257
+ 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);
258
+ const auth = `Bearer ${token}`;
259
+ yield browseRequest(port, '/session/start', 'POST', auth, { headless: true });
260
+ yield browseRequest(port, '/navigate', 'POST', auth, { url: opts.url });
261
+ yield browseRequest(port, '/refs/refresh', 'POST', auth, {});
262
+ const png = yield browseBuffer(port, '/screenshot', auth);
263
+ const out = path_1.default.join(process.cwd(), 'cm-qa-visual.png');
264
+ fs_1.default.writeFileSync(out, png);
265
+ console.log(chalk_1.default.green('Screenshot saved'), out);
266
+ }));
267
+ program
268
+ .command('canary')
269
+ .description('Post-deploy smoke: HTTP fetch + optional browse console; baseline compare')
270
+ .requiredOption('--url <u>', 'URL to fetch (http/https)')
271
+ .option('--browse-port <n>', 'if set, GET /console from browse (default: config canary.browse_port)')
272
+ .option('--token <t>', 'browse token (env CM_BROWSE_TOKEN or config)')
273
+ .option('--save-baseline', 'write .cm/canary-baseline.json after check')
274
+ .option('--compare-baseline', 'fail on HTTP regression or 2× latency vs baseline')
275
+ .action((opts) => __awaiter(this, void 0, void 0, function* () {
276
+ var _a, _b, _c, _d;
277
+ const root = process.cwd();
278
+ const cfg = (0, cm_config_1.loadCmConfig)(root);
279
+ const u = new URL(opts.url);
280
+ const { status, latency_ms } = yield httpProbeUrl(u.href);
281
+ if (status >= 400) {
282
+ console.error(chalk_1.default.red(`HTTP ${status}`), u.href);
283
+ process.exit(1);
284
+ }
285
+ if (opts.compareBaseline) {
286
+ const baselinePath = path_1.default.join(root, '.cm', 'canary-baseline.json');
287
+ if (!fs_1.default.existsSync(baselinePath)) {
288
+ console.error(chalk_1.default.red('No baseline file. Run once with --save-baseline'));
289
+ process.exit(1);
290
+ }
291
+ const prev = JSON.parse(fs_1.default.readFileSync(baselinePath, 'utf8'));
292
+ if (prev.http_status !== undefined &&
293
+ prev.http_status < 400 &&
294
+ status >= 400) {
295
+ console.error(chalk_1.default.red('HTTP regression vs baseline'));
296
+ process.exit(1);
297
+ }
298
+ if (typeof prev.latency_ms === 'number' &&
299
+ prev.latency_ms > 50 &&
300
+ latency_ms > prev.latency_ms * 2) {
301
+ console.error(chalk_1.default.red(`Latency regression: ${latency_ms}ms vs baseline ${prev.latency_ms}ms`));
302
+ process.exit(1);
303
+ }
304
+ console.log(chalk_1.default.dim('Baseline compare OK'));
305
+ }
306
+ if (opts.saveBaseline) {
307
+ const baselinePath = path_1.default.join(root, '.cm', 'canary-baseline.json');
308
+ fs_1.default.mkdirSync(path_1.default.dirname(baselinePath), { recursive: true });
309
+ fs_1.default.writeFileSync(baselinePath, JSON.stringify({
310
+ url: opts.url,
311
+ http_status: status,
312
+ latency_ms,
313
+ at: new Date().toISOString(),
314
+ }, null, 2), 'utf8');
315
+ console.log(chalk_1.default.dim('Wrote'), baselinePath);
316
+ }
317
+ console.log(chalk_1.default.green('HTTP OK'), u.href, chalk_1.default.dim(`${status} ${latency_ms}ms`));
318
+ 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);
319
+ if (browsePort) {
320
+ const token = opts.token ||
321
+ process.env.CM_BROWSE_TOKEN ||
322
+ ((_c = cfg.canary) === null || _c === void 0 ? void 0 : _c.token) ||
323
+ ((_d = cfg.browse) === null || _d === void 0 ? void 0 : _d.token) ||
324
+ 'dev-token-change-me';
325
+ const raw = yield browseRaw(parseInt(browsePort, 10), '/console', `Bearer ${token}`);
326
+ console.log(chalk_1.default.dim('Browse console (last messages):'), raw.slice(0, 500));
327
+ }
328
+ }));
329
+ const conductor = program.command('conductor').description('Git worktree helpers for parallel sprints');
330
+ conductor
331
+ .command('add')
332
+ .requiredOption('--at <dir>', 'new worktree directory')
333
+ .requiredOption('--branch <b>', 'branch name')
334
+ .option('--base <b>', 'start from branch', 'main')
335
+ .action((opts) => {
336
+ (0, child_process_1.execSync)(`git worktree add -b ${opts.branch} ${opts.at} ${opts.base}`, {
337
+ stdio: 'inherit',
338
+ cwd: process.cwd(),
339
+ });
340
+ console.log(chalk_1.default.green('Worktree created'));
341
+ });
342
+ conductor.command('list').action(() => {
343
+ (0, child_process_1.execSync)('git worktree list', { stdio: 'inherit', cwd: process.cwd() });
344
+ });
345
+ const retro = program
346
+ .command('retro')
347
+ .description('Append operational learning (.cm/operational-learnings.jsonl) or print summary');
348
+ retro
349
+ .command('summary')
350
+ .description('Aggregate JSONL by tool; optional --since filter')
351
+ .option('--project <dir>')
352
+ .option('--since <iso>', 'include entries on or after this ISO timestamp')
353
+ .option('--format <f>', 'json | md', 'md')
354
+ .action((opts) => {
355
+ const root = projectPath(opts.project);
356
+ const j = path_1.default.join(root, '.cm', 'operational-learnings.jsonl');
357
+ let entries = (0, retro_summary_1.loadRetroEntries)(j);
358
+ if (opts.since)
359
+ entries = (0, retro_summary_1.filterSince)(entries, opts.since);
360
+ const byTool = (0, retro_summary_1.countByTool)(entries);
361
+ const fmt = (opts.format || 'md').toLowerCase();
362
+ if (fmt === 'json')
363
+ console.log((0, retro_summary_1.formatRetroJson)(entries, byTool));
364
+ else
365
+ console.log((0, retro_summary_1.formatRetroMarkdown)(entries, byTool));
366
+ });
367
+ retro
368
+ .option('--note <text>', 'append entry')
369
+ .option('--tool <t>', 'tool label', 'cli')
370
+ .option('--project <dir>')
371
+ .option('--summary', 'print last 20 lines (legacy quick view)')
372
+ .action((opts) => {
373
+ const root = projectPath(opts.project);
374
+ const dir = path_1.default.join(root, '.cm');
375
+ if (!fs_1.default.existsSync(dir))
376
+ fs_1.default.mkdirSync(dir, { recursive: true });
377
+ const j = path_1.default.join(dir, 'operational-learnings.jsonl');
378
+ if (opts.summary) {
379
+ if (!fs_1.default.existsSync(j)) {
380
+ console.log(chalk_1.default.yellow('No entries yet'));
381
+ return;
382
+ }
383
+ const lines = fs_1.default.readFileSync(j, 'utf8').trim().split('\n').filter(Boolean).slice(-20);
384
+ for (const line of lines)
385
+ console.log(line);
386
+ return;
387
+ }
388
+ if (!opts.note) {
389
+ console.log(chalk_1.default.yellow('Pass --note "...", --summary, or: cm retro summary'));
390
+ return;
391
+ }
392
+ const rec = {
393
+ ts: new Date().toISOString(),
394
+ tool: opts.tool,
395
+ note: opts.note,
396
+ };
397
+ fs_1.default.appendFileSync(j, JSON.stringify(rec) + '\n', 'utf8');
398
+ console.log(chalk_1.default.green('Recorded'));
399
+ });
400
+ program
401
+ .command('suggest')
402
+ .description('Proactive skill hints from git status + sprint state')
403
+ .option('--project <dir>')
404
+ .action((opts) => {
405
+ const root = projectPath(opts.project);
406
+ const list = (0, cm_suggest_1.suggestFromContext)(root);
407
+ if (list.length === 0) {
408
+ console.log(chalk_1.default.yellow('No strong signals. Try cm-start or cm-planning for the next step.'));
409
+ return;
410
+ }
411
+ for (const s of list) {
412
+ console.log(chalk_1.default.cyan(s.skill));
413
+ console.log(chalk_1.default.dim(` ${s.reason}`));
414
+ }
415
+ });
416
+ }
417
+ function browseRequest(port, pathname, method, auth, body) {
418
+ return new Promise((resolve, reject) => {
419
+ const data = Buffer.from(JSON.stringify(body));
420
+ const req = http_1.default.request({
421
+ hostname: '127.0.0.1',
422
+ port,
423
+ path: pathname,
424
+ method,
425
+ headers: {
426
+ 'Content-Type': 'application/json',
427
+ 'Content-Length': data.length,
428
+ Authorization: auth,
429
+ },
430
+ }, (res) => {
431
+ res.resume();
432
+ if (res.statusCode && res.statusCode >= 400)
433
+ reject(new Error(`HTTP ${res.statusCode}`));
434
+ else
435
+ resolve();
436
+ });
437
+ req.on('error', reject);
438
+ req.write(data);
439
+ req.end();
440
+ });
441
+ }
442
+ function browseBuffer(port, pathname, auth) {
443
+ return new Promise((resolve, reject) => {
444
+ http_1.default.get({
445
+ hostname: '127.0.0.1',
446
+ port,
447
+ path: pathname,
448
+ headers: { Authorization: auth },
449
+ }, (res) => {
450
+ const chunks = [];
451
+ res.on('data', (c) => chunks.push(c));
452
+ res.on('end', () => {
453
+ if (res.statusCode && res.statusCode >= 400)
454
+ reject(new Error(`HTTP ${res.statusCode}`));
455
+ else
456
+ resolve(Buffer.concat(chunks));
457
+ });
458
+ }).on('error', reject);
459
+ });
460
+ }
461
+ function browseRaw(port, pathname, auth) {
462
+ return new Promise((resolve, reject) => {
463
+ http_1.default.get({
464
+ hostname: '127.0.0.1',
465
+ port,
466
+ path: pathname,
467
+ headers: { Authorization: auth },
468
+ }, (res) => {
469
+ let s = '';
470
+ res.on('data', (c) => (s += c));
471
+ res.on('end', () => {
472
+ if (res.statusCode && res.statusCode >= 400)
473
+ reject(new Error(`HTTP ${res.statusCode}`));
474
+ else
475
+ resolve(s);
476
+ });
477
+ }).on('error', reject);
478
+ });
479
+ }
480
+ function httpProbeUrl(url) {
481
+ return __awaiter(this, void 0, void 0, function* () {
482
+ const t0 = performance.now();
483
+ const res = yield fetch(url, { redirect: 'follow' });
484
+ yield res.arrayBuffer().catch(() => { });
485
+ const latency_ms = Math.round(performance.now() - t0);
486
+ return { status: res.status, latency_ms };
487
+ });
488
+ }