codymaster 4.4.4 → 4.5.1
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 +33 -0
- package/README.md +29 -14
- package/commands/demo.md +1 -1
- package/dist/context-bus.js +70 -0
- package/dist/context-db.js +265 -0
- package/dist/continuity.js +12 -0
- package/dist/file-watcher.js +79 -0
- package/dist/index.js +152 -1
- package/dist/l0-indexer.js +158 -0
- package/dist/mcp-context-server.js +400 -0
- package/dist/migrate-json-to-sqlite.js +126 -0
- package/dist/skill-chain.js +19 -3
- package/dist/token-budget.js +108 -0
- package/dist/uri-resolver.js +203 -0
- package/package.json +7 -1
- package/skills/_shared/helpers.md +50 -14
- package/skills/cm-autopilot/SKILL.md +29 -0
- package/skills/cm-autopilot/scripts/autopilot.py +190 -0
- package/skills/cm-continuity/SKILL.md +90 -28
- package/skills/cm-quality-gate/SKILL.md +11 -1
- package/skills/cm-safe-deploy/SKILL.md +38 -2
- package/skills/cm-security-gate/SKILL.md +158 -34
- package/skills/cm-skill-chain/SKILL.md +47 -1
- package/skills/cm-start/SKILL.md +11 -2
- package/skills/cm-test-gate/SKILL.md +3 -0
- package/skills/boxme-git-config/SKILL.md +0 -56
- package/skills/boxme-local-dev/SKILL.md +0 -66
- package/skills/jobs-to-be-done/SKILL.md +0 -266
- package/skills/jobs-to-be-done/references/case-studies.md +0 -154
- package/skills/jobs-to-be-done/references/competitive-strategy.md +0 -280
- package/skills/jobs-to-be-done/references/diagnostics.md +0 -158
- package/skills/jobs-to-be-done/references/innovation-process.md +0 -392
- package/skills/jobs-to-be-done/references/organizational-change.md +0 -328
- package/skills/marketplace-report-crawler/SKILL.md +0 -176
- package/skills/marketplace-report-crawler/config/accounts.json +0 -41
- package/skills/marketplace-report-crawler/config/report-types.json +0 -422
- package/skills/marketplace-report-crawler/config/sessions.json +0 -3
- package/skills/marketplace-report-crawler/scripts/ab-wrapper.sh +0 -102
- package/skills/marketplace-report-crawler/scripts/browser-actions/lazada/lazada-actions.js +0 -114
- package/skills/marketplace-report-crawler/scripts/browser-actions/shopee/shopee-actions.js +0 -94
- package/skills/marketplace-report-crawler/scripts/browser-actions/tiktok/tiktok-actions.js +0 -272
- package/skills/marketplace-report-crawler/scripts/crawl-runner.js +0 -281
- package/skills/marketplace-report-crawler/scripts/session-check.sh +0 -72
- package/skills/marketplace-report-crawler/scripts/session-manager.sh +0 -349
- package/skills/marketplace-report-crawler/scripts/setup-folders.sh +0 -83
- package/skills/medical-research/SKILL.md +0 -194
- package/skills/medical-research/scripts/evidence_checker.py +0 -288
- package/skills/mom-test/SKILL.md +0 -267
- package/skills/mom-test/references/avoiding-bad-data.md +0 -221
- package/skills/mom-test/references/case-studies.md +0 -306
- package/skills/mom-test/references/commitment-advancement.md +0 -219
- package/skills/mom-test/references/finding-conversations.md +0 -251
- package/skills/mom-test/references/processing-learning.md +0 -256
- package/skills/mom-test/references/question-patterns.md +0 -198
- package/skills/pandasai-analytics/SKILL.md +0 -251
- package/skills/release-it/SKILL.md +0 -235
- package/skills/release-it/references/anti-patterns.md +0 -279
- package/skills/release-it/references/capacity-planning.md +0 -285
- package/skills/release-it/references/chaos-engineering.md +0 -325
- package/skills/release-it/references/deployment-strategies.md +0 -331
- package/skills/release-it/references/observability.md +0 -301
- package/skills/release-it/references/stability-patterns.md +0 -355
- package/skills/skill-creator-ultra/.agents/workflows/skill-audit.md +0 -37
- package/skills/skill-creator-ultra/.agents/workflows/skill-compare.md +0 -34
- package/skills/skill-creator-ultra/.agents/workflows/skill-export.md +0 -51
- package/skills/skill-creator-ultra/.agents/workflows/skill-generate.md +0 -39
- package/skills/skill-creator-ultra/.agents/workflows/skill-scaffold.md +0 -52
- package/skills/skill-creator-ultra/.agents/workflows/skill-simulate.md +0 -25
- package/skills/skill-creator-ultra/.agents/workflows/skill-stats.md +0 -31
- package/skills/skill-creator-ultra/.agents/workflows/skill-validate.md +0 -25
- package/skills/skill-creator-ultra/README.md +0 -1242
- package/skills/skill-creator-ultra/SKILL.md +0 -388
- package/skills/skill-creator-ultra/agents/analyzer.md +0 -274
- package/skills/skill-creator-ultra/agents/comparator.md +0 -202
- package/skills/skill-creator-ultra/agents/grader.md +0 -223
- package/skills/skill-creator-ultra/assets/eval_review.html +0 -146
- package/skills/skill-creator-ultra/eval-viewer/generate_review.py +0 -471
- package/skills/skill-creator-ultra/eval-viewer/viewer.html +0 -1325
- package/skills/skill-creator-ultra/examples/example_anthropic_frontend.md +0 -109
- package/skills/skill-creator-ultra/examples/example_anthropic_pdf.md +0 -116
- package/skills/skill-creator-ultra/examples/example_api_docs.md +0 -189
- package/skills/skill-creator-ultra/examples/example_db_migration.md +0 -253
- package/skills/skill-creator-ultra/examples/example_git_commit.md +0 -111
- package/skills/skill-creator-ultra/install.ps1 +0 -289
- package/skills/skill-creator-ultra/install.sh +0 -313
- package/skills/skill-creator-ultra/phases/phase1_interview.md +0 -202
- package/skills/skill-creator-ultra/phases/phase2_extract.md +0 -55
- package/skills/skill-creator-ultra/phases/phase3_detect.md +0 -57
- package/skills/skill-creator-ultra/phases/phase4_generate.md +0 -543
- package/skills/skill-creator-ultra/phases/phase5_test.md +0 -319
- package/skills/skill-creator-ultra/phases/phase6_eval.md +0 -301
- package/skills/skill-creator-ultra/phases/phase7_iterate.md +0 -103
- package/skills/skill-creator-ultra/phases/phase8_optimize.md +0 -113
- package/skills/skill-creator-ultra/resources/advanced_patterns.md +0 -499
- package/skills/skill-creator-ultra/resources/anti_patterns.md +0 -376
- package/skills/skill-creator-ultra/resources/blueprints.md +0 -498
- package/skills/skill-creator-ultra/resources/checklist.md +0 -243
- package/skills/skill-creator-ultra/resources/composition_cookbook.md +0 -291
- package/skills/skill-creator-ultra/resources/description_optimization.md +0 -90
- package/skills/skill-creator-ultra/resources/eval_guide.md +0 -133
- package/skills/skill-creator-ultra/resources/industry_questions.md +0 -189
- package/skills/skill-creator-ultra/resources/interview_questions.md +0 -200
- package/skills/skill-creator-ultra/resources/pattern_detection.md +0 -200
- package/skills/skill-creator-ultra/resources/prompt_engineering.md +0 -531
- package/skills/skill-creator-ultra/resources/schemas.md +0 -430
- package/skills/skill-creator-ultra/resources/script_integration.md +0 -593
- package/skills/skill-creator-ultra/resources/scripts_guide.md +0 -339
- package/skills/skill-creator-ultra/resources/skill_template.md +0 -124
- package/skills/skill-creator-ultra/resources/skill_writing_guide.md +0 -634
- package/skills/skill-creator-ultra/resources/versioning_guide.md +0 -193
- package/skills/skill-creator-ultra/scripts/ci_eval.py +0 -200
- package/skills/skill-creator-ultra/scripts/package_skill.py +0 -165
- package/skills/skill-creator-ultra/scripts/simulate_skill.py +0 -398
- package/skills/skill-creator-ultra/scripts/skill_audit.py +0 -611
- package/skills/skill-creator-ultra/scripts/skill_compare.py +0 -265
- package/skills/skill-creator-ultra/scripts/skill_export.py +0 -334
- package/skills/skill-creator-ultra/scripts/skill_scaffold.py +0 -403
- package/skills/skill-creator-ultra/scripts/skill_stats.py +0 -339
- package/skills/skill-creator-ultra/scripts/validate_skill.py +0 -411
- package/skills/tailwind-mastery/SKILL.md +0 -229
- package/skills/vercel-react-best-practices/AGENTS.md +0 -3373
- package/skills/vercel-react-best-practices/README.md +0 -123
- package/skills/vercel-react-best-practices/SKILL.md +0 -143
- package/skills/vercel-react-best-practices/rules/_sections.md +0 -46
- package/skills/vercel-react-best-practices/rules/_template.md +0 -28
- package/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -55
- package/skills/vercel-react-best-practices/rules/advanced-init-once.md +0 -42
- package/skills/vercel-react-best-practices/rules/advanced-use-latest.md +0 -39
- package/skills/vercel-react-best-practices/rules/async-api-routes.md +0 -38
- package/skills/vercel-react-best-practices/rules/async-defer-await.md +0 -80
- package/skills/vercel-react-best-practices/rules/async-dependencies.md +0 -51
- package/skills/vercel-react-best-practices/rules/async-parallel.md +0 -28
- package/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -99
- package/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -59
- package/skills/vercel-react-best-practices/rules/bundle-conditional.md +0 -31
- package/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -49
- package/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -35
- package/skills/vercel-react-best-practices/rules/bundle-preload.md +0 -50
- package/skills/vercel-react-best-practices/rules/client-event-listeners.md +0 -74
- package/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -71
- package/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -48
- package/skills/vercel-react-best-practices/rules/client-swr-dedup.md +0 -56
- package/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -107
- package/skills/vercel-react-best-practices/rules/js-cache-function-results.md +0 -80
- package/skills/vercel-react-best-practices/rules/js-cache-property-access.md +0 -28
- package/skills/vercel-react-best-practices/rules/js-cache-storage.md +0 -70
- package/skills/vercel-react-best-practices/rules/js-combine-iterations.md +0 -32
- package/skills/vercel-react-best-practices/rules/js-early-exit.md +0 -50
- package/skills/vercel-react-best-practices/rules/js-flatmap-filter.md +0 -60
- package/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -45
- package/skills/vercel-react-best-practices/rules/js-index-maps.md +0 -37
- package/skills/vercel-react-best-practices/rules/js-length-check-first.md +0 -49
- package/skills/vercel-react-best-practices/rules/js-min-max-loop.md +0 -82
- package/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -24
- package/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -57
- package/skills/vercel-react-best-practices/rules/rendering-activity.md +0 -26
- package/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
- package/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -40
- package/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -38
- package/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -46
- package/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
- package/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +0 -30
- package/skills/vercel-react-best-practices/rules/rendering-resource-hints.md +0 -85
- package/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +0 -68
- package/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -28
- package/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +0 -75
- package/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -39
- package/skills/vercel-react-best-practices/rules/rerender-dependencies.md +0 -45
- package/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +0 -40
- package/skills/vercel-react-best-practices/rules/rerender-derived-state.md +0 -29
- package/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -74
- package/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -58
- package/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +0 -38
- package/skills/vercel-react-best-practices/rules/rerender-memo.md +0 -44
- package/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +0 -45
- package/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +0 -82
- package/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +0 -35
- package/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +0 -64
- package/skills/vercel-react-best-practices/rules/rerender-transitions.md +0 -40
- package/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md +0 -59
- package/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +0 -73
- package/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -73
- package/skills/vercel-react-best-practices/rules/server-auth-actions.md +0 -96
- package/skills/vercel-react-best-practices/rules/server-cache-lru.md +0 -41
- package/skills/vercel-react-best-practices/rules/server-cache-react.md +0 -76
- package/skills/vercel-react-best-practices/rules/server-dedup-props.md +0 -65
- package/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +0 -142
- package/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -83
- package/skills/vercel-react-best-practices/rules/server-serialization.md +0 -38
- package/skills/web-design-guidelines/SKILL.md +0 -39
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,39 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
Categories: 🚀 **Improvements** | 🐛 **Bug Fixes** | 🔒 **Security**
|
|
6
6
|
|
|
7
|
+
## [4.5.0] - 2026-03-31
|
|
8
|
+
|
|
9
|
+
### 🚀 Improvements — Context Backbone v5 (Smart Spine)
|
|
10
|
+
|
|
11
|
+
- **SQLite + FTS5 Storage Layer** — Learnings and decisions migrated from flat JSON to a WAL-mode SQLite database (`.cm/context.db`) with full-text search via FTS5 virtual tables. BM25-ranked `cm_query` replaces linear JSON scans. FTS5 indexes are kept in sync automatically via `AFTER INSERT`/`AFTER DELETE` triggers.
|
|
12
|
+
- **L0 / L1 / L2 Progressive Loading** — Every memory resource is now available at three granularities. L0 compact indexes (`learnings-index.md`, `skeleton-index.md`) reduce context cost by up to 96% for the common "just give me a summary" case. L0 is pre-generated on every `addLearning()` write and refreshable on demand.
|
|
13
|
+
- **cm:// URI Scheme** — Unified content addressing for all CodyMaster resources. Skills reference context by URI (`cm://memory/learnings`, `cm://skills/cm-tdd/L0`, `cm://pipeline/current`) and the resolver handles depth selection, caching, and fallbacks transparently.
|
|
14
|
+
- **MCP Context Server** — Standalone stdio MCP server (`src/mcp-context-server.ts`) exposing 7 tools to Claude Desktop and any MCP-compatible client:
|
|
15
|
+
- `cm_query` — FTS5 search across learnings + decisions with scope filter
|
|
16
|
+
- `cm_resolve` — resolve any `cm://` URI at L0/L1/L2
|
|
17
|
+
- `cm_bus_read` / `cm_bus_write` — read/publish to the context bus
|
|
18
|
+
- `cm_budget_check` — pre-flight token budget check by category
|
|
19
|
+
- `cm_memory_decay` — TTL-based archival with `dry_run` option
|
|
20
|
+
- `cm_index_refresh` — regenerate L0 indexes on demand
|
|
21
|
+
- **Token Budget Enforcement** — 200k-token window pre-allocated by category in `.cm/token-budget.json`. Budget checked at load time; overages return a remediation suggestion. Configurable per-project.
|
|
22
|
+
- **Context Bus** — `.cm/context-bus.json` tracks skill chain state across steps. Each skill publishes its output; downstream skills read what upstream steps produced via `cm://pipeline/current`. Integrated into `createChainExecution()` and `advanceChain()`.
|
|
23
|
+
- **File Watcher** — `src/file-watcher.ts` (chokidar) watches `.cm/memory/*.json` and auto-regenerates the L0 learnings index on change with 300ms debounce.
|
|
24
|
+
- **JSON → SQLite Migration** — One-time migration utility (`src/migrate-json-to-sqlite.ts`) reads existing `learnings.json` / `decisions.json`, inserts into SQLite, and creates `.backup` files. Handles both camelCase and snake_case legacy field names.
|
|
25
|
+
- **New CLI commands** — `cm continuity index` (regenerate L0 indexes), `cm continuity budget` (show token allocation table), `cm continuity bus` (pretty-print context bus), `cm continuity mcp` (print Claude Desktop config snippet).
|
|
26
|
+
- **Test suite expanded** — 4 new test files covering SQLite CRUD + FTS5 relevance, context bus roundtrip, token budget enforcement, and L0 index generation. Test count: 78 → 92 (all passing).
|
|
27
|
+
|
|
28
|
+
## [4.4.5] - 2026-03-30
|
|
29
|
+
|
|
30
|
+
### 🔒 Security
|
|
31
|
+
|
|
32
|
+
- **Security Checkpoints Upgraded** — Deployed unified security updates across `cm-security-gate`, `cm-quality-gate`, `cm-safe-deploy`, and `cm-test-gate`.
|
|
33
|
+
|
|
34
|
+
## [4.4.4] - 2026-03-29
|
|
35
|
+
|
|
36
|
+
### 🐛 Bug Fixes
|
|
37
|
+
|
|
38
|
+
- **Version Bump** — Minor bug fixes and dependency updates.
|
|
39
|
+
|
|
7
40
|
## [4.4.3] - 2026-03-29
|
|
8
41
|
|
|
9
42
|
### 🚀 Improvements — The Self-Healing Update
|
package/README.md
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
### Your AI Agent is smart. CodyMaster makes it *wise*.
|
|
8
8
|
|
|
9
|
-
**68+ Skills ·
|
|
9
|
+
**68+ Skills · 18 Commands · 1 Plugin · 7+ Platforms · 6 Languages**
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<img alt="Version" src="https://img.shields.io/badge/version-4.
|
|
12
|
+
<img alt="Version" src="https://img.shields.io/badge/version-4.5.0-blue.svg?cacheSeconds=2592000" />
|
|
13
13
|
<img alt="Skills" src="https://img.shields.io/badge/skills-68+-success.svg" />
|
|
14
14
|
<img alt="Platforms" src="https://img.shields.io/badge/platforms-7+-orange.svg" />
|
|
15
15
|
<img alt="Open Source" src="https://img.shields.io/badge/license-MIT-purple.svg" />
|
|
@@ -105,9 +105,9 @@ graph LR
|
|
|
105
105
|
class A,B,C,D,E,F,G,H,I,J,K,L phase;
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
### 🧠 The Unified Brain: 5-Tier Memory
|
|
108
|
+
### 🧠 The Unified Brain: 5-Tier Memory + Smart Spine
|
|
109
109
|
|
|
110
|
-
Your AI doesn't just execute — it **understands and remembers** using a multi-scale, 5-Tier
|
|
110
|
+
Your AI doesn't just execute — it **understands and remembers** using a multi-scale, 5-Tier + Smart Spine architecture that persists across sessions and machines:
|
|
111
111
|
|
|
112
112
|
1. **Sensory Memory (Session)** — Immediate context of active files and terminals.
|
|
113
113
|
2. **Working Memory (`cm-continuity`)** — Cross-session scratchpad. AI never repeats the same mistake.
|
|
@@ -115,10 +115,18 @@ Your AI doesn't just execute — it **understands and remembers** using a multi-
|
|
|
115
115
|
4. **Semantic Memory (`cm-deep-search`)** — Local vector search across docs using `qmd`.
|
|
116
116
|
5. **Structural Memory (`cm-codeintell`)** — AST-based CodeGraph. Up to 95% token compression for full codebase context.
|
|
117
117
|
|
|
118
|
+
🦴 **Smart Spine (v4.5+)** — The nervous system connecting all 5 tiers:
|
|
119
|
+
- **SQLite + FTS5** — BM25-ranked keyword search replaces flat JSON scans.
|
|
120
|
+
- **Progressive Loading (L0/L1/L2)** — Context loaded at cheapest sufficient depth. 78% token savings.
|
|
121
|
+
- **cm:// URI Scheme** — Skills request context by URI, not file paths.
|
|
122
|
+
- **Token Budget** — 200k window pre-allocated by category. No more silent overflow.
|
|
123
|
+
- **Context Bus** — Skills share outputs in real-time within a chain.
|
|
124
|
+
- **MCP Server** — 7 tools exposed to Claude Desktop and any MCP client.
|
|
125
|
+
|
|
118
126
|
☁️ **The Cloud Brain (`cm-notebooklm`)**
|
|
119
127
|
High-value knowledge and design patterns are synced to NotebookLM, providing a universal, cross-machine "Soul" for your project. Auto-generate podcasts and flashcards to onboard human developers alongside the AI.
|
|
120
128
|
|
|
121
|
-
📖 [Read the full Knowledge Architecture →](docs/knowledge-architecture.md)
|
|
129
|
+
📖 [Read the full Knowledge Architecture →](docs/architecture/knowledge-architecture.md)
|
|
122
130
|
|
|
123
131
|
### 🛡️ Multi-Layer Protection (Your Codebase Won't Get Destroyed)
|
|
124
132
|
|
|
@@ -166,7 +174,7 @@ Don't know what the old code does? **`cm-dockit`** reads your entire codebase an
|
|
|
166
174
|
|
|
167
175
|
Before diving into code for complex requests, **`cm-brainstorm-idea`** evaluates your product using multi-dimensional analysis (Tech, Product, Design, Business). It generates 2-3 qualified options using the 9 Windows (TRIZ) framework and provides a visual UI Preview via **Pencil.dev** or **Google Stitch** to validate the direction before detailed planning.
|
|
168
176
|
|
|
169
|
-
📖 [Read more about the UI Preview Phase →](docs/
|
|
177
|
+
📖 [Read more about the UI Preview Phase →](docs/workflows/brainstorm-ui-preview.md)
|
|
170
178
|
|
|
171
179
|
|
|
172
180
|
### 🏭 AI Content Factory v2.0 & Visual Dashboard
|
|
@@ -294,14 +302,21 @@ The CLI will greet you and keep you organized on your long coding sessions!
|
|
|
294
302
|
## 🎮 Commands
|
|
295
303
|
|
|
296
304
|
```
|
|
297
|
-
cm
|
|
298
|
-
cm task add "..."
|
|
299
|
-
cm task list
|
|
300
|
-
cm status
|
|
301
|
-
cm dashboard
|
|
302
|
-
cm list
|
|
303
|
-
cm profile
|
|
304
|
-
cm deploy <env>
|
|
305
|
+
cm → Quick menu with Cody 🐹
|
|
306
|
+
cm task add "..." → Add a task
|
|
307
|
+
cm task list → View tasks
|
|
308
|
+
cm status → Project health
|
|
309
|
+
cm dashboard → Open Mission Control
|
|
310
|
+
cm list → Browse 68+ skills
|
|
311
|
+
cm profile → Your stats & achievements
|
|
312
|
+
cm deploy <env> → Record deployment
|
|
313
|
+
cm continuity index → Regenerate L0 memory indexes
|
|
314
|
+
cm continuity budget → Show token budget allocation
|
|
315
|
+
cm continuity bus → View context bus state
|
|
316
|
+
cm continuity mcp → Print MCP server config
|
|
317
|
+
cm continuity migrate → Migrate JSON → SQLite
|
|
318
|
+
cm continuity export → Export SQLite → JSON
|
|
319
|
+
cm resolve <uri> → Resolve any cm:// URI
|
|
305
320
|
```
|
|
306
321
|
|
|
307
322
|
**Slash Commands (inside AI agents):**
|
package/commands/demo.md
CHANGED
|
@@ -24,7 +24,7 @@ Remember the chosen language and respond in it for all following steps.
|
|
|
24
24
|
|
|
25
25
|
Show a warm, concise welcome. Include:
|
|
26
26
|
- What CodyMaster is (1 sentence)
|
|
27
|
-
- That it gives AI agents
|
|
27
|
+
- That it gives AI agents 60+ specialized skills
|
|
28
28
|
- That this demo will take ~2 minutes
|
|
29
29
|
|
|
30
30
|
## Step 3 — Show the Workflow
|
|
@@ -0,0 +1,70 @@
|
|
|
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.initBus = initBus;
|
|
7
|
+
exports.readBus = readBus;
|
|
8
|
+
exports.writeBus = writeBus;
|
|
9
|
+
exports.updateBusStep = updateBusStep;
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
// ─── Constants ──────────────────────────────────────────────────────────────
|
|
13
|
+
const CM_DIR = '.cm';
|
|
14
|
+
const BUS_FILE = 'context-bus.json';
|
|
15
|
+
function getBusPath(projectPath) {
|
|
16
|
+
return path_1.default.join(projectPath, CM_DIR, BUS_FILE);
|
|
17
|
+
}
|
|
18
|
+
// ─── Init ───────────────────────────────────────────────────────────────────
|
|
19
|
+
function initBus(projectPath, pipeline, sessionId) {
|
|
20
|
+
const now = new Date().toISOString();
|
|
21
|
+
const bus = {
|
|
22
|
+
version: '1.0',
|
|
23
|
+
session_id: sessionId,
|
|
24
|
+
pipeline,
|
|
25
|
+
current_step: '',
|
|
26
|
+
started_at: now,
|
|
27
|
+
updated_at: now,
|
|
28
|
+
shared_context: {},
|
|
29
|
+
resource_state: {
|
|
30
|
+
skeleton_generated: null,
|
|
31
|
+
learnings_indexed: null,
|
|
32
|
+
codegraph_indexed: null,
|
|
33
|
+
qmd_synced: null,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
writeBus(projectPath, bus);
|
|
37
|
+
return bus;
|
|
38
|
+
}
|
|
39
|
+
// ─── Read ───────────────────────────────────────────────────────────────────
|
|
40
|
+
function readBus(projectPath) {
|
|
41
|
+
const busPath = getBusPath(projectPath);
|
|
42
|
+
if (!fs_1.default.existsSync(busPath))
|
|
43
|
+
return null;
|
|
44
|
+
try {
|
|
45
|
+
return JSON.parse(fs_1.default.readFileSync(busPath, 'utf-8'));
|
|
46
|
+
}
|
|
47
|
+
catch (_a) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// ─── Write ──────────────────────────────────────────────────────────────────
|
|
52
|
+
function writeBus(projectPath, bus) {
|
|
53
|
+
const busPath = getBusPath(projectPath);
|
|
54
|
+
const dir = path_1.default.dirname(busPath);
|
|
55
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
56
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
fs_1.default.writeFileSync(busPath, JSON.stringify(bus, null, 2), 'utf-8');
|
|
59
|
+
}
|
|
60
|
+
// ─── Update Step ────────────────────────────────────────────────────────────
|
|
61
|
+
function updateBusStep(projectPath, skill, output) {
|
|
62
|
+
const bus = readBus(projectPath);
|
|
63
|
+
if (!bus) {
|
|
64
|
+
throw new Error(`Context bus not initialized at ${projectPath}. Call initBus() first.`);
|
|
65
|
+
}
|
|
66
|
+
bus.current_step = skill;
|
|
67
|
+
bus.updated_at = new Date().toISOString();
|
|
68
|
+
bus.shared_context[skill] = output;
|
|
69
|
+
writeBus(projectPath, bus);
|
|
70
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
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.openDb = openDb;
|
|
7
|
+
exports.closeDb = closeDb;
|
|
8
|
+
exports.insertLearning = insertLearning;
|
|
9
|
+
exports.getLearningById = getLearningById;
|
|
10
|
+
exports.queryLearnings = queryLearnings;
|
|
11
|
+
exports.insertDecision = insertDecision;
|
|
12
|
+
exports.queryDecisions = queryDecisions;
|
|
13
|
+
exports.upsertIndex = upsertIndex;
|
|
14
|
+
exports.getIndex = getIndex;
|
|
15
|
+
exports.writeSkillOutput = writeSkillOutput;
|
|
16
|
+
exports.getSkillOutputs = getSkillOutputs;
|
|
17
|
+
exports.getDbPath = getDbPath;
|
|
18
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
19
|
+
const path_1 = __importDefault(require("path"));
|
|
20
|
+
// ─── DB Cache (one connection per path) ─────────────────────────────────────
|
|
21
|
+
const dbCache = new Map();
|
|
22
|
+
function getDb(dbPath) {
|
|
23
|
+
if (dbCache.has(dbPath))
|
|
24
|
+
return dbCache.get(dbPath);
|
|
25
|
+
const db = new better_sqlite3_1.default(dbPath);
|
|
26
|
+
db.pragma('journal_mode = WAL');
|
|
27
|
+
db.pragma('foreign_keys = ON');
|
|
28
|
+
dbCache.set(dbPath, db);
|
|
29
|
+
return db;
|
|
30
|
+
}
|
|
31
|
+
// ─── Schema ─────────────────────────────────────────────────────────────────
|
|
32
|
+
const SCHEMA = `
|
|
33
|
+
CREATE TABLE IF NOT EXISTS learnings (
|
|
34
|
+
id TEXT PRIMARY KEY,
|
|
35
|
+
what_failed TEXT NOT NULL,
|
|
36
|
+
why_failed TEXT NOT NULL DEFAULT '',
|
|
37
|
+
how_to_prevent TEXT NOT NULL DEFAULT '',
|
|
38
|
+
scope TEXT DEFAULT 'global',
|
|
39
|
+
ttl INTEGER DEFAULT 60,
|
|
40
|
+
reinforce_count INTEGER DEFAULT 0,
|
|
41
|
+
status TEXT DEFAULT 'active',
|
|
42
|
+
created_at TEXT NOT NULL,
|
|
43
|
+
updated_at TEXT NOT NULL,
|
|
44
|
+
agent TEXT,
|
|
45
|
+
task_id TEXT,
|
|
46
|
+
module TEXT
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS learnings_fts USING fts5(
|
|
50
|
+
what_failed, why_failed, how_to_prevent,
|
|
51
|
+
content=learnings, content_rowid=rowid
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
CREATE TRIGGER IF NOT EXISTS learnings_ai AFTER INSERT ON learnings BEGIN
|
|
55
|
+
INSERT INTO learnings_fts(rowid, what_failed, why_failed, how_to_prevent)
|
|
56
|
+
VALUES (new.rowid, new.what_failed, new.why_failed, new.how_to_prevent);
|
|
57
|
+
END;
|
|
58
|
+
|
|
59
|
+
CREATE TRIGGER IF NOT EXISTS learnings_ad AFTER DELETE ON learnings BEGIN
|
|
60
|
+
INSERT INTO learnings_fts(learnings_fts, rowid, what_failed, why_failed, how_to_prevent)
|
|
61
|
+
VALUES('delete', old.rowid, old.what_failed, old.why_failed, old.how_to_prevent);
|
|
62
|
+
END;
|
|
63
|
+
|
|
64
|
+
CREATE TABLE IF NOT EXISTS decisions (
|
|
65
|
+
id TEXT PRIMARY KEY,
|
|
66
|
+
decision TEXT NOT NULL,
|
|
67
|
+
rationale TEXT NOT NULL DEFAULT '',
|
|
68
|
+
scope TEXT DEFAULT 'global',
|
|
69
|
+
status TEXT DEFAULT 'active',
|
|
70
|
+
superseded_by TEXT,
|
|
71
|
+
created_at TEXT NOT NULL,
|
|
72
|
+
agent TEXT
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS decisions_fts USING fts5(
|
|
76
|
+
decision, rationale,
|
|
77
|
+
content=decisions, content_rowid=rowid
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
CREATE TRIGGER IF NOT EXISTS decisions_ai AFTER INSERT ON decisions BEGIN
|
|
81
|
+
INSERT INTO decisions_fts(rowid, decision, rationale)
|
|
82
|
+
VALUES (new.rowid, new.decision, new.rationale);
|
|
83
|
+
END;
|
|
84
|
+
|
|
85
|
+
CREATE TRIGGER IF NOT EXISTS decisions_ad AFTER DELETE ON decisions BEGIN
|
|
86
|
+
INSERT INTO decisions_fts(decisions_fts, rowid, decision, rationale)
|
|
87
|
+
VALUES ('delete', old.rowid, old.decision, old.rationale);
|
|
88
|
+
END;
|
|
89
|
+
|
|
90
|
+
CREATE TABLE IF NOT EXISTS skill_outputs (
|
|
91
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
92
|
+
session_id TEXT NOT NULL,
|
|
93
|
+
chain_id TEXT,
|
|
94
|
+
skill TEXT NOT NULL,
|
|
95
|
+
output_path TEXT,
|
|
96
|
+
summary TEXT,
|
|
97
|
+
affected_files TEXT,
|
|
98
|
+
metadata TEXT,
|
|
99
|
+
created_at TEXT NOT NULL
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
CREATE TABLE IF NOT EXISTS indexes (
|
|
103
|
+
resource TEXT NOT NULL,
|
|
104
|
+
level TEXT NOT NULL,
|
|
105
|
+
content TEXT NOT NULL,
|
|
106
|
+
token_count INTEGER,
|
|
107
|
+
generated_at TEXT NOT NULL,
|
|
108
|
+
source_hash TEXT,
|
|
109
|
+
PRIMARY KEY (resource, level)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
CREATE TABLE IF NOT EXISTS token_usage (
|
|
113
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
114
|
+
session_id TEXT NOT NULL,
|
|
115
|
+
category TEXT NOT NULL,
|
|
116
|
+
tokens_used INTEGER NOT NULL,
|
|
117
|
+
timestamp TEXT NOT NULL
|
|
118
|
+
);
|
|
119
|
+
`;
|
|
120
|
+
// ─── Open / Close ────────────────────────────────────────────────────────────
|
|
121
|
+
function openDb(dbPath) {
|
|
122
|
+
const db = getDb(dbPath);
|
|
123
|
+
db.exec(SCHEMA);
|
|
124
|
+
return db;
|
|
125
|
+
}
|
|
126
|
+
function closeDb(dbPath) {
|
|
127
|
+
const db = dbCache.get(dbPath);
|
|
128
|
+
if (db) {
|
|
129
|
+
try {
|
|
130
|
+
db.close();
|
|
131
|
+
}
|
|
132
|
+
catch ( /* already closed */_a) { /* already closed */ }
|
|
133
|
+
dbCache.delete(dbPath);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// ─── Learnings ───────────────────────────────────────────────────────────────
|
|
137
|
+
function insertLearning(dbPath, learning) {
|
|
138
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
139
|
+
const db = openDb(dbPath);
|
|
140
|
+
db.prepare(`
|
|
141
|
+
INSERT OR REPLACE INTO learnings
|
|
142
|
+
(id, what_failed, why_failed, how_to_prevent, scope, ttl, reinforce_count,
|
|
143
|
+
status, created_at, updated_at, agent, task_id, module)
|
|
144
|
+
VALUES
|
|
145
|
+
(@id, @what_failed, @why_failed, @how_to_prevent, @scope, @ttl, @reinforce_count,
|
|
146
|
+
@status, @created_at, @updated_at, @agent, @task_id, @module)
|
|
147
|
+
`).run({
|
|
148
|
+
id: learning.id,
|
|
149
|
+
what_failed: learning.what_failed,
|
|
150
|
+
why_failed: (_a = learning.why_failed) !== null && _a !== void 0 ? _a : '',
|
|
151
|
+
how_to_prevent: (_b = learning.how_to_prevent) !== null && _b !== void 0 ? _b : '',
|
|
152
|
+
scope: (_c = learning.scope) !== null && _c !== void 0 ? _c : 'global',
|
|
153
|
+
ttl: (_d = learning.ttl) !== null && _d !== void 0 ? _d : 60,
|
|
154
|
+
reinforce_count: (_e = learning.reinforce_count) !== null && _e !== void 0 ? _e : 0,
|
|
155
|
+
status: (_f = learning.status) !== null && _f !== void 0 ? _f : 'active',
|
|
156
|
+
created_at: learning.created_at,
|
|
157
|
+
updated_at: learning.updated_at,
|
|
158
|
+
agent: (_g = learning.agent) !== null && _g !== void 0 ? _g : null,
|
|
159
|
+
task_id: (_h = learning.task_id) !== null && _h !== void 0 ? _h : null,
|
|
160
|
+
module: (_j = learning.module) !== null && _j !== void 0 ? _j : null,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function getLearningById(dbPath, id) {
|
|
164
|
+
var _a;
|
|
165
|
+
const db = openDb(dbPath);
|
|
166
|
+
return (_a = db.prepare('SELECT * FROM learnings WHERE id = ?').get(id)) !== null && _a !== void 0 ? _a : null;
|
|
167
|
+
}
|
|
168
|
+
function queryLearnings(dbPath, query, scope, limit = 10) {
|
|
169
|
+
const db = openDb(dbPath);
|
|
170
|
+
if (!query.trim()) {
|
|
171
|
+
const sql = scope
|
|
172
|
+
? "SELECT * FROM learnings WHERE scope = ? AND status != 'archived' ORDER BY created_at DESC LIMIT ?"
|
|
173
|
+
: "SELECT * FROM learnings WHERE status != 'archived' ORDER BY created_at DESC LIMIT ?";
|
|
174
|
+
return scope
|
|
175
|
+
? db.prepare(sql).all(scope, limit)
|
|
176
|
+
: db.prepare(sql).all(limit);
|
|
177
|
+
}
|
|
178
|
+
// Sanitize scope for SQL (not user-facing, but defensive)
|
|
179
|
+
const scopeClause = scope ? `AND learnings.scope = '${scope.replace(/'/g, "''")}'` : '';
|
|
180
|
+
return db.prepare(`
|
|
181
|
+
SELECT learnings.* FROM learnings
|
|
182
|
+
JOIN learnings_fts ON learnings.rowid = learnings_fts.rowid
|
|
183
|
+
WHERE learnings_fts MATCH ?
|
|
184
|
+
AND learnings.status != 'archived'
|
|
185
|
+
${scopeClause}
|
|
186
|
+
ORDER BY bm25(learnings_fts)
|
|
187
|
+
LIMIT ?
|
|
188
|
+
`).all(query, limit);
|
|
189
|
+
}
|
|
190
|
+
// ─── Decisions ───────────────────────────────────────────────────────────────
|
|
191
|
+
function insertDecision(dbPath, decision) {
|
|
192
|
+
var _a, _b, _c, _d, _e;
|
|
193
|
+
const db = openDb(dbPath);
|
|
194
|
+
db.prepare(`
|
|
195
|
+
INSERT OR REPLACE INTO decisions
|
|
196
|
+
(id, decision, rationale, scope, status, superseded_by, created_at, agent)
|
|
197
|
+
VALUES
|
|
198
|
+
(@id, @decision, @rationale, @scope, @status, @superseded_by, @created_at, @agent)
|
|
199
|
+
`).run({
|
|
200
|
+
id: decision.id,
|
|
201
|
+
decision: decision.decision,
|
|
202
|
+
rationale: (_a = decision.rationale) !== null && _a !== void 0 ? _a : '',
|
|
203
|
+
scope: (_b = decision.scope) !== null && _b !== void 0 ? _b : 'global',
|
|
204
|
+
status: (_c = decision.status) !== null && _c !== void 0 ? _c : 'active',
|
|
205
|
+
superseded_by: (_d = decision.superseded_by) !== null && _d !== void 0 ? _d : null,
|
|
206
|
+
created_at: decision.created_at,
|
|
207
|
+
agent: (_e = decision.agent) !== null && _e !== void 0 ? _e : null,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
function queryDecisions(dbPath, query, limit = 10) {
|
|
211
|
+
const db = openDb(dbPath);
|
|
212
|
+
if (!query.trim()) {
|
|
213
|
+
return db.prepare("SELECT * FROM decisions WHERE status != 'archived' ORDER BY created_at DESC LIMIT ?").all(limit);
|
|
214
|
+
}
|
|
215
|
+
return db.prepare(`
|
|
216
|
+
SELECT decisions.* FROM decisions
|
|
217
|
+
JOIN decisions_fts ON decisions.rowid = decisions_fts.rowid
|
|
218
|
+
WHERE decisions_fts MATCH ?
|
|
219
|
+
AND decisions.status != 'archived'
|
|
220
|
+
ORDER BY bm25(decisions_fts)
|
|
221
|
+
LIMIT ?
|
|
222
|
+
`).all(query, limit);
|
|
223
|
+
}
|
|
224
|
+
// ─── Index Cache ─────────────────────────────────────────────────────────────
|
|
225
|
+
function upsertIndex(dbPath, resource, level, content, sourceHash) {
|
|
226
|
+
const db = openDb(dbPath);
|
|
227
|
+
const tokenCount = Math.ceil(content.length / 4);
|
|
228
|
+
db.prepare(`
|
|
229
|
+
INSERT OR REPLACE INTO indexes (resource, level, content, token_count, generated_at, source_hash)
|
|
230
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
231
|
+
`).run(resource, level, content, tokenCount, new Date().toISOString(), sourceHash !== null && sourceHash !== void 0 ? sourceHash : null);
|
|
232
|
+
}
|
|
233
|
+
function getIndex(dbPath, resource, level) {
|
|
234
|
+
var _a;
|
|
235
|
+
const db = openDb(dbPath);
|
|
236
|
+
return (_a = db.prepare('SELECT * FROM indexes WHERE resource = ? AND level = ?').get(resource, level)) !== null && _a !== void 0 ? _a : null;
|
|
237
|
+
}
|
|
238
|
+
// ─── Skill Outputs ────────────────────────────────────────────────────────────
|
|
239
|
+
function writeSkillOutput(dbPath, output) {
|
|
240
|
+
var _a, _b, _c, _d, _e;
|
|
241
|
+
const db = openDb(dbPath);
|
|
242
|
+
db.prepare(`
|
|
243
|
+
INSERT INTO skill_outputs
|
|
244
|
+
(session_id, chain_id, skill, output_path, summary, affected_files, metadata, created_at)
|
|
245
|
+
VALUES
|
|
246
|
+
(@session_id, @chain_id, @skill, @output_path, @summary, @affected_files, @metadata, @created_at)
|
|
247
|
+
`).run({
|
|
248
|
+
session_id: output.session_id,
|
|
249
|
+
chain_id: (_a = output.chain_id) !== null && _a !== void 0 ? _a : null,
|
|
250
|
+
skill: output.skill,
|
|
251
|
+
output_path: (_b = output.output_path) !== null && _b !== void 0 ? _b : null,
|
|
252
|
+
summary: (_c = output.summary) !== null && _c !== void 0 ? _c : null,
|
|
253
|
+
affected_files: (_d = output.affected_files) !== null && _d !== void 0 ? _d : null,
|
|
254
|
+
metadata: (_e = output.metadata) !== null && _e !== void 0 ? _e : null,
|
|
255
|
+
created_at: output.created_at,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
function getSkillOutputs(dbPath, sessionId) {
|
|
259
|
+
const db = openDb(dbPath);
|
|
260
|
+
return db.prepare('SELECT * FROM skill_outputs WHERE session_id = ? ORDER BY id ASC').all(sessionId);
|
|
261
|
+
}
|
|
262
|
+
// ─── DB Path Helper ──────────────────────────────────────────────────────────
|
|
263
|
+
function getDbPath(projectPath) {
|
|
264
|
+
return path_1.default.join(projectPath, '.cm', 'context.db');
|
|
265
|
+
}
|
package/dist/continuity.js
CHANGED
|
@@ -19,6 +19,8 @@ exports.hasCmDir = hasCmDir;
|
|
|
19
19
|
const fs_1 = __importDefault(require("fs"));
|
|
20
20
|
const path_1 = __importDefault(require("path"));
|
|
21
21
|
const crypto_1 = __importDefault(require("crypto"));
|
|
22
|
+
const l0_indexer_1 = require("./l0-indexer");
|
|
23
|
+
const token_budget_1 = require("./token-budget");
|
|
22
24
|
// ─── Constants ──────────────────────────────────────────────────────────────
|
|
23
25
|
const CM_DIR = '.cm';
|
|
24
26
|
const CONTINUITY_FILE = 'CONTINUITY.md';
|
|
@@ -55,6 +57,11 @@ function ensureCmDir(projectPath) {
|
|
|
55
57
|
if (!fs_1.default.existsSync(configPath)) {
|
|
56
58
|
fs_1.default.writeFileSync(configPath, generateDefaultConfig());
|
|
57
59
|
}
|
|
60
|
+
// Initialize token budget if not present
|
|
61
|
+
const budgetPath = path_1.default.join(cmDir, 'token-budget.json');
|
|
62
|
+
if (!fs_1.default.existsSync(budgetPath)) {
|
|
63
|
+
fs_1.default.writeFileSync(budgetPath, JSON.stringify((0, token_budget_1.getDefaultBudget)(), null, 2));
|
|
64
|
+
}
|
|
58
65
|
// Add .cm to .gitignore if not already there
|
|
59
66
|
const gitignorePath = path_1.default.join(projectPath, '.gitignore');
|
|
60
67
|
if (fs_1.default.existsSync(gitignorePath)) {
|
|
@@ -252,6 +259,11 @@ function addLearning(projectPath, learning) {
|
|
|
252
259
|
catch ( /* empty */_a) { /* empty */ }
|
|
253
260
|
learnings.push(fullLearning);
|
|
254
261
|
fs_1.default.writeFileSync(learningsPath, JSON.stringify(learnings, null, 2));
|
|
262
|
+
// Refresh L0 index after adding a learning
|
|
263
|
+
try {
|
|
264
|
+
(0, l0_indexer_1.generateLearningsIndex)(projectPath);
|
|
265
|
+
}
|
|
266
|
+
catch ( /* non-fatal */_b) { /* non-fatal */ }
|
|
255
267
|
return fullLearning;
|
|
256
268
|
}
|
|
257
269
|
function archiveLearnings(projectPath, learnings) {
|
|
@@ -0,0 +1,79 @@
|
|
|
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.startWatcher = startWatcher;
|
|
7
|
+
exports.stopWatcher = stopWatcher;
|
|
8
|
+
exports.isWatching = isWatching;
|
|
9
|
+
const chokidar_1 = __importDefault(require("chokidar"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const l0_indexer_1 = require("./l0-indexer");
|
|
12
|
+
// ─── Watcher ─────────────────────────────────────────────────────────────────
|
|
13
|
+
let watcher = null;
|
|
14
|
+
const debounceTimers = new Map();
|
|
15
|
+
function debounce(key, ms, fn) {
|
|
16
|
+
const existing = debounceTimers.get(key);
|
|
17
|
+
if (existing)
|
|
18
|
+
clearTimeout(existing);
|
|
19
|
+
debounceTimers.set(key, setTimeout(() => {
|
|
20
|
+
debounceTimers.delete(key);
|
|
21
|
+
fn();
|
|
22
|
+
}, ms));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Start watching `.cm/memory/learnings.json` and `.cm/memory/decisions.json`.
|
|
26
|
+
* On change, regenerates the L0 learnings index automatically.
|
|
27
|
+
*/
|
|
28
|
+
function startWatcher(projectPath, options = {}) {
|
|
29
|
+
if (watcher)
|
|
30
|
+
return watcher;
|
|
31
|
+
const { debounceMs = 300, onRefresh, onError } = options;
|
|
32
|
+
const memoryDir = path_1.default.join(projectPath, '.cm', 'memory');
|
|
33
|
+
const watched = [
|
|
34
|
+
path_1.default.join(memoryDir, 'learnings.json'),
|
|
35
|
+
path_1.default.join(memoryDir, 'decisions.json'),
|
|
36
|
+
];
|
|
37
|
+
watcher = chokidar_1.default.watch(watched, {
|
|
38
|
+
ignoreInitial: true,
|
|
39
|
+
persistent: false,
|
|
40
|
+
awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 50 },
|
|
41
|
+
});
|
|
42
|
+
watcher.on('change', (filePath) => {
|
|
43
|
+
const basename = path_1.default.basename(filePath);
|
|
44
|
+
debounce(`refresh:${basename}`, debounceMs, () => {
|
|
45
|
+
try {
|
|
46
|
+
if (basename === 'learnings.json') {
|
|
47
|
+
(0, l0_indexer_1.generateLearningsIndex)(projectPath);
|
|
48
|
+
onRefresh === null || onRefresh === void 0 ? void 0 : onRefresh('learnings');
|
|
49
|
+
}
|
|
50
|
+
// decisions don't have an L0 index yet — placeholder for future
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
onError === null || onError === void 0 ? void 0 : onError(err);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
watcher.on('error', (err) => {
|
|
58
|
+
onError === null || onError === void 0 ? void 0 : onError(err);
|
|
59
|
+
});
|
|
60
|
+
return watcher;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Stop the active watcher and clear pending debounce timers.
|
|
64
|
+
*/
|
|
65
|
+
function stopWatcher() {
|
|
66
|
+
for (const timer of debounceTimers.values())
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
debounceTimers.clear();
|
|
69
|
+
if (watcher) {
|
|
70
|
+
watcher.close();
|
|
71
|
+
watcher = null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Returns true if a watcher is currently active.
|
|
76
|
+
*/
|
|
77
|
+
function isWatching() {
|
|
78
|
+
return watcher !== null;
|
|
79
|
+
}
|