codymaster 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/README.md +285 -0
- package/adapters/antigravity.js +15 -0
- package/adapters/claude-code.js +17 -0
- package/adapters/cursor.js +16 -0
- package/commands/bootstrap.md +49 -0
- package/commands/build.md +48 -0
- package/commands/content.md +48 -0
- package/commands/continuity.md +60 -0
- package/commands/debug.md +51 -0
- package/commands/demo.md +96 -0
- package/commands/deploy.md +51 -0
- package/commands/plan.md +42 -0
- package/commands/review.md +55 -0
- package/commands/track.md +46 -0
- package/commands/ux.md +46 -0
- package/dist/agent-dispatch.js +161 -0
- package/dist/chains/builtin.js +85 -0
- package/dist/continuity.js +385 -0
- package/dist/dashboard.js +926 -0
- package/dist/data.js +122 -0
- package/dist/index.js +2434 -0
- package/dist/judge.js +252 -0
- package/dist/parallel-dispatch.js +359 -0
- package/dist/parallel-quality.js +172 -0
- package/dist/skill-chain.js +258 -0
- package/install.sh +513 -0
- package/package.json +79 -0
- package/skills/.content-factory-state.json +132 -0
- package/skills/.git 2/logs/refs/heads/main +1 -0
- package/skills/.git 2/logs/refs/remotes/origin/main +1 -0
- package/skills/.git 2/objects/02/fb0956734b5f8ba3f918b7defd04a89cfe0076 +0 -0
- package/skills/.git 2/objects/08/1e129d75dc6feac6c02037272e6bd1a04e3324 +0 -0
- package/skills/.git 2/objects/0c/5393416f3c5e01c9a655a802bff0dd52f76f0a +0 -0
- package/skills/.git 2/objects/10/0b9be46978a946a77188f68be725098a122001 +0 -0
- package/skills/.git 2/objects/10/cf041167fc9843610eb3d90259ef3396315fdc +0 -0
- package/skills/.git 2/objects/12/5e19538dd6e1338ffe74f6c4c165b00435bf48 +0 -0
- package/skills/.git 2/objects/16/a9b9d0088d5c1347628b45a2620b479d8ad57c +0 -0
- package/skills/.git 2/objects/17/8c2a9ef93c33ae4eec9d58e82321f9229843a1 +0 -0
- package/skills/.git 2/objects/25/397ae41d09104d763bdcac2695209d85cdea89 +0 -0
- package/skills/.git 2/objects/2f/a836b7947f2d458e1f639788bf4bb0983a3305 +0 -0
- package/skills/.git 2/objects/3a/baaaf0a1c0909c0828335791557125fba911e0 +0 -0
- package/skills/.git 2/objects/42/2924221b81f5ce3c4e4daac9a64a24f9b01f9a +0 -0
- package/skills/.git 2/objects/42/ec0ce707447dc11446a34c9995fb8533801731 +0 -0
- package/skills/.git 2/objects/46/e43ce92866d56ce74b1d750db307cfe6154a15 +0 -0
- package/skills/.git 2/objects/48/5e41b633c63f55b8277bcc59f44f67681f671a +0 -0
- package/skills/.git 2/objects/49/49c596a3a89fa240642acd95dd3258e261eb09 +0 -0
- package/skills/.git 2/objects/50/9d42d8412ef8eaf7f7e138476bac2e4d10ce60 +0 -0
- package/skills/.git 2/objects/55/0c8c389d981b463ef849aeb792d8be3ccb6ec8 +0 -0
- package/skills/.git 2/objects/5d/82d3b18410cdda3ace3677436f0cb599dbe2d2 +0 -0
- package/skills/.git 2/objects/60/0617c58e871a38b33bf29e282d132bb3c381ad +0 -0
- package/skills/.git 2/objects/6a/8369a99c687b7245c92ffaf0e0f0dab9014504 +0 -0
- package/skills/.git 2/objects/79/bea435d40ab531c1aaf6be0432c6a5b7aaed21 +0 -0
- package/skills/.git 2/objects/7e/5ebd79251c2f14e4aceb86c74b6b6daae6b500 +0 -0
- package/skills/.git 2/objects/81/98a822a60178d6d5023ddb3e222cddf048742e +0 -0
- package/skills/.git 2/objects/86/0a0e1943dfe53411d2e499a1f16f46a96ef758 +0 -0
- package/skills/.git 2/objects/86/971fb55fdc081fdbae52376f0f13e57a4e9b04 +0 -0
- package/skills/.git 2/objects/88/b89dd609a0a03f8d4fe8bfde20d5b8fc1d326d +0 -0
- package/skills/.git 2/objects/90/8737edb6b7809e32cc01590b4e08ba42a9d40d +0 -0
- package/skills/.git 2/objects/93/d5a8a9a7d4fb7f11491cb596a6880528725118 +0 -0
- package/skills/.git 2/objects/98/46a2ab81d0c3b3eb00ef88fc56989aa7e9f316 +0 -0
- package/skills/.git 2/objects/9b/d8dd1e49cf274eaf9c555f3ab39dce7af5715e +0 -0
- package/skills/.git 2/objects/a1/13329fb0cec96ae78b222d33a24c3b5bc7fa1f +0 -0
- package/skills/.git 2/objects/a9/e6effe626e8a3aea3a8fc3364b492191c6e7d0 +0 -0
- package/skills/.git 2/objects/ad/6de7e48d9782cca9353d1ff0aa1aab7fe1df85 +0 -0
- package/skills/.git 2/objects/af/54ae316f771ff692e299ffcd8bf2f06b413b59 +0 -0
- package/skills/.git 2/objects/b0/4cb8b0b00dad633e731c1472161419e738d674 +0 -0
- package/skills/.git 2/objects/b3/094abb0b9ed46419b269e4a4e36a459690e3b0 +0 -0
- package/skills/.git 2/objects/b9/435c5d4baac2cfc5c83009ddd27b46b60db5f1 +0 -0
- package/skills/.git 2/objects/ba/5da17dbaec5ec2dcfdfd126aead518d1171d5c +0 -0
- package/skills/.git 2/objects/c0/bf58703aa258ba5dd63083bebaec8f223d844c +0 -0
- package/skills/.git 2/objects/c4/701a34edf1fc1bad58ccc57bd03f9426acb59a +0 -0
- package/skills/.git 2/objects/c7/5ccce9a4e5cc74d9b3174550cf6d993ca43638 +0 -0
- package/skills/.git 2/objects/c7/710d59b5a35b0f1f0a0399386643a0bd94c929 +0 -0
- package/skills/.git 2/objects/d1/fe58237112e953e5fec52da22cf38e08be3df9 +5 -0
- package/skills/.git 2/objects/d2/2bbe9fd2f74c95bc5583e803f5e435f1e2cd86 +0 -0
- package/skills/.git 2/objects/d7/e72852ea2bff74581dbf247d400120086229f4 +0 -0
- package/skills/.git 2/objects/d8/d4c3b5553e4fd72807e1d4b49ef07d9ef3ac35 +0 -0
- package/skills/.git 2/objects/dc/75050c2876f6a02ae2a53a3c886f395b622977 +0 -0
- package/skills/.git 2/objects/ee/e8546f95acec500187c08a28a8b9ee02db0dec +0 -0
- package/skills/.git 2/objects/ef/263c059208b416c2146434f10cb2b9fabcba16 +0 -0
- package/skills/.git 2/objects/f3/ae597e84d9a59b88acd21c99bde2eaf686d785 +0 -0
- package/skills/.git 2/objects/f3/f6f5673c821d3d8e76fa267a9e882e7a5387ea +0 -0
- package/skills/.git 2/objects/f9/6e6d0ad02624dd11d5848594d056caef7a5e8b +0 -0
- package/skills/.git 2/objects/ff/278988fc1edf0db3abcf18de795f4cc0b4f3e1 +0 -0
- package/skills/.git 2/refs/heads/main +1 -0
- package/skills/.git 2/refs/remotes/origin/main +1 -0
- package/skills/.pytest_cache 2/v/cache/nodeids +76 -0
- package/skills/.pytest_cache 2/v/cache/stepwise +1 -0
- package/skills/_shared/helpers.md +123 -0
- package/skills/_shared/outputs-convention.md +24 -0
- package/skills/cm-ads-tracker/SKILL.md +109 -0
- package/skills/cm-ads-tracker/evals/evals.json +55 -0
- package/skills/cm-ads-tracker/references/gtm-architecture.md +321 -0
- package/skills/cm-ads-tracker/references/industry-events.md +294 -0
- package/skills/cm-ads-tracker/references/platforms-api.md +238 -0
- package/skills/cm-ads-tracker/templates/capi-payload.md +79 -0
- package/skills/cm-ads-tracker/templates/datalayer-push.js +104 -0
- package/skills/cm-ads-tracker/templates/gtm-variables.js +56 -0
- package/skills/cm-brainstorm-idea/SKILL.md +423 -0
- package/skills/cm-code-review/SKILL.md +151 -0
- package/skills/cm-content-factory/SKILL.md +416 -0
- package/skills/cm-continuity/SKILL.md +399 -0
- package/skills/cm-dashboard/SKILL.md +533 -0
- package/skills/cm-dashboard/ui/app.js +1270 -0
- package/skills/cm-dashboard/ui/index.html +206 -0
- package/skills/cm-dashboard/ui/style.css +440 -0
- package/skills/cm-debugging/SKILL.md +412 -0
- package/skills/cm-deep-search/SKILL.md +242 -0
- package/skills/cm-design-system/SKILL.md +97 -0
- package/skills/cm-design-system/resources/halo-modern.md +40 -0
- package/skills/cm-design-system/resources/lunaris-advanced.md +40 -0
- package/skills/cm-design-system/resources/nitro-enterprise.md +39 -0
- package/skills/cm-design-system/resources/shadcn-default.md +37 -0
- package/skills/cm-dockit/README.md +100 -0
- package/skills/cm-dockit/SKILL.md +302 -0
- package/skills/cm-dockit/index.html +443 -0
- package/skills/cm-dockit/package-lock.json +1850 -0
- package/skills/cm-dockit/package.json +14 -0
- package/skills/cm-dockit/prompts/analysis.md +34 -0
- package/skills/cm-dockit/prompts/api-reference.md +24 -0
- package/skills/cm-dockit/prompts/architecture.md +21 -0
- package/skills/cm-dockit/prompts/data-flow.md +20 -0
- package/skills/cm-dockit/prompts/database.md +21 -0
- package/skills/cm-dockit/prompts/deployment.md +22 -0
- package/skills/cm-dockit/prompts/flows.md +21 -0
- package/skills/cm-dockit/prompts/jtbd.md +20 -0
- package/skills/cm-dockit/prompts/personas.md +24 -0
- package/skills/cm-dockit/prompts/sop-modules.md +40 -0
- package/skills/cm-dockit/scripts/doc-gen.sh +121 -0
- package/skills/cm-dockit/scripts/dockit-dashboard.sh +142 -0
- package/skills/cm-dockit/scripts/dockit-runner.sh +607 -0
- package/skills/cm-dockit/scripts/dockit-task.sh +166 -0
- package/skills/cm-dockit/skills/analyze-codebase.md +174 -0
- package/skills/cm-dockit/skills/api-reference.md +237 -0
- package/skills/cm-dockit/skills/changelog-guide.md +195 -0
- package/skills/cm-dockit/skills/content-guidelines.md +190 -0
- package/skills/cm-dockit/skills/sop-guide.md +184 -0
- package/skills/cm-dockit/skills/tech-docs.md +287 -0
- package/skills/cm-dockit/templates/markdown/structure.md +60 -0
- package/skills/cm-dockit/templates/vitepress-premium/.vitepress/config.mts +110 -0
- package/skills/cm-dockit/templates/vitepress-premium/.vitepress/theme/custom.css +189 -0
- package/skills/cm-dockit/templates/vitepress-premium/.vitepress/theme/index.ts +4 -0
- package/skills/cm-dockit/templates/vitepress-premium/package.json +19 -0
- package/skills/cm-dockit/templates/vitepress-premium/tests/frontend.test.ts +45 -0
- package/skills/cm-dockit/tests/runner.test.ts +66 -0
- package/skills/cm-dockit/workflows/export-markdown.md +82 -0
- package/skills/cm-dockit/workflows/generate-docs.md +68 -0
- package/skills/cm-dockit/workflows/setup-vitepress.md +181 -0
- package/skills/cm-example/SKILL.md +26 -0
- package/skills/cm-execution/SKILL.md +268 -0
- package/skills/cm-git-worktrees/SKILL.md +164 -0
- package/skills/cm-how-it-work/SKILL.md +189 -0
- package/skills/cm-identity-guard/SKILL.md +412 -0
- package/skills/cm-jtbd/SKILL.md +98 -0
- package/skills/cm-planning/SKILL.md +130 -0
- package/skills/cm-project-bootstrap/SKILL.md +161 -0
- package/skills/cm-project-bootstrap/templates/AGENTS.md +42 -0
- package/skills/cm-project-bootstrap/templates/frontend-safety.test.js +51 -0
- package/skills/cm-project-bootstrap/templates/i18n-sync.test.js +38 -0
- package/skills/cm-project-bootstrap/templates/pr-template.md +12 -0
- package/skills/cm-project-bootstrap/templates/project-identity.json +29 -0
- package/skills/cm-project-bootstrap/templates/vitest.config.js +10 -0
- package/skills/cm-quality-gate/SKILL.md +218 -0
- package/skills/cm-readit/SKILL.md +289 -0
- package/skills/cm-readit/audio-player.md +206 -0
- package/skills/cm-readit/examples/blog-reader.js +352 -0
- package/skills/cm-readit/examples/voice-cro.js +390 -0
- package/skills/cm-readit/tts-engine.md +262 -0
- package/skills/cm-readit/ui-patterns.md +362 -0
- package/skills/cm-readit/voice-cro.md +223 -0
- package/skills/cm-safe-deploy/SKILL.md +120 -0
- package/skills/cm-safe-deploy/templates/deploy.sh +89 -0
- package/skills/cm-safe-i18n/SKILL.md +473 -0
- package/skills/cm-secret-shield/SKILL.md +580 -0
- package/skills/cm-skill-chain/SKILL.md +78 -0
- package/skills/cm-skill-index/SKILL.md +318 -0
- package/skills/cm-skill-mastery/SKILL.md +169 -0
- package/skills/cm-start/SKILL.md +65 -0
- package/skills/cm-status/SKILL.md +12 -0
- package/skills/cm-tdd/SKILL.md +370 -0
- package/skills/cm-terminal/SKILL.md +177 -0
- package/skills/cm-test-gate/SKILL.md +242 -0
- package/skills/cm-ui-preview/SKILL.md +291 -0
- package/skills/cm-ux-master/DESIGN_STANDARD_TEMPLATE.md +54 -0
- package/skills/cm-ux-master/SKILL.md +114 -0
- package/skills/cro-methodology/SKILL.md +98 -0
- package/skills/cro-methodology/references/COPYWRITING.md +178 -0
- package/skills/cro-methodology/references/OBJECTIONS.md +135 -0
- package/skills/cro-methodology/references/PERSUASION.md +158 -0
- package/skills/cro-methodology/references/RESEARCH.md +220 -0
- package/skills/cro-methodology/references/funnel-analysis.md +365 -0
- package/skills/cro-methodology/references/testing-methodology.md +330 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2434 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
37
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
38
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
39
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
40
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
41
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
42
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
46
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
47
|
+
};
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
const commander_1 = require("commander");
|
|
50
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
51
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
52
|
+
const fs_1 = __importDefault(require("fs"));
|
|
53
|
+
const data_1 = require("./data");
|
|
54
|
+
const dashboard_1 = require("./dashboard");
|
|
55
|
+
const agent_dispatch_1 = require("./agent-dispatch");
|
|
56
|
+
const continuity_1 = require("./continuity");
|
|
57
|
+
const judge_1 = require("./judge");
|
|
58
|
+
const skill_chain_1 = require("./skill-chain");
|
|
59
|
+
const path_1 = __importDefault(require("path"));
|
|
60
|
+
const os_1 = __importDefault(require("os"));
|
|
61
|
+
const https_1 = __importDefault(require("https"));
|
|
62
|
+
const VERSION = '3.4.0';
|
|
63
|
+
// ─── Branding ───────────────────────────────────────────────────────────────
|
|
64
|
+
function showBanner() {
|
|
65
|
+
const cPath = process.cwd().replace(os_1.default.homedir(), '~');
|
|
66
|
+
const bg = chalk_1.default.bgHex('#F59E0B');
|
|
67
|
+
console.log('');
|
|
68
|
+
console.log(` ${bg(' ')} ${chalk_1.default.bold.white('CodyMaster')} ${chalk_1.default.gray(`v${VERSION}`)}`);
|
|
69
|
+
console.log(` ${bg.black.bold(' CM ')} ${chalk_1.default.gray('34 Skills. Ship 10x faster.')}`);
|
|
70
|
+
console.log(` ${bg(' ')} ${chalk_1.default.gray(cPath)}`);
|
|
71
|
+
console.log(chalk_1.default.gray(' ' + '─'.repeat(46)));
|
|
72
|
+
}
|
|
73
|
+
// ─── Utility ────────────────────────────────────────────────────────────────
|
|
74
|
+
const COL_COLORS = {
|
|
75
|
+
'backlog': chalk_1.default.gray, 'in-progress': chalk_1.default.blue, 'review': chalk_1.default.yellow, 'done': chalk_1.default.green,
|
|
76
|
+
};
|
|
77
|
+
const PRIORITY_COLORS = {
|
|
78
|
+
'low': chalk_1.default.green, 'medium': chalk_1.default.yellow, 'high': chalk_1.default.red, 'urgent': chalk_1.default.magenta,
|
|
79
|
+
};
|
|
80
|
+
const STATUS_COLORS = {
|
|
81
|
+
'success': chalk_1.default.green, 'failed': chalk_1.default.red, 'pending': chalk_1.default.yellow,
|
|
82
|
+
'running': chalk_1.default.blue, 'rolled_back': chalk_1.default.magenta,
|
|
83
|
+
};
|
|
84
|
+
function padRight(str, len) {
|
|
85
|
+
return str.length >= len ? str.substring(0, len) : str + ' '.repeat(len - str.length);
|
|
86
|
+
}
|
|
87
|
+
function openUrl(url) {
|
|
88
|
+
const { execFile } = require('child_process');
|
|
89
|
+
const [cmd, ...args] = process.platform === 'darwin' ? ['open', url] :
|
|
90
|
+
process.platform === 'win32' ? ['cmd', '/c', 'start', url] :
|
|
91
|
+
['xdg-open', url];
|
|
92
|
+
execFile(cmd, args, () => { });
|
|
93
|
+
}
|
|
94
|
+
// ─── Post-install Onboarding ─────────────────────────────────────────────────
|
|
95
|
+
function postInstallOnboarding(platform) {
|
|
96
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
97
|
+
console.log();
|
|
98
|
+
console.log(chalk_1.default.green('╔══════════════════════════════════════════════════╗'));
|
|
99
|
+
console.log(chalk_1.default.green('║ 🎉 You\'re all set! What would you like to do? ║'));
|
|
100
|
+
console.log(chalk_1.default.green('╚══════════════════════════════════════════════════╝'));
|
|
101
|
+
console.log();
|
|
102
|
+
const p = (yield Promise.resolve().then(() => __importStar(require('prompts')))).default;
|
|
103
|
+
const invoke = platform === 'claude' ? '/cm:demo (type in Claude Code)' :
|
|
104
|
+
platform === 'gemini' ? '@[/cm-planning] in Gemini CLI' :
|
|
105
|
+
platform === 'cursor' ? '@cm-planning in Cursor Agent' :
|
|
106
|
+
'@cm-planning in your AI tool';
|
|
107
|
+
const resp = yield p({
|
|
108
|
+
type: 'select',
|
|
109
|
+
name: 'action',
|
|
110
|
+
message: 'Choose an action:',
|
|
111
|
+
choices: [
|
|
112
|
+
{
|
|
113
|
+
title: chalk_1.default.cyan('📊 Launch Dashboard'), value: 'dashboard',
|
|
114
|
+
description: `Open Mission Control → http://localhost:${data_1.DEFAULT_PORT}`
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
title: chalk_1.default.magenta('🚀 Start in ' + platform.charAt(0).toUpperCase() + platform.slice(1)), value: 'invoke',
|
|
118
|
+
description: invoke
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
title: chalk_1.default.white('🧩 Browse all 34 skills'), value: 'skills',
|
|
122
|
+
description: 'See every skill, domain, and usage'
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
title: chalk_1.default.yellow('⚡ Install `cm` globally'), value: 'global',
|
|
126
|
+
description: 'Add cody / cm / codymaster to your PATH'
|
|
127
|
+
},
|
|
128
|
+
{ title: chalk_1.default.gray('✅ Done'), value: 'done' },
|
|
129
|
+
],
|
|
130
|
+
hint: '↑↓ navigate · Enter select · Ctrl+C exit',
|
|
131
|
+
});
|
|
132
|
+
switch (resp === null || resp === void 0 ? void 0 : resp.action) {
|
|
133
|
+
case 'dashboard': {
|
|
134
|
+
console.log();
|
|
135
|
+
if (!isDashboardRunning()) {
|
|
136
|
+
(0, dashboard_1.launchDashboard)(data_1.DEFAULT_PORT, false);
|
|
137
|
+
yield new Promise(r => setTimeout(r, 800)); // let server start
|
|
138
|
+
}
|
|
139
|
+
console.log(chalk_1.default.cyan(`🌐 Opening http://localhost:${data_1.DEFAULT_PORT} ...`));
|
|
140
|
+
openUrl(`http://localhost:${data_1.DEFAULT_PORT}`);
|
|
141
|
+
console.log(chalk_1.default.gray(' Dashboard is running. Press Ctrl+C to stop.\n'));
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
case 'invoke':
|
|
145
|
+
console.log();
|
|
146
|
+
if (platform === 'claude') {
|
|
147
|
+
console.log(chalk_1.default.white('Open Claude Code and type:\n'));
|
|
148
|
+
console.log(chalk_1.default.cyan(' /cm:demo'));
|
|
149
|
+
console.log(chalk_1.default.gray('\n This will run an interactive tour of all 34 skills.\n'));
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
console.log(chalk_1.default.cyan(` ${invoke}\n`));
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
case 'skills':
|
|
156
|
+
console.log();
|
|
157
|
+
skillList();
|
|
158
|
+
break;
|
|
159
|
+
case 'global':
|
|
160
|
+
console.log();
|
|
161
|
+
console.log(chalk_1.default.white('Run this to install the `cm` CLI globally:\n'));
|
|
162
|
+
console.log(chalk_1.default.cyan(' npm install -g codymaster'));
|
|
163
|
+
console.log(chalk_1.default.gray('\nThen use:'));
|
|
164
|
+
console.log(chalk_1.default.cyan(' cm task add "My task"'));
|
|
165
|
+
console.log(chalk_1.default.cyan(' cm dashboard'));
|
|
166
|
+
console.log(chalk_1.default.cyan(' cm status\n'));
|
|
167
|
+
break;
|
|
168
|
+
default:
|
|
169
|
+
console.log(chalk_1.default.gray('\nRun `npx codymaster` any time to open the menu.\n'));
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
// ─── Interactive Quick Menu (no-args entry point) ─────────────────────────────
|
|
174
|
+
function showInteractiveMenu() {
|
|
175
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
176
|
+
showBanner();
|
|
177
|
+
const dashStatus = isDashboardRunning()
|
|
178
|
+
? chalk_1.default.green('● RUNNING') + chalk_1.default.gray(` http://localhost:${data_1.DEFAULT_PORT}`)
|
|
179
|
+
: chalk_1.default.gray('○ stopped');
|
|
180
|
+
console.log(chalk_1.default.gray(` Dashboard: ${dashStatus}`));
|
|
181
|
+
console.log();
|
|
182
|
+
const p = (yield Promise.resolve().then(() => __importStar(require('prompts')))).default;
|
|
183
|
+
const resp = yield p({
|
|
184
|
+
type: 'select',
|
|
185
|
+
name: 'action',
|
|
186
|
+
message: 'Quick menu:',
|
|
187
|
+
choices: [
|
|
188
|
+
{
|
|
189
|
+
title: chalk_1.default.cyan('📊 Dashboard'), value: 'dashboard',
|
|
190
|
+
description: isDashboardRunning() ? 'Open in browser' : 'Start & open in browser'
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
title: chalk_1.default.white('📋 My Tasks'), value: 'tasks',
|
|
194
|
+
description: 'View all tasks across projects'
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
title: chalk_1.default.white('📈 Status'), value: 'status',
|
|
198
|
+
description: 'Project health snapshot'
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
title: chalk_1.default.magenta('🧩 Browse Skills'), value: 'skills',
|
|
202
|
+
description: 'All 34 skills by domain'
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
title: chalk_1.default.yellow('➕ Add a Task'), value: 'addtask',
|
|
206
|
+
description: 'Quickly add a task to backlog'
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
title: chalk_1.default.green('⚡ Install/Update Skills'), value: 'install',
|
|
210
|
+
description: 'npx codymaster add --all'
|
|
211
|
+
},
|
|
212
|
+
{ title: chalk_1.default.gray('❓ Help'), value: 'help' },
|
|
213
|
+
],
|
|
214
|
+
hint: '↑↓ navigate · Enter select · Ctrl+C exit',
|
|
215
|
+
});
|
|
216
|
+
console.log();
|
|
217
|
+
switch (resp === null || resp === void 0 ? void 0 : resp.action) {
|
|
218
|
+
case 'dashboard':
|
|
219
|
+
if (!isDashboardRunning()) {
|
|
220
|
+
(0, dashboard_1.launchDashboard)(data_1.DEFAULT_PORT, false);
|
|
221
|
+
yield new Promise(r => setTimeout(r, 800));
|
|
222
|
+
}
|
|
223
|
+
console.log(chalk_1.default.cyan(`🌐 Opening http://localhost:${data_1.DEFAULT_PORT} ...`));
|
|
224
|
+
openUrl(`http://localhost:${data_1.DEFAULT_PORT}`);
|
|
225
|
+
console.log(chalk_1.default.gray('Dashboard is running. Ctrl+C to stop.\n'));
|
|
226
|
+
break;
|
|
227
|
+
case 'tasks':
|
|
228
|
+
// Inline task list
|
|
229
|
+
require('child_process').spawnSync(process.execPath, [process.argv[1], 'task', 'list'], { stdio: 'inherit' });
|
|
230
|
+
break;
|
|
231
|
+
case 'status':
|
|
232
|
+
require('child_process').spawnSync(process.execPath, [process.argv[1], 'status'], { stdio: 'inherit' });
|
|
233
|
+
break;
|
|
234
|
+
case 'skills':
|
|
235
|
+
skillList();
|
|
236
|
+
break;
|
|
237
|
+
case 'addtask': {
|
|
238
|
+
const t = yield p({ type: 'text', name: 'title', message: 'Task title:' });
|
|
239
|
+
if (t === null || t === void 0 ? void 0 : t.title) {
|
|
240
|
+
require('child_process').spawnSync(process.execPath, [process.argv[1], 'task', 'add', t.title], { stdio: 'inherit' });
|
|
241
|
+
}
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
case 'install':
|
|
245
|
+
console.log(chalk_1.default.cyan('Run: npx codymaster add --all\n'));
|
|
246
|
+
break;
|
|
247
|
+
case 'help':
|
|
248
|
+
default:
|
|
249
|
+
console.log(chalk_1.default.white('Usage: cm <command> [options]\n'));
|
|
250
|
+
console.log(chalk_1.default.gray(' cm dashboard Open Mission Control'));
|
|
251
|
+
console.log(chalk_1.default.gray(' cm status Project overview'));
|
|
252
|
+
console.log(chalk_1.default.gray(' cm task add "Title" Add a task'));
|
|
253
|
+
console.log(chalk_1.default.gray(' cm task list View tasks'));
|
|
254
|
+
console.log(chalk_1.default.gray(' cm list Browse 34 skills'));
|
|
255
|
+
console.log(chalk_1.default.gray(' cm deploy staging Record deployment'));
|
|
256
|
+
console.log(chalk_1.default.gray(' npx codymaster add --all Install/update skills\n'));
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
// ─── Program ────────────────────────────────────────────────────────────────
|
|
261
|
+
const program = new commander_1.Command();
|
|
262
|
+
program
|
|
263
|
+
.name('cm')
|
|
264
|
+
.description('Cody — 34 Skills. Ship 10x faster.')
|
|
265
|
+
.version(VERSION, '-v, --version', 'Show version')
|
|
266
|
+
.action(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
267
|
+
// Interactive quick menu (Amp-style)
|
|
268
|
+
yield showInteractiveMenu();
|
|
269
|
+
}));
|
|
270
|
+
// ─── Dashboard Command ─────────────────────────────────────────────────────
|
|
271
|
+
program
|
|
272
|
+
.command('dashboard [cmd]')
|
|
273
|
+
.alias('dash')
|
|
274
|
+
.description('Dashboard server (start|stop|status|open)')
|
|
275
|
+
.option('-p, --port <port>', 'Port number', String(data_1.DEFAULT_PORT))
|
|
276
|
+
.action((cmd, opts) => {
|
|
277
|
+
const port = parseInt(opts.port) || data_1.DEFAULT_PORT;
|
|
278
|
+
switch (cmd) {
|
|
279
|
+
case 'start':
|
|
280
|
+
case undefined:
|
|
281
|
+
if (isDashboardRunning()) {
|
|
282
|
+
console.log(chalk_1.default.yellow('⚠️ Dashboard already running.'));
|
|
283
|
+
console.log(chalk_1.default.gray(` URL: http://localhost:${port}`));
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
(0, dashboard_1.launchDashboard)(port);
|
|
287
|
+
break;
|
|
288
|
+
case 'stop':
|
|
289
|
+
stopDashboard();
|
|
290
|
+
break;
|
|
291
|
+
case 'status':
|
|
292
|
+
dashboardStatus(port);
|
|
293
|
+
break;
|
|
294
|
+
case 'open':
|
|
295
|
+
console.log(chalk_1.default.blue(`🌐 Opening http://localhost:${port} ...`));
|
|
296
|
+
openUrl(`http://localhost:${port}`);
|
|
297
|
+
break;
|
|
298
|
+
case 'url':
|
|
299
|
+
console.log(`http://localhost:${port}`);
|
|
300
|
+
break;
|
|
301
|
+
default:
|
|
302
|
+
console.log(chalk_1.default.red(`Unknown: ${cmd}`));
|
|
303
|
+
console.log(chalk_1.default.gray('Available: start, stop, status, open, url'));
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
function isDashboardRunning() {
|
|
307
|
+
try {
|
|
308
|
+
if (!fs_1.default.existsSync(data_1.PID_FILE))
|
|
309
|
+
return false;
|
|
310
|
+
const pid = parseInt(fs_1.default.readFileSync(data_1.PID_FILE, 'utf-8').trim());
|
|
311
|
+
process.kill(pid, 0);
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
catch (_a) {
|
|
315
|
+
try {
|
|
316
|
+
fs_1.default.unlinkSync(data_1.PID_FILE);
|
|
317
|
+
}
|
|
318
|
+
catch (_b) { }
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function stopDashboard() {
|
|
323
|
+
try {
|
|
324
|
+
if (!fs_1.default.existsSync(data_1.PID_FILE)) {
|
|
325
|
+
console.log(chalk_1.default.yellow('⚠️ No dashboard running.'));
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const pid = parseInt(fs_1.default.readFileSync(data_1.PID_FILE, 'utf-8').trim());
|
|
329
|
+
process.kill(pid, 'SIGTERM');
|
|
330
|
+
try {
|
|
331
|
+
fs_1.default.unlinkSync(data_1.PID_FILE);
|
|
332
|
+
}
|
|
333
|
+
catch (_a) { }
|
|
334
|
+
console.log(chalk_1.default.green(`✅ Dashboard stopped (PID ${pid}).`));
|
|
335
|
+
}
|
|
336
|
+
catch (err) {
|
|
337
|
+
console.log(chalk_1.default.red(`Failed to stop: ${err.message}`));
|
|
338
|
+
try {
|
|
339
|
+
fs_1.default.unlinkSync(data_1.PID_FILE);
|
|
340
|
+
}
|
|
341
|
+
catch (_b) { }
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function dashboardStatus(port) {
|
|
345
|
+
if (isDashboardRunning()) {
|
|
346
|
+
const pid = fs_1.default.readFileSync(data_1.PID_FILE, 'utf-8').trim();
|
|
347
|
+
console.log(chalk_1.default.green(`✅ Dashboard RUNNING`));
|
|
348
|
+
console.log(chalk_1.default.gray(` PID: ${pid}`));
|
|
349
|
+
console.log(chalk_1.default.gray(` URL: http://localhost:${port}`));
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
console.log(chalk_1.default.yellow('⚫ Dashboard NOT running'));
|
|
353
|
+
console.log(chalk_1.default.gray(' Start with: cm dashboard start'));
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
// ─── Task Command ───────────────────────────────────────────────────────────
|
|
357
|
+
program
|
|
358
|
+
.command('task <cmd> [args...]')
|
|
359
|
+
.alias('t')
|
|
360
|
+
.description('Task management (add|list|move|done|rm)')
|
|
361
|
+
.option('-p, --project <name>', 'Project name or ID')
|
|
362
|
+
.option('-c, --column <column>', 'Column (backlog|in-progress|review|done)', 'backlog')
|
|
363
|
+
.option('--priority <level>', 'Priority (low|medium|high|urgent)', 'medium')
|
|
364
|
+
.option('--agent <agent>', 'Agent name')
|
|
365
|
+
.option('--skill <skill>', 'Skill name')
|
|
366
|
+
.option('--all', 'Show all projects')
|
|
367
|
+
.option('--force', 'Force re-dispatch')
|
|
368
|
+
.action((cmd, args, opts) => {
|
|
369
|
+
switch (cmd) {
|
|
370
|
+
case 'add':
|
|
371
|
+
taskAdd(args.join(' '), opts);
|
|
372
|
+
break;
|
|
373
|
+
case 'list':
|
|
374
|
+
case 'ls':
|
|
375
|
+
taskList(opts);
|
|
376
|
+
break;
|
|
377
|
+
case 'move':
|
|
378
|
+
taskMove(args[0], args[1]);
|
|
379
|
+
break;
|
|
380
|
+
case 'done':
|
|
381
|
+
taskDone(args[0]);
|
|
382
|
+
break;
|
|
383
|
+
case 'rm':
|
|
384
|
+
case 'delete':
|
|
385
|
+
taskRemove(args[0]);
|
|
386
|
+
break;
|
|
387
|
+
case 'dispatch':
|
|
388
|
+
taskDispatch(args[0], opts);
|
|
389
|
+
break;
|
|
390
|
+
case 'stuck':
|
|
391
|
+
taskStuck(opts);
|
|
392
|
+
break;
|
|
393
|
+
default:
|
|
394
|
+
console.log(chalk_1.default.red(`Unknown: ${cmd}`));
|
|
395
|
+
console.log(chalk_1.default.gray('Available: add, list, move, done, rm, dispatch, stuck'));
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
function taskAdd(title, opts) {
|
|
399
|
+
if (!title) {
|
|
400
|
+
console.log(chalk_1.default.red('❌ Title required. Usage: cm task add "My task"'));
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const data = (0, data_1.loadData)();
|
|
404
|
+
let projectId;
|
|
405
|
+
if (opts.project) {
|
|
406
|
+
const project = (0, data_1.findProjectByNameOrId)(data, opts.project);
|
|
407
|
+
if (!project) {
|
|
408
|
+
console.log(chalk_1.default.red(`❌ Project not found: ${opts.project}`));
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
projectId = project.id;
|
|
412
|
+
}
|
|
413
|
+
else if (data.projects.length > 0) {
|
|
414
|
+
projectId = data.projects[0].id;
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
const dp = { id: crypto_1.default.randomUUID(), name: 'Default Project', path: process.cwd(), agents: [], createdAt: new Date().toISOString() };
|
|
418
|
+
data.projects.push(dp);
|
|
419
|
+
projectId = dp.id;
|
|
420
|
+
}
|
|
421
|
+
const now = new Date().toISOString();
|
|
422
|
+
const column = opts.column || 'backlog';
|
|
423
|
+
const ct = data.tasks.filter(t => t.column === column && t.projectId === projectId);
|
|
424
|
+
const mo = ct.length > 0 ? Math.max(...ct.map(t => t.order)) : -1;
|
|
425
|
+
const task = { id: crypto_1.default.randomUUID(), projectId: projectId, title: title.trim(), description: '', column, order: mo + 1, priority: opts.priority || 'medium', agent: opts.agent || '', skill: opts.skill || '', createdAt: now, updatedAt: now };
|
|
426
|
+
data.tasks.push(task);
|
|
427
|
+
(0, data_1.logActivity)(data, 'task_created', `Task "${task.title}" created via CLI`, projectId, opts.agent || '');
|
|
428
|
+
(0, data_1.saveData)(data);
|
|
429
|
+
const project = data.projects.find(p => p.id === projectId);
|
|
430
|
+
console.log(chalk_1.default.green(`✅ Task created: ${title}`));
|
|
431
|
+
console.log(chalk_1.default.gray(` ID: ${(0, data_1.shortId)(task.id)} | Project: ${(project === null || project === void 0 ? void 0 : project.name) || 'Default'} | ${column} | ${opts.priority || 'medium'}`));
|
|
432
|
+
}
|
|
433
|
+
function taskList(opts) {
|
|
434
|
+
const data = (0, data_1.loadData)();
|
|
435
|
+
let tasks = data.tasks;
|
|
436
|
+
if (opts.project && !opts.all) {
|
|
437
|
+
const project = (0, data_1.findProjectByNameOrId)(data, opts.project);
|
|
438
|
+
if (!project) {
|
|
439
|
+
console.log(chalk_1.default.red(`❌ Project not found: ${opts.project}`));
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
tasks = tasks.filter(t => t.projectId === project.id);
|
|
443
|
+
console.log(chalk_1.default.cyan(`\n📋 Tasks — ${project.name}\n`));
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
console.log(chalk_1.default.cyan('\n📋 All Tasks\n'));
|
|
447
|
+
}
|
|
448
|
+
if (tasks.length === 0) {
|
|
449
|
+
console.log(chalk_1.default.gray(' No tasks found.\n'));
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
console.log(chalk_1.default.gray(' ' + padRight('ID', 10) + padRight('Title', 36) + padRight('Column', 14) + padRight('Priority', 10) + padRight('Agent', 14) + 'Project'));
|
|
453
|
+
console.log(chalk_1.default.gray(' ' + '─'.repeat(100)));
|
|
454
|
+
const co = ['backlog', 'in-progress', 'review', 'done'];
|
|
455
|
+
tasks.sort((a, b) => co.indexOf(a.column) - co.indexOf(b.column) || a.order - b.order);
|
|
456
|
+
for (const task of tasks) {
|
|
457
|
+
const cc = COL_COLORS[task.column] || chalk_1.default.white;
|
|
458
|
+
const pc = PRIORITY_COLORS[task.priority] || chalk_1.default.white;
|
|
459
|
+
const project = data.projects.find(p => p.id === task.projectId);
|
|
460
|
+
console.log(' ' + chalk_1.default.gray(padRight((0, data_1.shortId)(task.id), 10)) + padRight(task.title.substring(0, 34), 36) + cc(padRight(task.column, 14)) + pc(padRight(task.priority, 10)) + chalk_1.default.gray(padRight(task.agent || '—', 14)) + chalk_1.default.gray((project === null || project === void 0 ? void 0 : project.name) || '—'));
|
|
461
|
+
}
|
|
462
|
+
console.log(chalk_1.default.gray(`\n Total: ${tasks.length} tasks\n`));
|
|
463
|
+
}
|
|
464
|
+
function taskMove(idPrefix, targetColumn) {
|
|
465
|
+
if (!idPrefix || !targetColumn) {
|
|
466
|
+
console.log(chalk_1.default.red('❌ Usage: cm task move <id> <column>'));
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const vc = ['backlog', 'in-progress', 'review', 'done'];
|
|
470
|
+
if (!vc.includes(targetColumn)) {
|
|
471
|
+
console.log(chalk_1.default.red(`❌ Invalid column: ${targetColumn}. Valid: ${vc.join(', ')}`));
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const data = (0, data_1.loadData)();
|
|
475
|
+
const task = (0, data_1.findTaskByIdPrefix)(data, idPrefix);
|
|
476
|
+
if (!task) {
|
|
477
|
+
console.log(chalk_1.default.red(`❌ Task not found: ${idPrefix}`));
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
const oldCol = task.column;
|
|
481
|
+
// Validate transition
|
|
482
|
+
const VALID_TRANSITIONS = {
|
|
483
|
+
'backlog': ['in-progress'],
|
|
484
|
+
'in-progress': ['review', 'done', 'backlog'],
|
|
485
|
+
'review': ['done', 'in-progress'],
|
|
486
|
+
'done': ['backlog'],
|
|
487
|
+
};
|
|
488
|
+
const allowed = VALID_TRANSITIONS[oldCol] || [];
|
|
489
|
+
if (oldCol !== targetColumn && !allowed.includes(targetColumn)) {
|
|
490
|
+
console.log(chalk_1.default.red(`❌ Invalid transition: ${oldCol} → ${targetColumn}`));
|
|
491
|
+
console.log(chalk_1.default.gray(` Allowed transitions: ${allowed.join(', ')}`));
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
if (oldCol === targetColumn) {
|
|
495
|
+
console.log(chalk_1.default.gray(` Task already in ${targetColumn}.`));
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
task.column = targetColumn;
|
|
499
|
+
task.updatedAt = new Date().toISOString();
|
|
500
|
+
task.stuckSince = undefined;
|
|
501
|
+
(0, data_1.logActivity)(data, targetColumn === 'done' ? 'task_done' : 'task_transitioned', `Task "${task.title}" moved: ${oldCol} → ${targetColumn} (CLI)`, task.projectId, task.agent, { from: oldCol, to: targetColumn });
|
|
502
|
+
(0, data_1.saveData)(data);
|
|
503
|
+
console.log(chalk_1.default.green(`✅ Moved "${task.title}"`));
|
|
504
|
+
console.log(chalk_1.default.gray(` ${oldCol} → `) + (COL_COLORS[targetColumn] || chalk_1.default.white)(targetColumn));
|
|
505
|
+
}
|
|
506
|
+
function taskStuck(opts) {
|
|
507
|
+
const data = (0, data_1.loadData)();
|
|
508
|
+
const thresholdMin = 30;
|
|
509
|
+
const now = Date.now();
|
|
510
|
+
let tasks = data.tasks.filter(t => t.column === 'in-progress');
|
|
511
|
+
if (opts.project) {
|
|
512
|
+
const project = (0, data_1.findProjectByNameOrId)(data, opts.project);
|
|
513
|
+
if (!project) {
|
|
514
|
+
console.log(chalk_1.default.red(`❌ Project not found: ${opts.project}`));
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
tasks = tasks.filter(t => t.projectId === project.id);
|
|
518
|
+
}
|
|
519
|
+
const stuck = tasks.filter(t => {
|
|
520
|
+
const elapsed = now - new Date(t.updatedAt).getTime();
|
|
521
|
+
return elapsed > thresholdMin * 60 * 1000;
|
|
522
|
+
}).sort((a, b) => new Date(a.updatedAt).getTime() - new Date(b.updatedAt).getTime());
|
|
523
|
+
if (stuck.length === 0) {
|
|
524
|
+
console.log(chalk_1.default.green(`\n ✅ No stuck tasks! All in-progress tasks updated within ${thresholdMin}m.\n`));
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
console.log(chalk_1.default.yellow(`\n⚠️ ${stuck.length} Stuck Tasks (>${thresholdMin}m in progress)\n`));
|
|
528
|
+
console.log(chalk_1.default.gray(' ' + padRight('ID', 10) + padRight('Title', 36) + padRight('Stuck For', 12) + padRight('Agent', 14) + 'Priority'));
|
|
529
|
+
console.log(chalk_1.default.gray(' ' + '─'.repeat(86)));
|
|
530
|
+
for (const task of stuck) {
|
|
531
|
+
const elapsed = now - new Date(task.updatedAt).getTime();
|
|
532
|
+
const minutes = Math.round(elapsed / 60000);
|
|
533
|
+
const timeStr = minutes < 60 ? `${minutes}m` : `${Math.floor(minutes / 60)}h ${minutes % 60}m`;
|
|
534
|
+
const project = data.projects.find(p => p.id === task.projectId);
|
|
535
|
+
const pc = PRIORITY_COLORS[task.priority] || chalk_1.default.white;
|
|
536
|
+
console.log(' ' + chalk_1.default.gray(padRight((0, data_1.shortId)(task.id), 10)) + padRight(task.title.substring(0, 34), 36) + chalk_1.default.yellow(padRight(timeStr, 12)) + chalk_1.default.gray(padRight(task.agent || '—', 14)) + pc(task.priority));
|
|
537
|
+
}
|
|
538
|
+
console.log();
|
|
539
|
+
console.log(chalk_1.default.gray(' Tip: Move tasks with: cm task move <id> review|done|backlog'));
|
|
540
|
+
console.log();
|
|
541
|
+
}
|
|
542
|
+
function taskDone(idPrefix) {
|
|
543
|
+
if (!idPrefix) {
|
|
544
|
+
console.log(chalk_1.default.red('❌ Usage: cm task done <id>'));
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
taskMove(idPrefix, 'done');
|
|
548
|
+
}
|
|
549
|
+
function taskRemove(idPrefix) {
|
|
550
|
+
if (!idPrefix) {
|
|
551
|
+
console.log(chalk_1.default.red('❌ Usage: cm task rm <id>'));
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const data = (0, data_1.loadData)();
|
|
555
|
+
const idx = data.tasks.findIndex(t => t.id === idPrefix || t.id.startsWith(idPrefix));
|
|
556
|
+
if (idx === -1) {
|
|
557
|
+
console.log(chalk_1.default.red(`❌ Task not found: ${idPrefix}`));
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const [removed] = data.tasks.splice(idx, 1);
|
|
561
|
+
(0, data_1.logActivity)(data, 'task_deleted', `Task "${removed.title}" deleted via CLI`, removed.projectId, removed.agent);
|
|
562
|
+
(0, data_1.saveData)(data);
|
|
563
|
+
console.log(chalk_1.default.green(`✅ Deleted: "${removed.title}" (${(0, data_1.shortId)(removed.id)})`));
|
|
564
|
+
}
|
|
565
|
+
function taskDispatch(idPrefix, opts) {
|
|
566
|
+
if (!idPrefix) {
|
|
567
|
+
console.log(chalk_1.default.red('❌ Usage: cm task dispatch <id> [--force]'));
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
const data = (0, data_1.loadData)();
|
|
571
|
+
const task = (0, data_1.findTaskByIdPrefix)(data, idPrefix);
|
|
572
|
+
if (!task) {
|
|
573
|
+
console.log(chalk_1.default.red(`❌ Task not found: ${idPrefix}`));
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const project = data.projects.find(p => p.id === task.projectId);
|
|
577
|
+
const result = (0, agent_dispatch_1.dispatchTaskToAgent)(task, project, opts.force || false);
|
|
578
|
+
if (result.success) {
|
|
579
|
+
task.dispatchStatus = 'dispatched';
|
|
580
|
+
task.dispatchedAt = new Date().toISOString();
|
|
581
|
+
task.dispatchError = undefined;
|
|
582
|
+
task.updatedAt = task.dispatchedAt;
|
|
583
|
+
(0, data_1.logActivity)(data, 'task_dispatched', `Task "${task.title}" dispatched to ${task.agent} via CLI`, task.projectId, task.agent, {
|
|
584
|
+
taskId: task.id, filePath: result.filePath, force: opts.force || false,
|
|
585
|
+
});
|
|
586
|
+
(0, data_1.saveData)(data);
|
|
587
|
+
console.log(chalk_1.default.green(`\n🚀 Task dispatched to ${task.agent}!`));
|
|
588
|
+
console.log(chalk_1.default.gray(` Task: ${task.title}`));
|
|
589
|
+
console.log(chalk_1.default.gray(` Agent: ${task.agent}`));
|
|
590
|
+
if (task.skill)
|
|
591
|
+
console.log(chalk_1.default.gray(` Skill: ${task.skill}`));
|
|
592
|
+
console.log(chalk_1.default.gray(` File: ${result.filePath}`));
|
|
593
|
+
console.log();
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
task.dispatchStatus = 'failed';
|
|
597
|
+
task.dispatchError = result.error;
|
|
598
|
+
task.updatedAt = new Date().toISOString();
|
|
599
|
+
(0, data_1.saveData)(data);
|
|
600
|
+
console.log(chalk_1.default.red(`❌ Dispatch failed: ${result.error}`));
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
// ─── Project Command ────────────────────────────────────────────────────────
|
|
604
|
+
program
|
|
605
|
+
.command('project <cmd> [args...]')
|
|
606
|
+
.alias('p')
|
|
607
|
+
.description('Project management (add|list|rm)')
|
|
608
|
+
.option('--path <path>', 'Workspace path')
|
|
609
|
+
.action((cmd, args, opts) => {
|
|
610
|
+
switch (cmd) {
|
|
611
|
+
case 'add':
|
|
612
|
+
projectAdd(args.join(' '), opts);
|
|
613
|
+
break;
|
|
614
|
+
case 'list':
|
|
615
|
+
case 'ls':
|
|
616
|
+
projectList();
|
|
617
|
+
break;
|
|
618
|
+
case 'rm':
|
|
619
|
+
case 'delete':
|
|
620
|
+
projectRemove(args[0]);
|
|
621
|
+
break;
|
|
622
|
+
default:
|
|
623
|
+
console.log(chalk_1.default.red(`Unknown: ${cmd}`));
|
|
624
|
+
console.log(chalk_1.default.gray('Available: add, list, rm'));
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
function projectAdd(name, opts) {
|
|
628
|
+
if (!name) {
|
|
629
|
+
console.log(chalk_1.default.red('❌ Usage: cm project add "my-project"'));
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
const data = (0, data_1.loadData)();
|
|
633
|
+
const project = { id: crypto_1.default.randomUUID(), name: name.trim(), path: opts.path || process.cwd(), agents: [], createdAt: new Date().toISOString() };
|
|
634
|
+
data.projects.push(project);
|
|
635
|
+
(0, data_1.logActivity)(data, 'project_created', `Project "${project.name}" created via CLI`, project.id);
|
|
636
|
+
(0, data_1.saveData)(data);
|
|
637
|
+
console.log(chalk_1.default.green(`✅ Project created: ${name}`));
|
|
638
|
+
console.log(chalk_1.default.gray(` ID: ${(0, data_1.shortId)(project.id)} | Path: ${project.path}`));
|
|
639
|
+
}
|
|
640
|
+
function projectList() {
|
|
641
|
+
const data = (0, data_1.loadData)();
|
|
642
|
+
if (data.projects.length === 0) {
|
|
643
|
+
console.log(chalk_1.default.gray('\n No projects.\n'));
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
console.log(chalk_1.default.cyan('\n📦 Projects\n'));
|
|
647
|
+
console.log(chalk_1.default.gray(' ' + padRight('ID', 10) + padRight('Name', 24) + padRight('Tasks', 8) + padRight('Agents', 20) + 'Path'));
|
|
648
|
+
console.log(chalk_1.default.gray(' ' + '─'.repeat(90)));
|
|
649
|
+
for (const project of data.projects) {
|
|
650
|
+
const pt = data.tasks.filter(t => t.projectId === project.id);
|
|
651
|
+
const agents = [...new Set(pt.map(t => t.agent).filter(Boolean))];
|
|
652
|
+
const done = pt.filter(t => t.column === 'done').length;
|
|
653
|
+
console.log(' ' + chalk_1.default.gray(padRight((0, data_1.shortId)(project.id), 10)) + chalk_1.default.white(padRight(project.name, 24)) + chalk_1.default.gray(padRight(`${done}/${pt.length}`, 8)) + chalk_1.default.gray(padRight(agents.join(', ') || '—', 20)) + chalk_1.default.gray(project.path || '—'));
|
|
654
|
+
}
|
|
655
|
+
console.log();
|
|
656
|
+
}
|
|
657
|
+
function projectRemove(query) {
|
|
658
|
+
if (!query) {
|
|
659
|
+
console.log(chalk_1.default.red('❌ Usage: cm project rm <name-or-id>'));
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
const data = (0, data_1.loadData)();
|
|
663
|
+
const project = (0, data_1.findProjectByNameOrId)(data, query);
|
|
664
|
+
if (!project) {
|
|
665
|
+
console.log(chalk_1.default.red(`❌ Project not found: ${query}`));
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
const tc = data.tasks.filter(t => t.projectId === project.id).length;
|
|
669
|
+
data.projects = data.projects.filter(p => p.id !== project.id);
|
|
670
|
+
data.tasks = data.tasks.filter(t => t.projectId !== project.id);
|
|
671
|
+
(0, data_1.logActivity)(data, 'project_deleted', `Project "${project.name}" deleted via CLI`, project.id);
|
|
672
|
+
(0, data_1.saveData)(data);
|
|
673
|
+
console.log(chalk_1.default.green(`✅ Deleted project "${project.name}" and ${tc} tasks.`));
|
|
674
|
+
}
|
|
675
|
+
// ─── Deploy Command ─────────────────────────────────────────────────────────
|
|
676
|
+
program
|
|
677
|
+
.command('deploy <cmd> [args...]')
|
|
678
|
+
.alias('d')
|
|
679
|
+
.description('Deploy management (staging|production|list)')
|
|
680
|
+
.option('-p, --project <name>', 'Project name or ID')
|
|
681
|
+
.option('-m, --message <msg>', 'Deploy message')
|
|
682
|
+
.option('--commit <hash>', 'Git commit hash')
|
|
683
|
+
.option('--branch <branch>', 'Git branch', 'main')
|
|
684
|
+
.option('--agent <agent>', 'Agent name')
|
|
685
|
+
.action((cmd, args, opts) => {
|
|
686
|
+
switch (cmd) {
|
|
687
|
+
case 'staging':
|
|
688
|
+
deployRecord('staging', opts);
|
|
689
|
+
break;
|
|
690
|
+
case 'production':
|
|
691
|
+
case 'prod':
|
|
692
|
+
deployRecord('production', opts);
|
|
693
|
+
break;
|
|
694
|
+
case 'list':
|
|
695
|
+
case 'ls':
|
|
696
|
+
deployList(opts);
|
|
697
|
+
break;
|
|
698
|
+
default:
|
|
699
|
+
console.log(chalk_1.default.red(`Unknown: ${cmd}`));
|
|
700
|
+
console.log(chalk_1.default.gray('Available: staging, production, list'));
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
function deployRecord(env, opts) {
|
|
704
|
+
const data = (0, data_1.loadData)();
|
|
705
|
+
let projectId;
|
|
706
|
+
if (opts.project) {
|
|
707
|
+
const p = (0, data_1.findProjectByNameOrId)(data, opts.project);
|
|
708
|
+
if (!p) {
|
|
709
|
+
console.log(chalk_1.default.red(`❌ Project not found: ${opts.project}`));
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
projectId = p.id;
|
|
713
|
+
}
|
|
714
|
+
else if (data.projects.length > 0) {
|
|
715
|
+
projectId = data.projects[0].id;
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
console.log(chalk_1.default.red('❌ No projects. Create one first: cm project add "my-project"'));
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
const now = new Date().toISOString();
|
|
722
|
+
const dep = {
|
|
723
|
+
id: crypto_1.default.randomUUID(), projectId: projectId, env, status: 'success',
|
|
724
|
+
commit: opts.commit || '', branch: opts.branch || 'main',
|
|
725
|
+
agent: opts.agent || '', message: opts.message || `Deploy to ${env}`,
|
|
726
|
+
startedAt: now, finishedAt: now,
|
|
727
|
+
};
|
|
728
|
+
data.deployments.unshift(dep);
|
|
729
|
+
(0, data_1.logActivity)(data, env === 'staging' ? 'deploy_staging' : 'deploy_production', `Deployed to ${env}: ${dep.message}`, projectId, opts.agent || '', { deploymentId: dep.id });
|
|
730
|
+
(0, data_1.saveData)(data);
|
|
731
|
+
const envColor = env === 'production' ? chalk_1.default.green : chalk_1.default.yellow;
|
|
732
|
+
const project = data.projects.find(p => p.id === projectId);
|
|
733
|
+
console.log(chalk_1.default.green(`\n🚀 Deployment recorded!`));
|
|
734
|
+
console.log(chalk_1.default.gray(` ID: ${(0, data_1.shortId)(dep.id)}`));
|
|
735
|
+
console.log(` Env: ${envColor(env)}`);
|
|
736
|
+
console.log(chalk_1.default.gray(` Project: ${(project === null || project === void 0 ? void 0 : project.name) || '—'}`));
|
|
737
|
+
console.log(chalk_1.default.gray(` Message: ${dep.message}`));
|
|
738
|
+
if (dep.commit)
|
|
739
|
+
console.log(chalk_1.default.gray(` Commit: ${dep.commit}`));
|
|
740
|
+
console.log(chalk_1.default.gray(` Branch: ${dep.branch}`));
|
|
741
|
+
console.log();
|
|
742
|
+
}
|
|
743
|
+
function deployList(opts) {
|
|
744
|
+
const data = (0, data_1.loadData)();
|
|
745
|
+
let deps = data.deployments;
|
|
746
|
+
if (opts.project) {
|
|
747
|
+
const p = (0, data_1.findProjectByNameOrId)(data, opts.project);
|
|
748
|
+
if (!p) {
|
|
749
|
+
console.log(chalk_1.default.red(`❌ Project not found: ${opts.project}`));
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
deps = deps.filter(d => d.projectId === p.id);
|
|
753
|
+
}
|
|
754
|
+
if (deps.length === 0) {
|
|
755
|
+
console.log(chalk_1.default.gray('\n No deployments yet.\n'));
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
console.log(chalk_1.default.cyan('\n🚀 Deployment History\n'));
|
|
759
|
+
console.log(chalk_1.default.gray(' ' + padRight('ID', 10) + padRight('Env', 12) + padRight('Status', 14) + padRight('Message', 32) + padRight('Branch', 12) + 'Time'));
|
|
760
|
+
console.log(chalk_1.default.gray(' ' + '─'.repeat(100)));
|
|
761
|
+
for (const dep of deps.slice(0, 20)) {
|
|
762
|
+
const sc = STATUS_COLORS[dep.status] || chalk_1.default.white;
|
|
763
|
+
const ec = dep.env === 'production' ? chalk_1.default.green : chalk_1.default.yellow;
|
|
764
|
+
const timeAgo = formatTimeAgoCli(dep.startedAt);
|
|
765
|
+
const rollbackFlag = dep.rollbackOf ? ' ⏪' : '';
|
|
766
|
+
console.log(' ' + chalk_1.default.gray(padRight((0, data_1.shortId)(dep.id), 10)) + ec(padRight(dep.env, 12)) + sc(padRight(dep.status.replace('_', ' ') + rollbackFlag, 14)) + padRight(dep.message.substring(0, 30), 32) + chalk_1.default.gray(padRight(dep.branch || '—', 12)) + chalk_1.default.gray(timeAgo));
|
|
767
|
+
}
|
|
768
|
+
console.log(chalk_1.default.gray(`\n Total: ${deps.length} deployments\n`));
|
|
769
|
+
}
|
|
770
|
+
// ─── Rollback Command ───────────────────────────────────────────────────────
|
|
771
|
+
program
|
|
772
|
+
.command('rollback <deployId>')
|
|
773
|
+
.alias('rb')
|
|
774
|
+
.description('Rollback a deployment')
|
|
775
|
+
.option('--agent <agent>', 'Agent name')
|
|
776
|
+
.action((deployId, opts) => {
|
|
777
|
+
const data = (0, data_1.loadData)();
|
|
778
|
+
const dep = data.deployments.find(d => d.id === deployId || d.id.startsWith(deployId));
|
|
779
|
+
if (!dep) {
|
|
780
|
+
console.log(chalk_1.default.red(`❌ Deployment not found: ${deployId}`));
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
if (dep.status === 'rolled_back') {
|
|
784
|
+
console.log(chalk_1.default.yellow('⚠️ Already rolled back.'));
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
dep.status = 'rolled_back';
|
|
788
|
+
const now = new Date().toISOString();
|
|
789
|
+
const rollback = {
|
|
790
|
+
id: crypto_1.default.randomUUID(), projectId: dep.projectId, env: dep.env, status: 'success',
|
|
791
|
+
commit: '', branch: dep.branch, agent: opts.agent || '', message: `Rollback of ${(0, data_1.shortId)(dep.id)}`,
|
|
792
|
+
startedAt: now, finishedAt: now, rollbackOf: dep.id,
|
|
793
|
+
};
|
|
794
|
+
data.deployments.unshift(rollback);
|
|
795
|
+
(0, data_1.logActivity)(data, 'rollback', `Rolled back ${dep.env} deploy: ${dep.message}`, dep.projectId, opts.agent || '', { originalDeployId: dep.id, rollbackId: rollback.id });
|
|
796
|
+
(0, data_1.saveData)(data);
|
|
797
|
+
console.log(chalk_1.default.magenta(`\n⏪ Rollback complete!`));
|
|
798
|
+
console.log(chalk_1.default.gray(` Original deploy: ${(0, data_1.shortId)(dep.id)} (${dep.env})`));
|
|
799
|
+
console.log(chalk_1.default.gray(` Rollback ID: ${(0, data_1.shortId)(rollback.id)}`));
|
|
800
|
+
console.log(chalk_1.default.gray(` Status: ${dep.message} → rolled back\n`));
|
|
801
|
+
});
|
|
802
|
+
// ─── History Command ────────────────────────────────────────────────────────
|
|
803
|
+
program
|
|
804
|
+
.command('history')
|
|
805
|
+
.alias('h')
|
|
806
|
+
.description('Show activity history')
|
|
807
|
+
.option('-n, --limit <n>', 'Number of entries', '20')
|
|
808
|
+
.option('-p, --project <name>', 'Filter by project')
|
|
809
|
+
.action((opts) => {
|
|
810
|
+
const data = (0, data_1.loadData)();
|
|
811
|
+
let acts = data.activities;
|
|
812
|
+
if (opts.project) {
|
|
813
|
+
const p = (0, data_1.findProjectByNameOrId)(data, opts.project);
|
|
814
|
+
if (!p) {
|
|
815
|
+
console.log(chalk_1.default.red(`❌ Project not found: ${opts.project}`));
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
acts = acts.filter(a => a.projectId === p.id);
|
|
819
|
+
}
|
|
820
|
+
const limit = parseInt(opts.limit) || 20;
|
|
821
|
+
acts = acts.slice(0, limit);
|
|
822
|
+
if (acts.length === 0) {
|
|
823
|
+
console.log(chalk_1.default.gray('\n No activity yet.\n'));
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
const ICONS = {
|
|
827
|
+
'task_created': '✨', 'task_moved': '↔️', 'task_done': '✅', 'task_deleted': '🗑️', 'task_updated': '✏️',
|
|
828
|
+
'project_created': '📦', 'project_deleted': '🗑️',
|
|
829
|
+
'deploy_staging': '🟡', 'deploy_production': '🚀', 'deploy_failed': '❌', 'rollback': '⏪',
|
|
830
|
+
'git_push': '📤', 'changelog_added': '📝',
|
|
831
|
+
};
|
|
832
|
+
console.log(chalk_1.default.cyan(`\n📜 Activity History (latest ${acts.length})\n`));
|
|
833
|
+
for (const a of acts) {
|
|
834
|
+
const icon = ICONS[a.type] || '📌';
|
|
835
|
+
const proj = data.projects.find(p => p.id === a.projectId);
|
|
836
|
+
const projTag = proj ? chalk_1.default.gray(` [${proj.name}]`) : '';
|
|
837
|
+
const agentTag = a.agent ? chalk_1.default.gray(` @${a.agent}`) : '';
|
|
838
|
+
const time = formatTimeAgoCli(a.createdAt);
|
|
839
|
+
console.log(` ${icon} ${a.message}${projTag}${agentTag} ${chalk_1.default.gray(`← ${time}`)}`);
|
|
840
|
+
}
|
|
841
|
+
console.log();
|
|
842
|
+
});
|
|
843
|
+
// ─── Changelog Command ─────────────────────────────────────────────────────
|
|
844
|
+
program
|
|
845
|
+
.command('changelog <cmd> [args...]')
|
|
846
|
+
.alias('cl')
|
|
847
|
+
.description('Changelog management (add|list)')
|
|
848
|
+
.option('-p, --project <name>', 'Project name or ID')
|
|
849
|
+
.option('--agent <agent>', 'Agent name')
|
|
850
|
+
.action((cmd, args, opts) => {
|
|
851
|
+
switch (cmd) {
|
|
852
|
+
case 'add':
|
|
853
|
+
changelogAdd(args, opts);
|
|
854
|
+
break;
|
|
855
|
+
case 'list':
|
|
856
|
+
case 'ls':
|
|
857
|
+
changelogList(opts);
|
|
858
|
+
break;
|
|
859
|
+
default:
|
|
860
|
+
console.log(chalk_1.default.red(`Unknown: ${cmd}`));
|
|
861
|
+
console.log(chalk_1.default.gray('Available: add, list'));
|
|
862
|
+
}
|
|
863
|
+
});
|
|
864
|
+
function changelogAdd(args, opts) {
|
|
865
|
+
if (args.length < 2) {
|
|
866
|
+
console.log(chalk_1.default.red('❌ Usage: cm changelog add <version> "<title>" [changes...]'));
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
const data = (0, data_1.loadData)();
|
|
870
|
+
let projectId = '';
|
|
871
|
+
if (opts.project) {
|
|
872
|
+
const p = (0, data_1.findProjectByNameOrId)(data, opts.project);
|
|
873
|
+
if (!p) {
|
|
874
|
+
console.log(chalk_1.default.red(`❌ Project not found: ${opts.project}`));
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
projectId = p.id;
|
|
878
|
+
}
|
|
879
|
+
else if (data.projects.length > 0) {
|
|
880
|
+
projectId = data.projects[0].id;
|
|
881
|
+
}
|
|
882
|
+
const version = args[0];
|
|
883
|
+
const title = args[1];
|
|
884
|
+
const changes = args.slice(2);
|
|
885
|
+
const entry = {
|
|
886
|
+
id: crypto_1.default.randomUUID(), projectId, version, title, changes,
|
|
887
|
+
agent: opts.agent || '', createdAt: new Date().toISOString(),
|
|
888
|
+
};
|
|
889
|
+
data.changelog.unshift(entry);
|
|
890
|
+
(0, data_1.logActivity)(data, 'changelog_added', `Changelog ${version}: ${title}`, projectId, opts.agent || '');
|
|
891
|
+
(0, data_1.saveData)(data);
|
|
892
|
+
console.log(chalk_1.default.green(`\n📝 Changelog entry added!`));
|
|
893
|
+
console.log(chalk_1.default.gray(` Version: ${version}`));
|
|
894
|
+
console.log(chalk_1.default.gray(` Title: ${title}`));
|
|
895
|
+
if (changes.length > 0) {
|
|
896
|
+
changes.forEach(c => console.log(chalk_1.default.gray(` • ${c}`)));
|
|
897
|
+
}
|
|
898
|
+
console.log();
|
|
899
|
+
}
|
|
900
|
+
function changelogList(opts) {
|
|
901
|
+
const data = (0, data_1.loadData)();
|
|
902
|
+
let entries = data.changelog;
|
|
903
|
+
if (opts.project) {
|
|
904
|
+
const p = (0, data_1.findProjectByNameOrId)(data, opts.project);
|
|
905
|
+
if (!p) {
|
|
906
|
+
console.log(chalk_1.default.red(`❌ Project not found: ${opts.project}`));
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
entries = entries.filter(c => c.projectId === p.id);
|
|
910
|
+
}
|
|
911
|
+
if (entries.length === 0) {
|
|
912
|
+
console.log(chalk_1.default.gray('\n No changelog entries.\n'));
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
console.log(chalk_1.default.cyan('\n📝 Changelog\n'));
|
|
916
|
+
for (const entry of entries) {
|
|
917
|
+
const proj = data.projects.find(p => p.id === entry.projectId);
|
|
918
|
+
console.log(chalk_1.default.blue(` ${entry.version}`) + chalk_1.default.white(` — ${entry.title}`) + chalk_1.default.gray(` (${formatTimeAgoCli(entry.createdAt)})${proj ? ' [' + proj.name + ']' : ''}`));
|
|
919
|
+
if (entry.changes.length > 0) {
|
|
920
|
+
entry.changes.forEach(c => console.log(chalk_1.default.gray(` • ${c}`)));
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
console.log();
|
|
924
|
+
}
|
|
925
|
+
// ─── Status Command ─────────────────────────────────────────────────────────
|
|
926
|
+
program
|
|
927
|
+
.command('status')
|
|
928
|
+
.alias('s')
|
|
929
|
+
.description('Show task & project summary')
|
|
930
|
+
.action(() => {
|
|
931
|
+
const data = (0, data_1.loadData)();
|
|
932
|
+
showBanner();
|
|
933
|
+
console.log(chalk_1.default.white('📊 Status Overview\n'));
|
|
934
|
+
// Projects
|
|
935
|
+
console.log(chalk_1.default.cyan(` Projects: ${data.projects.length}`));
|
|
936
|
+
for (const p of data.projects) {
|
|
937
|
+
const pt = data.tasks.filter(t => t.projectId === p.id);
|
|
938
|
+
const done = pt.filter(t => t.column === 'done').length;
|
|
939
|
+
const pct = pt.length > 0 ? Math.round((done / pt.length) * 100) : 0;
|
|
940
|
+
console.log(chalk_1.default.gray(` 📦 ${padRight(p.name, 20)} ${progressBar(pct)} ${done}/${pt.length} (${pct}%)`));
|
|
941
|
+
}
|
|
942
|
+
// Tasks
|
|
943
|
+
const total = data.tasks.length;
|
|
944
|
+
const byCol = { backlog: 0, 'in-progress': 0, review: 0, done: 0 };
|
|
945
|
+
data.tasks.forEach(t => { byCol[t.column] = (byCol[t.column] || 0) + 1; });
|
|
946
|
+
console.log();
|
|
947
|
+
console.log(chalk_1.default.white(` Tasks: ${total}`));
|
|
948
|
+
console.log(chalk_1.default.gray(` ⚪ Backlog: ${byCol.backlog}`));
|
|
949
|
+
console.log(chalk_1.default.blue(` 🔵 In Progress: ${byCol['in-progress']}`));
|
|
950
|
+
console.log(chalk_1.default.yellow(` 🟡 Review: ${byCol.review}`));
|
|
951
|
+
console.log(chalk_1.default.green(` 🟢 Done: ${byCol.done}`));
|
|
952
|
+
// Deploys
|
|
953
|
+
if (data.deployments.length > 0) {
|
|
954
|
+
console.log();
|
|
955
|
+
console.log(chalk_1.default.white(` Deployments: ${data.deployments.length}`));
|
|
956
|
+
const latest = data.deployments[0];
|
|
957
|
+
const sc = STATUS_COLORS[latest.status] || chalk_1.default.white;
|
|
958
|
+
console.log(chalk_1.default.gray(` Latest: ${latest.env} — ${sc(latest.status)} — ${latest.message} (${formatTimeAgoCli(latest.startedAt)})`));
|
|
959
|
+
}
|
|
960
|
+
// Agents
|
|
961
|
+
const agentCounts = {};
|
|
962
|
+
data.tasks.forEach(t => { if (t.agent)
|
|
963
|
+
agentCounts[t.agent] = (agentCounts[t.agent] || 0) + 1; });
|
|
964
|
+
const agentNames = Object.keys(agentCounts);
|
|
965
|
+
if (agentNames.length > 0) {
|
|
966
|
+
console.log();
|
|
967
|
+
console.log(chalk_1.default.white(` Active Agents: ${agentNames.length}`));
|
|
968
|
+
for (const agent of agentNames.sort()) {
|
|
969
|
+
console.log(chalk_1.default.gray(` 🤖 ${padRight(agent, 16)} ${agentCounts[agent]} tasks`));
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
// Dashboard
|
|
973
|
+
console.log();
|
|
974
|
+
if (isDashboardRunning()) {
|
|
975
|
+
console.log(chalk_1.default.green(` 🚀 Dashboard: RUNNING at http://codymaster.localhost:${data_1.DEFAULT_PORT}`));
|
|
976
|
+
}
|
|
977
|
+
else {
|
|
978
|
+
console.log(chalk_1.default.gray(` ⚫ Dashboard: not running (start with: cm dashboard)`));
|
|
979
|
+
}
|
|
980
|
+
console.log();
|
|
981
|
+
});
|
|
982
|
+
function progressBar(pct) {
|
|
983
|
+
const total = 12;
|
|
984
|
+
const filled = Math.round((pct / 100) * total);
|
|
985
|
+
return chalk_1.default.green('█'.repeat(filled)) + chalk_1.default.gray('░'.repeat(total - filled));
|
|
986
|
+
}
|
|
987
|
+
function formatTimeAgoCli(dateStr) {
|
|
988
|
+
const ms = Date.now() - new Date(dateStr).getTime();
|
|
989
|
+
const m = Math.floor(ms / 60000), h = Math.floor(ms / 3600000), d = Math.floor(ms / 86400000);
|
|
990
|
+
if (m < 1)
|
|
991
|
+
return 'just now';
|
|
992
|
+
if (m < 60)
|
|
993
|
+
return `${m}m ago`;
|
|
994
|
+
if (h < 24)
|
|
995
|
+
return `${h}h ago`;
|
|
996
|
+
if (d < 7)
|
|
997
|
+
return `${d}d ago`;
|
|
998
|
+
return new Date(dateStr).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
999
|
+
}
|
|
1000
|
+
// ─── Install Command ────────────────────────────────────────────────────────
|
|
1001
|
+
program
|
|
1002
|
+
.command('install <skill>')
|
|
1003
|
+
.description('Install an agent skill')
|
|
1004
|
+
.option('-p, --platform <platform>', 'Target platform (gemini|claude|cursor|windsurf|cline)')
|
|
1005
|
+
.action((skill, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1006
|
+
console.log(chalk_1.default.blue(`Installing skill: ${skill}...`));
|
|
1007
|
+
if (!opts.platform) {
|
|
1008
|
+
const prompts = (yield Promise.resolve().then(() => __importStar(require('prompts')))).default;
|
|
1009
|
+
const response = yield prompts({
|
|
1010
|
+
type: 'select', name: 'platform', message: 'Which platform?',
|
|
1011
|
+
choices: [
|
|
1012
|
+
{ title: 'Google Antigravity', value: 'gemini' },
|
|
1013
|
+
{ title: 'Claude Code', value: 'claude' },
|
|
1014
|
+
{ title: 'Cursor', value: 'cursor' },
|
|
1015
|
+
{ title: 'Windsurf', value: 'windsurf' },
|
|
1016
|
+
{ title: 'Cline / RooCode', value: 'cline' },
|
|
1017
|
+
]
|
|
1018
|
+
});
|
|
1019
|
+
opts.platform = response.platform;
|
|
1020
|
+
}
|
|
1021
|
+
console.log(chalk_1.default.green(`\n✅ Skill '${skill}' installed for ${opts.platform}!`));
|
|
1022
|
+
}));
|
|
1023
|
+
// ─── Add Command (npx codymaster add --skill cm-debugging) ───────────────────
|
|
1024
|
+
const ALL_SKILLS = [
|
|
1025
|
+
// Engineering
|
|
1026
|
+
'cm-tdd', 'cm-debugging', 'cm-quality-gate', 'cm-test-gate', 'cm-code-review',
|
|
1027
|
+
// Operations
|
|
1028
|
+
'cm-safe-deploy', 'cm-identity-guard', 'cm-git-worktrees', 'cm-terminal', 'cm-secret-shield', 'cm-safe-i18n',
|
|
1029
|
+
// Product
|
|
1030
|
+
'cm-planning', 'cm-ux-master', 'cm-ui-preview', 'cm-brainstorm-idea', 'cm-jtbd', 'cm-dockit', 'cm-project-bootstrap', 'cm-readit',
|
|
1031
|
+
// Growth
|
|
1032
|
+
'cm-content-factory', 'cm-ads-tracker', 'cro-methodology', 'cm-deep-search',
|
|
1033
|
+
// Orchestration
|
|
1034
|
+
'cm-execution', 'cm-continuity', 'cm-skill-index', 'cm-skill-mastery', 'cm-skill-chain',
|
|
1035
|
+
// Workflow
|
|
1036
|
+
'cm-start', 'cm-dashboard', 'cm-status', 'cm-how-it-work', 'cm-example',
|
|
1037
|
+
];
|
|
1038
|
+
const PLATFORM_TARGETS = {
|
|
1039
|
+
gemini: { dir: '.gemini/skills', invoke: '@[/<skill>]', note: 'or ~/.gemini/antigravity/skills/ for global' },
|
|
1040
|
+
cursor: { dir: '.cursor/rules', invoke: '@<skill>', note: 'Cursor rules directory' },
|
|
1041
|
+
windsurf: { dir: '.windsurf/rules', invoke: '@<skill>', note: 'Windsurf rules directory' },
|
|
1042
|
+
cline: { dir: '.cline/skills', invoke: '@<skill>', note: 'Cline / RooCode skills directory' },
|
|
1043
|
+
opencode: { dir: '.opencode/skills', invoke: '@[/<skill>]', note: 'OpenCode skills directory' },
|
|
1044
|
+
kiro: { dir: '.kiro/steering', invoke: '@<skill>', note: 'Kiro steering documents' },
|
|
1045
|
+
copilot: { dir: '.github', invoke: '(auto-context)', note: 'Added to copilot-instructions.md' },
|
|
1046
|
+
};
|
|
1047
|
+
const RAW_BASE = 'https://raw.githubusercontent.com/tody-agent/codymaster/main';
|
|
1048
|
+
function autoDetectPlatform() {
|
|
1049
|
+
const { execFileSync } = require('child_process');
|
|
1050
|
+
try {
|
|
1051
|
+
execFileSync('claude', ['--version'], { stdio: 'pipe' });
|
|
1052
|
+
return 'claude';
|
|
1053
|
+
}
|
|
1054
|
+
catch (_a) { }
|
|
1055
|
+
try {
|
|
1056
|
+
execFileSync('gemini', ['--version'], { stdio: 'pipe' });
|
|
1057
|
+
return 'gemini';
|
|
1058
|
+
}
|
|
1059
|
+
catch (_b) { }
|
|
1060
|
+
if (fs_1.default.existsSync(path_1.default.join(os_1.default.homedir(), '.cursor')))
|
|
1061
|
+
return 'cursor';
|
|
1062
|
+
if (fs_1.default.existsSync(path_1.default.join(os_1.default.homedir(), '.windsurf')))
|
|
1063
|
+
return 'windsurf';
|
|
1064
|
+
return 'manual';
|
|
1065
|
+
}
|
|
1066
|
+
function downloadFile(url, dest) {
|
|
1067
|
+
return new Promise((resolve) => {
|
|
1068
|
+
try {
|
|
1069
|
+
fs_1.default.mkdirSync(path_1.default.dirname(dest), { recursive: true });
|
|
1070
|
+
const file = fs_1.default.createWriteStream(dest);
|
|
1071
|
+
https_1.default.get(url, (res) => {
|
|
1072
|
+
if (res.statusCode !== 200) {
|
|
1073
|
+
file.close();
|
|
1074
|
+
try {
|
|
1075
|
+
fs_1.default.unlinkSync(dest);
|
|
1076
|
+
}
|
|
1077
|
+
catch (_a) { }
|
|
1078
|
+
resolve(false);
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
res.pipe(file);
|
|
1082
|
+
file.on('finish', () => { file.close(); resolve(true); });
|
|
1083
|
+
}).on('error', () => { file.close(); resolve(false); });
|
|
1084
|
+
}
|
|
1085
|
+
catch (_a) {
|
|
1086
|
+
resolve(false);
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
function doAddSkills(skills, platform) {
|
|
1091
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1092
|
+
console.log();
|
|
1093
|
+
const { execFileSync } = require('child_process');
|
|
1094
|
+
if (platform === 'claude') {
|
|
1095
|
+
console.log(chalk_1.default.magenta('🟣 Claude Code — Installing via plugin system'));
|
|
1096
|
+
console.log(chalk_1.default.gray(' (Claude installs all 34 skills as one bundle)\n'));
|
|
1097
|
+
// Step 1: Register marketplace — "already installed" is OK, just continue
|
|
1098
|
+
console.log(chalk_1.default.gray(' $ claude plugin marketplace add tody-agent/codymaster'));
|
|
1099
|
+
try {
|
|
1100
|
+
// Use 'pipe' so we can inspect output on failure; print stdout ourselves
|
|
1101
|
+
const r1 = require('child_process').spawnSync('claude', ['plugin', 'marketplace', 'add', 'tody-agent/codymaster'], { encoding: 'utf8' });
|
|
1102
|
+
if (r1.stdout)
|
|
1103
|
+
process.stdout.write(r1.stdout);
|
|
1104
|
+
if (r1.stderr)
|
|
1105
|
+
process.stderr.write(r1.stderr);
|
|
1106
|
+
const combined = String(r1.stdout || '') + String(r1.stderr || '');
|
|
1107
|
+
if (r1.status !== 0 && !combined.includes('already installed') && !combined.includes('already exists')) {
|
|
1108
|
+
console.log(chalk_1.default.yellow(' ⚠️ Marketplace warning — continuing anyway'));
|
|
1109
|
+
}
|
|
1110
|
+
else if (combined.includes('already installed') || combined.includes('already exists')) {
|
|
1111
|
+
console.log(chalk_1.default.gray(' ℹ️ Marketplace already registered'));
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
catch (_a) {
|
|
1115
|
+
console.log(chalk_1.default.yellow(' ⚠️ Could not reach marketplace — continuing'));
|
|
1116
|
+
}
|
|
1117
|
+
// Step 2: Install / update the plugin
|
|
1118
|
+
console.log(chalk_1.default.gray(' $ claude plugin install codymaster@codymaster'));
|
|
1119
|
+
try {
|
|
1120
|
+
execFileSync('claude', ['plugin', 'install', 'codymaster@codymaster'], { stdio: 'inherit' });
|
|
1121
|
+
console.log('\n' + chalk_1.default.green('✅ All 34 skills installed!'));
|
|
1122
|
+
yield postInstallOnboarding('claude');
|
|
1123
|
+
}
|
|
1124
|
+
catch (_b) {
|
|
1125
|
+
console.log(chalk_1.default.yellow('\n⚠️ Plugin install failed. Run manually:\n'));
|
|
1126
|
+
console.log(chalk_1.default.cyan(' claude plugin install codymaster@codymaster'));
|
|
1127
|
+
console.log(chalk_1.default.gray('\n Or one-liner:'));
|
|
1128
|
+
console.log(chalk_1.default.cyan(' bash <(curl -fsSL https://raw.githubusercontent.com/tody-agent/codymaster/main/install.sh) --claude'));
|
|
1129
|
+
}
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
if (platform === 'gemini') {
|
|
1133
|
+
console.log(chalk_1.default.cyan('💻 Gemini CLI — Installing via extensions'));
|
|
1134
|
+
try {
|
|
1135
|
+
execFileSync('gemini', ['extensions', 'install', 'https://github.com/tody-agent/codymaster'], { stdio: 'inherit' });
|
|
1136
|
+
console.log('\n' + chalk_1.default.green('✅ All 34 skills installed for Gemini CLI!'));
|
|
1137
|
+
yield postInstallOnboarding('gemini');
|
|
1138
|
+
}
|
|
1139
|
+
catch (_c) {
|
|
1140
|
+
console.log(chalk_1.default.yellow('💡 Run this in your terminal:\n'));
|
|
1141
|
+
console.log(chalk_1.default.cyan(' gemini extensions install https://github.com/tody-agent/codymaster\n'));
|
|
1142
|
+
}
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
const target = PLATFORM_TARGETS[platform];
|
|
1146
|
+
if (!target) {
|
|
1147
|
+
console.log(chalk_1.default.red(`❌ Unknown platform: ${platform}`));
|
|
1148
|
+
console.log(chalk_1.default.gray(` Supported: claude, gemini, cursor, windsurf, cline, opencode, kiro, copilot`));
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
if (platform === 'copilot') {
|
|
1152
|
+
const instrFile = path_1.default.join('.github', 'copilot-instructions.md');
|
|
1153
|
+
fs_1.default.mkdirSync('.github', { recursive: true });
|
|
1154
|
+
const header = '\n\n## Cody Master Skills\nThe following AI skills are available — reference them by name:\n';
|
|
1155
|
+
const lines = skills.map(s => `- **${s}**: see https://github.com/tody-agent/codymaster/blob/main/skills/${s}/SKILL.md`).join('\n');
|
|
1156
|
+
const existing = fs_1.default.existsSync(instrFile) ? fs_1.default.readFileSync(instrFile, 'utf-8') : '';
|
|
1157
|
+
if (!existing.includes('Cody Master Skills')) {
|
|
1158
|
+
fs_1.default.appendFileSync(instrFile, header + lines + '\n');
|
|
1159
|
+
}
|
|
1160
|
+
console.log(chalk_1.default.green(`✅ ${skills.length} skills referenced in ${instrFile}`));
|
|
1161
|
+
console.log(chalk_1.default.gray(' GitHub Copilot will use these as context automatically.'));
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
const icons = { cursor: '🔵', windsurf: '🟠', cline: '⚫', opencode: '📦', kiro: '🔶' };
|
|
1165
|
+
const icon = icons[platform] || '📦';
|
|
1166
|
+
const label = skills.length === ALL_SKILLS.length ? 'all 34 skills' : skills.join(', ');
|
|
1167
|
+
console.log(`${icon} ${platform} — Installing ${label}`);
|
|
1168
|
+
console.log(chalk_1.default.gray(` Target: ./${target.dir}/\n`));
|
|
1169
|
+
let ok = 0, fail = 0;
|
|
1170
|
+
for (const skill of skills) {
|
|
1171
|
+
const url = `${RAW_BASE}/skills/${skill}/SKILL.md`;
|
|
1172
|
+
const dest = path_1.default.join(target.dir, skill, 'SKILL.md');
|
|
1173
|
+
const success = yield downloadFile(url, dest);
|
|
1174
|
+
if (success) {
|
|
1175
|
+
process.stdout.write(chalk_1.default.green(` ✅ ${skill}\n`));
|
|
1176
|
+
ok++;
|
|
1177
|
+
}
|
|
1178
|
+
else {
|
|
1179
|
+
process.stdout.write(chalk_1.default.red(` ❌ ${skill}\n`));
|
|
1180
|
+
fail++;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
console.log();
|
|
1184
|
+
if (ok > 0) {
|
|
1185
|
+
console.log(chalk_1.default.green(`✅ ${ok} skill${ok > 1 ? 's' : ''} installed → ./${target.dir}/`));
|
|
1186
|
+
const invoke = target.invoke.replace('<skill>', skills[0]);
|
|
1187
|
+
console.log(chalk_1.default.cyan(`📖 Usage: ${invoke} Your prompt here`));
|
|
1188
|
+
if (target.note)
|
|
1189
|
+
console.log(chalk_1.default.gray(` Note: ${target.note}`));
|
|
1190
|
+
yield postInstallOnboarding(platform);
|
|
1191
|
+
}
|
|
1192
|
+
if (fail > 0) {
|
|
1193
|
+
console.log(chalk_1.default.yellow(`⚠️ ${fail} failed — check connection or clone manually:`));
|
|
1194
|
+
console.log(chalk_1.default.gray(` git clone https://github.com/tody-agent/codymaster.git`));
|
|
1195
|
+
}
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
program
|
|
1199
|
+
.command('add')
|
|
1200
|
+
.description('Add skills to your AI agent (npx codymaster add --skill cm-debugging)')
|
|
1201
|
+
.option('--skill <name>', 'Specific skill to add (e.g. cm-debugging)')
|
|
1202
|
+
.option('--all', 'Add all 34 skills')
|
|
1203
|
+
.option('--platform <platform>', 'Target: claude|gemini|cursor|windsurf|cline|opencode|kiro|copilot')
|
|
1204
|
+
.option('--list', 'Show available skills and exit')
|
|
1205
|
+
.action((opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1206
|
+
showBanner();
|
|
1207
|
+
if (opts.list) {
|
|
1208
|
+
skillList();
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
// Resolve skills array
|
|
1212
|
+
let skills = null;
|
|
1213
|
+
if (opts.all) {
|
|
1214
|
+
skills = ALL_SKILLS;
|
|
1215
|
+
}
|
|
1216
|
+
else if (opts.skill) {
|
|
1217
|
+
if (!ALL_SKILLS.includes(opts.skill)) {
|
|
1218
|
+
console.log(chalk_1.default.red(`❌ Unknown skill: ${opts.skill}`));
|
|
1219
|
+
console.log(chalk_1.default.gray(' Run: npx codymaster add --list'));
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
skills = [opts.skill];
|
|
1223
|
+
}
|
|
1224
|
+
// Detect or prompt platform
|
|
1225
|
+
let platform = opts.platform || autoDetectPlatform();
|
|
1226
|
+
if (platform === 'manual') {
|
|
1227
|
+
const prompts = (yield Promise.resolve().then(() => __importStar(require('prompts')))).default;
|
|
1228
|
+
const resp = yield prompts({
|
|
1229
|
+
type: 'select', name: 'platform', message: 'Select your AI coding platform:',
|
|
1230
|
+
choices: [
|
|
1231
|
+
{ title: '🟣 Claude Code (recommended)', value: 'claude' },
|
|
1232
|
+
{ title: '💻 Gemini CLI', value: 'gemini' },
|
|
1233
|
+
{ title: '🔵 Cursor', value: 'cursor' },
|
|
1234
|
+
{ title: '🟠 Windsurf', value: 'windsurf' },
|
|
1235
|
+
{ title: '⚫ Cline / RooCode', value: 'cline' },
|
|
1236
|
+
{ title: '📦 OpenCode', value: 'opencode' },
|
|
1237
|
+
{ title: '🔶 Kiro (AWS)', value: 'kiro' },
|
|
1238
|
+
{ title: '🐙 GitHub Copilot', value: 'copilot' },
|
|
1239
|
+
],
|
|
1240
|
+
});
|
|
1241
|
+
if (!resp.platform)
|
|
1242
|
+
return;
|
|
1243
|
+
platform = resp.platform;
|
|
1244
|
+
}
|
|
1245
|
+
// If no skills chosen yet, prompt
|
|
1246
|
+
if (!skills) {
|
|
1247
|
+
if (platform === 'claude' || platform === 'gemini') {
|
|
1248
|
+
skills = ALL_SKILLS;
|
|
1249
|
+
}
|
|
1250
|
+
else {
|
|
1251
|
+
const prompts = (yield Promise.resolve().then(() => __importStar(require('prompts')))).default;
|
|
1252
|
+
const resp = yield prompts({
|
|
1253
|
+
type: 'select', name: 'mode', message: 'What to install?',
|
|
1254
|
+
choices: [
|
|
1255
|
+
{ title: 'All 34 skills (full kit)', value: 'all' },
|
|
1256
|
+
{ title: 'Search & pick one skill', value: 'pick' },
|
|
1257
|
+
],
|
|
1258
|
+
});
|
|
1259
|
+
if (resp.mode === 'all') {
|
|
1260
|
+
skills = ALL_SKILLS;
|
|
1261
|
+
}
|
|
1262
|
+
else {
|
|
1263
|
+
const pick = yield prompts({
|
|
1264
|
+
type: 'autocomplete', name: 'skill', message: 'Type to search skill:',
|
|
1265
|
+
choices: ALL_SKILLS.map(s => ({ title: s, value: s })),
|
|
1266
|
+
});
|
|
1267
|
+
if (!pick.skill)
|
|
1268
|
+
return;
|
|
1269
|
+
skills = [pick.skill];
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
yield doAddSkills(skills, platform);
|
|
1274
|
+
}));
|
|
1275
|
+
// ─── List Command (quick alias for `cody skill list`) ─────────────────────────
|
|
1276
|
+
program
|
|
1277
|
+
.command('list')
|
|
1278
|
+
.alias('ls')
|
|
1279
|
+
.description('List all 34 available skills')
|
|
1280
|
+
.option('-d, --domain <domain>', 'Filter by domain')
|
|
1281
|
+
.action((opts) => {
|
|
1282
|
+
skillList(opts.domain);
|
|
1283
|
+
});
|
|
1284
|
+
// ─── Continuity Command (Working Memory) ────────────────────────────────────
|
|
1285
|
+
program
|
|
1286
|
+
.command('continuity [cmd]')
|
|
1287
|
+
.alias('ctx')
|
|
1288
|
+
.description('Working memory (init|status|reset|learnings|decisions)')
|
|
1289
|
+
.option('--path <path>', 'Project path', process.cwd())
|
|
1290
|
+
.action((cmd, opts) => {
|
|
1291
|
+
const projectPath = opts.path || process.cwd();
|
|
1292
|
+
switch (cmd) {
|
|
1293
|
+
case 'init':
|
|
1294
|
+
continuityInit(projectPath);
|
|
1295
|
+
break;
|
|
1296
|
+
case 'status':
|
|
1297
|
+
case undefined:
|
|
1298
|
+
continuityStatus(projectPath);
|
|
1299
|
+
break;
|
|
1300
|
+
case 'reset':
|
|
1301
|
+
continuityReset(projectPath);
|
|
1302
|
+
break;
|
|
1303
|
+
case 'learnings':
|
|
1304
|
+
case 'learn':
|
|
1305
|
+
continuityLearnings(projectPath);
|
|
1306
|
+
break;
|
|
1307
|
+
case 'decisions':
|
|
1308
|
+
case 'dec':
|
|
1309
|
+
continuityDecisions(projectPath);
|
|
1310
|
+
break;
|
|
1311
|
+
default:
|
|
1312
|
+
console.log(chalk_1.default.red(`Unknown: ${cmd}`));
|
|
1313
|
+
console.log(chalk_1.default.gray('Available: init, status, reset, learnings, decisions'));
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
function continuityInit(projectPath) {
|
|
1317
|
+
if ((0, continuity_1.hasCmDir)(projectPath)) {
|
|
1318
|
+
console.log(chalk_1.default.yellow('⚠️ .cm/ directory already exists.'));
|
|
1319
|
+
console.log(chalk_1.default.gray(` Path: ${projectPath}/.cm/`));
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
(0, continuity_1.ensureCmDir)(projectPath);
|
|
1323
|
+
console.log(chalk_1.default.green('✅ Working memory initialized!'));
|
|
1324
|
+
console.log(chalk_1.default.gray(` Created: ${projectPath}/.cm/`));
|
|
1325
|
+
console.log(chalk_1.default.gray(' ├── CONTINUITY.md (working memory)'));
|
|
1326
|
+
console.log(chalk_1.default.gray(' ├── config.yaml (RARV settings)'));
|
|
1327
|
+
console.log(chalk_1.default.gray(' └── memory/'));
|
|
1328
|
+
console.log(chalk_1.default.gray(' ├── learnings.json (error patterns)'));
|
|
1329
|
+
console.log(chalk_1.default.gray(' └── decisions.json (architecture decisions)'));
|
|
1330
|
+
console.log();
|
|
1331
|
+
console.log(chalk_1.default.cyan('💡 Protocol: Read CONTINUITY.md at session start, update at session end.'));
|
|
1332
|
+
}
|
|
1333
|
+
function continuityStatus(projectPath) {
|
|
1334
|
+
const status = (0, continuity_1.getContinuityStatus)(projectPath);
|
|
1335
|
+
if (!status.initialized) {
|
|
1336
|
+
console.log(chalk_1.default.yellow('⚫ Working memory not initialized.'));
|
|
1337
|
+
console.log(chalk_1.default.gray(' Run: cm continuity init'));
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
console.log(chalk_1.default.cyan('\n🧠 Working Memory Status\n'));
|
|
1341
|
+
console.log(` ${chalk_1.default.white('Project:')} ${status.project}`);
|
|
1342
|
+
console.log(` ${chalk_1.default.white('Phase:')} ${phaseColor(status.phase)(status.phase)}`);
|
|
1343
|
+
console.log(` ${chalk_1.default.white('Iteration:')} ${status.iteration}`);
|
|
1344
|
+
if (status.activeGoal) {
|
|
1345
|
+
console.log(` ${chalk_1.default.white('Goal:')} ${status.activeGoal}`);
|
|
1346
|
+
}
|
|
1347
|
+
if (status.currentTask) {
|
|
1348
|
+
console.log(` ${chalk_1.default.white('Task:')} ${status.currentTask}`);
|
|
1349
|
+
}
|
|
1350
|
+
console.log();
|
|
1351
|
+
console.log(chalk_1.default.gray(` ✅ Completed: ${status.completedCount} | 🚧 Blockers: ${status.blockerCount}`));
|
|
1352
|
+
console.log(chalk_1.default.gray(` 📚 Learnings: ${status.learningCount} | 📋 Decisions: ${status.decisionCount}`));
|
|
1353
|
+
if (status.lastUpdated) {
|
|
1354
|
+
console.log(chalk_1.default.gray(` 🕐 Updated: ${formatTimeAgoCli(status.lastUpdated)}`));
|
|
1355
|
+
}
|
|
1356
|
+
console.log();
|
|
1357
|
+
}
|
|
1358
|
+
function phaseColor(phase) {
|
|
1359
|
+
const colors = {
|
|
1360
|
+
planning: chalk_1.default.blue, executing: chalk_1.default.yellow, testing: chalk_1.default.magenta,
|
|
1361
|
+
deploying: chalk_1.default.green, reviewing: chalk_1.default.cyan, idle: chalk_1.default.gray,
|
|
1362
|
+
};
|
|
1363
|
+
return colors[phase] || chalk_1.default.white;
|
|
1364
|
+
}
|
|
1365
|
+
function continuityReset(projectPath) {
|
|
1366
|
+
if (!(0, continuity_1.hasCmDir)(projectPath)) {
|
|
1367
|
+
console.log(chalk_1.default.yellow('⚠️ No .cm/ directory found.'));
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
(0, continuity_1.resetContinuity)(projectPath);
|
|
1371
|
+
console.log(chalk_1.default.green('✅ Working memory reset.'));
|
|
1372
|
+
console.log(chalk_1.default.gray(' CONTINUITY.md cleared. Learnings preserved.'));
|
|
1373
|
+
}
|
|
1374
|
+
function continuityLearnings(projectPath) {
|
|
1375
|
+
if (!(0, continuity_1.hasCmDir)(projectPath)) {
|
|
1376
|
+
console.log(chalk_1.default.yellow('⚠️ No .cm/ directory found. Run: cm continuity init'));
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
const learnings = (0, continuity_1.getLearnings)(projectPath);
|
|
1380
|
+
if (learnings.length === 0) {
|
|
1381
|
+
console.log(chalk_1.default.gray('\n No learnings captured yet. 🎉\n'));
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
console.log(chalk_1.default.cyan(`\n📚 Mistakes & Learnings (${learnings.length})\n`));
|
|
1385
|
+
for (const l of learnings.slice(-10)) {
|
|
1386
|
+
console.log(chalk_1.default.red(` ❌ ${l.whatFailed}`));
|
|
1387
|
+
console.log(chalk_1.default.gray(` Why: ${l.whyFailed}`));
|
|
1388
|
+
console.log(chalk_1.default.green(` Fix: ${l.howToPrevent}`));
|
|
1389
|
+
console.log(chalk_1.default.gray(` ${formatTimeAgoCli(l.timestamp)} | ${l.agent || 'unknown'}\n`));
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
function continuityDecisions(projectPath) {
|
|
1393
|
+
if (!(0, continuity_1.hasCmDir)(projectPath)) {
|
|
1394
|
+
console.log(chalk_1.default.yellow('⚠️ No .cm/ directory found. Run: cm continuity init'));
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
const decisions = (0, continuity_1.getDecisions)(projectPath);
|
|
1398
|
+
if (decisions.length === 0) {
|
|
1399
|
+
console.log(chalk_1.default.gray('\n No decisions recorded yet.\n'));
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
console.log(chalk_1.default.cyan(`\n📋 Key Decisions (${decisions.length})\n`));
|
|
1403
|
+
for (const d of decisions.slice(-10)) {
|
|
1404
|
+
console.log(chalk_1.default.white(` 📌 ${d.decision}`));
|
|
1405
|
+
console.log(chalk_1.default.gray(` Rationale: ${d.rationale}`));
|
|
1406
|
+
console.log(chalk_1.default.gray(` ${formatTimeAgoCli(d.timestamp)} | ${d.agent || 'unknown'}\n`));
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
// ─── Brain Command (Enhanced Memory Explorer) ────────────────────────────────
|
|
1410
|
+
program
|
|
1411
|
+
.command('brain [cmd]')
|
|
1412
|
+
.alias('b')
|
|
1413
|
+
.description('Memory explorer (status|learnings|decisions|delete|stats|export)')
|
|
1414
|
+
.option('--path <path>', 'Project path', process.cwd())
|
|
1415
|
+
.option('--search <query>', 'Search learnings')
|
|
1416
|
+
.option('--last <n>', 'Show last N items')
|
|
1417
|
+
.option('--format <fmt>', 'Export format: json|md', 'json')
|
|
1418
|
+
.action((cmd, opts) => {
|
|
1419
|
+
const projectPath = opts.path || process.cwd();
|
|
1420
|
+
switch (cmd) {
|
|
1421
|
+
case 'status':
|
|
1422
|
+
case undefined:
|
|
1423
|
+
brainStatus(projectPath);
|
|
1424
|
+
break;
|
|
1425
|
+
case 'learnings':
|
|
1426
|
+
case 'learn':
|
|
1427
|
+
case 'l':
|
|
1428
|
+
brainLearnings(projectPath, opts);
|
|
1429
|
+
break;
|
|
1430
|
+
case 'decisions':
|
|
1431
|
+
case 'dec':
|
|
1432
|
+
case 'd':
|
|
1433
|
+
brainDecisions(projectPath, opts);
|
|
1434
|
+
break;
|
|
1435
|
+
case 'delete':
|
|
1436
|
+
case 'del':
|
|
1437
|
+
case 'rm':
|
|
1438
|
+
console.log(chalk_1.default.gray('Usage: cm brain delete <type> <id>'));
|
|
1439
|
+
console.log(chalk_1.default.gray(' type: learning | decision'));
|
|
1440
|
+
console.log(chalk_1.default.gray(' id: first 8 chars of the ID'));
|
|
1441
|
+
break;
|
|
1442
|
+
case 'stats':
|
|
1443
|
+
brainStats(projectPath);
|
|
1444
|
+
break;
|
|
1445
|
+
case 'export':
|
|
1446
|
+
brainExport(projectPath, opts);
|
|
1447
|
+
break;
|
|
1448
|
+
default:
|
|
1449
|
+
// Try as delete: cm brain learning <id> or cm brain decision <id>
|
|
1450
|
+
if (cmd === 'learning' || cmd === 'decision') {
|
|
1451
|
+
console.log(chalk_1.default.gray(`Did you mean: cm brain ${cmd}s ?`));
|
|
1452
|
+
}
|
|
1453
|
+
else {
|
|
1454
|
+
console.log(chalk_1.default.red(`Unknown: ${cmd}`));
|
|
1455
|
+
console.log(chalk_1.default.gray('Available: status, learnings, decisions, delete, stats, export'));
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
});
|
|
1459
|
+
program
|
|
1460
|
+
.command('brain-delete <type> <id>')
|
|
1461
|
+
.description('Delete a learning or decision by ID prefix')
|
|
1462
|
+
.option('--path <path>', 'Project path', process.cwd())
|
|
1463
|
+
.action((type, id, opts) => {
|
|
1464
|
+
const projectPath = opts.path || process.cwd();
|
|
1465
|
+
brainDelete(projectPath, type, id);
|
|
1466
|
+
});
|
|
1467
|
+
function brainStatus(projectPath) {
|
|
1468
|
+
const status = (0, continuity_1.getContinuityStatus)(projectPath);
|
|
1469
|
+
if (!status.initialized) {
|
|
1470
|
+
console.log(chalk_1.default.yellow('\n⚫ Working memory not initialized.'));
|
|
1471
|
+
console.log(chalk_1.default.gray(' Run: cm continuity init'));
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
showBanner();
|
|
1475
|
+
console.log(chalk_1.default.cyan('\n🧠 Brain — Memory Status\n'));
|
|
1476
|
+
// Stats row
|
|
1477
|
+
console.log(chalk_1.default.white(' ┌──────────────┬──────────────┬──────────────┬──────────────┐'));
|
|
1478
|
+
console.log(chalk_1.default.white(' │') + chalk_1.default.red(` ❤ Learn: ${padRight(String(status.learningCount), 4)}`) +
|
|
1479
|
+
chalk_1.default.white(' │') + chalk_1.default.blue(` 📋 Decide: ${padRight(String(status.decisionCount), 3)}`) +
|
|
1480
|
+
chalk_1.default.white(' │') + phaseColor(status.phase)(` ● ${padRight(status.phase, 9)}`) +
|
|
1481
|
+
chalk_1.default.white(' │') + chalk_1.default.gray(` #${padRight(String(status.iteration), 10)}`) + chalk_1.default.white('│'));
|
|
1482
|
+
console.log(chalk_1.default.white(' └──────────────┴──────────────┴──────────────┴──────────────┘'));
|
|
1483
|
+
console.log();
|
|
1484
|
+
console.log(` ${chalk_1.default.white('Project:')} ${status.project}`);
|
|
1485
|
+
if (status.activeGoal)
|
|
1486
|
+
console.log(` ${chalk_1.default.white('Goal:')} ${status.activeGoal}`);
|
|
1487
|
+
if (status.currentTask)
|
|
1488
|
+
console.log(` ${chalk_1.default.white('Task:')} ${status.currentTask}`);
|
|
1489
|
+
console.log(` ${chalk_1.default.white('Completed:')} ${status.completedCount} items`);
|
|
1490
|
+
console.log(` ${chalk_1.default.white('Blockers:')} ${status.blockerCount > 0 ? chalk_1.default.yellow(`🚧 ${status.blockerCount}`) : chalk_1.default.green('✅ None')}`);
|
|
1491
|
+
if (status.lastUpdated)
|
|
1492
|
+
console.log(` ${chalk_1.default.white('Updated:')} ${formatTimeAgoCli(status.lastUpdated)}`);
|
|
1493
|
+
console.log();
|
|
1494
|
+
console.log(chalk_1.default.gray(' Commands:'));
|
|
1495
|
+
console.log(chalk_1.default.gray(' cm brain learnings — View mistakes & lessons'));
|
|
1496
|
+
console.log(chalk_1.default.gray(' cm brain decisions — View architecture decisions'));
|
|
1497
|
+
console.log(chalk_1.default.gray(' cm brain stats — Memory statistics'));
|
|
1498
|
+
console.log(chalk_1.default.gray(' cm brain export — Export memory data'));
|
|
1499
|
+
console.log();
|
|
1500
|
+
}
|
|
1501
|
+
function brainLearnings(projectPath, opts) {
|
|
1502
|
+
if (!(0, continuity_1.hasCmDir)(projectPath)) {
|
|
1503
|
+
console.log(chalk_1.default.yellow('⚠️ No .cm/ directory found. Run: cm continuity init'));
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
let learnings = (0, continuity_1.getLearnings)(projectPath);
|
|
1507
|
+
// Search filter
|
|
1508
|
+
if (opts.search) {
|
|
1509
|
+
const q = opts.search.toLowerCase();
|
|
1510
|
+
learnings = learnings.filter(l => (l.whatFailed || '').toLowerCase().includes(q) ||
|
|
1511
|
+
(l.whyFailed || '').toLowerCase().includes(q) ||
|
|
1512
|
+
(l.howToPrevent || '').toLowerCase().includes(q));
|
|
1513
|
+
}
|
|
1514
|
+
// Last N
|
|
1515
|
+
const limit = opts.last ? parseInt(opts.last) : 15;
|
|
1516
|
+
const display = learnings.slice(-limit);
|
|
1517
|
+
if (display.length === 0) {
|
|
1518
|
+
console.log(chalk_1.default.gray(`\n No learnings ${opts.search ? 'matching "' + opts.search + '"' : 'captured yet'}. 🎉\n`));
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
console.log(chalk_1.default.cyan(`\n📚 Learnings (${display.length}${learnings.length > limit ? '/' + learnings.length : ''})\n`));
|
|
1522
|
+
for (const l of display) {
|
|
1523
|
+
const shortId = l.id ? l.id.substring(0, 8) : '???';
|
|
1524
|
+
console.log(chalk_1.default.red(` ❌ ${l.whatFailed}`) + chalk_1.default.gray(` [${shortId}]`));
|
|
1525
|
+
if (l.whyFailed)
|
|
1526
|
+
console.log(chalk_1.default.gray(` Why: ${l.whyFailed}`));
|
|
1527
|
+
if (l.howToPrevent)
|
|
1528
|
+
console.log(chalk_1.default.green(` Fix: ${l.howToPrevent}`));
|
|
1529
|
+
console.log(chalk_1.default.gray(` ${formatTimeAgoCli(l.timestamp)} | ${l.agent || 'unknown'}${l.module ? ' | 📦 ' + l.module : ''}\n`));
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
function brainDecisions(projectPath, opts) {
|
|
1533
|
+
if (!(0, continuity_1.hasCmDir)(projectPath)) {
|
|
1534
|
+
console.log(chalk_1.default.yellow('⚠️ No .cm/ directory found. Run: cm continuity init'));
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
const decisions = (0, continuity_1.getDecisions)(projectPath);
|
|
1538
|
+
const limit = opts.last ? parseInt(opts.last) : 15;
|
|
1539
|
+
const display = decisions.slice(-limit);
|
|
1540
|
+
if (display.length === 0) {
|
|
1541
|
+
console.log(chalk_1.default.gray('\n No decisions recorded yet.\n'));
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
console.log(chalk_1.default.cyan(`\n📋 Key Decisions (${display.length}${decisions.length > limit ? '/' + decisions.length : ''})\n`));
|
|
1545
|
+
for (const d of display) {
|
|
1546
|
+
const shortId = d.id ? d.id.substring(0, 8) : '???';
|
|
1547
|
+
console.log(chalk_1.default.white(` 📌 ${d.decision}`) + chalk_1.default.gray(` [${shortId}]`));
|
|
1548
|
+
if (d.rationale)
|
|
1549
|
+
console.log(chalk_1.default.gray(` Rationale: ${d.rationale}`));
|
|
1550
|
+
console.log(chalk_1.default.gray(` ${formatTimeAgoCli(d.timestamp)} | ${d.agent || 'unknown'}\n`));
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
function brainDelete(projectPath, type, id) {
|
|
1554
|
+
if (!(0, continuity_1.hasCmDir)(projectPath)) {
|
|
1555
|
+
console.log(chalk_1.default.yellow('⚠️ No .cm/ directory found.'));
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
if (type === 'learning' || type === 'l') {
|
|
1559
|
+
const learnings = (0, continuity_1.getLearnings)(projectPath);
|
|
1560
|
+
const target = learnings.find(l => l.id && l.id.startsWith(id));
|
|
1561
|
+
if (!target) {
|
|
1562
|
+
console.log(chalk_1.default.red(`❌ Learning not found with ID prefix: ${id}`));
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
const success = (0, continuity_1.deleteLearning)(projectPath, target.id);
|
|
1566
|
+
if (success) {
|
|
1567
|
+
console.log(chalk_1.default.green(`✅ Deleted learning: ${target.whatFailed}`));
|
|
1568
|
+
}
|
|
1569
|
+
else {
|
|
1570
|
+
console.log(chalk_1.default.red('❌ Failed to delete'));
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
else if (type === 'decision' || type === 'd') {
|
|
1574
|
+
const decisions = (0, continuity_1.getDecisions)(projectPath);
|
|
1575
|
+
const target = decisions.find(d => d.id && d.id.startsWith(id));
|
|
1576
|
+
if (!target) {
|
|
1577
|
+
console.log(chalk_1.default.red(`❌ Decision not found with ID prefix: ${id}`));
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1580
|
+
const success = (0, continuity_1.deleteDecision)(projectPath, target.id);
|
|
1581
|
+
if (success) {
|
|
1582
|
+
console.log(chalk_1.default.green(`✅ Deleted decision: ${target.decision}`));
|
|
1583
|
+
}
|
|
1584
|
+
else {
|
|
1585
|
+
console.log(chalk_1.default.red('❌ Failed to delete'));
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
else {
|
|
1589
|
+
console.log(chalk_1.default.red(`❌ Unknown type: ${type}`));
|
|
1590
|
+
console.log(chalk_1.default.gray(' Use: cm brain-delete learning <id> | cm brain-delete decision <id>'));
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
function brainStats(projectPath) {
|
|
1594
|
+
if (!(0, continuity_1.hasCmDir)(projectPath)) {
|
|
1595
|
+
console.log(chalk_1.default.yellow('⚠️ No .cm/ directory found. Run: cm continuity init'));
|
|
1596
|
+
return;
|
|
1597
|
+
}
|
|
1598
|
+
const status = (0, continuity_1.getContinuityStatus)(projectPath);
|
|
1599
|
+
const learnings = (0, continuity_1.getLearnings)(projectPath);
|
|
1600
|
+
const decisions = (0, continuity_1.getDecisions)(projectPath);
|
|
1601
|
+
console.log(chalk_1.default.cyan('\n📊 Brain Statistics\n'));
|
|
1602
|
+
console.log(` ${chalk_1.default.white('Learnings:')} ${learnings.length}`);
|
|
1603
|
+
console.log(` ${chalk_1.default.white('Decisions:')} ${decisions.length}`);
|
|
1604
|
+
console.log(` ${chalk_1.default.white('Completed:')} ${status.completedCount} items`);
|
|
1605
|
+
console.log(` ${chalk_1.default.white('Blockers:')} ${status.blockerCount}`);
|
|
1606
|
+
console.log(` ${chalk_1.default.white('Iteration:')} #${status.iteration}`);
|
|
1607
|
+
// Agent breakdown
|
|
1608
|
+
const agentMap = {};
|
|
1609
|
+
learnings.forEach(l => { if (l.agent)
|
|
1610
|
+
agentMap[l.agent] = (agentMap[l.agent] || 0) + 1; });
|
|
1611
|
+
decisions.forEach(d => { if (d.agent)
|
|
1612
|
+
agentMap[d.agent] = (agentMap[d.agent] || 0) + 1; });
|
|
1613
|
+
const agents = Object.entries(agentMap).sort((a, b) => b[1] - a[1]);
|
|
1614
|
+
if (agents.length > 0) {
|
|
1615
|
+
console.log();
|
|
1616
|
+
console.log(chalk_1.default.white(' Agents:'));
|
|
1617
|
+
for (const [agent, count] of agents) {
|
|
1618
|
+
console.log(chalk_1.default.gray(` 🤖 ${padRight(agent, 20)} ${count} entries`));
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
// Module breakdown
|
|
1622
|
+
const moduleMap = {};
|
|
1623
|
+
learnings.forEach(l => { if (l.module)
|
|
1624
|
+
moduleMap[l.module] = (moduleMap[l.module] || 0) + 1; });
|
|
1625
|
+
const modules = Object.entries(moduleMap).sort((a, b) => b[1] - a[1]);
|
|
1626
|
+
if (modules.length > 0) {
|
|
1627
|
+
console.log();
|
|
1628
|
+
console.log(chalk_1.default.white(' Modules (most error-prone):'));
|
|
1629
|
+
for (const [mod, count] of modules.slice(0, 5)) {
|
|
1630
|
+
console.log(chalk_1.default.gray(` 📦 ${padRight(mod, 20)} ${count} learnings`));
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
// Time range
|
|
1634
|
+
const allTimestamps = [...learnings.map(l => l.timestamp), ...decisions.map(d => d.timestamp)].filter(Boolean).sort();
|
|
1635
|
+
if (allTimestamps.length > 0) {
|
|
1636
|
+
console.log();
|
|
1637
|
+
console.log(chalk_1.default.gray(` First entry: ${formatTimeAgoCli(allTimestamps[0])}`));
|
|
1638
|
+
console.log(chalk_1.default.gray(` Latest: ${formatTimeAgoCli(allTimestamps[allTimestamps.length - 1])}`));
|
|
1639
|
+
}
|
|
1640
|
+
console.log();
|
|
1641
|
+
}
|
|
1642
|
+
function brainExport(projectPath, opts) {
|
|
1643
|
+
if (!(0, continuity_1.hasCmDir)(projectPath)) {
|
|
1644
|
+
console.log(chalk_1.default.yellow('⚠️ No .cm/ directory found.'));
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
const learnings = (0, continuity_1.getLearnings)(projectPath);
|
|
1648
|
+
const decisions = (0, continuity_1.getDecisions)(projectPath);
|
|
1649
|
+
const status = (0, continuity_1.getContinuityStatus)(projectPath);
|
|
1650
|
+
const format = opts.format || 'json';
|
|
1651
|
+
if (format === 'json') {
|
|
1652
|
+
const data = { status, learnings, decisions, exportedAt: new Date().toISOString() };
|
|
1653
|
+
const outFile = `brain-export-${new Date().toISOString().slice(0, 10)}.json`;
|
|
1654
|
+
fs_1.default.writeFileSync(outFile, JSON.stringify(data, null, 2));
|
|
1655
|
+
console.log(chalk_1.default.green(`✅ Exported to ${outFile}`));
|
|
1656
|
+
console.log(chalk_1.default.gray(` ${learnings.length} learnings, ${decisions.length} decisions`));
|
|
1657
|
+
}
|
|
1658
|
+
else if (format === 'md') {
|
|
1659
|
+
let md = `# Brain Export\n\n**Project:** ${status.project || 'Unknown'}\n**Exported:** ${new Date().toISOString()}\n\n`;
|
|
1660
|
+
md += `## Learnings (${learnings.length})\n\n`;
|
|
1661
|
+
for (const l of learnings) {
|
|
1662
|
+
md += `### ❌ ${l.whatFailed}\n- **Why:** ${l.whyFailed || 'N/A'}\n- **Fix:** ${l.howToPrevent || 'N/A'}\n- **Agent:** ${l.agent || 'unknown'} | **Date:** ${l.timestamp || 'N/A'}\n\n`;
|
|
1663
|
+
}
|
|
1664
|
+
md += `## Decisions (${decisions.length})\n\n`;
|
|
1665
|
+
for (const d of decisions) {
|
|
1666
|
+
md += `### 📌 ${d.decision}\n- **Rationale:** ${d.rationale || 'N/A'}\n- **Agent:** ${d.agent || 'unknown'} | **Date:** ${d.timestamp || 'N/A'}\n\n`;
|
|
1667
|
+
}
|
|
1668
|
+
const outFile = `brain-export-${new Date().toISOString().slice(0, 10)}.md`;
|
|
1669
|
+
fs_1.default.writeFileSync(outFile, md);
|
|
1670
|
+
console.log(chalk_1.default.green(`✅ Exported to ${outFile}`));
|
|
1671
|
+
console.log(chalk_1.default.gray(` ${learnings.length} learnings, ${decisions.length} decisions`));
|
|
1672
|
+
}
|
|
1673
|
+
else {
|
|
1674
|
+
console.log(chalk_1.default.red(`❌ Unknown format: ${format}`));
|
|
1675
|
+
console.log(chalk_1.default.gray(' Use: --format json | --format md'));
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
// ─── Skill Command ──────────────────────────────────────────────────────────
|
|
1679
|
+
const SKILL_CATALOG = {
|
|
1680
|
+
engineering: {
|
|
1681
|
+
icon: '🔧',
|
|
1682
|
+
skills: [
|
|
1683
|
+
{ name: 'cm-tdd', desc: 'Red-Green-Refactor cycle — test before code' },
|
|
1684
|
+
{ name: 'cm-debugging', desc: '5-phase root cause investigation' },
|
|
1685
|
+
{ name: 'cm-quality-gate', desc: '6-gate verification system' },
|
|
1686
|
+
{ name: 'cm-test-gate', desc: 'Setup 4-layer test infrastructure' },
|
|
1687
|
+
{ name: 'cm-code-review', desc: 'Professional PR review lifecycle' },
|
|
1688
|
+
],
|
|
1689
|
+
},
|
|
1690
|
+
operations: {
|
|
1691
|
+
icon: '⚙️',
|
|
1692
|
+
skills: [
|
|
1693
|
+
{ name: 'cm-safe-deploy', desc: 'Multi-gate deploy pipeline with rollback' },
|
|
1694
|
+
{ name: 'cm-identity-guard', desc: 'Prevent wrong-account deploys' },
|
|
1695
|
+
{ name: 'cm-git-worktrees', desc: 'Isolated feature branches without context-switch' },
|
|
1696
|
+
{ name: 'cm-terminal', desc: 'Safe terminal execution with logging' },
|
|
1697
|
+
{ name: 'cm-secret-shield', desc: 'Scan & block secrets before commit/deploy' },
|
|
1698
|
+
{ name: 'cm-safe-i18n', desc: 'Multi-pass translation with 8 audit gates' },
|
|
1699
|
+
],
|
|
1700
|
+
},
|
|
1701
|
+
product: {
|
|
1702
|
+
icon: '🎨',
|
|
1703
|
+
skills: [
|
|
1704
|
+
{ name: 'cm-planning', desc: 'Intent → design → structured plan' },
|
|
1705
|
+
{ name: 'cm-ux-master', desc: '48 UX Laws + 37 Design Tests' },
|
|
1706
|
+
{ name: 'cm-ui-preview', desc: 'Browser-previewed UI prototypes' },
|
|
1707
|
+
{ name: 'cm-brainstorm-idea', desc: 'Multi-lens ideation with scoring' },
|
|
1708
|
+
{ name: 'cm-jtbd', desc: 'Jobs-To-Be-Done framework & canvas' },
|
|
1709
|
+
{ name: 'cm-dockit', desc: 'Complete knowledge base from codebase' },
|
|
1710
|
+
{ name: 'cm-project-bootstrap', desc: 'Full project setup: design → CI → deploy' },
|
|
1711
|
+
{ name: 'cm-readit', desc: 'Web audio TTS reader & MP3 player' },
|
|
1712
|
+
],
|
|
1713
|
+
},
|
|
1714
|
+
growth: {
|
|
1715
|
+
icon: '📈',
|
|
1716
|
+
skills: [
|
|
1717
|
+
{ name: 'cm-content-factory', desc: 'AI content engine: research → deploy' },
|
|
1718
|
+
{ name: 'cm-ads-tracker', desc: 'Facebook/TikTok/Google pixel setup' },
|
|
1719
|
+
{ name: 'cro-methodology', desc: 'Conversion audit + A/B test design' },
|
|
1720
|
+
{ name: 'cm-deep-search', desc: 'Multi-source deep research synthesis' },
|
|
1721
|
+
],
|
|
1722
|
+
},
|
|
1723
|
+
orchestration: {
|
|
1724
|
+
icon: '🎯',
|
|
1725
|
+
skills: [
|
|
1726
|
+
{ name: 'cm-execution', desc: 'Execute plans: batch, parallel, RARV' },
|
|
1727
|
+
{ name: 'cm-continuity', desc: 'Working memory: read/update per session' },
|
|
1728
|
+
{ name: 'cm-skill-index', desc: 'Progressive skill discovery & routing' },
|
|
1729
|
+
{ name: 'cm-skill-mastery', desc: 'Meta: when/how to invoke skills' },
|
|
1730
|
+
{ name: 'cm-skill-chain', desc: 'Multi-skill pipeline execution' },
|
|
1731
|
+
],
|
|
1732
|
+
},
|
|
1733
|
+
workflow: {
|
|
1734
|
+
icon: '⚡',
|
|
1735
|
+
skills: [
|
|
1736
|
+
{ name: 'cm-start', desc: 'Onboarding & session kick-off wizard' },
|
|
1737
|
+
{ name: 'cm-dashboard', desc: 'Project status & task Kanban board' },
|
|
1738
|
+
{ name: 'cm-status', desc: 'Quick project health snapshot' },
|
|
1739
|
+
{ name: 'cm-how-it-work', desc: 'Interactive explainer for all 34 skills' },
|
|
1740
|
+
{ name: 'cm-example', desc: 'Minimal template for new skills' },
|
|
1741
|
+
],
|
|
1742
|
+
},
|
|
1743
|
+
};
|
|
1744
|
+
program
|
|
1745
|
+
.command('skill [cmd] [name]')
|
|
1746
|
+
.alias('sk')
|
|
1747
|
+
.description('Skill management (list|info|domains)')
|
|
1748
|
+
.action((cmd, name) => {
|
|
1749
|
+
switch (cmd) {
|
|
1750
|
+
case 'list':
|
|
1751
|
+
case 'ls':
|
|
1752
|
+
case undefined:
|
|
1753
|
+
skillList();
|
|
1754
|
+
break;
|
|
1755
|
+
case 'info':
|
|
1756
|
+
if (!name) {
|
|
1757
|
+
console.log(chalk_1.default.red('❌ Usage: cm skill info <skill-name>'));
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
skillInfo(name);
|
|
1761
|
+
break;
|
|
1762
|
+
case 'domains':
|
|
1763
|
+
skillDomains();
|
|
1764
|
+
break;
|
|
1765
|
+
default:
|
|
1766
|
+
// Treat cmd as skill name for `cody skill cm-tdd`
|
|
1767
|
+
skillInfo(cmd);
|
|
1768
|
+
}
|
|
1769
|
+
});
|
|
1770
|
+
function skillList(filterDomain) {
|
|
1771
|
+
const entries = filterDomain
|
|
1772
|
+
? Object.entries(SKILL_CATALOG).filter(([d]) => d.toLowerCase().startsWith(filterDomain.toLowerCase()))
|
|
1773
|
+
: Object.entries(SKILL_CATALOG);
|
|
1774
|
+
if (entries.length === 0) {
|
|
1775
|
+
console.log(chalk_1.default.red(`❌ Domain not found: ${filterDomain}`));
|
|
1776
|
+
console.log(chalk_1.default.gray(' Domains: engineering, operations, product, growth, orchestration, workflow'));
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
console.log(chalk_1.default.cyan('\n🧩 Cody Master — 34 Skills\n'));
|
|
1780
|
+
let total = 0;
|
|
1781
|
+
for (const [domain, data] of entries) {
|
|
1782
|
+
console.log(chalk_1.default.white(` ${data.icon} ${domain.charAt(0).toUpperCase() + domain.slice(1)}`));
|
|
1783
|
+
for (const skill of data.skills) {
|
|
1784
|
+
console.log(` ${chalk_1.default.cyan(padRight(skill.name, 26))} ${chalk_1.default.gray(skill.desc)}`);
|
|
1785
|
+
total++;
|
|
1786
|
+
}
|
|
1787
|
+
console.log();
|
|
1788
|
+
}
|
|
1789
|
+
console.log(chalk_1.default.gray(` ${total} skills across ${entries.length} domains`));
|
|
1790
|
+
console.log(chalk_1.default.gray(` Install: npx codymaster add --all`));
|
|
1791
|
+
console.log(chalk_1.default.gray(` Add one: npx codymaster add --skill <name>\n`));
|
|
1792
|
+
}
|
|
1793
|
+
function skillInfo(name) {
|
|
1794
|
+
for (const [domain, data] of Object.entries(SKILL_CATALOG)) {
|
|
1795
|
+
const skill = data.skills.find(s => s.name === name);
|
|
1796
|
+
if (skill) {
|
|
1797
|
+
console.log(chalk_1.default.cyan(`\n🧩 Skill: ${skill.name}\n`));
|
|
1798
|
+
console.log(` ${chalk_1.default.white('Domain:')} ${domain}`);
|
|
1799
|
+
console.log(` ${chalk_1.default.white('Description:')} ${skill.desc}`);
|
|
1800
|
+
const agents = (0, judge_1.suggestAgentsForSkill)(skill.name);
|
|
1801
|
+
console.log(` ${chalk_1.default.white('Best Agents:')} ${agents.join(', ')}`);
|
|
1802
|
+
console.log(` ${chalk_1.default.white('Invoke:')} @[/${skill.name}] (Antigravity/Gemini)`);
|
|
1803
|
+
console.log(` /${skill.name} (Claude Code)`);
|
|
1804
|
+
console.log(` @${skill.name} (Cursor/Windsurf/Cline)`);
|
|
1805
|
+
console.log();
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
console.log(chalk_1.default.red(`❌ Skill not found: ${name}`));
|
|
1810
|
+
console.log(chalk_1.default.gray(' Use "cm skill list" to see all available skills.'));
|
|
1811
|
+
}
|
|
1812
|
+
function skillDomains() {
|
|
1813
|
+
console.log(chalk_1.default.cyan('\n🎯 Skill Domains\n'));
|
|
1814
|
+
let total = 0;
|
|
1815
|
+
for (const [domain, data] of Object.entries(SKILL_CATALOG)) {
|
|
1816
|
+
console.log(` ${data.icon} ${chalk_1.default.white(padRight(domain.charAt(0).toUpperCase() + domain.slice(1), 16))} ${chalk_1.default.gray(`${data.skills.length} skills`)}`);
|
|
1817
|
+
total += data.skills.length;
|
|
1818
|
+
}
|
|
1819
|
+
console.log(chalk_1.default.gray(`\n Total: ${total} skills across ${Object.keys(SKILL_CATALOG).length} domains`));
|
|
1820
|
+
console.log();
|
|
1821
|
+
}
|
|
1822
|
+
// ─── Judge Command ──────────────────────────────────────────────────────────
|
|
1823
|
+
program
|
|
1824
|
+
.command('judge [taskId]')
|
|
1825
|
+
.alias('j')
|
|
1826
|
+
.description('Judge agent decisions for tasks')
|
|
1827
|
+
.option('-p, --project <name>', 'Filter by project')
|
|
1828
|
+
.action((taskId, opts) => {
|
|
1829
|
+
const data = (0, data_1.loadData)();
|
|
1830
|
+
if (taskId) {
|
|
1831
|
+
// Single task evaluation
|
|
1832
|
+
const task = (0, data_1.findTaskByIdPrefix)(data, taskId);
|
|
1833
|
+
if (!task) {
|
|
1834
|
+
console.log(chalk_1.default.red(`❌ Task not found: ${taskId}`));
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1837
|
+
const project = data.projects.find(p => p.id === task.projectId);
|
|
1838
|
+
let learnings = [];
|
|
1839
|
+
if ((project === null || project === void 0 ? void 0 : project.path) && (0, continuity_1.hasCmDir)(project.path)) {
|
|
1840
|
+
learnings = (0, continuity_1.getLearnings)(project.path);
|
|
1841
|
+
}
|
|
1842
|
+
const decision = (0, judge_1.evaluateTaskState)(task, data.tasks, learnings);
|
|
1843
|
+
console.log(chalk_1.default.cyan(`\n🤖 Judge Decision\n`));
|
|
1844
|
+
console.log(` ${chalk_1.default.white('Task:')} ${task.title}`);
|
|
1845
|
+
console.log(` ${chalk_1.default.white('Column:')} ${task.column}`);
|
|
1846
|
+
console.log(` ${chalk_1.default.white('Action:')} ${decision.badge} ${decision.action}`);
|
|
1847
|
+
console.log(` ${chalk_1.default.white('Reason:')} ${decision.reason}`);
|
|
1848
|
+
console.log(` ${chalk_1.default.white('Confidence:')} ${Math.round(decision.confidence * 100)}%`);
|
|
1849
|
+
if (decision.suggestedNextSkill) {
|
|
1850
|
+
console.log(` ${chalk_1.default.white('Suggested:')} ${decision.suggestedNextSkill}`);
|
|
1851
|
+
}
|
|
1852
|
+
console.log();
|
|
1853
|
+
}
|
|
1854
|
+
else {
|
|
1855
|
+
// All active tasks
|
|
1856
|
+
let tasks = data.tasks;
|
|
1857
|
+
if (opts.project) {
|
|
1858
|
+
const project = (0, data_1.findProjectByNameOrId)(data, opts.project);
|
|
1859
|
+
if (!project) {
|
|
1860
|
+
console.log(chalk_1.default.red(`❌ Project not found: ${opts.project}`));
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
tasks = tasks.filter(t => t.projectId === project.id);
|
|
1864
|
+
}
|
|
1865
|
+
let allLearnings = [];
|
|
1866
|
+
for (const project of data.projects) {
|
|
1867
|
+
if (project.path && (0, continuity_1.hasCmDir)(project.path)) {
|
|
1868
|
+
allLearnings = allLearnings.concat((0, continuity_1.getLearnings)(project.path));
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
const decisions = (0, judge_1.evaluateAllTasks)(tasks, allLearnings);
|
|
1872
|
+
if (decisions.size === 0) {
|
|
1873
|
+
console.log(chalk_1.default.gray('\n No active tasks to evaluate.\n'));
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
console.log(chalk_1.default.cyan(`\n🤖 Judge Decisions (${decisions.size} active tasks)\n`));
|
|
1877
|
+
console.log(chalk_1.default.gray(' ' + padRight('Badge', 8) + padRight('Action', 12) + padRight('Confidence', 12) + 'Task'));
|
|
1878
|
+
console.log(chalk_1.default.gray(' ' + '─'.repeat(70)));
|
|
1879
|
+
for (const [tid, dec] of decisions) {
|
|
1880
|
+
const task = tasks.find(t => t.id === tid);
|
|
1881
|
+
const actionColor = dec.action === 'CONTINUE' ? chalk_1.default.green
|
|
1882
|
+
: dec.action === 'COMPLETE' ? chalk_1.default.blue
|
|
1883
|
+
: dec.action === 'ESCALATE' ? chalk_1.default.yellow
|
|
1884
|
+
: chalk_1.default.magenta;
|
|
1885
|
+
console.log(' ' + padRight(dec.badge, 8) + actionColor(padRight(dec.action, 12)) + chalk_1.default.gray(padRight(`${Math.round(dec.confidence * 100)}%`, 12)) + ((task === null || task === void 0 ? void 0 : task.title) || tid.substring(0, 8)));
|
|
1886
|
+
}
|
|
1887
|
+
console.log();
|
|
1888
|
+
}
|
|
1889
|
+
});
|
|
1890
|
+
// ─── Init Command ───────────────────────────────────────────────────────────
|
|
1891
|
+
program
|
|
1892
|
+
.command('init')
|
|
1893
|
+
.description('Initialize project from current directory')
|
|
1894
|
+
.option('-n, --name <name>', 'Project name')
|
|
1895
|
+
.option('--path <path>', 'Workspace path', process.cwd())
|
|
1896
|
+
.action((opts) => {
|
|
1897
|
+
const data = (0, data_1.loadData)();
|
|
1898
|
+
const projectName = opts.name || path_1.default.basename(opts.path || process.cwd());
|
|
1899
|
+
const projectPath = opts.path || process.cwd();
|
|
1900
|
+
// Check if already exists
|
|
1901
|
+
const existing = data.projects.find(p => p.path === projectPath || p.name === projectName);
|
|
1902
|
+
if (existing) {
|
|
1903
|
+
console.log(chalk_1.default.yellow(`⚠️ Project already exists: ${existing.name}`));
|
|
1904
|
+
console.log(chalk_1.default.gray(` ID: ${(0, data_1.shortId)(existing.id)} | Path: ${existing.path}`));
|
|
1905
|
+
return;
|
|
1906
|
+
}
|
|
1907
|
+
const project = {
|
|
1908
|
+
id: crypto_1.default.randomUUID(),
|
|
1909
|
+
name: projectName,
|
|
1910
|
+
path: projectPath,
|
|
1911
|
+
agents: [],
|
|
1912
|
+
createdAt: new Date().toISOString(),
|
|
1913
|
+
};
|
|
1914
|
+
data.projects.push(project);
|
|
1915
|
+
(0, data_1.logActivity)(data, 'project_created', `Project "${project.name}" initialized via CLI`, project.id);
|
|
1916
|
+
(0, data_1.saveData)(data);
|
|
1917
|
+
// Also init working memory
|
|
1918
|
+
(0, continuity_1.ensureCmDir)(projectPath);
|
|
1919
|
+
console.log(chalk_1.default.green(`\n✅ Project initialized: ${projectName}`));
|
|
1920
|
+
console.log(chalk_1.default.gray(` ID: ${(0, data_1.shortId)(project.id)}`));
|
|
1921
|
+
console.log(chalk_1.default.gray(` Path: ${projectPath}`));
|
|
1922
|
+
console.log(chalk_1.default.gray(` .cm/ Working memory created`));
|
|
1923
|
+
console.log();
|
|
1924
|
+
if (!isDashboardRunning()) {
|
|
1925
|
+
(0, dashboard_1.launchDashboard)(data_1.DEFAULT_PORT);
|
|
1926
|
+
console.log(chalk_1.default.green(` 🚀 Dashboard auto-started! You can track progress at http://codymaster.localhost:${data_1.DEFAULT_PORT}`));
|
|
1927
|
+
}
|
|
1928
|
+
console.log(chalk_1.default.cyan('💡 Next steps:'));
|
|
1929
|
+
console.log(chalk_1.default.gray(' cm task add "My first task"'));
|
|
1930
|
+
console.log(chalk_1.default.gray(' cm open'));
|
|
1931
|
+
console.log();
|
|
1932
|
+
});
|
|
1933
|
+
// ─── Open Command ───────────────────────────────────────────────────────────
|
|
1934
|
+
program
|
|
1935
|
+
.command('open')
|
|
1936
|
+
.alias('o')
|
|
1937
|
+
.description('Open dashboard in browser')
|
|
1938
|
+
.option('-p, --port <port>', 'Port number', String(data_1.DEFAULT_PORT))
|
|
1939
|
+
.action((opts) => {
|
|
1940
|
+
const port = parseInt(opts.port) || data_1.DEFAULT_PORT;
|
|
1941
|
+
if (!isDashboardRunning()) {
|
|
1942
|
+
console.log(chalk_1.default.yellow('⚠️ Dashboard not running. Starting it first...'));
|
|
1943
|
+
(0, dashboard_1.launchDashboard)(port);
|
|
1944
|
+
setTimeout(() => openUrl(`http://codymaster.localhost:${port}`), 1500);
|
|
1945
|
+
}
|
|
1946
|
+
else {
|
|
1947
|
+
console.log(chalk_1.default.blue(`🌐 Opening http://codymaster.localhost:${port} ...`));
|
|
1948
|
+
openUrl(`http://codymaster.localhost:${port}`);
|
|
1949
|
+
}
|
|
1950
|
+
});
|
|
1951
|
+
// ─── Config Command ─────────────────────────────────────────────────────────
|
|
1952
|
+
program
|
|
1953
|
+
.command('config')
|
|
1954
|
+
.alias('cfg')
|
|
1955
|
+
.description('Show configuration & data paths')
|
|
1956
|
+
.action(() => {
|
|
1957
|
+
console.log(chalk_1.default.cyan(`\n⚙️ Cody Configuration\n`));
|
|
1958
|
+
console.log(` ${chalk_1.default.white('Version:')} ${VERSION}`);
|
|
1959
|
+
console.log(` ${chalk_1.default.white('Data Dir:')} ${data_1.DATA_DIR}`);
|
|
1960
|
+
console.log(` ${chalk_1.default.white('Data File:')} ${data_1.DATA_FILE}`);
|
|
1961
|
+
console.log(` ${chalk_1.default.white('PID File:')} ${data_1.PID_FILE}`);
|
|
1962
|
+
console.log(` ${chalk_1.default.white('Port:')} ${data_1.DEFAULT_PORT}`);
|
|
1963
|
+
console.log(` ${chalk_1.default.white('CLI Names:')} cm | cm | codymaster`);
|
|
1964
|
+
console.log();
|
|
1965
|
+
// Show data stats
|
|
1966
|
+
const data = (0, data_1.loadData)();
|
|
1967
|
+
console.log(chalk_1.default.white(' Data Stats:'));
|
|
1968
|
+
console.log(chalk_1.default.gray(` Projects: ${data.projects.length}`));
|
|
1969
|
+
console.log(chalk_1.default.gray(` Tasks: ${data.tasks.length}`));
|
|
1970
|
+
console.log(chalk_1.default.gray(` Deploys: ${data.deployments.length}`));
|
|
1971
|
+
console.log(chalk_1.default.gray(` Activities: ${data.activities.length}`));
|
|
1972
|
+
console.log(chalk_1.default.gray(` Changelog: ${data.changelog.length}`));
|
|
1973
|
+
console.log();
|
|
1974
|
+
// Dashboard status
|
|
1975
|
+
if (isDashboardRunning()) {
|
|
1976
|
+
console.log(chalk_1.default.green(` 🚀 Dashboard: RUNNING at http://codymaster.localhost:${data_1.DEFAULT_PORT}`));
|
|
1977
|
+
}
|
|
1978
|
+
else {
|
|
1979
|
+
console.log(chalk_1.default.gray(` ⚫ Dashboard: not running`));
|
|
1980
|
+
}
|
|
1981
|
+
console.log();
|
|
1982
|
+
});
|
|
1983
|
+
// ─── Agents Command ─────────────────────────────────────────────────────────
|
|
1984
|
+
const AGENT_LIST = [
|
|
1985
|
+
{ id: 'antigravity', name: 'Google Antigravity', icon: '🟢' },
|
|
1986
|
+
{ id: 'claude-code', name: 'Claude Code', icon: '🟣' },
|
|
1987
|
+
{ id: 'cursor', name: 'Cursor', icon: '🔵' },
|
|
1988
|
+
{ id: 'gemini-cli', name: 'Gemini CLI', icon: '💻' },
|
|
1989
|
+
{ id: 'windsurf', name: 'Windsurf', icon: '🟠' },
|
|
1990
|
+
{ id: 'cline', name: 'Cline / RooCode', icon: '🟤' },
|
|
1991
|
+
{ id: 'copilot', name: 'GitHub Copilot', icon: '🐈' },
|
|
1992
|
+
];
|
|
1993
|
+
program
|
|
1994
|
+
.command('agents [skill]')
|
|
1995
|
+
.alias('ag')
|
|
1996
|
+
.description('List agents or suggest best agent for a skill')
|
|
1997
|
+
.action((skill) => {
|
|
1998
|
+
if (skill) {
|
|
1999
|
+
// Suggest best agents for skill
|
|
2000
|
+
const domain = (0, judge_1.getSkillDomain)(skill);
|
|
2001
|
+
const agents = (0, judge_1.suggestAgentsForSkill)(skill);
|
|
2002
|
+
console.log(chalk_1.default.cyan(`\n🤖 Agent Suggestions for ${chalk_1.default.white(skill)}\n`));
|
|
2003
|
+
console.log(chalk_1.default.gray(` Domain: ${domain}\n`));
|
|
2004
|
+
agents.forEach((agentId, index) => {
|
|
2005
|
+
const agent = AGENT_LIST.find(a => a.id === agentId);
|
|
2006
|
+
const affinity = index === 0 ? chalk_1.default.green('★ BEST') : index === 1 ? chalk_1.default.yellow('● GOOD') : chalk_1.default.gray('○ OK');
|
|
2007
|
+
console.log(` ${(agent === null || agent === void 0 ? void 0 : agent.icon) || '🤖'} ${padRight((agent === null || agent === void 0 ? void 0 : agent.name) || agentId, 24)} ${affinity}`);
|
|
2008
|
+
});
|
|
2009
|
+
console.log();
|
|
2010
|
+
}
|
|
2011
|
+
else {
|
|
2012
|
+
// List all agents
|
|
2013
|
+
console.log(chalk_1.default.cyan('\n🤖 Available Agents\n'));
|
|
2014
|
+
for (const agent of AGENT_LIST) {
|
|
2015
|
+
console.log(` ${agent.icon} ${chalk_1.default.white(padRight(agent.name, 24))} ${chalk_1.default.gray(agent.id)}`);
|
|
2016
|
+
}
|
|
2017
|
+
console.log();
|
|
2018
|
+
console.log(chalk_1.default.gray(' 💡 Tip: cm agents <skill-name> to see best agents for a skill'));
|
|
2019
|
+
console.log();
|
|
2020
|
+
}
|
|
2021
|
+
});
|
|
2022
|
+
// ─── Sync Command ───────────────────────────────────────────────────────────
|
|
2023
|
+
program
|
|
2024
|
+
.command('sync <file>')
|
|
2025
|
+
.description('Bulk import tasks from JSON file')
|
|
2026
|
+
.option('-p, --project <name>', 'Target project')
|
|
2027
|
+
.option('--agent <agent>', 'Agent name')
|
|
2028
|
+
.option('--skill <skill>', 'Skill name')
|
|
2029
|
+
.action((file, opts) => {
|
|
2030
|
+
const filePath = path_1.default.resolve(file);
|
|
2031
|
+
if (!fs_1.default.existsSync(filePath)) {
|
|
2032
|
+
console.log(chalk_1.default.red(`❌ File not found: ${filePath}`));
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
let tasks;
|
|
2036
|
+
try {
|
|
2037
|
+
const content = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
2038
|
+
const parsed = JSON.parse(content);
|
|
2039
|
+
tasks = Array.isArray(parsed) ? parsed : parsed.tasks;
|
|
2040
|
+
if (!Array.isArray(tasks))
|
|
2041
|
+
throw new Error('Invalid format');
|
|
2042
|
+
}
|
|
2043
|
+
catch (err) {
|
|
2044
|
+
console.log(chalk_1.default.red(`❌ Invalid JSON file: ${err.message}`));
|
|
2045
|
+
console.log(chalk_1.default.gray(' Expected format: [{"title": "...", "priority": "...", "column": "..."}]'));
|
|
2046
|
+
return;
|
|
2047
|
+
}
|
|
2048
|
+
const data = (0, data_1.loadData)();
|
|
2049
|
+
let projectId;
|
|
2050
|
+
if (opts.project) {
|
|
2051
|
+
const p = (0, data_1.findProjectByNameOrId)(data, opts.project);
|
|
2052
|
+
if (!p) {
|
|
2053
|
+
console.log(chalk_1.default.red(`❌ Project not found: ${opts.project}`));
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
projectId = p.id;
|
|
2057
|
+
}
|
|
2058
|
+
else if (data.projects.length > 0) {
|
|
2059
|
+
projectId = data.projects[0].id;
|
|
2060
|
+
}
|
|
2061
|
+
else {
|
|
2062
|
+
const dp = { id: crypto_1.default.randomUUID(), name: 'Default Project', path: process.cwd(), agents: [], createdAt: new Date().toISOString() };
|
|
2063
|
+
data.projects.push(dp);
|
|
2064
|
+
projectId = dp.id;
|
|
2065
|
+
}
|
|
2066
|
+
const now = new Date().toISOString();
|
|
2067
|
+
let count = 0;
|
|
2068
|
+
for (const t of tasks) {
|
|
2069
|
+
const col = t.column || 'backlog';
|
|
2070
|
+
const ct = data.tasks.filter(tk => tk.column === col && tk.projectId === projectId);
|
|
2071
|
+
const mo = ct.length > 0 ? Math.max(...ct.map(tk => tk.order)) : -1;
|
|
2072
|
+
const task = {
|
|
2073
|
+
id: crypto_1.default.randomUUID(), projectId: projectId,
|
|
2074
|
+
title: String(t.title || '').trim(),
|
|
2075
|
+
description: String(t.description || '').trim(),
|
|
2076
|
+
column: col, order: mo + 1,
|
|
2077
|
+
priority: t.priority || 'medium',
|
|
2078
|
+
agent: opts.agent || t.agent || '',
|
|
2079
|
+
skill: opts.skill || t.skill || '',
|
|
2080
|
+
createdAt: now, updatedAt: now,
|
|
2081
|
+
};
|
|
2082
|
+
data.tasks.push(task);
|
|
2083
|
+
count++;
|
|
2084
|
+
}
|
|
2085
|
+
(0, data_1.logActivity)(data, 'task_created', `Synced ${count} tasks from ${path_1.default.basename(filePath)}`, projectId, opts.agent || '', { count, file: filePath });
|
|
2086
|
+
(0, data_1.saveData)(data);
|
|
2087
|
+
const project = data.projects.find(p => p.id === projectId);
|
|
2088
|
+
console.log(chalk_1.default.green(`\n✅ Synced ${count} tasks!`));
|
|
2089
|
+
console.log(chalk_1.default.gray(` Project: ${(project === null || project === void 0 ? void 0 : project.name) || 'Default'}`));
|
|
2090
|
+
console.log(chalk_1.default.gray(` Source: ${filePath}`));
|
|
2091
|
+
if (opts.agent)
|
|
2092
|
+
console.log(chalk_1.default.gray(` Agent: ${opts.agent}`));
|
|
2093
|
+
console.log();
|
|
2094
|
+
});
|
|
2095
|
+
// ─── Chain Command ──────────────────────────────────────────────────────────
|
|
2096
|
+
// TRIZ #40 Composite Materials — skills compose into pipelines
|
|
2097
|
+
program
|
|
2098
|
+
.command('chain <cmd> [args...]')
|
|
2099
|
+
.alias('ch')
|
|
2100
|
+
.description('Skill chain pipelines (list|info|start|status|advance|skip|abort|auto|history)')
|
|
2101
|
+
.option('-p, --project <name>', 'Project name or ID')
|
|
2102
|
+
.option('--agent <agent>', 'Agent name', 'antigravity')
|
|
2103
|
+
.action((cmd, args, opts) => {
|
|
2104
|
+
switch (cmd) {
|
|
2105
|
+
case 'list':
|
|
2106
|
+
case 'ls':
|
|
2107
|
+
chainList();
|
|
2108
|
+
break;
|
|
2109
|
+
case 'info':
|
|
2110
|
+
chainInfo(args[0]);
|
|
2111
|
+
break;
|
|
2112
|
+
case 'start':
|
|
2113
|
+
chainStart(args[0], args.slice(1).join(' '), opts);
|
|
2114
|
+
break;
|
|
2115
|
+
case 'status':
|
|
2116
|
+
case 'st':
|
|
2117
|
+
chainStatus(args[0]);
|
|
2118
|
+
break;
|
|
2119
|
+
case 'advance':
|
|
2120
|
+
case 'next':
|
|
2121
|
+
chainAdvance(args[0], args.slice(1).join(' '));
|
|
2122
|
+
break;
|
|
2123
|
+
case 'skip':
|
|
2124
|
+
chainSkip(args[0], args.slice(1).join(' '));
|
|
2125
|
+
break;
|
|
2126
|
+
case 'abort':
|
|
2127
|
+
chainAbort(args[0], args.slice(1).join(' '));
|
|
2128
|
+
break;
|
|
2129
|
+
case 'auto':
|
|
2130
|
+
chainAuto(args.join(' '), opts);
|
|
2131
|
+
break;
|
|
2132
|
+
case 'history':
|
|
2133
|
+
case 'hist':
|
|
2134
|
+
chainHistory();
|
|
2135
|
+
break;
|
|
2136
|
+
default:
|
|
2137
|
+
console.log(chalk_1.default.red(`Unknown: ${cmd}`));
|
|
2138
|
+
console.log(chalk_1.default.gray('Available: list, info, start, status, advance, skip, abort, auto, history'));
|
|
2139
|
+
}
|
|
2140
|
+
});
|
|
2141
|
+
function chainList() {
|
|
2142
|
+
const chains = (0, skill_chain_1.listChains)();
|
|
2143
|
+
console.log(chalk_1.default.cyan('\n🔗 Available Skill Chains\n'));
|
|
2144
|
+
for (const chain of chains) {
|
|
2145
|
+
console.log(` ${chain.icon} ${chalk_1.default.white(padRight(chain.name, 24))} ${chalk_1.default.gray(chain.description)}`);
|
|
2146
|
+
console.log(chalk_1.default.gray(` ID: ${chain.id} | Steps: ${chain.steps.length} | Triggers: ${chain.triggers.slice(0, 4).join(', ')}...`));
|
|
2147
|
+
console.log();
|
|
2148
|
+
}
|
|
2149
|
+
console.log(chalk_1.default.gray(` Total: ${chains.length} chains\n`));
|
|
2150
|
+
console.log(chalk_1.default.cyan('💡 Quick start:'));
|
|
2151
|
+
console.log(chalk_1.default.gray(' cm chain auto "Build user authentication" # Auto-detect chain'));
|
|
2152
|
+
console.log(chalk_1.default.gray(' cm chain start feature-development "My task" # Start specific chain'));
|
|
2153
|
+
console.log();
|
|
2154
|
+
}
|
|
2155
|
+
function chainInfo(chainId) {
|
|
2156
|
+
if (!chainId) {
|
|
2157
|
+
console.log(chalk_1.default.red('❌ Usage: cm chain info <chain-id>'));
|
|
2158
|
+
return;
|
|
2159
|
+
}
|
|
2160
|
+
const chain = (0, skill_chain_1.findChain)(chainId);
|
|
2161
|
+
if (!chain) {
|
|
2162
|
+
console.log(chalk_1.default.red(`❌ Chain not found: ${chainId}`));
|
|
2163
|
+
console.log(chalk_1.default.gray(' Use "cm chain list" to see available chains.'));
|
|
2164
|
+
return;
|
|
2165
|
+
}
|
|
2166
|
+
console.log(chalk_1.default.cyan(`\n${chain.icon} Chain: ${chain.name}\n`));
|
|
2167
|
+
console.log(` ${chalk_1.default.white('ID:')} ${chain.id}`);
|
|
2168
|
+
console.log(` ${chalk_1.default.white('Description:')} ${chain.description}`);
|
|
2169
|
+
console.log(` ${chalk_1.default.white('Steps:')} ${chain.steps.length}`);
|
|
2170
|
+
console.log(` ${chalk_1.default.white('Triggers:')} ${chain.triggers.join(', ')}`);
|
|
2171
|
+
console.log();
|
|
2172
|
+
console.log(chalk_1.default.white(' Pipeline:'));
|
|
2173
|
+
for (let i = 0; i < chain.steps.length; i++) {
|
|
2174
|
+
const step = chain.steps[i];
|
|
2175
|
+
const condBadge = step.condition === 'always' ? chalk_1.default.green('ALWAYS') : step.condition === 'if-complex' ? chalk_1.default.yellow('IF-COMPLEX') : chalk_1.default.blue('IF-READY');
|
|
2176
|
+
const optBadge = step.optional ? chalk_1.default.gray(' (optional)') : '';
|
|
2177
|
+
const connector = i < chain.steps.length - 1 ? ' │' : ' ';
|
|
2178
|
+
console.log(` ${chalk_1.default.cyan(`${i + 1}.`)} ${padRight(step.skill, 24)} ${condBadge}${optBadge}`);
|
|
2179
|
+
console.log(chalk_1.default.gray(` ${connector} ${step.description}`));
|
|
2180
|
+
if (i < chain.steps.length - 1)
|
|
2181
|
+
console.log(chalk_1.default.gray(' │'));
|
|
2182
|
+
}
|
|
2183
|
+
console.log();
|
|
2184
|
+
}
|
|
2185
|
+
function chainStart(chainId, taskTitle, opts) {
|
|
2186
|
+
var _a, _b, _c;
|
|
2187
|
+
if (!chainId) {
|
|
2188
|
+
console.log(chalk_1.default.red('❌ Usage: cm chain start <chain-id> "Task title"'));
|
|
2189
|
+
return;
|
|
2190
|
+
}
|
|
2191
|
+
if (!taskTitle) {
|
|
2192
|
+
console.log(chalk_1.default.red('❌ Task title required. Usage: cm chain start <chain-id> "My task"'));
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
const chain = (0, skill_chain_1.findChain)(chainId);
|
|
2196
|
+
if (!chain) {
|
|
2197
|
+
console.log(chalk_1.default.red(`❌ Chain not found: ${chainId}`));
|
|
2198
|
+
return;
|
|
2199
|
+
}
|
|
2200
|
+
const data = (0, data_1.loadData)();
|
|
2201
|
+
let projectId;
|
|
2202
|
+
if (opts.project) {
|
|
2203
|
+
const project = (0, data_1.findProjectByNameOrId)(data, opts.project);
|
|
2204
|
+
if (!project) {
|
|
2205
|
+
console.log(chalk_1.default.red(`❌ Project not found: ${opts.project}`));
|
|
2206
|
+
return;
|
|
2207
|
+
}
|
|
2208
|
+
projectId = project.id;
|
|
2209
|
+
}
|
|
2210
|
+
else if (data.projects.length > 0) {
|
|
2211
|
+
projectId = data.projects[0].id;
|
|
2212
|
+
}
|
|
2213
|
+
else {
|
|
2214
|
+
console.log(chalk_1.default.red('❌ No projects. Create one first: cm init'));
|
|
2215
|
+
return;
|
|
2216
|
+
}
|
|
2217
|
+
const agent = opts.agent || 'antigravity';
|
|
2218
|
+
const execution = (0, skill_chain_1.createChainExecution)(chain, projectId, taskTitle, agent);
|
|
2219
|
+
data.chainExecutions.push(execution);
|
|
2220
|
+
// Create a task linked to this chain
|
|
2221
|
+
const now = new Date().toISOString();
|
|
2222
|
+
const task = {
|
|
2223
|
+
id: crypto_1.default.randomUUID(), projectId, title: taskTitle, description: `Chain: ${chain.name}`,
|
|
2224
|
+
column: 'in-progress', order: 0, priority: 'medium', agent, skill: ((_a = execution.steps[0]) === null || _a === void 0 ? void 0 : _a.skill) || '',
|
|
2225
|
+
createdAt: now, updatedAt: now, chainId: chain.id, chainExecutionId: execution.id,
|
|
2226
|
+
};
|
|
2227
|
+
data.tasks.push(task);
|
|
2228
|
+
(0, data_1.logActivity)(data, 'chain_started', `Chain "${chain.name}" started: "${taskTitle}"`, projectId, agent, {
|
|
2229
|
+
chainId: chain.id, executionId: execution.id, steps: chain.steps.length,
|
|
2230
|
+
});
|
|
2231
|
+
(0, data_1.saveData)(data);
|
|
2232
|
+
const project = data.projects.find(p => p.id === projectId);
|
|
2233
|
+
console.log(chalk_1.default.green(`\n🔗 Chain started!`));
|
|
2234
|
+
console.log(chalk_1.default.gray(` Chain: ${chain.icon} ${chain.name}`));
|
|
2235
|
+
console.log(chalk_1.default.gray(` Task: ${taskTitle}`));
|
|
2236
|
+
console.log(chalk_1.default.gray(` Project: ${(project === null || project === void 0 ? void 0 : project.name) || '—'}`));
|
|
2237
|
+
console.log(chalk_1.default.gray(` Agent: ${agent}`));
|
|
2238
|
+
console.log(chalk_1.default.gray(` Steps: ${chain.steps.length}`));
|
|
2239
|
+
console.log(chalk_1.default.gray(` Exec ID: ${(0, data_1.shortId)(execution.id)}`));
|
|
2240
|
+
console.log();
|
|
2241
|
+
console.log(chalk_1.default.cyan(` ▶ Current step: ${(_b = execution.steps[0]) === null || _b === void 0 ? void 0 : _b.skill} — ${(_c = execution.steps[0]) === null || _c === void 0 ? void 0 : _c.description}`));
|
|
2242
|
+
console.log();
|
|
2243
|
+
console.log(chalk_1.default.gray(` Next: cm chain advance ${(0, data_1.shortId)(execution.id)} "output summary"`));
|
|
2244
|
+
console.log();
|
|
2245
|
+
}
|
|
2246
|
+
function chainStatus(execIdPrefix) {
|
|
2247
|
+
const data = (0, data_1.loadData)();
|
|
2248
|
+
if (execIdPrefix) {
|
|
2249
|
+
// Show specific execution
|
|
2250
|
+
const exec = data.chainExecutions.find(e => e.id === execIdPrefix || e.id.startsWith(execIdPrefix));
|
|
2251
|
+
if (!exec) {
|
|
2252
|
+
console.log(chalk_1.default.red(`❌ Chain execution not found: ${execIdPrefix}`));
|
|
2253
|
+
return;
|
|
2254
|
+
}
|
|
2255
|
+
console.log();
|
|
2256
|
+
console.log((0, skill_chain_1.formatChainProgress)(exec));
|
|
2257
|
+
console.log();
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2260
|
+
// Show all active executions
|
|
2261
|
+
const active = data.chainExecutions.filter(e => e.status === 'running' || e.status === 'paused');
|
|
2262
|
+
if (active.length === 0) {
|
|
2263
|
+
console.log(chalk_1.default.gray('\n No active chain executions.'));
|
|
2264
|
+
console.log(chalk_1.default.gray(' Start one with: cm chain auto "task description"\n'));
|
|
2265
|
+
return;
|
|
2266
|
+
}
|
|
2267
|
+
console.log(chalk_1.default.cyan(`\n🔗 Active Chains (${active.length})\n`));
|
|
2268
|
+
for (const exec of active) {
|
|
2269
|
+
const project = data.projects.find(p => p.id === exec.projectId);
|
|
2270
|
+
const currentSkill = (0, skill_chain_1.getCurrentSkill)(exec);
|
|
2271
|
+
const progressBar = (0, skill_chain_1.formatChainProgressBar)(exec);
|
|
2272
|
+
console.log(` ${chalk_1.default.white(exec.chainName)} — "${exec.taskTitle}"`);
|
|
2273
|
+
console.log(chalk_1.default.gray(` ${progressBar} | Step ${exec.currentStepIndex + 1}/${exec.steps.length}: ${currentSkill || 'done'}`));
|
|
2274
|
+
console.log(chalk_1.default.gray(` ID: ${(0, data_1.shortId)(exec.id)} | Agent: ${exec.agent} | Project: ${(project === null || project === void 0 ? void 0 : project.name) || '—'}`));
|
|
2275
|
+
console.log();
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
function chainAdvance(execIdPrefix, output) {
|
|
2279
|
+
if (!execIdPrefix) {
|
|
2280
|
+
console.log(chalk_1.default.red('❌ Usage: cm chain advance <exec-id> ["output summary"]'));
|
|
2281
|
+
return;
|
|
2282
|
+
}
|
|
2283
|
+
const data = (0, data_1.loadData)();
|
|
2284
|
+
const exec = data.chainExecutions.find(e => e.id === execIdPrefix || e.id.startsWith(execIdPrefix));
|
|
2285
|
+
if (!exec) {
|
|
2286
|
+
console.log(chalk_1.default.red(`❌ Chain execution not found: ${execIdPrefix}`));
|
|
2287
|
+
return;
|
|
2288
|
+
}
|
|
2289
|
+
if (exec.status !== 'running') {
|
|
2290
|
+
console.log(chalk_1.default.yellow(`⚠️ Chain is ${exec.status}, cannot advance.`));
|
|
2291
|
+
return;
|
|
2292
|
+
}
|
|
2293
|
+
const completedStep = exec.steps[exec.currentStepIndex];
|
|
2294
|
+
const result = (0, skill_chain_1.advanceChain)(exec, output);
|
|
2295
|
+
// Update linked task's current skill
|
|
2296
|
+
const linkedTask = data.tasks.find(t => t.chainExecutionId === exec.id);
|
|
2297
|
+
if (linkedTask && result.nextSkill) {
|
|
2298
|
+
linkedTask.skill = result.nextSkill;
|
|
2299
|
+
linkedTask.updatedAt = new Date().toISOString();
|
|
2300
|
+
}
|
|
2301
|
+
if (result.completed) {
|
|
2302
|
+
if (linkedTask) {
|
|
2303
|
+
linkedTask.column = 'review';
|
|
2304
|
+
linkedTask.updatedAt = new Date().toISOString();
|
|
2305
|
+
}
|
|
2306
|
+
(0, data_1.logActivity)(data, 'chain_completed', `Chain "${exec.chainName}" completed: "${exec.taskTitle}"`, exec.projectId, exec.agent, {
|
|
2307
|
+
executionId: exec.id, totalSteps: exec.steps.length,
|
|
2308
|
+
});
|
|
2309
|
+
(0, data_1.saveData)(data);
|
|
2310
|
+
console.log(chalk_1.default.green(`\n✅ Chain completed! All ${exec.steps.length} steps done.`));
|
|
2311
|
+
console.log(chalk_1.default.gray(` Chain: ${exec.chainName}`));
|
|
2312
|
+
console.log(chalk_1.default.gray(` Task: ${exec.taskTitle}`));
|
|
2313
|
+
console.log();
|
|
2314
|
+
}
|
|
2315
|
+
else {
|
|
2316
|
+
(0, data_1.logActivity)(data, 'chain_step_completed', `Chain step completed: ${completedStep === null || completedStep === void 0 ? void 0 : completedStep.skill} → next: ${result.nextSkill}`, exec.projectId, exec.agent, {
|
|
2317
|
+
executionId: exec.id, completedSkill: completedStep === null || completedStep === void 0 ? void 0 : completedStep.skill, nextSkill: result.nextSkill,
|
|
2318
|
+
});
|
|
2319
|
+
(0, data_1.saveData)(data);
|
|
2320
|
+
const nextStep = exec.steps[exec.currentStepIndex];
|
|
2321
|
+
console.log(chalk_1.default.green(`\n✅ Step completed: ${completedStep === null || completedStep === void 0 ? void 0 : completedStep.skill}`));
|
|
2322
|
+
console.log(chalk_1.default.cyan(` ▶ Next step: ${result.nextSkill} — ${nextStep === null || nextStep === void 0 ? void 0 : nextStep.description}`));
|
|
2323
|
+
console.log(chalk_1.default.gray(` Progress: ${(0, skill_chain_1.formatChainProgressBar)(exec)}`));
|
|
2324
|
+
console.log();
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
function chainSkip(execIdPrefix, reason) {
|
|
2328
|
+
if (!execIdPrefix) {
|
|
2329
|
+
console.log(chalk_1.default.red('❌ Usage: cm chain skip <exec-id> ["reason"]'));
|
|
2330
|
+
return;
|
|
2331
|
+
}
|
|
2332
|
+
const data = (0, data_1.loadData)();
|
|
2333
|
+
const exec = data.chainExecutions.find(e => e.id === execIdPrefix || e.id.startsWith(execIdPrefix));
|
|
2334
|
+
if (!exec) {
|
|
2335
|
+
console.log(chalk_1.default.red(`❌ Chain execution not found: ${execIdPrefix}`));
|
|
2336
|
+
return;
|
|
2337
|
+
}
|
|
2338
|
+
if (exec.status !== 'running') {
|
|
2339
|
+
console.log(chalk_1.default.yellow(`⚠️ Chain is ${exec.status}, cannot skip.`));
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
2342
|
+
const skippedStep = exec.steps[exec.currentStepIndex];
|
|
2343
|
+
const result = (0, skill_chain_1.skipChainStep)(exec, reason);
|
|
2344
|
+
(0, data_1.saveData)(data);
|
|
2345
|
+
console.log(chalk_1.default.yellow(` ⏭️ Skipped: ${skippedStep === null || skippedStep === void 0 ? void 0 : skippedStep.skill}`));
|
|
2346
|
+
if (result.completed) {
|
|
2347
|
+
console.log(chalk_1.default.green(` ✅ Chain completed!`));
|
|
2348
|
+
}
|
|
2349
|
+
else {
|
|
2350
|
+
console.log(chalk_1.default.cyan(` ▶ Next: ${result.nextSkill}`));
|
|
2351
|
+
}
|
|
2352
|
+
console.log();
|
|
2353
|
+
}
|
|
2354
|
+
function chainAbort(execIdPrefix, reason) {
|
|
2355
|
+
if (!execIdPrefix) {
|
|
2356
|
+
console.log(chalk_1.default.red('❌ Usage: cm chain abort <exec-id> ["reason"]'));
|
|
2357
|
+
return;
|
|
2358
|
+
}
|
|
2359
|
+
const data = (0, data_1.loadData)();
|
|
2360
|
+
const exec = data.chainExecutions.find(e => e.id === execIdPrefix || e.id.startsWith(execIdPrefix));
|
|
2361
|
+
if (!exec) {
|
|
2362
|
+
console.log(chalk_1.default.red(`❌ Chain execution not found: ${execIdPrefix}`));
|
|
2363
|
+
return;
|
|
2364
|
+
}
|
|
2365
|
+
if (exec.status !== 'running' && exec.status !== 'paused') {
|
|
2366
|
+
console.log(chalk_1.default.yellow(`⚠️ Chain already ${exec.status}.`));
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
(0, skill_chain_1.abortChain)(exec, reason);
|
|
2370
|
+
(0, data_1.logActivity)(data, 'chain_aborted', `Chain "${exec.chainName}" aborted: ${reason || 'no reason'}`, exec.projectId, exec.agent, {
|
|
2371
|
+
executionId: exec.id,
|
|
2372
|
+
});
|
|
2373
|
+
(0, data_1.saveData)(data);
|
|
2374
|
+
console.log(chalk_1.default.red(`\n🛑 Chain aborted: ${exec.chainName}`));
|
|
2375
|
+
if (reason)
|
|
2376
|
+
console.log(chalk_1.default.gray(` Reason: ${reason}`));
|
|
2377
|
+
console.log();
|
|
2378
|
+
}
|
|
2379
|
+
function chainAuto(taskTitle, opts) {
|
|
2380
|
+
if (!taskTitle) {
|
|
2381
|
+
console.log(chalk_1.default.red('❌ Usage: cm chain auto "task description"'));
|
|
2382
|
+
console.log(chalk_1.default.gray(' Example: cm chain auto "Build user authentication"'));
|
|
2383
|
+
return;
|
|
2384
|
+
}
|
|
2385
|
+
const chain = (0, skill_chain_1.matchChain)(taskTitle);
|
|
2386
|
+
if (!chain) {
|
|
2387
|
+
console.log(chalk_1.default.yellow(`\n⚠️ No matching chain found for: "${taskTitle}"`));
|
|
2388
|
+
console.log(chalk_1.default.gray(' Available chains:'));
|
|
2389
|
+
for (const c of (0, skill_chain_1.listChains)()) {
|
|
2390
|
+
console.log(chalk_1.default.gray(` ${c.icon} ${c.id}: ${c.triggers.slice(0, 3).join(', ')}...`));
|
|
2391
|
+
}
|
|
2392
|
+
console.log(chalk_1.default.gray('\n Use "cm chain start <chain-id> <title>" to start manually.'));
|
|
2393
|
+
return;
|
|
2394
|
+
}
|
|
2395
|
+
console.log(chalk_1.default.cyan(`\n🤖 Auto-detected chain: ${chain.icon} ${chain.name}`));
|
|
2396
|
+
console.log(chalk_1.default.gray(` Matched from: "${taskTitle}"`));
|
|
2397
|
+
console.log();
|
|
2398
|
+
// Delegate to chainStart
|
|
2399
|
+
chainStart(chain.id, taskTitle, opts);
|
|
2400
|
+
}
|
|
2401
|
+
function chainHistory() {
|
|
2402
|
+
const data = (0, data_1.loadData)();
|
|
2403
|
+
const execs = data.chainExecutions;
|
|
2404
|
+
if (execs.length === 0) {
|
|
2405
|
+
console.log(chalk_1.default.gray('\n No chain executions yet.\n'));
|
|
2406
|
+
return;
|
|
2407
|
+
}
|
|
2408
|
+
const STATUS_ICONS = {
|
|
2409
|
+
pending: '⚪', running: '🔵', paused: '⏸️', completed: '✅', failed: '❌', aborted: '🛑',
|
|
2410
|
+
};
|
|
2411
|
+
console.log(chalk_1.default.cyan(`\n🔗 Chain History (${execs.length})\n`));
|
|
2412
|
+
console.log(chalk_1.default.gray(' ' + padRight('Status', 8) + padRight('Chain', 24) + padRight('Task', 30) + padRight('Progress', 14) + 'Time'));
|
|
2413
|
+
console.log(chalk_1.default.gray(' ' + '─'.repeat(86)));
|
|
2414
|
+
for (const exec of execs.slice(0, 20)) {
|
|
2415
|
+
const icon = STATUS_ICONS[exec.status] || '❓';
|
|
2416
|
+
const completed = exec.steps.filter(s => s.status === 'completed' || s.status === 'skipped').length;
|
|
2417
|
+
const progress = `${completed}/${exec.steps.length} steps`;
|
|
2418
|
+
const time = formatTimeAgoCli(exec.startedAt);
|
|
2419
|
+
console.log(' ' + padRight(icon, 8) + padRight(exec.chainName.substring(0, 22), 24) + padRight(exec.taskTitle.substring(0, 28), 30) + chalk_1.default.gray(padRight(progress, 14)) + chalk_1.default.gray(time));
|
|
2420
|
+
}
|
|
2421
|
+
console.log();
|
|
2422
|
+
}
|
|
2423
|
+
// ─── Parse ──────────────────────────────────────────────────────────────────
|
|
2424
|
+
// Auto-start dashboard in background for project commands
|
|
2425
|
+
// Skip for: add, list, install, help, version, --help, -v
|
|
2426
|
+
const SKIP_DASHBOARD_CMDS = new Set(['add', 'list', 'ls', 'install', 'help', '--help', '-h', '-v', '--version', 'version']);
|
|
2427
|
+
const firstArg = process.argv[2] || '';
|
|
2428
|
+
if (!SKIP_DASHBOARD_CMDS.has(firstArg) && firstArg !== '' && !firstArg.startsWith('-')) {
|
|
2429
|
+
if (!isDashboardRunning()) {
|
|
2430
|
+
// Silent background start — no banner, just ensure it's running
|
|
2431
|
+
(0, dashboard_1.launchDashboard)(data_1.DEFAULT_PORT, true);
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
program.parse(process.argv);
|