mdkg 0.1.3 → 0.1.4
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 -1
- package/README.md +11 -10
- package/dist/cli.js +180 -86
- package/dist/commands/bundle.js +7 -7
- package/dist/commands/capability.js +118 -4
- package/dist/commands/doctor.js +15 -15
- package/dist/commands/index.js +1 -1
- package/dist/commands/list.js +1 -1
- package/dist/commands/next.js +1 -1
- package/dist/commands/node_card.js +1 -1
- package/dist/commands/pack.js +1 -1
- package/dist/commands/search.js +1 -1
- package/dist/commands/show.js +1 -1
- package/dist/commands/subgraph.js +312 -0
- package/dist/commands/task.js +1 -1
- package/dist/commands/upgrade.js +51 -4
- package/dist/commands/validate.js +6 -6
- package/dist/commands/work.js +1 -1
- package/dist/core/config.js +95 -39
- package/dist/graph/index_cache.js +12 -12
- package/dist/graph/indexer.js +1 -1
- package/dist/graph/reindex.js +6 -6
- package/dist/graph/sqlite_index.js +6 -6
- package/dist/graph/{bundle_imports.js → subgraphs.js} +214 -140
- package/dist/graph/visibility.js +3 -3
- package/dist/init/AGENT_START.md +2 -1
- package/dist/init/CLI_COMMAND_MATRIX.md +16 -7
- package/dist/init/README.md +9 -8
- package/dist/init/config.json +1 -1
- package/dist/init/core/rule-3-cli-contract.md +30 -23
- package/dist/init/core/rule-4-repo-safety-and-ignores.md +1 -1
- package/dist/init/init-manifest.json +8 -8
- package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +1 -1
- package/dist/util/argparse.js +2 -0
- package/package.json +7 -6
- package/dist/commands/bundle_import.js +0 -255
package/dist/init/config.json
CHANGED
|
@@ -69,9 +69,9 @@ Workspaces are registered in `.mdkg/config.json`.
|
|
|
69
69
|
|
|
70
70
|
Qualified IDs may be used as input:
|
|
71
71
|
- `<ws>:<id>` (example: `e2e:task-12`)
|
|
72
|
-
-
|
|
73
|
-
- `<
|
|
74
|
-
-
|
|
72
|
+
- Subgraph nodes use the same qualified form with the subgraph alias:
|
|
73
|
+
- `<subgraph-alias>:<id>` (example: `agent_image:work.generate-image`)
|
|
74
|
+
- subgraph nodes are read-only planning context and MUST NOT be selected by local mutation commands
|
|
75
75
|
|
|
76
76
|
If a user provides an unqualified ID and it is ambiguous globally:
|
|
77
77
|
- mdkg MUST error and suggest qualified IDs.
|
|
@@ -145,7 +145,7 @@ If a user provides an unqualified ID and it is ambiguous globally:
|
|
|
145
145
|
- rebuild global cache `.mdkg/index/global.json`
|
|
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
|
-
- rebuild
|
|
148
|
+
- rebuild subgraph projection cache `.mdkg/index/subgraphs.json` when subgraphs are configured
|
|
149
149
|
- rebuild SQLite access cache `.mdkg/index/mdkg.sqlite` when `index.backend` is `sqlite`
|
|
150
150
|
- tolerate `.mdkg/skills/<slug>/SKILLS.md` on read with warning
|
|
151
151
|
- fail validation if both `SKILL.md` and `SKILLS.md` exist in one skill directory
|
|
@@ -183,10 +183,10 @@ Common flags:
|
|
|
183
183
|
- `mdkg search "<query>" [--type <type>] [--status <status>] [--ws <alias>] [--tags <tag,tag,...>] [--tags-mode any|all] [--json|--xml|--toon|--md]`
|
|
184
184
|
- search SHOULD match on IDs, titles, tags, path tokens, and searchable frontmatter lists (`links`, `artifacts`, `refs`, `aliases`)
|
|
185
185
|
- `mdkg list [--type <type>] [--status <status>] [--ws <alias>] [--epic <id>] [--blocked] [--priority <n>] [--tags <tag,tag,...>] [--tags-mode any|all] [--json|--xml|--toon|--md]`
|
|
186
|
-
- enabled
|
|
187
|
-
-
|
|
188
|
-
- human output labels
|
|
189
|
-
- stale
|
|
186
|
+
- enabled subgraphs are included in `show`, `search`, `list`, `pack`, and `capability` reads by default:
|
|
187
|
+
- subgraph nodes surface `source.imported: true` and `source.subgraph_alias` in JSON output
|
|
188
|
+
- human output labels subgraph nodes as read-only and stale when applicable
|
|
189
|
+
- stale subgraphs warn during planning reads but remain usable
|
|
190
190
|
- skills are first-class under `mdkg skill ...` only:
|
|
191
191
|
- `mdkg skill list [--tags <tag,tag,...>] [--tags-mode any|all] [--json|--xml|--toon|--md]`
|
|
192
192
|
- `mdkg skill show <slug> [--meta] [--json|--xml|--toon|--md]`
|
|
@@ -197,7 +197,9 @@ Common flags:
|
|
|
197
197
|
- `mdkg capability list [--kind <skill|spec|work|core|design>] [--visibility <private|internal|public>] [--json]`
|
|
198
198
|
- `mdkg capability search "<query>" [--kind <kind>] [--visibility <level>] [--json]`
|
|
199
199
|
- `mdkg capability show <id-or-qid-or-slug> [--json]`
|
|
200
|
+
- `mdkg capability resolve [query] [--requires <capability>] [--fresh-only] [--json]`
|
|
200
201
|
- capability records are read-only derived cache entries, not source of truth
|
|
202
|
+
- `resolve` ranks local and subgraph capability candidates deterministically and degrades stale subgraphs unless `--fresh-only` is supplied
|
|
201
203
|
- normal task, epic, feat, bug, test, and checkpoint nodes are not capability records
|
|
202
204
|
- archives are first-class sidecar nodes under `mdkg archive ...`:
|
|
203
205
|
- `mdkg archive add <file> [--id <archive.id>] [--kind source|artifact] [--visibility private|internal|public] [--title <title>] [--refs <...>] [--relates <...>] [--json]`
|
|
@@ -215,21 +217,26 @@ Common flags:
|
|
|
215
217
|
- `mdkg bundle verify [bundle-path] [--json]`
|
|
216
218
|
- `mdkg bundle show <bundle-path> [--json]`
|
|
217
219
|
- `mdkg bundle list [--json]`
|
|
218
|
-
- `mdkg bundle import add <alias> <bundle-path> [--visibility private|internal|public] [--profile private|public] [--source-path <path>] [--source-repo <ref>] [--max-stale-seconds <seconds>] [--json]`
|
|
219
|
-
- `mdkg bundle import list [--json]`
|
|
220
|
-
- `mdkg bundle import rm <alias> [--json]`
|
|
221
|
-
- `mdkg bundle import enable <alias> [--json]`
|
|
222
|
-
- `mdkg bundle import disable <alias> [--json]`
|
|
223
|
-
- `mdkg bundle import verify [alias|--all] [--json]`
|
|
224
220
|
- bundles are explicit transport artifacts and are not rewritten by `mdkg index`
|
|
225
221
|
- default output is `.mdkg/bundles/<profile>/<workspace-or-all>.mdkg.zip`
|
|
226
222
|
- public bundles must fail closed when public records reference private graph or archive records
|
|
227
|
-
- public bundles must fail closed when public records reference private/internal
|
|
228
|
-
- bundle
|
|
229
|
-
- `bundle import verify` exits nonzero for stale, missing, corrupt, profile-mismatched, or duplicate-id imports
|
|
230
|
-
- public bundle creation must not re-export imported child graph content and must fail if public local nodes reference private/internal imports
|
|
231
|
-
- public/internal imports require `expected_profile: public`; private bundle profiles cannot be promoted through import visibility
|
|
223
|
+
- public bundles must fail closed when public records reference private/internal subgraph records
|
|
224
|
+
- public bundle creation must not re-export subgraph content and must fail if public local nodes reference private/internal subgraphs
|
|
232
225
|
- `mdkg pack --visibility public|internal|private` records explicit pack visibility and filters public/internal packs through the same fail-closed policy
|
|
226
|
+
- subgraph orchestration lives under `mdkg subgraph ...`:
|
|
227
|
+
- `mdkg subgraph add <alias> <bundle-path> [--visibility private|internal|public] [--profile private|public] [--source-path <path>] [--source-repo <ref>] [--max-stale-seconds <seconds>] [--json]`
|
|
228
|
+
- `mdkg subgraph list [--json]`
|
|
229
|
+
- `mdkg subgraph show <alias> [--json]`
|
|
230
|
+
- `mdkg subgraph rm <alias> [--json]`
|
|
231
|
+
- `mdkg subgraph enable <alias> [--json]`
|
|
232
|
+
- `mdkg subgraph disable <alias> [--json]`
|
|
233
|
+
- `mdkg subgraph verify [alias|--all] [--json]`
|
|
234
|
+
- `mdkg subgraph refresh [alias|--all] [--json]`
|
|
235
|
+
- subgraphs are read-only projected graph views; child repos remain owners of real mutations and commits
|
|
236
|
+
- `subgraph refresh` reloads configured bundle sources only and never builds or mutates child repos
|
|
237
|
+
- `subgraph verify` exits nonzero for stale, missing, corrupt, profile-mismatched, or duplicate-id subgraphs
|
|
238
|
+
- public/internal subgraphs require `expected_profile: public`; private bundle profiles cannot be promoted through subgraph visibility
|
|
239
|
+
- legacy `mdkg bundle import ...` exits with guidance to run `mdkg upgrade --apply` and use `mdkg subgraph ...`
|
|
233
240
|
- work lifecycle helpers live under `mdkg work ...`:
|
|
234
241
|
- `mdkg work contract new "<title>" --id <work.id> --agent-id <agent.id> --kind <kind> --inputs <...> --outputs <...> [--required-capabilities <...>] [--pricing-model <...>] [--json]`
|
|
235
242
|
- `mdkg work order new "<title>" --id <order.id> --work-id <work.id> --requester <ref> [--request-ref <ref>] [--input-refs <...>] [--requested-outputs <...>] [--json]`
|
|
@@ -240,7 +247,7 @@ Common flags:
|
|
|
240
247
|
- these commands mutate mdkg semantic mirror files only; production order, receipt, feedback, dispute, payment, ledger, marketplace inventory, fulfillment, and execution state remains canonical outside mdkg
|
|
241
248
|
- work mirrors must not store raw secrets, credentials, live payment state, ledger mutations, or canonical marketplace state
|
|
242
249
|
- `artifact://...` refs identify external/runtime-managed artifacts; `archive://...` refs identify committed mdkg archive sidecars
|
|
243
|
-
- update and artifact commands accept local ids or local qids;
|
|
250
|
+
- update and artifact commands accept local ids or local qids; subgraph qids are read-only and must be changed in their source workspace
|
|
244
251
|
- discovery/show output flags are mutually exclusive; text mode remains the default when none are supplied
|
|
245
252
|
|
|
246
253
|
### Task lifecycle mutation
|
|
@@ -251,7 +258,7 @@ Common flags:
|
|
|
251
258
|
- supports additive list mutation for `artifacts`, `links`, `refs`, `skills`, `tags`, and `blocked_by`
|
|
252
259
|
- supports scalar replacement for `status` and `priority`
|
|
253
260
|
- `--clear-blocked-by` resets blockers before optional re-add
|
|
254
|
-
-
|
|
261
|
+
- subgraph qids fail with an explicit read-only subgraph error
|
|
255
262
|
- `mdkg task done <id-or-qid> [--checkpoint "<title>"] [...]`
|
|
256
263
|
- supports `task`, `bug`, and `test` nodes only
|
|
257
264
|
- sets `status: done`
|
|
@@ -308,8 +315,8 @@ Common flags:
|
|
|
308
315
|
- `mdkg validate`
|
|
309
316
|
- strict frontmatter + graph integrity checks (exit code 2 on failure)
|
|
310
317
|
- validates optional node->skill references
|
|
311
|
-
- validates configured
|
|
312
|
-
- warns, but does not fail, on stale
|
|
318
|
+
- validates configured subgraphs and fails on missing/corrupt enabled bundles, malformed subgraph config, duplicate projected ids, and invalid subgraph refs
|
|
319
|
+
- warns, but does not fail, on stale subgraphs
|
|
313
320
|
- validates optional `.mdkg/work/events/events.jsonl` record shape when file exists
|
|
314
321
|
- warns when `.agents/skills/` or `.claude/skills/` drift from canonical `.mdkg/skills/`
|
|
315
322
|
- `mdkg format`
|
|
@@ -108,7 +108,7 @@ Explicit flags remain available and take precedence:
|
|
|
108
108
|
|
|
109
109
|
- `.mdkg/bundles/` stores explicit snapshot artifacts and is not ignored by default.
|
|
110
110
|
- Private bundles may include sensitive authored mdkg content and should stay in private repos.
|
|
111
|
-
- Public bundles must be created with `mdkg bundle create --profile public` so private graph, archive, and
|
|
111
|
+
- Public bundles must be created with `mdkg bundle create --profile public` so private graph, archive, and subgraph refs fail closed.
|
|
112
112
|
- Public-safe packs must be created with `mdkg pack <id> --visibility public`; internal-safe packs use `--visibility internal`. These filters do not redact Markdown body text.
|
|
113
113
|
- Bundle ZIPs must exclude `.mdkg/pack/`, existing `.mdkg/index/`, nested `.mdkg/bundles/`, and raw `.mdkg/archive/**/source/` files.
|
|
114
114
|
- Repos that track archive caches or bundles should refresh in this order before commit: `mdkg archive compress --all`, `mdkg archive verify --json`, `mdkg bundle create --profile private`, then bundle verify.
|
|
@@ -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.4",
|
|
5
5
|
"files": [
|
|
6
6
|
{
|
|
7
7
|
"path": ".mdkg/config.json",
|
|
8
8
|
"category": "config",
|
|
9
|
-
"sha256": "
|
|
9
|
+
"sha256": "5d2cf5e773353a59178fe86f8528413a00a73e2879fb1e020d01b49c0715a9ce"
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"path": ".mdkg/core/core.md",
|
|
@@ -36,12 +36,12 @@
|
|
|
36
36
|
{
|
|
37
37
|
"path": ".mdkg/core/rule-3-cli-contract.md",
|
|
38
38
|
"category": "core",
|
|
39
|
-
"sha256": "
|
|
39
|
+
"sha256": "4ec25c6936eefe0857a2946b47f0b65c9481bd65f1159fd5456bec6cce9de7ea"
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
"path": ".mdkg/core/rule-4-repo-safety-and-ignores.md",
|
|
43
43
|
"category": "core",
|
|
44
|
-
"sha256": "
|
|
44
|
+
"sha256": "2374e8684dfb32d24d560c83c99c33ff655cfdfe6811372dc3959ec93318a6db"
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
"path": ".mdkg/core/rule-5-release-and-versioning.md",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
{
|
|
62
62
|
"path": ".mdkg/README.md",
|
|
63
63
|
"category": "mdkg_doc",
|
|
64
|
-
"sha256": "
|
|
64
|
+
"sha256": "5ee6c141c0052e78671762e69c111f7f0a5d7f79b35c8f78c5b3b41901bd6959"
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
"path": ".mdkg/skills/build-pack-and-execute-task/SKILL.md",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
{
|
|
77
77
|
"path": ".mdkg/skills/verify-close-and-checkpoint/SKILL.md",
|
|
78
78
|
"category": "default_skill",
|
|
79
|
-
"sha256": "
|
|
79
|
+
"sha256": "67fc3d7e3c59b53add62306624391c1a416d0c66077729d79e1be0a303538f42"
|
|
80
80
|
},
|
|
81
81
|
{
|
|
82
82
|
"path": ".mdkg/templates/default/archive.md",
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
{
|
|
177
177
|
"path": "AGENT_START.md",
|
|
178
178
|
"category": "startup_doc",
|
|
179
|
-
"sha256": "
|
|
179
|
+
"sha256": "abde8671d34fcc7abd8570cf8c10b940cbe8f339edb369bd046045aa5626c0fc"
|
|
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": "14deecded057a99284d2dc147855d12dedffec049da51fed3b1ebdae3105d537"
|
|
195
195
|
},
|
|
196
196
|
{
|
|
197
197
|
"path": "llms.txt",
|
|
@@ -46,7 +46,7 @@ Use this local repo-only checklist before publishing mdkg:
|
|
|
46
46
|
|
|
47
47
|
1. Confirm package intent and version in `package.json`, `package-lock.json`, `README.md`, `CLI_COMMAND_MATRIX.md`, and `CHANGELOG.md`.
|
|
48
48
|
2. Use a clean npm cache: `export NPM_CONFIG_CACHE=/private/tmp/mdkg-npm-cache`.
|
|
49
|
-
3. Run `npm ci`, `npm run build`, `node scripts/assert-publish-ready.js`, `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:
|
|
49
|
+
3. Run `npm ci`, `npm run build`, `node scripts/assert-publish-ready.js`, `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`, and `npm run smoke:visibility`.
|
|
50
50
|
4. Run `npm pack --dry-run --json` and confirm the tarball includes `dist/cli.js`, compiled folders, `dist/init/`, release docs, and `scripts/postinstall.js`.
|
|
51
51
|
5. Confirm registry state with `npm view mdkg version --registry=https://registry.npmjs.org/`.
|
|
52
52
|
6. Publish only after the registry still shows the previous version and npm auth is known to have write access.
|
package/dist/util/argparse.js
CHANGED
|
@@ -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
|
+
"version": "0.1.4",
|
|
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,16 @@
|
|
|
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
|
|
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
25
|
"cli:snapshot": "npm run build && node scripts/cli_help_snapshot.js",
|
|
26
26
|
"cli:check": "npm run build && node scripts/cli_help_snapshot.js --check",
|
|
27
27
|
"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:
|
|
29
|
-
"postinstall": "node scripts/postinstall.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:subgraph && npm run smoke:visibility && npm run smoke:sqlite && npm run smoke:parallel && node scripts/assert-publish-ready.js",
|
|
29
|
+
"postinstall": "node scripts/postinstall.js",
|
|
30
|
+
"smoke:subgraph": "npm run build && node scripts/smoke-subgraph.js"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"@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
|
-
}
|