codymaster 4.8.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 +55 -7
- package/README.md +142 -95
- package/dist/advisory-handoff.js +89 -0
- package/dist/advisory-report.js +105 -0
- package/dist/cli/command-registry.js +8 -0
- package/dist/cli/commands/bench.js +69 -0
- package/dist/cli/commands/brain.js +108 -0
- package/dist/cli/commands/engineering.js +108 -0
- package/dist/cli/commands/evolve.js +123 -0
- package/dist/cli/commands/mcp-serve.js +104 -0
- package/dist/cm-config.js +0 -18
- 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/execution-analyzer.js +138 -0
- 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 +230 -1
- 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/storage-backend.js +10 -8
- package/dist/token-budget.js +88 -0
- package/package.json +2 -3
- package/scripts/postinstall.js +10 -59
- package/skills/CLAUDE.md +0 -5
- package/skills/_shared/helpers.md +2 -8
- package/skills/cm-browse/SKILL.md +6 -0
- package/skills/cm-conductor-worktrees/SKILL.md +4 -0
- package/skills/cm-content-factory/landing/docs/content/changelog.md +4 -4
- package/skills/cm-content-factory/landing/docs/content/deployment.md +3 -3
- package/skills/cm-content-factory/landing/docs/content/execution-flow.md +8 -8
- package/skills/cm-content-factory/landing/docs/content/memory-system.md +38 -0
- package/skills/cm-content-factory/landing/docs/content/openspace.md +1 -1
- package/skills/cm-content-factory/landing/docs/content/use-cases.md +2 -2
- package/skills/cm-content-factory/landing/docs/content/v5-intro.md +3 -3
- package/skills/cm-content-factory/landing/docs/index.html +1 -1
- package/skills/cm-content-factory/landing/index.html +3 -3
- package/skills/cm-content-factory/landing/translations.js +37 -37
- package/skills/cm-continuity/SKILL.md +32 -33
- package/skills/cm-design-studio/SKILL.md +4 -0
- package/skills/cm-ecosystem-roadmap/SKILL.md +4 -0
- package/skills/cm-engineering-meta/SKILL.md +4 -0
- package/skills/cm-guardian-runtime/SKILL.md +5 -1
- package/skills/cm-mcp-engineering/SKILL.md +4 -0
- package/skills/cm-post-deploy-canary/SKILL.md +4 -0
- package/skills/cm-project-bootstrap/SKILL.md +11 -0
- package/skills/cm-qa-visual-cli/SKILL.md +4 -0
- package/skills/cm-retro-cli/SKILL.md +4 -0
- package/skills/cm-second-opinion-cli/SKILL.md +4 -0
- 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 +4 -0
- package/skills/cm-start/SKILL.md +0 -10
- package/skills/cm-tdd/SKILL.md +2 -2
- package/skills/profiles/full.txt +4 -0
- package/install.sh +0 -1125
- package/scripts/viking-demo.ts +0 -105
- package/skills/cm-content-factory/landing/docs/content/openviking.md +0 -33
|
@@ -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
|
+
}
|
package/dist/storage-backend.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.SqliteBackend = void 0;
|
|
4
4
|
exports.getBackend = getBackend;
|
|
5
5
|
const context_db_1 = require("./context-db");
|
|
6
|
-
const viking_backend_1 = require("./backends/viking-backend");
|
|
7
|
-
Object.defineProperty(exports, "VikingBackend", { enumerable: true, get: function () { return viking_backend_1.VikingBackend; } });
|
|
8
|
-
const viking_http_client_1 = require("./backends/viking-http-client");
|
|
9
6
|
const cm_config_1 = require("./cm-config");
|
|
10
7
|
// ─── SqliteBackend ────────────────────────────────────────────────────────────
|
|
11
8
|
/**
|
|
@@ -33,6 +30,10 @@ class SqliteBackend {
|
|
|
33
30
|
}
|
|
34
31
|
writeSkillOutput(o) { (0, context_db_1.writeSkillOutput)(this.dbPath, o); }
|
|
35
32
|
getSkillOutputs(sessionId) { return (0, context_db_1.getSkillOutputs)(this.dbPath, sessionId); }
|
|
33
|
+
recordExecutionAnalysis(a) { (0, context_db_1.recordExecutionAnalysis)(this.dbPath, a); }
|
|
34
|
+
getExecutionAnalyses(limit = 20) { return (0, context_db_1.getExecutionAnalyses)(this.dbPath, limit); }
|
|
35
|
+
getSkillMetric(skill) { return (0, context_db_1.getSkillMetric)(this.dbPath, skill); }
|
|
36
|
+
listSkillMetrics(limit = 50) { return (0, context_db_1.listSkillMetrics)(this.dbPath, limit); }
|
|
36
37
|
}
|
|
37
38
|
exports.SqliteBackend = SqliteBackend;
|
|
38
39
|
// ─── Factory ─────────────────────────────────────────────────────────────────
|
|
@@ -40,7 +41,7 @@ exports.SqliteBackend = SqliteBackend;
|
|
|
40
41
|
* Returns the configured StorageBackend for the given project.
|
|
41
42
|
*
|
|
42
43
|
* Reads `.cm/config.yaml → storage.backend` via `loadCmConfig` (default: `sqlite`).
|
|
43
|
-
*
|
|
44
|
+
* Legacy `storage.backend: viking` configs are warned and routed back to sqlite.
|
|
44
45
|
*
|
|
45
46
|
* Usage:
|
|
46
47
|
* const backend = getBackend('/path/to/project');
|
|
@@ -48,13 +49,14 @@ exports.SqliteBackend = SqliteBackend;
|
|
|
48
49
|
* const results = backend.queryLearnings('i18n locale');
|
|
49
50
|
*/
|
|
50
51
|
function getBackend(projectPath) {
|
|
51
|
-
var _a, _b
|
|
52
|
+
var _a, _b;
|
|
52
53
|
const cfg = (0, cm_config_1.loadCmConfig)(projectPath);
|
|
53
54
|
const engine = ((_b = (_a = cfg.storage) === null || _a === void 0 ? void 0 : _a.backend) !== null && _b !== void 0 ? _b : 'sqlite').toLowerCase();
|
|
54
55
|
switch (engine) {
|
|
55
56
|
case 'viking': {
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
console.warn('[CodyMaster] storage.backend: viking has been removed. ' +
|
|
58
|
+
'Falling back to sqlite for the supported default path.');
|
|
59
|
+
return new SqliteBackend(projectPath);
|
|
58
60
|
}
|
|
59
61
|
case 'sqlite':
|
|
60
62
|
default:
|
package/dist/token-budget.js
CHANGED
|
@@ -8,6 +8,10 @@ exports.loadBudget = loadBudget;
|
|
|
8
8
|
exports.checkBudget = checkBudget;
|
|
9
9
|
exports.estimateTokens = estimateTokens;
|
|
10
10
|
exports.generateBudgetReport = generateBudgetReport;
|
|
11
|
+
exports.getDefaultTierBudgets = getDefaultTierBudgets;
|
|
12
|
+
exports.checkTierBudget = checkTierBudget;
|
|
13
|
+
exports.generateTierReport = generateTierReport;
|
|
14
|
+
exports.formatSavingsReport = formatSavingsReport;
|
|
11
15
|
const fs_1 = __importDefault(require("fs"));
|
|
12
16
|
const path_1 = __importDefault(require("path"));
|
|
13
17
|
// ─── Constants ──────────────────────────────────────────────────────────────
|
|
@@ -106,3 +110,87 @@ function generateBudgetReport(budget) {
|
|
|
106
110
|
lines.push(`${'TOTAL'.padEnd(25)} ${sum.toLocaleString().padStart(10)} ${(((sum / total) * 100).toFixed(2) + '%').padStart(10)}`);
|
|
107
111
|
return lines.join('\n');
|
|
108
112
|
}
|
|
113
|
+
// ─── Per-Tier Budgets (Smart Brain Router integration) ───────────────────────
|
|
114
|
+
/**
|
|
115
|
+
* Default per-tier token budgets for the 5-Tier Brain architecture.
|
|
116
|
+
* Used by SmartBrainRouter to enforce tier-level spending limits.
|
|
117
|
+
*/
|
|
118
|
+
function getDefaultTierBudgets() {
|
|
119
|
+
return [
|
|
120
|
+
{ tier: 1, label: 'Sensory', soft: 0, hard: 0 }, // Always on, zero cost
|
|
121
|
+
{ tier: 2, label: 'Working', soft: 500, hard: 1000 }, // CONTINUITY.md
|
|
122
|
+
{ tier: 3, label: 'Long-term', soft: 500, hard: 3000 }, // Learnings/decisions
|
|
123
|
+
{ tier: 4, label: 'Semantic', soft: 0, hard: 2000 }, // qmd vector search
|
|
124
|
+
{ tier: 5, label: 'Structural', soft: 0, hard: 4000 }, // CodeGraph AST
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Check if a tier's token usage is within budget.
|
|
129
|
+
*/
|
|
130
|
+
function checkTierBudget(tierBudgets, tier, tokenCount, enforcement = 'soft') {
|
|
131
|
+
const budget = tierBudgets.find(tb => tb.tier === tier);
|
|
132
|
+
if (!budget) {
|
|
133
|
+
return { allowed: false, remaining: 0, suggestion: `Unknown tier ${tier}` };
|
|
134
|
+
}
|
|
135
|
+
const limit = enforcement === 'hard' ? budget.hard : budget.soft;
|
|
136
|
+
if (limit === 0) {
|
|
137
|
+
// Tier is disabled by default (soft=0), but allowed up to hard limit
|
|
138
|
+
if (tokenCount <= budget.hard) {
|
|
139
|
+
return { allowed: true, remaining: budget.hard - tokenCount };
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
allowed: false,
|
|
143
|
+
remaining: budget.hard - tokenCount,
|
|
144
|
+
suggestion: `Tier ${tier} (${budget.label}) exceeds hard limit of ${budget.hard} tokens.`,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const remaining = limit - tokenCount;
|
|
148
|
+
if (remaining >= 0) {
|
|
149
|
+
return { allowed: true, remaining };
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
allowed: enforcement !== 'hard',
|
|
153
|
+
remaining,
|
|
154
|
+
suggestion: `Tier ${tier} (${budget.label}) over by ~${Math.abs(remaining)} tokens. Consider L0 loading.`,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Generate a tier-level budget report.
|
|
159
|
+
*/
|
|
160
|
+
function generateTierReport(tierBudgets) {
|
|
161
|
+
const lines = [
|
|
162
|
+
`Brain Tier Budget Report`,
|
|
163
|
+
'─'.repeat(60),
|
|
164
|
+
`${'Tier'.padEnd(6)} ${'Label'.padEnd(12)} ${'Soft'.padStart(8)} ${'Hard'.padStart(8)} ${'Status'.padStart(10)}`,
|
|
165
|
+
'─'.repeat(60),
|
|
166
|
+
];
|
|
167
|
+
for (const tb of tierBudgets) {
|
|
168
|
+
const status = tb.soft === 0 ? 'off/demand' : 'active';
|
|
169
|
+
lines.push(`${String(tb.tier).padEnd(6)} ${tb.label.padEnd(12)} ${tb.soft.toLocaleString().padStart(8)} ${tb.hard.toLocaleString().padStart(8)} ${status.padStart(10)}`);
|
|
170
|
+
}
|
|
171
|
+
const totalSoft = tierBudgets.reduce((s, tb) => s + tb.soft, 0);
|
|
172
|
+
const totalHard = tierBudgets.reduce((s, tb) => s + tb.hard, 0);
|
|
173
|
+
lines.push('─'.repeat(60));
|
|
174
|
+
lines.push(`${'TOTAL'.padEnd(18)} ${totalSoft.toLocaleString().padStart(8)} ${totalHard.toLocaleString().padStart(8)}`);
|
|
175
|
+
return lines.join('\n');
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Format token savings report for CLI display.
|
|
179
|
+
*/
|
|
180
|
+
function formatSavingsReport(savings) {
|
|
181
|
+
const lines = [
|
|
182
|
+
`💰 Token Savings Report`,
|
|
183
|
+
'─'.repeat(50),
|
|
184
|
+
`Brain routing: ~${savings.brainRoutingSaved.toLocaleString()} tokens saved`,
|
|
185
|
+
`Cache hits: ~${savings.cacheHitsSaved.toLocaleString()} tokens saved`,
|
|
186
|
+
`Progressive loading: ~${savings.progressiveLoadSaved.toLocaleString()} tokens saved`,
|
|
187
|
+
'─'.repeat(50),
|
|
188
|
+
`Total saved: ~${savings.totalSaved.toLocaleString()} tokens`,
|
|
189
|
+
`Tasks this session: ${savings.sessionTasks}`,
|
|
190
|
+
];
|
|
191
|
+
if (savings.sessionTasks > 0) {
|
|
192
|
+
const avgSaved = Math.round(savings.totalSaved / savings.sessionTasks);
|
|
193
|
+
lines.push(`Avg saved per task: ~${avgSaved.toLocaleString()} tokens`);
|
|
194
|
+
}
|
|
195
|
+
return lines.join('\n');
|
|
196
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codymaster",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "68+ Skills. Ship 10x faster. AI-powered coding skill kit for Claude, Cursor, Gemini & more.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -60,8 +60,7 @@
|
|
|
60
60
|
"commands/",
|
|
61
61
|
"public/",
|
|
62
62
|
"README.md",
|
|
63
|
-
"CHANGELOG.md"
|
|
64
|
-
"install.sh"
|
|
63
|
+
"CHANGELOG.md"
|
|
65
64
|
],
|
|
66
65
|
"publishConfig": {
|
|
67
66
|
"access": "public"
|