mdkg 0.1.2 → 0.1.3
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 +25 -0
- package/README.md +22 -7
- package/dist/cli.js +3 -0
- package/dist/commands/archive.js +20 -8
- package/dist/commands/bundle_import.js +18 -6
- package/dist/commands/checkpoint.js +31 -5
- package/dist/commands/doctor.js +46 -9
- package/dist/commands/index.js +12 -23
- package/dist/commands/init.js +7 -1
- package/dist/commands/new.js +33 -7
- package/dist/commands/task.js +20 -6
- package/dist/commands/validate.js +6 -0
- package/dist/commands/work.js +43 -11
- package/dist/core/config.js +15 -0
- package/dist/graph/bundle_imports.js +2 -2
- package/dist/graph/capabilities_index_cache.js +2 -2
- package/dist/graph/index_cache.js +2 -2
- package/dist/graph/reindex.js +46 -0
- package/dist/graph/skills_index_cache.js +2 -2
- package/dist/graph/sqlite_index.js +293 -0
- package/dist/init/AGENT_START.md +3 -0
- package/dist/init/CLI_COMMAND_MATRIX.md +5 -0
- package/dist/init/README.md +11 -2
- package/dist/init/config.json +5 -1
- package/dist/init/core/rule-1-mdkg-conventions.md +2 -1
- package/dist/init/core/rule-3-cli-contract.md +2 -1
- package/dist/init/core/rule-4-repo-safety-and-ignores.md +27 -11
- package/dist/init/core/rule-5-release-and-versioning.md +4 -3
- package/dist/init/init-manifest.json +9 -9
- package/dist/util/atomic.js +44 -0
- package/dist/util/lock.js +72 -0
- package/package.json +8 -5
|
@@ -0,0 +1,293 @@
|
|
|
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.SQLITE_SCHEMA_VERSION = void 0;
|
|
7
|
+
exports.isSqliteBackend = isSqliteBackend;
|
|
8
|
+
exports.resolveSqlitePath = resolveSqlitePath;
|
|
9
|
+
exports.writeSqliteIndex = writeSqliteIndex;
|
|
10
|
+
exports.reserveSqliteNumericId = reserveSqliteNumericId;
|
|
11
|
+
exports.sqliteHealth = sqliteHealth;
|
|
12
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
13
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
const staleness_1 = require("./staleness");
|
|
16
|
+
exports.SQLITE_SCHEMA_VERSION = 1;
|
|
17
|
+
function loadDatabaseCtor() {
|
|
18
|
+
try {
|
|
19
|
+
const loaded = require("node:sqlite");
|
|
20
|
+
if (!loaded.DatabaseSync) {
|
|
21
|
+
throw new Error("node:sqlite DatabaseSync is unavailable");
|
|
22
|
+
}
|
|
23
|
+
return loaded.DatabaseSync;
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
27
|
+
throw new Error(`node:sqlite is required for mdkg SQLite index support: ${message}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function toPosixPath(value) {
|
|
31
|
+
return value.split(path_1.default.sep).join("/");
|
|
32
|
+
}
|
|
33
|
+
function sha256File(filePath) {
|
|
34
|
+
return `sha256:${crypto_1.default.createHash("sha256").update(fs_1.default.readFileSync(filePath)).digest("hex")}`;
|
|
35
|
+
}
|
|
36
|
+
function stripVolatileCacheFields(value) {
|
|
37
|
+
if (Array.isArray(value)) {
|
|
38
|
+
return value.map((item) => stripVolatileCacheFields(item));
|
|
39
|
+
}
|
|
40
|
+
if (value && typeof value === "object") {
|
|
41
|
+
const input = value;
|
|
42
|
+
const output = {};
|
|
43
|
+
for (const key of Object.keys(input).sort()) {
|
|
44
|
+
if (key === "generated_at" || key === "indexed_at") {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
output[key] = stripVolatileCacheFields(input[key]);
|
|
48
|
+
}
|
|
49
|
+
return output;
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
function stableCacheJson(value) {
|
|
54
|
+
return JSON.stringify(stripVolatileCacheFields(value));
|
|
55
|
+
}
|
|
56
|
+
function isSqliteBackend(config) {
|
|
57
|
+
return config.index.backend === "sqlite";
|
|
58
|
+
}
|
|
59
|
+
function resolveSqlitePath(root, config) {
|
|
60
|
+
return path_1.default.resolve(root, config.index.sqlite_path);
|
|
61
|
+
}
|
|
62
|
+
function sqliteTempPath(sqlitePath) {
|
|
63
|
+
return path_1.default.join(path_1.default.dirname(sqlitePath), `.${path_1.default.basename(sqlitePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
64
|
+
}
|
|
65
|
+
function createSchema(db) {
|
|
66
|
+
db.exec(`
|
|
67
|
+
PRAGMA journal_mode = DELETE;
|
|
68
|
+
PRAGMA synchronous = FULL;
|
|
69
|
+
CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
70
|
+
CREATE TABLE nodes (
|
|
71
|
+
qid TEXT PRIMARY KEY,
|
|
72
|
+
id TEXT NOT NULL,
|
|
73
|
+
ws TEXT NOT NULL,
|
|
74
|
+
type TEXT NOT NULL,
|
|
75
|
+
title TEXT NOT NULL,
|
|
76
|
+
path TEXT NOT NULL,
|
|
77
|
+
status TEXT,
|
|
78
|
+
priority INTEGER,
|
|
79
|
+
updated TEXT,
|
|
80
|
+
source_hash TEXT,
|
|
81
|
+
json TEXT NOT NULL
|
|
82
|
+
);
|
|
83
|
+
CREATE TABLE edges (
|
|
84
|
+
source_qid TEXT NOT NULL,
|
|
85
|
+
kind TEXT NOT NULL,
|
|
86
|
+
target_qid TEXT NOT NULL,
|
|
87
|
+
PRIMARY KEY (source_qid, kind, target_qid)
|
|
88
|
+
);
|
|
89
|
+
CREATE TABLE skills (
|
|
90
|
+
qid TEXT PRIMARY KEY,
|
|
91
|
+
slug TEXT NOT NULL,
|
|
92
|
+
ws TEXT NOT NULL,
|
|
93
|
+
name TEXT NOT NULL,
|
|
94
|
+
path TEXT NOT NULL,
|
|
95
|
+
json TEXT NOT NULL
|
|
96
|
+
);
|
|
97
|
+
CREATE TABLE capabilities (
|
|
98
|
+
qid TEXT NOT NULL,
|
|
99
|
+
kind TEXT NOT NULL,
|
|
100
|
+
workspace TEXT NOT NULL,
|
|
101
|
+
visibility TEXT NOT NULL,
|
|
102
|
+
id TEXT NOT NULL,
|
|
103
|
+
path TEXT NOT NULL,
|
|
104
|
+
source_hash TEXT NOT NULL,
|
|
105
|
+
json TEXT NOT NULL,
|
|
106
|
+
PRIMARY KEY (qid, kind, path)
|
|
107
|
+
);
|
|
108
|
+
CREATE TABLE archives (
|
|
109
|
+
qid TEXT PRIMARY KEY,
|
|
110
|
+
visibility TEXT NOT NULL,
|
|
111
|
+
compressed_path TEXT,
|
|
112
|
+
compressed_sha256 TEXT,
|
|
113
|
+
json TEXT NOT NULL
|
|
114
|
+
);
|
|
115
|
+
CREATE TABLE bundle_imports (
|
|
116
|
+
alias TEXT PRIMARY KEY,
|
|
117
|
+
enabled INTEGER NOT NULL,
|
|
118
|
+
stale INTEGER NOT NULL,
|
|
119
|
+
error_count INTEGER NOT NULL,
|
|
120
|
+
warning_count INTEGER NOT NULL,
|
|
121
|
+
json TEXT NOT NULL
|
|
122
|
+
);
|
|
123
|
+
CREATE TABLE id_allocations (
|
|
124
|
+
ws TEXT NOT NULL,
|
|
125
|
+
prefix TEXT NOT NULL,
|
|
126
|
+
next_value INTEGER NOT NULL,
|
|
127
|
+
PRIMARY KEY (ws, prefix)
|
|
128
|
+
);
|
|
129
|
+
`);
|
|
130
|
+
}
|
|
131
|
+
function insertMeta(db, key, value) {
|
|
132
|
+
db.prepare("INSERT INTO meta (key, value) VALUES (?, ?)").run(key, value);
|
|
133
|
+
}
|
|
134
|
+
function nodeSourceHash(root, nodePath) {
|
|
135
|
+
const fullPath = path_1.default.resolve(root, nodePath);
|
|
136
|
+
if (!fs_1.default.existsSync(fullPath) || !fs_1.default.statSync(fullPath).isFile()) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
return sha256File(fullPath);
|
|
140
|
+
}
|
|
141
|
+
function buildSourceFingerprint(options) {
|
|
142
|
+
const payload = {
|
|
143
|
+
nodes: Object.values(options.nodeIndex.nodes)
|
|
144
|
+
.sort((a, b) => a.qid.localeCompare(b.qid))
|
|
145
|
+
.map((node) => ({
|
|
146
|
+
qid: node.qid,
|
|
147
|
+
path: node.path,
|
|
148
|
+
hash: options.nodeHashes.get(node.qid) ?? "",
|
|
149
|
+
})),
|
|
150
|
+
skills: Object.values(options.skillsIndex.skills).sort((a, b) => a.qid.localeCompare(b.qid)),
|
|
151
|
+
capabilities: options.capabilitiesIndex.records,
|
|
152
|
+
imports: options.importsIndex.imports,
|
|
153
|
+
};
|
|
154
|
+
return `sha256:${crypto_1.default.createHash("sha256").update(stableCacheJson(payload)).digest("hex")}`;
|
|
155
|
+
}
|
|
156
|
+
function writeSqliteIndex(options) {
|
|
157
|
+
const sqlitePath = resolveSqlitePath(options.root, options.config);
|
|
158
|
+
fs_1.default.mkdirSync(path_1.default.dirname(sqlitePath), { recursive: true });
|
|
159
|
+
const tempPath = sqliteTempPath(sqlitePath);
|
|
160
|
+
fs_1.default.rmSync(tempPath, { force: true });
|
|
161
|
+
const DatabaseSync = loadDatabaseCtor();
|
|
162
|
+
const db = new DatabaseSync(tempPath);
|
|
163
|
+
try {
|
|
164
|
+
const nodeHashes = new Map();
|
|
165
|
+
for (const node of Object.values(options.nodeIndex.nodes)) {
|
|
166
|
+
nodeHashes.set(node.qid, nodeSourceHash(options.root, node.path));
|
|
167
|
+
}
|
|
168
|
+
createSchema(db);
|
|
169
|
+
insertMeta(db, "tool", "mdkg");
|
|
170
|
+
insertMeta(db, "schema_version", String(exports.SQLITE_SCHEMA_VERSION));
|
|
171
|
+
insertMeta(db, "package_schema_version", String(options.config.schema_version));
|
|
172
|
+
insertMeta(db, "backend", options.config.index.backend);
|
|
173
|
+
insertMeta(db, "source_fingerprint", buildSourceFingerprint({ ...options, nodeHashes }));
|
|
174
|
+
insertMeta(db, "root", ".");
|
|
175
|
+
const insertNode = db.prepare("INSERT INTO nodes (qid, id, ws, type, title, path, status, priority, updated, source_hash, json) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
|
176
|
+
const insertEdge = db.prepare("INSERT OR IGNORE INTO edges (source_qid, kind, target_qid) VALUES (?, ?, ?)");
|
|
177
|
+
for (const node of Object.values(options.nodeIndex.nodes).sort((a, b) => a.qid.localeCompare(b.qid))) {
|
|
178
|
+
insertNode.run(node.qid, node.id, node.ws, node.type, node.title, toPosixPath(node.path), node.status ?? null, node.priority ?? null, node.updated ?? null, nodeHashes.get(node.qid) ?? null, stableCacheJson(node));
|
|
179
|
+
for (const [kind, values] of [
|
|
180
|
+
["relates", node.edges.relates],
|
|
181
|
+
["blocked_by", node.edges.blocked_by],
|
|
182
|
+
["blocks", node.edges.blocks],
|
|
183
|
+
]) {
|
|
184
|
+
for (const target of values) {
|
|
185
|
+
insertEdge.run(node.qid, kind, target);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const [kind, target] of [
|
|
189
|
+
["epic", node.edges.epic],
|
|
190
|
+
["parent", node.edges.parent],
|
|
191
|
+
["prev", node.edges.prev],
|
|
192
|
+
["next", node.edges.next],
|
|
193
|
+
]) {
|
|
194
|
+
if (target) {
|
|
195
|
+
insertEdge.run(node.qid, kind, target);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const insertSkill = db.prepare("INSERT INTO skills (qid, slug, ws, name, path, json) VALUES (?, ?, ?, ?, ?, ?)");
|
|
200
|
+
for (const skill of Object.values(options.skillsIndex.skills).sort((a, b) => a.qid.localeCompare(b.qid))) {
|
|
201
|
+
insertSkill.run(skill.qid, skill.slug, skill.ws, skill.name, skill.path, stableCacheJson(skill));
|
|
202
|
+
}
|
|
203
|
+
const insertCapability = db.prepare("INSERT INTO capabilities (qid, kind, workspace, visibility, id, path, source_hash, json) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
|
204
|
+
for (const record of options.capabilitiesIndex.records) {
|
|
205
|
+
insertCapability.run(record.qid, record.kind, record.workspace, record.visibility, record.id, record.path, record.source_hash, stableCacheJson(record));
|
|
206
|
+
}
|
|
207
|
+
const insertArchive = db.prepare("INSERT INTO archives (qid, visibility, compressed_path, compressed_sha256, json) VALUES (?, ?, ?, ?, ?)");
|
|
208
|
+
for (const node of Object.values(options.nodeIndex.nodes).filter((item) => item.type === "archive")) {
|
|
209
|
+
insertArchive.run(node.qid, String(node.attributes.visibility ?? "private"), String(node.attributes.compressed_path ?? ""), String(node.attributes.compressed_sha256 ?? ""), stableCacheJson(node));
|
|
210
|
+
}
|
|
211
|
+
const insertImport = db.prepare("INSERT INTO bundle_imports (alias, enabled, stale, error_count, warning_count, json) VALUES (?, ?, ?, ?, ?, ?)");
|
|
212
|
+
for (const item of options.importsIndex.imports) {
|
|
213
|
+
insertImport.run(item.alias, item.enabled ? 1 : 0, item.stale ? 1 : 0, item.error_count, item.warning_count, stableCacheJson(item));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
finally {
|
|
217
|
+
db.close();
|
|
218
|
+
}
|
|
219
|
+
fs_1.default.renameSync(tempPath, sqlitePath);
|
|
220
|
+
return sqlitePath;
|
|
221
|
+
}
|
|
222
|
+
function reserveSqliteNumericId(options) {
|
|
223
|
+
if (!isSqliteBackend(options.config)) {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
const sqlitePath = resolveSqlitePath(options.root, options.config);
|
|
227
|
+
fs_1.default.mkdirSync(path_1.default.dirname(sqlitePath), { recursive: true });
|
|
228
|
+
const DatabaseSync = loadDatabaseCtor();
|
|
229
|
+
const db = new DatabaseSync(sqlitePath);
|
|
230
|
+
try {
|
|
231
|
+
db.exec("CREATE TABLE IF NOT EXISTS id_allocations (ws TEXT NOT NULL, prefix TEXT NOT NULL, next_value INTEGER NOT NULL, PRIMARY KEY (ws, prefix));");
|
|
232
|
+
db.exec("BEGIN IMMEDIATE");
|
|
233
|
+
const row = db
|
|
234
|
+
.prepare("SELECT next_value FROM id_allocations WHERE ws = ? AND prefix = ?")
|
|
235
|
+
.get(options.ws, options.prefix);
|
|
236
|
+
const existing = typeof row?.next_value === "number" ? row.next_value : undefined;
|
|
237
|
+
const nextValue = Math.max(existing ?? 1, options.currentMax + 1);
|
|
238
|
+
db.prepare("INSERT INTO id_allocations (ws, prefix, next_value) VALUES (?, ?, ?) ON CONFLICT(ws, prefix) DO UPDATE SET next_value = excluded.next_value").run(options.ws, options.prefix, nextValue + 1);
|
|
239
|
+
db.exec("COMMIT");
|
|
240
|
+
return `${options.prefix}-${nextValue}`;
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
try {
|
|
244
|
+
db.exec("ROLLBACK");
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// ignore rollback failures when no transaction is active
|
|
248
|
+
}
|
|
249
|
+
throw err;
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
db.close();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function sqliteHealth(root, config) {
|
|
256
|
+
const sqlitePath = resolveSqlitePath(root, config);
|
|
257
|
+
const warnings = [];
|
|
258
|
+
const errors = [];
|
|
259
|
+
if (!fs_1.default.existsSync(sqlitePath)) {
|
|
260
|
+
warnings.push(`SQLite cache missing at ${toPosixPath(path_1.default.relative(root, sqlitePath))}; run mdkg index`);
|
|
261
|
+
return { path: sqlitePath, exists: false, size: 0, warnings, errors };
|
|
262
|
+
}
|
|
263
|
+
const size = fs_1.default.statSync(sqlitePath).size;
|
|
264
|
+
if (config.index.sqlite_commit_warning_bytes > 0 && size > config.index.sqlite_commit_warning_bytes) {
|
|
265
|
+
warnings.push(`SQLite cache exceeds ${config.index.sqlite_commit_warning_bytes} bytes: ${toPosixPath(path_1.default.relative(root, sqlitePath))} (${size} bytes)`);
|
|
266
|
+
}
|
|
267
|
+
for (const suffix of ["-wal", "-shm", "-journal"]) {
|
|
268
|
+
const transient = `${sqlitePath}${suffix}`;
|
|
269
|
+
if (fs_1.default.existsSync(transient)) {
|
|
270
|
+
warnings.push(`transient SQLite file present: ${toPosixPath(path_1.default.relative(root, transient))}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
const DatabaseSync = loadDatabaseCtor();
|
|
275
|
+
const db = new DatabaseSync(sqlitePath);
|
|
276
|
+
try {
|
|
277
|
+
const row = db.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get();
|
|
278
|
+
if (String(row?.value ?? "") !== String(exports.SQLITE_SCHEMA_VERSION)) {
|
|
279
|
+
errors.push(`SQLite schema mismatch; run mdkg index`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
finally {
|
|
283
|
+
db.close();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
errors.push(`failed to read SQLite cache: ${err instanceof Error ? err.message : String(err)}`);
|
|
288
|
+
}
|
|
289
|
+
if ((0, staleness_1.isIndexStale)(root, config)) {
|
|
290
|
+
warnings.push("SQLite cache may be stale because source graph files or config changed; run mdkg index");
|
|
291
|
+
}
|
|
292
|
+
return { path: sqlitePath, exists: true, size, warnings, errors };
|
|
293
|
+
}
|
package/dist/init/AGENT_START.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
This repository uses mdkg for deterministic project memory.
|
|
4
4
|
|
|
5
|
+
mdkg is pre-v1 public alpha software. Treat generated graph, cache, bundle, and DAL contracts as usable but still stabilizing before v1.
|
|
6
|
+
|
|
5
7
|
Read these files in order:
|
|
6
8
|
1. `.mdkg/core/SOUL.md` if it exists
|
|
7
9
|
2. `.mdkg/core/HUMAN.md` if it exists
|
|
@@ -23,6 +25,7 @@ Agent operating prompt:
|
|
|
23
25
|
- Use `mdkg search "..."` and `mdkg next` to discover current work.
|
|
24
26
|
- Use `mdkg skill list`, `mdkg skill search`, and `mdkg skill show <slug>` for skill discovery.
|
|
25
27
|
- Use `mdkg capability list/search/show` for deterministic skills, `SPEC.md`, `WORK.md`, core-doc, and design-doc capability discovery.
|
|
28
|
+
- Use `mdkg index` to refresh JSON compatibility caches and `.mdkg/index/mdkg.sqlite` when SQLite mode is enabled.
|
|
26
29
|
- Use `mdkg archive add/list/show/verify/compress` for committed source and artifact sidecars under `.mdkg/archive`.
|
|
27
30
|
- Use `mdkg work ...` helpers for semantic mirror contracts, work orders, receipts, and artifact registration.
|
|
28
31
|
- Treat work contracts, orders, and receipts as committed semantic mirrors only; never store raw secrets, credentials, live payment state, ledger mutations, or canonical marketplace state in mdkg.
|
|
@@ -22,6 +22,11 @@ Primary commands:
|
|
|
22
22
|
- `mdkg task`
|
|
23
23
|
- `mdkg validate`
|
|
24
24
|
|
|
25
|
+
Index backend:
|
|
26
|
+
- fresh mdkg workspaces default to `index.backend: sqlite`
|
|
27
|
+
- `.mdkg/index/mdkg.sqlite` is rebuildable and commit-eligible when intentionally tracked
|
|
28
|
+
- JSON compatibility caches remain generated and ignored by default
|
|
29
|
+
|
|
25
30
|
Validation commands:
|
|
26
31
|
- `mdkg validate [--out <path>] [--quiet] [--json]`
|
|
27
32
|
|
package/dist/init/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
This repository is initialized for mdkg.
|
|
4
4
|
|
|
5
|
+
mdkg is pre-v1 public alpha software. Graph, cache, bundle, and DAL contracts may change quickly before v1.
|
|
6
|
+
|
|
5
7
|
## Layout
|
|
6
8
|
|
|
7
9
|
- `core/`: rules, operating guide, and pinned docs
|
|
@@ -10,7 +12,7 @@ This repository is initialized for mdkg.
|
|
|
10
12
|
- `templates/`: default node templates
|
|
11
13
|
- `archive/`: sidecar metadata and deterministic compressed source/artifact caches
|
|
12
14
|
- `bundles/`: optional committed full graph snapshot bundles
|
|
13
|
-
- `index/`: generated
|
|
15
|
+
- `index/`: generated JSON caches plus optional commit-eligible `mdkg.sqlite`
|
|
14
16
|
- `pack/`: generated context packs (do not commit)
|
|
15
17
|
|
|
16
18
|
## Next Commands
|
|
@@ -51,10 +53,17 @@ Read `AGENT_START.md` first when this repo includes it.
|
|
|
51
53
|
|
|
52
54
|
Ensure ignore files include:
|
|
53
55
|
|
|
54
|
-
- `.mdkg/index
|
|
56
|
+
- `.mdkg/index/*.json`
|
|
57
|
+
- `.mdkg/index/*.tmp`
|
|
58
|
+
- `.mdkg/index/write.lock/`
|
|
59
|
+
- `.mdkg/index/*.sqlite-wal`
|
|
60
|
+
- `.mdkg/index/*.sqlite-shm`
|
|
61
|
+
- `.mdkg/index/*.sqlite-journal`
|
|
55
62
|
- `.mdkg/pack/`
|
|
56
63
|
- `.mdkg/archive/**/source/`
|
|
57
64
|
|
|
65
|
+
Fresh mdkg workspaces default to `index.backend: sqlite`; `.mdkg/index/mdkg.sqlite` is a rebuildable cache and may be committed when the repo intentionally tracks it and it stays reasonably small.
|
|
66
|
+
|
|
58
67
|
Recommended:
|
|
59
68
|
|
|
60
69
|
```bash
|
package/dist/init/config.json
CHANGED
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
"index": {
|
|
9
9
|
"auto_reindex": true,
|
|
10
10
|
"tolerant": false,
|
|
11
|
-
"
|
|
11
|
+
"backend": "sqlite",
|
|
12
|
+
"global_index_path": ".mdkg/index/global.json",
|
|
13
|
+
"sqlite_path": ".mdkg/index/mdkg.sqlite",
|
|
14
|
+
"sqlite_commit_warning_bytes": 52428800,
|
|
15
|
+
"lock_timeout_ms": 10000
|
|
12
16
|
},
|
|
13
17
|
"capabilities": {
|
|
14
18
|
"cache_path": ".mdkg/index/capabilities.json"
|
|
@@ -229,8 +229,9 @@ The cache is enabled by default.
|
|
|
229
229
|
|
|
230
230
|
- Root global index lives at `.mdkg/index/global.json`
|
|
231
231
|
- Root skills index lives at `.mdkg/index/skills.json`
|
|
232
|
+
- Root SQLite access cache lives at `.mdkg/index/mdkg.sqlite` when `index.backend` is `sqlite`.
|
|
232
233
|
- Index is rebuilt automatically when stale unless disabled by flag/config.
|
|
233
|
-
- `.mdkg/index
|
|
234
|
+
- Generated JSON index/temp/lock files are ignored. `.mdkg/index/mdkg.sqlite` is rebuildable and may be committed only by explicit repo policy.
|
|
234
235
|
|
|
235
236
|
## Safety guidance (high level)
|
|
236
237
|
|
|
@@ -124,7 +124,7 @@ If a user provides an unqualified ID and it is ambiguous globally:
|
|
|
124
124
|
- `.agents/skills/`
|
|
125
125
|
- `.claude/skills/`
|
|
126
126
|
- deterministic `core.md` pin insertion (`rule-soul`, then `rule-human`)
|
|
127
|
-
- ignore policy for
|
|
127
|
+
- ignore policy for generated JSON index/temp/lock files, `.mdkg/pack/`, and raw archive source copies under `.mdkg/archive/**/source/`
|
|
128
128
|
- mirrored skills are append-focused outputs:
|
|
129
129
|
- `.mdkg/skills/` remains canonical
|
|
130
130
|
- unrelated existing folders under `.agents/skills/` and `.claude/skills/` are preserved
|
|
@@ -146,6 +146,7 @@ If a user provides an unqualified ID and it is ambiguous globally:
|
|
|
146
146
|
- rebuild skills cache `.mdkg/index/skills.json` from `.mdkg/skills/<slug>/SKILL.md`
|
|
147
147
|
- rebuild capability cache `.mdkg/index/capabilities.json` from skills, `SPEC.md`, `WORK.md`, core docs, and design docs
|
|
148
148
|
- rebuild bundle import projection cache `.mdkg/index/imports.json` when bundle imports are configured
|
|
149
|
+
- rebuild SQLite access cache `.mdkg/index/mdkg.sqlite` when `index.backend` is `sqlite`
|
|
149
150
|
- tolerate `.mdkg/skills/<slug>/SKILLS.md` on read with warning
|
|
150
151
|
- fail validation if both `SKILL.md` and `SKILLS.md` exist in one skill directory
|
|
151
152
|
- strict by default (fails on invalid frontmatter)
|
|
@@ -21,7 +21,8 @@ mdkg content may contain sensitive notes and internal project planning. This rul
|
|
|
21
21
|
|
|
22
22
|
- `.mdkg/` must not be shipped to production deployments.
|
|
23
23
|
- `.mdkg/` must not be published to npm.
|
|
24
|
-
- `.mdkg/index/` must
|
|
24
|
+
- Generated JSON index, temp, lock, WAL, SHM, and journal files under `.mdkg/index/` must not be committed.
|
|
25
|
+
- `.mdkg/index/mdkg.sqlite` is a rebuildable access cache and may be committed when the repo intentionally tracks it and it stays reasonably small.
|
|
25
26
|
- `.mdkg/bundles/` may be committed only when the repo intentionally tracks private or public snapshot bundles.
|
|
26
27
|
|
|
27
28
|
## Git ignore requirements
|
|
@@ -30,13 +31,22 @@ The repo MUST ignore at minimum:
|
|
|
30
31
|
|
|
31
32
|
- `node_modules/`
|
|
32
33
|
- `dist/`
|
|
33
|
-
- `.mdkg/index
|
|
34
|
+
- `.mdkg/index/*.json`
|
|
35
|
+
- `.mdkg/index/*.tmp`
|
|
36
|
+
- `.mdkg/index/write.lock/`
|
|
37
|
+
- `.mdkg/index/*.sqlite-wal`
|
|
38
|
+
- `.mdkg/index/*.sqlite-shm`
|
|
39
|
+
- `.mdkg/index/*.sqlite-journal`
|
|
34
40
|
- `.mdkg/pack/`
|
|
35
41
|
- `.mdkg/archive/**/source/`
|
|
36
42
|
|
|
37
43
|
Recommended `.gitignore` entries:
|
|
38
|
-
- `.mdkg/index
|
|
39
|
-
- `.mdkg/index
|
|
44
|
+
- `.mdkg/index/*.json`
|
|
45
|
+
- `.mdkg/index/*.tmp`
|
|
46
|
+
- `.mdkg/index/write.lock/`
|
|
47
|
+
- `.mdkg/index/*.sqlite-wal`
|
|
48
|
+
- `.mdkg/index/*.sqlite-shm`
|
|
49
|
+
- `.mdkg/index/*.sqlite-journal`
|
|
40
50
|
- `.mdkg/pack/`
|
|
41
51
|
- `.mdkg/archive/**/source/`
|
|
42
52
|
|
|
@@ -60,7 +70,12 @@ Additional belt-and-suspenders:
|
|
|
60
70
|
If the repo is containerized:
|
|
61
71
|
- `.dockerignore` SHOULD exclude:
|
|
62
72
|
- `.mdkg/`
|
|
63
|
-
- `.mdkg/index
|
|
73
|
+
- `.mdkg/index/*.json`
|
|
74
|
+
- `.mdkg/index/*.tmp`
|
|
75
|
+
- `.mdkg/index/write.lock/`
|
|
76
|
+
- `.mdkg/index/*.sqlite-wal`
|
|
77
|
+
- `.mdkg/index/*.sqlite-shm`
|
|
78
|
+
- `.mdkg/index/*.sqlite-journal`
|
|
64
79
|
- `node_modules/`
|
|
65
80
|
- `dist/` (if built in container)
|
|
66
81
|
- any other local artifacts
|
|
@@ -72,8 +87,8 @@ For application builds:
|
|
|
72
87
|
|
|
73
88
|
`mdkg init` updates ignore files by default for safety:
|
|
74
89
|
|
|
75
|
-
- `.gitignore` appends
|
|
76
|
-
- `.npmignore` appends `.mdkg/`,
|
|
90
|
+
- `.gitignore` appends generated index cache/temp/lock patterns, `.mdkg/pack/`, and raw archive source ignores.
|
|
91
|
+
- `.npmignore` appends `.mdkg/`, generated index cache/temp/lock patterns, and `.mdkg/pack/`.
|
|
77
92
|
- `--no-update-ignores` disables these default writes
|
|
78
93
|
|
|
79
94
|
Explicit flags remain available and take precedence:
|
|
@@ -84,9 +99,9 @@ Explicit flags remain available and take precedence:
|
|
|
84
99
|
|
|
85
100
|
## Index safety
|
|
86
101
|
|
|
87
|
-
- `.mdkg/index/`
|
|
88
|
-
-
|
|
89
|
-
-
|
|
102
|
+
- `.mdkg/index/` contains generated caches.
|
|
103
|
+
- JSON index files may contain extracted metadata and could expose sensitive strings; they MUST be ignored from git.
|
|
104
|
+
- `.mdkg/index/mdkg.sqlite` contains the same rebuildable access data and may be committed only by explicit repo policy; `mdkg doctor` warns when it exceeds `index.sqlite_commit_warning_bytes`.
|
|
90
105
|
- Index rebuild should be deterministic and safe to regenerate at any time.
|
|
91
106
|
|
|
92
107
|
## Bundle safety
|
|
@@ -115,7 +130,8 @@ Workspace-local `.mdkg/` directories (near code) should follow the same rules:
|
|
|
115
130
|
|
|
116
131
|
## Summary checklist
|
|
117
132
|
|
|
118
|
-
- ✅
|
|
133
|
+
- ✅ generated JSON index/temp/lock files ignored
|
|
134
|
+
- ✅ `.mdkg/index/mdkg.sqlite` committed only by explicit repo policy
|
|
119
135
|
- ✅ event logs are committed by default unless a repo chooses to ignore them manually
|
|
120
136
|
- ✅ npm publishes only `dist/`, `README.md`, `LICENSE`
|
|
121
137
|
- ✅ optional `.npmignore` excludes `.mdkg/`
|
|
@@ -37,14 +37,15 @@ The npm package MUST include:
|
|
|
37
37
|
|
|
38
38
|
It MUST NOT include:
|
|
39
39
|
- `.mdkg/` docs
|
|
40
|
-
- `.mdkg/index/`
|
|
40
|
+
- generated `.mdkg/index/` caches
|
|
41
41
|
- source code (optional; can be included later, but not required)
|
|
42
42
|
|
|
43
43
|
## Release checklist (v1)
|
|
44
44
|
|
|
45
45
|
1) Ensure clean working tree
|
|
46
46
|
- no uncommitted changes
|
|
47
|
-
-
|
|
47
|
+
- generated JSON index/temp/lock files ignored
|
|
48
|
+
- `.mdkg/index/mdkg.sqlite` either intentionally tracked or absent
|
|
48
49
|
|
|
49
50
|
2) Rebuild and validate
|
|
50
51
|
- run `mdkg index`
|
|
@@ -79,4 +80,4 @@ Release notes should call out:
|
|
|
79
80
|
- new commands / flags
|
|
80
81
|
- changes to pack behavior
|
|
81
82
|
- changes to config schema (and how migration behaves)
|
|
82
|
-
- new node types or template changes
|
|
83
|
+
- new node types or template changes
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"tool": "mdkg",
|
|
4
|
-
"mdkg_version": "0.1.
|
|
4
|
+
"mdkg_version": "0.1.3",
|
|
5
5
|
"files": [
|
|
6
6
|
{
|
|
7
7
|
"path": ".mdkg/config.json",
|
|
8
8
|
"category": "config",
|
|
9
|
-
"sha256": "
|
|
9
|
+
"sha256": "0319f2e92ae0030d67816c025802be50ec5eecc3274e75a4e0ded3e753d945b3"
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"path": ".mdkg/core/core.md",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
{
|
|
27
27
|
"path": ".mdkg/core/rule-1-mdkg-conventions.md",
|
|
28
28
|
"category": "core",
|
|
29
|
-
"sha256": "
|
|
29
|
+
"sha256": "15f886e6661046de45ad798c292d74cc1ff3cc14c8cfcc82ed0b8c35635d0477"
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
"path": ".mdkg/core/rule-2-context-pack-rules.md",
|
|
@@ -36,17 +36,17 @@
|
|
|
36
36
|
{
|
|
37
37
|
"path": ".mdkg/core/rule-3-cli-contract.md",
|
|
38
38
|
"category": "core",
|
|
39
|
-
"sha256": "
|
|
39
|
+
"sha256": "4ff22c60e3b4093492f00c8ddd738f7241192c7b1edbfdcbdc4ed09d95192aa6"
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
"path": ".mdkg/core/rule-4-repo-safety-and-ignores.md",
|
|
43
43
|
"category": "core",
|
|
44
|
-
"sha256": "
|
|
44
|
+
"sha256": "4edf0efa29ec470e96cad3e0abc7b8f44e2e718d7a9959f9cefc5e1db7e02b6c"
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
"path": ".mdkg/core/rule-5-release-and-versioning.md",
|
|
48
48
|
"category": "core",
|
|
49
|
-
"sha256": "
|
|
49
|
+
"sha256": "e97b00aa3ade29011f1f2b482042ec292f74aad8b1d72e2f8e691cdeca5d4f70"
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
"path": ".mdkg/core/rule-6-templates-and-schemas.md",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
{
|
|
62
62
|
"path": ".mdkg/README.md",
|
|
63
63
|
"category": "mdkg_doc",
|
|
64
|
-
"sha256": "
|
|
64
|
+
"sha256": "6ff69157098b5cc780f063ffadabff36b70e8567cec7fc406d7550385c467d0e"
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
"path": ".mdkg/skills/build-pack-and-execute-task/SKILL.md",
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
{
|
|
177
177
|
"path": "AGENT_START.md",
|
|
178
178
|
"category": "startup_doc",
|
|
179
|
-
"sha256": "
|
|
179
|
+
"sha256": "e5bd54a4321443216645b11fd5f27aff2e8f6c760f21dad12b6d0dbe634b0986"
|
|
180
180
|
},
|
|
181
181
|
{
|
|
182
182
|
"path": "AGENTS.md",
|
|
@@ -191,7 +191,7 @@
|
|
|
191
191
|
{
|
|
192
192
|
"path": "CLI_COMMAND_MATRIX.md",
|
|
193
193
|
"category": "startup_doc",
|
|
194
|
-
"sha256": "
|
|
194
|
+
"sha256": "6e347620120c93eb9f330d137087f9e6b3279c629fcc24b65af561bd3cf385a0"
|
|
195
195
|
},
|
|
196
196
|
{
|
|
197
197
|
"path": "llms.txt",
|
|
@@ -0,0 +1,44 @@
|
|
|
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.atomicWriteFile = atomicWriteFile;
|
|
7
|
+
exports.writeFileExclusive = writeFileExclusive;
|
|
8
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
function randomSuffix() {
|
|
12
|
+
return `${process.pid}-${Date.now()}-${crypto_1.default.randomBytes(6).toString("hex")}`;
|
|
13
|
+
}
|
|
14
|
+
function writeAndSync(filePath, data, flags) {
|
|
15
|
+
const handle = fs_1.default.openSync(filePath, flags);
|
|
16
|
+
try {
|
|
17
|
+
if (typeof data === "string") {
|
|
18
|
+
fs_1.default.writeFileSync(handle, data, "utf8");
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
fs_1.default.writeFileSync(handle, data);
|
|
22
|
+
}
|
|
23
|
+
fs_1.default.fsyncSync(handle);
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
fs_1.default.closeSync(handle);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function atomicWriteFile(filePath, data) {
|
|
30
|
+
fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
|
|
31
|
+
const tempPath = path_1.default.join(path_1.default.dirname(filePath), `.${path_1.default.basename(filePath)}.${randomSuffix()}.tmp`);
|
|
32
|
+
try {
|
|
33
|
+
writeAndSync(tempPath, data, "wx");
|
|
34
|
+
fs_1.default.renameSync(tempPath, filePath);
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
fs_1.default.rmSync(tempPath, { force: true });
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function writeFileExclusive(filePath, data) {
|
|
42
|
+
fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
|
|
43
|
+
writeAndSync(filePath, data, "wx");
|
|
44
|
+
}
|