mdkg 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +103 -15
- package/CLI_COMMAND_MATRIX.md +109 -2
- package/README.md +40 -2
- package/dist/cli.js +262 -2
- package/dist/command-contract.json +1902 -524
- package/dist/commands/archive.js +31 -14
- package/dist/commands/bundle.js +180 -44
- package/dist/commands/capability.js +1 -0
- package/dist/commands/checkpoint.js +6 -5
- package/dist/commands/event_support.js +16 -7
- package/dist/commands/fix.js +11 -8
- package/dist/commands/git.js +624 -0
- package/dist/commands/goal.js +23 -23
- package/dist/commands/graph.js +64 -10
- package/dist/commands/handoff.js +4 -2
- package/dist/commands/init.js +28 -23
- package/dist/commands/loop.js +1668 -0
- package/dist/commands/loop_descriptors.js +219 -0
- package/dist/commands/mcp.js +101 -11
- package/dist/commands/new.js +49 -3
- package/dist/commands/pack.js +14 -7
- package/dist/commands/search.js +10 -1
- package/dist/commands/skill.js +12 -9
- package/dist/commands/skill_mirror.js +39 -31
- package/dist/commands/subgraph.js +59 -26
- package/dist/commands/task.js +77 -4
- package/dist/commands/upgrade.js +13 -15
- package/dist/commands/validate.js +20 -7
- package/dist/commands/work.js +44 -26
- package/dist/commands/workspace.js +10 -4
- package/dist/core/config.js +45 -17
- package/dist/core/filesystem_authority.js +281 -0
- package/dist/core/project_db_events.js +4 -0
- package/dist/core/project_db_materializer.js +2 -0
- package/dist/core/project_db_migrations.js +238 -181
- package/dist/core/project_db_snapshot.js +64 -14
- package/dist/core/workspace_path.js +7 -0
- package/dist/graph/archive_integrity.js +1 -1
- package/dist/graph/capabilities_index_cache.js +4 -1
- package/dist/graph/frontmatter.js +38 -0
- package/dist/graph/goal_scope.js +4 -1
- package/dist/graph/index_cache.js +37 -7
- package/dist/graph/loop_bindings.js +39 -0
- package/dist/graph/node.js +209 -1
- package/dist/graph/node_body.js +52 -6
- package/dist/graph/skills_index_cache.js +41 -6
- package/dist/graph/skills_indexer.js +5 -2
- package/dist/graph/sqlite_index.js +118 -105
- package/dist/graph/subgraphs.js +142 -5
- package/dist/graph/validate_graph.js +109 -13
- package/dist/graph/workspace_files.js +39 -9
- package/dist/init/AGENT_START.md +16 -0
- package/dist/init/CLI_COMMAND_MATRIX.md +14 -0
- package/dist/init/config.json +7 -1
- package/dist/init/init-manifest.json +49 -4
- package/dist/init/skills/default/pursue-mdkg-loop/SKILL.md +150 -0
- package/dist/init/templates/default/loop.md +160 -0
- package/dist/init/templates/loops/backend-api-cli-bloat-audit.loop.md +117 -0
- package/dist/init/templates/loops/design-frontend-ux-audit.loop.md +117 -0
- package/dist/init/templates/loops/duplicate-code-and-linting-audit.loop.md +114 -0
- package/dist/init/templates/loops/security-audit.loop.md +140 -0
- package/dist/init/templates/loops/tech-stack-best-practices-audit.loop.md +116 -0
- package/dist/init/templates/loops/test-ci-skill-infrastructure-audit.loop.md +116 -0
- package/dist/init/templates/loops/user-story-audit-and-recommendations.loop.md +116 -0
- package/dist/pack/order.js +3 -1
- package/dist/pack/pack.js +139 -6
- package/dist/templates/loader.js +9 -3
- package/dist/util/argparse.js +5 -0
- package/dist/util/lock.js +10 -9
- package/dist/util/zip.js +163 -9
- package/package.json +8 -4
|
@@ -12,6 +12,7 @@ exports.scaffoldMirrorRoots = scaffoldMirrorRoots;
|
|
|
12
12
|
const fs_1 = __importDefault(require("fs"));
|
|
13
13
|
const path_1 = __importDefault(require("path"));
|
|
14
14
|
const config_1 = require("../core/config");
|
|
15
|
+
const filesystem_authority_1 = require("../core/filesystem_authority");
|
|
15
16
|
const skills_indexer_1 = require("../graph/skills_indexer");
|
|
16
17
|
const errors_1 = require("../util/errors");
|
|
17
18
|
const MANIFEST_FILE = ".mdkg-managed.json";
|
|
@@ -29,7 +30,9 @@ function resolveMirrorTargets(root, config) {
|
|
|
29
30
|
return configuredSkillMirrorTargets(config).map((configuredPath) => {
|
|
30
31
|
const skillsRoot = path_1.default.join(root, configuredPath);
|
|
31
32
|
return {
|
|
33
|
+
boundaryRoot: root,
|
|
32
34
|
configuredPath,
|
|
35
|
+
relativeSkillsRoot: configuredPath.split(/[\\/]/).join("/"),
|
|
33
36
|
rootDir: path_1.default.dirname(skillsRoot),
|
|
34
37
|
skillsRoot,
|
|
35
38
|
manifestPath: path_1.default.join(skillsRoot, MANIFEST_FILE),
|
|
@@ -37,17 +40,20 @@ function resolveMirrorTargets(root, config) {
|
|
|
37
40
|
});
|
|
38
41
|
}
|
|
39
42
|
function readManifest(target) {
|
|
40
|
-
|
|
43
|
+
const relativeManifest = `${target.relativeSkillsRoot}/${MANIFEST_FILE}`;
|
|
44
|
+
if (!(0, filesystem_authority_1.containedPathExists)({ root: target.boundaryRoot, relativePath: relativeManifest })) {
|
|
41
45
|
return new Set();
|
|
42
46
|
}
|
|
43
47
|
try {
|
|
44
|
-
const parsed = JSON.parse(
|
|
48
|
+
const parsed = JSON.parse((0, filesystem_authority_1.readContainedFile)({ root: target.boundaryRoot, relativePath: relativeManifest }));
|
|
45
49
|
if (!Array.isArray(parsed.managed_slugs)) {
|
|
46
50
|
return new Set();
|
|
47
51
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
const slugs = parsed.managed_slugs.map((value) => String(value).trim().toLowerCase());
|
|
53
|
+
if (slugs.some((slug) => !skills_indexer_1.SKILL_SLUG_RE.test(slug))) {
|
|
54
|
+
return new Set();
|
|
55
|
+
}
|
|
56
|
+
return new Set(slugs);
|
|
51
57
|
}
|
|
52
58
|
catch {
|
|
53
59
|
return new Set();
|
|
@@ -57,8 +63,8 @@ function writeManifest(target, managed) {
|
|
|
57
63
|
const payload = {
|
|
58
64
|
managed_slugs: Array.from(new Set(Array.from(managed).map((value) => value.toLowerCase()))).sort(),
|
|
59
65
|
};
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
(0, filesystem_authority_1.ensureContainedDirectory)({ root: target.boundaryRoot, relativePath: target.relativeSkillsRoot });
|
|
67
|
+
(0, filesystem_authority_1.atomicReplaceContainedFile)({ root: target.boundaryRoot, relativePath: `${target.relativeSkillsRoot}/${MANIFEST_FILE}` }, `${JSON.stringify(payload, null, 2)}\n`);
|
|
62
68
|
}
|
|
63
69
|
function shouldCreateMirrorRoots(root, config) {
|
|
64
70
|
if (MANAGED_ROOT_MARKERS.some((relPath) => fs_1.default.existsSync(path_1.default.join(root, relPath)))) {
|
|
@@ -250,30 +256,32 @@ function syncSkillMirrors(options) {
|
|
|
250
256
|
continue;
|
|
251
257
|
}
|
|
252
258
|
touchedTargets += 1;
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
259
|
+
(0, filesystem_authority_1.ensureContainedDirectory)({ root: options.root, relativePath: target.relativeSkillsRoot });
|
|
260
|
+
(0, filesystem_authority_1.withContainedTreeSink)({ root: options.root, relativePath: target.relativeSkillsRoot, operation: "replace" }, () => {
|
|
261
|
+
const managed = readManifest(target);
|
|
262
|
+
for (const source of sources) {
|
|
263
|
+
const destDir = path_1.default.join(target.skillsRoot, source.slug);
|
|
264
|
+
const exists = fs_1.default.existsSync(destDir);
|
|
265
|
+
if (exists && !managed.has(source.slug) && !force) {
|
|
266
|
+
throw new errors_1.UsageError(`${path_1.default.relative(options.root, destDir)} already exists and is not mdkg-managed; rerun \`mdkg skill sync --force\` to replace it`);
|
|
267
|
+
}
|
|
268
|
+
materializeSkillMirror(source, destDir);
|
|
269
|
+
managed.add(source.slug);
|
|
270
|
+
synced += 1;
|
|
260
271
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
fs_1.default.rmSync(destDir, { recursive: true, force: true });
|
|
272
|
-
pruned += 1;
|
|
272
|
+
for (const slug of Array.from(managed)) {
|
|
273
|
+
if (sources.some((source) => source.slug === slug)) {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
const destDir = path_1.default.join(target.skillsRoot, slug);
|
|
277
|
+
if (fs_1.default.existsSync(destDir)) {
|
|
278
|
+
fs_1.default.rmSync(destDir, { recursive: true, force: true });
|
|
279
|
+
pruned += 1;
|
|
280
|
+
}
|
|
281
|
+
managed.delete(slug);
|
|
273
282
|
}
|
|
274
|
-
managed
|
|
275
|
-
}
|
|
276
|
-
writeManifest(target, managed);
|
|
283
|
+
writeManifest(target, managed);
|
|
284
|
+
});
|
|
277
285
|
}
|
|
278
286
|
return { synced, pruned, targets: touchedTargets };
|
|
279
287
|
}
|
|
@@ -342,8 +350,8 @@ function auditSkillMirrors(root, config) {
|
|
|
342
350
|
}
|
|
343
351
|
function scaffoldMirrorRoots(root, config) {
|
|
344
352
|
for (const target of resolveMirrorTargets(root, config)) {
|
|
345
|
-
|
|
346
|
-
if (!
|
|
353
|
+
(0, filesystem_authority_1.ensureContainedDirectory)({ root, relativePath: target.relativeSkillsRoot });
|
|
354
|
+
if (!(0, filesystem_authority_1.containedPathExists)({ root, relativePath: `${target.relativeSkillsRoot}/${MANIFEST_FILE}` })) {
|
|
347
355
|
writeManifest(target, []);
|
|
348
356
|
}
|
|
349
357
|
}
|
|
@@ -19,6 +19,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
19
19
|
const path_1 = __importDefault(require("path"));
|
|
20
20
|
const child_process_1 = require("child_process");
|
|
21
21
|
const config_1 = require("../core/config");
|
|
22
|
+
const filesystem_authority_1 = require("../core/filesystem_authority");
|
|
22
23
|
const migrate_1 = require("../core/migrate");
|
|
23
24
|
const workspace_path_1 = require("../core/workspace_path");
|
|
24
25
|
const subgraphs_1 = require("../graph/subgraphs");
|
|
@@ -35,6 +36,9 @@ function normalizeAlias(alias) {
|
|
|
35
36
|
if (alias === "all") {
|
|
36
37
|
throw new errors_1.UsageError("subgraph alias cannot be 'all'");
|
|
37
38
|
}
|
|
39
|
+
if (alias === "__proto__" || alias === "constructor" || alias === "prototype") {
|
|
40
|
+
throw new errors_1.UsageError(`subgraph alias is reserved: ${alias}`);
|
|
41
|
+
}
|
|
38
42
|
if (alias !== alias.toLowerCase() || !ALIAS_RE.test(alias)) {
|
|
39
43
|
throw new errors_1.UsageError("subgraph alias must be lowercase and use [a-z0-9_]");
|
|
40
44
|
}
|
|
@@ -674,10 +678,12 @@ function inspectSourcePath(root, alias, subgraph, allowDirty) {
|
|
|
674
678
|
throw new errors_1.UsageError(`subgraph ${alias} is missing source_path`);
|
|
675
679
|
}
|
|
676
680
|
const sourcePath = normalizeContained(subgraph.source_path, `subgraphs.${alias}.source_path`);
|
|
677
|
-
const sourceRoot =
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
+
const sourceRoot = (0, filesystem_authority_1.withContainedPathSink)({ root, relativePath: sourcePath, operation: "read" }, ({ absolutePath }) => {
|
|
682
|
+
if (!fs_1.default.existsSync(absolutePath) || !fs_1.default.lstatSync(absolutePath).isDirectory()) {
|
|
683
|
+
throw new errors_1.NotFoundError(`subgraph ${alias} source_path does not exist: ${sourcePath}`);
|
|
684
|
+
}
|
|
685
|
+
return absolutePath;
|
|
686
|
+
});
|
|
681
687
|
const gitTop = gitRun(sourceRoot, ["rev-parse", "--show-toplevel"]);
|
|
682
688
|
const gitTopPath = gitTop.stdout.trim();
|
|
683
689
|
if (!gitTop.ok || !gitTopPath) {
|
|
@@ -774,13 +780,34 @@ function syncOneAlias(options) {
|
|
|
774
780
|
const planned = [];
|
|
775
781
|
for (const source of activeSources) {
|
|
776
782
|
const outputPath = path_1.default.resolve(options.root, source.path);
|
|
783
|
+
let oldBundleHash;
|
|
784
|
+
let oldZipSha256;
|
|
785
|
+
try {
|
|
786
|
+
(0, filesystem_authority_1.withContainedPathSink)({ root: options.root, relativePath: source.path, operation: "replace", createParents: true }, ({ absolutePath }) => {
|
|
787
|
+
oldBundleHash = existingBundleHash(absolutePath);
|
|
788
|
+
oldZipSha256 = fs_1.default.existsSync(absolutePath) ? (0, bundle_1.sha256Buffer)(fs_1.default.readFileSync(absolutePath)) : undefined;
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
catch (error) {
|
|
792
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
793
|
+
sources.push({
|
|
794
|
+
label: source.label,
|
|
795
|
+
path: source.path,
|
|
796
|
+
enabled: source.enabled,
|
|
797
|
+
expected_profile: source.expected_profile,
|
|
798
|
+
warnings: [],
|
|
799
|
+
errors: [message],
|
|
800
|
+
});
|
|
801
|
+
errors.push(`source ${source.path}: ${message}`);
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
777
804
|
const sourceReceipt = {
|
|
778
805
|
label: source.label,
|
|
779
806
|
path: source.path,
|
|
780
807
|
enabled: source.enabled,
|
|
781
808
|
expected_profile: source.expected_profile,
|
|
782
|
-
old_bundle_hash:
|
|
783
|
-
old_zip_sha256:
|
|
809
|
+
old_bundle_hash: oldBundleHash,
|
|
810
|
+
old_zip_sha256: oldZipSha256,
|
|
784
811
|
warnings: [],
|
|
785
812
|
errors: [],
|
|
786
813
|
};
|
|
@@ -821,7 +848,7 @@ function syncOneAlias(options) {
|
|
|
821
848
|
for (const item of planned) {
|
|
822
849
|
const sourceReceipt = sources.find((source) => source.path === item.source.path);
|
|
823
850
|
try {
|
|
824
|
-
(0,
|
|
851
|
+
(0, filesystem_authority_1.atomicReplaceContainedFile)({ root: options.root, relativePath: relativeToRoot(options.root, item.outputPath) }, item.zip);
|
|
825
852
|
const verify = (0, bundle_1.verifyBundle)(gitState.sourceRoot, item.outputPath);
|
|
826
853
|
sourceReceipt.verified = verify.ok;
|
|
827
854
|
if (!verify.ok) {
|
|
@@ -902,11 +929,11 @@ function safeZipEntryPath(entryName) {
|
|
|
902
929
|
}
|
|
903
930
|
return normalized;
|
|
904
931
|
}
|
|
905
|
-
function writeMaterializeGitignore(targetRoot) {
|
|
906
|
-
const
|
|
932
|
+
function writeMaterializeGitignore(root, targetRoot) {
|
|
933
|
+
const gitignoreRelative = relativeToRoot(root, path_1.default.join(targetRoot, ".gitignore"));
|
|
907
934
|
const required = ["*", "!.gitignore"];
|
|
908
|
-
const existing =
|
|
909
|
-
?
|
|
935
|
+
const existing = (0, filesystem_authority_1.containedPathExists)({ root, relativePath: gitignoreRelative })
|
|
936
|
+
? (0, filesystem_authority_1.readContainedFile)({ root, relativePath: gitignoreRelative }, "utf8").split(/\r?\n/)
|
|
910
937
|
: [];
|
|
911
938
|
const lines = existing.filter((line) => line.length > 0);
|
|
912
939
|
for (const line of required) {
|
|
@@ -914,13 +941,21 @@ function writeMaterializeGitignore(targetRoot) {
|
|
|
914
941
|
lines.push(line);
|
|
915
942
|
}
|
|
916
943
|
}
|
|
917
|
-
(0,
|
|
944
|
+
(0, filesystem_authority_1.atomicReplaceContainedFile)({ root, relativePath: gitignoreRelative }, `${lines.join("\n")}\n`);
|
|
918
945
|
}
|
|
919
946
|
function materializeOneAlias(options) {
|
|
920
947
|
const enabled = enabledSources(options.subgraph);
|
|
921
948
|
const errors = [];
|
|
922
949
|
const warnings = [];
|
|
923
950
|
const outputDir = path_1.default.join(options.targetRoot, options.alias);
|
|
951
|
+
const outputRelative = relativeToRoot(options.root, outputDir);
|
|
952
|
+
try {
|
|
953
|
+
(0, filesystem_authority_1.withContainedPathSink)({ root: options.root, relativePath: outputRelative, operation: "replace", createParents: true }, () => undefined);
|
|
954
|
+
}
|
|
955
|
+
catch (error) {
|
|
956
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
957
|
+
return { alias: options.alias, ok: false, output_path: outputRelative, warnings, errors };
|
|
958
|
+
}
|
|
924
959
|
if (enabled.length !== 1) {
|
|
925
960
|
errors.push(`materialize requires exactly one enabled source for ${options.alias}; found ${enabled.length}`);
|
|
926
961
|
return { alias: options.alias, ok: false, output_path: relativeToRoot(options.root, outputDir), warnings, errors };
|
|
@@ -943,15 +978,14 @@ function materializeOneAlias(options) {
|
|
|
943
978
|
}
|
|
944
979
|
}
|
|
945
980
|
const tempDir = path_1.default.join(options.targetRoot, `.${options.alias}.${process.pid}.${Date.now()}.tmp`);
|
|
946
|
-
|
|
981
|
+
const tempRelative = relativeToRoot(options.root, tempDir);
|
|
982
|
+
(0, filesystem_authority_1.removeContainedPath)({ root: options.root, relativePath: tempRelative, recursive: true, force: true });
|
|
947
983
|
try {
|
|
948
|
-
const parsed = (0, bundle_1.parseBundle)(
|
|
949
|
-
|
|
984
|
+
const parsed = (0, filesystem_authority_1.withContainedPathSink)({ root: options.root, relativePath: source.path, operation: "read" }, ({ absolutePath }) => (0, bundle_1.parseBundle)(absolutePath));
|
|
985
|
+
(0, filesystem_authority_1.ensureContainedDirectory)({ root: options.root, relativePath: tempRelative });
|
|
950
986
|
for (const [entryName, data] of parsed.entries.entries()) {
|
|
951
987
|
const safeName = safeZipEntryPath(entryName);
|
|
952
|
-
|
|
953
|
-
fs_1.default.mkdirSync(path_1.default.dirname(target), { recursive: true });
|
|
954
|
-
fs_1.default.writeFileSync(target, data);
|
|
988
|
+
(0, filesystem_authority_1.writeContainedFileExclusive)({ root: options.root, relativePath: `${tempRelative}/${safeName}` }, data);
|
|
955
989
|
}
|
|
956
990
|
const marker = {
|
|
957
991
|
tool: "mdkg",
|
|
@@ -959,19 +993,18 @@ function materializeOneAlias(options) {
|
|
|
959
993
|
alias: options.alias,
|
|
960
994
|
bundle_path: source.path,
|
|
961
995
|
bundle_hash: parsed.manifest.bundle_hash,
|
|
962
|
-
zip_sha256: (0, bundle_1.sha256Buffer)(
|
|
996
|
+
zip_sha256: (0, bundle_1.sha256Buffer)((0, filesystem_authority_1.readContainedFile)({ root: options.root, relativePath: source.path }, null)),
|
|
963
997
|
profile: parsed.manifest.profile,
|
|
964
998
|
source_repo: options.subgraph.source_repo ?? parsed.manifest.source.repo,
|
|
965
999
|
source_git_head: parsed.manifest.source.git_head,
|
|
966
1000
|
generated_at: new Date().toISOString(),
|
|
967
1001
|
mdkg_version: readPackageVersion(),
|
|
968
1002
|
};
|
|
969
|
-
|
|
1003
|
+
(0, filesystem_authority_1.writeContainedFileExclusive)({ root: options.root, relativePath: `${tempRelative}/.mdkg-materialized.json` }, `${JSON.stringify(marker, null, 2)}\n`);
|
|
970
1004
|
if (fs_1.default.existsSync(outputDir)) {
|
|
971
|
-
|
|
1005
|
+
(0, filesystem_authority_1.removeContainedPath)({ root: options.root, relativePath: outputRelative, recursive: true, force: true });
|
|
972
1006
|
}
|
|
973
|
-
|
|
974
|
-
fs_1.default.renameSync(tempDir, outputDir);
|
|
1007
|
+
(0, filesystem_authority_1.withContainedPathSink)({ root: options.root, relativePath: outputRelative, operation: "replace", createParents: true }, ({ absolutePath: safeOutput }) => (0, filesystem_authority_1.withContainedPathSink)({ root: options.root, relativePath: tempRelative, operation: "read" }, ({ absolutePath: safeTemp }) => fs_1.default.renameSync(safeTemp, safeOutput)));
|
|
975
1008
|
return {
|
|
976
1009
|
alias: options.alias,
|
|
977
1010
|
ok: true,
|
|
@@ -984,7 +1017,7 @@ function materializeOneAlias(options) {
|
|
|
984
1017
|
};
|
|
985
1018
|
}
|
|
986
1019
|
catch (err) {
|
|
987
|
-
|
|
1020
|
+
(0, filesystem_authority_1.removeContainedPath)({ root: options.root, relativePath: tempRelative, recursive: true, force: true });
|
|
988
1021
|
errors.push(err instanceof Error ? err.message : String(err));
|
|
989
1022
|
return { alias: options.alias, ok: false, output_path: relativeToRoot(options.root, outputDir), warnings, errors };
|
|
990
1023
|
}
|
|
@@ -1002,8 +1035,8 @@ function runSubgraphMaterializeCommand(options) {
|
|
|
1002
1035
|
clean: Boolean(options.clean),
|
|
1003
1036
|
}));
|
|
1004
1037
|
if (options.gitignore) {
|
|
1005
|
-
|
|
1006
|
-
writeMaterializeGitignore(targetRoot);
|
|
1038
|
+
(0, filesystem_authority_1.ensureContainedDirectory)({ root: options.root, relativePath: relativeToRoot(options.root, targetRoot) });
|
|
1039
|
+
writeMaterializeGitignore(options.root, targetRoot);
|
|
1007
1040
|
}
|
|
1008
1041
|
const ok = results.every((item) => item.ok === true);
|
|
1009
1042
|
const receipt = {
|
package/dist/commands/task.js
CHANGED
|
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.runTaskStartCommand = runTaskStartCommand;
|
|
7
7
|
exports.runTaskUpdateCommand = runTaskUpdateCommand;
|
|
8
8
|
exports.runTaskDoneCommand = runTaskDoneCommand;
|
|
9
|
-
const fs_1 = __importDefault(require("fs"));
|
|
10
9
|
const path_1 = __importDefault(require("path"));
|
|
11
10
|
const config_1 = require("../core/config");
|
|
11
|
+
const filesystem_authority_1 = require("../core/filesystem_authority");
|
|
12
12
|
const indexer_1 = require("../graph/indexer");
|
|
13
13
|
const index_cache_1 = require("../graph/index_cache");
|
|
14
14
|
const reindex_1 = require("../graph/reindex");
|
|
@@ -18,7 +18,6 @@ const date_1 = require("../util/date");
|
|
|
18
18
|
const errors_1 = require("../util/errors");
|
|
19
19
|
const qid_1 = require("../util/qid");
|
|
20
20
|
const id_1 = require("../util/id");
|
|
21
|
-
const atomic_1 = require("../util/atomic");
|
|
22
21
|
const lock_1 = require("../util/lock");
|
|
23
22
|
const event_support_1 = require("./event_support");
|
|
24
23
|
const checkpoint_1 = require("./checkpoint");
|
|
@@ -66,6 +65,75 @@ function normalizeIdList(raw, flag) {
|
|
|
66
65
|
function normalizeIdRefList(raw, flag) {
|
|
67
66
|
return parseCsvList(raw).map((value) => normalizeIdRef(value, flag));
|
|
68
67
|
}
|
|
68
|
+
function taskCompletionCheckpointBody(loaded, artifacts, note) {
|
|
69
|
+
const title = typeof loaded.frontmatter.title === "string" ? loaded.frontmatter.title : loaded.id;
|
|
70
|
+
const artifactLines = artifacts.length > 0
|
|
71
|
+
? artifacts.map((artifact) => `- ${artifact}`)
|
|
72
|
+
: ["- No artifacts were attached by the completion command."];
|
|
73
|
+
return [
|
|
74
|
+
"# Summary",
|
|
75
|
+
"",
|
|
76
|
+
note?.trim() || `${loaded.id} was marked done through the mdkg task lifecycle.`,
|
|
77
|
+
"",
|
|
78
|
+
"# Scope Covered",
|
|
79
|
+
"",
|
|
80
|
+
`- Completed node: ${loaded.id} (${title})`,
|
|
81
|
+
`- Node type: ${loaded.type}`,
|
|
82
|
+
"- Checkpoint source: `mdkg task done --checkpoint`",
|
|
83
|
+
"",
|
|
84
|
+
"## Changed Surfaces",
|
|
85
|
+
"",
|
|
86
|
+
`- Completed work node: ${loaded.id}`,
|
|
87
|
+
"- Attached artifacts are listed in checkpoint frontmatter and below.",
|
|
88
|
+
"",
|
|
89
|
+
"## Boundaries",
|
|
90
|
+
"",
|
|
91
|
+
"- in scope: structured task completion and checkpoint evidence",
|
|
92
|
+
"- out of scope: unrelated graph, runtime, provider, or release mutations",
|
|
93
|
+
"- raw secrets, raw prompts, raw payloads, and bulky execution traces excluded",
|
|
94
|
+
"",
|
|
95
|
+
"# Decisions Captured",
|
|
96
|
+
"",
|
|
97
|
+
"- See decision and approval refs on the completed node and checkpoint frontmatter.",
|
|
98
|
+
"",
|
|
99
|
+
"# Implementation Summary",
|
|
100
|
+
"",
|
|
101
|
+
`- Completion of ${loaded.id} was recorded through the structured task lifecycle.`,
|
|
102
|
+
"- Detailed implementation or test evidence remains on the completed node and linked refs.",
|
|
103
|
+
"",
|
|
104
|
+
"# Verification / Testing",
|
|
105
|
+
"",
|
|
106
|
+
"## Command Evidence",
|
|
107
|
+
"",
|
|
108
|
+
"- command: `mdkg task done --checkpoint`",
|
|
109
|
+
"- result: completed node and evidence checkpoint written",
|
|
110
|
+
"",
|
|
111
|
+
"## Pass / Fail Status",
|
|
112
|
+
"",
|
|
113
|
+
"- status: done",
|
|
114
|
+
"",
|
|
115
|
+
"## Known Warnings",
|
|
116
|
+
"",
|
|
117
|
+
"- warning: none recorded by the completion command",
|
|
118
|
+
"",
|
|
119
|
+
"# Known Issues / Follow-ups",
|
|
120
|
+
"",
|
|
121
|
+
"- Inspect the completed node and linked refs for any explicitly recorded residual work.",
|
|
122
|
+
"",
|
|
123
|
+
"## Follow-up Refs",
|
|
124
|
+
"",
|
|
125
|
+
"- task/test/goal refs: inspect the completed node and checkpoint frontmatter",
|
|
126
|
+
"",
|
|
127
|
+
"# Links / Artifacts",
|
|
128
|
+
"",
|
|
129
|
+
...artifactLines,
|
|
130
|
+
"",
|
|
131
|
+
"# Raw Content Safety",
|
|
132
|
+
"",
|
|
133
|
+
"- This checkpoint stores the completion summary and artifact refs, not raw prompts, secrets, payloads, or bulky traces.",
|
|
134
|
+
"",
|
|
135
|
+
].join("\n");
|
|
136
|
+
}
|
|
69
137
|
function normalizeSkillList(raw) {
|
|
70
138
|
return parseCsvList(raw).map((value) => {
|
|
71
139
|
const normalized = value.toLowerCase();
|
|
@@ -116,7 +184,7 @@ function loadMutableTaskNode(root, idOrQid, wsHint) {
|
|
|
116
184
|
throw new errors_1.UsageError(`mdkg task only supports feat, task, bug, test, and spike nodes; use markdown editing for ${node.type}:${node.id}`);
|
|
117
185
|
}
|
|
118
186
|
const filePath = path_1.default.resolve(root, node.path);
|
|
119
|
-
const content =
|
|
187
|
+
const content = (0, filesystem_authority_1.readContainedFile)({ root, relativePath: node.path });
|
|
120
188
|
const parsed = (0, frontmatter_1.parseFrontmatter)(content, filePath);
|
|
121
189
|
return {
|
|
122
190
|
config,
|
|
@@ -134,7 +202,7 @@ function writeNodeFile(root, filePath, frontmatter, body) {
|
|
|
134
202
|
const lines = (0, frontmatter_1.formatFrontmatter)(frontmatter, frontmatter_1.DEFAULT_FRONTMATTER_KEY_ORDER);
|
|
135
203
|
const frontmatterBlock = ["---", ...lines, "---"].join("\n");
|
|
136
204
|
const content = body.length > 0 ? `${frontmatterBlock}\n${body}` : frontmatterBlock;
|
|
137
|
-
(0,
|
|
205
|
+
(0, filesystem_authority_1.atomicReplaceContainedFile)({ root, relativePath: path_1.default.relative(root, filePath).split(path_1.default.sep).join("/") }, content);
|
|
138
206
|
}
|
|
139
207
|
function ensureStatusAllowed(config, statusRaw, flag = "--status") {
|
|
140
208
|
const normalized = statusRaw.trim().toLowerCase();
|
|
@@ -290,6 +358,7 @@ function runTaskDoneCommandLocked(options) {
|
|
|
290
358
|
now,
|
|
291
359
|
});
|
|
292
360
|
let checkpoint;
|
|
361
|
+
const checkpointBody = taskCompletionCheckpointBody(loaded, nextArtifacts, options.note);
|
|
293
362
|
if (options.checkpoint && options.json) {
|
|
294
363
|
checkpoint = (0, checkpoint_1.createCheckpoint)({
|
|
295
364
|
root: options.root,
|
|
@@ -298,6 +367,8 @@ function runTaskDoneCommandLocked(options) {
|
|
|
298
367
|
relates: loaded.id,
|
|
299
368
|
scope: loaded.id,
|
|
300
369
|
kind: options.checkpointKind,
|
|
370
|
+
status: "done",
|
|
371
|
+
body: checkpointBody,
|
|
301
372
|
runId: options.runId,
|
|
302
373
|
note: `checkpoint created from mdkg task done for ${loaded.id}`,
|
|
303
374
|
now,
|
|
@@ -311,6 +382,8 @@ function runTaskDoneCommandLocked(options) {
|
|
|
311
382
|
relates: loaded.id,
|
|
312
383
|
scope: loaded.id,
|
|
313
384
|
kind: options.checkpointKind,
|
|
385
|
+
status: "done",
|
|
386
|
+
body: checkpointBody,
|
|
314
387
|
runId: options.runId,
|
|
315
388
|
note: `checkpoint created from mdkg task done for ${loaded.id}`,
|
|
316
389
|
now,
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -9,6 +9,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const child_process_1 = require("child_process");
|
|
10
10
|
const migrate_1 = require("../core/migrate");
|
|
11
11
|
const config_1 = require("../core/config");
|
|
12
|
+
const filesystem_authority_1 = require("../core/filesystem_authority");
|
|
12
13
|
const paths_1 = require("../core/paths");
|
|
13
14
|
const version_1 = require("../core/version");
|
|
14
15
|
const project_db_1 = require("../core/project_db");
|
|
@@ -16,7 +17,6 @@ const errors_1 = require("../util/errors");
|
|
|
16
17
|
const frontmatter_1 = require("../graph/frontmatter");
|
|
17
18
|
const workspace_files_1 = require("../graph/workspace_files");
|
|
18
19
|
const agent_file_types_1 = require("../graph/agent_file_types");
|
|
19
|
-
const atomic_1 = require("../util/atomic");
|
|
20
20
|
const init_manifest_1 = require("./init_manifest");
|
|
21
21
|
const skill_support_1 = require("./skill_support");
|
|
22
22
|
const skill_mirror_1 = require("./skill_mirror");
|
|
@@ -58,13 +58,11 @@ function isAgentWorkspace(root, config) {
|
|
|
58
58
|
...(0, skill_mirror_1.configuredSkillMirrorTargets)(config).map((target) => path_1.default.join(root, target)),
|
|
59
59
|
].some((candidate) => fs_1.default.existsSync(candidate));
|
|
60
60
|
}
|
|
61
|
-
function copyFile(src, dest) {
|
|
62
|
-
|
|
63
|
-
fs_1.default.copyFileSync(src, dest);
|
|
61
|
+
function copyFile(root, src, dest) {
|
|
62
|
+
(0, filesystem_authority_1.atomicReplaceContainedFile)({ root, relativePath: repoRelativePath(root, dest) }, fs_1.default.readFileSync(src));
|
|
64
63
|
}
|
|
65
|
-
function writeFile(filePath, content) {
|
|
66
|
-
|
|
67
|
-
fs_1.default.writeFileSync(filePath, content, "utf8");
|
|
64
|
+
function writeFile(root, filePath, content) {
|
|
65
|
+
(0, filesystem_authority_1.atomicReplaceContainedFile)({ root, relativePath: repoRelativePath(root, filePath) }, content);
|
|
68
66
|
}
|
|
69
67
|
function repoRelativePath(root, filePath) {
|
|
70
68
|
return path_1.default.relative(root, filePath).split(path_1.default.sep).join("/");
|
|
@@ -142,7 +140,7 @@ function planSeedFile(options) {
|
|
|
142
140
|
reason: "missing managed init asset",
|
|
143
141
|
});
|
|
144
142
|
if (!options.dryRun) {
|
|
145
|
-
copyFile(srcPath, destPath);
|
|
143
|
+
copyFile(options.root, srcPath, destPath);
|
|
146
144
|
}
|
|
147
145
|
options.managedCurrentFiles.push(options.file);
|
|
148
146
|
return true;
|
|
@@ -165,7 +163,7 @@ function planSeedFile(options) {
|
|
|
165
163
|
reason: "matches a managed seed hash",
|
|
166
164
|
});
|
|
167
165
|
if (!options.dryRun) {
|
|
168
|
-
copyFile(srcPath, destPath);
|
|
166
|
+
copyFile(options.root, srcPath, destPath);
|
|
169
167
|
}
|
|
170
168
|
options.managedCurrentFiles.push(options.file);
|
|
171
169
|
return true;
|
|
@@ -295,7 +293,7 @@ function migrateConfigIfNeeded(root, dryRun, summary, changes) {
|
|
|
295
293
|
reason: reasons.join(" and "),
|
|
296
294
|
});
|
|
297
295
|
if (!dryRun) {
|
|
298
|
-
writeFile(cfgPath, `${JSON.stringify(customizationConfig.config, null, 2)}\n`);
|
|
296
|
+
writeFile(root, cfgPath, `${JSON.stringify(customizationConfig.config, null, 2)}\n`);
|
|
299
297
|
}
|
|
300
298
|
}
|
|
301
299
|
function sameStringArray(left, right) {
|
|
@@ -369,7 +367,7 @@ function migrateClosedGoalActiveNodes(root, dryRun, summary, changes) {
|
|
|
369
367
|
frontmatter.last_active_node = activeNode;
|
|
370
368
|
}
|
|
371
369
|
delete frontmatter.active_node;
|
|
372
|
-
writeFile(filePath, renderFrontmatterDocument(frontmatter, parsed.body.length > 0 ? parsed.body : ""));
|
|
370
|
+
writeFile(root, filePath, renderFrontmatterDocument(frontmatter, parsed.body.length > 0 ? parsed.body : ""));
|
|
373
371
|
}
|
|
374
372
|
}
|
|
375
373
|
}
|
|
@@ -420,8 +418,8 @@ function migrateLegacySpecManifests(root, dryRun, summary, changes) {
|
|
|
420
418
|
});
|
|
421
419
|
if (!dryRun) {
|
|
422
420
|
const frontmatter = { ...parsed.frontmatter, type: "manifest" };
|
|
423
|
-
(0,
|
|
424
|
-
fs_1.default.renameSync(
|
|
421
|
+
(0, filesystem_authority_1.atomicReplaceContainedFile)({ root, relativePath: repoRelativePath(root, filePath) }, renderFrontmatterDocument(frontmatter, parsed.body));
|
|
422
|
+
(0, filesystem_authority_1.withContainedPathSink)({ root, relativePath: repoRelativePath(root, targetPath), operation: "create", createParents: true }, ({ absolutePath: safeTarget }) => (0, filesystem_authority_1.withContainedPathSink)({ root, relativePath: repoRelativePath(root, filePath), operation: "delete" }, ({ absolutePath: safeSource }) => fs_1.default.renameSync(safeSource, safeTarget)));
|
|
425
423
|
}
|
|
426
424
|
}
|
|
427
425
|
}
|
|
@@ -487,7 +485,7 @@ function ensureAgentRuntimeFiles(root, dryRun, summary, changes) {
|
|
|
487
485
|
reason: "agent workspace is missing event log",
|
|
488
486
|
});
|
|
489
487
|
if (!dryRun) {
|
|
490
|
-
writeFile(eventsPath, seededInitEvent(new Date().toISOString()));
|
|
488
|
+
writeFile(root, eventsPath, seededInitEvent(new Date().toISOString()));
|
|
491
489
|
}
|
|
492
490
|
}
|
|
493
491
|
else {
|
|
@@ -512,7 +510,7 @@ function ensureArchiveIgnorePolicy(root, dryRun, summary, changes) {
|
|
|
512
510
|
});
|
|
513
511
|
if (!dryRun) {
|
|
514
512
|
const suffix = raw.length === 0 || raw.endsWith("\n") ? "" : "\n";
|
|
515
|
-
writeFile(ignorePath, `${raw}${suffix}${additions.join("\n")}\n`);
|
|
513
|
+
writeFile(root, ignorePath, `${raw}${suffix}${additions.join("\n")}\n`);
|
|
516
514
|
}
|
|
517
515
|
}
|
|
518
516
|
function isWritableChange(change) {
|
|
@@ -208,20 +208,33 @@ function collectContractProfileErrors(qid, node, profile) {
|
|
|
208
208
|
return errors;
|
|
209
209
|
}
|
|
210
210
|
function collectChangedPaths(root) {
|
|
211
|
-
const result = (0, child_process_1.spawnSync)("git", ["-C", root, "status", "--porcelain", "--", ".mdkg"], {
|
|
211
|
+
const result = (0, child_process_1.spawnSync)("git", ["-C", root, "status", "--porcelain=v1", "-z", "--untracked-files=all", "--", ".mdkg"], {
|
|
212
212
|
encoding: "utf8",
|
|
213
213
|
});
|
|
214
214
|
if (result.status !== 0) {
|
|
215
|
-
|
|
215
|
+
const detail = result.stderr.trim();
|
|
216
|
+
throw new errors_1.ValidationError(`changed-only validation could not enumerate Git paths${detail ? `: ${detail}` : ""}`);
|
|
216
217
|
}
|
|
217
218
|
const changed = new Set();
|
|
218
|
-
|
|
219
|
-
|
|
219
|
+
const records = result.stdout.split("\0");
|
|
220
|
+
for (let index = 0; index < records.length; index += 1) {
|
|
221
|
+
const record = records[index];
|
|
222
|
+
if (!record) {
|
|
220
223
|
continue;
|
|
221
224
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
+
if (record.length < 4 || record[2] !== " ") {
|
|
226
|
+
throw new errors_1.ValidationError("changed-only validation received malformed Git status output");
|
|
227
|
+
}
|
|
228
|
+
const status = record.slice(0, 2);
|
|
229
|
+
changed.add(record.slice(3).replace(/\\/g, "/"));
|
|
230
|
+
if (status.includes("R") || status.includes("C")) {
|
|
231
|
+
const sourcePath = records[index + 1];
|
|
232
|
+
if (!sourcePath) {
|
|
233
|
+
throw new errors_1.ValidationError("changed-only validation received an incomplete Git rename record");
|
|
234
|
+
}
|
|
235
|
+
changed.add(sourcePath.replace(/\\/g, "/"));
|
|
236
|
+
index += 1;
|
|
237
|
+
}
|
|
225
238
|
}
|
|
226
239
|
return changed;
|
|
227
240
|
}
|