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.
Files changed (72) hide show
  1. package/CHANGELOG.md +55 -7
  2. package/README.md +142 -95
  3. package/dist/advisory-handoff.js +89 -0
  4. package/dist/advisory-report.js +105 -0
  5. package/dist/cli/command-registry.js +8 -0
  6. package/dist/cli/commands/bench.js +69 -0
  7. package/dist/cli/commands/brain.js +108 -0
  8. package/dist/cli/commands/engineering.js +108 -0
  9. package/dist/cli/commands/evolve.js +123 -0
  10. package/dist/cli/commands/mcp-serve.js +104 -0
  11. package/dist/cm-config.js +0 -18
  12. package/dist/codybench/judges/automated.js +31 -0
  13. package/dist/codybench/runners/claude-code.js +32 -0
  14. package/dist/codybench/suites/memory-retention.js +85 -0
  15. package/dist/codybench/suites/tdd-regression.js +35 -0
  16. package/dist/codybench/suites/token-efficiency.js +55 -0
  17. package/dist/codybench/types.js +2 -0
  18. package/dist/context-db.js +157 -0
  19. package/dist/continuity.js +2 -6
  20. package/dist/execution-analyzer.js +138 -0
  21. package/dist/indexer/skills-lib.js +533 -0
  22. package/dist/indexer/skills-map.js +1374 -0
  23. package/dist/indexer/skills.js +16 -0
  24. package/dist/learning-promoter.js +246 -0
  25. package/dist/mcp-context-server.js +230 -1
  26. package/dist/skill-chain.js +63 -1
  27. package/dist/skill-evolver.js +456 -0
  28. package/dist/skill-execution-cache.js +254 -0
  29. package/dist/smart-brain-router.js +184 -0
  30. package/dist/storage-backend.js +10 -8
  31. package/dist/token-budget.js +88 -0
  32. package/package.json +2 -3
  33. package/scripts/postinstall.js +10 -59
  34. package/skills/CLAUDE.md +0 -5
  35. package/skills/_shared/helpers.md +2 -8
  36. package/skills/cm-browse/SKILL.md +6 -0
  37. package/skills/cm-conductor-worktrees/SKILL.md +4 -0
  38. package/skills/cm-content-factory/landing/docs/content/changelog.md +4 -4
  39. package/skills/cm-content-factory/landing/docs/content/deployment.md +3 -3
  40. package/skills/cm-content-factory/landing/docs/content/execution-flow.md +8 -8
  41. package/skills/cm-content-factory/landing/docs/content/memory-system.md +38 -0
  42. package/skills/cm-content-factory/landing/docs/content/openspace.md +1 -1
  43. package/skills/cm-content-factory/landing/docs/content/use-cases.md +2 -2
  44. package/skills/cm-content-factory/landing/docs/content/v5-intro.md +3 -3
  45. package/skills/cm-content-factory/landing/docs/index.html +1 -1
  46. package/skills/cm-content-factory/landing/index.html +3 -3
  47. package/skills/cm-content-factory/landing/translations.js +37 -37
  48. package/skills/cm-continuity/SKILL.md +32 -33
  49. package/skills/cm-design-studio/SKILL.md +4 -0
  50. package/skills/cm-ecosystem-roadmap/SKILL.md +4 -0
  51. package/skills/cm-engineering-meta/SKILL.md +4 -0
  52. package/skills/cm-guardian-runtime/SKILL.md +5 -1
  53. package/skills/cm-mcp-engineering/SKILL.md +4 -0
  54. package/skills/cm-post-deploy-canary/SKILL.md +4 -0
  55. package/skills/cm-project-bootstrap/SKILL.md +11 -0
  56. package/skills/cm-qa-visual-cli/SKILL.md +4 -0
  57. package/skills/cm-retro-cli/SKILL.md +4 -0
  58. package/skills/cm-second-opinion-cli/SKILL.md +4 -0
  59. package/skills/cm-security-gate/SKILL.md +1 -0
  60. package/skills/cm-skill-chain/SKILL.md +25 -4
  61. package/skills/cm-skill-evolution/SKILL.md +83 -0
  62. package/skills/cm-skill-health/SKILL.md +83 -0
  63. package/skills/cm-skill-index/SKILL.md +11 -3
  64. package/skills/cm-skill-search/SKILL.md +49 -0
  65. package/skills/cm-skill-share/SKILL.md +58 -0
  66. package/skills/cm-sprint-bus/SKILL.md +4 -0
  67. package/skills/cm-start/SKILL.md +0 -10
  68. package/skills/cm-tdd/SKILL.md +2 -2
  69. package/skills/profiles/full.txt +4 -0
  70. package/install.sh +0 -1125
  71. package/scripts/viking-demo.ts +0 -105
  72. 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
+ }
@@ -1,11 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VikingBackend = exports.SqliteBackend = void 0;
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
- * For `viking` backend, reads `storage.viking.*` for connection config.
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, _c;
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
- const vikingConfig = Object.assign(Object.assign({}, viking_http_client_1.DEFAULT_VIKING_CONFIG), (_c = cfg.storage) === null || _c === void 0 ? void 0 : _c.viking);
57
- return new viking_backend_1.VikingBackend(vikingConfig);
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:
@@ -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": "4.8.0",
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"