mdkg 0.1.3 → 0.1.5

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 (49) hide show
  1. package/CHANGELOG.md +45 -1
  2. package/README.md +34 -10
  3. package/dist/cli.js +354 -87
  4. package/dist/commands/bundle.js +7 -7
  5. package/dist/commands/capability.js +118 -4
  6. package/dist/commands/doctor.js +15 -15
  7. package/dist/commands/goal.js +548 -0
  8. package/dist/commands/index.js +1 -1
  9. package/dist/commands/init.js +1 -0
  10. package/dist/commands/list.js +1 -1
  11. package/dist/commands/new.js +7 -1
  12. package/dist/commands/next.js +1 -1
  13. package/dist/commands/node_card.js +1 -1
  14. package/dist/commands/pack.js +1 -1
  15. package/dist/commands/search.js +1 -1
  16. package/dist/commands/show.js +1 -1
  17. package/dist/commands/subgraph.js +312 -0
  18. package/dist/commands/task.js +1 -1
  19. package/dist/commands/upgrade.js +54 -7
  20. package/dist/commands/validate.js +39 -7
  21. package/dist/commands/work.js +1 -1
  22. package/dist/core/config.js +95 -39
  23. package/dist/graph/frontmatter.js +8 -0
  24. package/dist/graph/goal_scope.js +127 -0
  25. package/dist/graph/index_cache.js +12 -12
  26. package/dist/graph/indexer.js +1 -1
  27. package/dist/graph/node.js +80 -1
  28. package/dist/graph/reindex.js +6 -6
  29. package/dist/graph/sqlite_index.js +6 -6
  30. package/dist/graph/{bundle_imports.js → subgraphs.js} +214 -140
  31. package/dist/graph/validate_graph.js +41 -0
  32. package/dist/graph/visibility.js +3 -3
  33. package/dist/init/AGENT_START.md +17 -1
  34. package/dist/init/CLI_COMMAND_MATRIX.md +43 -7
  35. package/dist/init/README.md +9 -8
  36. package/dist/init/config.json +1 -1
  37. package/dist/init/core/rule-3-cli-contract.md +56 -23
  38. package/dist/init/core/rule-4-repo-safety-and-ignores.md +7 -2
  39. package/dist/init/core/rule-6-templates-and-schemas.md +10 -1
  40. package/dist/init/init-manifest.json +20 -10
  41. package/dist/init/skills/default/pursue-mdkg-goal/SKILL.md +68 -0
  42. package/dist/init/skills/default/select-work-and-ground-context/SKILL.md +9 -7
  43. package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +11 -10
  44. package/dist/init/templates/default/goal.md +91 -0
  45. package/dist/pack/order.js +2 -1
  46. package/dist/pack/pack.js +17 -0
  47. package/dist/util/argparse.js +2 -0
  48. package/package.json +8 -6
  49. package/dist/commands/bundle_import.js +0 -255
@@ -0,0 +1,91 @@
1
+ ---
2
+ id: {{id}}
3
+ type: goal
4
+ title: {{title}}
5
+ status: {{status}}
6
+ priority: {{priority}}
7
+ goal_state: {{goal_state}}
8
+ goal_condition: {{goal_condition}}
9
+ scope_refs: []
10
+ active_node: {{active_node}}
11
+ required_skills: []
12
+ required_checks: []
13
+ max_iterations: {{max_iterations}}
14
+ blocked_after_attempts: {{blocked_after_attempts}}
15
+ epic: {{epic}}
16
+ parent: {{parent}}
17
+ prev: {{prev}}
18
+ next: {{next}}
19
+ tags: []
20
+ owners: []
21
+ links: []
22
+ artifacts: []
23
+ relates: []
24
+ blocked_by: []
25
+ blocks: []
26
+ refs: []
27
+ aliases: []
28
+ skills: []
29
+ created: {{created}}
30
+ updated: {{updated}}
31
+ ---
32
+
33
+ # Objective
34
+
35
+ State the durable objective the agent should pursue.
36
+
37
+ # End Condition
38
+
39
+ Define the exact measurable condition that makes this goal achieved.
40
+
41
+ # Non-Goals
42
+
43
+ - Out-of-scope item
44
+
45
+ # Recursive Algorithm
46
+
47
+ 1. Inspect the current goal state, relevant graph nodes, and required skills.
48
+ 2. Create missing mdkg nodes only when evidence shows they are needed.
49
+ 3. Select one concrete child node and work it to completion.
50
+ 4. Run required checks and record evidence.
51
+ 5. Re-evaluate the end condition and continue, pause, or close.
52
+
53
+ # Required Skills
54
+
55
+ - Skill slug
56
+
57
+ # Required Checks
58
+
59
+ - Command or verification gate
60
+
61
+ # Acceptance Criteria
62
+
63
+ - Criterion
64
+
65
+ # Definition Of Done
66
+
67
+ - Goal condition is achieved.
68
+ - Required checks have evidence.
69
+ - Completion evidence is recorded in the goal.
70
+
71
+ # Stop Conditions
72
+
73
+ - Goal is blocked beyond the configured attempt limit.
74
+ - Required context or permissions are missing.
75
+ - Budget or safety constraints prevent continued work.
76
+
77
+ # Current State
78
+
79
+ Record the current active node and any relevant state summary.
80
+
81
+ # Iteration Log
82
+
83
+ - No iterations recorded yet.
84
+
85
+ # Skill Improvement Candidates
86
+
87
+ - None yet.
88
+
89
+ # Completion Evidence
90
+
91
+ - Pending.
@@ -16,13 +16,14 @@ const FALLBACK_TYPES = [
16
16
  "dispute",
17
17
  "proposal",
18
18
  "archive",
19
+ "goal",
19
20
  "epic",
20
21
  "feat",
21
22
  "task",
22
23
  "bug",
23
24
  "checkpoint",
24
25
  ];
25
- const WORK_TYPES = ["epic", "feat", "task", "bug", "checkpoint"];
26
+ const WORK_TYPES = ["goal", "epic", "feat", "task", "bug", "checkpoint"];
26
27
  function idNumber(id) {
27
28
  const match = id.match(/-(\d+)$/);
28
29
  if (!match) {
package/dist/pack/pack.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildPack = buildPack;
4
+ const goal_scope_1 = require("../graph/goal_scope");
4
5
  const node_body_1 = require("../graph/node_body");
5
6
  const qid_1 = require("../util/qid");
6
7
  const order_1 = require("./order");
@@ -153,6 +154,22 @@ function buildPack(options) {
153
154
  const warnings = [];
154
155
  const includeLatestCheckpoint = options.includeLatestCheckpoint ?? true;
155
156
  const { qids, depths } = collectNodes(options.index, options.rootQid, options.depth, options.edges);
157
+ const rootNode = options.index.nodes[options.rootQid];
158
+ if (rootNode?.type === "goal") {
159
+ const scoped = (0, goal_scope_1.collectGoalScope)(options.index, rootNode);
160
+ for (const qid of scoped.qids) {
161
+ qids.add(qid);
162
+ if (!depths.has(qid)) {
163
+ depths.set(qid, 1);
164
+ }
165
+ }
166
+ for (const missing of scoped.missingRefs) {
167
+ mergeWarnings(warnings, `goal scope ref missing: ${missing}`);
168
+ }
169
+ for (const invalid of scoped.invalidRefs) {
170
+ mergeWarnings(warnings, `goal scope ref unsupported: ${invalid}`);
171
+ }
172
+ }
156
173
  const workspace = checkpointWorkspaceFromQid(options.rootQid);
157
174
  const latestCheckpointHint = options.index.meta.latest_checkpoint_qid?.[workspace];
158
175
  const latestCheckpointResolved = includeLatestCheckpoint
@@ -83,6 +83,7 @@ const VALUE_FLAGS = new Set([
83
83
  "--source-path",
84
84
  "--source-repo",
85
85
  "--max-stale-seconds",
86
+ "--requires",
86
87
  ]);
87
88
  const BOOLEAN_FLAGS = new Set([
88
89
  "--tolerant",
@@ -116,6 +117,7 @@ const BOOLEAN_FLAGS = new Set([
116
117
  "--with-scripts",
117
118
  "--clear-blocked-by",
118
119
  "--all",
120
+ "--fresh-only",
119
121
  ]);
120
122
  const FLAG_ALIASES = {
121
123
  "--o": "--out",
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "mdkg",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Markdown Knowledge Graph",
5
5
  "license": "MIT",
6
6
  "bin": {
7
7
  "mdkg": "dist/cli.js"
8
8
  },
9
9
  "scripts": {
10
- "build": "tsc -p tsconfig.build.json && node scripts/add-shebang.js && node scripts/copy-init-assets.js",
11
- "build:test": "tsc -p tsconfig.test.json",
10
+ "build": "node scripts/clean-build-output.js && tsc -p tsconfig.build.json && node scripts/add-shebang.js && node scripts/copy-init-assets.js",
11
+ "build:test": "node scripts/clean-build-output.js tests && tsc -p tsconfig.test.json",
12
12
  "test": "npm run build && npm run build:test && node --test dist/tests/**/*.test.js",
13
13
  "test:coverage": "npm run build && npm run build:test && node --test --experimental-test-coverage dist/tests/**/*.test.js",
14
14
  "smoke:consumer": "npm run build && node scripts/smoke-consumer.js",
@@ -18,15 +18,17 @@
18
18
  "smoke:capabilities": "npm run build && node scripts/smoke-capabilities.js",
19
19
  "smoke:archive-work": "npm run build && node scripts/smoke-archive-work.js",
20
20
  "smoke:bundle": "npm run build && node scripts/smoke-bundle.js",
21
- "smoke:bundle-import": "npm run build && node scripts/smoke-bundle-import.js",
21
+ "smoke:bundle-import": "npm run smoke:subgraph",
22
22
  "smoke:visibility": "npm run build && node scripts/smoke-visibility.js",
23
23
  "smoke:sqlite": "npm run build && node scripts/smoke-sqlite.js",
24
24
  "smoke:parallel": "npm run build && node scripts/smoke-parallel.js",
25
+ "smoke:goal": "npm run build && node scripts/smoke-goal.js",
25
26
  "cli:snapshot": "npm run build && node scripts/cli_help_snapshot.js",
26
27
  "cli:check": "npm run build && node scripts/cli_help_snapshot.js --check",
27
28
  "prepack": "npm run build && node scripts/assert-publish-ready.js",
28
- "prepublishOnly": "npm run test && npm run cli:check && node dist/cli.js validate && npm run smoke:consumer && npm run smoke:matrix && npm run smoke:upgrade && npm run smoke:init && npm run smoke:capabilities && npm run smoke:archive-work && npm run smoke:bundle && npm run smoke:bundle-import && npm run smoke:visibility && npm run smoke:sqlite && npm run smoke:parallel && node scripts/assert-publish-ready.js",
29
- "postinstall": "node scripts/postinstall.js"
29
+ "prepublishOnly": "npm run test && npm run cli:check && node dist/cli.js validate && npm run smoke:consumer && npm run smoke:matrix && npm run smoke:upgrade && npm run smoke:init && npm run smoke:capabilities && npm run smoke:archive-work && npm run smoke:bundle && npm run smoke:subgraph && npm run smoke:visibility && npm run smoke:sqlite && npm run smoke:parallel && npm run smoke:goal && node scripts/assert-publish-ready.js",
30
+ "postinstall": "node scripts/postinstall.js",
31
+ "smoke:subgraph": "npm run build && node scripts/smoke-subgraph.js"
30
32
  },
31
33
  "devDependencies": {
32
34
  "@types/node": "^24.0.0",
@@ -1,255 +0,0 @@
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.runBundleImportAddCommand = runBundleImportAddCommand;
7
- exports.runBundleImportListCommand = runBundleImportListCommand;
8
- exports.runBundleImportRemoveCommand = runBundleImportRemoveCommand;
9
- exports.runBundleImportEnableCommand = runBundleImportEnableCommand;
10
- exports.runBundleImportDisableCommand = runBundleImportDisableCommand;
11
- exports.runBundleImportVerifyCommand = runBundleImportVerifyCommand;
12
- const fs_1 = __importDefault(require("fs"));
13
- const path_1 = __importDefault(require("path"));
14
- const config_1 = require("../core/config");
15
- const migrate_1 = require("../core/migrate");
16
- const workspace_path_1 = require("../core/workspace_path");
17
- const bundle_imports_1 = require("../graph/bundle_imports");
18
- const errors_1 = require("../util/errors");
19
- const atomic_1 = require("../util/atomic");
20
- const lock_1 = require("../util/lock");
21
- const ALIAS_RE = /^[a-z][a-z0-9_]*$/;
22
- function writeJson(value) {
23
- console.log(JSON.stringify(value, null, 2));
24
- }
25
- function normalizeAlias(alias) {
26
- if (alias === "all") {
27
- throw new errors_1.UsageError("bundle import alias cannot be 'all'");
28
- }
29
- if (alias !== alias.toLowerCase() || !ALIAS_RE.test(alias)) {
30
- throw new errors_1.UsageError("bundle import alias must be lowercase and use [a-z0-9_]");
31
- }
32
- return alias;
33
- }
34
- function normalizeVisibility(value) {
35
- const normalized = (value ?? "private").toLowerCase();
36
- if (normalized === "private" || normalized === "internal" || normalized === "public") {
37
- return normalized;
38
- }
39
- throw new errors_1.UsageError("--visibility must be private, internal, or public");
40
- }
41
- function normalizeProfile(value) {
42
- const normalized = (value ?? "private").toLowerCase();
43
- if (normalized === "private" || normalized === "public") {
44
- return normalized;
45
- }
46
- throw new errors_1.UsageError("--profile must be private or public");
47
- }
48
- function normalizeContained(value, label) {
49
- try {
50
- return (0, workspace_path_1.normalizeContainedWorkspacePath)(value, label);
51
- }
52
- catch (err) {
53
- throw new errors_1.UsageError(err instanceof Error ? err.message : String(err));
54
- }
55
- }
56
- function readRawConfig(root) {
57
- const configPath = path_1.default.join(root, ".mdkg", "config.json");
58
- if (!fs_1.default.existsSync(configPath)) {
59
- throw new errors_1.NotFoundError(`config not found at ${configPath}`);
60
- }
61
- let parsed;
62
- try {
63
- parsed = JSON.parse(fs_1.default.readFileSync(configPath, "utf8"));
64
- }
65
- catch (err) {
66
- throw new errors_1.UsageError(`failed to read config: ${err instanceof Error ? err.message : String(err)}`);
67
- }
68
- const migrated = (0, migrate_1.migrateConfig)(parsed).config;
69
- (0, config_1.validateConfigSchema)(migrated);
70
- if (typeof migrated !== "object" || migrated === null || Array.isArray(migrated)) {
71
- throw new errors_1.UsageError("config must be a JSON object");
72
- }
73
- return { configPath, raw: migrated };
74
- }
75
- function writeRawConfig(configPath, raw) {
76
- (0, atomic_1.atomicWriteFile)(configPath, `${JSON.stringify(raw, null, 2)}\n`);
77
- }
78
- function getImports(raw) {
79
- const imports = raw.bundle_imports;
80
- if (imports === undefined) {
81
- raw.bundle_imports = {};
82
- return raw.bundle_imports;
83
- }
84
- if (typeof imports !== "object" || imports === null || Array.isArray(imports)) {
85
- throw new errors_1.UsageError("config.bundle_imports must be an object");
86
- }
87
- return imports;
88
- }
89
- function receiptForHealth(action, health) {
90
- return {
91
- action,
92
- import: health,
93
- };
94
- }
95
- function healthByAlias(root, alias) {
96
- const config = (0, config_1.loadConfig)(root);
97
- const health = (0, bundle_imports_1.buildBundleImportsIndex)(root, config).index.imports.find((item) => item.alias === alias);
98
- if (!health) {
99
- throw new errors_1.NotFoundError(`bundle import not found: ${alias}`);
100
- }
101
- return health;
102
- }
103
- function withBundleImportLock(root, fn) {
104
- const config = (0, config_1.loadConfig)(root);
105
- return (0, lock_1.withMutationLock)(root, config.index.lock_timeout_ms, fn);
106
- }
107
- function runBundleImportAddCommandLocked(options) {
108
- const alias = normalizeAlias(options.alias);
109
- const bundlePath = normalizeContained(options.bundlePath, "bundle import path");
110
- const visibility = normalizeVisibility(options.visibility);
111
- const expected_profile = normalizeProfile(options.profile);
112
- if (visibility !== "private" && expected_profile !== "public") {
113
- throw new errors_1.UsageError("--profile public is required when --visibility is public or internal");
114
- }
115
- const source_path = options.sourcePath
116
- ? normalizeContained(options.sourcePath, "bundle import source path")
117
- : undefined;
118
- if (options.maxStaleSeconds !== undefined && (!Number.isInteger(options.maxStaleSeconds) || options.maxStaleSeconds <= 0)) {
119
- throw new errors_1.UsageError("--max-stale-seconds must be a positive integer");
120
- }
121
- const { configPath, raw } = readRawConfig(options.root);
122
- const imports = getImports(raw);
123
- if (imports[alias]) {
124
- throw new errors_1.UsageError(`bundle import already exists: ${alias}`);
125
- }
126
- const workspaces = raw.workspaces;
127
- if (workspaces && workspaces[alias]) {
128
- throw new errors_1.UsageError(`bundle import alias collides with workspace: ${alias}`);
129
- }
130
- imports[alias] = {
131
- path: bundlePath,
132
- enabled: true,
133
- visibility,
134
- expected_profile,
135
- ...(source_path ? { source_path } : {}),
136
- ...(options.sourceRepo ? { source_repo: options.sourceRepo } : {}),
137
- ...(options.maxStaleSeconds !== undefined ? { max_stale_seconds: options.maxStaleSeconds } : {}),
138
- };
139
- raw.bundle_imports = imports;
140
- const validated = (0, config_1.validateConfigSchema)(raw);
141
- const health = (0, bundle_imports_1.buildBundleImportsIndex)(options.root, validated).index.imports.find((item) => item.alias === alias);
142
- if (!health) {
143
- throw new errors_1.NotFoundError(`bundle import not found after validation: ${alias}`);
144
- }
145
- if (health.error_count > 0) {
146
- throw new errors_1.ValidationError(`bundle import ${alias} is invalid:\n${health.errors.join("\n")}`);
147
- }
148
- writeRawConfig(configPath, raw);
149
- const receipt = receiptForHealth("added", health);
150
- if (options.json) {
151
- writeJson(receipt);
152
- return;
153
- }
154
- console.log(`bundle import added: ${alias} (${bundlePath})`);
155
- if (health.warning_count > 0) {
156
- console.log(`warnings: ${health.warning_count}`);
157
- }
158
- }
159
- function runBundleImportAddCommand(options) {
160
- return withBundleImportLock(options.root, () => runBundleImportAddCommandLocked(options));
161
- }
162
- function runBundleImportListCommand(options) {
163
- const config = (0, config_1.loadConfig)(options.root);
164
- const imports = (0, bundle_imports_1.buildBundleImportsIndex)(options.root, config).index.imports;
165
- if (options.json) {
166
- writeJson({ action: "list", count: imports.length, imports });
167
- return;
168
- }
169
- if (imports.length === 0) {
170
- console.log("no bundle imports configured");
171
- return;
172
- }
173
- for (const item of imports) {
174
- const status = item.enabled ? item.error_count > 0 ? "invalid" : item.stale ? "stale" : "ok" : "disabled";
175
- console.log(`${item.alias} | ${status} | ${item.visibility} | ${item.path}`);
176
- }
177
- }
178
- function runBundleImportRemoveCommandLocked(options) {
179
- const alias = normalizeAlias(options.alias);
180
- const { configPath, raw } = readRawConfig(options.root);
181
- const imports = getImports(raw);
182
- const existing = imports[alias];
183
- if (!existing) {
184
- throw new errors_1.NotFoundError(`bundle import not found: ${alias}`);
185
- }
186
- delete imports[alias];
187
- raw.bundle_imports = imports;
188
- (0, config_1.validateConfigSchema)(raw);
189
- writeRawConfig(configPath, raw);
190
- const receipt = { action: "removed", import: { alias } };
191
- if (options.json) {
192
- writeJson(receipt);
193
- return;
194
- }
195
- console.log(`bundle import removed: ${alias}`);
196
- }
197
- function runBundleImportRemoveCommand(options) {
198
- return withBundleImportLock(options.root, () => runBundleImportRemoveCommandLocked(options));
199
- }
200
- function setBundleImportEnabledLocked(options, enabled) {
201
- const alias = normalizeAlias(options.alias);
202
- const { configPath, raw } = readRawConfig(options.root);
203
- const imports = getImports(raw);
204
- const existing = imports[alias];
205
- if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
206
- throw new errors_1.NotFoundError(`bundle import not found: ${alias}`);
207
- }
208
- imports[alias] = { ...existing, enabled };
209
- raw.bundle_imports = imports;
210
- (0, config_1.validateConfigSchema)(raw);
211
- writeRawConfig(configPath, raw);
212
- const health = healthByAlias(options.root, alias);
213
- const receipt = receiptForHealth(enabled ? "enabled" : "disabled", health);
214
- if (options.json) {
215
- writeJson(receipt);
216
- return;
217
- }
218
- console.log(`bundle import ${enabled ? "enabled" : "disabled"}: ${alias}`);
219
- }
220
- function runBundleImportEnableCommand(options) {
221
- withBundleImportLock(options.root, () => setBundleImportEnabledLocked(options, true));
222
- }
223
- function runBundleImportDisableCommand(options) {
224
- withBundleImportLock(options.root, () => setBundleImportEnabledLocked(options, false));
225
- }
226
- function runBundleImportVerifyCommand(options) {
227
- const config = (0, config_1.loadConfig)(options.root);
228
- const all = options.all || !options.alias;
229
- const imports = (0, bundle_imports_1.buildBundleImportsIndex)(options.root, config).index.imports.filter((item) => all ? true : item.alias === options.alias);
230
- if (!all && imports.length === 0) {
231
- throw new errors_1.NotFoundError(`bundle import not found: ${options.alias}`);
232
- }
233
- const ok = imports.every((item) => item.error_count === 0 && !item.stale);
234
- const receipt = { action: "verified", ok, count: imports.length, imports };
235
- if (options.json) {
236
- writeJson(receipt);
237
- }
238
- else if (ok) {
239
- console.log(`bundle imports verified: ${imports.length}`);
240
- }
241
- else {
242
- console.log(`bundle import verify failed: ${imports.length}`);
243
- for (const item of imports) {
244
- for (const warning of item.warnings) {
245
- console.log(`warning: ${item.alias}: ${warning}`);
246
- }
247
- for (const error of item.errors) {
248
- console.log(`error: ${item.alias}: ${error}`);
249
- }
250
- }
251
- }
252
- if (!ok) {
253
- throw new errors_1.ValidationError("bundle import verify failed");
254
- }
255
- }