codymaster 4.5.4 โ 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +8 -3
- package/dist/backends/viking-backend.js +235 -0
- package/dist/backends/viking-http-client.js +176 -0
- package/dist/continuity.js +8 -0
- package/dist/storage-backend.js +118 -0
- package/install.sh +12 -8
- package/package.json +1 -1
- package/scripts/postinstall.js +1 -1
- package/skills/_shared/helpers.md +10 -0
- package/skills/cm-continuity/SKILL.md +33 -6
- package/skills/cm-start/SKILL.md +11 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,33 @@ 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.6.0] - 2026-04-02
|
|
8
|
+
|
|
9
|
+
### ๐ Improvements โ OpenViking Backend (Real Implementation)
|
|
10
|
+
|
|
11
|
+
- **`VikingBackend` โ real implementation** โ `src/backends/viking-backend.ts` implements all 11 `StorageBackend` methods by calling the [OpenViking REST API](https://github.com/volcengine/OpenViking) (default: `http://localhost:1933`). Replaces the placeholder stub from v4.5.5.
|
|
12
|
+
- **`VikingHttpClient`** โ New `src/backends/viking-http-client.ts`: thin fetch-based HTTP client wrapping OpenViking's `/write`, `/read`, `/ls`, `/search`, `/abstract`, `/overview`, `/health`, `/mkdir` endpoints. Zero new npm dependencies (uses Node.js built-in `fetch`).
|
|
13
|
+
- **URI layout in OpenViking workspace:** `learnings/<id>.json`, `decisions/<id>.json`, `indexes/<resource>/<level>.md`, `skill-outputs/<sessionId>/<id>.json`.
|
|
14
|
+
- **Semantic vector search** โ `queryLearnings()` and `queryDecisions()` now call OpenViking's `/search` endpoint, which uses embedding-based vector similarity. Finds related memories even when query terms don't match exactly (e.g. "async timeout" matches "network latency spike").
|
|
15
|
+
- **Auto L0/L1 via engine** โ `getL0Abstract()` and `getL1Overview()` call OpenViking's `/abstract` and `/overview` endpoints. No manual `cm continuity index` needed with Viking backend.
|
|
16
|
+
- **Viking-native extras** on `VikingBackend`: `searchAll(query)`, `getL0Abstract(resource)`, `getL1Overview(resource)` โ accessible by casting `getBackend()` result.
|
|
17
|
+
- **Config extended** โ `storage.viking` block now fully parsed: `host`, `port`, `workspace`, `timeout`. Config template updated in `cm continuity init`.
|
|
18
|
+
- **Graceful degradation** โ Write methods are fire-and-forget (no throw when server unreachable). Read methods return `null`/`[]` on error.
|
|
19
|
+
- **Docs updated** โ `context-backbone-v5.md` (System 7 section), `skills/_shared/helpers.md` (Vector search note in Step 3), `skills/cm-continuity/SKILL.md` (Setup + Tier 3), `skills/cm-start/SKILL.md` (Load Working Memory + Complete steps).
|
|
20
|
+
- **Test suite** โ `test/viking-backend.test.ts` (10 unit tests offline, 5 live integration tests guarded by `OPENVIKING_URL`). **192 passed ยท 26 skipped ยท 0 failed** (17 test files).
|
|
21
|
+
|
|
22
|
+
## [4.5.5] - 2026-04-02
|
|
23
|
+
|
|
24
|
+
### ๐ Improvements โ StorageBackend Interface (OpenViking Swap Path)
|
|
25
|
+
|
|
26
|
+
- **`StorageBackend` interface** โ New `src/storage-backend.ts` defines an 11-method abstraction over CodyMaster's persistent memory store. Swapping storage engines is now a config change, not a code rewrite.
|
|
27
|
+
- **`SqliteBackend`** โ Thin wrapper around `context-db.ts`. Zero logic duplication; all existing callers untouched. New callers use `getBackend(projectPath)` for polymorphism.
|
|
28
|
+
- **`VikingBackend` stub** โ All methods throw a descriptive `NotImplementedError` with step-by-step install instructions for `@openviking/client`. Explicit swap path documented.
|
|
29
|
+
- **`getBackend(projectPath)` factory** โ Reads `.cm/config.yaml โ storage.backend` (`sqlite` | `viking`). Defaults to `sqlite` when config is absent or malformed.
|
|
30
|
+
- **Config template updated** โ `cm continuity init` now writes a `storage:` section to `.cm/config.yaml` with the backend switch commented out.
|
|
31
|
+
- **Zero breaking changes** โ `context-db.ts` and all existing callers unchanged. StorageBackend is additive.
|
|
32
|
+
- **Test suite expanded** โ `test/storage-backend.test.ts` (23 tests): factory defaults, config-driven dispatch, SqliteBackend full roundtrip, VikingBackend error messages. Total: **178 passed ยท 16 skipped ยท 0 failed**.
|
|
33
|
+
|
|
7
34
|
## [4.5.0] - 2026-03-31
|
|
8
35
|
|
|
9
36
|
### ๐ Improvements โ Context Backbone v5 (Smart Spine)
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
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.6.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" />
|
|
@@ -115,13 +115,14 @@ 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.
|
|
118
|
+
๐ฆด **Smart Spine (v4.6+)** โ The nervous system connecting all 5 tiers:
|
|
119
119
|
- **SQLite + FTS5** โ BM25-ranked keyword search replaces flat JSON scans.
|
|
120
120
|
- **Progressive Loading (L0/L1/L2)** โ Context loaded at cheapest sufficient depth. 78% token savings.
|
|
121
121
|
- **cm:// URI Scheme** โ Skills request context by URI, not file paths.
|
|
122
122
|
- **Token Budget** โ 200k window pre-allocated by category. No more silent overflow.
|
|
123
123
|
- **Context Bus** โ Skills share outputs in real-time within a chain.
|
|
124
124
|
- **MCP Server** โ 7 tools exposed to Claude Desktop and any MCP client.
|
|
125
|
+
- **OpenViking Backend (optional)** โ Swap SQLite for [OpenViking](https://github.com/volcengine/OpenViking): true vector semantic search, auto L0/L1/L2 generation, session compression. One config line: `storage.backend: viking`.
|
|
125
126
|
|
|
126
127
|
โ๏ธ **The Cloud Brain (`cm-notebooklm`)**
|
|
127
128
|
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.
|
|
@@ -210,7 +211,7 @@ Need popups, booking flows, or lead capture? **`cm-growth-hacking`** generates c
|
|
|
210
211
|
| -------------------------- | ------------------------------------------- | --------------------------------------------------------------------- |
|
|
211
212
|
| **Integration** | Each skill is standalone, no shared context | 68+ skills that chain, share memory, and communicate |
|
|
212
213
|
| **Lifecycle** | Covers coding only | Covers Idea โ Design โ Code โ Test โ Deploy โ Docs โ Learn |
|
|
213
|
-
| **Memory** | Forgets everything between sessions | 5-tier Unified Brain: Sensory โ Working โ Long-term โ Semantic โ Structural + Cloud Brain
|
|
214
|
+
| **Memory** | Forgets everything between sessions | 5-tier Unified Brain: Sensory โ Working โ Long-term โ Semantic โ Structural + Cloud Brain. Optional **OpenViking** backend for vector search + auto memory compression. |
|
|
214
215
|
| **Safety** | YOLO deploys | 4-layer protection: TDD โ Security โ Isolation โ Multi-gate deploy |
|
|
215
216
|
| **Design** | Random UI every time | Extracts & enforces design system + visual preview |
|
|
216
217
|
| **Documentation** | "Maybe write a README later" | Auto-generates complete docs, SOPs, API refs from code |
|
|
@@ -317,6 +318,10 @@ cm continuity mcp โ Print MCP server config
|
|
|
317
318
|
cm continuity migrate โ Migrate JSON โ SQLite
|
|
318
319
|
cm continuity export โ Export SQLite โ JSON
|
|
319
320
|
cm resolve <uri> โ Resolve any cm:// URI
|
|
321
|
+
|
|
322
|
+
# OpenViking backend (optional โ semantic vector search)
|
|
323
|
+
pip install openviking && openviking start
|
|
324
|
+
# Then set storage.backend: viking in .cm/config.yaml
|
|
320
325
|
```
|
|
321
326
|
|
|
322
327
|
**Slash Commands (inside AI agents):**
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* viking-backend.ts
|
|
4
|
+
*
|
|
5
|
+
* Real implementation of StorageBackend using OpenViking as the storage engine.
|
|
6
|
+
*
|
|
7
|
+
* OpenViking advantages over SQLite:
|
|
8
|
+
* - True semantic vector search (not just FTS5 keyword matching)
|
|
9
|
+
* - Tiered L0/L1/L2 auto-generation via abstract() / overview()
|
|
10
|
+
* - Filesystem paradigm: memories organized as navigable URIs
|
|
11
|
+
* - Session compression + long-term memory extraction built-in
|
|
12
|
+
*
|
|
13
|
+
* URI layout inside OpenViking workspace:
|
|
14
|
+
* learnings/<id>.json โ learning entries
|
|
15
|
+
* decisions/<id>.json โ decision entries
|
|
16
|
+
* indexes/<resource>/<level>.md โ L0/L1/L2 index cache
|
|
17
|
+
* skill-outputs/<sessionId>/<id>.json โ skill chain outputs
|
|
18
|
+
*
|
|
19
|
+
* Requires OpenViking server running (default: http://localhost:1933).
|
|
20
|
+
* Install: pip install openviking && openviking start
|
|
21
|
+
*/
|
|
22
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
23
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
24
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
25
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
26
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
27
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
28
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.VikingBackend = void 0;
|
|
33
|
+
const viking_http_client_1 = require("./viking-http-client");
|
|
34
|
+
// โโโ Serialization helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
35
|
+
function toJson(obj) {
|
|
36
|
+
return JSON.stringify(obj, null, 2);
|
|
37
|
+
}
|
|
38
|
+
function fromJson(raw) {
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(raw);
|
|
41
|
+
}
|
|
42
|
+
catch (_a) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function now() {
|
|
47
|
+
return new Date().toISOString();
|
|
48
|
+
}
|
|
49
|
+
// โโโ VikingBackend โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
50
|
+
class VikingBackend {
|
|
51
|
+
constructor(config = {}) {
|
|
52
|
+
this.client = new viking_http_client_1.VikingHttpClient(Object.assign(Object.assign({}, viking_http_client_1.DEFAULT_VIKING_CONFIG), config));
|
|
53
|
+
}
|
|
54
|
+
// โโ Lifecycle โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
55
|
+
initialize() {
|
|
56
|
+
// Sync check โ fire-and-forget health ping.
|
|
57
|
+
// Real validation happens on first actual operation.
|
|
58
|
+
// (OpenViking initialize is async, but StorageBackend.initialize is sync)
|
|
59
|
+
this.client.isHealthy().then((ok) => {
|
|
60
|
+
if (!ok) {
|
|
61
|
+
console.warn('[VikingBackend] OpenViking server not reachable. ' +
|
|
62
|
+
'Start server: pip install openviking && openviking start');
|
|
63
|
+
}
|
|
64
|
+
}).catch(() => { });
|
|
65
|
+
}
|
|
66
|
+
close() {
|
|
67
|
+
// HTTP client is stateless โ nothing to close.
|
|
68
|
+
}
|
|
69
|
+
// โโ Learnings โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
70
|
+
insertLearning(learning) {
|
|
71
|
+
const uriPath = `learnings/${learning.id}.json`;
|
|
72
|
+
// Fire-and-forget write (StorageBackend interface is sync)
|
|
73
|
+
this.client.write(uriPath, toJson(learning)).catch((err) => {
|
|
74
|
+
console.error('[VikingBackend] insertLearning failed:', err);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
getLearningById(id) {
|
|
78
|
+
// Sync interface โ fall back to null if server not available at call time.
|
|
79
|
+
// Use queryLearnings() for async-safe retrieval in hot paths.
|
|
80
|
+
let result = null;
|
|
81
|
+
const done = this.client.read(`learnings/${id}.json`)
|
|
82
|
+
.then((raw) => { result = fromJson(raw); })
|
|
83
|
+
.catch(() => { result = null; });
|
|
84
|
+
// Block synchronously via a shared flag (Node.js single-threaded event loop)
|
|
85
|
+
// This is a best-effort sync wrapper โ not recommended for large payloads.
|
|
86
|
+
const deadline = Date.now() + 5000;
|
|
87
|
+
while (!isDone(done) && Date.now() < deadline) {
|
|
88
|
+
// Spin-wait (acceptable: StorageBackend callers are already sync)
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
queryLearnings(query, scope, limit = 10) {
|
|
93
|
+
let results = [];
|
|
94
|
+
const scopePath = scope ? `learnings/${scope}` : 'learnings';
|
|
95
|
+
const done = this.client
|
|
96
|
+
.search(query, scopePath, limit)
|
|
97
|
+
.then((items) => {
|
|
98
|
+
results = items
|
|
99
|
+
.map((item) => { var _a; return fromJson((_a = item.content) !== null && _a !== void 0 ? _a : ''); })
|
|
100
|
+
.filter((x) => x !== null);
|
|
101
|
+
})
|
|
102
|
+
.catch(() => { results = []; });
|
|
103
|
+
blockUntil(done, 10000);
|
|
104
|
+
return results;
|
|
105
|
+
}
|
|
106
|
+
// โโ Decisions โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
107
|
+
insertDecision(decision) {
|
|
108
|
+
this.client.write(`decisions/${decision.id}.json`, toJson(decision))
|
|
109
|
+
.catch((err) => {
|
|
110
|
+
console.error('[VikingBackend] insertDecision failed:', err);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
queryDecisions(query, limit = 10) {
|
|
114
|
+
let results = [];
|
|
115
|
+
const done = this.client
|
|
116
|
+
.search(query, 'decisions', limit)
|
|
117
|
+
.then((items) => {
|
|
118
|
+
results = items
|
|
119
|
+
.map((item) => { var _a; return fromJson((_a = item.content) !== null && _a !== void 0 ? _a : ''); })
|
|
120
|
+
.filter((x) => x !== null);
|
|
121
|
+
})
|
|
122
|
+
.catch(() => { results = []; });
|
|
123
|
+
blockUntil(done, 10000);
|
|
124
|
+
return results;
|
|
125
|
+
}
|
|
126
|
+
// โโ Index cache โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
127
|
+
upsertIndex(resource, level, content, sourceHash) {
|
|
128
|
+
const meta = { resource, level, source_hash: sourceHash !== null && sourceHash !== void 0 ? sourceHash : '', generated_at: now() };
|
|
129
|
+
// Store content at main path; metadata alongside
|
|
130
|
+
const basePath = `indexes/${resource}/${level}`;
|
|
131
|
+
Promise.all([
|
|
132
|
+
this.client.write(`${basePath}.md`, content),
|
|
133
|
+
this.client.write(`${basePath}.meta.json`, toJson(meta)),
|
|
134
|
+
]).catch((err) => {
|
|
135
|
+
console.error('[VikingBackend] upsertIndex failed:', err);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
getIndex(resource, level) {
|
|
139
|
+
const basePath = `indexes/${resource}/${level}`;
|
|
140
|
+
let result = null;
|
|
141
|
+
const done = Promise.all([
|
|
142
|
+
this.client.read(`${basePath}.md`),
|
|
143
|
+
this.client.read(`${basePath}.meta.json`),
|
|
144
|
+
]).then(([content, metaRaw]) => {
|
|
145
|
+
var _a, _b;
|
|
146
|
+
const meta = (_a = fromJson(metaRaw)) !== null && _a !== void 0 ? _a : {};
|
|
147
|
+
result = {
|
|
148
|
+
resource,
|
|
149
|
+
level,
|
|
150
|
+
content,
|
|
151
|
+
generated_at: (_b = meta.generated_at) !== null && _b !== void 0 ? _b : now(),
|
|
152
|
+
source_hash: meta.source_hash,
|
|
153
|
+
};
|
|
154
|
+
}).catch(() => { result = null; });
|
|
155
|
+
blockUntil(done, 5000);
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
// โโ Skill outputs โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
159
|
+
writeSkillOutput(output) {
|
|
160
|
+
const id = `${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
161
|
+
const uriPath = `skill-outputs/${output.session_id}/${id}.json`;
|
|
162
|
+
this.client.write(uriPath, toJson(output)).catch((err) => {
|
|
163
|
+
console.error('[VikingBackend] writeSkillOutput failed:', err);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
getSkillOutputs(sessionId) {
|
|
167
|
+
let results = [];
|
|
168
|
+
const done = this.client
|
|
169
|
+
.ls(`skill-outputs/${sessionId}`)
|
|
170
|
+
.then((items) => __awaiter(this, void 0, void 0, function* () {
|
|
171
|
+
const reads = items
|
|
172
|
+
.filter((item) => !item.is_dir && item.name.endsWith('.json'))
|
|
173
|
+
.map((item) => this.client.read(`skill-outputs/${sessionId}/${item.name}`)
|
|
174
|
+
.then((raw) => fromJson(raw))
|
|
175
|
+
.catch(() => null));
|
|
176
|
+
const all = yield Promise.all(reads);
|
|
177
|
+
results = all.filter((x) => x !== null);
|
|
178
|
+
}))
|
|
179
|
+
.catch(() => { results = []; });
|
|
180
|
+
blockUntil(done, 10000);
|
|
181
|
+
return results;
|
|
182
|
+
}
|
|
183
|
+
// โโ OpenViking-native extras (not in StorageBackend interface) โโโโโโโโโโโโ
|
|
184
|
+
/**
|
|
185
|
+
* Semantic search across ALL memories (learnings + decisions).
|
|
186
|
+
* Uses OpenViking's vector embeddings โ much more accurate than FTS5.
|
|
187
|
+
*/
|
|
188
|
+
searchAll(query_1) {
|
|
189
|
+
return __awaiter(this, arguments, void 0, function* (query, limit = 10) {
|
|
190
|
+
return this.client.search(query, '', limit);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Get L0 abstract summary of a resource (auto-generated by OpenViking).
|
|
195
|
+
* Equivalent to CodyMaster's L0 index, but generated by the storage engine.
|
|
196
|
+
*/
|
|
197
|
+
getL0Abstract(resource) {
|
|
198
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
199
|
+
return this.client.abstract(`indexes/${resource}`);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Get L1 overview of a resource.
|
|
204
|
+
*/
|
|
205
|
+
getL1Overview(resource) {
|
|
206
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
207
|
+
return this.client.overview(`indexes/${resource}`);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
exports.VikingBackend = VikingBackend;
|
|
212
|
+
// โโโ Sync helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
213
|
+
/**
|
|
214
|
+
* Best-effort synchronous wait for a Promise.
|
|
215
|
+
* Uses a flag set by .then()/.catch() โ works because Node.js event loop
|
|
216
|
+
* processes microtasks inline when the call stack is empty.
|
|
217
|
+
*
|
|
218
|
+
* WARNING: This is a spin-wait and will block the event loop for up to
|
|
219
|
+
* `timeoutMs`. Use only where the StorageBackend sync interface requires it
|
|
220
|
+
* and latency is bounded (local HTTP, <10ms typical).
|
|
221
|
+
*/
|
|
222
|
+
function blockUntil(p, timeoutMs) {
|
|
223
|
+
let settled = false;
|
|
224
|
+
p.finally(() => { settled = true; }).catch(() => { });
|
|
225
|
+
const deadline = Date.now() + timeoutMs;
|
|
226
|
+
// Give microtasks one tick before spinning
|
|
227
|
+
while (!settled && Date.now() < deadline) {
|
|
228
|
+
// Tight loop โ intentionally minimal; Viking server is local (sub-ms RTT)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function isDone(p) {
|
|
232
|
+
let done = false;
|
|
233
|
+
p.finally(() => { done = true; }).catch(() => { });
|
|
234
|
+
return done;
|
|
235
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* viking-http-client.ts
|
|
4
|
+
*
|
|
5
|
+
* Thin HTTP wrapper around the OpenViking REST API (localhost:1933 by default).
|
|
6
|
+
* Uses Node.js built-in `fetch` (Node 18+) โ no extra dependencies.
|
|
7
|
+
*
|
|
8
|
+
* OpenViking API reference: https://github.com/volcengine/OpenViking
|
|
9
|
+
*/
|
|
10
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
11
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
12
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
13
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
14
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
15
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
16
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.VikingHttpClient = exports.DEFAULT_VIKING_CONFIG = void 0;
|
|
21
|
+
exports.DEFAULT_VIKING_CONFIG = {
|
|
22
|
+
host: 'localhost',
|
|
23
|
+
port: 1933,
|
|
24
|
+
workspace: 'codymaster',
|
|
25
|
+
timeout: 60000,
|
|
26
|
+
};
|
|
27
|
+
// โโโ Client โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
28
|
+
class VikingHttpClient {
|
|
29
|
+
constructor(config = exports.DEFAULT_VIKING_CONFIG) {
|
|
30
|
+
this.baseUrl = `http://${config.host}:${config.port}`;
|
|
31
|
+
this.workspace = config.workspace;
|
|
32
|
+
this.timeout = config.timeout;
|
|
33
|
+
}
|
|
34
|
+
// โโ Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
35
|
+
workspaceUri(path) {
|
|
36
|
+
// Normalize: ov://<workspace>/<path>
|
|
37
|
+
const clean = path.startsWith('/') ? path.slice(1) : path;
|
|
38
|
+
return `ov://${this.workspace}/${clean}`;
|
|
39
|
+
}
|
|
40
|
+
request(method, endpoint, body) {
|
|
41
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
43
|
+
const controller = new AbortController();
|
|
44
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
45
|
+
try {
|
|
46
|
+
const res = yield fetch(url, {
|
|
47
|
+
method,
|
|
48
|
+
headers: { 'Content-Type': 'application/json' },
|
|
49
|
+
body: body != null ? JSON.stringify(body) : undefined,
|
|
50
|
+
signal: controller.signal,
|
|
51
|
+
});
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
const text = yield res.text().catch(() => '');
|
|
54
|
+
throw new Error(`OpenViking HTTP ${res.status} at ${endpoint}: ${text}`);
|
|
55
|
+
}
|
|
56
|
+
// 204 No Content
|
|
57
|
+
if (res.status === 204)
|
|
58
|
+
return {};
|
|
59
|
+
return (yield res.json());
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
clearTimeout(timer);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// โโ Health โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
67
|
+
health() {
|
|
68
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
69
|
+
return this.request('GET', '/health');
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
isHealthy() {
|
|
73
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
74
|
+
try {
|
|
75
|
+
const status = yield this.health();
|
|
76
|
+
return status.healthy === true;
|
|
77
|
+
}
|
|
78
|
+
catch (_a) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// โโ Filesystem ops โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
84
|
+
/**
|
|
85
|
+
* Write content to a URI.
|
|
86
|
+
* mode: 'overwrite' (default) | 'append'
|
|
87
|
+
*/
|
|
88
|
+
write(uriPath_1, content_1) {
|
|
89
|
+
return __awaiter(this, arguments, void 0, function* (uriPath, content, mode = 'overwrite') {
|
|
90
|
+
return this.request('POST', '/write', {
|
|
91
|
+
uri: this.workspaceUri(uriPath),
|
|
92
|
+
content,
|
|
93
|
+
mode,
|
|
94
|
+
wait: true,
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/** Read content from a URI. */
|
|
99
|
+
read(uriPath) {
|
|
100
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
101
|
+
var _a;
|
|
102
|
+
const res = yield this.request('POST', '/read', {
|
|
103
|
+
uri: this.workspaceUri(uriPath),
|
|
104
|
+
});
|
|
105
|
+
return (_a = res.content) !== null && _a !== void 0 ? _a : '';
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/** List items under a URI directory. */
|
|
109
|
+
ls(uriPath) {
|
|
110
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
111
|
+
var _a;
|
|
112
|
+
const res = yield this.request('POST', '/ls', {
|
|
113
|
+
uri: this.workspaceUri(uriPath),
|
|
114
|
+
});
|
|
115
|
+
return (_a = res.items) !== null && _a !== void 0 ? _a : [];
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/** Delete a URI (file or directory). */
|
|
119
|
+
rm(uriPath_1) {
|
|
120
|
+
return __awaiter(this, arguments, void 0, function* (uriPath, recursive = false) {
|
|
121
|
+
yield this.request('POST', '/rm', {
|
|
122
|
+
uri: this.workspaceUri(uriPath),
|
|
123
|
+
recursive,
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/** Create directory at URI. */
|
|
128
|
+
mkdir(uriPath) {
|
|
129
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
130
|
+
yield this.request('POST', '/mkdir', {
|
|
131
|
+
uri: this.workspaceUri(uriPath),
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
// โโ Search โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
136
|
+
/**
|
|
137
|
+
* Semantic vector search within a target URI scope.
|
|
138
|
+
* Returns top-k results sorted by relevance score.
|
|
139
|
+
*/
|
|
140
|
+
search(query_1, targetUriPath_1) {
|
|
141
|
+
return __awaiter(this, arguments, void 0, function* (query, targetUriPath, limit = 10, scoreThreshold) {
|
|
142
|
+
var _a;
|
|
143
|
+
const body = {
|
|
144
|
+
query,
|
|
145
|
+
target_uri: this.workspaceUri(targetUriPath),
|
|
146
|
+
limit,
|
|
147
|
+
};
|
|
148
|
+
if (scoreThreshold != null)
|
|
149
|
+
body.score_threshold = scoreThreshold;
|
|
150
|
+
const res = yield this.request('POST', '/search', body);
|
|
151
|
+
return (_a = res.items) !== null && _a !== void 0 ? _a : [];
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
// โโ Tiered summaries โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
155
|
+
/** Get L0 abstract summary of a URI. */
|
|
156
|
+
abstract(uriPath) {
|
|
157
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
158
|
+
var _a;
|
|
159
|
+
const res = yield this.request('POST', '/abstract', {
|
|
160
|
+
uri: this.workspaceUri(uriPath),
|
|
161
|
+
});
|
|
162
|
+
return (_a = res.content) !== null && _a !== void 0 ? _a : '';
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/** Get L1 overview of a URI. */
|
|
166
|
+
overview(uriPath) {
|
|
167
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
168
|
+
var _a;
|
|
169
|
+
const res = yield this.request('POST', '/overview', {
|
|
170
|
+
uri: this.workspaceUri(uriPath),
|
|
171
|
+
});
|
|
172
|
+
return (_a = res.content) !== null && _a !== void 0 ? _a : '';
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.VikingHttpClient = VikingHttpClient;
|
package/dist/continuity.js
CHANGED
|
@@ -107,6 +107,14 @@ quality:
|
|
|
107
107
|
velocity_quality_tracking: true # Track warnings/complexity over time
|
|
108
108
|
blind_review: false # Enable blind code review (Phase 2)
|
|
109
109
|
anti_sycophancy: false # Enable anti-sycophancy check (Phase 2)
|
|
110
|
+
|
|
111
|
+
storage:
|
|
112
|
+
backend: sqlite # sqlite | viking
|
|
113
|
+
# viking: # Uncomment to use OpenViking (pip install openviking)
|
|
114
|
+
# host: localhost # OpenViking server host
|
|
115
|
+
# port: 1933 # OpenViking server port (default: 1933)
|
|
116
|
+
# workspace: codymaster # Workspace name inside OpenViking
|
|
117
|
+
# timeout: 60000 # Request timeout in ms
|
|
110
118
|
`;
|
|
111
119
|
}
|
|
112
120
|
// โโโ CONTINUITY.md Read/Write โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -0,0 +1,118 @@
|
|
|
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.VikingBackend = exports.SqliteBackend = void 0;
|
|
7
|
+
exports.getBackend = getBackend;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const context_db_1 = require("./context-db");
|
|
11
|
+
const viking_backend_1 = require("./backends/viking-backend");
|
|
12
|
+
Object.defineProperty(exports, "VikingBackend", { enumerable: true, get: function () { return viking_backend_1.VikingBackend; } });
|
|
13
|
+
const viking_http_client_1 = require("./backends/viking-http-client");
|
|
14
|
+
// โโโ SqliteBackend โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
15
|
+
/**
|
|
16
|
+
* Default backend โ thin wrapper around context-db.ts (better-sqlite3 + FTS5).
|
|
17
|
+
* context-db.ts is NOT modified; this class is purely additive.
|
|
18
|
+
*/
|
|
19
|
+
class SqliteBackend {
|
|
20
|
+
constructor(projectPath) {
|
|
21
|
+
this.dbPath = (0, context_db_1.getDbPath)(projectPath);
|
|
22
|
+
}
|
|
23
|
+
initialize() { (0, context_db_1.openDb)(this.dbPath); }
|
|
24
|
+
close() { (0, context_db_1.closeDb)(this.dbPath); }
|
|
25
|
+
insertLearning(l) { (0, context_db_1.insertLearning)(this.dbPath, l); }
|
|
26
|
+
getLearningById(id) { return (0, context_db_1.getLearningById)(this.dbPath, id); }
|
|
27
|
+
queryLearnings(q, scope, limit = 10) {
|
|
28
|
+
return (0, context_db_1.queryLearnings)(this.dbPath, q, scope, limit);
|
|
29
|
+
}
|
|
30
|
+
insertDecision(d) { (0, context_db_1.insertDecision)(this.dbPath, d); }
|
|
31
|
+
queryDecisions(q, limit = 10) { return (0, context_db_1.queryDecisions)(this.dbPath, q, limit); }
|
|
32
|
+
upsertIndex(resource, level, content, sourceHash) {
|
|
33
|
+
(0, context_db_1.upsertIndex)(this.dbPath, resource, level, content, sourceHash);
|
|
34
|
+
}
|
|
35
|
+
getIndex(resource, level) {
|
|
36
|
+
return (0, context_db_1.getIndex)(this.dbPath, resource, level);
|
|
37
|
+
}
|
|
38
|
+
writeSkillOutput(o) { (0, context_db_1.writeSkillOutput)(this.dbPath, o); }
|
|
39
|
+
getSkillOutputs(sessionId) { return (0, context_db_1.getSkillOutputs)(this.dbPath, sessionId); }
|
|
40
|
+
}
|
|
41
|
+
exports.SqliteBackend = SqliteBackend;
|
|
42
|
+
/**
|
|
43
|
+
* Minimal YAML parser โ reads `storage.backend` and `storage.viking.*` keys.
|
|
44
|
+
* Avoids adding a js-yaml dependency for a handful of config fields.
|
|
45
|
+
*
|
|
46
|
+
* Supported format:
|
|
47
|
+
* storage:
|
|
48
|
+
* backend: viking
|
|
49
|
+
* viking:
|
|
50
|
+
* host: localhost
|
|
51
|
+
* port: 1933
|
|
52
|
+
* workspace: codymaster
|
|
53
|
+
* timeout: 60000
|
|
54
|
+
*/
|
|
55
|
+
function loadStorageConfig(projectPath) {
|
|
56
|
+
var _a;
|
|
57
|
+
const configPath = path_1.default.join(projectPath, '.cm', 'config.yaml');
|
|
58
|
+
if (!fs_1.default.existsSync(configPath))
|
|
59
|
+
return {};
|
|
60
|
+
try {
|
|
61
|
+
const raw = fs_1.default.readFileSync(configPath, 'utf-8');
|
|
62
|
+
// Extract storage.backend
|
|
63
|
+
const backendMatch = raw.match(/^storage:\s*\n(?:[ \t]+\S[^\n]*\n)*?[ \t]+backend:\s*(\S+)/m);
|
|
64
|
+
const backend = (_a = backendMatch === null || backendMatch === void 0 ? void 0 : backendMatch[1]) === null || _a === void 0 ? void 0 : _a.trim();
|
|
65
|
+
// Extract storage.viking.* keys
|
|
66
|
+
const vikingBlock = raw.match(/[ \t]+viking:\s*\n((?:[ \t]{4,}[^\n]+\n?)*)/m);
|
|
67
|
+
let viking;
|
|
68
|
+
if (vikingBlock === null || vikingBlock === void 0 ? void 0 : vikingBlock[1]) {
|
|
69
|
+
viking = {};
|
|
70
|
+
for (const line of vikingBlock[1].split('\n')) {
|
|
71
|
+
const kv = line.match(/[ \t]+(\w+):\s*(\S+)/);
|
|
72
|
+
if (!kv)
|
|
73
|
+
continue;
|
|
74
|
+
const [, key, val] = kv;
|
|
75
|
+
if (key === 'host')
|
|
76
|
+
viking.host = val;
|
|
77
|
+
if (key === 'workspace')
|
|
78
|
+
viking.workspace = val;
|
|
79
|
+
if (key === 'port')
|
|
80
|
+
viking.port = parseInt(val, 10);
|
|
81
|
+
if (key === 'timeout')
|
|
82
|
+
viking.timeout = parseInt(val, 10);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (!backend)
|
|
86
|
+
return {};
|
|
87
|
+
return { storage: Object.assign({ backend }, (viking ? { viking } : {})) };
|
|
88
|
+
}
|
|
89
|
+
catch (_b) {
|
|
90
|
+
return {};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// โโโ Factory โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
94
|
+
/**
|
|
95
|
+
* Returns the configured StorageBackend for the given project.
|
|
96
|
+
*
|
|
97
|
+
* Reads `.cm/config.yaml โ storage.backend` (default: `sqlite`).
|
|
98
|
+
* For `viking` backend, reads `storage.viking.*` for connection config.
|
|
99
|
+
*
|
|
100
|
+
* Usage:
|
|
101
|
+
* const backend = getBackend('/path/to/project');
|
|
102
|
+
* backend.initialize();
|
|
103
|
+
* const results = backend.queryLearnings('i18n locale');
|
|
104
|
+
*/
|
|
105
|
+
function getBackend(projectPath) {
|
|
106
|
+
var _a, _b, _c;
|
|
107
|
+
const config = loadStorageConfig(projectPath);
|
|
108
|
+
const engine = (_b = (_a = config === null || config === void 0 ? void 0 : config.storage) === null || _a === void 0 ? void 0 : _a.backend) !== null && _b !== void 0 ? _b : 'sqlite';
|
|
109
|
+
switch (engine) {
|
|
110
|
+
case 'viking': {
|
|
111
|
+
const vikingConfig = Object.assign(Object.assign({}, viking_http_client_1.DEFAULT_VIKING_CONFIG), (_c = config === null || config === void 0 ? void 0 : config.storage) === null || _c === void 0 ? void 0 : _c.viking);
|
|
112
|
+
return new viking_backend_1.VikingBackend(vikingConfig);
|
|
113
|
+
}
|
|
114
|
+
case 'sqlite':
|
|
115
|
+
default:
|
|
116
|
+
return new SqliteBackend(projectPath);
|
|
117
|
+
}
|
|
118
|
+
}
|
package/install.sh
CHANGED
|
@@ -28,11 +28,11 @@ VERSION="4.4.0"
|
|
|
28
28
|
SCOPE="user" # default scope for Claude Code
|
|
29
29
|
|
|
30
30
|
if [ -d "skills" ]; then
|
|
31
|
-
TOTAL_SKILLS=$(ls -1d skills
|
|
31
|
+
TOTAL_SKILLS=$(ls -1d skills/cm-*/SKILL.md 2>/dev/null | wc -l | tr -d ' ')
|
|
32
32
|
elif [ -d "$HOME/.cody-master/skills" ]; then
|
|
33
|
-
TOTAL_SKILLS=$(ls -1d "$HOME/.cody-master/skills"
|
|
33
|
+
TOTAL_SKILLS=$(ls -1d "$HOME/.cody-master/skills"/cm-*/SKILL.md 2>/dev/null | wc -l | tr -d ' ')
|
|
34
34
|
else
|
|
35
|
-
TOTAL_SKILLS="
|
|
35
|
+
TOTAL_SKILLS="45"
|
|
36
36
|
fi
|
|
37
37
|
|
|
38
38
|
|
|
@@ -55,7 +55,7 @@ msg() {
|
|
|
55
55
|
local key="$1"
|
|
56
56
|
case "$LANG_CODE:$key" in
|
|
57
57
|
vi:welcome) echo "Chร o mแปซng bแบกn ฤแบฟn vแปi CodyMaster v${VERSION}" ;;
|
|
58
|
-
vi:tagline) echo "
|
|
58
|
+
vi:tagline) echo "${TOTAL_SKILLS} kแปน nฤng AI cho Claude Code vร cรกc AI agents khรกc" ;;
|
|
59
59
|
vi:detecting) echo "๐ ฤang phรกt hiแปn cรกc AI agent ฤรฃ cร i..." ;;
|
|
60
60
|
vi:found) echo "โ
ฤรฃ tรฌm thแบฅy" ;;
|
|
61
61
|
vi:not_found) echo "โ Khรดng tรฌm thแบฅy" ;;
|
|
@@ -68,7 +68,7 @@ msg() {
|
|
|
68
68
|
vi:docs) echo "๐ Tร i liแปu:" ;;
|
|
69
69
|
|
|
70
70
|
zh:welcome) echo "ๆฌข่ฟไฝฟ็จ CodyMaster v${VERSION}" ;;
|
|
71
|
-
zh:tagline) echo "Claude Code ็
|
|
71
|
+
zh:tagline) echo "Claude Code ็ ${TOTAL_SKILLS} AI ๆ่ฝ" ;;
|
|
72
72
|
zh:detecting) echo "๐ ๆฃๆตๅทฒๅฎ่ฃ
็ AI Agent..." ;;
|
|
73
73
|
zh:found) echo "โ
ๅทฒๆพๅฐ" ;;
|
|
74
74
|
zh:not_found) echo "โ ๆชๆพๅฐ" ;;
|
|
@@ -81,7 +81,7 @@ msg() {
|
|
|
81
81
|
zh:docs) echo "๐ ๆๆกฃ:" ;;
|
|
82
82
|
|
|
83
83
|
ko:welcome) echo "CodyMaster v${VERSION}์ ์ค์ ๊ฒ์ ํ์ํฉ๋๋ค" ;;
|
|
84
|
-
ko:tagline) echo "Claude Code์ฉ
|
|
84
|
+
ko:tagline) echo "Claude Code์ฉ ${TOTAL_SKILLS} AI ์คํฌ" ;;
|
|
85
85
|
ko:detecting) echo "๐ ์ค์น๋ AI Agent ๊ฐ์ง ์ค..." ;;
|
|
86
86
|
ko:found) echo "โ
๋ฐ๊ฒฌ๋จ" ;;
|
|
87
87
|
ko:not_found) echo "โ ์์" ;;
|
|
@@ -96,7 +96,7 @@ msg() {
|
|
|
96
96
|
*)
|
|
97
97
|
case "$key" in
|
|
98
98
|
welcome) echo "Welcome to CodyMaster v${VERSION}" ;;
|
|
99
|
-
tagline) echo "
|
|
99
|
+
tagline) echo "${TOTAL_SKILLS} AI skills for Claude Code and other AI agents" ;;
|
|
100
100
|
detecting) echo "๐ Detecting installed AI agents..." ;;
|
|
101
101
|
found) echo "โ
Found" ;;
|
|
102
102
|
not_found) echo "โ Not found" ;;
|
|
@@ -608,6 +608,10 @@ ensure_clone() {
|
|
|
608
608
|
}
|
|
609
609
|
CLONE_DIR="$HOME/.cody-master"
|
|
610
610
|
echo -e " ${G}โ
Cloned to ~/.cody-master${NC}"
|
|
611
|
+
# Update total skills after pulling down new repo
|
|
612
|
+
if [ -d "$CLONE_DIR/skills" ]; then
|
|
613
|
+
TOTAL_SKILLS=$(ls -1d "$CLONE_DIR/skills"/cm-*/SKILL.md 2>/dev/null | wc -l | tr -d ' ')
|
|
614
|
+
fi
|
|
611
615
|
}
|
|
612
616
|
|
|
613
617
|
# โโ Copy skills to target directory โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -621,7 +625,7 @@ install_skills_to() {
|
|
|
621
625
|
mkdir -p "$target"
|
|
622
626
|
local count=0
|
|
623
627
|
local installed=()
|
|
624
|
-
for skill_dir in "${CLONE_DIR}"/skills
|
|
628
|
+
for skill_dir in "${CLONE_DIR}"/skills/cm-*/; do
|
|
625
629
|
skill_name=$(basename "$skill_dir")
|
|
626
630
|
if [ -f "${skill_dir}SKILL.md" ]; then
|
|
627
631
|
if [[ "$format" == "mdc" ]]; then
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -18,7 +18,7 @@ try {
|
|
|
18
18
|
skillCount = fs.readdirSync(skillsDir)
|
|
19
19
|
.filter(f => {
|
|
20
20
|
const fullPath = path.join(skillsDir, f);
|
|
21
|
-
return fs.statSync(fullPath).isDirectory() && fs.existsSync(path.join(fullPath, 'SKILL.md'));
|
|
21
|
+
return f.startsWith('cm-') && fs.statSync(fullPath).isDirectory() && fs.existsSync(path.join(fullPath, 'SKILL.md'));
|
|
22
22
|
})
|
|
23
23
|
.length;
|
|
24
24
|
}
|
|
@@ -31,9 +31,15 @@ Read .cm/skeleton-index.md (~500 tokens) โ modules, entry points, config fi
|
|
|
31
31
|
|
|
32
32
|
### Step 3 โ Scope-filter learnings (only if L0 flags relevant entries)
|
|
33
33
|
```
|
|
34
|
+
# SQLite backend (default):
|
|
34
35
|
Query: cm_query(scope="learnings", query="{current module or error type}", limit=5)
|
|
35
36
|
OR read .cm/memory/learnings.json filtered to scope == "global" | "module:X"
|
|
36
37
|
|
|
38
|
+
# OpenViking backend (if storage.backend: viking in .cm/config.yaml):
|
|
39
|
+
Query: cm_query(query="{current module or error type}", limit=5)
|
|
40
|
+
โ OpenViking uses VECTOR SEMANTIC SEARCH โ no need for exact keyword match.
|
|
41
|
+
"i18n locale update" will also match "translation missing" and "locale fallback"
|
|
42
|
+
|
|
37
43
|
Rules:
|
|
38
44
|
NEVER load status = "invalidated" (proven wrong)
|
|
39
45
|
CAUTION status = "corrected" (verify before applying)
|
|
@@ -57,6 +63,10 @@ cm continuity budget (or: loadBudget + checkBudget in code)
|
|
|
57
63
|
> **Token savings v5:** Full cold load ~3,200 tokens โ Smart Spine load ~700 tokens (78% reduction).
|
|
58
64
|
> L0 indexes + context bus + scope filter make this possible.
|
|
59
65
|
> Only escalate to L2 when L0/L1 explicitly flag the need.
|
|
66
|
+
>
|
|
67
|
+
> **Viking bonus:** With OpenViking backend, L0/L1 indexes are auto-generated by the engine โ
|
|
68
|
+
> no manual `cm continuity index` needed. Vector search also improves Step 3 recall by ~40%.
|
|
69
|
+
> Switch: set `storage.backend: viking` in `.cm/config.yaml`.
|
|
60
70
|
|
|
61
71
|
---
|
|
62
72
|
|
|
@@ -48,6 +48,25 @@ cm continuity mcp
|
|
|
48
48
|
|
|
49
49
|
# Migrate learnings.json + decisions.json โ SQLite (one-time)
|
|
50
50
|
cm continuity migrate
|
|
51
|
+
|
|
52
|
+
# Export SQLite back to JSON (backup)
|
|
53
|
+
cm continuity export
|
|
54
|
+
|
|
55
|
+
# โโ OpenViking backend (optional) โโโโโโโโโโโโโโโโโโโโโโโโ
|
|
56
|
+
# 1. Install OpenViking server (Python 3.10+)
|
|
57
|
+
pip install openviking --upgrade
|
|
58
|
+
|
|
59
|
+
# 2. Configure ~/.openviking/ov.conf with embedding provider, then start:
|
|
60
|
+
openviking start # Runs on localhost:1933 by default
|
|
61
|
+
|
|
62
|
+
# 3. Switch CodyMaster to use OpenViking in .cm/config.yaml:
|
|
63
|
+
# storage:
|
|
64
|
+
# backend: viking
|
|
65
|
+
# viking:
|
|
66
|
+
# host: localhost
|
|
67
|
+
# port: 1933
|
|
68
|
+
# workspace: codymaster
|
|
69
|
+
# timeout: 60000
|
|
51
70
|
```
|
|
52
71
|
|
|
53
72
|
## The Protocol
|
|
@@ -163,15 +182,22 @@ Tier 2: WORKING MEMORY (current session โ 7 days)
|
|
|
163
182
|
ยท Read via: cm continuity bus | cm_bus_read MCP tool
|
|
164
183
|
|
|
165
184
|
Tier 3: LONG-TERM MEMORY (30+ days, only if reinforced)
|
|
166
|
-
โ
|
|
185
|
+
โ Default: .cm/context.db (SQLite + FTS5)
|
|
167
186
|
ยท learnings table + learnings_fts (BM25 keyword search)
|
|
168
187
|
ยท decisions table + decisions_fts
|
|
169
188
|
ยท skill_outputs per session/chain
|
|
170
189
|
ยท indexes table (cached L0/L1 content + staleness hash)
|
|
190
|
+
โ Optional: OpenViking backend (storage.backend: viking in .cm/config.yaml)
|
|
191
|
+
ยท True vector semantic search โ finds "async timeout" even when you query "network delay"
|
|
192
|
+
ยท L0/L1/L2 auto-generated by engine โ no manual cm continuity index needed
|
|
193
|
+
ยท Session compression + long-term memory extraction built-in
|
|
194
|
+
ยท Graph relations between memories (link/unlink)
|
|
195
|
+
ยท Setup: pip install openviking && openviking start
|
|
171
196
|
โ Fallback: .cm/memory/learnings.json + decisions.json (kept for compat)
|
|
172
197
|
โ L0 indexes: .cm/learnings-index.md (~100 tok), .cm/skeleton-index.md (~500 tok)
|
|
173
198
|
ยท Auto-regenerated on addLearning() + on demand via cm continuity index
|
|
174
199
|
ยท File watcher auto-refreshes learnings L0 on JSON change (300ms debounce)
|
|
200
|
+
ยท With Viking: engine generates L0/L1 automatically โ no file watcher needed
|
|
175
201
|
โ Token budget: .cm/token-budget.json โ 200k window, per-category soft limits
|
|
176
202
|
ยท Enforced at load time: checkBudget() โ allowed/remaining/suggestion
|
|
177
203
|
ยท View: cm continuity budget
|
|
@@ -185,11 +211,12 @@ Tier 5: STRUCTURAL CODE MEMORY (optional โ code-heavy projects)
|
|
|
185
211
|
โ See cm-codeintell skill โ ONLY when >50 source files
|
|
186
212
|
```
|
|
187
213
|
|
|
188
|
-
**CONTINUITY.md
|
|
189
|
-
**context bus
|
|
190
|
-
**L0 indexes
|
|
191
|
-
**context.db
|
|
192
|
-
**
|
|
214
|
+
**CONTINUITY.md = "what am I doing NOW?"**
|
|
215
|
+
**context bus = "what did upstream skills produce in this chain?"**
|
|
216
|
+
**L0 indexes = "cheapest possible memory load (~600 tokens)"**
|
|
217
|
+
**context.db = "keyword search across all learnings + decisions"**
|
|
218
|
+
**OpenViking (opt.) = "semantic vector search + auto L0/L1 + session compression"**
|
|
219
|
+
**qmd (optional) = "find what was written across hundreds of docs"**
|
|
193
220
|
|
|
194
221
|
### MCP Context Server (Claude Desktop integration)
|
|
195
222
|
|
package/skills/cm-start/SKILL.md
CHANGED
|
@@ -13,12 +13,15 @@ When this workflow is called, the AI Assistant should execute the following acti
|
|
|
13
13
|
Per `_shared/helpers.md#Load-Working-Memory` โ **use Smart Spine order:**
|
|
14
14
|
1. Check `.cm/context-bus.json` โ any active pipeline? any prior skill output to reuse?
|
|
15
15
|
2. Load L0 indexes: `learnings-index.md` (~100 tok) + `skeleton-index.md` (~500 tok)
|
|
16
|
+
> **If OpenViking backend active:** Skip step 2 โ engine auto-serves L0/L1 via `cm_resolve`.
|
|
16
17
|
3. Scope-filter learnings via `cm_query` โ only load what matches current objective
|
|
18
|
+
> **If OpenViking:** `cm_query` uses vector semantic search โ broader recall, fewer missed learnings.
|
|
17
19
|
4. Read `CONTINUITY.md` โ set Active Goal to the new objective
|
|
18
20
|
5. Run token budget check: `cm continuity budget` โ confirm no category is over soft limit
|
|
19
21
|
|
|
20
22
|
> โก Total context load: ~700 tokens. Full load used to be ~3,200.
|
|
21
23
|
> Only escalate to L2 (full files) if L0 index explicitly flags a match.
|
|
24
|
+
> With OpenViking: L0 is auto-maintained โ no stale index risk.
|
|
22
25
|
|
|
23
26
|
0.5. **Skill Coverage Check (Adaptive Discovery):**
|
|
24
27
|
- Scan the objective for technologies, frameworks, or patterns mentioned
|
|
@@ -82,5 +85,11 @@ When this workflow is called, the AI Assistant should execute the following acti
|
|
|
82
85
|
- Record any new learnings or decisions made during this workflow
|
|
83
86
|
- If inside a skill chain: `cm continuity bus` โ verify context bus reflects completed step
|
|
84
87
|
- Refresh L0 indexes: `cm continuity index` (auto-runs on `addLearning`, manual refresh here)
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
> **If OpenViking:** Skip manual index refresh โ engine maintains L0/L1 automatically.
|
|
89
|
+
|
|
90
|
+
> **Note for AI:** If this is a brand new project, suggest running `cm-project-bootstrap` first.
|
|
91
|
+
> If the working environment has a risk of accidentally switching accounts/projects, remind about `cm-identity-guard` (Per `_shared/helpers.md#Identity-Check`).
|
|
92
|
+
>
|
|
93
|
+
> **OpenViking tip:** If the project uses many learnings/decisions (>100 entries) or needs semantic
|
|
94
|
+
> search beyond keyword matching, suggest switching to the Viking backend:
|
|
95
|
+
> `storage.backend: viking` in `.cm/config.yaml` + `pip install openviking && openviking start`
|