mdkg 0.0.8 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/CONTRIBUTING.md +124 -0
  3. package/README.md +49 -14
  4. package/dist/cli.js +113 -32
  5. package/dist/commands/checkpoint.js +19 -2
  6. package/dist/commands/event.js +12 -0
  7. package/dist/commands/init.js +4 -0
  8. package/dist/commands/init_manifest.js +131 -0
  9. package/dist/commands/new.js +57 -21
  10. package/dist/commands/pack.js +14 -0
  11. package/dist/commands/query_output.js +2 -0
  12. package/dist/commands/search.js +8 -0
  13. package/dist/commands/show.js +7 -0
  14. package/dist/commands/skill.js +80 -12
  15. package/dist/commands/task.js +42 -12
  16. package/dist/commands/upgrade.js +286 -0
  17. package/dist/commands/validate.js +31 -3
  18. package/dist/commands/workspace.js +105 -13
  19. package/dist/core/config.js +217 -22
  20. package/dist/core/migrate.js +39 -5
  21. package/dist/core/version.js +31 -0
  22. package/dist/core/workspace_path.js +41 -0
  23. package/dist/graph/agent_file_types.js +392 -0
  24. package/dist/graph/edges.js +13 -10
  25. package/dist/graph/frontmatter.js +33 -0
  26. package/dist/graph/indexer.js +1 -0
  27. package/dist/graph/node.js +43 -16
  28. package/dist/graph/skills_indexer.js +14 -1
  29. package/dist/graph/template_schema.js +13 -126
  30. package/dist/graph/validate_graph.js +302 -2
  31. package/dist/init/AGENT_START.md +14 -2
  32. package/dist/init/CLI_COMMAND_MATRIX.md +49 -1
  33. package/dist/init/README.md +14 -0
  34. package/dist/init/core/rule-6-templates-and-schemas.md +1 -3
  35. package/dist/init/init-manifest.json +197 -0
  36. package/dist/init/legacy/v0.0.9-init-manifest.json +197 -0
  37. package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +12 -11
  38. package/dist/init/templates/default/dispute.md +31 -0
  39. package/dist/init/templates/default/feedback.md +27 -0
  40. package/dist/init/templates/default/proposal.md +35 -0
  41. package/dist/init/templates/default/receipt.md +31 -0
  42. package/dist/init/templates/default/spec.md +43 -0
  43. package/dist/init/templates/default/work.md +44 -0
  44. package/dist/init/templates/default/work_order.md +32 -0
  45. package/dist/pack/export_json.js +3 -0
  46. package/dist/pack/export_md.js +9 -0
  47. package/dist/pack/export_xml.js +9 -0
  48. package/dist/pack/order.js +7 -0
  49. package/dist/pack/pack.js +1 -0
  50. package/dist/templates/loader.js +2 -2
  51. package/dist/util/argparse.js +2 -0
  52. package/dist/util/id.js +19 -0
  53. package/package.json +10 -2
  54. package/scripts/postinstall.js +89 -0
@@ -3,10 +3,23 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.validateConfigSchema = validateConfigSchema;
6
7
  exports.loadConfig = loadConfig;
7
8
  const fs_1 = __importDefault(require("fs"));
8
9
  const paths_1 = require("./paths");
9
10
  const migrate_1 = require("./migrate");
11
+ const workspace_path_1 = require("./workspace_path");
12
+ const WORKSPACE_ALIAS_RE = /^[a-z][a-z0-9_]*$/;
13
+ const PACK_EDGE_KEYS = new Set([
14
+ "parent",
15
+ "epic",
16
+ "relates",
17
+ "blocked_by",
18
+ "blocks",
19
+ "prev",
20
+ "next",
21
+ ]);
22
+ const NEXT_WORK_STRATEGIES = new Set(["chain_then_priority"]);
10
23
  function isObject(value) {
11
24
  return typeof value === "object" && value !== null && !Array.isArray(value);
12
25
  }
@@ -17,6 +30,17 @@ function requireString(value, path, errors) {
17
30
  }
18
31
  return value;
19
32
  }
33
+ function requireStringInSet(value, path, allowed, errors) {
34
+ const raw = requireString(value, path, errors);
35
+ if (raw === undefined) {
36
+ return undefined;
37
+ }
38
+ if (!allowed.has(raw)) {
39
+ errors.push(`${path} must be one of ${Array.from(allowed).join(", ")}`);
40
+ return undefined;
41
+ }
42
+ return raw;
43
+ }
20
44
  function requireBoolean(value, path, errors) {
21
45
  if (typeof value !== "boolean") {
22
46
  errors.push(`${path} must be a boolean`);
@@ -31,6 +55,39 @@ function requireNumber(value, path, errors) {
31
55
  }
32
56
  return value;
33
57
  }
58
+ function requireInteger(value, path, errors) {
59
+ const number = requireNumber(value, path, errors);
60
+ if (number === undefined) {
61
+ return undefined;
62
+ }
63
+ if (!Number.isInteger(number)) {
64
+ errors.push(`${path} must be an integer`);
65
+ return undefined;
66
+ }
67
+ return number;
68
+ }
69
+ function requireNonNegativeInteger(value, path, errors) {
70
+ const integer = requireInteger(value, path, errors);
71
+ if (integer === undefined) {
72
+ return undefined;
73
+ }
74
+ if (integer < 0) {
75
+ errors.push(`${path} must be a non-negative integer`);
76
+ return undefined;
77
+ }
78
+ return integer;
79
+ }
80
+ function requirePositiveInteger(value, path, errors) {
81
+ const integer = requireInteger(value, path, errors);
82
+ if (integer === undefined) {
83
+ return undefined;
84
+ }
85
+ if (integer <= 0) {
86
+ errors.push(`${path} must be a positive integer`);
87
+ return undefined;
88
+ }
89
+ return integer;
90
+ }
34
91
  function requireStringArray(value, path, errors) {
35
92
  if (!Array.isArray(value)) {
36
93
  errors.push(`${path} must be an array of strings`);
@@ -46,6 +103,53 @@ function requireStringArray(value, path, errors) {
46
103
  }
47
104
  return items;
48
105
  }
106
+ function requireLowercaseUniqueStringArray(value, path, errors, allowEmpty = false) {
107
+ const items = requireStringArray(value, path, errors);
108
+ if (items === undefined) {
109
+ return undefined;
110
+ }
111
+ if (items.length === 0) {
112
+ if (!allowEmpty) {
113
+ errors.push(`${path} must not be empty`);
114
+ }
115
+ return items;
116
+ }
117
+ const seen = new Set();
118
+ for (let i = 0; i < items.length; i += 1) {
119
+ const item = items[i];
120
+ if (item.trim().length === 0) {
121
+ errors.push(`${path}[${i}] must not be empty`);
122
+ continue;
123
+ }
124
+ if (item !== item.trim()) {
125
+ errors.push(`${path}[${i}] must not include surrounding whitespace`);
126
+ }
127
+ if (item !== item.toLowerCase()) {
128
+ errors.push(`${path}[${i}] must be lowercase`);
129
+ }
130
+ if (seen.has(item)) {
131
+ errors.push(`${path} must not contain duplicate value "${item}"`);
132
+ continue;
133
+ }
134
+ seen.add(item);
135
+ }
136
+ return items;
137
+ }
138
+ function requireKnownLowercaseUniqueStringArray(value, path, allowed, errors, allowEmpty = false) {
139
+ const items = requireLowercaseUniqueStringArray(value, path, errors, allowEmpty);
140
+ if (items === undefined) {
141
+ return undefined;
142
+ }
143
+ for (let i = 0; i < items.length; i += 1) {
144
+ if (items[i].trim().length === 0) {
145
+ continue;
146
+ }
147
+ if (!allowed.has(items[i])) {
148
+ errors.push(`${path}[${i}] must be one of ${Array.from(allowed).join(", ")}`);
149
+ }
150
+ }
151
+ return items;
152
+ }
49
153
  function requireObject(value, path, errors) {
50
154
  if (!isObject(value)) {
51
155
  errors.push(`${path} must be an object`);
@@ -53,6 +157,29 @@ function requireObject(value, path, errors) {
53
157
  }
54
158
  return value;
55
159
  }
160
+ function validateWorkspaceAlias(alias, errors) {
161
+ if (alias === "all") {
162
+ errors.push("workspaces.all alias is reserved");
163
+ return;
164
+ }
165
+ if (alias !== alias.toLowerCase() || !WORKSPACE_ALIAS_RE.test(alias)) {
166
+ errors.push(`workspaces.${alias} alias must be lowercase and use [a-z0-9_]`);
167
+ }
168
+ }
169
+ function requireContainedPath(value, path, errors) {
170
+ const raw = requireString(value, path, errors);
171
+ if (!raw) {
172
+ return undefined;
173
+ }
174
+ try {
175
+ return (0, workspace_path_1.normalizeContainedWorkspacePath)(raw, path);
176
+ }
177
+ catch (err) {
178
+ const message = err instanceof Error ? err.message : String(err);
179
+ errors.push(message);
180
+ return undefined;
181
+ }
182
+ }
56
183
  function validateConfigSchema(raw) {
57
184
  const errors = [];
58
185
  if (!isObject(raw)) {
@@ -70,50 +197,70 @@ function validateConfigSchema(raw) {
70
197
  ? {
71
198
  auto_reindex: requireBoolean(indexRaw.auto_reindex, "index.auto_reindex", errors),
72
199
  tolerant: requireBoolean(indexRaw.tolerant, "index.tolerant", errors),
73
- global_index_path: requireString(indexRaw.global_index_path, "index.global_index_path", errors),
200
+ global_index_path: requireContainedPath(indexRaw.global_index_path, "index.global_index_path", errors),
74
201
  }
75
202
  : undefined;
76
203
  const packLimitsRaw = packRaw ? requireObject(packRaw.limits, "pack.limits", errors) : undefined;
77
204
  const pack = packRaw
78
205
  ? {
79
- default_depth: requireNumber(packRaw.default_depth, "pack.default_depth", errors),
80
- default_edges: requireStringArray(packRaw.default_edges, "pack.default_edges", errors),
81
- verbose_core_list_path: requireString(packRaw.verbose_core_list_path, "pack.verbose_core_list_path", errors),
206
+ default_depth: requireNonNegativeInteger(packRaw.default_depth, "pack.default_depth", errors),
207
+ default_edges: requireKnownLowercaseUniqueStringArray(packRaw.default_edges, "pack.default_edges", PACK_EDGE_KEYS, errors, true),
208
+ verbose_core_list_path: requireContainedPath(packRaw.verbose_core_list_path, "pack.verbose_core_list_path", errors),
82
209
  limits: packLimitsRaw
83
210
  ? {
84
- max_nodes: requireNumber(packLimitsRaw.max_nodes, "pack.limits.max_nodes", errors),
85
- max_bytes: requireNumber(packLimitsRaw.max_bytes, "pack.limits.max_bytes", errors),
211
+ max_nodes: requirePositiveInteger(packLimitsRaw.max_nodes, "pack.limits.max_nodes", errors),
212
+ max_bytes: requirePositiveInteger(packLimitsRaw.max_bytes, "pack.limits.max_bytes", errors),
86
213
  }
87
214
  : undefined,
88
215
  }
89
216
  : undefined;
90
217
  const templates = templatesRaw
91
218
  ? {
92
- root_path: requireString(templatesRaw.root_path, "templates.root_path", errors),
219
+ root_path: requireContainedPath(templatesRaw.root_path, "templates.root_path", errors),
93
220
  default_set: requireString(templatesRaw.default_set, "templates.default_set", errors),
94
221
  workspace_overrides_enabled: requireBoolean(templatesRaw.workspace_overrides_enabled, "templates.workspace_overrides_enabled", errors),
95
222
  }
96
223
  : undefined;
97
224
  const workNextRaw = workRaw ? requireObject(workRaw.next, "work.next", errors) : undefined;
98
225
  const work = workRaw
99
- ? {
100
- status_enum: requireStringArray(workRaw.status_enum, "work.status_enum", errors),
101
- priority_min: requireNumber(workRaw.priority_min, "work.priority_min", errors),
102
- priority_max: requireNumber(workRaw.priority_max, "work.priority_max", errors),
103
- next: workNextRaw
104
- ? {
105
- strategy: requireString(workNextRaw.strategy, "work.next.strategy", errors),
106
- status_preference: requireStringArray(workNextRaw.status_preference, "work.next.status_preference", errors),
226
+ ? (() => {
227
+ const statusEnum = requireLowercaseUniqueStringArray(workRaw.status_enum, "work.status_enum", errors);
228
+ const priorityMin = requireInteger(workRaw.priority_min, "work.priority_min", errors);
229
+ const priorityMax = requireInteger(workRaw.priority_max, "work.priority_max", errors);
230
+ if (priorityMin !== undefined &&
231
+ priorityMax !== undefined &&
232
+ priorityMin > priorityMax) {
233
+ errors.push("work.priority_min must be less than or equal to work.priority_max");
234
+ }
235
+ const statusPreference = workNextRaw
236
+ ? requireLowercaseUniqueStringArray(workNextRaw.status_preference, "work.next.status_preference", errors)
237
+ : undefined;
238
+ if (statusEnum !== undefined && statusPreference !== undefined) {
239
+ const allowedStatuses = new Set(statusEnum);
240
+ for (let i = 0; i < statusPreference.length; i += 1) {
241
+ if (!allowedStatuses.has(statusPreference[i])) {
242
+ errors.push(`work.next.status_preference[${i}] must be listed in work.status_enum`);
243
+ }
107
244
  }
108
- : undefined,
109
- }
245
+ }
246
+ return {
247
+ status_enum: statusEnum,
248
+ priority_min: priorityMin,
249
+ priority_max: priorityMax,
250
+ next: workNextRaw
251
+ ? {
252
+ strategy: requireStringInSet(workNextRaw.strategy, "work.next.strategy", NEXT_WORK_STRATEGIES, errors),
253
+ status_preference: statusPreference,
254
+ }
255
+ : undefined,
256
+ };
257
+ })()
110
258
  : undefined;
111
259
  const workspaces = {};
260
+ const workspaceDocRootOwners = new Map();
112
261
  if (workspacesRaw) {
113
262
  for (const [alias, entry] of Object.entries(workspacesRaw)) {
114
- if (alias !== alias.toLowerCase()) {
115
- errors.push(`workspaces.${alias} alias must be lowercase`);
116
- }
263
+ validateWorkspaceAlias(alias, errors);
117
264
  const ws = requireObject(entry, `workspaces.${alias}`, errors);
118
265
  if (!ws) {
119
266
  continue;
@@ -122,14 +269,62 @@ function validateConfigSchema(raw) {
122
269
  const wsEnabled = requireBoolean(ws.enabled, `workspaces.${alias}.enabled`, errors);
123
270
  const wsMdkgDir = requireString(ws.mdkg_dir, `workspaces.${alias}.mdkg_dir`, errors);
124
271
  if (wsPath && wsEnabled !== undefined && wsMdkgDir) {
272
+ let normalizedPath;
273
+ let normalizedMdkgDir;
274
+ try {
275
+ normalizedPath = (0, workspace_path_1.normalizeContainedWorkspacePath)(wsPath, `workspaces.${alias}.path`);
276
+ }
277
+ catch (err) {
278
+ const message = err instanceof Error ? err.message : String(err);
279
+ errors.push(message);
280
+ }
281
+ try {
282
+ normalizedMdkgDir = (0, workspace_path_1.normalizeContainedWorkspacePath)(wsMdkgDir, `workspaces.${alias}.mdkg_dir`);
283
+ }
284
+ catch (err) {
285
+ const message = err instanceof Error ? err.message : String(err);
286
+ errors.push(message);
287
+ }
288
+ if (!normalizedPath || !normalizedMdkgDir) {
289
+ continue;
290
+ }
291
+ if (alias !== "root" && (0, workspace_path_1.isRootWorkspacePath)(normalizedPath)) {
292
+ errors.push(`workspaces.${alias}.path must not be "." for non-root workspaces`);
293
+ continue;
294
+ }
295
+ const docRootKey = (0, workspace_path_1.workspaceDocumentRootKey)(normalizedPath, normalizedMdkgDir);
296
+ const existingAlias = workspaceDocRootOwners.get(docRootKey);
297
+ if (existingAlias) {
298
+ errors.push(`workspaces.${alias} document root duplicates workspaces.${existingAlias}`);
299
+ continue;
300
+ }
301
+ workspaceDocRootOwners.set(docRootKey, alias);
125
302
  workspaces[alias] = {
126
- path: wsPath,
303
+ path: normalizedPath,
127
304
  enabled: wsEnabled,
128
- mdkg_dir: wsMdkgDir,
305
+ mdkg_dir: normalizedMdkgDir,
129
306
  };
130
307
  }
131
308
  }
132
309
  }
310
+ const rootWorkspace = workspaces.root;
311
+ if (root_required !== undefined && root_required !== true) {
312
+ errors.push("root_required must be true");
313
+ }
314
+ if (!rootWorkspace) {
315
+ errors.push("workspaces.root is required");
316
+ }
317
+ else {
318
+ if (rootWorkspace.path !== ".") {
319
+ errors.push('workspaces.root.path must be "."');
320
+ }
321
+ if (rootWorkspace.enabled !== true) {
322
+ errors.push("workspaces.root.enabled must be true");
323
+ }
324
+ if (rootWorkspace.mdkg_dir !== ".mdkg") {
325
+ errors.push('workspaces.root.mdkg_dir must be ".mdkg"');
326
+ }
327
+ }
133
328
  if (errors.length > 0) {
134
329
  throw new Error(`config validation failed:\n${errors.join("\n")}`);
135
330
  }
@@ -3,15 +3,49 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LATEST_SCHEMA_VERSION = void 0;
4
4
  exports.migrateConfig = migrateConfig;
5
5
  exports.LATEST_SCHEMA_VERSION = 1;
6
- const MIGRATIONS = {};
7
- function migrateConfig(raw) {
8
- if (typeof raw !== "object" || raw === null) {
9
- throw new Error("config must be a JSON object");
10
- }
6
+ const LEGACY_SCHEMA_VERSION = 0;
7
+ function isJsonObject(value) {
8
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9
+ }
10
+ function getSchemaVersion(raw) {
11
11
  const version = raw.schema_version;
12
+ if (version === undefined) {
13
+ return LEGACY_SCHEMA_VERSION;
14
+ }
12
15
  if (typeof version !== "number" || !Number.isInteger(version)) {
13
16
  throw new Error("config schema_version must be an integer");
14
17
  }
18
+ if (version < LEGACY_SCHEMA_VERSION) {
19
+ throw new Error("config schema_version must be non-negative");
20
+ }
21
+ return version;
22
+ }
23
+ function migrateLegacyConfig(input) {
24
+ if (!isJsonObject(input)) {
25
+ throw new Error("config must be a JSON object");
26
+ }
27
+ return {
28
+ ...input,
29
+ schema_version: 1,
30
+ workspaces: input.workspaces === undefined
31
+ ? {
32
+ root: {
33
+ path: ".",
34
+ enabled: true,
35
+ mdkg_dir: ".mdkg",
36
+ },
37
+ }
38
+ : input.workspaces,
39
+ };
40
+ }
41
+ const MIGRATIONS = {
42
+ [LEGACY_SCHEMA_VERSION]: migrateLegacyConfig,
43
+ };
44
+ function migrateConfig(raw) {
45
+ if (!isJsonObject(raw)) {
46
+ throw new Error("config must be a JSON object");
47
+ }
48
+ const version = getSchemaVersion(raw);
15
49
  if (version > exports.LATEST_SCHEMA_VERSION) {
16
50
  throw new Error(`config schema_version ${version} is newer than supported ${exports.LATEST_SCHEMA_VERSION}`);
17
51
  }
@@ -0,0 +1,31 @@
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.readPackageVersion = readPackageVersion;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ function readPackageVersion(startDir = __dirname) {
10
+ let current = path_1.default.resolve(startDir);
11
+ for (let i = 0; i < 5; i += 1) {
12
+ const packagePath = path_1.default.join(current, "package.json");
13
+ if (fs_1.default.existsSync(packagePath)) {
14
+ try {
15
+ const raw = JSON.parse(fs_1.default.readFileSync(packagePath, "utf8"));
16
+ if (raw.name === "mdkg" && typeof raw.version === "string" && raw.version.trim().length > 0) {
17
+ return raw.version;
18
+ }
19
+ }
20
+ catch {
21
+ return "unknown";
22
+ }
23
+ }
24
+ const parent = path_1.default.dirname(current);
25
+ if (parent === current) {
26
+ break;
27
+ }
28
+ current = parent;
29
+ }
30
+ return "unknown";
31
+ }
@@ -0,0 +1,41 @@
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.normalizeContainedWorkspacePath = normalizeContainedWorkspacePath;
7
+ exports.isRootWorkspacePath = isRootWorkspacePath;
8
+ exports.workspaceDocumentRootKey = workspaceDocumentRootKey;
9
+ const path_1 = __importDefault(require("path"));
10
+ function isAbsoluteOnSupportedPlatform(value) {
11
+ return path_1.default.isAbsolute(value) || path_1.default.posix.isAbsolute(value) || path_1.default.win32.isAbsolute(value);
12
+ }
13
+ function normalizeContainedWorkspacePath(value, label) {
14
+ const normalized = value.trim();
15
+ if (!normalized) {
16
+ throw new Error(`${label} cannot be empty`);
17
+ }
18
+ if (normalized.includes("\0")) {
19
+ throw new Error(`${label} cannot contain NUL bytes`);
20
+ }
21
+ if (isAbsoluteOnSupportedPlatform(normalized)) {
22
+ throw new Error(`${label} must be relative`);
23
+ }
24
+ if (normalized.split(/[\\/]+/).some((part) => part === "..")) {
25
+ throw new Error(`${label} cannot contain parent-directory components`);
26
+ }
27
+ return normalized;
28
+ }
29
+ function isRootWorkspacePath(value) {
30
+ const parts = value
31
+ .trim()
32
+ .split(/[\\/]+/)
33
+ .filter(Boolean);
34
+ return parts.length > 0 && parts.every((part) => part === ".");
35
+ }
36
+ function workspaceDocumentRootKey(workspacePath, mdkgDir) {
37
+ return [workspacePath, mdkgDir]
38
+ .flatMap((value) => value.trim().split(/[\\/]+/))
39
+ .filter((part) => part && part !== ".")
40
+ .join("/");
41
+ }