dld-kit 1.0.0-rc.3 → 1.0.0-rc.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/README.md +109 -346
- package/dist/dld.mjs +154 -52
- package/package.json +1 -1
- package/skills/dld-adjust/SKILL.md +1 -1
- package/skills/dld-audit/SKILL.md +1 -1
- package/skills/dld-audit-auto/SKILL.md +1 -1
- package/skills/dld-common/SKILL.md +1 -1
- package/skills/dld-common/scripts/dld.mjs +154 -52
- package/skills/dld-decide/SKILL.md +1 -1
- package/skills/dld-implement/SKILL.md +1 -1
- package/skills/dld-init/SKILL.md +1 -1
- package/skills/dld-lookup/SKILL.md +1 -1
- package/skills/dld-plan/SKILL.md +1 -1
- package/skills/dld-reindex/SKILL.md +70 -23
- package/skills/dld-retrofit/SKILL.md +1 -1
- package/skills/dld-snapshot/SKILL.md +1 -1
- package/skills/dld-status/SKILL.md +1 -1
- package/templates/rules/dld-workflow.md +1 -1
- package/templates/skills/dld-reindex/SKILL.md +69 -22
|
@@ -7372,7 +7372,7 @@ import { createInterface } from "node:readline/promises";
|
|
|
7372
7372
|
import { fileURLToPath } from "node:url";
|
|
7373
7373
|
|
|
7374
7374
|
// package.json
|
|
7375
|
-
var version = "1.0.0-rc.
|
|
7375
|
+
var version = "1.0.0-rc.4";
|
|
7376
7376
|
|
|
7377
7377
|
// src/core/errors.ts
|
|
7378
7378
|
var EXIT_ERROR = 1;
|
|
@@ -7902,11 +7902,11 @@ function detectSnapshotChanges(ctx, project) {
|
|
|
7902
7902
|
if ((stored === void 0 || stored === "unknown") && lastRun !== void 0) {
|
|
7903
7903
|
stored = gitOrEmpty(git, "log", `--until=${lastRun}`, "--format=%h", "-1").trim() || void 0;
|
|
7904
7904
|
}
|
|
7905
|
-
const
|
|
7905
|
+
const commit2 = resolveStateCommit(git, stored);
|
|
7906
7906
|
let modifiedDecisions = [];
|
|
7907
7907
|
let commitRange = "";
|
|
7908
|
-
if (
|
|
7909
|
-
commitRange = `${
|
|
7908
|
+
if (commit2 !== void 0 && commit2 !== shortHead(ctx, paths.root)) {
|
|
7909
|
+
commitRange = `${commit2}..HEAD`;
|
|
7910
7910
|
const numbers = nulSeparated(
|
|
7911
7911
|
gitOrEmpty(git, "diff", "-z", "--name-only", commitRange, "--", recordsPathspec(paths))
|
|
7912
7912
|
).map((path) => basename3(path)).filter((name) => RECORD_FILE.test(name)).map((name) => recordNumber(name)).filter((number) => number <= includedNumber);
|
|
@@ -7927,17 +7927,17 @@ function formatSnapshotChanges(changes) {
|
|
|
7927
7927
|
function updateSnapshotState(ctx, project, customArtifacts) {
|
|
7928
7928
|
const { paths } = project;
|
|
7929
7929
|
const timestamp = formatTimestamp(ctx.now());
|
|
7930
|
-
const
|
|
7930
|
+
const commit2 = shortHead(ctx, paths.root);
|
|
7931
7931
|
const highest = readRecords(ctx, project).filter((record) => record.accepted).reduce((max, record) => Math.max(max, record.number), 0);
|
|
7932
7932
|
const artifacts = /* @__PURE__ */ Object.create(null);
|
|
7933
7933
|
for (const name of [...BUILT_IN_ARTIFACTS, ...customArtifacts]) artifacts[name] = timestamp;
|
|
7934
7934
|
writeStateSection(ctx, paths, "snapshot", {
|
|
7935
7935
|
last_run: timestamp,
|
|
7936
|
-
commit_hash:
|
|
7936
|
+
commit_hash: commit2,
|
|
7937
7937
|
decisions_included: String(highest),
|
|
7938
7938
|
artifacts
|
|
7939
7939
|
});
|
|
7940
|
-
return { timestamp, commit, highest };
|
|
7940
|
+
return { timestamp, commit: commit2, highest };
|
|
7941
7941
|
}
|
|
7942
7942
|
|
|
7943
7943
|
// src/cli/commands/collect-active-decisions.ts
|
|
@@ -7962,8 +7962,9 @@ import { basename as basename4 } from "node:path";
|
|
|
7962
7962
|
// src/core/open-prs.ts
|
|
7963
7963
|
var GITHUB_REMOTE = /github\.com[:/]/;
|
|
7964
7964
|
var PR_LIMIT = "100";
|
|
7965
|
+
var COMMIT_ID = /^[0-9a-f]{40}([0-9a-f]{24})?$/;
|
|
7965
7966
|
function openPrIds(ctx, paths, base) {
|
|
7966
|
-
const skipped = (reason) => ({
|
|
7967
|
+
const skipped = (reason) => ({ claims: [], skipped: reason });
|
|
7967
7968
|
try {
|
|
7968
7969
|
ctx.gh(["--version"]);
|
|
7969
7970
|
} catch (error) {
|
|
@@ -7990,7 +7991,7 @@ function openPrIds(ctx, paths, base) {
|
|
|
7990
7991
|
"open",
|
|
7991
7992
|
`--base=${prBase}`,
|
|
7992
7993
|
"--json",
|
|
7993
|
-
"files,headRefName,isCrossRepository",
|
|
7994
|
+
"files,headRefName,headRefOid,isCrossRepository",
|
|
7994
7995
|
"--limit",
|
|
7995
7996
|
PR_LIMIT
|
|
7996
7997
|
]);
|
|
@@ -8004,14 +8005,15 @@ function openPrIds(ctx, paths, base) {
|
|
|
8004
8005
|
if (prs === void 0) return skipped("gh pr list returned unexpected output");
|
|
8005
8006
|
const current = gitOrEmpty(git, "rev-parse", "--abbrev-ref", "HEAD").trim();
|
|
8006
8007
|
const prefix = `${recordsPathspec(paths)}/`;
|
|
8007
|
-
const
|
|
8008
|
+
const claims = [];
|
|
8008
8009
|
for (const pr of prs) {
|
|
8009
8010
|
if (!pr.isCrossRepository && pr.headRefName === current) continue;
|
|
8010
8011
|
for (const path of pr.files) {
|
|
8011
|
-
if (path.startsWith(prefix))
|
|
8012
|
+
if (!path.startsWith(prefix)) continue;
|
|
8013
|
+
for (const id of path.match(DECISION_MENTION) ?? []) claims.push({ id, head: pr.headRefOid });
|
|
8012
8014
|
}
|
|
8013
8015
|
}
|
|
8014
|
-
return {
|
|
8016
|
+
return { claims };
|
|
8015
8017
|
}
|
|
8016
8018
|
function parsePrList(output) {
|
|
8017
8019
|
let value;
|
|
@@ -8027,6 +8029,7 @@ function parsePrList(output) {
|
|
|
8027
8029
|
const files = Array.isArray(item.files) ? item.files : [];
|
|
8028
8030
|
prs.push({
|
|
8029
8031
|
headRefName: typeof item.headRefName === "string" ? item.headRefName : void 0,
|
|
8032
|
+
headRefOid: typeof item.headRefOid === "string" && COMMIT_ID.test(item.headRefOid) ? item.headRefOid : void 0,
|
|
8030
8033
|
isCrossRepository: item.isCrossRepository === true,
|
|
8031
8034
|
files: files.flatMap(
|
|
8032
8035
|
(file) => isObject(file) && typeof file.path === "string" ? [file.path] : []
|
|
@@ -8095,24 +8098,56 @@ function takenIds(ctx, paths, base) {
|
|
|
8095
8098
|
gitOrEmpty(git, "ls-tree", "-r", "-z", "--name-only", base, "--", recordsPathspec(paths))
|
|
8096
8099
|
).flatMap((path) => path.match(DECISION_MENTION) ?? []);
|
|
8097
8100
|
const scan = openPrIds(ctx, paths, base);
|
|
8098
|
-
const ids = [.../* @__PURE__ */ new Set([...onBase, ...scan.
|
|
8099
|
-
|
|
8101
|
+
const ids = [.../* @__PURE__ */ new Set([...onBase, ...scan.claims.map(({ id }) => id)])].sort(compareIds);
|
|
8102
|
+
const taken = { ids, onBase: new Set(onBase), claims: scan.claims };
|
|
8103
|
+
return scan.skipped === void 0 ? taken : { ...taken, skipped: scan.skipped };
|
|
8104
|
+
}
|
|
8105
|
+
function isAncestor(git, ancestor, commit2) {
|
|
8106
|
+
try {
|
|
8107
|
+
git("merge-base", "--is-ancestor", ancestor, commit2);
|
|
8108
|
+
return true;
|
|
8109
|
+
} catch (error) {
|
|
8110
|
+
if (error instanceof GitCommandError) return false;
|
|
8111
|
+
throw error;
|
|
8112
|
+
}
|
|
8113
|
+
}
|
|
8114
|
+
function collides(git, base, { path, id }, taken) {
|
|
8115
|
+
if (taken.onBase.has(id)) return true;
|
|
8116
|
+
const claims = taken.claims.filter((claim) => claim.id === id);
|
|
8117
|
+
if (claims.length === 0) return false;
|
|
8118
|
+
const added = git(
|
|
8119
|
+
"--literal-pathspecs",
|
|
8120
|
+
"log",
|
|
8121
|
+
// log.follow or log.showSignature in the user's config would change the output.
|
|
8122
|
+
"--no-follow",
|
|
8123
|
+
"--no-show-signature",
|
|
8124
|
+
"-1",
|
|
8125
|
+
"--format=%H",
|
|
8126
|
+
"--diff-filter=A",
|
|
8127
|
+
`${base}..HEAD`,
|
|
8128
|
+
"--",
|
|
8129
|
+
path
|
|
8130
|
+
).trim();
|
|
8131
|
+
return claims.some(
|
|
8132
|
+
({ head }) => head === void 0 || added === "" || !isAncestor(git, added, head)
|
|
8133
|
+
);
|
|
8100
8134
|
}
|
|
8101
8135
|
function listTakenIds(ctx, { paths }, base) {
|
|
8102
8136
|
verifyBase(ctx, paths, base, " Fetch first or pass --base.");
|
|
8103
|
-
|
|
8137
|
+
const { ids, skipped } = takenIds(ctx, paths, base);
|
|
8138
|
+
return skipped === void 0 ? { ids } : { ids, skipped };
|
|
8104
8139
|
}
|
|
8105
8140
|
function collisionsOf(ctx, paths, base) {
|
|
8106
|
-
const
|
|
8141
|
+
const git = gitAt(ctx, paths.root);
|
|
8142
|
+
const added = localAdditions(git, paths, base);
|
|
8107
8143
|
const local = added.flatMap((path) => {
|
|
8108
8144
|
const id = basename4(path, ".md");
|
|
8109
8145
|
return RECORD_FILE.test(basename4(path)) ? [{ path, id }] : [];
|
|
8110
8146
|
});
|
|
8111
8147
|
if (local.length === 0) return { collisions: [], localIds: [], taken: [] };
|
|
8112
8148
|
const taken = takenIds(ctx, paths, base);
|
|
8113
|
-
const takenSet = new Set(taken.ids);
|
|
8114
8149
|
const result = {
|
|
8115
|
-
collisions: local.filter((
|
|
8150
|
+
collisions: local.filter((record) => collides(git, base, record, taken)),
|
|
8116
8151
|
localIds: local.map(({ id }) => id),
|
|
8117
8152
|
taken: taken.ids
|
|
8118
8153
|
};
|
|
@@ -8190,7 +8225,7 @@ function parseRenamePlan(paths, text) {
|
|
|
8190
8225
|
|
|
8191
8226
|
// src/core/commit-reindex.ts
|
|
8192
8227
|
var SUBJECT_LIST_LIMIT = 3;
|
|
8193
|
-
function reindexMessage(renames, originalSubjects) {
|
|
8228
|
+
function reindexMessage(renames, originalSubjects = "") {
|
|
8194
8229
|
const pairs = renames.map(({ oldId, newId }) => `${oldId} -> ${newId}`);
|
|
8195
8230
|
const subject = renames.length > SUBJECT_LIST_LIMIT ? `reindex ${renames.length} local decisions to avoid base-branch collisions` : `reindex local decisions: ${pairs.join(", ")}`;
|
|
8196
8231
|
const message = `${subject}
|
|
@@ -8202,11 +8237,75 @@ ${pairs.map((pair) => `- ${pair}`).join("\n")}`;
|
|
|
8202
8237
|
Squashed from original branch commits:
|
|
8203
8238
|
${originalSubjects}`;
|
|
8204
8239
|
}
|
|
8205
|
-
function commitReindex(ctx, { paths }, planText, base) {
|
|
8206
|
-
const git = gitAt(ctx, paths.root);
|
|
8240
|
+
function commitReindex(ctx, { paths }, planText, base, { squash = false } = {}) {
|
|
8207
8241
|
verifyBase(ctx, paths, base);
|
|
8208
8242
|
const renames = parseRenamePlan(paths, planText);
|
|
8209
8243
|
if (renames.length === 0) throw new DldError("no rename plan on stdin.");
|
|
8244
|
+
for (const { oldId, newId, path } of renames) {
|
|
8245
|
+
const renamed = posix2.join(posix2.dirname(path), `${newId}.md`);
|
|
8246
|
+
if (!ctx.fs.isRegularFile(join7(paths.root, renamed))) {
|
|
8247
|
+
throw new DldError(`${renamed} not found. Run rename-decision for ${oldId} first.`);
|
|
8248
|
+
}
|
|
8249
|
+
}
|
|
8250
|
+
const git = gitAt(ctx, paths.root);
|
|
8251
|
+
return squash ? squashReindex(ctx, git, paths, renames, base) : commitOnTop(ctx, git, paths, renames);
|
|
8252
|
+
}
|
|
8253
|
+
function commitOnTop(ctx, git, paths, renames) {
|
|
8254
|
+
const stage = /* @__PURE__ */ new Set();
|
|
8255
|
+
for (const { path, newId } of renames) {
|
|
8256
|
+
stage.add(path);
|
|
8257
|
+
stage.add(posix2.join(posix2.dirname(path), `${newId}.md`));
|
|
8258
|
+
}
|
|
8259
|
+
for (const file of nulSeparated(git("diff", "-z", "--no-renames", "--name-only", "HEAD"))) {
|
|
8260
|
+
stage.add(file);
|
|
8261
|
+
}
|
|
8262
|
+
const head = git("rev-parse", "HEAD").trim();
|
|
8263
|
+
const tree = git("write-tree").trim();
|
|
8264
|
+
let stepName = "staging";
|
|
8265
|
+
try {
|
|
8266
|
+
for (const path of [...stage].sort()) {
|
|
8267
|
+
stepName = `staging ${path}`;
|
|
8268
|
+
stagePath(ctx, git, join7(paths.root, path), path);
|
|
8269
|
+
}
|
|
8270
|
+
stepName = "checking the staged changes";
|
|
8271
|
+
if (!hasStagedChanges(git)) {
|
|
8272
|
+
throw new DldError(
|
|
8273
|
+
"nothing to commit. The reindex may have already been committed, or the plan didn't match the working tree."
|
|
8274
|
+
);
|
|
8275
|
+
}
|
|
8276
|
+
stepName = "committing";
|
|
8277
|
+
commit(git, reindexMessage(renames));
|
|
8278
|
+
} catch (error) {
|
|
8279
|
+
try {
|
|
8280
|
+
git("read-tree", tree);
|
|
8281
|
+
} catch (restoreError) {
|
|
8282
|
+
throw new DldError(
|
|
8283
|
+
`commit-reindex failed while ${stepName}: ${describe(error)}
|
|
8284
|
+
Restoring the index also failed: ${describe(restoreError)}
|
|
8285
|
+
To restore by hand: git read-tree ${tree}`
|
|
8286
|
+
);
|
|
8287
|
+
}
|
|
8288
|
+
if (!(error instanceof DldError)) throw error;
|
|
8289
|
+
throw new DldError(
|
|
8290
|
+
`commit-reindex failed while ${stepName}: ${error.message}
|
|
8291
|
+
The index was restored.`,
|
|
8292
|
+
error.exitCode
|
|
8293
|
+
);
|
|
8294
|
+
}
|
|
8295
|
+
return {
|
|
8296
|
+
commit: git("rev-parse", "--short", "HEAD").trim(),
|
|
8297
|
+
onto: git("rev-parse", "--short", head).trim()
|
|
8298
|
+
};
|
|
8299
|
+
}
|
|
8300
|
+
function commit(git, message) {
|
|
8301
|
+
try {
|
|
8302
|
+
git("commit", "--quiet", "-m", message);
|
|
8303
|
+
} catch (error) {
|
|
8304
|
+
if (error instanceof GitCommandError) throw new DldError(`git commit failed: ${error.stderr}`);
|
|
8305
|
+
throw error;
|
|
8306
|
+
}
|
|
8307
|
+
}
|
|
8308
|
+
function squashReindex(ctx, git, paths, renames, base) {
|
|
8210
8309
|
const onto = mergeBase(ctx, paths, base);
|
|
8211
8310
|
const head = git("rev-parse", "HEAD").trim();
|
|
8212
8311
|
if (onto === head) throw new DldError("HEAD is already at the merge-base \u2014 nothing to squash.");
|
|
@@ -8255,15 +8354,7 @@ function commitReindex(ctx, { paths }, planText, base) {
|
|
|
8255
8354
|
);
|
|
8256
8355
|
}
|
|
8257
8356
|
});
|
|
8258
|
-
step("committing", () =>
|
|
8259
|
-
try {
|
|
8260
|
-
git("commit", "--quiet", "-m", message);
|
|
8261
|
-
} catch (error) {
|
|
8262
|
-
if (error instanceof GitCommandError)
|
|
8263
|
-
throw new DldError(`git commit failed: ${error.stderr}`);
|
|
8264
|
-
throw error;
|
|
8265
|
-
}
|
|
8266
|
-
});
|
|
8357
|
+
step("committing", () => commit(git, message));
|
|
8267
8358
|
} catch (error) {
|
|
8268
8359
|
throw rollback(ctx, git, indexFull, saved, stepName, error);
|
|
8269
8360
|
}
|
|
@@ -8326,24 +8417,35 @@ function skippedNotice(reason) {
|
|
|
8326
8417
|
// src/cli/commands/commit-reindex.ts
|
|
8327
8418
|
var commitReindexCommand = {
|
|
8328
8419
|
name: "commit-reindex",
|
|
8329
|
-
summary: "
|
|
8420
|
+
summary: "Commit the renamed decisions",
|
|
8330
8421
|
internal: true,
|
|
8331
|
-
usage: `Usage: dld commit-reindex --base <ref> < plan
|
|
8422
|
+
usage: `Usage: dld commit-reindex --base <ref> [--squash] < plan
|
|
8332
8423
|
|
|
8333
|
-
Read a rename plan on standard input and
|
|
8334
|
-
|
|
8335
|
-
|
|
8424
|
+
Read a rename plan on standard input and commit the renames, which rename-decision has
|
|
8425
|
+
already applied. By default this adds one commit on top of HEAD with the plan's paths and
|
|
8426
|
+
every tracked file changed since HEAD; untracked files are left out. On failure the index is
|
|
8427
|
+
restored.
|
|
8428
|
+
|
|
8429
|
+
With --squash, the branch's commits since the merge-base with <ref> are squashed into one
|
|
8430
|
+
reindex commit instead, and INDEX.md is left at its merge-base state. On failure the branch,
|
|
8431
|
+
index and INDEX.md are restored.
|
|
8336
8432
|
|
|
8337
8433
|
Options:
|
|
8338
8434
|
--base <ref> Base ref (required)
|
|
8435
|
+
--squash Squash the branch instead of adding a commit
|
|
8339
8436
|
`,
|
|
8340
8437
|
run(args, io, ctx) {
|
|
8341
|
-
const { values } = parseCommandArgs({
|
|
8438
|
+
const { values } = parseCommandArgs({
|
|
8439
|
+
args: [...args],
|
|
8440
|
+
options: { base: { type: "string" }, squash: { type: "boolean" } }
|
|
8441
|
+
});
|
|
8342
8442
|
if (!values.base) throw new DldError("--base is required.");
|
|
8343
8443
|
const base = baseOption(values.base);
|
|
8344
8444
|
const project = loadProject(ctx);
|
|
8345
|
-
const { commit, onto } = commitReindex(ctx, project, ctx.readStdin(), base
|
|
8346
|
-
|
|
8445
|
+
const { commit: commit2, onto } = commitReindex(ctx, project, ctx.readStdin(), base, {
|
|
8446
|
+
squash: values.squash === true
|
|
8447
|
+
});
|
|
8448
|
+
io.stdout(`Created reindex commit ${commit2} on top of ${onto}
|
|
8347
8449
|
`);
|
|
8348
8450
|
return EXIT_OK;
|
|
8349
8451
|
}
|
|
@@ -8726,11 +8828,11 @@ var idNumber2 = (id) => Number.parseInt(id.slice(3), 10);
|
|
|
8726
8828
|
function recordsChangedSinceAudit(ctx, { paths }) {
|
|
8727
8829
|
const git = gitAt(ctx, paths.root);
|
|
8728
8830
|
const stored = stateString(readStateSection(ctx, paths, "audit"), "commit_hash");
|
|
8729
|
-
const
|
|
8730
|
-
if (
|
|
8831
|
+
const commit2 = resolveStateCommit(git, stored);
|
|
8832
|
+
if (commit2 === void 0) return void 0;
|
|
8731
8833
|
const records = recordsPathspec(paths);
|
|
8732
8834
|
return /* @__PURE__ */ new Set([
|
|
8733
|
-
...nulSeparated(git("diff", "-z", "--name-only",
|
|
8835
|
+
...nulSeparated(git("diff", "-z", "--name-only", commit2, "--", records)),
|
|
8734
8836
|
...nulSeparated(git("ls-files", "-z", "--others", "--exclude-standard", "--", records))
|
|
8735
8837
|
]);
|
|
8736
8838
|
}
|
|
@@ -8756,9 +8858,9 @@ function findMissingAmends(ctx, project, { all }) {
|
|
|
8756
8858
|
}
|
|
8757
8859
|
function updateAuditState(ctx, { paths }) {
|
|
8758
8860
|
const timestamp = formatTimestamp(ctx.now());
|
|
8759
|
-
const
|
|
8760
|
-
writeStateSection(ctx, paths, "audit", { last_run: timestamp, commit_hash:
|
|
8761
|
-
return { timestamp, commit };
|
|
8861
|
+
const commit2 = shortHead(ctx, paths.root);
|
|
8862
|
+
writeStateSection(ctx, paths, "audit", { last_run: timestamp, commit_hash: commit2 });
|
|
8863
|
+
return { timestamp, commit: commit2 };
|
|
8762
8864
|
}
|
|
8763
8865
|
|
|
8764
8866
|
// src/cli/commands/find-missing-amends.ts
|
|
@@ -8787,7 +8889,7 @@ Options:
|
|
|
8787
8889
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
8788
8890
|
import { join as join12, posix as posix3 } from "node:path";
|
|
8789
8891
|
var idPattern = (id, flags = "") => new RegExp(`${id}(?![0-9])`, flags);
|
|
8790
|
-
function changedFiles(ctx, paths,
|
|
8892
|
+
function changedFiles(ctx, paths, commit2) {
|
|
8791
8893
|
return nulSeparated(
|
|
8792
8894
|
gitAt(ctx, paths.root)(
|
|
8793
8895
|
"diff",
|
|
@@ -8795,7 +8897,7 @@ function changedFiles(ctx, paths, commit) {
|
|
|
8795
8897
|
"--find-renames",
|
|
8796
8898
|
"--name-only",
|
|
8797
8899
|
"--diff-filter=AMR",
|
|
8798
|
-
|
|
8900
|
+
commit2
|
|
8799
8901
|
)
|
|
8800
8902
|
);
|
|
8801
8903
|
}
|
|
@@ -8846,7 +8948,7 @@ function renameDecision(ctx, project, rename, base) {
|
|
|
8846
8948
|
}
|
|
8847
8949
|
function findStaleMentions(ctx, { paths }, planText, base) {
|
|
8848
8950
|
const renames = parseRenamePlan(paths, planText);
|
|
8849
|
-
if (renames.length === 0)
|
|
8951
|
+
if (renames.length === 0) throw new DldError("no rename plan on stdin.");
|
|
8850
8952
|
verifyBase(ctx, paths, base);
|
|
8851
8953
|
const decisions = `${decisionsPathspec(paths)}/`;
|
|
8852
8954
|
const files = changedFiles(ctx, paths, mergeBase(ctx, paths, base)).flatMap((file) => {
|
|
@@ -9279,7 +9381,7 @@ function removeEmptyParents(ctx, dir, paths) {
|
|
|
9279
9381
|
import { dirname as dirname3, join as join15, posix as posix4 } from "node:path";
|
|
9280
9382
|
|
|
9281
9383
|
// templates/rules/dld-workflow.md
|
|
9282
|
-
var dld_workflow_default = "# DLD (Decision-Linked Development)\n\nThis project uses Decision-Linked Development. Decision records (DL-*.md) live in the `records/` subdirectory of the decisions directory set in `dld.config.yaml` (`decisions/` by default). High-level docs (INDEX.md, OVERVIEW.md, SNAPSHOT.md) live in the decisions directory.\n\n## Rules\n\n- When you encounter `@decision(DL-XXX)` annotations in code, read the referenced decision with the dld-lookup skill BEFORE modifying the annotated code.\n- ALWAYS look up and verify related decisions before modifying annotated code. Do not skip this step.\n- NEVER modify code in a way that contradicts an existing decision without first confirming with the user. If the change requires breaking a previous decision, a new decision must be recorded (with the dld-decide skill) that explicitly supersedes the old one. If it only partially modifies a previous decision, record it as an amendment instead.\n\n## Skills\n\n- dld-decide: record a new decision\n- dld-plan: break down a feature into multiple grouped decisions\n- dld-implement: implement proposed decisions\n- dld-lookup: query decisions by ID, tag, or code path\n- dld-adjust: adjust or update existing decisions\n- dld-audit: scan for drift between decisions and code\n- dld-snapshot: regenerate SNAPSHOT.md and OVERVIEW.md from the decision log\n- dld-status: a quick overview of the decision log state\n- dld-retrofit: generate decisions from an existing codebase\n- dld-reindex: resolve decision-ID collisions with the base branch
|
|
9384
|
+
var dld_workflow_default = "# DLD (Decision-Linked Development)\n\nThis project uses Decision-Linked Development. Decision records (DL-*.md) live in the `records/` subdirectory of the decisions directory set in `dld.config.yaml` (`decisions/` by default). High-level docs (INDEX.md, OVERVIEW.md, SNAPSHOT.md) live in the decisions directory.\n\n## Rules\n\n- When you encounter `@decision(DL-XXX)` annotations in code, read the referenced decision with the dld-lookup skill BEFORE modifying the annotated code.\n- ALWAYS look up and verify related decisions before modifying annotated code. Do not skip this step.\n- NEVER modify code in a way that contradicts an existing decision without first confirming with the user. If the change requires breaking a previous decision, a new decision must be recorded (with the dld-decide skill) that explicitly supersedes the old one. If it only partially modifies a previous decision, record it as an amendment instead.\n\n## Skills\n\n- dld-decide: record a new decision\n- dld-plan: break down a feature into multiple grouped decisions\n- dld-implement: implement proposed decisions\n- dld-lookup: query decisions by ID, tag, or code path\n- dld-adjust: adjust or update existing decisions\n- dld-audit: scan for drift between decisions and code\n- dld-snapshot: regenerate SNAPSHOT.md and OVERVIEW.md from the decision log\n- dld-status: a quick overview of the decision log state\n- dld-retrofit: generate decisions from an existing codebase\n- dld-reindex: resolve decision-ID collisions with the base branch and open PRs\n";
|
|
9283
9385
|
|
|
9284
9386
|
// src/generate/rule.ts
|
|
9285
9387
|
var RULE_TEXT = dld_workflow_default;
|
|
@@ -10194,8 +10296,8 @@ var updateAuditStateCommand = {
|
|
|
10194
10296
|
usage: "Usage: dld update-audit-state\n\nRecord the current time and HEAD commit as the last audit.\n",
|
|
10195
10297
|
run(args, io, ctx) {
|
|
10196
10298
|
parseCommandArgs({ args: [...args], options: {} });
|
|
10197
|
-
const { timestamp, commit } = updateAuditState(ctx, loadProject(ctx));
|
|
10198
|
-
io.stdout(`Audit state updated: ${timestamp} at ${
|
|
10299
|
+
const { timestamp, commit: commit2 } = updateAuditState(ctx, loadProject(ctx));
|
|
10300
|
+
io.stdout(`Audit state updated: ${timestamp} at ${commit2}
|
|
10199
10301
|
`);
|
|
10200
10302
|
return EXIT_OK;
|
|
10201
10303
|
}
|
|
@@ -10217,8 +10319,8 @@ SNAPSHOT.md, OVERVIEW.md and any custom artifacts named.
|
|
|
10217
10319
|
options: {},
|
|
10218
10320
|
allowPositionals: true
|
|
10219
10321
|
});
|
|
10220
|
-
const { timestamp, commit, highest } = updateSnapshotState(ctx, loadProject(ctx), positionals);
|
|
10221
|
-
io.stdout(`Snapshot state updated: ${timestamp} at ${
|
|
10322
|
+
const { timestamp, commit: commit2, highest } = updateSnapshotState(ctx, loadProject(ctx), positionals);
|
|
10323
|
+
io.stdout(`Snapshot state updated: ${timestamp} at ${commit2} (through ${formatId(highest)})
|
|
10222
10324
|
`);
|
|
10223
10325
|
return EXIT_OK;
|
|
10224
10326
|
}
|
|
@@ -3,7 +3,7 @@ name: dld-decide
|
|
|
3
3
|
description: Record a single development decision as a markdown file with YAML frontmatter. Collects context, rationale, and code references interactively.
|
|
4
4
|
compatibility: Requires Node.js 20+ and git.
|
|
5
5
|
metadata:
|
|
6
|
-
dld-kit-version: "1.0.0-rc.
|
|
6
|
+
dld-kit-version: "1.0.0-rc.4"
|
|
7
7
|
---
|
|
8
8
|
<!-- Generated by dld-kit from templates/skills/dld-decide/SKILL.md. Do not edit: updating the skills overwrites this file. -->
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@ name: dld-implement
|
|
|
3
3
|
description: Implement one or more proposed decisions. Makes code changes, adds `@decision` annotations, and updates decision status.
|
|
4
4
|
compatibility: Requires Node.js 20+ and git.
|
|
5
5
|
metadata:
|
|
6
|
-
dld-kit-version: "1.0.0-rc.
|
|
6
|
+
dld-kit-version: "1.0.0-rc.4"
|
|
7
7
|
---
|
|
8
8
|
<!-- Generated by dld-kit from templates/skills/dld-implement/SKILL.md. Do not edit: updating the skills overwrites this file. -->
|
|
9
9
|
|
package/skills/dld-init/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: dld-init
|
|
|
3
3
|
description: Bootstrap DLD (Decision-Linked Development) in a repository. Creates dld.config.yaml, the decisions/ directory, and INDEX.md. Run once per project.
|
|
4
4
|
compatibility: Requires Node.js 20+ and git.
|
|
5
5
|
metadata:
|
|
6
|
-
dld-kit-version: "1.0.0-rc.
|
|
6
|
+
dld-kit-version: "1.0.0-rc.4"
|
|
7
7
|
---
|
|
8
8
|
<!-- Generated by dld-kit from templates/skills/dld-init/SKILL.md. Do not edit: updating the skills overwrites this file. -->
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@ name: dld-lookup
|
|
|
3
3
|
description: Look up decisions by ID, tag, code path, or keyword. IMPORTANT — use this proactively whenever you encounter `@decision` annotations in code you are about to read or modify.
|
|
4
4
|
compatibility: No external dependencies.
|
|
5
5
|
metadata:
|
|
6
|
-
dld-kit-version: "1.0.0-rc.
|
|
6
|
+
dld-kit-version: "1.0.0-rc.4"
|
|
7
7
|
---
|
|
8
8
|
<!-- Generated by dld-kit from templates/skills/dld-lookup/SKILL.md. Do not edit: updating the skills overwrites this file. -->
|
|
9
9
|
|
package/skills/dld-plan/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: dld-plan
|
|
|
3
3
|
description: Break down a feature into multiple decisions interactively. Creates a set of decision records grouped by a shared tag.
|
|
4
4
|
compatibility: Requires Node.js 20+ and git.
|
|
5
5
|
metadata:
|
|
6
|
-
dld-kit-version: "1.0.0-rc.
|
|
6
|
+
dld-kit-version: "1.0.0-rc.4"
|
|
7
7
|
---
|
|
8
8
|
<!-- Generated by dld-kit from templates/skills/dld-plan/SKILL.md. Do not edit: updating the skills overwrites this file. -->
|
|
9
9
|
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dld-reindex
|
|
3
|
-
description: Resolve decision ID collisions between a local branch and the base branch (and open PRs)
|
|
3
|
+
description: Resolve decision ID collisions between a local branch and the base branch (and open PRs). Renames colliding local decisions with git mv, rewrites cross-references and annotations, and commits the renames on top of the branch. Squashes the branch only when the user asks.
|
|
4
4
|
compatibility: Requires Node.js 20+ and git. Open-PR scanning additionally needs the `gh` CLI authenticated against a GitHub remote — the skill falls back gracefully when unavailable.
|
|
5
5
|
metadata:
|
|
6
|
-
dld-kit-version: "1.0.0-rc.
|
|
6
|
+
dld-kit-version: "1.0.0-rc.4"
|
|
7
7
|
---
|
|
8
8
|
<!-- Generated by dld-kit from templates/skills/dld-reindex/SKILL.md. Do not edit: updating the skills overwrites this file. -->
|
|
9
9
|
|
|
10
10
|
# /dld-reindex — Resolve Decision ID Collisions
|
|
11
11
|
|
|
12
|
-
You are helping the developer untangle decision ID collisions
|
|
12
|
+
You are helping the developer untangle decision ID collisions. Two or more developers can draft `DL-NNN` decisions in parallel; once one of them lands on the base branch (or appears in an open PR), the others must rename their local copies to the next free ID. This skill does that mechanically.
|
|
13
13
|
|
|
14
|
-
**
|
|
14
|
+
**By default the skill adds a commit and does not rewrite history.** The renames go into one new commit on top of the branch (or a few, for a large reindex). A merge only compares end states, so once the branch tip has the record at its new path, `git merge <base>` and merging the PR on GitHub (merge commit or squash merge) don't conflict on the records. A normal push is enough.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
**Squash mode, only when the user asks for it.** A rebase replays every branch commit, and the commit that added the colliding path (e.g. `decisions/records/DL-205.md`) hits an add/add conflict, whatever later commits do. If the user wants to rebase (or their repo uses GitHub's "Rebase and merge"), they can ask for a squash: the branch's commits since the merge-base become one reindex commit, and a pushed branch then needs `git push --force-with-lease`. Use squash mode only when the user's request says so (e.g. `/dld-reindex squash`, "squash it", "I want to rebase"). Never pick it on your own.
|
|
17
17
|
|
|
18
18
|
## Interaction style
|
|
19
19
|
|
|
20
|
-
Use the `AskUserQuestion` tool when
|
|
20
|
+
Use the `AskUserQuestion` tool when asking about pushing and, in squash mode, for consent to rewrite history. Everything else is deterministic.
|
|
21
21
|
|
|
22
22
|
**Do not redirect any command output to `/tmp` files.** The commands in this skill emit only what you need to act on; piping to `/tmp/*.txt`, `tee`-ing into scratch files, or stashing stderr separately is unnecessary and creates clutter outside the repo. If a command's output is too long to read in one go, narrow it (`| tail -N`, `| head -N`, or pass a more specific flag) rather than persisting it.
|
|
23
23
|
|
|
@@ -27,6 +27,8 @@ The commands below run the `dld` CLI bundled with the dld-common skill, and need
|
|
|
27
27
|
|
|
28
28
|
This skill uses: `resolve-base`, `plan-renames`, `rename-decision`, `find-stale-mentions`, `commit-reindex`, `regenerate-index`.
|
|
29
29
|
|
|
30
|
+
The steps below use shell variables (`BASE`, `PLAN`, `GROUP`). If your shell doesn't keep variables between commands, set them again in each command, or write the values out (e.g. pipe the plan lines with `printf '%s\t%s\t%s\n' ...`). `find-stale-mentions` and `commit-reindex` fail on an empty plan, so a lost variable shows up as an error.
|
|
31
|
+
|
|
30
32
|
## Prerequisites
|
|
31
33
|
|
|
32
34
|
1. Check that `dld.config.yaml` exists at the repo root. If not, tell the user to run `/dld-init` first and stop.
|
|
@@ -44,7 +46,7 @@ BASE=$(node "<skill-dir>/../dld-common/scripts/dld.mjs" resolve-base)
|
|
|
44
46
|
|
|
45
47
|
`resolve-base` prefers the branch's upstream when it tracks a *different* branch (the typical "feature → main" setup). It falls back to `origin/main` if the upstream is unset OR if the upstream tracks the same branch name as the current branch (i.e. it's just the remote copy of this same branch, not a useful collision base).
|
|
46
48
|
|
|
47
|
-
The user may pass an explicit base when invoking the skill (e.g. `/dld-reindex origin/develop`) — honor it if present.
|
|
49
|
+
The user may pass an explicit base when invoking the skill (e.g. `/dld-reindex origin/develop`) — honor it if present. The word `squash` is not a base: it selects squash mode (`/dld-reindex squash origin/develop` does both).
|
|
48
50
|
|
|
49
51
|
## Step 2: Plan the renames
|
|
50
52
|
|
|
@@ -61,17 +63,26 @@ Output is tab-separated, one rename per line:
|
|
|
61
63
|
|
|
62
64
|
If the output is empty, exit with:
|
|
63
65
|
|
|
64
|
-
> No ID collisions
|
|
66
|
+
> No ID collisions with `$BASE` or open PRs.
|
|
65
67
|
|
|
66
68
|
`plan-renames` may print a stderr note like `[dld-reindex] open PRs not scanned: gh CLI not installed`. **Always surface this to the user** so they know the renamed IDs were chosen against base-branch state only and may still collide with an open PR.
|
|
67
69
|
|
|
70
|
+
**Check that each rename is a real collision.** For each plan line, compare the local record with the base's record of the same ID: `git show "$BASE:<path>"`, or, when the base keeps it in another namespace directory, find it with `git ls-tree -r --name-only "$BASE" | grep '/DL-205\.md$'`. If they are the same decision (same title and substance, give or take later edits), the branch got it from a PR that has since merged into the base, typically a decisions PR this branch was stacked on that was squash- or rebase-merged. Don't rename it. Stop and tell the user to merge the base into the branch first (`git merge $BASE`; if INDEX.md conflicts, `node "<skill-dir>/../dld-common/scripts/dld.mjs" regenerate-index` and `git add` it), then run `/dld-reindex` again.
|
|
71
|
+
|
|
72
|
+
A record this branch got from an open PR it is stacked on (an implementation branch cut from a decisions PR) is not a collision with that PR: it is the same decision. This needs the PR's head commit locally, which the `git fetch origin` above provides; a PR from a fork whose head isn't fetched still counts as a collision.
|
|
73
|
+
|
|
68
74
|
The underlying helpers (`find-collisions`, `list-taken-ids`) remain available for debugging, but the SKILL flow always goes through `plan-renames`.
|
|
69
75
|
|
|
70
|
-
## Step 3:
|
|
76
|
+
## Step 3: Choose how to commit
|
|
77
|
+
|
|
78
|
+
**Default mode (no squash requested).** Don't ask for consent; the renames are new commits the user can review or revert. Decide how many commits to make:
|
|
71
79
|
|
|
72
|
-
|
|
80
|
+
- **One commit** for the whole plan. This is the normal case.
|
|
81
|
+
- **Several commits** when the reindex is large: many renames, or renames that rewrite many files (lots of annotations, amendments, supersedes or references between the renamed decisions). Each commit holds one decision, or a group of related ones (e.g. a decision and the local decisions that amend or supersede it). Say in a line which groups you chose.
|
|
73
82
|
|
|
74
|
-
|
|
83
|
+
**Squash mode (the user asked for it).** Show the rename plan and the implication, then use `AskUserQuestion`:
|
|
84
|
+
|
|
85
|
+
> Squashing rewrites branch history. I will squash the N commits since `<merge-base>` into a single reindex commit. The original commit subjects will be preserved in the new commit body. If the branch has already been pushed, finishing will require `git push --force-with-lease`. How should I proceed?
|
|
75
86
|
|
|
76
87
|
Options:
|
|
77
88
|
- **Rewrite and force-push** — agent applies renames, squashes, commits, and runs `git push --force-with-lease`.
|
|
@@ -82,7 +93,14 @@ If the user cancels, exit without touching anything.
|
|
|
82
93
|
|
|
83
94
|
## Step 4: Apply renames
|
|
84
95
|
|
|
85
|
-
|
|
96
|
+
Run steps 4 to 6 once for the whole plan, or, in default mode, once per group when you split the work. Put the lines you are working on in `GROUP`, in the plan's format. For a single commit or a squash, that's the whole plan; for a group, pick its lines by old ID:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
GROUP="$PLAN" # whole plan
|
|
100
|
+
GROUP=$(printf '%s\n' "$PLAN" | grep -E $'\t(DL-205|DL-207)\t') # one group
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
For each line in the plan (or group), call:
|
|
86
104
|
|
|
87
105
|
```bash
|
|
88
106
|
node "<skill-dir>/../dld-common/scripts/dld.mjs" rename-decision --old DL-OLD --new DL-NEW --path <relative-path> --base "$BASE"
|
|
@@ -102,10 +120,10 @@ The substitution is digit-aware: renaming `DL-100` will not accidentally rewrite
|
|
|
102
120
|
|
|
103
121
|
## Step 5: Review plain-text DL-OLD mentions
|
|
104
122
|
|
|
105
|
-
After
|
|
123
|
+
After the renames are applied (and before committing), find any remaining bare `DL-OLD` references in non-decision changed files:
|
|
106
124
|
|
|
107
125
|
```bash
|
|
108
|
-
echo "$
|
|
126
|
+
echo "$GROUP" | node "<skill-dir>/../dld-common/scripts/dld.mjs" find-stale-mentions --base "$BASE"
|
|
109
127
|
```
|
|
110
128
|
|
|
111
129
|
Output is tab-separated, one match per line: `<path>\t<line>\t<DL-OLD>\t<DL-NEW>\t<line-content>`. Empty output means nothing to review.
|
|
@@ -119,12 +137,25 @@ For each match:
|
|
|
119
137
|
|
|
120
138
|
Surface the list of matches to the user with your verdicts before you finish, so they can sanity-check the judgment calls.
|
|
121
139
|
|
|
122
|
-
## Step 6:
|
|
140
|
+
## Step 6: Commit
|
|
141
|
+
|
|
142
|
+
**Do not use `git add -A` or `git commit -a` anywhere in this flow.** Use only `commit-reindex` to commit.
|
|
143
|
+
|
|
144
|
+
**Default mode.** Regenerate INDEX.md so it lists the new IDs, then commit:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
node "<skill-dir>/../dld-common/scripts/dld.mjs" regenerate-index
|
|
148
|
+
echo "$GROUP" | node "<skill-dir>/../dld-common/scripts/dld.mjs" commit-reindex --base "$BASE"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`commit-reindex` checks that every renamed file exists, stages the plan's old and new paths plus every tracked file changed since HEAD (the renames, the rewritten references and annotations, your step 5 edits, INDEX.md), and commits on top of HEAD. Untracked files are not staged. The message lists the group's renames. If the commit fails (e.g. a hook rejects it), the index is restored and HEAD has not moved.
|
|
123
152
|
|
|
124
|
-
|
|
153
|
+
If you split the work, go back to step 4 for the next group.
|
|
154
|
+
|
|
155
|
+
**Squash mode.** Pipe the whole rename plan into `commit-reindex --squash`:
|
|
125
156
|
|
|
126
157
|
```bash
|
|
127
|
-
echo "$PLAN" | node "<skill-dir>/../dld-common/scripts/dld.mjs" commit-reindex --base "$BASE"
|
|
158
|
+
echo "$PLAN" | node "<skill-dir>/../dld-common/scripts/dld.mjs" commit-reindex --base "$BASE" --squash
|
|
128
159
|
```
|
|
129
160
|
|
|
130
161
|
This:
|
|
@@ -135,11 +166,19 @@ This:
|
|
|
135
166
|
4. Stages **only** an explicit path list derived from the original branch diff and the rename plan — the old paths (for deletions), the new paths (for additions), every other file the branch touched. Untracked unrelated paths (e.g. `.claude/worktrees`, scratch files, in-progress edits to unrelated files) are deliberately NOT swept in.
|
|
136
167
|
5. Commits with a templated message that lists the renames in the subject and preserves the original branch commits' subjects in the body.
|
|
137
168
|
|
|
138
|
-
|
|
169
|
+
## Step 7: Push
|
|
139
170
|
|
|
140
|
-
|
|
171
|
+
**Default mode.** Use `AskUserQuestion` to ask whether to push now (options: **Push** / **Don't push**). If yes:
|
|
141
172
|
|
|
142
|
-
|
|
173
|
+
```bash
|
|
174
|
+
if git rev-parse --verify --quiet "@{upstream}" >/dev/null; then
|
|
175
|
+
git push
|
|
176
|
+
else
|
|
177
|
+
git push -u origin HEAD
|
|
178
|
+
fi
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Squash mode.** If step 3's answer was "Rewrite and force-push":
|
|
143
182
|
|
|
144
183
|
```bash
|
|
145
184
|
if git rev-parse --verify --quiet "@{upstream}" >/dev/null; then
|
|
@@ -157,8 +196,16 @@ Print:
|
|
|
157
196
|
|
|
158
197
|
- The renames table.
|
|
159
198
|
- The stderr note from step 2 if `gh` was skipped.
|
|
160
|
-
- The number of commits squashed.
|
|
161
|
-
- The next steps
|
|
199
|
+
- The commits created (default mode), or the number of commits squashed (squash mode).
|
|
200
|
+
- The next steps.
|
|
201
|
+
|
|
202
|
+
Default mode:
|
|
203
|
+
|
|
204
|
+
> 1. Bring in the base when you need it: `git merge $BASE`, or merge the PR as usual.
|
|
205
|
+
> 2. If INDEX.md conflicts (both sides added rows), run `node "<skill-dir>/../dld-common/scripts/dld.mjs" regenerate-index`, then `git add` INDEX.md and finish the merge.
|
|
206
|
+
> 3. Don't rebase this branch onto `$BASE`: its earlier commits still add the old paths and will conflict. To rebase anyway, before you merge the base, drop the reindex commits (`git reset --keep HEAD~N`, N = the number of reindex commits) and run `/dld-reindex squash`.
|
|
207
|
+
|
|
208
|
+
Squash mode:
|
|
162
209
|
|
|
163
210
|
> 1. `git rebase $BASE`
|
|
164
211
|
> 2. `node "<skill-dir>/../dld-common/scripts/dld.mjs" regenerate-index` (to repopulate INDEX.md with the renamed locals — the reindex commit intentionally leaves INDEX.md alone to keep the rebase conflict-free; INDEX.md is missing the renamed rows until you regenerate)
|
|
@@ -169,5 +216,5 @@ The skill never rebases or merges — that is always the user's call.
|
|
|
169
216
|
## Out of scope
|
|
170
217
|
|
|
171
218
|
- **Already-conflicted rebases.** If the user is mid-rebase with conflicts, tell them to `git rebase --abort` first and re-run this skill.
|
|
172
|
-
- **
|
|
219
|
+
- **Rewriting each commit in place.** Neither mode rewrites the branch commit by commit (a cherry-pick walk); the edge cases (commits modifying an already-renamed file, merge commits, partial reruns) make it much more complex than adding a commit or squashing.
|
|
173
220
|
- **Cross-namespace ID reconciliation** in namespaced projects. IDs are assumed globally unique across namespaces, matching `next-id`.
|
|
@@ -3,7 +3,7 @@ name: dld-retrofit
|
|
|
3
3
|
description: Bootstrap DLD decisions from an existing codebase. Analyzes code to infer rationale, generates decision records, and adds `@decision` annotations.
|
|
4
4
|
compatibility: Requires Node.js 20+ and git.
|
|
5
5
|
metadata:
|
|
6
|
-
dld-kit-version: "1.0.0-rc.
|
|
6
|
+
dld-kit-version: "1.0.0-rc.4"
|
|
7
7
|
---
|
|
8
8
|
<!-- Generated by dld-kit from templates/skills/dld-retrofit/SKILL.md. Do not edit: updating the skills overwrites this file. -->
|
|
9
9
|
|