codymaster 4.6.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -8
- package/README.md +192 -95
- package/dist/advisory-handoff.js +89 -0
- package/dist/advisory-report.js +105 -0
- package/dist/browse-server.js +251 -0
- package/dist/cli/command-registry.js +34 -0
- package/dist/cli/commands/agent.js +120 -0
- package/dist/cli/commands/bench.js +69 -0
- package/dist/cli/commands/brain.js +108 -0
- package/dist/cli/commands/dashboard.js +93 -0
- package/dist/cli/commands/design-studio.js +111 -0
- package/dist/cli/commands/distro.js +25 -0
- package/dist/cli/commands/engineering.js +596 -0
- package/dist/cli/commands/evolve.js +123 -0
- package/dist/cli/commands/mcp-serve.js +104 -0
- package/dist/cli/commands/project.js +324 -0
- package/dist/cli/commands/skill-chain.js +269 -0
- package/dist/cli/commands/system.js +89 -0
- package/dist/cli/commands/task.js +254 -0
- package/dist/cli/update-check.js +83 -0
- package/dist/cm-config.js +92 -0
- package/dist/cm-suggest.js +77 -0
- package/dist/codybench/judges/automated.js +31 -0
- package/dist/codybench/runners/claude-code.js +32 -0
- package/dist/codybench/suites/memory-retention.js +85 -0
- package/dist/codybench/suites/tdd-regression.js +35 -0
- package/dist/codybench/suites/token-efficiency.js +55 -0
- package/dist/codybench/types.js +2 -0
- package/dist/context-db.js +157 -0
- package/dist/continuity.js +2 -6
- package/dist/distro-validate.js +54 -0
- package/dist/execution-analyzer.js +138 -0
- package/dist/guardian-core.js +74 -0
- package/dist/index.js +36 -2759
- package/dist/indexer/skills-lib.js +533 -0
- package/dist/indexer/skills-map.js +1374 -0
- package/dist/indexer/skills.js +16 -0
- package/dist/learning-promoter.js +246 -0
- package/dist/mcp-context-server.js +289 -1
- package/dist/mcp-skills-tools.js +81 -0
- package/dist/retro-summary.js +70 -0
- package/dist/second-opinion-providers.js +79 -0
- package/dist/skill-chain.js +63 -1
- package/dist/skill-evolver.js +456 -0
- package/dist/skill-execution-cache.js +254 -0
- package/dist/smart-brain-router.js +184 -0
- package/dist/sprint-pipeline.js +228 -0
- package/dist/storage-backend.js +14 -67
- package/dist/token-budget.js +88 -0
- package/dist/utils/cli-utils.js +76 -0
- package/dist/utils/skill-utils.js +32 -0
- package/package.json +17 -7
- package/scripts/build-skills.mjs +51 -0
- package/scripts/gate-0-repo-hygiene.js +75 -0
- package/scripts/postinstall.js +34 -28
- package/scripts/security-scan.js +1 -1
- package/scripts/validate-skills.mjs +42 -0
- package/skills/CLAUDE.md +2 -7
- package/skills/_shared/helpers.md +2 -8
- package/skills/cm-ads-tracker/SKILL.md +3 -6
- package/skills/cm-browse/SKILL.md +34 -0
- package/skills/cm-conductor-worktrees/SKILL.md +28 -0
- package/skills/cm-content-factory/SKILL.md +1 -1
- package/skills/cm-content-factory/landing/docs/content/changelog.md +36 -0
- package/skills/cm-content-factory/landing/docs/content/deployment.md +46 -0
- package/skills/cm-content-factory/landing/docs/content/execution-flow.md +67 -0
- package/skills/cm-content-factory/landing/docs/content/memory-system.md +38 -0
- package/skills/cm-content-factory/landing/docs/content/openspace.md +27 -0
- package/skills/cm-content-factory/landing/docs/content/use-cases.md +26 -0
- package/skills/cm-content-factory/landing/docs/content/v5-intro.md +28 -0
- package/skills/cm-content-factory/landing/docs/index.html +240 -0
- package/skills/cm-content-factory/landing/index.html +100 -100
- package/skills/cm-content-factory/landing/script.js +42 -0
- package/skills/cm-content-factory/landing/translations.js +400 -400
- package/skills/cm-continuity/SKILL.md +32 -33
- package/skills/cm-design-studio/SKILL.md +34 -0
- package/skills/cm-ecosystem-roadmap/SKILL.md +15 -0
- package/skills/cm-engineering-meta/SKILL.md +73 -0
- package/skills/cm-growth-hacking/SKILL.md +1 -12
- package/skills/cm-guardian-runtime/SKILL.md +26 -0
- package/skills/cm-mcp-engineering/SKILL.md +22 -0
- package/skills/cm-notebooklm/SKILL.md +1 -17
- package/skills/cm-post-deploy-canary/SKILL.md +22 -0
- package/skills/cm-project-bootstrap/SKILL.md +11 -0
- package/skills/cm-qa-visual-cli/SKILL.md +22 -0
- package/skills/cm-retro-cli/SKILL.md +23 -0
- package/skills/cm-second-opinion-cli/SKILL.md +23 -0
- package/skills/cm-secret-shield/SKILL.md +2 -2
- package/skills/cm-security-gate/SKILL.md +1 -0
- package/skills/cm-skill-chain/SKILL.md +25 -4
- package/skills/cm-skill-evolution/SKILL.md +83 -0
- package/skills/cm-skill-health/SKILL.md +83 -0
- package/skills/cm-skill-index/SKILL.md +11 -3
- package/skills/cm-skill-search/SKILL.md +49 -0
- package/skills/cm-skill-share/SKILL.md +58 -0
- package/skills/cm-sprint-bus/SKILL.md +33 -0
- package/skills/cm-start/SKILL.md +0 -10
- package/skills/cm-tdd/SKILL.md +59 -72
- package/skills/profiles/README.md +21 -0
- package/skills/profiles/core.txt +23 -0
- package/skills/profiles/design.txt +6 -0
- package/skills/profiles/full.txt +62 -0
- package/skills/profiles/growth.txt +10 -0
- package/skills/profiles/knowledge.txt +7 -0
- package/install.sh +0 -901
- package/scripts/test-gemini.js +0 -13
- package/skills/cm-frappe-agent/SKILL.md +0 -134
- package/skills/cm-frappe-agent/agents/doctype-architect.md +0 -596
- package/skills/cm-frappe-agent/agents/erpnext-customizer.md +0 -643
- package/skills/cm-frappe-agent/agents/frappe-backend.md +0 -814
- package/skills/cm-frappe-agent/agents/frappe-custom-frontend.md +0 -557
- package/skills/cm-frappe-agent/agents/frappe-debugger.md +0 -625
- package/skills/cm-frappe-agent/agents/frappe-fixer.md +0 -275
- package/skills/cm-frappe-agent/agents/frappe-frontend.md +0 -660
- package/skills/cm-frappe-agent/agents/frappe-installer.md +0 -158
- package/skills/cm-frappe-agent/agents/frappe-performance.md +0 -307
- package/skills/cm-frappe-agent/agents/frappe-planner.md +0 -419
- package/skills/cm-frappe-agent/agents/frappe-remote-ops.md +0 -153
- package/skills/cm-frappe-agent/agents/github-workflow.md +0 -286
- package/skills/cm-frappe-agent/commands/frappe-app.md +0 -351
- package/skills/cm-frappe-agent/commands/frappe-backend.md +0 -162
- package/skills/cm-frappe-agent/commands/frappe-bench.md +0 -254
- package/skills/cm-frappe-agent/commands/frappe-debug.md +0 -263
- package/skills/cm-frappe-agent/commands/frappe-doctype-create.md +0 -272
- package/skills/cm-frappe-agent/commands/frappe-doctype-field.md +0 -310
- package/skills/cm-frappe-agent/commands/frappe-erpnext.md +0 -210
- package/skills/cm-frappe-agent/commands/frappe-fix.md +0 -59
- package/skills/cm-frappe-agent/commands/frappe-frontend.md +0 -210
- package/skills/cm-frappe-agent/commands/frappe-fullstack.md +0 -243
- package/skills/cm-frappe-agent/commands/frappe-github.md +0 -57
- package/skills/cm-frappe-agent/commands/frappe-install.md +0 -52
- package/skills/cm-frappe-agent/commands/frappe-plan.md +0 -442
- package/skills/cm-frappe-agent/commands/frappe-remote.md +0 -58
- package/skills/cm-frappe-agent/commands/frappe-test.md +0 -356
- package/skills/cm-frappe-agent/docs/README.md +0 -51
- package/skills/cm-frappe-agent/docs/agents-catalog.md +0 -113
- package/skills/cm-frappe-agent/docs/architecture.md +0 -149
- package/skills/cm-frappe-agent/docs/commands-catalog.md +0 -82
- package/skills/cm-frappe-agent/docs/resources-catalog.md +0 -66
- package/skills/cm-frappe-agent/docs/sitemap-urls.txt +0 -52
- package/skills/cm-frappe-agent/docs/sitemap.md +0 -81
- package/skills/cm-frappe-agent/docs/sop/user-guide.md +0 -178
- package/skills/cm-frappe-agent/docs/sop/vibe-coding-guide.md +0 -122
- package/skills/cm-frappe-agent/resources/7-layer-architecture.md +0 -985
- package/skills/cm-frappe-agent/resources/bench_commands.md +0 -73
- package/skills/cm-frappe-agent/resources/code-patterns-guide.md +0 -948
- package/skills/cm-frappe-agent/resources/common_pitfalls.md +0 -266
- package/skills/cm-frappe-agent/resources/doctype-registry.md +0 -158
- package/skills/cm-frappe-agent/resources/installation-guide.md +0 -289
- package/skills/cm-frappe-agent/resources/rest-api-patterns.md +0 -182
- package/skills/cm-frappe-agent/resources/scaffold_checklist.md +0 -82
- package/skills/cm-frappe-agent/resources/upgrade_patterns.md +0 -113
- package/skills/cm-frappe-agent/resources/web-form-patterns.md +0 -252
- package/skills/cm-frappe-agent/skills/bench-commands/SKILL.md +0 -621
- package/skills/cm-frappe-agent/skills/client-scripts/SKILL.md +0 -642
- package/skills/cm-frappe-agent/skills/doctype-patterns/SKILL.md +0 -576
- package/skills/cm-frappe-agent/skills/frappe-api/SKILL.md +0 -740
- package/skills/cm-frappe-agent/skills/remote-operations/SKILL.md +0 -47
- package/skills/cm-frappe-agent/skills/server-scripts/SKILL.md +0 -608
- package/skills/cm-frappe-agent/skills/web-forms/SKILL.md +0 -46
- package/skills/frappe-app-builder.zip +0 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SkillExecutionCache = void 0;
|
|
7
|
+
exports.formatCacheStats = formatCacheStats;
|
|
8
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
// ─── Schema ─────────────────────────────────────────────────────────────────
|
|
11
|
+
const CACHE_SCHEMA = `
|
|
12
|
+
CREATE TABLE IF NOT EXISTS skill_cache (
|
|
13
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
14
|
+
task_pattern TEXT NOT NULL,
|
|
15
|
+
skill_chain_json TEXT NOT NULL,
|
|
16
|
+
effectiveness REAL NOT NULL DEFAULT 0,
|
|
17
|
+
token_used INTEGER NOT NULL DEFAULT 0,
|
|
18
|
+
hit_count INTEGER NOT NULL DEFAULT 0,
|
|
19
|
+
last_hit TEXT NOT NULL,
|
|
20
|
+
created_at TEXT NOT NULL
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
CREATE INDEX IF NOT EXISTS idx_skill_cache_pattern ON skill_cache(task_pattern);
|
|
24
|
+
|
|
25
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS skill_cache_fts USING fts5(
|
|
26
|
+
task_pattern,
|
|
27
|
+
content=skill_cache, content_rowid=rowid
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
CREATE TRIGGER IF NOT EXISTS skill_cache_ai AFTER INSERT ON skill_cache BEGIN
|
|
31
|
+
INSERT INTO skill_cache_fts(rowid, task_pattern)
|
|
32
|
+
VALUES (new.rowid, new.task_pattern);
|
|
33
|
+
END;
|
|
34
|
+
|
|
35
|
+
CREATE TRIGGER IF NOT EXISTS skill_cache_ad AFTER DELETE ON skill_cache BEGIN
|
|
36
|
+
INSERT INTO skill_cache_fts(skill_cache_fts, rowid, task_pattern)
|
|
37
|
+
VALUES('delete', old.rowid, old.task_pattern);
|
|
38
|
+
END;
|
|
39
|
+
`;
|
|
40
|
+
// ─── DB Cache (share connection with context-db) ─────────────────────────────
|
|
41
|
+
const dbCache = new Map();
|
|
42
|
+
function getDb(dbPath) {
|
|
43
|
+
if (dbCache.has(dbPath))
|
|
44
|
+
return dbCache.get(dbPath);
|
|
45
|
+
const db = new better_sqlite3_1.default(dbPath);
|
|
46
|
+
db.pragma('journal_mode = WAL');
|
|
47
|
+
dbCache.set(dbPath, db);
|
|
48
|
+
return db;
|
|
49
|
+
}
|
|
50
|
+
// ─── Skill Execution Cache ───────────────────────────────────────────────────
|
|
51
|
+
// TRIZ #35 Parameter Changes — reuse successful execution patterns
|
|
52
|
+
/**
|
|
53
|
+
* SkillExecutionCache — OpenSpace-inspired warm cache for skill chains.
|
|
54
|
+
*
|
|
55
|
+
* When a task succeeds with a specific skill chain, we cache the pattern.
|
|
56
|
+
* For similar future tasks, skip BM25 ranking entirely and reuse the chain.
|
|
57
|
+
*
|
|
58
|
+
* Token savings: ~2000 tokens/task (skip ranking + LLM selection).
|
|
59
|
+
*/
|
|
60
|
+
class SkillExecutionCache {
|
|
61
|
+
constructor(projectPath) {
|
|
62
|
+
this.dbPath = path_1.default.join(projectPath, '.cm', 'context.db');
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Initialize cache tables. Call once before use.
|
|
66
|
+
*/
|
|
67
|
+
initialize() {
|
|
68
|
+
const db = getDb(this.dbPath);
|
|
69
|
+
db.exec(CACHE_SCHEMA);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Cache a successful task execution pattern.
|
|
73
|
+
* Only caches executions with effectiveness >= 0.7.
|
|
74
|
+
*/
|
|
75
|
+
cacheExecution(taskPattern, skillChain, effectiveness, tokenUsed) {
|
|
76
|
+
if (effectiveness < 0.7)
|
|
77
|
+
return; // Only cache good executions
|
|
78
|
+
if (!taskPattern.trim() || skillChain.length === 0)
|
|
79
|
+
return;
|
|
80
|
+
const db = getDb(this.dbPath);
|
|
81
|
+
const now = new Date().toISOString();
|
|
82
|
+
// Check if similar pattern already exists
|
|
83
|
+
const existing = this.findExactMatch(taskPattern);
|
|
84
|
+
if (existing) {
|
|
85
|
+
// Update existing entry if new execution is better
|
|
86
|
+
if (effectiveness >= existing.effectiveness) {
|
|
87
|
+
db.prepare(`
|
|
88
|
+
UPDATE skill_cache SET
|
|
89
|
+
skill_chain_json = ?,
|
|
90
|
+
effectiveness = ?,
|
|
91
|
+
token_used = ?,
|
|
92
|
+
hit_count = hit_count + 1,
|
|
93
|
+
last_hit = ?
|
|
94
|
+
WHERE task_pattern = ?
|
|
95
|
+
`).run(JSON.stringify(skillChain), effectiveness, tokenUsed, now, taskPattern);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// Just increment hit count
|
|
99
|
+
db.prepare(`
|
|
100
|
+
UPDATE skill_cache SET hit_count = hit_count + 1, last_hit = ? WHERE task_pattern = ?
|
|
101
|
+
`).run(now, taskPattern);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
db.prepare(`
|
|
106
|
+
INSERT INTO skill_cache
|
|
107
|
+
(task_pattern, skill_chain_json, effectiveness, token_used, hit_count, last_hit, created_at)
|
|
108
|
+
VALUES (?, ?, ?, ?, 1, ?, ?)
|
|
109
|
+
`).run(taskPattern, JSON.stringify(skillChain), effectiveness, tokenUsed, now, now);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Find a cached chain for a task description using BM25 similarity.
|
|
113
|
+
* Returns null if no suitable match (effectiveness >= 0.7).
|
|
114
|
+
*/
|
|
115
|
+
findCachedChain(taskDescription) {
|
|
116
|
+
const db = getDb(this.dbPath);
|
|
117
|
+
const query = this.normalizeForSearch(taskDescription);
|
|
118
|
+
if (!query)
|
|
119
|
+
return null;
|
|
120
|
+
try {
|
|
121
|
+
const row = db.prepare(`
|
|
122
|
+
SELECT skill_cache.* FROM skill_cache
|
|
123
|
+
JOIN skill_cache_fts ON skill_cache.rowid = skill_cache_fts.rowid
|
|
124
|
+
WHERE skill_cache_fts MATCH ?
|
|
125
|
+
AND skill_cache.effectiveness >= 0.7
|
|
126
|
+
ORDER BY bm25(skill_cache_fts) * skill_cache.effectiveness * (1.0 + skill_cache.hit_count * 0.1)
|
|
127
|
+
LIMIT 1
|
|
128
|
+
`).get(query);
|
|
129
|
+
if (!row)
|
|
130
|
+
return null;
|
|
131
|
+
return this.rowToCachedChain(row);
|
|
132
|
+
}
|
|
133
|
+
catch (_a) {
|
|
134
|
+
// FTS5 query might fail with special characters
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Record a cache hit (increment counter, update timestamp).
|
|
140
|
+
*/
|
|
141
|
+
recordHit(taskPattern) {
|
|
142
|
+
const db = getDb(this.dbPath);
|
|
143
|
+
db.prepare(`
|
|
144
|
+
UPDATE skill_cache SET hit_count = hit_count + 1, last_hit = ? WHERE task_pattern = ?
|
|
145
|
+
`).run(new Date().toISOString(), taskPattern);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get cache statistics.
|
|
149
|
+
*/
|
|
150
|
+
getStats() {
|
|
151
|
+
const db = getDb(this.dbPath);
|
|
152
|
+
try {
|
|
153
|
+
const stats = db.prepare(`
|
|
154
|
+
SELECT
|
|
155
|
+
COUNT(*) as total_entries,
|
|
156
|
+
COALESCE(SUM(hit_count), 0) as total_hits,
|
|
157
|
+
COALESCE(AVG(effectiveness), 0) as avg_effectiveness,
|
|
158
|
+
COALESCE(SUM(hit_count * token_used), 0) as estimated_tokens_saved
|
|
159
|
+
FROM skill_cache
|
|
160
|
+
`).get();
|
|
161
|
+
return {
|
|
162
|
+
totalEntries: stats.total_entries,
|
|
163
|
+
totalHits: stats.total_hits,
|
|
164
|
+
avgEffectiveness: Math.round(stats.avg_effectiveness * 100) / 100,
|
|
165
|
+
estimatedTokensSaved: stats.estimated_tokens_saved,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
catch (_a) {
|
|
169
|
+
return { totalEntries: 0, totalHits: 0, avgEffectiveness: 0, estimatedTokensSaved: 0 };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* List all cached chains.
|
|
174
|
+
*/
|
|
175
|
+
listCachedChains(limit = 20) {
|
|
176
|
+
const db = getDb(this.dbPath);
|
|
177
|
+
try {
|
|
178
|
+
const rows = db.prepare('SELECT * FROM skill_cache ORDER BY hit_count DESC, effectiveness DESC LIMIT ?').all(limit);
|
|
179
|
+
return rows.map(r => this.rowToCachedChain(r));
|
|
180
|
+
}
|
|
181
|
+
catch (_a) {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Clear all cached chains.
|
|
187
|
+
*/
|
|
188
|
+
clearCache() {
|
|
189
|
+
const db = getDb(this.dbPath);
|
|
190
|
+
const info = db.prepare('DELETE FROM skill_cache').run();
|
|
191
|
+
return info.changes;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Close the database connection.
|
|
195
|
+
*/
|
|
196
|
+
close() {
|
|
197
|
+
const db = dbCache.get(this.dbPath);
|
|
198
|
+
if (db) {
|
|
199
|
+
try {
|
|
200
|
+
db.close();
|
|
201
|
+
}
|
|
202
|
+
catch ( /* already closed */_a) { /* already closed */ }
|
|
203
|
+
dbCache.delete(this.dbPath);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// ─── Private Helpers ────────────────────────────────────────────────────────
|
|
207
|
+
findExactMatch(taskPattern) {
|
|
208
|
+
const db = getDb(this.dbPath);
|
|
209
|
+
const row = db.prepare('SELECT * FROM skill_cache WHERE task_pattern = ? LIMIT 1').get(taskPattern);
|
|
210
|
+
return row ? this.rowToCachedChain(row) : null;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Normalize a task description for FTS5 search.
|
|
214
|
+
* Removes special characters that break FTS5 MATCH syntax.
|
|
215
|
+
*/
|
|
216
|
+
normalizeForSearch(text) {
|
|
217
|
+
return text
|
|
218
|
+
.replace(/[^\w\s]/g, ' ') // Remove special chars
|
|
219
|
+
.replace(/\s+/g, ' ') // Collapse whitespace
|
|
220
|
+
.trim()
|
|
221
|
+
.split(' ')
|
|
222
|
+
.filter(w => w.length > 2) // Drop short words
|
|
223
|
+
.slice(0, 10) // Limit query length
|
|
224
|
+
.join(' ');
|
|
225
|
+
}
|
|
226
|
+
rowToCachedChain(row) {
|
|
227
|
+
let chain = [];
|
|
228
|
+
try {
|
|
229
|
+
chain = JSON.parse(String(row.skill_chain_json || '[]'));
|
|
230
|
+
}
|
|
231
|
+
catch ( /* empty */_a) { /* empty */ }
|
|
232
|
+
return {
|
|
233
|
+
taskPattern: String(row.task_pattern || ''),
|
|
234
|
+
skillChain: chain,
|
|
235
|
+
effectiveness: Number(row.effectiveness || 0),
|
|
236
|
+
tokenUsed: Number(row.token_used || 0),
|
|
237
|
+
hitCount: Number(row.hit_count || 0),
|
|
238
|
+
lastHit: String(row.last_hit || ''),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
exports.SkillExecutionCache = SkillExecutionCache;
|
|
243
|
+
// ─── Display Helpers ─────────────────────────────────────────────────────────
|
|
244
|
+
function formatCacheStats(stats) {
|
|
245
|
+
const lines = [
|
|
246
|
+
`📦 Skill Execution Cache`,
|
|
247
|
+
`─`.repeat(50),
|
|
248
|
+
`Cached patterns: ${stats.totalEntries}`,
|
|
249
|
+
`Total cache hits: ${stats.totalHits}`,
|
|
250
|
+
`Avg effectiveness: ${(stats.avgEffectiveness * 100).toFixed(1)}%`,
|
|
251
|
+
`Est. tokens saved: ~${stats.estimatedTokensSaved.toLocaleString()}`,
|
|
252
|
+
];
|
|
253
|
+
return lines.join('\n');
|
|
254
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ─── Smart Brain Router ─────────────────────────────────────────────────────
|
|
3
|
+
// Task-type classifier + brain tier selector for CodyMaster's 5-Tier Brain.
|
|
4
|
+
// TRIZ #1 Segmentation — load only the brain tiers needed for each task type.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.classifyTask = classifyTask;
|
|
7
|
+
exports.routeTask = routeTask;
|
|
8
|
+
exports.estimateSavings = estimateSavings;
|
|
9
|
+
exports.formatBrainPlan = formatBrainPlan;
|
|
10
|
+
// ─── Tier Costs (average token estimates) ────────────────────────────────────
|
|
11
|
+
const TIER_COSTS = {
|
|
12
|
+
1: { label: 'Sensory', avgTokens: 0, latencyMs: 0 },
|
|
13
|
+
2: { label: 'Working', avgTokens: 400, latencyMs: 0 },
|
|
14
|
+
3: { label: 'Long-term', avgTokens: 650, latencyMs: 50 },
|
|
15
|
+
4: { label: 'Semantic', avgTokens: 1500, latencyMs: 2000 },
|
|
16
|
+
5: { label: 'Structural', avgTokens: 3000, latencyMs: 5000 },
|
|
17
|
+
};
|
|
18
|
+
// ─── Task Classifier ────────────────────────────────────────────────────────
|
|
19
|
+
// TRIZ #1 Segmentation — classify to load only what's needed
|
|
20
|
+
/**
|
|
21
|
+
* Lightweight task classifier — keyword based, no LLM call.
|
|
22
|
+
* Cost: ~0 tokens, <1ms.
|
|
23
|
+
*/
|
|
24
|
+
function classifyTask(description) {
|
|
25
|
+
const lower = description.toLowerCase();
|
|
26
|
+
// Order matters: more specific patterns first
|
|
27
|
+
if (lower.match(/\b(resume|continue|what was|pick up|carry on|tiếp tục|đang làm gì)\b/)) {
|
|
28
|
+
return 'resume_session';
|
|
29
|
+
}
|
|
30
|
+
if (lower.match(/\b(deploy|ship|release|publish|push to prod|triển khai|xuất bản)\b/)) {
|
|
31
|
+
return 'deployment';
|
|
32
|
+
}
|
|
33
|
+
if (lower.match(/\b(blog|article|content|write about|documentation|bài viết|nội dung|tài liệu)\b/)) {
|
|
34
|
+
return 'content_creation';
|
|
35
|
+
}
|
|
36
|
+
if (lower.match(/\b(fix|bug|error|crash|broken|debug|sửa|lỗi|hỏng)\b/)) {
|
|
37
|
+
return 'code_change';
|
|
38
|
+
}
|
|
39
|
+
if (lower.match(/\b(build|create|implement|add|feature|system|xây|tạo|thêm|tính năng)\b/)) {
|
|
40
|
+
return 'complex_feature';
|
|
41
|
+
}
|
|
42
|
+
// Short descriptive queries are likely simple questions
|
|
43
|
+
if (lower.match(/\b(what|how|why|explain|status|where|show|list)\b/) && lower.length < 120) {
|
|
44
|
+
return 'simple_question';
|
|
45
|
+
}
|
|
46
|
+
return 'code_change'; // Safe default
|
|
47
|
+
}
|
|
48
|
+
// ─── Brain Router ────────────────────────────────────────────────────────────
|
|
49
|
+
/**
|
|
50
|
+
* Smart Brain Router — selects minimal brain layers needed for a task.
|
|
51
|
+
*
|
|
52
|
+
* Problem solved:
|
|
53
|
+
* Without routing, every task loads all 5 tiers = ~5550 tokens + 7s latency.
|
|
54
|
+
* With routing, simple tasks use ~0-400 tokens, complex tasks scale progressively.
|
|
55
|
+
*
|
|
56
|
+
* Estimated savings: ~60-80% token reduction on brain context loading.
|
|
57
|
+
*/
|
|
58
|
+
function routeTask(taskDescription) {
|
|
59
|
+
const taskType = classifyTask(taskDescription);
|
|
60
|
+
switch (taskType) {
|
|
61
|
+
case 'simple_question':
|
|
62
|
+
return {
|
|
63
|
+
taskType,
|
|
64
|
+
tiers: [1],
|
|
65
|
+
totalBudget: 200,
|
|
66
|
+
progressiveLoad: false,
|
|
67
|
+
rationale: 'Simple question — Tier 1 (Sensory) only. No memory or code context needed.',
|
|
68
|
+
};
|
|
69
|
+
case 'resume_session':
|
|
70
|
+
return {
|
|
71
|
+
taskType,
|
|
72
|
+
tiers: [1, 2],
|
|
73
|
+
totalBudget: 500,
|
|
74
|
+
progressiveLoad: false,
|
|
75
|
+
rationale: 'Session resume — Tier 1 + Tier 2 (CONTINUITY.md) to recall working state.',
|
|
76
|
+
};
|
|
77
|
+
case 'code_change':
|
|
78
|
+
return {
|
|
79
|
+
taskType,
|
|
80
|
+
tiers: [1, 2, 3, 5],
|
|
81
|
+
tier3Scope: extractScope(taskDescription),
|
|
82
|
+
tier5Filter: extractFilePaths(taskDescription),
|
|
83
|
+
totalBudget: 3000,
|
|
84
|
+
progressiveLoad: false,
|
|
85
|
+
rationale: 'Code change — Tiers 1+2+3+5. Learnings scoped, CodeGraph for affected files.',
|
|
86
|
+
};
|
|
87
|
+
case 'deployment':
|
|
88
|
+
return {
|
|
89
|
+
taskType,
|
|
90
|
+
tiers: [1, 2, 3],
|
|
91
|
+
tier3Scope: 'deploy',
|
|
92
|
+
totalBudget: 1500,
|
|
93
|
+
progressiveLoad: false,
|
|
94
|
+
rationale: 'Deployment — Tiers 1+2+3. Scoped learnings for deploy patterns.',
|
|
95
|
+
};
|
|
96
|
+
case 'content_creation':
|
|
97
|
+
return {
|
|
98
|
+
taskType,
|
|
99
|
+
tiers: [1, 3, 4],
|
|
100
|
+
totalBudget: 2000,
|
|
101
|
+
progressiveLoad: false,
|
|
102
|
+
rationale: 'Content creation — Tiers 1+3+4. Semantic search for related content.',
|
|
103
|
+
};
|
|
104
|
+
case 'complex_feature':
|
|
105
|
+
return {
|
|
106
|
+
taskType,
|
|
107
|
+
tiers: [1, 2, 3, 4, 5],
|
|
108
|
+
totalBudget: 5000,
|
|
109
|
+
progressiveLoad: true,
|
|
110
|
+
rationale: 'Complex feature — All tiers, progressive loading. Start L0, upgrade on demand.',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// ─── Scope Extraction ────────────────────────────────────────────────────────
|
|
115
|
+
/**
|
|
116
|
+
* Extract a module scope from the task description for Tier 3 scoping.
|
|
117
|
+
* Returns undefined if no clear scope detected.
|
|
118
|
+
*/
|
|
119
|
+
function extractScope(description) {
|
|
120
|
+
const lower = description.toLowerCase();
|
|
121
|
+
// Priority 1: Look for common scope keywords (most reliable)
|
|
122
|
+
const scopes = ['auth', 'api', 'deploy', 'test', 'build', 'db', 'ui', 'cli', 'i18n', 'security'];
|
|
123
|
+
const found = scopes.find(s => lower.includes(s));
|
|
124
|
+
if (found)
|
|
125
|
+
return found;
|
|
126
|
+
// Priority 2: Look for explicit "module: xxx" or "component: xxx" patterns
|
|
127
|
+
const moduleMatch = lower.match(/(?:module|component|service|package)[:\s]+(\w[\w-]*)/);
|
|
128
|
+
if (moduleMatch)
|
|
129
|
+
return moduleMatch[1];
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Extract file paths mentioned in the task for Tier 5 filtering.
|
|
134
|
+
*/
|
|
135
|
+
function extractFilePaths(description) {
|
|
136
|
+
const matches = description.match(/[\w./-]+\.(ts|js|tsx|jsx|py|go|rs|md|json|yaml|yml)/g);
|
|
137
|
+
return matches && matches.length > 0 ? matches : undefined;
|
|
138
|
+
}
|
|
139
|
+
// ─── Token Savings Estimator ─────────────────────────────────────────────────
|
|
140
|
+
/**
|
|
141
|
+
* Estimate token savings from smart routing vs loading all tiers.
|
|
142
|
+
*/
|
|
143
|
+
function estimateSavings(plan) {
|
|
144
|
+
const allTiersTokens = Object.values(TIER_COSTS).reduce((sum, t) => sum + t.avgTokens, 0);
|
|
145
|
+
const planTokens = plan.tiers.reduce((sum, tier) => sum + TIER_COSTS[tier].avgTokens, 0);
|
|
146
|
+
return {
|
|
147
|
+
withoutRouting: allTiersTokens,
|
|
148
|
+
withRouting: planTokens,
|
|
149
|
+
saved: allTiersTokens - planTokens,
|
|
150
|
+
percentage: Math.round(((allTiersTokens - planTokens) / allTiersTokens) * 100),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// ─── Display Helpers ─────────────────────────────────────────────────────────
|
|
154
|
+
/**
|
|
155
|
+
* Format a BrainLoadPlan for CLI display.
|
|
156
|
+
*/
|
|
157
|
+
function formatBrainPlan(plan) {
|
|
158
|
+
const savings = estimateSavings(plan);
|
|
159
|
+
const tierLabels = plan.tiers.map(t => ` ${t}. ${TIER_COSTS[t].label} (~${TIER_COSTS[t].avgTokens} tokens)`);
|
|
160
|
+
const lines = [
|
|
161
|
+
`🧠 Brain Load Plan`,
|
|
162
|
+
`─`.repeat(50),
|
|
163
|
+
`Task type: ${plan.taskType}`,
|
|
164
|
+
`Progressive: ${plan.progressiveLoad ? 'Yes' : 'No'}`,
|
|
165
|
+
`Budget: ${plan.totalBudget.toLocaleString()} tokens`,
|
|
166
|
+
``,
|
|
167
|
+
`Active tiers:`,
|
|
168
|
+
...tierLabels,
|
|
169
|
+
``,
|
|
170
|
+
`Token savings:`,
|
|
171
|
+
` All tiers: ~${savings.withoutRouting.toLocaleString()} tokens`,
|
|
172
|
+
` This plan: ~${savings.withRouting.toLocaleString()} tokens`,
|
|
173
|
+
` Saved: ~${savings.saved.toLocaleString()} tokens (${savings.percentage}%)`,
|
|
174
|
+
``,
|
|
175
|
+
`Rationale: ${plan.rationale}`,
|
|
176
|
+
];
|
|
177
|
+
if (plan.tier3Scope) {
|
|
178
|
+
lines.push(`Tier 3 scope: ${plan.tier3Scope}`);
|
|
179
|
+
}
|
|
180
|
+
if (plan.tier5Filter) {
|
|
181
|
+
lines.push(`Tier 5 filter: ${plan.tier5Filter.join(', ')}`);
|
|
182
|
+
}
|
|
183
|
+
return lines.join('\n');
|
|
184
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Opinionated sprint pipeline + Context Bus files under `.cm/sprint/`.
|
|
4
|
+
* Complements root `context-bus.json` with step artifacts (ADR 002).
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.SPRINT_STEPS = void 0;
|
|
11
|
+
exports.readSprintState = readSprintState;
|
|
12
|
+
exports.writeSprintState = writeSprintState;
|
|
13
|
+
exports.initSprint = initSprint;
|
|
14
|
+
exports.completeSprintStep = completeSprintStep;
|
|
15
|
+
exports.skipSprintStep = skipSprintStep;
|
|
16
|
+
exports.resetSprint = resetSprint;
|
|
17
|
+
exports.sprintDryRun = sprintDryRun;
|
|
18
|
+
exports.sprintArtifactPreviewFromDisk = sprintArtifactPreviewFromDisk;
|
|
19
|
+
exports.skillMappingForStep = skillMappingForStep;
|
|
20
|
+
const fs_1 = __importDefault(require("fs"));
|
|
21
|
+
const path_1 = __importDefault(require("path"));
|
|
22
|
+
exports.SPRINT_STEPS = [
|
|
23
|
+
'brainstorm',
|
|
24
|
+
'plan',
|
|
25
|
+
'design',
|
|
26
|
+
'tdd',
|
|
27
|
+
'build',
|
|
28
|
+
'review',
|
|
29
|
+
'qa',
|
|
30
|
+
'security',
|
|
31
|
+
'ship',
|
|
32
|
+
'monitor',
|
|
33
|
+
'retro',
|
|
34
|
+
];
|
|
35
|
+
function sprintDir(projectPath) {
|
|
36
|
+
return path_1.default.join(projectPath, '.cm', 'sprint');
|
|
37
|
+
}
|
|
38
|
+
function statePath(projectPath) {
|
|
39
|
+
return path_1.default.join(sprintDir(projectPath), 'state.json');
|
|
40
|
+
}
|
|
41
|
+
function ensureSprintDir(projectPath) {
|
|
42
|
+
const d = sprintDir(projectPath);
|
|
43
|
+
const art = path_1.default.join(d, 'artifacts');
|
|
44
|
+
if (!fs_1.default.existsSync(art))
|
|
45
|
+
fs_1.default.mkdirSync(art, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
function normalizeSprintState(raw) {
|
|
48
|
+
var _a;
|
|
49
|
+
return Object.assign(Object.assign({}, raw), { version: raw.version === 2 ? 2 : 1, skipped: (_a = raw.skipped) !== null && _a !== void 0 ? _a : [] });
|
|
50
|
+
}
|
|
51
|
+
function readSprintState(projectPath) {
|
|
52
|
+
const p = statePath(projectPath);
|
|
53
|
+
if (!fs_1.default.existsSync(p))
|
|
54
|
+
return null;
|
|
55
|
+
try {
|
|
56
|
+
const raw = JSON.parse(fs_1.default.readFileSync(p, 'utf8'));
|
|
57
|
+
return normalizeSprintState(raw);
|
|
58
|
+
}
|
|
59
|
+
catch (_a) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function writeSprintState(projectPath, state) {
|
|
64
|
+
ensureSprintDir(projectPath);
|
|
65
|
+
fs_1.default.writeFileSync(statePath(projectPath), JSON.stringify(state, null, 2), 'utf8');
|
|
66
|
+
}
|
|
67
|
+
function initSprint(projectPath, fromStep) {
|
|
68
|
+
ensureSprintDir(projectPath);
|
|
69
|
+
const now = new Date().toISOString();
|
|
70
|
+
let pipeline = [...exports.SPRINT_STEPS];
|
|
71
|
+
let startIdx = 0;
|
|
72
|
+
if (fromStep) {
|
|
73
|
+
const i = pipeline.indexOf(fromStep);
|
|
74
|
+
if (i >= 0)
|
|
75
|
+
startIdx = i;
|
|
76
|
+
}
|
|
77
|
+
const state = {
|
|
78
|
+
version: 2,
|
|
79
|
+
pipeline,
|
|
80
|
+
current_index: startIdx,
|
|
81
|
+
completed: [],
|
|
82
|
+
skipped: [],
|
|
83
|
+
started_at: now,
|
|
84
|
+
updated_at: now,
|
|
85
|
+
artifacts_dir: path_1.default.join(sprintDir(projectPath), 'artifacts'),
|
|
86
|
+
};
|
|
87
|
+
writeSprintState(projectPath, state);
|
|
88
|
+
appendEvent(projectPath, { type: 'init', from: fromStep !== null && fromStep !== void 0 ? fromStep : null, at: now });
|
|
89
|
+
return state;
|
|
90
|
+
}
|
|
91
|
+
function completeSprintStep(projectPath, step, artifactBody) {
|
|
92
|
+
let state = readSprintState(projectPath);
|
|
93
|
+
if (!state)
|
|
94
|
+
state = initSprint(projectPath);
|
|
95
|
+
if (state.current_index >= state.pipeline.length) {
|
|
96
|
+
throw new Error('Sprint pipeline already finished');
|
|
97
|
+
}
|
|
98
|
+
const expected = state.pipeline[state.current_index];
|
|
99
|
+
if (expected !== step) {
|
|
100
|
+
throw new Error(`Expected step "${expected}", got "${step}"`);
|
|
101
|
+
}
|
|
102
|
+
const artFile = path_1.default.join(state.artifacts_dir, `${step}.md`);
|
|
103
|
+
fs_1.default.writeFileSync(artFile, artifactBody, 'utf8');
|
|
104
|
+
state.completed.push(step);
|
|
105
|
+
state.current_index = Math.min(state.current_index + 1, state.pipeline.length);
|
|
106
|
+
state.updated_at = new Date().toISOString();
|
|
107
|
+
state.version = 2;
|
|
108
|
+
writeSprintState(projectPath, state);
|
|
109
|
+
appendEvent(projectPath, { type: 'complete', step, at: state.updated_at });
|
|
110
|
+
return state;
|
|
111
|
+
}
|
|
112
|
+
const SKIP_STUB = (step, at) => `# ${step}\n\n_Skipped via \`cm sprint skip\` at ${at}._\n`;
|
|
113
|
+
function skipSprintStep(projectPath, step) {
|
|
114
|
+
let state = readSprintState(projectPath);
|
|
115
|
+
if (!state)
|
|
116
|
+
state = initSprint(projectPath);
|
|
117
|
+
if (state.current_index >= state.pipeline.length) {
|
|
118
|
+
throw new Error('Sprint pipeline already finished');
|
|
119
|
+
}
|
|
120
|
+
const expected = state.pipeline[state.current_index];
|
|
121
|
+
if (expected !== step) {
|
|
122
|
+
throw new Error(`Expected step "${expected}", got "${step}"`);
|
|
123
|
+
}
|
|
124
|
+
const at = new Date().toISOString();
|
|
125
|
+
const artFile = path_1.default.join(state.artifacts_dir, `${step}.md`);
|
|
126
|
+
fs_1.default.writeFileSync(artFile, SKIP_STUB(step, at), 'utf8');
|
|
127
|
+
state.skipped.push(step);
|
|
128
|
+
state.current_index = Math.min(state.current_index + 1, state.pipeline.length);
|
|
129
|
+
state.updated_at = at;
|
|
130
|
+
state.version = 2;
|
|
131
|
+
writeSprintState(projectPath, state);
|
|
132
|
+
appendEvent(projectPath, { type: 'skip', step, at });
|
|
133
|
+
return state;
|
|
134
|
+
}
|
|
135
|
+
function backupDirName() {
|
|
136
|
+
return new Date().toISOString().replace(/:/g, '-');
|
|
137
|
+
}
|
|
138
|
+
/** Remove sprint state; optional backup under `.cm/sprint/backup/<timestamp>/`. */
|
|
139
|
+
function resetSprint(projectPath, options) {
|
|
140
|
+
const backup = (options === null || options === void 0 ? void 0 : options.backup) !== false;
|
|
141
|
+
const sd = sprintDir(projectPath);
|
|
142
|
+
const st = statePath(projectPath);
|
|
143
|
+
const ev = eventsPath(projectPath);
|
|
144
|
+
const art = path_1.default.join(sd, 'artifacts');
|
|
145
|
+
const hasState = fs_1.default.existsSync(st);
|
|
146
|
+
let hasEvents = false;
|
|
147
|
+
if (fs_1.default.existsSync(ev)) {
|
|
148
|
+
try {
|
|
149
|
+
hasEvents = fs_1.default.statSync(ev).size > 0;
|
|
150
|
+
}
|
|
151
|
+
catch (_a) {
|
|
152
|
+
hasEvents = false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
let hasArtifacts = false;
|
|
156
|
+
if (fs_1.default.existsSync(art)) {
|
|
157
|
+
try {
|
|
158
|
+
hasArtifacts = fs_1.default.readdirSync(art).length > 0;
|
|
159
|
+
}
|
|
160
|
+
catch (_b) {
|
|
161
|
+
hasArtifacts = false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (!hasState && !hasEvents && !hasArtifacts) {
|
|
165
|
+
return { ok: false, reason: 'no_sprint_data' };
|
|
166
|
+
}
|
|
167
|
+
let backupPath;
|
|
168
|
+
if (backup) {
|
|
169
|
+
const stamp = backupDirName();
|
|
170
|
+
backupPath = path_1.default.join(sd, 'backup', stamp);
|
|
171
|
+
fs_1.default.mkdirSync(backupPath, { recursive: true });
|
|
172
|
+
if (hasState)
|
|
173
|
+
fs_1.default.copyFileSync(st, path_1.default.join(backupPath, 'state.json'));
|
|
174
|
+
if (fs_1.default.existsSync(ev))
|
|
175
|
+
fs_1.default.copyFileSync(ev, path_1.default.join(backupPath, 'events.jsonl'));
|
|
176
|
+
if (fs_1.default.existsSync(art)) {
|
|
177
|
+
const destArt = path_1.default.join(backupPath, 'artifacts');
|
|
178
|
+
fs_1.default.cpSync(art, destArt, { recursive: true });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
fs_1.default.rmSync(st, { force: true });
|
|
182
|
+
fs_1.default.rmSync(ev, { force: true });
|
|
183
|
+
fs_1.default.rmSync(art, { recursive: true, force: true });
|
|
184
|
+
fs_1.default.mkdirSync(art, { recursive: true });
|
|
185
|
+
return { ok: true, backupDir: backupPath };
|
|
186
|
+
}
|
|
187
|
+
function sprintDryRun(projectPath) {
|
|
188
|
+
var _a;
|
|
189
|
+
const state = (_a = readSprintState(projectPath)) !== null && _a !== void 0 ? _a : initSprint(projectPath);
|
|
190
|
+
return sprintArtifactPreview(state);
|
|
191
|
+
}
|
|
192
|
+
/** Read-only preview without creating files (for MCP / status). */
|
|
193
|
+
function sprintArtifactPreviewFromDisk(projectPath) {
|
|
194
|
+
const state = readSprintState(projectPath);
|
|
195
|
+
if (!state) {
|
|
196
|
+
const base = path_1.default.join(projectPath, '.cm', 'sprint', 'artifacts');
|
|
197
|
+
const artifacts = exports.SPRINT_STEPS.map((s) => path_1.default.join(base, `${s}.md`));
|
|
198
|
+
return { steps: [...exports.SPRINT_STEPS], artifacts };
|
|
199
|
+
}
|
|
200
|
+
return sprintArtifactPreview(state);
|
|
201
|
+
}
|
|
202
|
+
function sprintArtifactPreview(state) {
|
|
203
|
+
const artifacts = state.pipeline.map((s) => path_1.default.join(state.artifacts_dir, `${s}.md`));
|
|
204
|
+
return { steps: [...state.pipeline], artifacts };
|
|
205
|
+
}
|
|
206
|
+
function eventsPath(projectPath) {
|
|
207
|
+
return path_1.default.join(sprintDir(projectPath), 'events.jsonl');
|
|
208
|
+
}
|
|
209
|
+
function appendEvent(projectPath, rec) {
|
|
210
|
+
ensureSprintDir(projectPath);
|
|
211
|
+
fs_1.default.appendFileSync(eventsPath(projectPath), JSON.stringify(rec) + '\n', 'utf8');
|
|
212
|
+
}
|
|
213
|
+
function skillMappingForStep(step) {
|
|
214
|
+
const map = {
|
|
215
|
+
brainstorm: 'cm-brainstorm-idea',
|
|
216
|
+
plan: 'cm-planning',
|
|
217
|
+
design: 'cm-ui-preview / cm-design-system',
|
|
218
|
+
tdd: 'cm-tdd',
|
|
219
|
+
build: 'cm-execution',
|
|
220
|
+
review: 'cm-code-review',
|
|
221
|
+
qa: 'cm-quality-gate / cm-test-gate',
|
|
222
|
+
security: 'cm-secret-shield / cm-security-gate',
|
|
223
|
+
ship: 'cm-safe-deploy',
|
|
224
|
+
monitor: 'cm-canary (post-deploy)',
|
|
225
|
+
retro: 'cm-retro',
|
|
226
|
+
};
|
|
227
|
+
return map[step];
|
|
228
|
+
}
|