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.
Files changed (161) hide show
  1. package/CHANGELOG.md +74 -8
  2. package/README.md +192 -95
  3. package/dist/advisory-handoff.js +89 -0
  4. package/dist/advisory-report.js +105 -0
  5. package/dist/browse-server.js +251 -0
  6. package/dist/cli/command-registry.js +34 -0
  7. package/dist/cli/commands/agent.js +120 -0
  8. package/dist/cli/commands/bench.js +69 -0
  9. package/dist/cli/commands/brain.js +108 -0
  10. package/dist/cli/commands/dashboard.js +93 -0
  11. package/dist/cli/commands/design-studio.js +111 -0
  12. package/dist/cli/commands/distro.js +25 -0
  13. package/dist/cli/commands/engineering.js +596 -0
  14. package/dist/cli/commands/evolve.js +123 -0
  15. package/dist/cli/commands/mcp-serve.js +104 -0
  16. package/dist/cli/commands/project.js +324 -0
  17. package/dist/cli/commands/skill-chain.js +269 -0
  18. package/dist/cli/commands/system.js +89 -0
  19. package/dist/cli/commands/task.js +254 -0
  20. package/dist/cli/update-check.js +83 -0
  21. package/dist/cm-config.js +92 -0
  22. package/dist/cm-suggest.js +77 -0
  23. package/dist/codybench/judges/automated.js +31 -0
  24. package/dist/codybench/runners/claude-code.js +32 -0
  25. package/dist/codybench/suites/memory-retention.js +85 -0
  26. package/dist/codybench/suites/tdd-regression.js +35 -0
  27. package/dist/codybench/suites/token-efficiency.js +55 -0
  28. package/dist/codybench/types.js +2 -0
  29. package/dist/context-db.js +157 -0
  30. package/dist/continuity.js +2 -6
  31. package/dist/distro-validate.js +54 -0
  32. package/dist/execution-analyzer.js +138 -0
  33. package/dist/guardian-core.js +74 -0
  34. package/dist/index.js +36 -2759
  35. package/dist/indexer/skills-lib.js +533 -0
  36. package/dist/indexer/skills-map.js +1374 -0
  37. package/dist/indexer/skills.js +16 -0
  38. package/dist/learning-promoter.js +246 -0
  39. package/dist/mcp-context-server.js +289 -1
  40. package/dist/mcp-skills-tools.js +81 -0
  41. package/dist/retro-summary.js +70 -0
  42. package/dist/second-opinion-providers.js +79 -0
  43. package/dist/skill-chain.js +63 -1
  44. package/dist/skill-evolver.js +456 -0
  45. package/dist/skill-execution-cache.js +254 -0
  46. package/dist/smart-brain-router.js +184 -0
  47. package/dist/sprint-pipeline.js +228 -0
  48. package/dist/storage-backend.js +14 -67
  49. package/dist/token-budget.js +88 -0
  50. package/dist/utils/cli-utils.js +76 -0
  51. package/dist/utils/skill-utils.js +32 -0
  52. package/package.json +17 -7
  53. package/scripts/build-skills.mjs +51 -0
  54. package/scripts/gate-0-repo-hygiene.js +75 -0
  55. package/scripts/postinstall.js +34 -28
  56. package/scripts/security-scan.js +1 -1
  57. package/scripts/validate-skills.mjs +42 -0
  58. package/skills/CLAUDE.md +2 -7
  59. package/skills/_shared/helpers.md +2 -8
  60. package/skills/cm-ads-tracker/SKILL.md +3 -6
  61. package/skills/cm-browse/SKILL.md +34 -0
  62. package/skills/cm-conductor-worktrees/SKILL.md +28 -0
  63. package/skills/cm-content-factory/SKILL.md +1 -1
  64. package/skills/cm-content-factory/landing/docs/content/changelog.md +36 -0
  65. package/skills/cm-content-factory/landing/docs/content/deployment.md +46 -0
  66. package/skills/cm-content-factory/landing/docs/content/execution-flow.md +67 -0
  67. package/skills/cm-content-factory/landing/docs/content/memory-system.md +38 -0
  68. package/skills/cm-content-factory/landing/docs/content/openspace.md +27 -0
  69. package/skills/cm-content-factory/landing/docs/content/use-cases.md +26 -0
  70. package/skills/cm-content-factory/landing/docs/content/v5-intro.md +28 -0
  71. package/skills/cm-content-factory/landing/docs/index.html +240 -0
  72. package/skills/cm-content-factory/landing/index.html +100 -100
  73. package/skills/cm-content-factory/landing/script.js +42 -0
  74. package/skills/cm-content-factory/landing/translations.js +400 -400
  75. package/skills/cm-continuity/SKILL.md +32 -33
  76. package/skills/cm-design-studio/SKILL.md +34 -0
  77. package/skills/cm-ecosystem-roadmap/SKILL.md +15 -0
  78. package/skills/cm-engineering-meta/SKILL.md +73 -0
  79. package/skills/cm-growth-hacking/SKILL.md +1 -12
  80. package/skills/cm-guardian-runtime/SKILL.md +26 -0
  81. package/skills/cm-mcp-engineering/SKILL.md +22 -0
  82. package/skills/cm-notebooklm/SKILL.md +1 -17
  83. package/skills/cm-post-deploy-canary/SKILL.md +22 -0
  84. package/skills/cm-project-bootstrap/SKILL.md +11 -0
  85. package/skills/cm-qa-visual-cli/SKILL.md +22 -0
  86. package/skills/cm-retro-cli/SKILL.md +23 -0
  87. package/skills/cm-second-opinion-cli/SKILL.md +23 -0
  88. package/skills/cm-secret-shield/SKILL.md +2 -2
  89. package/skills/cm-security-gate/SKILL.md +1 -0
  90. package/skills/cm-skill-chain/SKILL.md +25 -4
  91. package/skills/cm-skill-evolution/SKILL.md +83 -0
  92. package/skills/cm-skill-health/SKILL.md +83 -0
  93. package/skills/cm-skill-index/SKILL.md +11 -3
  94. package/skills/cm-skill-search/SKILL.md +49 -0
  95. package/skills/cm-skill-share/SKILL.md +58 -0
  96. package/skills/cm-sprint-bus/SKILL.md +33 -0
  97. package/skills/cm-start/SKILL.md +0 -10
  98. package/skills/cm-tdd/SKILL.md +59 -72
  99. package/skills/profiles/README.md +21 -0
  100. package/skills/profiles/core.txt +23 -0
  101. package/skills/profiles/design.txt +6 -0
  102. package/skills/profiles/full.txt +62 -0
  103. package/skills/profiles/growth.txt +10 -0
  104. package/skills/profiles/knowledge.txt +7 -0
  105. package/install.sh +0 -901
  106. package/scripts/test-gemini.js +0 -13
  107. package/skills/cm-frappe-agent/SKILL.md +0 -134
  108. package/skills/cm-frappe-agent/agents/doctype-architect.md +0 -596
  109. package/skills/cm-frappe-agent/agents/erpnext-customizer.md +0 -643
  110. package/skills/cm-frappe-agent/agents/frappe-backend.md +0 -814
  111. package/skills/cm-frappe-agent/agents/frappe-custom-frontend.md +0 -557
  112. package/skills/cm-frappe-agent/agents/frappe-debugger.md +0 -625
  113. package/skills/cm-frappe-agent/agents/frappe-fixer.md +0 -275
  114. package/skills/cm-frappe-agent/agents/frappe-frontend.md +0 -660
  115. package/skills/cm-frappe-agent/agents/frappe-installer.md +0 -158
  116. package/skills/cm-frappe-agent/agents/frappe-performance.md +0 -307
  117. package/skills/cm-frappe-agent/agents/frappe-planner.md +0 -419
  118. package/skills/cm-frappe-agent/agents/frappe-remote-ops.md +0 -153
  119. package/skills/cm-frappe-agent/agents/github-workflow.md +0 -286
  120. package/skills/cm-frappe-agent/commands/frappe-app.md +0 -351
  121. package/skills/cm-frappe-agent/commands/frappe-backend.md +0 -162
  122. package/skills/cm-frappe-agent/commands/frappe-bench.md +0 -254
  123. package/skills/cm-frappe-agent/commands/frappe-debug.md +0 -263
  124. package/skills/cm-frappe-agent/commands/frappe-doctype-create.md +0 -272
  125. package/skills/cm-frappe-agent/commands/frappe-doctype-field.md +0 -310
  126. package/skills/cm-frappe-agent/commands/frappe-erpnext.md +0 -210
  127. package/skills/cm-frappe-agent/commands/frappe-fix.md +0 -59
  128. package/skills/cm-frappe-agent/commands/frappe-frontend.md +0 -210
  129. package/skills/cm-frappe-agent/commands/frappe-fullstack.md +0 -243
  130. package/skills/cm-frappe-agent/commands/frappe-github.md +0 -57
  131. package/skills/cm-frappe-agent/commands/frappe-install.md +0 -52
  132. package/skills/cm-frappe-agent/commands/frappe-plan.md +0 -442
  133. package/skills/cm-frappe-agent/commands/frappe-remote.md +0 -58
  134. package/skills/cm-frappe-agent/commands/frappe-test.md +0 -356
  135. package/skills/cm-frappe-agent/docs/README.md +0 -51
  136. package/skills/cm-frappe-agent/docs/agents-catalog.md +0 -113
  137. package/skills/cm-frappe-agent/docs/architecture.md +0 -149
  138. package/skills/cm-frappe-agent/docs/commands-catalog.md +0 -82
  139. package/skills/cm-frappe-agent/docs/resources-catalog.md +0 -66
  140. package/skills/cm-frappe-agent/docs/sitemap-urls.txt +0 -52
  141. package/skills/cm-frappe-agent/docs/sitemap.md +0 -81
  142. package/skills/cm-frappe-agent/docs/sop/user-guide.md +0 -178
  143. package/skills/cm-frappe-agent/docs/sop/vibe-coding-guide.md +0 -122
  144. package/skills/cm-frappe-agent/resources/7-layer-architecture.md +0 -985
  145. package/skills/cm-frappe-agent/resources/bench_commands.md +0 -73
  146. package/skills/cm-frappe-agent/resources/code-patterns-guide.md +0 -948
  147. package/skills/cm-frappe-agent/resources/common_pitfalls.md +0 -266
  148. package/skills/cm-frappe-agent/resources/doctype-registry.md +0 -158
  149. package/skills/cm-frappe-agent/resources/installation-guide.md +0 -289
  150. package/skills/cm-frappe-agent/resources/rest-api-patterns.md +0 -182
  151. package/skills/cm-frappe-agent/resources/scaffold_checklist.md +0 -82
  152. package/skills/cm-frappe-agent/resources/upgrade_patterns.md +0 -113
  153. package/skills/cm-frappe-agent/resources/web-form-patterns.md +0 -252
  154. package/skills/cm-frappe-agent/skills/bench-commands/SKILL.md +0 -621
  155. package/skills/cm-frappe-agent/skills/client-scripts/SKILL.md +0 -642
  156. package/skills/cm-frappe-agent/skills/doctype-patterns/SKILL.md +0 -576
  157. package/skills/cm-frappe-agent/skills/frappe-api/SKILL.md +0 -740
  158. package/skills/cm-frappe-agent/skills/remote-operations/SKILL.md +0 -47
  159. package/skills/cm-frappe-agent/skills/server-scripts/SKILL.md +0 -608
  160. package/skills/cm-frappe-agent/skills/web-forms/SKILL.md +0 -46
  161. 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
+ }