gitifact 0.8.0 → 0.8.1
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/dist/browser/assets/Banner-c1hFCLs8.js +1 -0
- package/dist/browser/assets/HoverCard-D6keobP0.js +1 -0
- package/dist/browser/assets/{Markdown-K4Mne1wf.js → Markdown-Droxhk-G.js} +1 -1
- package/dist/browser/assets/{Selector-DuT8AsMB.js → Selector-D_xYW7R_.js} +1 -1
- package/dist/browser/assets/Tab-VVHagF2Z.js +1 -0
- package/dist/browser/assets/{Table-8qCDrsKI.js → Table-D4v8xCrw.js} +2 -2
- package/dist/browser/assets/TimestampHoverCard-qfGbCkoP.js +1 -0
- package/dist/browser/assets/{Token-Dmavo-NE.js → Token-BLd8-jfF.js} +1 -1
- package/dist/browser/assets/{about-B8tYN8_7.js → about-DeUi-_2z.js} +2 -2
- package/dist/browser/assets/activity-timeline-BEeyw2GA.css +1 -0
- package/dist/browser/assets/activity-timeline-bFoqFwVu.js +2 -0
- package/dist/browser/assets/changelog-Brd1Ninm.js +2 -0
- package/dist/browser/assets/commit-CiMtFlZl.js +4 -0
- package/dist/browser/assets/{commit-45NumYDT.css → commit-DVyOH45R.css} +1 -1
- package/dist/browser/assets/contributor-2mkEa2Wo.css +1 -0
- package/dist/browser/assets/contributor-C_IskSHq.js +1 -0
- package/dist/browser/assets/contributors-CbwXLc7M.js +1 -0
- package/dist/browser/assets/{contributors._email-CW4qlA9y.js → contributors._email-BRfre3dw.js} +1 -1
- package/dist/browser/assets/contributors.index-CV84owW7.js +1 -0
- package/dist/browser/assets/dashboard.index-CQdr7hsf.js +1 -0
- package/dist/browser/assets/document-BZmiLx-e.js +2 -0
- package/dist/browser/assets/document-DEGaf2yT.css +1 -0
- package/dist/browser/assets/{document-D4osdtS6.js → document-JLY2-z8S.js} +4 -4
- package/dist/browser/assets/features-Cj41kcXf.js +4 -0
- package/dist/browser/assets/features-g3j2elTj.css +1 -0
- package/dist/browser/assets/features._featureId-BgFHsRcy.js +1 -0
- package/dist/browser/assets/features.index-Ca0rzFW6.js +1 -0
- package/dist/browser/assets/getting-started-CgL3Vi9o.js +1 -0
- package/dist/browser/assets/git-B7oxggGB.js +1 -0
- package/dist/browser/assets/{index-BER0M7UG.css → index-CmU7dwFQ.css} +1 -1
- package/dist/browser/assets/index-DWRGm9YO.js +48 -0
- package/dist/browser/assets/instructions-2OUMP-vh.css +1 -0
- package/dist/browser/assets/instructions-D3cdAPD3.js +1 -0
- package/dist/browser/assets/instructions._instructionId-BkT14646.js +1 -0
- package/dist/browser/assets/instructions.agents-B3jLRl1u.js +1 -0
- package/dist/browser/assets/instructions.index-8qlhSXoy.js +1 -0
- package/dist/browser/assets/{lazyRouteComponent-Dbwmw-_u.js → lazyRouteComponent-JkRa2CHo.js} +1 -1
- package/dist/browser/assets/{page-header-PYFVxi9j.js → page-header-CPL7myjo.js} +1 -1
- package/dist/browser/assets/records-Dk3shbYl.css +1 -0
- package/dist/browser/assets/records-page-B4Al3fIQ.js +1 -0
- package/dist/browser/assets/records-page-D2F1WJrh.css +1 -0
- package/dist/browser/assets/records._recordId-Chn1SO5R.js +1 -0
- package/dist/browser/assets/records.commits._commit-D4Wk4i_m.js +1 -0
- package/dist/browser/assets/records.index-CViTeD08.js +1 -0
- package/dist/browser/assets/records.working-BsHKMIz-.js +1 -0
- package/dist/browser/assets/request-state-oiWyP9c-.js +1 -0
- package/dist/browser/assets/{search-palette-C-XxJJ6I.js → search-palette-D0ctICJy.js} +5 -5
- package/dist/browser/assets/{settings-BdZ_rM9V.js → settings-BDVKHrS8.js} +1 -1
- package/dist/browser/assets/{useInfiniteQuery-Cs8d91AB.js → useInfiniteQuery-Bn_R13Ih.js} +1 -1
- package/dist/browser/assets/{useKeyboardHint-CuvkDYsZ.js → useKeyboardHint-lSxUw5Qj.js} +1 -1
- package/dist/browser/index.html +12 -11
- package/dist/i18n/en/block.md +23 -23
- package/dist/i18n/en/changelog.md +20 -0
- package/dist/i18n/en/docs/commit.md +1 -1
- package/dist/i18n/en/docs/instructions.md +16 -3
- package/dist/i18n/en/docs/migrate.md +3 -2
- package/dist/i18n/en/docs/spec.md +1 -1
- package/dist/i18n/en/docs/workflow.md +1 -1
- package/dist/i18n/ko/block.md +25 -25
- package/dist/i18n/ko/changelog.md +20 -0
- package/dist/i18n/ko/docs/commit.md +1 -1
- package/dist/i18n/ko/docs/instructions.md +16 -3
- package/dist/i18n/ko/docs/migrate.md +3 -2
- package/dist/i18n/ko/docs/spec.md +1 -1
- package/dist/i18n/ko/docs/workflow.md +1 -1
- package/dist/main.js +919 -417
- package/package.json +1 -1
- package/dist/browser/assets/HgiRefresh-DVzwwzGM.js +0 -1
- package/dist/browser/assets/TimestampHoverCard-_VKbn1Py.js +0 -1
- package/dist/browser/assets/activity-timeline-CiFzANgu.css +0 -1
- package/dist/browser/assets/activity-timeline-USDjdupU.js +0 -2
- package/dist/browser/assets/changelog-BwEMGKor.js +0 -2
- package/dist/browser/assets/commit-C0G_pkoH.js +0 -4
- package/dist/browser/assets/contributors-FNt-eYzY.js +0 -1
- package/dist/browser/assets/contributors.index-BMT_fvWV.js +0 -1
- package/dist/browser/assets/dashboard.index-IeAMhDp4.js +0 -1
- package/dist/browser/assets/document-BbpOg7j8.js +0 -1
- package/dist/browser/assets/document-DpkFXhPm.css +0 -1
- package/dist/browser/assets/features-D91CUmI3.css +0 -1
- package/dist/browser/assets/features-DUe_HTbz.js +0 -4
- package/dist/browser/assets/features._featureId-CPuJxCrI.js +0 -1
- package/dist/browser/assets/features.index-BqKMq6DZ.js +0 -1
- package/dist/browser/assets/getting-started-DG-Skl34.js +0 -1
- package/dist/browser/assets/git-B2XLYoEb.js +0 -1
- package/dist/browser/assets/index-BoqBsl1i.js +0 -48
- package/dist/browser/assets/instructions-CX6doP-p.css +0 -1
- package/dist/browser/assets/instructions-CmMSgO5v.js +0 -1
- package/dist/browser/assets/instructions._instructionId-Biq4h3Db.js +0 -1
- package/dist/browser/assets/instructions.agents-C1LXUm7K.js +0 -1
- package/dist/browser/assets/instructions.index-DCiLsKxg.js +0 -1
- package/dist/browser/assets/records-CiGcLzEi.css +0 -1
- package/dist/browser/assets/records-page-BSx-Bxmm.css +0 -1
- package/dist/browser/assets/records-page-D8qxMJNY.js +0 -1
- package/dist/browser/assets/records._recordId-B6sdT5ro.js +0 -1
- package/dist/browser/assets/records.commits._commit-goSqW9tl.js +0 -1
- package/dist/browser/assets/records.index-CxubA4G5.js +0 -1
- package/dist/browser/assets/related-list-CU-kUhYj.js +0 -2
- package/dist/browser/assets/related-list-Rp3yvN_G.css +0 -1
- package/dist/browser/assets/request-state-rD3dhp0I.js +0 -1
- package/dist/browser/assets/useCollapsible-D7UZAUy-.js +0 -1
package/dist/main.js
CHANGED
|
@@ -2427,11 +2427,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2427
2427
|
* @private
|
|
2428
2428
|
*/
|
|
2429
2429
|
_parseCommand(operands, unknown2) {
|
|
2430
|
-
const
|
|
2430
|
+
const parsed2 = this.parseOptions(unknown2);
|
|
2431
2431
|
this._parseOptionsEnv();
|
|
2432
2432
|
this._parseOptionsImplied();
|
|
2433
|
-
operands = operands.concat(
|
|
2434
|
-
unknown2 =
|
|
2433
|
+
operands = operands.concat(parsed2.operands);
|
|
2434
|
+
unknown2 = parsed2.unknown;
|
|
2435
2435
|
this.args = operands.concat(unknown2);
|
|
2436
2436
|
if (operands && this._findCommand(operands[0])) {
|
|
2437
2437
|
return this._dispatchSubcommand(operands[0], operands.slice(1), unknown2);
|
|
@@ -2450,12 +2450,12 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2450
2450
|
if (this.commands.length && this.args.length === 0 && !this._actionHandler && !this._defaultCommandName) {
|
|
2451
2451
|
this.help({ error: true });
|
|
2452
2452
|
}
|
|
2453
|
-
this._outputHelpIfRequested(
|
|
2453
|
+
this._outputHelpIfRequested(parsed2.unknown);
|
|
2454
2454
|
this._checkForMissingMandatoryOptions();
|
|
2455
2455
|
this._checkForConflictingOptions();
|
|
2456
2456
|
const checkForUnknownOptions = () => {
|
|
2457
|
-
if (
|
|
2458
|
-
this.unknownOption(
|
|
2457
|
+
if (parsed2.unknown.length > 0) {
|
|
2458
|
+
this.unknownOption(parsed2.unknown[0]);
|
|
2459
2459
|
}
|
|
2460
2460
|
};
|
|
2461
2461
|
const commandEvent = `command:${this.name()}`;
|
|
@@ -3381,7 +3381,7 @@ var program = new Command();
|
|
|
3381
3381
|
import { createServer } from "node:http";
|
|
3382
3382
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
3383
3383
|
import { fileURLToPath } from "node:url";
|
|
3384
|
-
import { readFile as
|
|
3384
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
3385
3385
|
|
|
3386
3386
|
// ../../packages/core/dist/domain/repository-status.js
|
|
3387
3387
|
function summarizeChanges(changes2) {
|
|
@@ -3659,16 +3659,16 @@ var RepositoryReadError = class extends Error {
|
|
|
3659
3659
|
// ../../packages/core/dist/use-cases/read-repository-status.js
|
|
3660
3660
|
async function readRepositoryStatus(reader, services) {
|
|
3661
3661
|
const startedAt = services.now();
|
|
3662
|
-
const
|
|
3662
|
+
const state2 = await reader.read();
|
|
3663
3663
|
return {
|
|
3664
|
-
...
|
|
3664
|
+
...state2,
|
|
3665
3665
|
observation: {
|
|
3666
3666
|
id: services.newId(),
|
|
3667
3667
|
startedAt,
|
|
3668
3668
|
completedAt: services.now(),
|
|
3669
3669
|
consistency: "best-effort"
|
|
3670
3670
|
},
|
|
3671
|
-
summary: summarizeChanges(
|
|
3671
|
+
summary: summarizeChanges(state2.changes)
|
|
3672
3672
|
};
|
|
3673
3673
|
}
|
|
3674
3674
|
|
|
@@ -3775,9 +3775,9 @@ function extractLinks(body2) {
|
|
|
3775
3775
|
fence = void 0;
|
|
3776
3776
|
continue;
|
|
3777
3777
|
}
|
|
3778
|
-
const
|
|
3779
|
-
if (
|
|
3780
|
-
fence = { char:
|
|
3778
|
+
const open6 = /^ {0,3}(`{3,}|~{3,})/.exec(line2);
|
|
3779
|
+
if (open6) {
|
|
3780
|
+
fence = { char: open6[1][0], size: open6[1].length };
|
|
3781
3781
|
continue;
|
|
3782
3782
|
}
|
|
3783
3783
|
for (const m of line2.replace(/`[^`]*`/g, "").matchAll(/!?\[[^\]]*\]\((?:<([^>]+)>|([^)\s]+))(?:\s+"[^"]*")?\)/g))
|
|
@@ -3925,9 +3925,9 @@ function parseDesign(source2, specId) {
|
|
|
3925
3925
|
fence = void 0;
|
|
3926
3926
|
return line2;
|
|
3927
3927
|
}
|
|
3928
|
-
const
|
|
3929
|
-
if (
|
|
3930
|
-
fence = { char:
|
|
3928
|
+
const open6 = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line2);
|
|
3929
|
+
if (open6) {
|
|
3930
|
+
fence = { char: open6[1][0], size: open6[1].length };
|
|
3931
3931
|
return line2;
|
|
3932
3932
|
}
|
|
3933
3933
|
if (/^#\s/.test(line2))
|
|
@@ -3970,9 +3970,9 @@ function parseDocument(path3, source2) {
|
|
|
3970
3970
|
fence = void 0;
|
|
3971
3971
|
continue;
|
|
3972
3972
|
}
|
|
3973
|
-
const
|
|
3974
|
-
if (
|
|
3975
|
-
fence = { char:
|
|
3973
|
+
const open6 = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line2);
|
|
3974
|
+
if (open6) {
|
|
3975
|
+
fence = { char: open6[1][0], size: open6[1].length };
|
|
3976
3976
|
continue;
|
|
3977
3977
|
}
|
|
3978
3978
|
if (/^#\s/.test(line2))
|
|
@@ -4404,14 +4404,14 @@ function sections(raw, path3) {
|
|
|
4404
4404
|
found.at(-1)?.lines.push(line2);
|
|
4405
4405
|
continue;
|
|
4406
4406
|
}
|
|
4407
|
-
const
|
|
4408
|
-
if (
|
|
4409
|
-
fence = { char:
|
|
4407
|
+
const open6 = /^ {0,3}(`{3,}|~{3,})/.exec(line2);
|
|
4408
|
+
if (open6)
|
|
4409
|
+
fence = { char: open6[1][0], size: open6[1].length };
|
|
4410
4410
|
else if (/^#\s/.test(line2))
|
|
4411
4411
|
throw new DocumentError("BODY_HEADING", path3, t("doc.BODY_HEADING", { path: path3 }));
|
|
4412
4412
|
else if (/<!-- gitifact-/.test(line2))
|
|
4413
4413
|
throw new DocumentError("BODY_MARKER", path3, t("doc.BODY_MARKER", { path: path3 }));
|
|
4414
|
-
const heading = !
|
|
4414
|
+
const heading = !open6 && /^##\s+(.+?)\s*#*\s*$/.exec(line2);
|
|
4415
4415
|
if (heading) {
|
|
4416
4416
|
const key = sectionKeyOf(heading[1]);
|
|
4417
4417
|
if (!key || found.some((s) => s.key === key))
|
|
@@ -4595,9 +4595,9 @@ function body(raw, path3) {
|
|
|
4595
4595
|
fence = void 0;
|
|
4596
4596
|
continue;
|
|
4597
4597
|
}
|
|
4598
|
-
const
|
|
4599
|
-
if (
|
|
4600
|
-
fence = { char:
|
|
4598
|
+
const open6 = /^ {0,3}(`{3,}|~{3,})/.exec(line2);
|
|
4599
|
+
if (open6) {
|
|
4600
|
+
fence = { char: open6[1][0], size: open6[1].length };
|
|
4601
4601
|
continue;
|
|
4602
4602
|
}
|
|
4603
4603
|
if (/^#\s/.test(line2))
|
|
@@ -4657,6 +4657,19 @@ function parseDocumentFile(path3, source2) {
|
|
|
4657
4657
|
}
|
|
4658
4658
|
return { kind: "design", feature: where.feature, order: order(), requirements: refs?.type === "list" ? refs.items : [], sources: sources(fields2, path3), ...common };
|
|
4659
4659
|
}
|
|
4660
|
+
var isInstructionReference = (path3) => path3.endsWith(".md");
|
|
4661
|
+
function parseInstructionFile(path3, source2) {
|
|
4662
|
+
if (source2.includes("\0") || /\r(?!\n)/.test(source2) || source2.charCodeAt(0) === 65279)
|
|
4663
|
+
throw new DocumentError("INVALID_CHARACTERS", path3, t("doc.INVALID_CHARACTERS", { path: path3 }));
|
|
4664
|
+
const { fields: fields2 } = parseFrontmatterBlock(source2.replace(/\r\n/g, "\n"), path3);
|
|
4665
|
+
for (const key of fields2.keys())
|
|
4666
|
+
if (key !== "title" && key !== "description")
|
|
4667
|
+
throw new DocumentError("FRONTMATTER_UNKNOWN_KEY", path3, t("doc.FRONTMATTER_UNKNOWN_KEY", { path: path3, key }));
|
|
4668
|
+
for (const key of ["title", "description"])
|
|
4669
|
+
if (!fields2.has(key))
|
|
4670
|
+
throw new DocumentError("FRONTMATTER_MISSING_KEY", path3, t("doc.FRONTMATTER_MISSING_KEY", { path: path3, key }));
|
|
4671
|
+
return { title: text(fields2, "title", path3, 200), description: text(fields2, "description", path3, 300) };
|
|
4672
|
+
}
|
|
4660
4673
|
function renderDocumentFile(doc) {
|
|
4661
4674
|
const front = renderFrontmatterBlock([
|
|
4662
4675
|
["id", doc.id],
|
|
@@ -4725,8 +4738,10 @@ function checkDocuments(files) {
|
|
|
4725
4738
|
}
|
|
4726
4739
|
if (where.type === "instruction-file") {
|
|
4727
4740
|
instructionFolders.set(where.name, instructionFolders.get(where.name) === true);
|
|
4728
|
-
if (path3
|
|
4741
|
+
if (isInstructionReference(path3)) {
|
|
4729
4742
|
specLinks(path3, source2);
|
|
4743
|
+
parseInstructionFile(path3, source2);
|
|
4744
|
+
}
|
|
4730
4745
|
continue;
|
|
4731
4746
|
}
|
|
4732
4747
|
if (where.kind === "instruction")
|
|
@@ -10387,11 +10402,11 @@ function settle(inst, answer) {
|
|
|
10387
10402
|
function isRecursiveSchema(inst) {
|
|
10388
10403
|
return isRecursive(inst, /* @__PURE__ */ new Set(), true) !== NONE;
|
|
10389
10404
|
}
|
|
10390
|
-
function bucketFor(
|
|
10391
|
-
let bucket =
|
|
10405
|
+
function bucketFor(state2, inst) {
|
|
10406
|
+
let bucket = state2.buckets.get(inst);
|
|
10392
10407
|
if (!bucket) {
|
|
10393
10408
|
bucket = /* @__PURE__ */ new WeakMap();
|
|
10394
|
-
|
|
10409
|
+
state2.buckets.set(inst, bucket);
|
|
10395
10410
|
}
|
|
10396
10411
|
return bucket;
|
|
10397
10412
|
}
|
|
@@ -10449,16 +10464,16 @@ var memo = {
|
|
|
10449
10464
|
const input2 = payload.value;
|
|
10450
10465
|
if (!isRef(input2))
|
|
10451
10466
|
return base(payload, ctx);
|
|
10452
|
-
let
|
|
10453
|
-
if (!
|
|
10454
|
-
|
|
10455
|
-
ctx[STATE] =
|
|
10467
|
+
let state2 = ctx[STATE];
|
|
10468
|
+
if (!state2) {
|
|
10469
|
+
state2 = { buckets: /* @__PURE__ */ new WeakMap(), backEdges: void 0 };
|
|
10470
|
+
ctx[STATE] = state2;
|
|
10456
10471
|
}
|
|
10457
10472
|
let bucket;
|
|
10458
10473
|
if (lastCtx === ctx) {
|
|
10459
10474
|
bucket = lastBucket;
|
|
10460
10475
|
} else {
|
|
10461
|
-
bucket = bucketFor(
|
|
10476
|
+
bucket = bucketFor(state2, inst);
|
|
10462
10477
|
lastCtx = ctx;
|
|
10463
10478
|
lastBucket = bucket;
|
|
10464
10479
|
}
|
|
@@ -10470,8 +10485,8 @@ var memo = {
|
|
|
10470
10485
|
payload.issues.push(...cloneIssues(hit.issues));
|
|
10471
10486
|
} else {
|
|
10472
10487
|
payload.memo = true;
|
|
10473
|
-
|
|
10474
|
-
|
|
10488
|
+
state2.backEdges ?? (state2.backEdges = /* @__PURE__ */ new WeakSet());
|
|
10489
|
+
state2.backEdges.add(hit.value);
|
|
10475
10490
|
}
|
|
10476
10491
|
return payload;
|
|
10477
10492
|
}
|
|
@@ -25003,9 +25018,11 @@ var browserHttpErrorV1 = external_exports.strictObject({
|
|
|
25003
25018
|
});
|
|
25004
25019
|
|
|
25005
25020
|
// ../../packages/contracts/dist/versions/browser-specs-v6.js
|
|
25021
|
+
var state = external_exports.enum(["committed", "added", "modified", "deleted"]);
|
|
25022
|
+
var standing = { state, previousPath: external_exports.string().optional() };
|
|
25006
25023
|
var contributor = external_exports.strictObject({ email: external_exports.string(), name: external_exports.string(), commits: external_exports.number().int().nonnegative(), latest: external_exports.string() });
|
|
25007
25024
|
var source = external_exports.strictObject({ id: external_exports.string().optional(), title: external_exports.string().optional(), path: external_exports.string().optional(), url: external_exports.string().optional(), note: external_exports.string().optional() });
|
|
25008
|
-
var requirement = external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string(), description: external_exports.string(), order: external_exports.number().int().nonnegative(), body: external_exports.string() });
|
|
25025
|
+
var requirement = external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string(), description: external_exports.string(), order: external_exports.number().int().nonnegative(), body: external_exports.string(), ...standing });
|
|
25009
25026
|
var design = external_exports.strictObject({
|
|
25010
25027
|
id: external_exports.string(),
|
|
25011
25028
|
path: external_exports.string(),
|
|
@@ -25014,7 +25031,8 @@ var design = external_exports.strictObject({
|
|
|
25014
25031
|
order: external_exports.number().int().nonnegative(),
|
|
25015
25032
|
body: external_exports.string(),
|
|
25016
25033
|
requirements: external_exports.array(external_exports.string()),
|
|
25017
|
-
sources: external_exports.array(source)
|
|
25034
|
+
sources: external_exports.array(source),
|
|
25035
|
+
...standing
|
|
25018
25036
|
});
|
|
25019
25037
|
var feature = external_exports.strictObject({
|
|
25020
25038
|
id: external_exports.string(),
|
|
@@ -25022,13 +25040,14 @@ var feature = external_exports.strictObject({
|
|
|
25022
25040
|
title: external_exports.string(),
|
|
25023
25041
|
description: external_exports.string(),
|
|
25024
25042
|
body: external_exports.string(),
|
|
25043
|
+
...standing,
|
|
25025
25044
|
requirements: external_exports.array(requirement),
|
|
25026
25045
|
designs: external_exports.array(design),
|
|
25027
25046
|
// Authors of commits touching the feature folder and the latest such commit; empty until first committed.
|
|
25028
25047
|
contributors: external_exports.array(contributor),
|
|
25029
25048
|
updatedAt: external_exports.string().nullable()
|
|
25030
25049
|
});
|
|
25031
|
-
var instructionFile = external_exports.strictObject({ path: external_exports.string(), size: external_exports.number().int().nonnegative() });
|
|
25050
|
+
var instructionFile = external_exports.strictObject({ path: external_exports.string(), size: external_exports.number().int().nonnegative(), title: external_exports.string().optional(), description: external_exports.string().optional() });
|
|
25032
25051
|
var instruction = external_exports.strictObject({
|
|
25033
25052
|
id: external_exports.string(),
|
|
25034
25053
|
name: external_exports.string(),
|
|
@@ -25036,6 +25055,7 @@ var instruction = external_exports.strictObject({
|
|
|
25036
25055
|
title: external_exports.string(),
|
|
25037
25056
|
description: external_exports.string(),
|
|
25038
25057
|
body: external_exports.string(),
|
|
25058
|
+
...standing,
|
|
25039
25059
|
// Paths relative to the instruction folder, index.md left out; `limited` when the folder holds more than the list carries.
|
|
25040
25060
|
files: external_exports.array(instructionFile),
|
|
25041
25061
|
filesLimited: external_exports.boolean(),
|
|
@@ -25044,13 +25064,14 @@ var instruction = external_exports.strictObject({
|
|
|
25044
25064
|
var agents = external_exports.strictObject({ path: external_exports.string(), body: external_exports.string(), updatedAt: external_exports.string().nullable() });
|
|
25045
25065
|
var problem = external_exports.strictObject({ code: external_exports.string(), path: external_exports.string(), message: external_exports.string() });
|
|
25046
25066
|
var oid2 = external_exports.string().regex(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/);
|
|
25047
|
-
var
|
|
25067
|
+
var browserSpecsV7 = external_exports.strictObject({
|
|
25048
25068
|
contract: external_exports.literal("browser-specs"),
|
|
25049
|
-
version: external_exports.literal(
|
|
25069
|
+
version: external_exports.literal(7),
|
|
25050
25070
|
sessionId: external_exports.string(),
|
|
25051
25071
|
head: oid2.nullable(),
|
|
25052
25072
|
observedAt: external_exports.string(),
|
|
25053
25073
|
working: external_exports.boolean(),
|
|
25074
|
+
stamp: external_exports.string(),
|
|
25054
25075
|
features: external_exports.array(feature),
|
|
25055
25076
|
instructions: external_exports.array(instruction),
|
|
25056
25077
|
agents: agents.nullable(),
|
|
@@ -25090,14 +25111,15 @@ var event = external_exports.strictObject({
|
|
|
25090
25111
|
after: reference,
|
|
25091
25112
|
records: external_exports.array(record2)
|
|
25092
25113
|
});
|
|
25093
|
-
var
|
|
25114
|
+
var browserHistoryV6 = external_exports.strictObject({
|
|
25094
25115
|
contract: external_exports.literal("browser-history"),
|
|
25095
|
-
version: external_exports.literal(
|
|
25116
|
+
version: external_exports.literal(6),
|
|
25096
25117
|
sessionId: external_exports.string(),
|
|
25097
25118
|
head: oid2,
|
|
25098
|
-
// Matching changes in all of history, not in this page
|
|
25119
|
+
// Matching changes and the commits that hold them in all of history, not in this page.
|
|
25099
25120
|
total: external_exports.number().int().nonnegative(),
|
|
25100
|
-
|
|
25121
|
+
commits: external_exports.number().int().nonnegative(),
|
|
25122
|
+
next: oid2.nullable(),
|
|
25101
25123
|
events: external_exports.array(event)
|
|
25102
25124
|
});
|
|
25103
25125
|
var browserHistorySummaryV4 = external_exports.strictObject({
|
|
@@ -25131,10 +25153,10 @@ var browserSearchV2 = external_exports.strictObject({
|
|
|
25131
25153
|
});
|
|
25132
25154
|
var headQuery = oid2;
|
|
25133
25155
|
var count = (max) => external_exports.string().regex(/^(0|[1-9]\d{0,6})$/).transform(Number).pipe(external_exports.number().int().min(0).max(max));
|
|
25134
|
-
var
|
|
25156
|
+
var browserHistoryQueryV4 = external_exports.strictObject({
|
|
25135
25157
|
head: headQuery,
|
|
25136
|
-
|
|
25137
|
-
limit: count(
|
|
25158
|
+
after: oid2.optional(),
|
|
25159
|
+
limit: count(50).pipe(external_exports.number().min(1)).optional(),
|
|
25138
25160
|
kind: changeType.optional(),
|
|
25139
25161
|
document: kind.optional(),
|
|
25140
25162
|
feature: external_exports.string().regex(/^S-[a-z2-7]{10}$/).optional(),
|
|
@@ -25176,9 +25198,9 @@ var browserCommitFileV1 = external_exports.strictObject({
|
|
|
25176
25198
|
});
|
|
25177
25199
|
var browserCommitFilesQueryV1 = external_exports.strictObject({ commit: oid2 });
|
|
25178
25200
|
var browserCommitFileQueryV1 = external_exports.strictObject({ commit: oid2, path: external_exports.string().min(1).max(4096) });
|
|
25179
|
-
var
|
|
25201
|
+
var browserCommitV4 = external_exports.strictObject({
|
|
25180
25202
|
contract: external_exports.literal("browser-commit"),
|
|
25181
|
-
version: external_exports.literal(
|
|
25203
|
+
version: external_exports.literal(4),
|
|
25182
25204
|
sessionId: external_exports.string(),
|
|
25183
25205
|
commit: oid2,
|
|
25184
25206
|
author: external_exports.string(),
|
|
@@ -25186,9 +25208,21 @@ var browserCommitV3 = external_exports.strictObject({
|
|
|
25186
25208
|
committer: external_exports.string(),
|
|
25187
25209
|
date: external_exports.string(),
|
|
25188
25210
|
message: external_exports.string(),
|
|
25189
|
-
|
|
25211
|
+
total: external_exports.number().int().nonnegative(),
|
|
25212
|
+
next: external_exports.string().nullable(),
|
|
25213
|
+
changes: external_exports.array(event)
|
|
25214
|
+
});
|
|
25215
|
+
var browserCommitQueryV2 = external_exports.strictObject({ commit: oid2, after: external_exports.string().min(1).max(200).optional(), limit: count(500).pipe(external_exports.number().min(1)).optional() });
|
|
25216
|
+
var browserCommitChangeV1 = external_exports.strictObject({
|
|
25217
|
+
contract: external_exports.literal("browser-commit-change"),
|
|
25218
|
+
version: external_exports.literal(1),
|
|
25219
|
+
sessionId: external_exports.string(),
|
|
25220
|
+
commit: oid2,
|
|
25221
|
+
event,
|
|
25222
|
+
before: snapshot,
|
|
25223
|
+
after: snapshot
|
|
25190
25224
|
});
|
|
25191
|
-
var
|
|
25225
|
+
var browserCommitChangeQueryV1 = external_exports.strictObject({ commit: oid2, id: external_exports.string().min(1).max(100) });
|
|
25192
25226
|
var recordId = external_exports.string().regex(/^(?:DR|H)-[A-Za-z0-9_-]{1,64}$/);
|
|
25193
25227
|
var browserRecordV1 = external_exports.strictObject({
|
|
25194
25228
|
contract: external_exports.literal("browser-record"),
|
|
@@ -25210,6 +25244,27 @@ var browserInstructionFileV1 = external_exports.strictObject({
|
|
|
25210
25244
|
binary: external_exports.boolean(),
|
|
25211
25245
|
tooLarge: external_exports.boolean()
|
|
25212
25246
|
});
|
|
25247
|
+
var workingChange = external_exports.strictObject({ id: external_exports.string(), kind, title: external_exports.string(), path: external_exports.string(), previousPath: external_exports.string().optional(), types: external_exports.array(changeType) });
|
|
25248
|
+
var pendingRecord = external_exports.strictObject({ id: external_exports.string(), title: external_exports.string(), docs: external_exports.array(external_exports.string()), sections: external_exports.array(recordSection), draft: external_exports.boolean() });
|
|
25249
|
+
var browserWorkingV1 = external_exports.strictObject({
|
|
25250
|
+
contract: external_exports.literal("browser-working"),
|
|
25251
|
+
version: external_exports.literal(1),
|
|
25252
|
+
sessionId: external_exports.string(),
|
|
25253
|
+
head: oid2.nullable(),
|
|
25254
|
+
records: external_exports.array(pendingRecord),
|
|
25255
|
+
changes: external_exports.array(workingChange),
|
|
25256
|
+
withoutRecord: external_exports.array(external_exports.string())
|
|
25257
|
+
});
|
|
25258
|
+
var browserWorkingChangeV1 = external_exports.strictObject({
|
|
25259
|
+
contract: external_exports.literal("browser-working-change"),
|
|
25260
|
+
version: external_exports.literal(1),
|
|
25261
|
+
sessionId: external_exports.string(),
|
|
25262
|
+
change: workingChange,
|
|
25263
|
+
before: snapshot,
|
|
25264
|
+
after: snapshot
|
|
25265
|
+
});
|
|
25266
|
+
var browserWorkingChangeQueryV1 = external_exports.strictObject({ id: external_exports.string().regex(/^[SRDWI]-[a-z2-7]{10}$/) });
|
|
25267
|
+
var browserStampV1 = external_exports.strictObject({ contract: external_exports.literal("browser-stamp"), version: external_exports.literal(1), sessionId: external_exports.string(), stamp: external_exports.string() });
|
|
25213
25268
|
|
|
25214
25269
|
// ../../packages/contracts/dist/versions/changelog-v1.js
|
|
25215
25270
|
var items = external_exports.array(external_exports.string().min(1));
|
|
@@ -25243,11 +25298,11 @@ var updateStateV1 = external_exports.strictObject({
|
|
|
25243
25298
|
latestVersion: release.nullable()
|
|
25244
25299
|
}).refine((value) => (value.status === "available" || value.status === "up-to-date") === (value.latestVersion !== null), "latestVersion is set exactly when the check produced a result.");
|
|
25245
25300
|
|
|
25246
|
-
// ../../packages/contracts/dist/versions/project-init-
|
|
25247
|
-
var
|
|
25301
|
+
// ../../packages/contracts/dist/versions/project-init-v8.js
|
|
25302
|
+
var projectInitV8 = external_exports.discriminatedUnion("ok", [
|
|
25248
25303
|
external_exports.strictObject({
|
|
25249
25304
|
contract: external_exports.literal("project-init"),
|
|
25250
|
-
version: external_exports.literal(
|
|
25305
|
+
version: external_exports.literal(8),
|
|
25251
25306
|
ok: external_exports.literal(true),
|
|
25252
25307
|
outcome: external_exports.enum(["planned", "created", "already-initialized"]),
|
|
25253
25308
|
rootPath: external_exports.string(),
|
|
@@ -25258,11 +25313,13 @@ var projectInitV7 = external_exports.discriminatedUnion("ok", [
|
|
|
25258
25313
|
external_exports.strictObject({ kind: external_exports.literal("commit"), objectFormat: external_exports.enum(["sha1", "sha256"]), commit: external_exports.string() })
|
|
25259
25314
|
]),
|
|
25260
25315
|
agentDocs: external_exports.strictObject({ mode: external_exports.enum(["install", "remove", "skip"]), paths: external_exports.array(external_exports.string()) }),
|
|
25316
|
+
// The file that keeps documents in LF on every OS. `created` is false when it was already there or the run is planned.
|
|
25317
|
+
lineEndings: external_exports.strictObject({ path: external_exports.literal(".gitifact/.gitattributes"), created: external_exports.boolean() }),
|
|
25261
25318
|
update: updateStateV1,
|
|
25262
25319
|
// Present only when a newer release exists. npx refreshes the project blocks without a global install; npmGlobal remains an alternative.
|
|
25263
25320
|
install: external_exports.strictObject({ npx: external_exports.string().min(1), npmGlobal: external_exports.string().min(1) }).nullable()
|
|
25264
25321
|
}),
|
|
25265
|
-
external_exports.strictObject({ contract: external_exports.literal("project-init"), version: external_exports.literal(
|
|
25322
|
+
external_exports.strictObject({ contract: external_exports.literal("project-init"), version: external_exports.literal(8), ok: external_exports.literal(false), error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }) })
|
|
25266
25323
|
]);
|
|
25267
25324
|
|
|
25268
25325
|
// ../../packages/contracts/dist/versions/update-v5.js
|
|
@@ -25448,6 +25505,8 @@ var messages_default3 = {
|
|
|
25448
25505
|
"server.sessionChanged": "\uC11C\uBC84 \uC138\uC158\uC774 \uBCC0\uACBD\uB410\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC5F0\uACB0\uD558\uC138\uC694.",
|
|
25449
25506
|
"server.badRange": "\uC870\uD68C \uBC94\uC704\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
25450
25507
|
"server.commitNotFound": "\uC774 \uC800\uC7A5\uC18C\uC5D0 \uADF8 \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25508
|
+
"server.cursorNotFound": "\uC774\uC5B4 \uC77D\uC744 \uC704\uCE58\uB97C \uBAA9\uB85D\uC5D0\uC11C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uCC98\uC74C\uBD80\uD130 \uB2E4\uC2DC \uBD88\uB7EC\uC624\uC138\uC694.",
|
|
25509
|
+
"server.workingChangeNotFound": "\uCEE4\uBC0B \uC804 \uBCC0\uACBD\uC5D0 \uADF8 \uBB38\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25451
25510
|
"server.recordNotFound": "\uC774 \uC774\uB825\uC5D0 \uADF8 \uACB0\uC815\uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25452
25511
|
"server.commitFileNotFound": "\uADF8 \uCEE4\uBC0B\uC774 \uBC14\uAFBC \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4.",
|
|
25453
25512
|
"server.instructionFileNotFound": "\uC9C0\uCE68 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
@@ -25491,6 +25550,7 @@ var messages_default3 = {
|
|
|
25491
25550
|
"init.text.blockUpdated": "\uBE14\uB85D \uAC31\uC2E0",
|
|
25492
25551
|
"init.text.storage": "\uC800\uC7A5 \uADDC\uC57D",
|
|
25493
25552
|
"init.text.agentDocs": "\uC5D0\uC774\uC804\uD2B8 \uC9C0\uCE68",
|
|
25553
|
+
"init.text.lineEndings": "\uC904\uBC14\uAFC8: {path}\uB85C \uBB38\uC11C\uB97C LF\uB85C \uACE0\uC815\uD588\uC2B5\uB2C8\uB2E4. \uC774\uBBF8 \uC788\uB358 \uBB38\uC11C\uAC00 \uBCC0\uACBD\uC73C\uB85C \uBCF4\uC774\uBA74 \uC904\uBC14\uAFC8\uB9CC \uBC14\uB010 \uAC83\uC774\uB2C8 \uADF8 \uBB38\uC11C\uB4E4\uC744 \uD55C \uBC88 \uCEE4\uBC0B\uD574 \uC815\uB9AC\uD558\uC138\uC694.",
|
|
25494
25554
|
"init.text.updateAvailable": "\uC0C8 \uBC84\uC804 {version}\uC774 \uC788\uC2B5\uB2C8\uB2E4. {command}\uB85C \uC0C8 \uBC84\uC804\uC744 \uC2E4\uD589\uD558\uACE0 \uD504\uB85C\uC81D\uD2B8 \uC9C0\uCE68\uC744 \uAC31\uC2E0\uD558\uC138\uC694.",
|
|
25495
25555
|
"init.failed": "\uCD08\uAE30\uD654\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uD30C\uC77C \uC811\uADFC \uAD8C\uD55C\uACFC \uC0DD\uC131\uB41C \uC124\uC815\xB7\uC784\uC2DC \uD30C\uC77C\uC744 \uD655\uC778\uD558\uC138\uC694.",
|
|
25496
25556
|
"config.storeNotDirectory": ".gitifact\uB294 \uB9C1\uD06C\uAC00 \uC544\uB2CC \uC77C\uBC18 \uB514\uB809\uD130\uB9AC\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
|
|
@@ -25534,7 +25594,7 @@ var messages_default3 = {
|
|
|
25534
25594
|
"update.text.noBlock": "GITIFACT \uBE14\uB85D\uC774 \uC788\uB294 \uC9C0\uCE68 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. npx --yes gitifact@{version} init\uC73C\uB85C \uC124\uCE58\uD558\uC138\uC694.",
|
|
25535
25595
|
"update.text.notInitialized": "\uCD08\uAE30\uD654\uB41C \uD504\uB85C\uC81D\uD2B8\uAC00 \uC544\uB2C8\uC5B4\uC11C \uBC84\uC804\uB9CC \uD655\uC778\uD588\uC2B5\uB2C8\uB2E4.",
|
|
25536
25596
|
"update.text.missing": "{paths} \uD30C\uC77C\uC774 \uC5C6\uC5B4 Claude Code\uAC00 AGENTS.md\uC758 \uC9C0\uCE68\uC744 \uC77D\uC9C0 \uBABB\uD569\uB2C8\uB2E4. npx --yes gitifact@{version} init\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 AGENTS.md\uB97C \uAC00\uC838\uC624\uB294 \uD30C\uC77C\uC744 \uB9CC\uB4ED\uB2C8\uB2E4.",
|
|
25537
|
-
"update.text.migrationRequired": "\uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uBB38\uC11C\uB294 \uC544\uC9C1 0.7 \uC800\uC7A5 \uADDC\uC57D(schemaVersion 2)\uC785\uB2C8\uB2E4. \uC9C0\uCE68 \uBE14\uB85D\uC740 \uC0C8 \uBA85\uB839\uC744 \uC548\uB0B4\uD558\uC9C0\uB9CC \uC804\uD658 \uC804\uC5D0\uB294
|
|
25597
|
+
"update.text.migrationRequired": "\uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uBB38\uC11C\uB294 \uC544\uC9C1 0.7 \uC800\uC7A5 \uADDC\uC57D(schemaVersion 2)\uC785\uB2C8\uB2E4. \uC9C0\uCE68 \uBE14\uB85D\uC740 \uC0C8 \uBA85\uB839\uC744 \uC548\uB0B4\uD558\uC9C0\uB9CC \uC804\uD658 \uC804\uC5D0\uB294 specs\xB7instructions\xB7records\xB7check\xB7changes \uBA85\uB839\uC774 \uB3D9\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. npx --yes gitifact@{version} guide show migrate\uC758 \uC808\uCC28\uB85C \uBB38\uC11C\uB97C \uC804\uD658\uD558\uC138\uC694.",
|
|
25538
25598
|
"update.text.committed": "\uC9C0\uCE68 \uBE14\uB85D \uCEE4\uBC0B: {commit} ({paths})",
|
|
25539
25599
|
"update.text.commitNothing": "\uCEE4\uBC0B\uD560 \uC9C0\uCE68 \uBE14\uB85D \uBCC0\uACBD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25540
25600
|
"update.text.commitSkipped.untracked": "\uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. Git\uC774 \uCD94\uC801\uD558\uC9C0 \uC54A\uB294 \uC9C0\uCE68 \uD30C\uC77C\uC785\uB2C8\uB2E4: {paths}",
|
|
@@ -25561,6 +25621,9 @@ var messages_default3 = {
|
|
|
25561
25621
|
"input.size": "\uC785\uB825 \uD06C\uAE30\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.",
|
|
25562
25622
|
"input.json": "\uC785\uB825 \uD30C\uC77C\uC740 UTF-8 JSON\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
|
|
25563
25623
|
"docs.draft": "\uCD08\uC548",
|
|
25624
|
+
"docs.state.added": "\uCEE4\uBC0B \uC804: \uCD94\uAC00",
|
|
25625
|
+
"docs.state.modified": "\uCEE4\uBC0B \uC804: \uBCC0\uACBD",
|
|
25626
|
+
"docs.state.deleted": "\uCEE4\uBC0B \uC804: \uC0AD\uC81C \uC608\uC815",
|
|
25564
25627
|
"docs.feature": "\uAE30\uB2A5",
|
|
25565
25628
|
"docs.requirements": "\uC694\uAD6C\uC0AC\uD56D",
|
|
25566
25629
|
"docs.designs": "\uC124\uACC4",
|
|
@@ -25644,7 +25707,7 @@ var messages_default3 = {
|
|
|
25644
25707
|
"help.newTitle": "\uBB38\uC11C \uC81C\uBAA9",
|
|
25645
25708
|
"help.newDescription": "\uD55C \uC904 \uC124\uBA85",
|
|
25646
25709
|
"help.instructions": "\uD504\uB85C\uC81D\uD2B8 \uC9C0\uCE68\uC758 \uBAA9\uB85D\xB7\uC6D0\uBB38\xB7\uC0DD\uC131",
|
|
25647
|
-
"help.instructionsList": "AGENTS.md\uC640 \uC9C0\uCE68 \uBAA9\uB85D (\uC9C0\uCE68\uB9C8\uB2E4 \uB538\uB9B0 \uD30C\uC77C \
|
|
25710
|
+
"help.instructionsList": "AGENTS.md\uC640 \uC9C0\uCE68 \uBAA9\uB85D (\uC9C0\uCE68\uB9C8\uB2E4 \uB538\uB9B0 \uD30C\uC77C\uC758 \uC81C\uBAA9\uACFC \uC124\uBA85)",
|
|
25648
25711
|
"help.instructionsShow": "\uC9C0\uCE68 index.md \uC6D0\uBB38, \uB530\uB974\uB294 \uC124\uACC4, \uD3F4\uB354 \uD30C\uC77C \uBAA9\uB85D",
|
|
25649
25712
|
"help.instructionsShowTargets": "\uC9C0\uCE68 \uD3F4\uB354 \uC774\uB984\uC774\uB098 I- ID (\uC5EC\uB7EC \uAC1C \uAC00\uB2A5)",
|
|
25650
25713
|
"help.instructionsShowFile": "\uC9C0\uCE68 \uD3F4\uB354 \uC548\uC758 \uD30C\uC77C \uD558\uB098\uB97C \uC77D\uC74C (\uC608: references/code-style.md)",
|
|
@@ -25657,18 +25720,26 @@ var messages_default3 = {
|
|
|
25657
25720
|
"help.listQ": "\uC81C\uBAA9\xB7\uC124\uBA85\xB7\uBCF8\uBB38\uC5D0 \uC774 \uB9D0\uC774 \uC788\uB294 \uAC83\uB9CC",
|
|
25658
25721
|
"help.listAuthor": "\uC774 Git \uC791\uC131\uC790(\uC774\uB984\uC774\uB098 \uC774\uBA54\uC77C)\uAC00 \uBC14\uAFBC \uAC83\uB9CC",
|
|
25659
25722
|
"help.listSort": "\uC815\uB82C \uAE30\uC900",
|
|
25660
|
-
"help.listLimit": "\
|
|
25723
|
+
"help.listLimit": "\uD55C \uD398\uC774\uC9C0\uC5D0 n\uAC1C (\uAE30\uBCF8 20)",
|
|
25724
|
+
"help.listAll": "\uD398\uC774\uC9C0\uB85C \uB098\uB204\uC9C0 \uC54A\uACE0 \uC804\uBD80",
|
|
25725
|
+
"help.listAfter": "\uC774 \uD56D\uBAA9 \uB2E4\uC74C\uBD80\uD130 (\uC55E \uD398\uC774\uC9C0 \uB05D\uC5D0 \uB098\uC628 \uAC12)",
|
|
25661
25726
|
"help.listFields": "\uC774 \uC5F4\uB9CC \uC27C\uD45C\uB85C (\uD14D\uC2A4\uD2B8\uB294 \uD0ED\uC73C\uB85C \uB098\uB208 \uD55C \uC904)",
|
|
25662
25727
|
"list.limit": "1 \uC774\uC0C1\uC758 \uC815\uC218\uB97C \uC801\uC73C\uC138\uC694.",
|
|
25663
25728
|
"list.unknownFields": "\uC5C6\uB294 \uC5F4\uC785\uB2C8\uB2E4: {fields}. \uC4F8 \uC218 \uC788\uB294 \uC5F4: {columns}",
|
|
25664
25729
|
"list.since": "\uB0A0\uC9DC(YYYY-MM-DD)\uB098 \uCEE4\uBC0B\uC774 \uC544\uB2D9\uB2C8\uB2E4: {value}",
|
|
25730
|
+
"list.after": "\uBAA9\uB85D\uC5D0 \uC5C6\uB294 \uCEE4\uC11C\uC785\uB2C8\uB2E4: {cursor}. --after \uC5C6\uC774 \uCC98\uC74C\uBD80\uD130 \uB2E4\uC2DC \uC77D\uC73C\uC138\uC694.",
|
|
25731
|
+
"list.more": "{unit} {shown}/{total}\uAC1C. \uB2E4\uC74C \uD398\uC774\uC9C0: \uAC19\uC740 \uBA85\uB839\uC5D0 --after {cursor}",
|
|
25732
|
+
"list.items": "\uD56D\uBAA9",
|
|
25733
|
+
"list.features": "\uAE30\uB2A5",
|
|
25734
|
+
"list.commits": "\uCEE4\uBC0B",
|
|
25735
|
+
"list.records": "\uACB0\uC815\uAE30\uB85D",
|
|
25665
25736
|
"docs.otherResource": "\uC774 \uBA85\uB839\uC774 \uB2E4\uB8E8\uB294 \uBB38\uC11C\uAC00 \uC544\uB2D9\uB2C8\uB2E4: {ids}. {command}\uB85C \uC77D\uC73C\uC138\uC694.",
|
|
25666
25737
|
"docs.noMatch": "\uC870\uAC74\uC5D0 \uB9DE\uB294 \uAC83\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25667
25738
|
"docs.requirement": "\uC694\uAD6C\uC0AC\uD56D",
|
|
25668
25739
|
"docs.design": "\uC124\uACC4",
|
|
25669
25740
|
"instructions.agents": "AGENTS.md \u2014 \uBE14\uB85D \uBC16 \uC0C9\uC778\uC774 \uC5B4\uB5A4 \uC791\uC5C5\uC5D0 \uC5B4\uB290 \uC9C0\uCE68\uC744 \uC77D\uC744\uC9C0 \uC54C\uB9B0\uB2E4",
|
|
25670
25741
|
"instructions.noAgents": "AGENTS.md \uC5C6\uC74C \u2014 \uC9C0\uCE68\uC744 \uC5B8\uC81C \uC77D\uC744\uC9C0 \uC54C\uB9AC\uB294 \uC0C9\uC778\uC774 \uC5C6\uC2B5\uB2C8\uB2E4",
|
|
25671
|
-
"instructions.
|
|
25742
|
+
"instructions.filesLimited": "(\uD30C\uC77C\uC774 \uB354 \uC788\uC5B4 \uC55E {count}\uAC1C\uB9CC \uBCF4\uC785\uB2C8\uB2E4)",
|
|
25672
25743
|
"instructions.empty": "\uC9C0\uCE68\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. instructions new\uB85C \uB9CC\uB4DC\uC138\uC694.",
|
|
25673
25744
|
"instructions.fileWithOne": "--file\uC740 \uC9C0\uCE68 \uD558\uB098\uC640 \uD568\uAED8 \uC4F0\uBA70 --ref\uC640 \uD568\uAED8 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25674
25745
|
"instructions.unknownFile": "\uC9C0\uCE68 {name} \uD3F4\uB354\uC5D0 \uADF8 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: {path}",
|
|
@@ -25738,6 +25809,8 @@ var messages_default4 = {
|
|
|
25738
25809
|
"server.sessionChanged": "The server session changed. Reconnect to continue.",
|
|
25739
25810
|
"server.badRange": "Check the requested range.",
|
|
25740
25811
|
"server.commitNotFound": "This repository has no such commit.",
|
|
25812
|
+
"server.cursorNotFound": "The place to read on from is no longer in the list. Load it again from the start.",
|
|
25813
|
+
"server.workingChangeNotFound": "No uncommitted change of that document.",
|
|
25741
25814
|
"server.recordNotFound": "This history has no such decision record.",
|
|
25742
25815
|
"server.commitFileNotFound": "That commit did not change this file.",
|
|
25743
25816
|
"server.instructionFileNotFound": "The instruction file was not found.",
|
|
@@ -25781,6 +25854,7 @@ var messages_default4 = {
|
|
|
25781
25854
|
"init.text.blockUpdated": "Block refreshed",
|
|
25782
25855
|
"init.text.storage": "Storage convention",
|
|
25783
25856
|
"init.text.agentDocs": "Agent instructions",
|
|
25857
|
+
"init.text.lineEndings": "Line endings: {path} keeps documents in LF. Documents that now show as changed differ only in line endings; commit them once to settle it.",
|
|
25784
25858
|
"init.text.updateAvailable": "Version {version} is available. Run {command} to use it and refresh the project instructions.",
|
|
25785
25859
|
"init.failed": "Initialization failed. Check file permissions and the generated configuration and temporary files.",
|
|
25786
25860
|
"config.storeNotDirectory": ".gitifact must be a regular directory, not a link.",
|
|
@@ -25824,7 +25898,7 @@ var messages_default4 = {
|
|
|
25824
25898
|
"update.text.noBlock": "No instruction files contain a GITIFACT block. Run npx --yes gitifact@{version} init to install one.",
|
|
25825
25899
|
"update.text.notInitialized": "Only the version was checked because this project is not initialized.",
|
|
25826
25900
|
"update.text.missing": "{paths} is missing, so Claude Code cannot load the AGENTS.md instructions. Rerun npx --yes gitifact@{version} init to create the import file.",
|
|
25827
|
-
"update.text.migrationRequired": "This project's documents still use the 0.7 storage convention (schemaVersion 2). The blocks describe the new commands, but
|
|
25901
|
+
"update.text.migrationRequired": "This project's documents still use the 0.7 storage convention (schemaVersion 2). The blocks describe the new commands, but the specs, instructions, records, check and changes commands refuse to run until the documents are moved. Follow npx --yes gitifact@{version} guide show migrate to move them.",
|
|
25828
25902
|
"update.text.committed": "Instruction blocks committed: {commit} ({paths})",
|
|
25829
25903
|
"update.text.commitNothing": "No instruction block changes to commit.",
|
|
25830
25904
|
"update.text.commitSkipped.untracked": "Not committed. Instruction files are untracked: {paths}",
|
|
@@ -25851,6 +25925,9 @@ var messages_default4 = {
|
|
|
25851
25925
|
"input.size": "The input is too large.",
|
|
25852
25926
|
"input.json": "The input file must be UTF-8 JSON.",
|
|
25853
25927
|
"docs.draft": "draft",
|
|
25928
|
+
"docs.state.added": "uncommitted: added",
|
|
25929
|
+
"docs.state.modified": "uncommitted: modified",
|
|
25930
|
+
"docs.state.deleted": "uncommitted: to be deleted",
|
|
25854
25931
|
"docs.feature": "Feature",
|
|
25855
25932
|
"docs.requirements": "Requirements",
|
|
25856
25933
|
"docs.designs": "Design",
|
|
@@ -25934,7 +26011,7 @@ var messages_default4 = {
|
|
|
25934
26011
|
"help.newTitle": "Document title",
|
|
25935
26012
|
"help.newDescription": "One-line description",
|
|
25936
26013
|
"help.instructions": "List, read and create project instructions",
|
|
25937
|
-
"help.instructionsList": "AGENTS.md and the instructions (with
|
|
26014
|
+
"help.instructionsList": "AGENTS.md and the instructions (with the title and description of each file in their folders)",
|
|
25938
26015
|
"help.instructionsShow": "An instruction's index.md as written, the designs that follow it and its folder's files",
|
|
25939
26016
|
"help.instructionsShowTargets": "Instruction folder names or I- IDs (one or more)",
|
|
25940
26017
|
"help.instructionsShowFile": "Read one file of the instruction folder (e.g. references/code-style.md)",
|
|
@@ -25947,18 +26024,26 @@ var messages_default4 = {
|
|
|
25947
26024
|
"help.listQ": "Only those whose title, description or text holds these words",
|
|
25948
26025
|
"help.listAuthor": "Only those this Git author (name or email) changed",
|
|
25949
26026
|
"help.listSort": "Sort key",
|
|
25950
|
-
"help.listLimit": "
|
|
26027
|
+
"help.listLimit": "n per page (20 by default)",
|
|
26028
|
+
"help.listAll": "Everything, not split into pages",
|
|
26029
|
+
"help.listAfter": "Start after this item (the value at the end of the previous page)",
|
|
25951
26030
|
"help.listFields": "Only these columns, comma-separated (text prints one tab-separated line each)",
|
|
25952
26031
|
"list.limit": "Give a whole number of 1 or more.",
|
|
25953
26032
|
"list.unknownFields": "Unknown columns: {fields}. Available columns: {columns}",
|
|
25954
26033
|
"list.since": "Not a date (YYYY-MM-DD) or a commit: {value}",
|
|
26034
|
+
"list.after": "No such cursor in the list: {cursor}. Read again from the start without --after.",
|
|
26035
|
+
"list.more": "{unit}: {shown} of {total}. Next page: the same command with --after {cursor}",
|
|
26036
|
+
"list.items": "Items",
|
|
26037
|
+
"list.features": "Features",
|
|
26038
|
+
"list.commits": "Commits",
|
|
26039
|
+
"list.records": "Records",
|
|
25955
26040
|
"docs.otherResource": "Not a document this command reads: {ids}. Use {command}.",
|
|
25956
26041
|
"docs.noMatch": "Nothing matches.",
|
|
25957
26042
|
"docs.requirement": "requirement",
|
|
25958
26043
|
"docs.design": "design",
|
|
25959
26044
|
"instructions.agents": "AGENTS.md \u2014 the index outside the block says which instruction to read for which work",
|
|
25960
26045
|
"instructions.noAgents": "No AGENTS.md \u2014 nothing tells when to read which instruction",
|
|
25961
|
-
"instructions.
|
|
26046
|
+
"instructions.filesLimited": "(the folder holds more; only the first {count} are shown)",
|
|
25962
26047
|
"instructions.empty": "No instructions yet. Create one with instructions new.",
|
|
25963
26048
|
"instructions.fileWithOne": "--file takes one instruction and does not go with --ref.",
|
|
25964
26049
|
"instructions.unknownFile": "The folder of instruction {name} has no such file: {path}",
|
|
@@ -26069,9 +26154,9 @@ async function dispatch(routes, sessionId, url2, path3, request, response, allow
|
|
|
26069
26154
|
if (found.query) {
|
|
26070
26155
|
const keys2 = [...url2.searchParams.keys()];
|
|
26071
26156
|
if (new Set(keys2).size !== keys2.length) return fail2(response, 400, "BAD_REQUEST", t2("server.badRange"));
|
|
26072
|
-
const
|
|
26073
|
-
if (!
|
|
26074
|
-
query =
|
|
26157
|
+
const parsed2 = found.query.safeParse(Object.fromEntries(url2.searchParams));
|
|
26158
|
+
if (!parsed2.success) return fail2(response, 400, "BAD_REQUEST", t2("server.badRange"));
|
|
26159
|
+
query = parsed2.data;
|
|
26075
26160
|
} else if (url2.search) return fail2(response, 400, "BAD_REQUEST", t2("server.noQuery"));
|
|
26076
26161
|
if (found.session && request.headers["x-gitifact-session"] !== sessionId) return fail2(response, 409, "SESSION_CHANGED", t2("server.sessionChanged"));
|
|
26077
26162
|
try {
|
|
@@ -26329,7 +26414,7 @@ function storeReader(cwd) {
|
|
|
26329
26414
|
import { DatabaseSync } from "node:sqlite";
|
|
26330
26415
|
import { lstat, mkdir, rm, writeFile } from "node:fs/promises";
|
|
26331
26416
|
import { join as join2 } from "node:path";
|
|
26332
|
-
var CACHE_FORMAT =
|
|
26417
|
+
var CACHE_FORMAT = 9;
|
|
26333
26418
|
var CACHE_DIR = ".gitifact/cache";
|
|
26334
26419
|
var SCHEMA = `
|
|
26335
26420
|
-- Working-tree files already read: the modification time and size decide whether a file is read again.
|
|
@@ -26344,13 +26429,17 @@ var SCHEMA = `
|
|
|
26344
26429
|
CREATE INDEX doc_references_to ON doc_references (to_id);
|
|
26345
26430
|
-- Commits already read, and with which parser: 'current', 'legacy' (0.7, before a migration) or 'migration' (hidden).
|
|
26346
26431
|
CREATE TABLE commits (oid TEXT PRIMARY KEY, reader TEXT NOT NULL);
|
|
26347
|
-
-- One row per change. 'row' is the list shape as JSON; 'detail'
|
|
26432
|
+
-- One row per change. 'row' is the list shape as JSON with its records by ID; 'detail' where each side's text is read
|
|
26433
|
+
-- again from Git (commit and path), or the text itself for 0.7 history, which has no files to read again.
|
|
26348
26434
|
CREATE TABLE changes (
|
|
26349
26435
|
key TEXT PRIMARY KEY, oid TEXT NOT NULL, ord INTEGER NOT NULL,
|
|
26350
26436
|
id TEXT NOT NULL, kind TEXT NOT NULL, types TEXT NOT NULL, email TEXT NOT NULL, date TEXT NOT NULL,
|
|
26351
26437
|
before_spec TEXT, after_spec TEXT, needle TEXT NOT NULL, row TEXT NOT NULL, detail TEXT NOT NULL);
|
|
26352
26438
|
CREATE INDEX changes_by_commit ON changes (oid, ord);
|
|
26353
26439
|
CREATE INDEX changes_by_id ON changes (id);
|
|
26440
|
+
-- The records each commit added, once each; change rows name them by ID.
|
|
26441
|
+
CREATE TABLE records (oid TEXT NOT NULL, id TEXT NOT NULL, title TEXT NOT NULL, sections TEXT NOT NULL, PRIMARY KEY (oid, id));
|
|
26442
|
+
CREATE INDEX records_by_id ON records (id);
|
|
26354
26443
|
-- All reachable record commits of one HEAD, children before parents (pos 0 is newest).
|
|
26355
26444
|
CREATE TABLE lineage (head TEXT NOT NULL, pos INTEGER NOT NULL, oid TEXT NOT NULL, PRIMARY KEY (head, pos));
|
|
26356
26445
|
CREATE INDEX lineage_by_commit ON lineage (head, oid);
|
|
@@ -26377,6 +26466,10 @@ function prepare(db) {
|
|
|
26377
26466
|
db.exec("ROLLBACK");
|
|
26378
26467
|
throw error62;
|
|
26379
26468
|
}
|
|
26469
|
+
try {
|
|
26470
|
+
db.exec("VACUUM");
|
|
26471
|
+
} catch {
|
|
26472
|
+
}
|
|
26380
26473
|
}
|
|
26381
26474
|
function createCacheDatabase(root) {
|
|
26382
26475
|
let path3;
|
|
@@ -26472,24 +26565,23 @@ var place = (doc) => doc.path.replace(/^\.gitifact\//, "");
|
|
|
26472
26565
|
function createDocumentCache(root, database) {
|
|
26473
26566
|
let syncing;
|
|
26474
26567
|
async function walk() {
|
|
26475
|
-
const found =
|
|
26568
|
+
const found = [];
|
|
26476
26569
|
async function visit2(relative, depth) {
|
|
26477
26570
|
const info5 = await lstat2(join3(root, ...relative.split("/"))).catch((error62) => {
|
|
26478
26571
|
if (error62.code === "ENOENT") return void 0;
|
|
26479
26572
|
throw error62;
|
|
26480
26573
|
});
|
|
26481
|
-
if (!info5 || found.
|
|
26574
|
+
if (!info5 || found.length >= 2 * COUNT_LIMIT) return;
|
|
26482
26575
|
if (info5.isDirectory() && !info5.isSymbolicLink()) {
|
|
26483
26576
|
if (depth > 10) return;
|
|
26484
|
-
|
|
26577
|
+
await Promise.all((await readdir2(join3(root, ...relative.split("/")))).map((name2) => visit2(relative + "/" + name2, depth + 1)));
|
|
26485
26578
|
return;
|
|
26486
26579
|
}
|
|
26487
|
-
found.
|
|
26580
|
+
found.push([relative, { mtime: info5.mtimeMs, size: info5.size, link: info5.isSymbolicLink() || !info5.isFile() }]);
|
|
26488
26581
|
}
|
|
26489
|
-
await visit2(SPEC_ROOT, 0);
|
|
26490
|
-
|
|
26491
|
-
|
|
26492
|
-
return found;
|
|
26582
|
+
await Promise.all([visit2(SPEC_ROOT, 0), visit2(WIKI_ROOT, 0), visit2(INSTRUCTIONS_ROOT, 0)]);
|
|
26583
|
+
found.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
26584
|
+
return new Map(found.slice(0, COUNT_LIMIT));
|
|
26493
26585
|
}
|
|
26494
26586
|
async function read(path3, seen) {
|
|
26495
26587
|
try {
|
|
@@ -26513,7 +26605,7 @@ function createDocumentCache(root, database) {
|
|
|
26513
26605
|
});
|
|
26514
26606
|
const gone = [...known.keys()].filter((path3) => !seen.has(path3));
|
|
26515
26607
|
if (!changed.length && !gone.length) return;
|
|
26516
|
-
const
|
|
26608
|
+
const parsed2 = await Promise.all(changed.map(async ([path3, s]) => ({ path: path3, seen: s, ...await read(path3, s) })));
|
|
26517
26609
|
await database.with((db) => transaction(db, () => {
|
|
26518
26610
|
const forget = (path3) => {
|
|
26519
26611
|
const row = db.prepare("SELECT id FROM documents WHERE path = ?").get(path3);
|
|
@@ -26530,7 +26622,7 @@ function createDocumentCache(root, database) {
|
|
|
26530
26622
|
const document = db.prepare("INSERT INTO documents (path, id, kind, feature, title, description, ord, doc) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
|
26531
26623
|
const reference2 = db.prepare("INSERT INTO doc_references (from_id, to_id, type) VALUES (?, ?, ?)");
|
|
26532
26624
|
const search = db.prepare("INSERT INTO search (scope, kind, ref, oid, payload, title, place, body) VALUES (?,?,?,?,?,?,?,?)");
|
|
26533
|
-
for (const p of
|
|
26625
|
+
for (const p of parsed2) {
|
|
26534
26626
|
forget(p.path);
|
|
26535
26627
|
file2.run(p.path, p.seen.mtime, p.seen.size, p.problem ? JSON.stringify(p.problem) : null);
|
|
26536
26628
|
const doc = p.doc;
|
|
@@ -26589,28 +26681,23 @@ function createDocumentCache(root, database) {
|
|
|
26589
26681
|
throw error62;
|
|
26590
26682
|
});
|
|
26591
26683
|
if (history) seen.set(HISTORY_PATH, { mtime: history.mtimeMs, size: history.size, link: history.isSymbolicLink() || !history.isFile() });
|
|
26592
|
-
const
|
|
26593
|
-
|
|
26594
|
-
|
|
26595
|
-
if (
|
|
26596
|
-
if (
|
|
26597
|
-
files.set(path3, "");
|
|
26598
|
-
continue;
|
|
26599
|
-
}
|
|
26600
|
-
if (s.link) {
|
|
26601
|
-
problems.push(docProblem("PATH_UNSUPPORTED", path3));
|
|
26602
|
-
continue;
|
|
26603
|
-
}
|
|
26604
|
-
if (s.size > (path3 === HISTORY_PATH ? 64 * FILE_LIMIT : FILE_LIMIT)) {
|
|
26605
|
-
problems.push(docProblem("FILE_TOO_LARGE", path3));
|
|
26606
|
-
continue;
|
|
26607
|
-
}
|
|
26684
|
+
const read2 = await Promise.all([...seen].map(async ([path3, s]) => {
|
|
26685
|
+
if (!readable(path3)) return { path: path3 };
|
|
26686
|
+
if (instructionFile3(path3) && !path3.endsWith(".md")) return { path: path3, text: "" };
|
|
26687
|
+
if (s.link) return { path: path3, problem: docProblem("PATH_UNSUPPORTED", path3) };
|
|
26688
|
+
if (s.size > (path3 === HISTORY_PATH ? 64 * FILE_LIMIT : FILE_LIMIT)) return { path: path3, problem: docProblem("FILE_TOO_LARGE", path3) };
|
|
26608
26689
|
try {
|
|
26609
|
-
|
|
26690
|
+
return { path: path3, text: decode3(await readFile2(join3(root, ...path3.split("/")))) };
|
|
26610
26691
|
} catch (error62) {
|
|
26611
|
-
if (error62 instanceof TypeError)
|
|
26612
|
-
|
|
26692
|
+
if (error62 instanceof TypeError) return { path: path3, problem: docProblem("INVALID_CHARACTERS", path3) };
|
|
26693
|
+
throw error62;
|
|
26613
26694
|
}
|
|
26695
|
+
}));
|
|
26696
|
+
const files = /* @__PURE__ */ new Map();
|
|
26697
|
+
const problems = [];
|
|
26698
|
+
for (const r of read2) {
|
|
26699
|
+
if (r.text !== void 0) files.set(r.path, r.text);
|
|
26700
|
+
if (r.problem) problems.push(r.problem);
|
|
26614
26701
|
}
|
|
26615
26702
|
return { files, problems };
|
|
26616
26703
|
},
|
|
@@ -26866,7 +26953,8 @@ function createCommitChanges(git) {
|
|
|
26866
26953
|
types: change2.types,
|
|
26867
26954
|
before: change2.before,
|
|
26868
26955
|
after: change2.after,
|
|
26869
|
-
records: recordsFor(records2, change2.id)
|
|
26956
|
+
records: recordsFor(records2, change2.id),
|
|
26957
|
+
source: { before: c.parent ?? null, after: c.commit }
|
|
26870
26958
|
}));
|
|
26871
26959
|
const newReasons = (before, after) => [...after.reasons.values()].filter((r) => before.every((b) => !b.reasons.has(r.id))).map((r) => reasonRecord(r.id, r.docs, r.reason));
|
|
26872
26960
|
const recordsAt = (blobs, rev, paths2) => paths2.flatMap((path3) => {
|
|
@@ -26951,6 +27039,21 @@ function createCommitChanges(git) {
|
|
|
26951
27039
|
return {
|
|
26952
27040
|
/** Every document and reason file of a commit (path → text): what `specs show --ref` and `changes` compare against. */
|
|
26953
27041
|
tree,
|
|
27042
|
+
/**
|
|
27043
|
+
* Documents read again at the commits and paths a change was read at, by one `cat-file --batch`: the text a
|
|
27044
|
+
* change's detail shows, which the cache does not keep. A place that no longer reads is null.
|
|
27045
|
+
*/
|
|
27046
|
+
async sides(places) {
|
|
27047
|
+
const blobs = await readBlobs([...new Set(places.map((p) => p.rev + ":" + p.path))]);
|
|
27048
|
+
return places.map((p) => {
|
|
27049
|
+
const text4 = blobs.get(p.rev + ":" + p.path);
|
|
27050
|
+
try {
|
|
27051
|
+
return text4 === void 0 ? null : snapshot3(parseDocumentFile(p.path, text4), p.specId);
|
|
27052
|
+
} catch {
|
|
27053
|
+
return null;
|
|
27054
|
+
}
|
|
27055
|
+
});
|
|
27056
|
+
},
|
|
26954
27057
|
/** All reachable record commits, children before parents even when author clocks differ. */
|
|
26955
27058
|
async lineage(head) {
|
|
26956
27059
|
return git.decode(await git.run(["rev-list", "--full-history", "--date-order", head, "--", ...HISTORY_PATHSPECS])).split("\n").filter(Boolean);
|
|
@@ -26989,8 +27092,10 @@ function createCommitChanges(git) {
|
|
|
26989
27092
|
// src/adapters/cache/history.ts
|
|
26990
27093
|
var listed = (e) => {
|
|
26991
27094
|
const reference2 = (s) => s ? { id: s.id, title: s.title, specId: s.specId, path: s.path } : null;
|
|
26992
|
-
|
|
27095
|
+
const { source: _source, ...event2 } = e;
|
|
27096
|
+
return { ...event2, before: reference2(e.before), after: reference2(e.after) };
|
|
26993
27097
|
};
|
|
27098
|
+
var sideOf3 = (snapshot5, rev) => !snapshot5 ? null : rev ? { rev, path: snapshot5.path } : snapshot5;
|
|
26994
27099
|
var titleOf = (e) => (e.after ?? e.before)?.title ?? e.id;
|
|
26995
27100
|
var DAY = 864e5;
|
|
26996
27101
|
var KEPT_HEADS = 4;
|
|
@@ -27003,11 +27108,15 @@ function createHistory(database, git) {
|
|
|
27003
27108
|
function insert(db, commits, readers) {
|
|
27004
27109
|
const commit = db.prepare("INSERT OR IGNORE INTO commits (oid, reader) VALUES (?, ?)");
|
|
27005
27110
|
const change2 = db.prepare("INSERT OR IGNORE INTO changes (key, oid, ord, id, kind, types, email, date, before_spec, after_spec, needle, row, detail) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)");
|
|
27111
|
+
const record4 = db.prepare("INSERT OR IGNORE INTO records (oid, id, title, sections) VALUES (?, ?, ?, ?)");
|
|
27006
27112
|
const search = db.prepare("INSERT INTO search (scope, kind, ref, oid, payload, title, place, body) VALUES (?,?,?,?,?,?,?,?)");
|
|
27007
27113
|
transaction(db, () => {
|
|
27008
27114
|
for (const c of commits) {
|
|
27009
27115
|
if (!Number(commit.run(c.commit, readers.get(c.commit) ?? "current").changes)) continue;
|
|
27010
27116
|
c.events.forEach((e, ord) => {
|
|
27117
|
+
for (const r of e.records) record4.run(e.commit, r.id, r.title, JSON.stringify(r.sections));
|
|
27118
|
+
const row = { ...listed(e), records: e.records.map((r) => r.id) };
|
|
27119
|
+
const detail = { before: sideOf3(e.before, e.source?.before), after: sideOf3(e.after, e.source?.after) };
|
|
27011
27120
|
change2.run(
|
|
27012
27121
|
e.key,
|
|
27013
27122
|
e.commit,
|
|
@@ -27020,8 +27129,8 @@ function createHistory(database, git) {
|
|
|
27020
27129
|
e.before?.specId ?? null,
|
|
27021
27130
|
e.after?.specId ?? null,
|
|
27022
27131
|
[e.id, e.before?.title ?? "", e.after?.title ?? ""].join(" ").toLowerCase(),
|
|
27023
|
-
JSON.stringify(
|
|
27024
|
-
JSON.stringify(
|
|
27132
|
+
JSON.stringify(row),
|
|
27133
|
+
JSON.stringify(detail)
|
|
27025
27134
|
);
|
|
27026
27135
|
const line2 = e.records.length ? e.records.map((r) => [r.title, ...r.sections.map((s) => s.body)].join(" ")).join(" \xB7 ") : e.message;
|
|
27027
27136
|
const where2 = e.commit.slice(0, 7) + " \xB7 " + e.author;
|
|
@@ -27048,6 +27157,7 @@ function createHistory(database, git) {
|
|
|
27048
27157
|
for (const oid3 of stale) {
|
|
27049
27158
|
db.prepare("DELETE FROM commits WHERE oid = ?").run(oid3);
|
|
27050
27159
|
db.prepare("DELETE FROM changes WHERE oid = ?").run(oid3);
|
|
27160
|
+
db.prepare("DELETE FROM records WHERE oid = ?").run(oid3);
|
|
27051
27161
|
db.prepare("DELETE FROM search WHERE scope = 'history' AND oid = ?").run(oid3);
|
|
27052
27162
|
}
|
|
27053
27163
|
}));
|
|
@@ -27078,6 +27188,39 @@ function createHistory(database, git) {
|
|
|
27078
27188
|
}
|
|
27079
27189
|
return pending;
|
|
27080
27190
|
}
|
|
27191
|
+
async function readCommit(commit) {
|
|
27192
|
+
const known = await database.with((db) => !!db.prepare("SELECT 1 FROM commits WHERE oid = ?").get(commit));
|
|
27193
|
+
if (known) return;
|
|
27194
|
+
const read = await changes2.of([commit]).catch(() => void 0);
|
|
27195
|
+
if (read) await database.with((db) => insert(db, read, /* @__PURE__ */ new Map()));
|
|
27196
|
+
}
|
|
27197
|
+
function hydrate(db, rows) {
|
|
27198
|
+
const events = rows.map((row) => JSON.parse(row));
|
|
27199
|
+
const read = db.prepare("SELECT id, title, sections FROM records WHERE oid = ?");
|
|
27200
|
+
const byCommit = /* @__PURE__ */ new Map();
|
|
27201
|
+
for (const e of events) if (e.records.length && !byCommit.has(e.commit)) {
|
|
27202
|
+
byCommit.set(e.commit, new Map(read.all(e.commit).map((r) => [r.id, { id: r.id, title: r.title, sections: JSON.parse(r.sections) }])));
|
|
27203
|
+
}
|
|
27204
|
+
return events.map((e) => ({ ...e, records: e.records.flatMap((id) => {
|
|
27205
|
+
const r = byCommit.get(e.commit)?.get(id);
|
|
27206
|
+
return r ? [r] : [];
|
|
27207
|
+
}) }));
|
|
27208
|
+
}
|
|
27209
|
+
async function detailOf(rows) {
|
|
27210
|
+
const events = await database.with((db) => hydrate(db, rows.map((r) => r.row)));
|
|
27211
|
+
const sides = rows.map((r) => JSON.parse(r.detail));
|
|
27212
|
+
const places = [];
|
|
27213
|
+
sides.forEach((side, i) => {
|
|
27214
|
+
for (const key of ["before", "after"]) {
|
|
27215
|
+
const value = side[key];
|
|
27216
|
+
if (value && "rev" in value) places.push({ rev: value.rev, path: value.path, specId: events[i][key]?.specId ?? "" });
|
|
27217
|
+
}
|
|
27218
|
+
});
|
|
27219
|
+
const read = places.length ? await changes2.sides(places) : [];
|
|
27220
|
+
const text4 = new Map(places.map((p, i) => [p.rev + ":" + p.path, read[i] ?? null]));
|
|
27221
|
+
const resolve = (value) => !value ? null : "rev" in value ? text4.get(value.rev + ":" + value.path) ?? null : value;
|
|
27222
|
+
return events.map((event2, i) => ({ event: event2, before: resolve(sides[i].before), after: resolve(sides[i].after) }));
|
|
27223
|
+
}
|
|
27081
27224
|
function where(filter) {
|
|
27082
27225
|
const clauses = [];
|
|
27083
27226
|
const params = [];
|
|
@@ -27107,21 +27250,55 @@ function createHistory(database, git) {
|
|
|
27107
27250
|
ensure,
|
|
27108
27251
|
/** The document and reason files of one commit, read from Git and not kept. */
|
|
27109
27252
|
filesAt: (rev) => changes2.tree(rev),
|
|
27110
|
-
/**
|
|
27111
|
-
|
|
27253
|
+
/**
|
|
27254
|
+
* The changes that match, newest first, a page of whole commits: `limit` commits after the commit `after`, each
|
|
27255
|
+
* with all of its matching changes, and how many changes and commits match in all. `next` is the last commit of the
|
|
27256
|
+
* page when more follow. A commit `after` that is not in `head`'s matching history answers undefined.
|
|
27257
|
+
*/
|
|
27258
|
+
async commits(head, filter, after, limit) {
|
|
27112
27259
|
await ensure(head);
|
|
27113
27260
|
return database.with((db) => {
|
|
27114
27261
|
const { sql, params } = where(filter);
|
|
27115
27262
|
const from = "FROM lineage l JOIN changes c ON c.oid = l.oid WHERE l.head = ?" + sql;
|
|
27116
|
-
const
|
|
27117
|
-
|
|
27118
|
-
|
|
27263
|
+
const counted = db.prepare("SELECT count(*) AS n, count(DISTINCT c.oid) AS commits " + from).get(head, ...params);
|
|
27264
|
+
let start = -1;
|
|
27265
|
+
if (after !== void 0) {
|
|
27266
|
+
const found = db.prepare("SELECT l.pos AS pos " + from + " AND l.oid = ? LIMIT 1").get(head, ...params, after);
|
|
27267
|
+
if (!found) return void 0;
|
|
27268
|
+
start = found.pos;
|
|
27269
|
+
}
|
|
27270
|
+
const commits = db.prepare(`SELECT l.pos AS pos, l.oid AS oid ${from} AND l.pos > ? GROUP BY l.pos, l.oid ORDER BY l.pos LIMIT ?`).all(head, ...params, start, limit + 1);
|
|
27271
|
+
const shown = commits.slice(0, limit);
|
|
27272
|
+
const rows = !shown.length ? [] : db.prepare(`SELECT c.row ${from} AND l.pos >= ? AND l.pos <= ? ORDER BY l.pos, c.ord`).all(head, ...params, shown[0].pos, shown[shown.length - 1].pos);
|
|
27273
|
+
return {
|
|
27274
|
+
total: Number(counted.n),
|
|
27275
|
+
commits: Number(counted.commits),
|
|
27276
|
+
next: commits.length > limit ? shown[shown.length - 1].oid : null,
|
|
27277
|
+
events: hydrate(db, rows.map((r) => r.row))
|
|
27278
|
+
};
|
|
27279
|
+
});
|
|
27280
|
+
},
|
|
27281
|
+
/** The changes of one commit without their text, a page at a time after the change `after` (a key): the commit page's list. */
|
|
27282
|
+
async commitChanges(commit, after, limit) {
|
|
27283
|
+
await readCommit(commit);
|
|
27284
|
+
return database.with((db) => {
|
|
27285
|
+
const all = db.prepare("SELECT key, row FROM changes WHERE oid = ? ORDER BY ord").all(commit);
|
|
27286
|
+
const start = after === void 0 ? 0 : all.findIndex((r) => r.key === after) + 1;
|
|
27287
|
+
if (after !== void 0 && !start) return void 0;
|
|
27288
|
+
const shown = all.slice(start, start + limit);
|
|
27289
|
+
return { total: all.length, next: start + shown.length < all.length ? shown[shown.length - 1].key : null, events: hydrate(db, shown.map((r) => r.row)) };
|
|
27119
27290
|
});
|
|
27120
27291
|
},
|
|
27292
|
+
/** One change of a commit with the text on both sides, read again from Git: the change the reader opened. */
|
|
27293
|
+
async commitChange(commit, id) {
|
|
27294
|
+
await readCommit(commit);
|
|
27295
|
+
const rows = await database.with((db) => db.prepare("SELECT row, detail FROM changes WHERE oid = ? AND id = ? ORDER BY ord LIMIT 1").all(commit, id));
|
|
27296
|
+
return rows.length ? (await detailOf(rows))[0] : void 0;
|
|
27297
|
+
},
|
|
27121
27298
|
/** Every change of one document, newest first: `records list --doc`. */
|
|
27122
27299
|
async ofDocument(head, id) {
|
|
27123
27300
|
await ensure(head);
|
|
27124
|
-
return database.with((db) => db.prepare("SELECT c.row FROM lineage l JOIN changes c ON c.oid = l.oid WHERE l.head = ? AND c.id = ? ORDER BY l.pos, c.ord").all(head, id).map((r) =>
|
|
27301
|
+
return database.with((db) => hydrate(db, db.prepare("SELECT c.row FROM lineage l JOIN changes c ON c.oid = l.oid WHERE l.head = ? AND c.id = ? ORDER BY l.pos, c.ord").all(head, id).map((r) => r.row)));
|
|
27125
27302
|
},
|
|
27126
27303
|
/** Who changed which document in which commit of `head`'s history, newest first: what the lists filter and sort by. */
|
|
27127
27304
|
async changesOf(head) {
|
|
@@ -27130,25 +27307,60 @@ function createHistory(database, git) {
|
|
|
27130
27307
|
FROM lineage l JOIN changes c ON c.oid = l.oid WHERE l.head = ? ORDER BY l.pos, c.ord`).all(head));
|
|
27131
27308
|
},
|
|
27132
27309
|
/**
|
|
27133
|
-
*
|
|
27134
|
-
*
|
|
27310
|
+
* The records of `head`'s history that match, newest first, a page at a time: what `records list` shows after the
|
|
27311
|
+
* records not committed yet. The filters and the page are SQL over the records table, so a page reads only its
|
|
27312
|
+
* rows. `after` is the ID of the last record of the previous page; `limit` undefined is every match.
|
|
27135
27313
|
*/
|
|
27136
|
-
async
|
|
27314
|
+
async records(head, filter, after, limit) {
|
|
27137
27315
|
await ensure(head);
|
|
27138
|
-
|
|
27139
|
-
|
|
27140
|
-
|
|
27141
|
-
|
|
27142
|
-
|
|
27143
|
-
|
|
27144
|
-
const known = records2.get(record4.id);
|
|
27145
|
-
if (known) {
|
|
27146
|
-
if (known.commit === row.commit && !known.docs.includes(row.doc)) known.docs.push(row.doc);
|
|
27147
|
-
continue;
|
|
27316
|
+
return database.with((db) => {
|
|
27317
|
+
const clauses = [];
|
|
27318
|
+
const params = [];
|
|
27319
|
+
if (filter.author) {
|
|
27320
|
+
clauses.push("(lower(json_extract(row, '$.author')) = ? OR lower(json_extract(row, '$.email')) = ?)");
|
|
27321
|
+
params.push(filter.author.toLowerCase(), filter.author.toLowerCase());
|
|
27148
27322
|
}
|
|
27149
|
-
|
|
27150
|
-
|
|
27151
|
-
|
|
27323
|
+
if (filter.q?.trim()) {
|
|
27324
|
+
clauses.push("instr(lower(title || ' ' || body || ' ' || json_extract(row, '$.message')), ?) > 0");
|
|
27325
|
+
params.push(filter.q.trim().toLowerCase());
|
|
27326
|
+
}
|
|
27327
|
+
if (filter.from !== void 0) {
|
|
27328
|
+
clauses.push("unixepoch(json_extract(row, '$.date')) * 1000 >= ?");
|
|
27329
|
+
params.push(filter.from);
|
|
27330
|
+
}
|
|
27331
|
+
if (filter.commits) {
|
|
27332
|
+
clauses.push("oid IN (SELECT value FROM json_each(?))");
|
|
27333
|
+
params.push(JSON.stringify(filter.commits));
|
|
27334
|
+
}
|
|
27335
|
+
const matching = `WITH meta AS (SELECT l.pos AS pos, r.oid AS oid, r.id AS id, r.title AS title, r.sections AS sections,
|
|
27336
|
+
(SELECT group_concat(json_extract(s.value, '$.body'), ' ') FROM json_each(r.sections) s) AS body,
|
|
27337
|
+
(SELECT c.row FROM changes c WHERE c.oid = r.oid ORDER BY c.ord LIMIT 1) AS row
|
|
27338
|
+
FROM lineage l JOIN records r ON r.oid = l.oid WHERE l.head = ?)
|
|
27339
|
+
SELECT * FROM meta WHERE 1 = 1${clauses.map((c) => " AND " + c).join("")}`;
|
|
27340
|
+
const total = Number(db.prepare(`SELECT count(*) AS n FROM (${matching})`).get(head, ...params).n);
|
|
27341
|
+
let from;
|
|
27342
|
+
if (after !== void 0) {
|
|
27343
|
+
from = db.prepare(`SELECT pos, id FROM (${matching}) WHERE id = ?`).get(head, ...params, after);
|
|
27344
|
+
if (!from) return void 0;
|
|
27345
|
+
}
|
|
27346
|
+
const rows = db.prepare(`SELECT oid, id, title, sections, row FROM (${matching})${from ? " WHERE (pos, id) > (?, ?)" : ""} ORDER BY pos, id LIMIT ?`).all(head, ...params, ...from ? [from.pos, from.id] : [], limit ?? -1);
|
|
27347
|
+
const docsOf = db.prepare("SELECT c.id AS id FROM changes c, json_each(c.row, '$.records') j WHERE c.oid = ? AND j.value = ? ORDER BY c.ord");
|
|
27348
|
+
const records2 = rows.map((r) => {
|
|
27349
|
+
const commit = JSON.parse(r.row);
|
|
27350
|
+
return {
|
|
27351
|
+
id: r.id,
|
|
27352
|
+
title: r.title,
|
|
27353
|
+
sections: JSON.parse(r.sections),
|
|
27354
|
+
commit: r.oid,
|
|
27355
|
+
date: commit.date,
|
|
27356
|
+
author: commit.author,
|
|
27357
|
+
email: commit.email,
|
|
27358
|
+
message: commit.message,
|
|
27359
|
+
docs: docsOf.all(r.oid, r.id).map((d) => d.id)
|
|
27360
|
+
};
|
|
27361
|
+
});
|
|
27362
|
+
return { records: records2, total };
|
|
27363
|
+
});
|
|
27152
27364
|
},
|
|
27153
27365
|
/** Counts over all of history and its newest commits, for the overview. */
|
|
27154
27366
|
async summary(head) {
|
|
@@ -27163,7 +27375,7 @@ function createHistory(database, git) {
|
|
|
27163
27375
|
const recent = commits.slice(0, RECENT_COMMITS).map((c) => ({
|
|
27164
27376
|
commit: c.oid,
|
|
27165
27377
|
count: Number(c.n),
|
|
27166
|
-
events: db.prepare("SELECT row FROM changes WHERE oid = ? ORDER BY ord LIMIT ?").all(c.oid, RECENT_CHANGES).map((r) =>
|
|
27378
|
+
events: hydrate(db, db.prepare("SELECT row FROM changes WHERE oid = ? ORDER BY ord LIMIT ?").all(c.oid, RECENT_CHANGES).map((r) => r.row))
|
|
27167
27379
|
}));
|
|
27168
27380
|
return { total, byType: { created: count2("created"), modified: count2("modified"), moved: count2("moved"), deleted: count2("deleted") }, pulse, recent };
|
|
27169
27381
|
});
|
|
@@ -27171,19 +27383,7 @@ function createHistory(database, git) {
|
|
|
27171
27383
|
/** The commit of `head`'s history that added a record, found through the records its changes carry. */
|
|
27172
27384
|
async commitOfRecord(head, id) {
|
|
27173
27385
|
await ensure(head);
|
|
27174
|
-
return database.with((db) => db.prepare(
|
|
27175
|
-
WHERE l.head = ? AND json_extract(r.value, '$.id') = ? ORDER BY l.pos LIMIT 1`).get(head, id)?.oid);
|
|
27176
|
-
},
|
|
27177
|
-
/** Every change of one commit with the text on both sides: what the commit page reads. */
|
|
27178
|
-
async ofCommit(commit) {
|
|
27179
|
-
const rows = await database.with((db) => db.prepare("SELECT row, detail FROM changes WHERE oid = ? ORDER BY ord").all(commit));
|
|
27180
|
-
if (rows.length) return rows.map((r) => ({ event: JSON.parse(r.row), ...JSON.parse(r.detail) }));
|
|
27181
|
-
const known = await database.with((db) => !!db.prepare("SELECT 1 FROM commits WHERE oid = ?").get(commit));
|
|
27182
|
-
if (known) return [];
|
|
27183
|
-
const read = await changes2.of([commit]).catch(() => void 0);
|
|
27184
|
-
if (!read) return [];
|
|
27185
|
-
await database.with((db) => insert(db, read, /* @__PURE__ */ new Map()));
|
|
27186
|
-
return database.with((db) => db.prepare("SELECT row, detail FROM changes WHERE oid = ? ORDER BY ord").all(commit).map((r) => ({ event: JSON.parse(r.row), ...JSON.parse(r.detail) })));
|
|
27386
|
+
return database.with((db) => db.prepare("SELECT l.oid AS oid FROM lineage l JOIN records r ON r.oid = l.oid WHERE l.head = ? AND r.id = ? ORDER BY l.pos LIMIT 1").get(head, id)?.oid);
|
|
27187
27387
|
},
|
|
27188
27388
|
/**
|
|
27189
27389
|
* Documents whose title, place or text holds the query — a title match before a place match before a text match —
|
|
@@ -27242,28 +27442,89 @@ function openCache(root, git) {
|
|
|
27242
27442
|
};
|
|
27243
27443
|
}
|
|
27244
27444
|
|
|
27245
|
-
// src/adapters/
|
|
27246
|
-
import { lstat as lstat3,
|
|
27445
|
+
// src/adapters/git/pending-records.ts
|
|
27446
|
+
import { lstat as lstat3, readdir as readdir3, readFile as readFile3 } from "node:fs/promises";
|
|
27247
27447
|
import { join as join4 } from "node:path";
|
|
27448
|
+
var decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
27449
|
+
var info = (path3) => lstat3(path3).catch((error62) => {
|
|
27450
|
+
if (error62.code === "ENOENT") return void 0;
|
|
27451
|
+
throw error62;
|
|
27452
|
+
});
|
|
27453
|
+
async function recordFolders(root) {
|
|
27454
|
+
return (await readdir3(join4(root, ...RECORDS_ROOT.split("/"))).catch((error62) => {
|
|
27455
|
+
if (error62.code === "ENOENT") return [];
|
|
27456
|
+
throw error62;
|
|
27457
|
+
})).filter(isRecordFolder);
|
|
27458
|
+
}
|
|
27459
|
+
async function findRecord(root, id) {
|
|
27460
|
+
for (const folder of await recordFolders(root)) {
|
|
27461
|
+
const path3 = `${RECORDS_ROOT}/${folder}/${id}.md`;
|
|
27462
|
+
if ((await info(join4(root, ...path3.split("/"))))?.isFile()) return path3;
|
|
27463
|
+
}
|
|
27464
|
+
return void 0;
|
|
27465
|
+
}
|
|
27466
|
+
async function readPendingRecords(root, run) {
|
|
27467
|
+
const status = (await run(["status", "--porcelain=v1", "-z", "--untracked-files=all", "--no-renames", "--", RECORDS_ROOT])).toString("utf8").split("\0").filter(Boolean);
|
|
27468
|
+
const files = /* @__PURE__ */ new Map();
|
|
27469
|
+
const altered = [];
|
|
27470
|
+
const problems = [];
|
|
27471
|
+
for (const entry2 of status) {
|
|
27472
|
+
const state2 = entry2.slice(0, 2);
|
|
27473
|
+
const path3 = entry2.slice(3);
|
|
27474
|
+
if (state2 !== "??" && !state2.includes("A")) {
|
|
27475
|
+
altered.push(path3);
|
|
27476
|
+
continue;
|
|
27477
|
+
}
|
|
27478
|
+
const file2 = join4(root, ...path3.split("/"));
|
|
27479
|
+
const stat3 = await info(file2);
|
|
27480
|
+
if (!stat3?.isFile() || stat3.isSymbolicLink()) {
|
|
27481
|
+
problems.push(docProblem("PATH_UNSUPPORTED", path3));
|
|
27482
|
+
continue;
|
|
27483
|
+
}
|
|
27484
|
+
if (stat3.size > 1024 * 1024) {
|
|
27485
|
+
problems.push(docProblem("FILE_TOO_LARGE", path3));
|
|
27486
|
+
continue;
|
|
27487
|
+
}
|
|
27488
|
+
try {
|
|
27489
|
+
files.set(path3, decoder.decode(await readFile3(file2)));
|
|
27490
|
+
} catch {
|
|
27491
|
+
problems.push(docProblem("INVALID_CHARACTERS", path3));
|
|
27492
|
+
}
|
|
27493
|
+
}
|
|
27494
|
+
const folders = await recordFolders(root);
|
|
27495
|
+
for (const path3 of files.keys()) {
|
|
27496
|
+
const id = recordIdOf(path3);
|
|
27497
|
+
if (!id) continue;
|
|
27498
|
+
for (const folder of folders) {
|
|
27499
|
+
const other = `${RECORDS_ROOT}/${folder}/${id}.md`;
|
|
27500
|
+
if (other !== path3 && !files.has(other) && (await info(join4(root, ...other.split("/"))))?.isFile()) problems.push(docProblem("DUPLICATE_RECORD_ID", path3, { id, other }));
|
|
27501
|
+
}
|
|
27502
|
+
}
|
|
27503
|
+
return { files, altered: altered.sort(), problems };
|
|
27504
|
+
}
|
|
27505
|
+
|
|
27506
|
+
// src/adapters/filesystem/config-file.ts
|
|
27507
|
+
import { lstat as lstat4, readFile as readFile4, realpath, mkdir as mkdir2, open as open2, link, rename, unlink } from "node:fs/promises";
|
|
27508
|
+
import { join as join5 } from "node:path";
|
|
27248
27509
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
27249
27510
|
var missing = (error62) => error62.code === "ENOENT";
|
|
27250
|
-
var fileInfo = (path3) =>
|
|
27511
|
+
var fileInfo = (path3) => lstat4(path3).catch((error62) => {
|
|
27251
27512
|
if (missing(error62)) return void 0;
|
|
27252
27513
|
throw error62;
|
|
27253
27514
|
});
|
|
27254
27515
|
async function configDirectory(root) {
|
|
27255
|
-
const path3 =
|
|
27516
|
+
const path3 = join5(root, ".gitifact");
|
|
27256
27517
|
const info5 = await fileInfo(path3);
|
|
27257
27518
|
if (info5 && (!info5.isDirectory() || info5.isSymbolicLink())) throw new InitError("PATH_CONFLICT", t2("config.storeNotDirectory"));
|
|
27258
27519
|
return info5;
|
|
27259
27520
|
}
|
|
27260
27521
|
async function readConfigFile(root) {
|
|
27261
27522
|
await configDirectory(root);
|
|
27262
|
-
const path3 =
|
|
27523
|
+
const path3 = join5(root, ".gitifact", "config.json");
|
|
27263
27524
|
const info5 = await fileInfo(path3);
|
|
27264
27525
|
if (!info5) return void 0;
|
|
27265
27526
|
if (!info5.isFile() || info5.isSymbolicLink() || info5.size > 65536) throw new InitError("PATH_CONFLICT", t2("config.fileShape"));
|
|
27266
|
-
const bytes = await
|
|
27527
|
+
const bytes = await readFile4(path3);
|
|
27267
27528
|
const after = await fileInfo(path3);
|
|
27268
27529
|
await configDirectory(root);
|
|
27269
27530
|
if (!after || after.ino !== info5.ino || after.dev !== info5.dev || after.mtimeMs !== info5.mtimeMs || bytes.length > 65536) {
|
|
@@ -27276,7 +27537,7 @@ async function readConfigFile(root) {
|
|
|
27276
27537
|
}
|
|
27277
27538
|
}
|
|
27278
27539
|
async function publishConfig(root, text4, recheck, beforePublish, replace = false) {
|
|
27279
|
-
const directory =
|
|
27540
|
+
const directory = join5(root, ".gitifact");
|
|
27280
27541
|
await configDirectory(root);
|
|
27281
27542
|
await mkdir2(directory).catch((error62) => {
|
|
27282
27543
|
if (error62.code !== "EEXIST") throw error62;
|
|
@@ -27289,7 +27550,7 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
|
|
|
27289
27550
|
throw new InitError("INPUT_CHANGED", t2("config.directoryChanged"));
|
|
27290
27551
|
}
|
|
27291
27552
|
};
|
|
27292
|
-
const temporary =
|
|
27553
|
+
const temporary = join5(directory, ".init-" + randomUUID2() + ".tmp");
|
|
27293
27554
|
const handle = await open2(temporary, "wx", 384);
|
|
27294
27555
|
const tempOwner = await handle.stat();
|
|
27295
27556
|
try {
|
|
@@ -27303,11 +27564,11 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
|
|
|
27303
27564
|
await unchanged();
|
|
27304
27565
|
await recheck();
|
|
27305
27566
|
await unchanged();
|
|
27306
|
-
const bytes = await
|
|
27567
|
+
const bytes = await readFile4(temporary, "utf8");
|
|
27307
27568
|
if (bytes !== text4) throw new InitError("INPUT_CHANGED", t2("config.temporaryChanged"));
|
|
27308
27569
|
parseManagedConfig(bytes);
|
|
27309
|
-
if (replace) await rename(temporary,
|
|
27310
|
-
else await link(temporary,
|
|
27570
|
+
if (replace) await rename(temporary, join5(directory, "config.json"));
|
|
27571
|
+
else await link(temporary, join5(directory, "config.json"));
|
|
27311
27572
|
} finally {
|
|
27312
27573
|
await unchanged();
|
|
27313
27574
|
const current2 = await fileInfo(temporary);
|
|
@@ -27316,42 +27577,64 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
|
|
|
27316
27577
|
}
|
|
27317
27578
|
|
|
27318
27579
|
// src/adapters/filesystem/instruction-folder.ts
|
|
27319
|
-
import { lstat as
|
|
27320
|
-
import { join as
|
|
27580
|
+
import { lstat as lstat5, open as open3, readdir as readdir4, readFile as readFile5 } from "node:fs/promises";
|
|
27581
|
+
import { join as join6 } from "node:path";
|
|
27321
27582
|
var INSTRUCTION_FILE_LIMIT = 500;
|
|
27322
27583
|
var INSTRUCTION_TEXT_LIMIT = 512 * 1024;
|
|
27323
|
-
var folderOf2 = (root, instructionPath) =>
|
|
27584
|
+
var folderOf2 = (root, instructionPath) => join6(root, ...instructionPath.split("/").slice(0, -1));
|
|
27324
27585
|
async function listInstructionFiles(root, instructionPath) {
|
|
27325
27586
|
const files = [];
|
|
27326
27587
|
let limited = false;
|
|
27327
27588
|
async function visit2(folder, prefix, depth) {
|
|
27328
27589
|
if (depth > 10) return;
|
|
27329
|
-
const entries = await
|
|
27590
|
+
const entries = await readdir4(folder, { withFileTypes: true }).catch(() => []);
|
|
27330
27591
|
for (const entry2 of entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
|
|
27331
27592
|
if (files.length >= INSTRUCTION_FILE_LIMIT) {
|
|
27332
27593
|
limited = true;
|
|
27333
27594
|
return;
|
|
27334
27595
|
}
|
|
27335
27596
|
const path3 = prefix + entry2.name;
|
|
27336
|
-
if (entry2.isDirectory()) await visit2(
|
|
27337
|
-
else if (entry2.isFile() && path3 !== INSTRUCTION_FILE) files.push({ path: path3, size: (await
|
|
27597
|
+
if (entry2.isDirectory()) await visit2(join6(folder, entry2.name), path3 + "/", depth + 1);
|
|
27598
|
+
else if (entry2.isFile() && path3 !== INSTRUCTION_FILE) files.push({ path: path3, size: (await lstat5(join6(folder, entry2.name))).size });
|
|
27338
27599
|
}
|
|
27339
27600
|
}
|
|
27340
27601
|
await visit2(folderOf2(root, instructionPath), "", 0);
|
|
27341
27602
|
return { files, limited };
|
|
27342
27603
|
}
|
|
27604
|
+
var FRONTMATTER_WINDOW = 8 * 1024;
|
|
27605
|
+
async function describeFiles(root, instructionPath, files) {
|
|
27606
|
+
const folder = folderOf2(root, instructionPath);
|
|
27607
|
+
return Promise.all(files.map(async (file2) => {
|
|
27608
|
+
if (!isInstructionReference(file2.path)) return file2;
|
|
27609
|
+
let handle;
|
|
27610
|
+
try {
|
|
27611
|
+
handle = await open3(join6(folder, ...file2.path.split("/")), "r");
|
|
27612
|
+
const buffer = Buffer.alloc(Math.min(FRONTMATTER_WINDOW, file2.size));
|
|
27613
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
27614
|
+
return { ...file2, ...parseInstructionFile(file2.path, buffer.subarray(0, bytesRead).toString("utf8")) };
|
|
27615
|
+
} catch {
|
|
27616
|
+
return file2;
|
|
27617
|
+
} finally {
|
|
27618
|
+
await handle?.close();
|
|
27619
|
+
}
|
|
27620
|
+
}));
|
|
27621
|
+
}
|
|
27622
|
+
async function readInstructionFiles(root, instructionPath) {
|
|
27623
|
+
const { files, limited } = await listInstructionFiles(root, instructionPath);
|
|
27624
|
+
return { files: await describeFiles(root, instructionPath, files), limited };
|
|
27625
|
+
}
|
|
27343
27626
|
async function readInstructionFile(root, instructionPath, path3) {
|
|
27344
27627
|
const parts = path3.split("/");
|
|
27345
27628
|
if (parts.some((p) => !p || p === "." || p === ".." || /[\\:\0]/.test(p))) return void 0;
|
|
27346
27629
|
let full = folderOf2(root, instructionPath);
|
|
27347
27630
|
for (const [index, part] of parts.entries()) {
|
|
27348
|
-
full =
|
|
27349
|
-
const info5 = await
|
|
27631
|
+
full = join6(full, part);
|
|
27632
|
+
const info5 = await lstat5(full).catch(() => void 0);
|
|
27350
27633
|
if (!info5 || info5.isSymbolicLink() || (index < parts.length - 1 ? !info5.isDirectory() : !info5.isFile())) return void 0;
|
|
27351
27634
|
}
|
|
27352
|
-
const size = (await
|
|
27635
|
+
const size = (await lstat5(full)).size;
|
|
27353
27636
|
if (size > INSTRUCTION_TEXT_LIMIT) return { text: null, size, binary: false, tooLarge: true };
|
|
27354
|
-
const bytes = await
|
|
27637
|
+
const bytes = await readFile5(full);
|
|
27355
27638
|
try {
|
|
27356
27639
|
if (bytes.includes(0)) throw new TypeError("binary");
|
|
27357
27640
|
return { text: new TextDecoder("utf-8", { fatal: true }).decode(bytes), size, binary: false, tooLarge: false };
|
|
@@ -27361,20 +27644,108 @@ async function readInstructionFile(root, instructionPath, path3) {
|
|
|
27361
27644
|
}
|
|
27362
27645
|
|
|
27363
27646
|
// src/adapters/filesystem/agents-file.ts
|
|
27364
|
-
import { lstat as
|
|
27365
|
-
import { join as
|
|
27647
|
+
import { lstat as lstat6, readFile as readFile6 } from "node:fs/promises";
|
|
27648
|
+
import { join as join7 } from "node:path";
|
|
27366
27649
|
var LIMIT = 1024 * 1024;
|
|
27367
27650
|
async function readAgentsFile(root) {
|
|
27368
|
-
const file2 =
|
|
27369
|
-
const info5 = await
|
|
27651
|
+
const file2 = join7(root, "AGENTS.md");
|
|
27652
|
+
const info5 = await lstat6(file2).catch(() => void 0);
|
|
27370
27653
|
if (!info5?.isFile() || info5.size > LIMIT) return null;
|
|
27371
27654
|
try {
|
|
27372
|
-
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: false }).decode(await
|
|
27655
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: false }).decode(await readFile6(file2));
|
|
27373
27656
|
} catch {
|
|
27374
27657
|
return null;
|
|
27375
27658
|
}
|
|
27376
27659
|
}
|
|
27377
27660
|
|
|
27661
|
+
// src/queries/document-states.ts
|
|
27662
|
+
var isDocument = (path3) => {
|
|
27663
|
+
try {
|
|
27664
|
+
return classifyDocPath(path3).type === "doc";
|
|
27665
|
+
} catch {
|
|
27666
|
+
return false;
|
|
27667
|
+
}
|
|
27668
|
+
};
|
|
27669
|
+
async function documentStates(project, working2) {
|
|
27670
|
+
const head = await project.head();
|
|
27671
|
+
const states = /* @__PURE__ */ new Map();
|
|
27672
|
+
const previous = /* @__PURE__ */ new Map();
|
|
27673
|
+
const result = (documents) => ({ documents, state: (id) => states.get(id) ?? "committed", previousPath: (id) => previous.get(id) });
|
|
27674
|
+
if (!head) {
|
|
27675
|
+
for (const doc of working2) states.set(doc.id, "added");
|
|
27676
|
+
return result(working2);
|
|
27677
|
+
}
|
|
27678
|
+
const status = project.reader.decode(await project.reader.run(["status", "--porcelain=v1", "-z", "--untracked-files=all", "--no-renames", "--", ...[SPEC_ROOT, INSTRUCTIONS_ROOT, WIKI_ROOT].map((root) => ":(top)" + root)])).split("\0").filter(Boolean);
|
|
27679
|
+
const byPath2 = new Map(working2.map((d) => [d.path, d]));
|
|
27680
|
+
const added = [];
|
|
27681
|
+
const deletedPaths = [];
|
|
27682
|
+
for (const entry2 of status) {
|
|
27683
|
+
const [x, y] = [entry2[0], entry2[1]];
|
|
27684
|
+
const path3 = entry2.slice(3);
|
|
27685
|
+
if (!isDocument(path3)) continue;
|
|
27686
|
+
const doc = byPath2.get(path3);
|
|
27687
|
+
const atHead = x !== "?" && x !== "A";
|
|
27688
|
+
if (!doc) {
|
|
27689
|
+
if (atHead && (x === "D" || y === "D")) deletedPaths.push(path3);
|
|
27690
|
+
continue;
|
|
27691
|
+
}
|
|
27692
|
+
if (!atHead) {
|
|
27693
|
+
added.push(doc);
|
|
27694
|
+
continue;
|
|
27695
|
+
}
|
|
27696
|
+
if (x !== " " || y !== " ") states.set(doc.id, "modified");
|
|
27697
|
+
}
|
|
27698
|
+
const gone = await documentsAtHead(project, head, deletedPaths);
|
|
27699
|
+
const goneById = new Map(gone.map((d) => [d.id, d]));
|
|
27700
|
+
for (const doc of added) {
|
|
27701
|
+
const before = goneById.get(doc.id);
|
|
27702
|
+
if (before) {
|
|
27703
|
+
states.set(doc.id, "modified");
|
|
27704
|
+
previous.set(doc.id, before.path);
|
|
27705
|
+
goneById.delete(doc.id);
|
|
27706
|
+
} else states.set(doc.id, "added");
|
|
27707
|
+
}
|
|
27708
|
+
const ids = new Set(working2.map((d) => d.id));
|
|
27709
|
+
const deleted = [...goneById.values()].filter((d) => !ids.has(d.id));
|
|
27710
|
+
for (const doc of deleted) states.set(doc.id, "deleted");
|
|
27711
|
+
return result([...working2, ...deleted]);
|
|
27712
|
+
}
|
|
27713
|
+
async function documentsAtHead(project, head, paths2) {
|
|
27714
|
+
if (!paths2.length) return [];
|
|
27715
|
+
const listing = project.reader.decode(await project.reader.run(["ls-tree", "--full-tree", "-z", head, "--", ...paths2])).split("\0").filter(Boolean).map((row) => /^\d+ blob ([a-f0-9]+)\t([\s\S]+)$/.exec(row)).filter((m) => !!m).map((m) => ({ blob: m[1], path: m[2] }));
|
|
27716
|
+
const blobs = await project.reader.blobs([...new Set(listing.map((l) => l.blob))]).catch(() => /* @__PURE__ */ new Map());
|
|
27717
|
+
return listing.flatMap(({ blob, path: path3 }) => {
|
|
27718
|
+
const text4 = blobs.get(blob);
|
|
27719
|
+
try {
|
|
27720
|
+
return text4 === void 0 ? [] : [parseDocumentFile(path3, text4)];
|
|
27721
|
+
} catch {
|
|
27722
|
+
return [];
|
|
27723
|
+
}
|
|
27724
|
+
});
|
|
27725
|
+
}
|
|
27726
|
+
|
|
27727
|
+
// src/server/checkout/stamp.ts
|
|
27728
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
27729
|
+
import { lstat as lstat7 } from "node:fs/promises";
|
|
27730
|
+
import { join as join8 } from "node:path";
|
|
27731
|
+
function createStampReader(root, inherited = process.env) {
|
|
27732
|
+
const runner = createGitRunner();
|
|
27733
|
+
const env = { ...inherited, GIT_OPTIONAL_LOCKS: "0", GIT_NO_REPLACE_OBJECTS: "1", GIT_TERMINAL_PROMPT: "0", GIT_LITERAL_PATHSPECS: "0", LC_ALL: "C" };
|
|
27734
|
+
const git = async (args, acceptedExitCodes = [0]) => (await runner(["--no-optional-locks", "-c", "core.fsmonitor=false", ...args], { cwd: root, env, timeoutMs: 15e3, maxBytes: 16 * 1024 * 1024, acceptedExitCodes })).toString("utf8");
|
|
27735
|
+
return async () => {
|
|
27736
|
+
const [head, status] = await Promise.all([
|
|
27737
|
+
git(["rev-parse", "--verify", "--quiet", "HEAD"], [0, 1]),
|
|
27738
|
+
git(["status", "--porcelain=v1", "-z", "--untracked-files=all", "--no-renames", "--", ":(top).gitifact", ":(top)AGENTS.md"])
|
|
27739
|
+
]);
|
|
27740
|
+
const paths2 = status.split("\0").filter(Boolean).map((entry2) => entry2.slice(3)).filter((path3) => !path3.startsWith(".gitifact/cache/"));
|
|
27741
|
+
const seen = await Promise.all(paths2.map(async (path3) => {
|
|
27742
|
+
const info5 = await lstat7(join8(root, ...path3.split("/"))).catch(() => void 0);
|
|
27743
|
+
return path3 + " " + (info5 ? info5.mtimeMs + ":" + info5.size : "-");
|
|
27744
|
+
}));
|
|
27745
|
+
return createHash2("sha256").update(head.trim() + "\n" + seen.sort().join("\n")).digest("hex").slice(0, 20);
|
|
27746
|
+
};
|
|
27747
|
+
}
|
|
27748
|
+
|
|
27378
27749
|
// src/server/checkout/checkout-reader.ts
|
|
27379
27750
|
var tally = (people, name2, email3, latest) => {
|
|
27380
27751
|
const person = people.get(email3);
|
|
@@ -27388,7 +27759,12 @@ async function settled(reads) {
|
|
|
27388
27759
|
return results.map((r) => r.value);
|
|
27389
27760
|
}
|
|
27390
27761
|
var folderOf3 = (path3) => path3.split("/").slice(0, 3).join("/");
|
|
27391
|
-
function checkoutDocuments(
|
|
27762
|
+
function checkoutDocuments(states) {
|
|
27763
|
+
const documents = states.documents;
|
|
27764
|
+
const standing2 = (id) => {
|
|
27765
|
+
const previousPath = states.previousPath(id);
|
|
27766
|
+
return { state: states.state(id), ...previousPath ? { previousPath } : {} };
|
|
27767
|
+
};
|
|
27392
27768
|
const byId = new Map(documents.map((d) => [d.id, d]));
|
|
27393
27769
|
const source2 = (s) => {
|
|
27394
27770
|
if (!("id" in s)) return { title: s.title, url: s.url, ...s.note ? { note: s.note } : {} };
|
|
@@ -27402,7 +27778,8 @@ function checkoutDocuments(documents) {
|
|
|
27402
27778
|
title: index.title,
|
|
27403
27779
|
description: index.description,
|
|
27404
27780
|
body: index.body,
|
|
27405
|
-
|
|
27781
|
+
...standing2(index.id),
|
|
27782
|
+
requirements: requirements.map((r) => ({ id: r.id, path: r.path, title: r.title, description: r.description, order: r.order, body: r.body, ...standing2(r.id) })),
|
|
27406
27783
|
designs: designs.map((d) => ({
|
|
27407
27784
|
id: d.id,
|
|
27408
27785
|
path: d.path,
|
|
@@ -27411,10 +27788,11 @@ function checkoutDocuments(documents) {
|
|
|
27411
27788
|
order: d.order,
|
|
27412
27789
|
body: d.body,
|
|
27413
27790
|
requirements: d.requirements,
|
|
27414
|
-
sources: d.sources.map(source2)
|
|
27791
|
+
sources: d.sources.map(source2),
|
|
27792
|
+
...standing2(d.id)
|
|
27415
27793
|
}))
|
|
27416
27794
|
}));
|
|
27417
|
-
const instructions2 = arranged.instructions.map((k) => ({ id: k.id, name: k.name, path: k.path, title: k.title, description: k.description, body: k.body }));
|
|
27795
|
+
const instructions2 = arranged.instructions.map((k) => ({ id: k.id, name: k.name, path: k.path, title: k.title, description: k.description, body: k.body, ...standing2(k.id) }));
|
|
27418
27796
|
return { features, instructions: instructions2, orphans: arranged.orphans };
|
|
27419
27797
|
}
|
|
27420
27798
|
function createCheckoutReader(root, sessionId, cache, inherited = process.env) {
|
|
@@ -27429,6 +27807,7 @@ function createCheckoutReader(root, sessionId, cache, inherited = process.env) {
|
|
|
27429
27807
|
return head || null;
|
|
27430
27808
|
};
|
|
27431
27809
|
const pending = /* @__PURE__ */ new Map();
|
|
27810
|
+
const readStamp = createStampReader(root, inherited);
|
|
27432
27811
|
const notMigration = ["-E", "--invert-grep", `--grep=^${MIGRATION_TRAILER}: `];
|
|
27433
27812
|
async function storeAuthors(head) {
|
|
27434
27813
|
const text4 = await git(["log", ...notMigration, "--format=%x1e%aN%x00%aE%x00%aI", "-z", "--name-only", "--no-renames", "--max-count=20000", head, "--", ".gitifact/spec", INSTRUCTIONS_ROOT, "AGENTS.md"]);
|
|
@@ -27463,6 +27842,7 @@ function createCheckoutReader(root, sessionId, cache, inherited = process.env) {
|
|
|
27463
27842
|
if (!raw) throw new StoreError(t2("specReader.schemaRequired"));
|
|
27464
27843
|
parseManagedConfig(raw);
|
|
27465
27844
|
const head = await readHead();
|
|
27845
|
+
const stamp = await readStamp();
|
|
27466
27846
|
const [current2, dirty, authors, everyone] = await settled([
|
|
27467
27847
|
cache.documents.list(),
|
|
27468
27848
|
head ? git(["status", "--porcelain=v1", "--", ".gitifact/spec", INSTRUCTIONS_ROOT]) : Promise.resolve(""),
|
|
@@ -27477,25 +27857,27 @@ function createCheckoutReader(root, sessionId, cache, inherited = process.env) {
|
|
|
27477
27857
|
if (!name2 || !email3 || !latest) throw new StoreError(t2("specReader.authorUnreadable"));
|
|
27478
27858
|
tally(people, name2, email3, latest);
|
|
27479
27859
|
}
|
|
27480
|
-
const
|
|
27860
|
+
const states = await documentStates({ head: async () => head, reader }, current2.documents);
|
|
27861
|
+
const arranged = checkoutDocuments(states);
|
|
27481
27862
|
const features = arranged.features.map((feature2) => {
|
|
27482
27863
|
const entry2 = authors?.folders.get(folderOf3(feature2.path));
|
|
27483
27864
|
return { ...feature2, contributors: entry2 ? [...entry2.people.values()].sort((a, b) => b.commits - a.commits) : [], updatedAt: entry2?.latest ?? null };
|
|
27484
27865
|
});
|
|
27485
27866
|
const instructions2 = await Promise.all(arranged.instructions.map(async (instruction2) => {
|
|
27486
|
-
const { files, limited } = await
|
|
27867
|
+
const { files, limited } = instruction2.state === "deleted" ? { files: [], limited: false } : await readInstructionFiles(root, instruction2.path);
|
|
27487
27868
|
return { ...instruction2, files, filesLimited: limited, updatedAt: authors?.instructions.get(instruction2.path.split("/").slice(0, 3).join("/")) ?? null };
|
|
27488
27869
|
}));
|
|
27489
27870
|
const agentsText = await readAgentsFile(root);
|
|
27490
27871
|
const agents2 = agentsText === null ? null : { path: "AGENTS.md", body: agentsText, updatedAt: authors?.instructions.get("AGENTS.md") ?? null };
|
|
27491
27872
|
const problems = [...current2.problems, ...arranged.orphans.map((feature2) => docProblem("FEATURE_INDEX_REQUIRED", `.gitifact/spec/${feature2}/index.md`, { feature: feature2 }))];
|
|
27492
27873
|
if (await readHead() !== head) throw new StoreError(t2("specReader.projectChanged"));
|
|
27493
|
-
const checkout =
|
|
27874
|
+
const checkout = browserSpecsV7.parse({
|
|
27494
27875
|
contract: "browser-specs",
|
|
27495
|
-
version:
|
|
27876
|
+
version: 7,
|
|
27496
27877
|
sessionId,
|
|
27497
27878
|
head,
|
|
27498
27879
|
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27880
|
+
stamp,
|
|
27499
27881
|
working: head ? !!dirty.trim() : features.length > 0 || instructions2.length > 0,
|
|
27500
27882
|
features,
|
|
27501
27883
|
instructions: instructions2,
|
|
@@ -27606,30 +27988,108 @@ function isUtf8(bytes) {
|
|
|
27606
27988
|
}
|
|
27607
27989
|
}
|
|
27608
27990
|
|
|
27991
|
+
// src/queries/working-changes.ts
|
|
27992
|
+
async function workingChanges(source2, known) {
|
|
27993
|
+
const head = known !== void 0 ? known : await source2.head();
|
|
27994
|
+
const headFiles = head ? await source2.cache.history.filesAt(head) : /* @__PURE__ */ new Map();
|
|
27995
|
+
const [working2, records2] = await Promise.all([source2.cache.documents.files(), source2.pendingRecords()]);
|
|
27996
|
+
return { head, headFiles, working: working2, records: records2, ...compareDocumentSets(headFiles, working2.files) };
|
|
27997
|
+
}
|
|
27998
|
+
var uncovered = (changes2, records2) => {
|
|
27999
|
+
const covered = new Set(records2.flatMap((r) => r.docs));
|
|
28000
|
+
return changes2.filter((c) => c.types.some((type) => type !== "created") && !covered.has(c.id)).map((c) => c.id);
|
|
28001
|
+
};
|
|
28002
|
+
async function workingOverview(source2) {
|
|
28003
|
+
const found = await workingChanges(source2);
|
|
28004
|
+
const records2 = checkDocuments(new Map([...found.working.files, ...found.records.files])).records;
|
|
28005
|
+
return {
|
|
28006
|
+
head: found.head,
|
|
28007
|
+
records: records2.map((r) => ({ id: r.id, title: r.title, docs: r.docs, sections: r.sections.map((s) => ({ key: s.key, body: s.body })), draft: !!r.draft })),
|
|
28008
|
+
changes: found.changes.map((c) => ({ id: c.id, kind: c.kind, title: c.title, path: c.path, ...c.previousPath ? { previousPath: c.previousPath } : {}, types: c.types })),
|
|
28009
|
+
withoutRecord: uncovered(found.changes, records2)
|
|
28010
|
+
};
|
|
28011
|
+
}
|
|
28012
|
+
function snapshot4(doc, docs) {
|
|
28013
|
+
const folder = doc.path.split("/").slice(0, 3).join("/");
|
|
28014
|
+
const specId = doc.kind === "wiki" || doc.kind === "instruction" ? doc.kind : docs.find((d) => d.kind === "feature" && d.path === folder + "/index.md")?.id ?? "";
|
|
28015
|
+
const base = { id: doc.id, kind: doc.kind, title: doc.title, description: doc.description, body: doc.body, specId, path: doc.path };
|
|
28016
|
+
if (doc.kind === "requirement") return { ...base, order: doc.order };
|
|
28017
|
+
if (doc.kind === "design") return { ...base, order: doc.order, requirements: doc.requirements, sources: doc.sources };
|
|
28018
|
+
return base;
|
|
28019
|
+
}
|
|
28020
|
+
var parsed = (files) => [...files].flatMap(([path3, text4]) => {
|
|
28021
|
+
try {
|
|
28022
|
+
return classifyDocPath(path3).type === "doc" ? [parseDocumentFile(path3, text4)] : [];
|
|
28023
|
+
} catch {
|
|
28024
|
+
return [];
|
|
28025
|
+
}
|
|
28026
|
+
});
|
|
28027
|
+
async function workingChange2(source2, id) {
|
|
28028
|
+
const found = await workingChanges(source2);
|
|
28029
|
+
const change2 = found.changes.find((c) => c.id === id);
|
|
28030
|
+
if (!change2) return void 0;
|
|
28031
|
+
const before = parsed(found.headFiles);
|
|
28032
|
+
const after = parsed(found.working.files);
|
|
28033
|
+
const side = (docs) => {
|
|
28034
|
+
const doc = docs.find((d) => d.id === id);
|
|
28035
|
+
return doc ? snapshot4(doc, docs) : null;
|
|
28036
|
+
};
|
|
28037
|
+
return {
|
|
28038
|
+
change: { id: change2.id, kind: change2.kind, title: change2.title, path: change2.path, ...change2.previousPath ? { previousPath: change2.previousPath } : {}, types: change2.types },
|
|
28039
|
+
before: side(before),
|
|
28040
|
+
after: side(after)
|
|
28041
|
+
};
|
|
28042
|
+
}
|
|
28043
|
+
|
|
27609
28044
|
// src/server/routes/record-routes.ts
|
|
27610
|
-
var PAGE =
|
|
28045
|
+
var PAGE = 20;
|
|
27611
28046
|
function recordRoutes(root, sessionId, env) {
|
|
27612
28047
|
const git = storeReader(root);
|
|
27613
28048
|
const cache = openCache(root, { run: (args, input2) => git.run(args, input2), decode: git.decode, legacyBundles: (oids) => git.readBundles(oids) });
|
|
27614
28049
|
const readCheckout = createCheckoutReader(root, sessionId, cache, env);
|
|
28050
|
+
const readStamp = createStampReader(root, env);
|
|
27615
28051
|
const commitFiles = createCommitFiles(git);
|
|
28052
|
+
const working2 = { head: async () => (await git.baseline()).head, cache, pendingRecords: () => readPendingRecords(root, (args) => git.run(args)) };
|
|
27616
28053
|
const unreadable2 = () => t2("server.specsUnreadable");
|
|
28054
|
+
const cursorGone = () => new HttpError(404, "NOT_FOUND", t2("server.cursorNotFound"));
|
|
27617
28055
|
return [
|
|
27618
28056
|
route({ method: "GET", path: "/api/v1/specs", session: true, unreadable: unreadable2, handle: async () => ok((await readCheckout()).checkout) }),
|
|
27619
|
-
|
|
27620
|
-
|
|
27621
|
-
|
|
27622
|
-
|
|
28057
|
+
// Whether the screen is behind, asked when the reader comes back to the tab; cheap enough to ask every time.
|
|
28058
|
+
route({ method: "GET", path: "/api/v1/stamp", session: true, unreadable: unreadable2, handle: async () => ok(browserStampV1.parse({ contract: "browser-stamp", version: 1, sessionId, stamp: await readStamp() })) }),
|
|
28059
|
+
route({ method: "GET", path: "/api/v1/history", session: true, query: browserHistoryQueryV4, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28060
|
+
const page = await cache.history.commits(query.head, { kind: query.kind, document: query.document, feature: query.feature, author: query.author, q: query.q }, query.after, query.limit ?? PAGE);
|
|
28061
|
+
if (!page) throw cursorGone();
|
|
28062
|
+
return ok(browserHistoryV6.parse({ contract: "browser-history", version: 6, sessionId, head: query.head, ...page }));
|
|
27623
28063
|
} }),
|
|
27624
28064
|
route({ method: "GET", path: "/api/v1/history/summary", session: true, query: browserHistorySummaryQueryV1, unreadable: unreadable2, handle: async ({ query }) => ok(browserHistorySummaryV4.parse({ contract: "browser-history-summary", version: 4, sessionId, head: query.head, ...await cache.history.summary(query.head) })) }),
|
|
27625
|
-
// One commit as its page reads it:
|
|
27626
|
-
route({ method: "GET", path: "/api/v1/commit", session: true, query:
|
|
27627
|
-
const
|
|
27628
|
-
|
|
28065
|
+
// One commit as its page reads it: who made it and a page of the documents it changed, even when it changed none.
|
|
28066
|
+
route({ method: "GET", path: "/api/v1/commit", session: true, query: browserCommitQueryV2, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28067
|
+
const page = await cache.history.commitChanges(query.commit, query.after, query.limit ?? PAGE);
|
|
28068
|
+
if (!page) throw cursorGone();
|
|
28069
|
+
const first = page.events[0];
|
|
27629
28070
|
const line2 = first ? [] : git.decode(await git.run(["log", "-1", "--format=%aN%x00%aE%x00%cN%x00%aI%x00%s", query.commit, "--"]).catch(() => Buffer.from(""))).split("\0");
|
|
27630
28071
|
if (!first && line2.length < 5) throw new HttpError(404, "NOT_FOUND", t2("server.commitNotFound"));
|
|
27631
28072
|
const [author, email3, committer, date5, message] = first ? [first.author, first.email, first.committer, first.date, first.message] : [line2[0] ?? "", line2[1] ?? "", line2[2] ?? "", line2[3] ?? "", (line2[4] ?? "").replace(/\n$/, "")];
|
|
27632
|
-
return ok(
|
|
28073
|
+
return ok(browserCommitV4.parse({
|
|
28074
|
+
contract: "browser-commit",
|
|
28075
|
+
version: 4,
|
|
28076
|
+
sessionId,
|
|
28077
|
+
commit: query.commit,
|
|
28078
|
+
author,
|
|
28079
|
+
email: email3,
|
|
28080
|
+
committer,
|
|
28081
|
+
date: date5,
|
|
28082
|
+
message,
|
|
28083
|
+
total: page.total,
|
|
28084
|
+
next: page.next,
|
|
28085
|
+
changes: page.events
|
|
28086
|
+
}));
|
|
28087
|
+
} }),
|
|
28088
|
+
// The text on both sides of one change, read from Git when the reader opens that document.
|
|
28089
|
+
route({ method: "GET", path: "/api/v1/commit/change", session: true, query: browserCommitChangeQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28090
|
+
const change2 = await cache.history.commitChange(query.commit, query.id);
|
|
28091
|
+
if (!change2) throw new HttpError(404, "NOT_FOUND", t2("server.commitNotFound"));
|
|
28092
|
+
return ok(browserCommitChangeV1.parse({ contract: "browser-commit-change", version: 1, sessionId, commit: query.commit, event: change2.event, before: change2.before, after: change2.after }));
|
|
27633
28093
|
} }),
|
|
27634
28094
|
// A record's page names the record; which commit added it is read from the history of the HEAD the reader is on.
|
|
27635
28095
|
route({ method: "GET", path: "/api/v1/record", session: true, query: browserRecordQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
@@ -27637,6 +28097,13 @@ function recordRoutes(root, sessionId, env) {
|
|
|
27637
28097
|
if (!commit) throw new HttpError(404, "NOT_FOUND", t2("server.recordNotFound"));
|
|
27638
28098
|
return ok(browserRecordV1.parse({ contract: "browser-record", version: 1, sessionId, head: query.head, id: query.id, commit }));
|
|
27639
28099
|
} }),
|
|
28100
|
+
// What is not committed yet, worked out again on every request: the uncommitted entry of the records list.
|
|
28101
|
+
route({ method: "GET", path: "/api/v1/working", session: true, unreadable: unreadable2, handle: async () => ok(browserWorkingV1.parse({ contract: "browser-working", version: 1, sessionId, ...await workingOverview(working2) })) }),
|
|
28102
|
+
route({ method: "GET", path: "/api/v1/working/change", session: true, query: browserWorkingChangeQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28103
|
+
const change2 = await workingChange2(working2, query.id);
|
|
28104
|
+
if (!change2) throw new HttpError(404, "NOT_FOUND", t2("server.workingChangeNotFound"));
|
|
28105
|
+
return ok(browserWorkingChangeV1.parse({ contract: "browser-working-change", version: 1, sessionId, ...change2 }));
|
|
28106
|
+
} }),
|
|
27640
28107
|
// The source a commit changed beside its documents, read from Git when the commit or record page asks for it.
|
|
27641
28108
|
route({ method: "GET", path: "/api/v1/commit/files", session: true, query: browserCommitFilesQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
27642
28109
|
const found = await commitFiles.files(query.commit);
|
|
@@ -27660,18 +28127,18 @@ function recordRoutes(root, sessionId, env) {
|
|
|
27660
28127
|
}
|
|
27661
28128
|
|
|
27662
28129
|
// src/server/routes/asset-routes.ts
|
|
27663
|
-
import { lstat as
|
|
27664
|
-
import { join as
|
|
28130
|
+
import { lstat as lstat8, readFile as readFile7 } from "node:fs/promises";
|
|
28131
|
+
import { join as join9 } from "node:path";
|
|
27665
28132
|
function assetRoutes(root) {
|
|
27666
28133
|
return [
|
|
27667
28134
|
route({ method: "GET", path: "/api/v1/assets/*", session: false, handle: async ({ path: path3, response }) => {
|
|
27668
28135
|
const relative = path3.slice("/api/v1/assets/".length);
|
|
27669
28136
|
const assetPath = ASSETS_DIR + "/" + relative;
|
|
27670
28137
|
if (!isAssetPath(assetPath)) throw new HttpError(404, "NOT_FOUND", t2("server.assetNotFound"));
|
|
27671
|
-
const file2 =
|
|
27672
|
-
const stat3 = await
|
|
28138
|
+
const file2 = join9(root, ...assetPath.split("/"));
|
|
28139
|
+
const stat3 = await lstat8(file2).catch(() => void 0);
|
|
27673
28140
|
if (!stat3?.isFile() || stat3.isSymbolicLink() || stat3.size > 20 * 1024 * 1024) throw new HttpError(404, "NOT_FOUND", t2("server.assetNotFound"));
|
|
27674
|
-
const bytes = await
|
|
28141
|
+
const bytes = await readFile7(file2);
|
|
27675
28142
|
const extension = assetExtension(relative);
|
|
27676
28143
|
const inline = ["png", "jpg", "jpeg", "gif", "webp", "svg"].includes(extension);
|
|
27677
28144
|
response.writeHead(200, {
|
|
@@ -27688,7 +28155,7 @@ function assetRoutes(root) {
|
|
|
27688
28155
|
}
|
|
27689
28156
|
|
|
27690
28157
|
// src/server/browser-server.ts
|
|
27691
|
-
var readBundledChangelog = (language) =>
|
|
28158
|
+
var readBundledChangelog = (language) => readFile8(new URL("./i18n/" + language + "/changelog.md", import.meta.url), "utf8").catch((error62) => {
|
|
27692
28159
|
if (error62.code === "ENOENT") return null;
|
|
27693
28160
|
throw error62;
|
|
27694
28161
|
});
|
|
@@ -27818,14 +28285,14 @@ async function runBrowser(options, version2) {
|
|
|
27818
28285
|
}
|
|
27819
28286
|
|
|
27820
28287
|
// src/commands/spec-init.ts
|
|
27821
|
-
import { readdir as
|
|
27822
|
-
import { join as
|
|
28288
|
+
import { readdir as readdir5 } from "node:fs/promises";
|
|
28289
|
+
import { join as join13 } from "node:path";
|
|
27823
28290
|
|
|
27824
28291
|
// src/adapters/git/init-repository.ts
|
|
27825
|
-
import { createHash as
|
|
27826
|
-
import { lstat as
|
|
27827
|
-
import { join as
|
|
27828
|
-
var markerPresent = (path3) =>
|
|
28292
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
28293
|
+
import { lstat as lstat9, readFile as readFile9 } from "node:fs/promises";
|
|
28294
|
+
import { join as join10 } from "node:path";
|
|
28295
|
+
var markerPresent = (path3) => lstat9(path3).then(() => true, (error62) => {
|
|
27829
28296
|
if (error62.code === "ENOENT") return false;
|
|
27830
28297
|
throw error62;
|
|
27831
28298
|
});
|
|
@@ -27842,27 +28309,27 @@ function initRepository(cwd, inherited = process.env) {
|
|
|
27842
28309
|
});
|
|
27843
28310
|
return {
|
|
27844
28311
|
async inspect(observedState, includeTrackedConfig = true) {
|
|
27845
|
-
const
|
|
27846
|
-
const root =
|
|
28312
|
+
const state2 = observedState ?? await createRepositoryReader(cwd, { env }).read();
|
|
28313
|
+
const root = state2.repository.rootPath;
|
|
27847
28314
|
const locations = decodeGitLine(await git(["rev-parse", "--path-format=absolute", "--absolute-git-dir", "--git-path", "index"], root)).split("\n");
|
|
27848
28315
|
const gitDir = locations.length === 2 ? locations[0] : decodeGitLine(await git(["rev-parse", "--absolute-git-dir"], root));
|
|
27849
28316
|
const indexPath = locations.length === 2 ? locations[1] : decodeGitLine(await git(["rev-parse", "--path-format=absolute", "--git-path", "index"], root));
|
|
27850
28317
|
for (const marker2 of ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"]) {
|
|
27851
|
-
if (await markerPresent(
|
|
28318
|
+
if (await markerPresent(join10(gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
|
|
27852
28319
|
}
|
|
27853
|
-
if (
|
|
27854
|
-
const index = await
|
|
28320
|
+
if (state2.changes.some((change2) => change2.kind === "unmerged")) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
28321
|
+
const index = await readFile9(indexPath).catch((error62) => {
|
|
27855
28322
|
if (error62.code === "ENOENT") return Buffer.alloc(0);
|
|
27856
28323
|
throw error62;
|
|
27857
28324
|
});
|
|
27858
28325
|
const [staged, committed2] = includeTrackedConfig ? await Promise.all([
|
|
27859
28326
|
git(["ls-files", "--stage", "-z", "--", ".gitifact/config.json"], root),
|
|
27860
|
-
|
|
28327
|
+
state2.head.commit ? git(["ls-tree", "-z", state2.head.commit, "--", ".gitifact/config.json"], root) : Buffer.alloc(0)
|
|
27861
28328
|
]) : [Buffer.alloc(0), Buffer.alloc(0)];
|
|
27862
28329
|
return {
|
|
27863
|
-
state,
|
|
28330
|
+
state: state2,
|
|
27864
28331
|
trackedConfig: staged.length > 0 || committed2.length > 0,
|
|
27865
|
-
stamp: JSON.stringify({ repository:
|
|
28332
|
+
stamp: JSON.stringify({ repository: state2.repository, head: state2.head, index: createHash3("sha256").update(index).digest("hex") })
|
|
27866
28333
|
};
|
|
27867
28334
|
},
|
|
27868
28335
|
async checkIgnore(root, path3 = ".gitifact/config.json") {
|
|
@@ -27884,19 +28351,52 @@ function initRepository(cwd, inherited = process.env) {
|
|
|
27884
28351
|
};
|
|
27885
28352
|
}
|
|
27886
28353
|
|
|
28354
|
+
// src/adapters/filesystem/document-file.ts
|
|
28355
|
+
import { randomBytes } from "node:crypto";
|
|
28356
|
+
import { lstat as lstat10, mkdir as mkdir3, writeFile as writeFile2 } from "node:fs/promises";
|
|
28357
|
+
import { join as join11 } from "node:path";
|
|
28358
|
+
var generateId = (prefix) => prefix + "-" + [...randomBytes(10)].map((n) => "abcdefghijklmnopqrstuvwxyz234567"[n & 31]).join("");
|
|
28359
|
+
async function createDocumentFile(root, path3, text4) {
|
|
28360
|
+
const parts = path3.split("/");
|
|
28361
|
+
for (let i = 1; i < parts.length; i++) {
|
|
28362
|
+
const folder = join11(root, ...parts.slice(0, i));
|
|
28363
|
+
const info5 = await lstat10(folder).catch((error62) => {
|
|
28364
|
+
if (error62.code === "ENOENT") return void 0;
|
|
28365
|
+
throw error62;
|
|
28366
|
+
});
|
|
28367
|
+
if (!info5) {
|
|
28368
|
+
await mkdir3(folder);
|
|
28369
|
+
continue;
|
|
28370
|
+
}
|
|
28371
|
+
if (!info5.isDirectory() || info5.isSymbolicLink()) throw Object.assign(new Error(folder), { code: "ENOTDIR" });
|
|
28372
|
+
}
|
|
28373
|
+
await writeFile2(join11(root, ...parts), text4, { flag: "wx" });
|
|
28374
|
+
}
|
|
28375
|
+
var LINE_ENDINGS_PATH = ".gitifact/.gitattributes";
|
|
28376
|
+
var LINE_ENDINGS = "# Written by gitifact init: documents keep LF on every OS, so what Git holds and the files read the same.\n* text=auto eol=lf\n";
|
|
28377
|
+
async function writeLineEndings(root) {
|
|
28378
|
+
try {
|
|
28379
|
+
await createDocumentFile(root, LINE_ENDINGS_PATH, LINE_ENDINGS);
|
|
28380
|
+
return true;
|
|
28381
|
+
} catch (error62) {
|
|
28382
|
+
if (error62.code === "EEXIST") return false;
|
|
28383
|
+
throw error62;
|
|
28384
|
+
}
|
|
28385
|
+
}
|
|
28386
|
+
|
|
27887
28387
|
// src/adapters/filesystem/managed-file.ts
|
|
27888
|
-
import { open as
|
|
27889
|
-
import { join as
|
|
28388
|
+
import { open as open4, readFile as readFile10, mkdir as mkdir4, realpath as realpath2, rename as rename2, link as link2, unlink as unlink2 } from "node:fs/promises";
|
|
28389
|
+
import { join as join12, dirname } from "node:path";
|
|
27890
28390
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
27891
28391
|
var DEFAULT_LIMIT = 1024 * 1024;
|
|
27892
28392
|
async function managedPath(root, path3, create = false) {
|
|
27893
28393
|
let full = root;
|
|
27894
28394
|
const parts = path3.split("/");
|
|
27895
28395
|
for (let i = 0; i < parts.length; i++) {
|
|
27896
|
-
full =
|
|
28396
|
+
full = join12(full, parts[i]);
|
|
27897
28397
|
let info5 = await fileInfo(full);
|
|
27898
28398
|
if (!info5 && create && i < parts.length - 1) {
|
|
27899
|
-
await
|
|
28399
|
+
await mkdir4(full).catch((error62) => {
|
|
27900
28400
|
if (error62.code !== "EEXIST") throw error62;
|
|
27901
28401
|
});
|
|
27902
28402
|
info5 = await fileInfo(full);
|
|
@@ -27912,7 +28412,7 @@ async function managedRead(root, path3, limit = DEFAULT_LIMIT) {
|
|
|
27912
28412
|
const before = await fileInfo(full);
|
|
27913
28413
|
if (!before) return null;
|
|
27914
28414
|
if (before.size > limit) throw new InitError("FILE_TOO_LARGE", t2("managedFile.tooLarge", { kib: limit / 1024, path: path3 }));
|
|
27915
|
-
const bytes = await
|
|
28415
|
+
const bytes = await readFile10(full);
|
|
27916
28416
|
await managedPath(root, path3);
|
|
27917
28417
|
const after = await fileInfo(full);
|
|
27918
28418
|
if (!after || before.ino !== after.ino || before.dev !== after.dev || before.mtimeMs !== after.mtimeMs || bytes.length > limit) {
|
|
@@ -27941,8 +28441,8 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
27941
28441
|
if (previous !== null) await unlink2(full);
|
|
27942
28442
|
return;
|
|
27943
28443
|
}
|
|
27944
|
-
const temp =
|
|
27945
|
-
const handle = await
|
|
28444
|
+
const temp = join12(directory, ".gitifact-managed-" + randomUUID4() + ".tmp");
|
|
28445
|
+
const handle = await open4(temp, "wx", 384);
|
|
27946
28446
|
const tempOwner = await handle.stat();
|
|
27947
28447
|
try {
|
|
27948
28448
|
try {
|
|
@@ -27953,7 +28453,7 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
27953
28453
|
}
|
|
27954
28454
|
await recheck();
|
|
27955
28455
|
await unchanged();
|
|
27956
|
-
if (await managedRead(root, path3) !== previous || await
|
|
28456
|
+
if (await managedRead(root, path3) !== previous || await readFile10(temp, "utf8") !== next) throw new InitError("INPUT_CHANGED", t2("managedFile.publishTargetChanged", { path: path3 }));
|
|
27957
28457
|
if (previous === null) await link2(temp, full);
|
|
27958
28458
|
else await rename2(temp, full);
|
|
27959
28459
|
} finally {
|
|
@@ -27964,10 +28464,10 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
27964
28464
|
}
|
|
27965
28465
|
|
|
27966
28466
|
// src/commands/agent-block.ts
|
|
27967
|
-
import { readFile as
|
|
28467
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
27968
28468
|
|
|
27969
28469
|
// src/commands/guide.ts
|
|
27970
|
-
import { readFile as
|
|
28470
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
27971
28471
|
|
|
27972
28472
|
// src/commands/output.ts
|
|
27973
28473
|
var formats = ["text", "json"];
|
|
@@ -28004,7 +28504,7 @@ var text2 = (lines) => lines.join("\n") + "\n";
|
|
|
28004
28504
|
// src/commands/guide.ts
|
|
28005
28505
|
var guideTopics = ["workflow", "spec", "design", "instructions", "records", "writing", "commit", "migrate"];
|
|
28006
28506
|
var isGuideTopic = (value) => guideTopics.includes(value);
|
|
28007
|
-
var readBundledGuide = (name2, lang) =>
|
|
28507
|
+
var readBundledGuide = (name2, lang) => readFile11(new URL("./i18n/" + lang + "/docs/" + name2 + ".md", import.meta.url), "utf8");
|
|
28008
28508
|
function guideSummary(source2) {
|
|
28009
28509
|
const { fields: fields2 } = parseFrontmatterBlock(source2.replace(/\r\n/g, "\n"), "guide");
|
|
28010
28510
|
const value = (key) => {
|
|
@@ -28045,7 +28545,7 @@ function boilerplateFor(path3) {
|
|
|
28045
28545
|
const name2 = path3 === "AGENTS.md" ? path3 : path3.split("/").pop().replace(/^\./, "").replace(/\.md$/, "");
|
|
28046
28546
|
return "# " + name2 + "\n\nProject-specific guidance for AI coding agents.\n";
|
|
28047
28547
|
}
|
|
28048
|
-
var readBundledBlock = (lang) =>
|
|
28548
|
+
var readBundledBlock = (lang) => readFile12(new URL("./i18n/" + lang + "/block.md", import.meta.url), "utf8");
|
|
28049
28549
|
async function renderAgentBlock(version2, controls = {}, lang = getLanguage()) {
|
|
28050
28550
|
const body2 = await (controls.readBlock ?? readBundledBlock)(lang);
|
|
28051
28551
|
const filled = body2.trimEnd().replace(/\{(version|language|topics)\}/g, (_whole, name2) => name2 === "version" ? version2 : name2 === "language" ? lang : guideTopics.join(", "));
|
|
@@ -28226,11 +28726,11 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
28226
28726
|
const first = await repo.inspect();
|
|
28227
28727
|
const root = first.state.repository.rootPath;
|
|
28228
28728
|
const docsPlan = agentDocs ? await planAgentDocs(root, agentDocs) : skippedAgentDocs;
|
|
28229
|
-
const result = async (config3, outcome) => {
|
|
28729
|
+
const result = async (config3, outcome, lineEndings = false) => {
|
|
28230
28730
|
const checked = await update;
|
|
28231
|
-
return
|
|
28731
|
+
return projectInitV8.parse({
|
|
28232
28732
|
contract: "project-init",
|
|
28233
|
-
version:
|
|
28733
|
+
version: 8,
|
|
28234
28734
|
ok: true,
|
|
28235
28735
|
outcome,
|
|
28236
28736
|
rootPath: root,
|
|
@@ -28238,6 +28738,7 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
28238
28738
|
schemaVersion: config3.schemaVersion,
|
|
28239
28739
|
baseline: config3.baseline,
|
|
28240
28740
|
agentDocs: { mode: docsPlan.mode, paths: docsPlan.paths },
|
|
28741
|
+
lineEndings: { path: LINE_ENDINGS_PATH, created: lineEndings },
|
|
28241
28742
|
update: checked,
|
|
28242
28743
|
install: checked.status === "available" ? { npx: npxUpdate(checked.latestVersion), npmGlobal: npmGlobalInstall(checked.latestVersion) } : null
|
|
28243
28744
|
});
|
|
@@ -28249,8 +28750,9 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
28249
28750
|
if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !== text5) throw new InitError("INPUT_CHANGED", t2("init.inputChanged"));
|
|
28250
28751
|
};
|
|
28251
28752
|
await unchanged();
|
|
28252
|
-
if (
|
|
28253
|
-
|
|
28753
|
+
if (dryRun) return result(config3, "already-initialized");
|
|
28754
|
+
await applyAgentDocs(root, docsPlan, unchanged);
|
|
28755
|
+
return result(config3, "already-initialized", await writeLineEndings(root));
|
|
28254
28756
|
};
|
|
28255
28757
|
const config2 = { schemaVersion: SCHEMA_VERSION, baseline: first.state.head.commit ? { kind: "commit", objectFormat: first.state.repository.objectFormat, commit: first.state.head.commit } : { kind: "empty" } };
|
|
28256
28758
|
const text4 = JSON.stringify(config2, null, 2) + "\n";
|
|
@@ -28260,13 +28762,13 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
28260
28762
|
};
|
|
28261
28763
|
await published();
|
|
28262
28764
|
await applyAgentDocs(root, docsPlan, published);
|
|
28263
|
-
return result(config2, outcome);
|
|
28765
|
+
return result(config2, outcome, await writeLineEndings(root));
|
|
28264
28766
|
};
|
|
28265
28767
|
const old = await readConfigFile(root);
|
|
28266
28768
|
if (old !== void 0) return existing(old);
|
|
28267
28769
|
if (first.trackedConfig) throw new InitError("CONFIG_DELETED", t2("init.configDeleted"));
|
|
28268
28770
|
const checkRecords = async () => {
|
|
28269
|
-
const entries = await
|
|
28771
|
+
const entries = await readdir5(join13(root, ".gitifact")).catch((e) => {
|
|
28270
28772
|
if (e.code === "ENOENT") return [];
|
|
28271
28773
|
throw e;
|
|
28272
28774
|
});
|
|
@@ -28318,117 +28820,33 @@ async function runInit(options, version2) {
|
|
|
28318
28820
|
);
|
|
28319
28821
|
if (!dto.ok) throw new Error("unreachable");
|
|
28320
28822
|
const docs = dto.agentDocs.mode === "skip" ? t2("init.text.skipped") : dto.agentDocs.paths.join(", ") + (dto.agentDocs.mode === "remove" ? " (" + t2("init.text.blockRemoved") + ")" : dto.outcome === "planned" ? " (" + t2("init.text.blockPlanned") + ")" : " (" + t2("init.text.blockUpdated") + ")");
|
|
28823
|
+
const lineEndings = dto.lineEndings.created ? t2("init.text.lineEndings", { path: dto.lineEndings.path }) + "\n" : "";
|
|
28321
28824
|
const newer = dto.install ? t2("init.text.updateAvailable", { version: dto.update.latestVersion, command: dto.install.npx }) + "\n" : "";
|
|
28322
28825
|
process.stdout.write(options.format === "text" ? `${dto.outcome}: ${dto.rootPath}/.gitifact/config.json
|
|
28323
28826
|
${t2("init.text.storage")}: schemaVersion ${dto.schemaVersion}
|
|
28324
28827
|
${t2("init.text.agentDocs")}: ${docs}
|
|
28325
|
-
${newer}` : JSON.stringify(dto) + "\n");
|
|
28828
|
+
${lineEndings}${newer}` : JSON.stringify(dto) + "\n");
|
|
28326
28829
|
} catch (error62) {
|
|
28327
28830
|
cancel.abort();
|
|
28328
28831
|
const known = error62 instanceof InitError || error62 instanceof RepositoryReadError;
|
|
28329
28832
|
const failure2 = { code: known ? error62.code : "INIT_FAILED", message: known ? error62.message : t2("init.failed") };
|
|
28330
|
-
process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(
|
|
28833
|
+
process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(projectInitV8.parse({ contract: "project-init", version: 8, ok: false, error: failure2 })) + "\n");
|
|
28331
28834
|
process.exitCode = 1;
|
|
28332
28835
|
}
|
|
28333
28836
|
}
|
|
28334
28837
|
|
|
28335
28838
|
// src/commands/documents.ts
|
|
28336
28839
|
import { readFile as readFile13 } from "node:fs/promises";
|
|
28337
|
-
import { join as
|
|
28338
|
-
|
|
28339
|
-
// src/adapters/filesystem/document-file.ts
|
|
28340
|
-
import { randomBytes } from "node:crypto";
|
|
28341
|
-
import { lstat as lstat8, mkdir as mkdir4, writeFile as writeFile2 } from "node:fs/promises";
|
|
28342
|
-
import { join as join11 } from "node:path";
|
|
28343
|
-
var generateId = (prefix) => prefix + "-" + [...randomBytes(10)].map((n) => "abcdefghijklmnopqrstuvwxyz234567"[n & 31]).join("");
|
|
28344
|
-
async function createDocumentFile(root, path3, text4) {
|
|
28345
|
-
const parts = path3.split("/");
|
|
28346
|
-
for (let i = 1; i < parts.length; i++) {
|
|
28347
|
-
const folder = join11(root, ...parts.slice(0, i));
|
|
28348
|
-
const info5 = await lstat8(folder).catch((error62) => {
|
|
28349
|
-
if (error62.code === "ENOENT") return void 0;
|
|
28350
|
-
throw error62;
|
|
28351
|
-
});
|
|
28352
|
-
if (!info5) {
|
|
28353
|
-
await mkdir4(folder);
|
|
28354
|
-
continue;
|
|
28355
|
-
}
|
|
28356
|
-
if (!info5.isDirectory() || info5.isSymbolicLink()) throw Object.assign(new Error(folder), { code: "ENOTDIR" });
|
|
28357
|
-
}
|
|
28358
|
-
await writeFile2(join11(root, ...parts), text4, { flag: "wx" });
|
|
28359
|
-
}
|
|
28360
|
-
|
|
28361
|
-
// src/commands/project.ts
|
|
28362
|
-
import { join as join13 } from "node:path";
|
|
28363
|
-
|
|
28364
|
-
// src/adapters/git/pending-records.ts
|
|
28365
|
-
import { lstat as lstat9, readdir as readdir5, readFile as readFile12 } from "node:fs/promises";
|
|
28366
|
-
import { join as join12 } from "node:path";
|
|
28367
|
-
var decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
28368
|
-
var info = (path3) => lstat9(path3).catch((error62) => {
|
|
28369
|
-
if (error62.code === "ENOENT") return void 0;
|
|
28370
|
-
throw error62;
|
|
28371
|
-
});
|
|
28372
|
-
async function recordFolders(root) {
|
|
28373
|
-
return (await readdir5(join12(root, ...RECORDS_ROOT.split("/"))).catch((error62) => {
|
|
28374
|
-
if (error62.code === "ENOENT") return [];
|
|
28375
|
-
throw error62;
|
|
28376
|
-
})).filter(isRecordFolder);
|
|
28377
|
-
}
|
|
28378
|
-
async function findRecord(root, id) {
|
|
28379
|
-
for (const folder of await recordFolders(root)) {
|
|
28380
|
-
const path3 = `${RECORDS_ROOT}/${folder}/${id}.md`;
|
|
28381
|
-
if ((await info(join12(root, ...path3.split("/"))))?.isFile()) return path3;
|
|
28382
|
-
}
|
|
28383
|
-
return void 0;
|
|
28384
|
-
}
|
|
28385
|
-
async function readPendingRecords(root, run) {
|
|
28386
|
-
const status = (await run(["status", "--porcelain=v1", "-z", "--untracked-files=all", "--no-renames", "--", RECORDS_ROOT])).toString("utf8").split("\0").filter(Boolean);
|
|
28387
|
-
const files = /* @__PURE__ */ new Map();
|
|
28388
|
-
const altered = [];
|
|
28389
|
-
const problems = [];
|
|
28390
|
-
for (const entry2 of status) {
|
|
28391
|
-
const state = entry2.slice(0, 2);
|
|
28392
|
-
const path3 = entry2.slice(3);
|
|
28393
|
-
if (state !== "??" && !state.includes("A")) {
|
|
28394
|
-
altered.push(path3);
|
|
28395
|
-
continue;
|
|
28396
|
-
}
|
|
28397
|
-
const file2 = join12(root, ...path3.split("/"));
|
|
28398
|
-
const stat3 = await info(file2);
|
|
28399
|
-
if (!stat3?.isFile() || stat3.isSymbolicLink()) {
|
|
28400
|
-
problems.push(docProblem("PATH_UNSUPPORTED", path3));
|
|
28401
|
-
continue;
|
|
28402
|
-
}
|
|
28403
|
-
if (stat3.size > 1024 * 1024) {
|
|
28404
|
-
problems.push(docProblem("FILE_TOO_LARGE", path3));
|
|
28405
|
-
continue;
|
|
28406
|
-
}
|
|
28407
|
-
try {
|
|
28408
|
-
files.set(path3, decoder.decode(await readFile12(file2)));
|
|
28409
|
-
} catch {
|
|
28410
|
-
problems.push(docProblem("INVALID_CHARACTERS", path3));
|
|
28411
|
-
}
|
|
28412
|
-
}
|
|
28413
|
-
const folders = await recordFolders(root);
|
|
28414
|
-
for (const path3 of files.keys()) {
|
|
28415
|
-
const id = recordIdOf(path3);
|
|
28416
|
-
if (!id) continue;
|
|
28417
|
-
for (const folder of folders) {
|
|
28418
|
-
const other = `${RECORDS_ROOT}/${folder}/${id}.md`;
|
|
28419
|
-
if (other !== path3 && !files.has(other) && (await info(join12(root, ...other.split("/"))))?.isFile()) problems.push(docProblem("DUPLICATE_RECORD_ID", path3, { id, other }));
|
|
28420
|
-
}
|
|
28421
|
-
}
|
|
28422
|
-
return { files, altered: altered.sort(), problems };
|
|
28423
|
-
}
|
|
28840
|
+
import { join as join15 } from "node:path";
|
|
28424
28841
|
|
|
28425
28842
|
// src/commands/project.ts
|
|
28843
|
+
import { join as join14 } from "node:path";
|
|
28426
28844
|
var operations = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"];
|
|
28427
28845
|
async function openProject(cwd, options = {}) {
|
|
28428
28846
|
const reader = storeReader(cwd);
|
|
28429
28847
|
const location = await reader.location();
|
|
28430
28848
|
if (options.writing) {
|
|
28431
|
-
for (const marker2 of operations) if (await fileInfo(
|
|
28849
|
+
for (const marker2 of operations) if (await fileInfo(join14(location.gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
|
|
28432
28850
|
if (await reader.hasUnmerged()) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
28433
28851
|
}
|
|
28434
28852
|
const raw = await readConfigFile(location.root);
|
|
@@ -28461,13 +28879,20 @@ function documentsOf(files) {
|
|
|
28461
28879
|
|
|
28462
28880
|
// src/commands/documents.ts
|
|
28463
28881
|
var draftMark = (doc) => doc.draft ? " (" + t2("docs.draft") + ")" : "";
|
|
28882
|
+
var stateLabel = {
|
|
28883
|
+
added: () => t2("docs.state.added"),
|
|
28884
|
+
modified: () => t2("docs.state.modified"),
|
|
28885
|
+
deleted: () => t2("docs.state.deleted")
|
|
28886
|
+
};
|
|
28887
|
+
var stateMark = (state2) => state2 === "committed" ? "" : " (" + stateLabel[state2]() + ")";
|
|
28464
28888
|
var line = (doc) => `${doc.id} ${doc.title}${draftMark(doc)} \u2014 ${doc.description}`;
|
|
28889
|
+
var fileLine = (f) => f.title ? `${f.path} ${f.title} \u2014 ${f.description}` : f.path;
|
|
28465
28890
|
var titledSources = (d, byId) => d.sources.map((s) => "id" in s ? { id: s.id, title: byId.get(s.id)?.title ?? null, ...s.note ? { note: s.note } : {} } : s);
|
|
28466
28891
|
async function working(project) {
|
|
28467
28892
|
const { documents } = await project.cache.documents.list();
|
|
28468
28893
|
return {
|
|
28469
28894
|
byId: new Map(documents.map((d) => [d.id, d])),
|
|
28470
|
-
read: (path3) => readFile13(
|
|
28895
|
+
read: (path3) => readFile13(join15(project.root, ...path3.split("/")), "utf8"),
|
|
28471
28896
|
referencing: async (id) => [...new Set((await project.cache.documents.referencing(id)).map((r) => r.from))]
|
|
28472
28897
|
};
|
|
28473
28898
|
}
|
|
@@ -28493,7 +28918,7 @@ async function showDocuments(project, ids, at, kinds, otherResource) {
|
|
|
28493
28918
|
...doc.sources.map((s) => "id" in s ? { id: s.id, title: view.byId.get(s.id)?.title ?? null } : { title: s.title, url: s.url })
|
|
28494
28919
|
] : [];
|
|
28495
28920
|
const referencedBy = (await view.referencing(id)).map((from) => ({ id: from, title: view.byId.get(from)?.title ?? null }));
|
|
28496
|
-
const files = doc.kind === "instruction" && at === void 0 ? { files: (await
|
|
28921
|
+
const files = doc.kind === "instruction" && at === void 0 ? { files: (await readInstructionFiles(project.root, doc.path)).files } : {};
|
|
28497
28922
|
return { id, kind: doc.kind, path: doc.path, title: doc.title, text: await view.read(doc.path), references, referencedBy, ...files };
|
|
28498
28923
|
}));
|
|
28499
28924
|
const named = (r) => r.id ? r.id + " " + (r.title ?? "(" + t2("docs.missing") + ")") : r.title + " <" + r.url + ">";
|
|
@@ -28502,7 +28927,7 @@ async function showDocuments(project, ids, at, kinds, otherResource) {
|
|
|
28502
28927
|
d.text.replace(/\n$/, ""),
|
|
28503
28928
|
...section("-- " + t2("docs.references"), d.references.map(named)),
|
|
28504
28929
|
...section("-- " + t2("docs.referencedBy"), d.referencedBy.map(named)),
|
|
28505
|
-
...section("-- " + t2("docs.files"), d.files ?? [])
|
|
28930
|
+
...section("-- " + t2("docs.files"), (d.files ?? []).map(fileLine))
|
|
28506
28931
|
].join("\n"));
|
|
28507
28932
|
return { json: { ...at ? { commit: at } : {}, documents: shown }, text: blocks.join("\n\n") + "\n" };
|
|
28508
28933
|
}
|
|
@@ -28598,6 +29023,22 @@ async function since(project, value, head) {
|
|
|
28598
29023
|
const after = new Set(project.reader.decode(await project.reader.run(["rev-list", `${commit}..${head}`, "--"])).split("\n").filter(Boolean));
|
|
28599
29024
|
return (change2) => after.has(change2.commit);
|
|
28600
29025
|
}
|
|
29026
|
+
async function sinceRange(project, value, head) {
|
|
29027
|
+
if (value === void 0) return {};
|
|
29028
|
+
if (/^\d{4}-\d\d-\d\d/.test(value)) {
|
|
29029
|
+
const from = Date.parse(value);
|
|
29030
|
+
if (!Number.isFinite(from)) throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29031
|
+
return { from };
|
|
29032
|
+
}
|
|
29033
|
+
let commit;
|
|
29034
|
+
try {
|
|
29035
|
+
commit = await project.reader.resolve(value);
|
|
29036
|
+
} catch {
|
|
29037
|
+
throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29038
|
+
}
|
|
29039
|
+
if (!head) return { commits: [] };
|
|
29040
|
+
return { commits: project.reader.decode(await project.reader.run(["rev-list", `${commit}..${head}`, "--"])).split("\n").filter(Boolean) };
|
|
29041
|
+
}
|
|
28601
29042
|
var contains = (query) => {
|
|
28602
29043
|
const wanted = query?.trim().toLowerCase();
|
|
28603
29044
|
return (...texts) => !wanted || texts.some((value) => value?.toLowerCase().includes(wanted));
|
|
@@ -28620,16 +29061,37 @@ function selected(rows, fields2) {
|
|
|
28620
29061
|
text: rows.length ? text2(rows.map((row) => fields2.map((f) => cell(row[f])).join(" "))) : ""
|
|
28621
29062
|
};
|
|
28622
29063
|
}
|
|
29064
|
+
var PAGE_SIZE = 20;
|
|
29065
|
+
function paginate(rows, keyOf, options) {
|
|
29066
|
+
let start = 0;
|
|
29067
|
+
if (options.after !== void 0) {
|
|
29068
|
+
const at = rows.findIndex((row) => keyOf(row) === options.after);
|
|
29069
|
+
if (at < 0) throw new CommandError("INVALID_VALUE", t2("list.after", { cursor: options.after }));
|
|
29070
|
+
start = at + 1;
|
|
29071
|
+
}
|
|
29072
|
+
const size = options.all ? rows.length : options.limit ?? PAGE_SIZE;
|
|
29073
|
+
const shown = rows.slice(start, start + size);
|
|
29074
|
+
const more = start + shown.length < rows.length && shown.length > 0;
|
|
29075
|
+
return { rows: shown, total: rows.length, next: more ? keyOf(shown[shown.length - 1]) : null };
|
|
29076
|
+
}
|
|
29077
|
+
var pageLine = (page, before = 0, unit = t2("list.items")) => page.next === null ? [] : [t2("list.more", { shown: before + page.rows.length, total: page.total, unit, cursor: page.next })];
|
|
29078
|
+
function aside(format2, lines) {
|
|
29079
|
+
if (format2 === "text" && lines.length) process.stderr.write(lines.join("\n") + "\n");
|
|
29080
|
+
return "";
|
|
29081
|
+
}
|
|
28623
29082
|
|
|
28624
29083
|
// src/commands/specs.ts
|
|
28625
29084
|
var specKinds = ["feature", "requirement", "design"];
|
|
28626
29085
|
var specSorts = ["order", "title", "updated"];
|
|
28627
|
-
var columns = ["id", "kind", "feature", "path", "title", "description", "order", "draft", "requirements", "sources", "designs", "updated", "line"];
|
|
29086
|
+
var columns = ["id", "kind", "feature", "path", "title", "description", "order", "draft", "state", "previousPath", "requirements", "sources", "designs", "updated", "line"];
|
|
28628
29087
|
var kindLabel = { feature: () => t2("docs.feature"), requirement: () => t2("docs.requirement"), design: () => t2("docs.design") };
|
|
28629
29088
|
var runSpecsList = (options) => runCommand("specs", options.format, async () => {
|
|
28630
29089
|
const fields2 = checkFields(options.fields, columns);
|
|
28631
29090
|
const project = await openProject(process.cwd());
|
|
28632
|
-
const
|
|
29091
|
+
const listed3 = await project.cache.documents.list();
|
|
29092
|
+
const problems = listed3.problems;
|
|
29093
|
+
const states = await documentStates(project, listed3.documents);
|
|
29094
|
+
const documents = states.documents;
|
|
28633
29095
|
const byId = new Map(documents.map((d) => [d.id, d]));
|
|
28634
29096
|
const arranged = arrangeDocuments(documents);
|
|
28635
29097
|
let features = arranged.features;
|
|
@@ -28671,6 +29133,8 @@ var runSpecsList = (options) => runCommand("specs", options.format, async () =>
|
|
|
28671
29133
|
description: doc.description,
|
|
28672
29134
|
...doc.kind === "requirement" || doc.kind === "design" ? { order: doc.order } : {},
|
|
28673
29135
|
...doc.draft ? { draft: true } : {},
|
|
29136
|
+
state: states.state(doc.id),
|
|
29137
|
+
...states.previousPath(doc.id) ? { previousPath: states.previousPath(doc.id) } : {},
|
|
28674
29138
|
...doc.kind === "design" ? { requirements: doc.requirements.map(titled), sources: titledSources(doc, byId) } : {},
|
|
28675
29139
|
...doc.kind === "requirement" ? { designs: designsOf.get(doc.id) ?? [] } : {},
|
|
28676
29140
|
...historyNeeded ? { updated: latest.get(doc.id) ?? null } : {},
|
|
@@ -28678,11 +29142,16 @@ var runSpecsList = (options) => runCommand("specs", options.format, async () =>
|
|
|
28678
29142
|
})));
|
|
28679
29143
|
if (options.sort === "title") rows.sort((a, b) => a.title.localeCompare(b.title));
|
|
28680
29144
|
if (options.sort === "updated") rows.sort((a, b) => (newest.get(a.id) ?? Infinity) - (newest.get(b.id) ?? Infinity));
|
|
28681
|
-
const
|
|
29145
|
+
const grouped = options.sort === "order";
|
|
29146
|
+
const groups = features.map((f) => ({ id: f.index.id, rows: rows.filter((r) => r.feature === f.index.feature) })).filter((g) => g.rows.length);
|
|
29147
|
+
const page = grouped ? paginate(groups, (g) => g.id, options) : paginate(rows, (r) => r.id, options);
|
|
29148
|
+
const shown = grouped ? page.rows.flatMap((g) => g.rows) : page.rows;
|
|
29149
|
+
const more = pageLine(page, 0, grouped ? t2("list.features") : t2("list.items"));
|
|
29150
|
+
const paging = { total: page.total, next: page.next, unit: grouped ? "feature" : "document" };
|
|
28682
29151
|
const orphans = options.feature === void 0 ? arranged.orphans : [];
|
|
28683
29152
|
if (fields2) {
|
|
28684
29153
|
const picked = selected(shown, fields2);
|
|
28685
|
-
return { json: { documents: picked.json, orphans, problems }, text: picked.text };
|
|
29154
|
+
return { json: { documents: picked.json, orphans, problems, page: paging }, text: picked.text + aside(options.format, more) };
|
|
28686
29155
|
}
|
|
28687
29156
|
const refName = (r) => r.id + " " + (r.title ?? "(" + t2("docs.missing") + ")");
|
|
28688
29157
|
const excerpt = (row) => row.line ? [" " + row.line] : [];
|
|
@@ -28691,29 +29160,30 @@ var runSpecsList = (options) => runCommand("specs", options.format, async () =>
|
|
|
28691
29160
|
for (const f of features) {
|
|
28692
29161
|
const own2 = shown.filter((r) => r.feature === f.index.feature);
|
|
28693
29162
|
if (!own2.length) continue;
|
|
28694
|
-
out.push(`[${t2("docs.feature")}] ${f.index.id} ${f.index.title}${draftMark(f.index)} (${f.index.feature}) \u2014 ${f.index.description}`);
|
|
29163
|
+
out.push(`[${t2("docs.feature")}] ${f.index.id} ${f.index.title}${draftMark(f.index)}${stateMark(states.state(f.index.id))} (${f.index.feature}) \u2014 ${f.index.description}`);
|
|
28695
29164
|
const index = own2.find((r) => r.kind === "feature");
|
|
28696
29165
|
if (index) out.push(...excerpt(index));
|
|
28697
|
-
const requirement2 = (r) => [`${r.order} ${line(byId.get(r.id))}`, ...excerpt(r)];
|
|
29166
|
+
const requirement2 = (r) => [`${r.order} ${line(byId.get(r.id))}${stateMark(r.state)}`, ...excerpt(r)];
|
|
28698
29167
|
out.push(...section(" " + t2("docs.requirements"), own2.filter((r) => r.kind === "requirement").flatMap(requirement2), " "));
|
|
28699
29168
|
const designs = own2.filter((r) => r.kind === "design").flatMap((r) => {
|
|
28700
29169
|
const refs = r.requirements.length ? " [" + r.requirements.map(refName).join(", ") + "]" : "";
|
|
28701
29170
|
const notes = r.sources.map((s) => "id" in s ? refName(s) : s.title);
|
|
28702
|
-
return [`${r.order} ${line(byId.get(r.id))}${refs}`, ...notes.length ? [" ".repeat(String(r.order).length + 1) + t2("docs.sources") + ": " + notes.join(", ")] : [], ...excerpt(r)];
|
|
29171
|
+
return [`${r.order} ${line(byId.get(r.id))}${stateMark(r.state)}${refs}`, ...notes.length ? [" ".repeat(String(r.order).length + 1) + t2("docs.sources") + ": " + notes.join(", ")] : [], ...excerpt(r)];
|
|
28703
29172
|
});
|
|
28704
29173
|
out.push(...section(" " + t2("docs.designs"), designs, " "));
|
|
28705
29174
|
}
|
|
28706
29175
|
} else {
|
|
28707
29176
|
for (const r of shown) {
|
|
28708
29177
|
const when = r.updated ? ` \xB7 ${r.updated.date.slice(0, 10)} ${r.updated.author}` : "";
|
|
28709
|
-
out.push(`${r.id} ${r.title}${draftMark(r)} (${kindLabel[r.kind]()} \xB7 ${r.feature}) \u2014 ${r.description}${when}`, ...excerpt(r));
|
|
29178
|
+
out.push(`${r.id} ${r.title}${draftMark(r)}${stateMark(r.state)} (${kindLabel[r.kind]()} \xB7 ${r.feature}) \u2014 ${r.description}${when}`, ...excerpt(r));
|
|
28710
29179
|
}
|
|
28711
29180
|
}
|
|
28712
29181
|
const filtered = options.type !== void 0 || options.feature !== void 0 || options.withoutDesign || options.uncovered || options.draft || options.changedSince !== void 0 || options.author !== void 0 || options.q !== void 0;
|
|
28713
29182
|
if (!out.length) out.push(filtered ? t2("docs.noMatch") : t2("docs.empty"));
|
|
29183
|
+
out.push(...more);
|
|
28714
29184
|
if (orphans.length) out.push(t2("docs.orphans", { folders: orphans.join(", ") }));
|
|
28715
29185
|
if (problems.length) out.push(t2("docs.unreadable", { count: problems.length }));
|
|
28716
|
-
return { json: { documents: shown, orphans, problems }, text: text2(out) };
|
|
29186
|
+
return { json: { documents: shown, orphans, problems, page: paging }, text: text2(out) };
|
|
28717
29187
|
});
|
|
28718
29188
|
var runSpecsShow = (ids, options) => runCommand("specs", options.format, async () => {
|
|
28719
29189
|
const project = await openProject(process.cwd());
|
|
@@ -28723,16 +29193,19 @@ var runSpecsShow = (ids, options) => runCommand("specs", options.format, async (
|
|
|
28723
29193
|
var runSpecsNew = (kind2, path3, options) => runCommand("specs", options.format, async () => createDocument(await openProject(process.cwd()), kind2, path3, options));
|
|
28724
29194
|
|
|
28725
29195
|
// src/commands/instructions.ts
|
|
28726
|
-
import { lstat as
|
|
28727
|
-
import { join as
|
|
29196
|
+
import { lstat as lstat11 } from "node:fs/promises";
|
|
29197
|
+
import { join as join16 } from "node:path";
|
|
28728
29198
|
var instructionSorts = ["name", "updated"];
|
|
28729
|
-
var columns2 = ["id", "name", "path", "title", "description", "draft", "files", "updated", "line"];
|
|
29199
|
+
var columns2 = ["id", "name", "path", "title", "description", "draft", "state", "previousPath", "files", "updated", "line"];
|
|
28730
29200
|
var AGENTS = "AGENTS.md";
|
|
28731
29201
|
var runInstructionsList = (options) => runCommand("instructions", options.format, async () => {
|
|
28732
29202
|
const fields2 = checkFields(options.fields, columns2);
|
|
28733
29203
|
const project = await openProject(process.cwd());
|
|
28734
|
-
const
|
|
28735
|
-
const
|
|
29204
|
+
const listed3 = await project.cache.documents.list();
|
|
29205
|
+
const problems = listed3.problems;
|
|
29206
|
+
const states = await documentStates(project, listed3.documents);
|
|
29207
|
+
const documents = states.documents;
|
|
29208
|
+
const agents2 = { path: AGENTS, exists: !!(await lstat11(join16(project.root, AGENTS)).catch(() => void 0))?.isFile() };
|
|
28736
29209
|
const historyNeeded = options.sort === "updated" || options.author !== void 0 || !!fields2?.includes("updated");
|
|
28737
29210
|
const latest = /* @__PURE__ */ new Map();
|
|
28738
29211
|
const newest = /* @__PURE__ */ new Map();
|
|
@@ -28753,33 +29226,46 @@ var runInstructionsList = (options) => runCommand("instructions", options.format
|
|
|
28753
29226
|
}
|
|
28754
29227
|
const matched = options.q !== void 0 ? await project.cache.documents.matching(options.q) : void 0;
|
|
28755
29228
|
const instructions2 = documents.filter((d) => d.kind === "instruction").filter((d) => (!touched || touched.has(d.id)) && (!matched || matched.has(d.id))).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
28756
|
-
const rows = await Promise.all(instructions2.map(async (d) =>
|
|
28757
|
-
|
|
28758
|
-
|
|
28759
|
-
|
|
28760
|
-
|
|
28761
|
-
|
|
28762
|
-
|
|
28763
|
-
|
|
28764
|
-
|
|
28765
|
-
|
|
28766
|
-
|
|
29229
|
+
const rows = await Promise.all(instructions2.map(async (d) => {
|
|
29230
|
+
const state2 = states.state(d.id);
|
|
29231
|
+
const { files, limited } = state2 === "deleted" ? { files: [], limited: false } : await readInstructionFiles(project.root, d.path);
|
|
29232
|
+
return {
|
|
29233
|
+
id: d.id,
|
|
29234
|
+
name: d.name,
|
|
29235
|
+
path: d.path,
|
|
29236
|
+
title: d.title,
|
|
29237
|
+
description: d.description,
|
|
29238
|
+
...d.draft ? { draft: true } : {},
|
|
29239
|
+
state: state2,
|
|
29240
|
+
...states.previousPath(d.id) ? { previousPath: states.previousPath(d.id) } : {},
|
|
29241
|
+
files,
|
|
29242
|
+
...limited ? { filesLimited: true } : {},
|
|
29243
|
+
...historyNeeded ? { updated: latest.get(d.id) ?? null } : {},
|
|
29244
|
+
...matched ? { line: matched.get(d.id) } : {}
|
|
29245
|
+
};
|
|
29246
|
+
}));
|
|
28767
29247
|
if (options.sort === "updated") rows.sort((a, b) => (newest.get(a.id) ?? Infinity) - (newest.get(b.id) ?? Infinity));
|
|
28768
|
-
const
|
|
29248
|
+
const page = paginate(rows, (r) => r.id, options);
|
|
29249
|
+
const shown = page.rows;
|
|
29250
|
+
const paging = { total: page.total, next: page.next, unit: "instruction" };
|
|
28769
29251
|
const unreadable2 = problems.filter((p) => p.path.startsWith(INSTRUCTIONS_ROOT + "/"));
|
|
28770
29252
|
if (fields2) {
|
|
28771
29253
|
const picked = selected(shown, fields2);
|
|
28772
|
-
return {
|
|
29254
|
+
return {
|
|
29255
|
+
json: { agents: agents2, instructions: picked.json, problems: unreadable2, page: paging },
|
|
29256
|
+
text: selected(shown.map((r) => ({ ...r, files: r.files.map((f) => f.path) })), fields2).text + aside(options.format, pageLine(page))
|
|
29257
|
+
};
|
|
28773
29258
|
}
|
|
28774
29259
|
const out = [agents2.exists ? t2("instructions.agents") : t2("instructions.noAgents")];
|
|
28775
29260
|
for (const r of shown) {
|
|
28776
|
-
const files = r.files ? " \xB7 " + t2("instructions.files", { count: r.files }) : "";
|
|
28777
29261
|
const when = r.updated ? ` \xB7 ${r.updated.date.slice(0, 10)} ${r.updated.author}` : "";
|
|
28778
|
-
out.push(`${r.id} ${r.title}${draftMark(r)} (${r.name}) \u2014 ${r.description}${
|
|
29262
|
+
out.push(`${r.id} ${r.title}${draftMark(r)}${stateMark(r.state)} (${r.name}) \u2014 ${r.description}${when}`, ...r.line ? [" " + r.line] : []);
|
|
29263
|
+
out.push(...r.files.map((f) => " " + fileLine(f)), ...r.filesLimited ? [" " + t2("instructions.filesLimited", { count: r.files.length })] : []);
|
|
28779
29264
|
}
|
|
28780
29265
|
if (!shown.length) out.push(options.q !== void 0 || options.author !== void 0 ? t2("docs.noMatch") : t2("instructions.empty"));
|
|
29266
|
+
out.push(...pageLine(page));
|
|
28781
29267
|
if (unreadable2.length) out.push(t2("docs.unreadable", { count: unreadable2.length }));
|
|
28782
|
-
return { json: { agents: agents2, instructions: shown, problems: unreadable2 }, text: text2(out) };
|
|
29268
|
+
return { json: { agents: agents2, instructions: shown, problems: unreadable2, page: paging }, text: text2(out) };
|
|
28783
29269
|
});
|
|
28784
29270
|
async function resolveInstructions(project, targets, at) {
|
|
28785
29271
|
const view = at === void 0 ? await working(project) : await committed(project, at);
|
|
@@ -28805,9 +29291,9 @@ var runInstructionsShow = (targets, options) => runCommand("instructions", optio
|
|
|
28805
29291
|
var runInstructionsNew = (name2, options) => runCommand("instructions", options.format, async () => createDocument(await openProject(process.cwd()), "instruction", name2, options));
|
|
28806
29292
|
|
|
28807
29293
|
// src/adapters/filesystem/document-warnings.ts
|
|
28808
|
-
import { lstat as
|
|
28809
|
-
import { join as
|
|
28810
|
-
var info2 = async (path3) =>
|
|
29294
|
+
import { lstat as lstat12, readdir as readdir6 } from "node:fs/promises";
|
|
29295
|
+
import { join as join17 } from "node:path";
|
|
29296
|
+
var info2 = async (path3) => lstat12(path3).catch((e) => {
|
|
28811
29297
|
if (e.code === "ENOENT") return void 0;
|
|
28812
29298
|
throw e;
|
|
28813
29299
|
});
|
|
@@ -28815,11 +29301,11 @@ var posix = (path3) => path3.split("\\").join("/");
|
|
|
28815
29301
|
async function listAssets(root) {
|
|
28816
29302
|
const out = [];
|
|
28817
29303
|
async function visit2(relative, depth) {
|
|
28818
|
-
const stat3 = await info2(
|
|
29304
|
+
const stat3 = await info2(join17(root, relative));
|
|
28819
29305
|
if (!stat3 || stat3.isSymbolicLink()) return;
|
|
28820
29306
|
if (stat3.isDirectory()) {
|
|
28821
29307
|
if (depth > 8 || out.length > 4e3) return;
|
|
28822
|
-
for (const name2 of (await readdir6(
|
|
29308
|
+
for (const name2 of (await readdir6(join17(root, relative))).sort()) await visit2(relative + "/" + name2, depth + 1);
|
|
28823
29309
|
} else if (stat3.isFile()) out.push({ path: posix(relative), bytes: stat3.size });
|
|
28824
29310
|
}
|
|
28825
29311
|
await visit2(ASSETS_DIR, 0);
|
|
@@ -28828,7 +29314,7 @@ async function listAssets(root) {
|
|
|
28828
29314
|
async function readDocumentWarnings(root, documents) {
|
|
28829
29315
|
const outside = [...new Set(documentBodyLinks(documents).map((l) => l.target).filter((t3) => !t3.startsWith(".gitifact/") || t3.startsWith(INSTRUCTIONS_ROOT + "/")))];
|
|
28830
29316
|
const existing = /* @__PURE__ */ new Set();
|
|
28831
|
-
for (const target of outside) if ((await info2(
|
|
29317
|
+
for (const target of outside) if ((await info2(join17(root, ...target.split("/"))))?.isFile()) existing.add(target);
|
|
28832
29318
|
return documentWarnings(documents, await listAssets(root), existing);
|
|
28833
29319
|
}
|
|
28834
29320
|
|
|
@@ -28852,30 +29338,30 @@ var runCheck = (options) => runCommand("check", options.format, async () => {
|
|
|
28852
29338
|
|
|
28853
29339
|
// src/commands/changes.ts
|
|
28854
29340
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
28855
|
-
import { mkdir as mkdir6, open as
|
|
28856
|
-
import { join as
|
|
29341
|
+
import { mkdir as mkdir6, open as open5, readFile as readFile16, rename as rename3, rmdir, unlink as unlink4, writeFile as writeFile4 } from "node:fs/promises";
|
|
29342
|
+
import { join as join20 } from "node:path";
|
|
28857
29343
|
|
|
28858
29344
|
// src/adapters/filesystem/agent-inputs.ts
|
|
28859
|
-
import { createHash as
|
|
28860
|
-
import { lstat as
|
|
29345
|
+
import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
|
|
29346
|
+
import { lstat as lstat13, mkdir as mkdir5, readdir as readdir7, realpath as realpath3, unlink as unlink3, writeFile as writeFile3 } from "node:fs/promises";
|
|
28861
29347
|
import { tmpdir } from "node:os";
|
|
28862
|
-
import { basename, dirname as dirname2, join as
|
|
29348
|
+
import { basename, dirname as dirname2, join as join18 } from "node:path";
|
|
28863
29349
|
var maxAge = 7 * 24 * 60 * 60 * 1e3;
|
|
28864
|
-
var info3 = (path3) =>
|
|
29350
|
+
var info3 = (path3) => lstat13(path3).catch((e) => {
|
|
28865
29351
|
if (e.code === "ENOENT") return void 0;
|
|
28866
29352
|
throw e;
|
|
28867
29353
|
});
|
|
28868
29354
|
var comparable = (path3) => process.platform === "win32" ? path3.toLowerCase() : path3;
|
|
28869
29355
|
async function candidates(root, controls) {
|
|
28870
|
-
const key =
|
|
28871
|
-
return { system:
|
|
29356
|
+
const key = createHash4("sha256").update(comparable(await realpath3(root))).digest("hex").slice(0, 16);
|
|
29357
|
+
return { system: join18((controls.tmpdir ?? tmpdir)(), "gitifact", key), project: join18(root, ".gitifact", "tmp") };
|
|
28872
29358
|
}
|
|
28873
29359
|
async function writable(directory) {
|
|
28874
29360
|
try {
|
|
28875
29361
|
await mkdir5(directory, { recursive: true });
|
|
28876
29362
|
const stat3 = await info3(directory);
|
|
28877
29363
|
if (!stat3?.isDirectory() || stat3.isSymbolicLink()) return false;
|
|
28878
|
-
const probe =
|
|
29364
|
+
const probe = join18(directory, ".probe-" + randomBytes2(8).toString("hex"));
|
|
28879
29365
|
await writeFile3(probe, "", { flag: "wx" });
|
|
28880
29366
|
await unlink3(probe);
|
|
28881
29367
|
return true;
|
|
@@ -28889,7 +29375,7 @@ async function prepareAgentInputs(root, controls = {}) {
|
|
|
28889
29375
|
if (await writable(system)) directory = system;
|
|
28890
29376
|
else if (await writable(project)) {
|
|
28891
29377
|
directory = project;
|
|
28892
|
-
await writeFile3(
|
|
29378
|
+
await writeFile3(join18(project, ".gitignore"), "*\n", { flag: "wx" }).catch((e) => {
|
|
28893
29379
|
if (e.code !== "EEXIST") throw e;
|
|
28894
29380
|
});
|
|
28895
29381
|
}
|
|
@@ -28897,10 +29383,10 @@ async function prepareAgentInputs(root, controls = {}) {
|
|
|
28897
29383
|
const now = (controls.now ?? Date.now)();
|
|
28898
29384
|
for (const name2 of await readdir7(directory).catch(() => [])) {
|
|
28899
29385
|
if (name2 === ".gitignore") continue;
|
|
28900
|
-
const stat3 = await info3(
|
|
28901
|
-
if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await unlink3(
|
|
29386
|
+
const stat3 = await info3(join18(directory, name2)).catch(() => void 0);
|
|
29387
|
+
if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await unlink3(join18(directory, name2)).catch(() => void 0);
|
|
28902
29388
|
}
|
|
28903
|
-
return { commit:
|
|
29389
|
+
return { commit: join18(directory, "commit.json") };
|
|
28904
29390
|
}
|
|
28905
29391
|
async function discardAgentInput(root, file2, controls = {}) {
|
|
28906
29392
|
try {
|
|
@@ -28921,18 +29407,18 @@ async function discardAgentInput(root, file2, controls = {}) {
|
|
|
28921
29407
|
}
|
|
28922
29408
|
|
|
28923
29409
|
// src/commands/commit-files.ts
|
|
28924
|
-
import { createHash as
|
|
28925
|
-
import { lstat as
|
|
28926
|
-
import { join as
|
|
29410
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
29411
|
+
import { lstat as lstat14, readFile as readFile14 } from "node:fs/promises";
|
|
29412
|
+
import { join as join19 } from "node:path";
|
|
28927
29413
|
var fail3 = (message) => {
|
|
28928
29414
|
throw new CommandError("INVALID_COMMIT", message);
|
|
28929
29415
|
};
|
|
28930
|
-
var hash2 = (value) =>
|
|
29416
|
+
var hash2 = (value) => createHash5("sha256").update(value).digest("hex");
|
|
28931
29417
|
var optional2 = (path3) => readFile14(path3).catch((e) => {
|
|
28932
29418
|
if (e.code === "ENOENT") return null;
|
|
28933
29419
|
throw e;
|
|
28934
29420
|
});
|
|
28935
|
-
var info4 = (path3) =>
|
|
29421
|
+
var info4 = (path3) => lstat14(path3).catch((e) => {
|
|
28936
29422
|
if (e.code === "ENOENT") return void 0;
|
|
28937
29423
|
throw e;
|
|
28938
29424
|
});
|
|
@@ -28958,11 +29444,11 @@ async function fingerprint(root, path3) {
|
|
|
28958
29444
|
validPath2(path3);
|
|
28959
29445
|
const parts = path3.split("/");
|
|
28960
29446
|
for (let i = 1; i <= parts.length; i++) {
|
|
28961
|
-
const target =
|
|
29447
|
+
const target = join19(root, ...parts.slice(0, i));
|
|
28962
29448
|
const stat3 = await info4(target);
|
|
28963
29449
|
if (!stat3) return null;
|
|
28964
29450
|
if (stat3.isSymbolicLink() || (i < parts.length ? !stat3.isDirectory() : !stat3.isFile())) fail3(t2("commitFiles.linkOrDirectory"));
|
|
28965
|
-
if (i < parts.length && await info4(
|
|
29451
|
+
if (i < parts.length && await info4(join19(target, ".git"))) fail3(t2("commitFiles.nestedRepository"));
|
|
28966
29452
|
if (i === parts.length) {
|
|
28967
29453
|
if (stat3.nlink !== 1 || stat3.size > 16 * 1024 * 1024) fail3(t2("commitFiles.hardLinkOrSize"));
|
|
28968
29454
|
return hash2(Buffer.concat([Buffer.from(String(stat3.mode & 73) + "\0"), await readFile14(target)]));
|
|
@@ -29029,18 +29515,12 @@ async function readStdin(stream) {
|
|
|
29029
29515
|
var fields = ["paths", "message", "authorization", "migration"];
|
|
29030
29516
|
var integration = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply"];
|
|
29031
29517
|
var MIGRATION_TARGET = "0.8.0";
|
|
29518
|
+
var PATH_LIMIT = 128;
|
|
29519
|
+
var MIGRATION_PATH_LIMIT = 5e3;
|
|
29032
29520
|
var same2 = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
29033
29521
|
var problemLines2 = (problems) => problems.map((p) => " " + p.code + " " + p.message);
|
|
29034
29522
|
var underDocuments = (path3) => path3.startsWith(SPEC_ROOT + "/") || path3.startsWith(WIKI_ROOT + "/") || path3 === HISTORY_PATH;
|
|
29035
|
-
|
|
29036
|
-
const headFiles = head ? await project.cache.history.filesAt(head) : /* @__PURE__ */ new Map();
|
|
29037
|
-
const [working2, records2] = await Promise.all([project.cache.documents.files(), project.pendingRecords()]);
|
|
29038
|
-
return { headFiles, working: working2, records: records2, ...compareDocumentSets(headFiles, working2.files) };
|
|
29039
|
-
}
|
|
29040
|
-
var uncovered = (changes2, records2) => {
|
|
29041
|
-
const covered = new Set(records2.flatMap((r) => r.docs));
|
|
29042
|
-
return changes2.filter((c) => c.types.some((type) => type !== "created") && !covered.has(c.id)).map((c) => c.id);
|
|
29043
|
-
};
|
|
29523
|
+
var pendingChanges = (project, head) => workingChanges(project, head);
|
|
29044
29524
|
var shared = (changes2, records2) => changes2.map((c) => c.id).filter((id) => records2.filter((r) => r.docs.includes(id)).length > 1);
|
|
29045
29525
|
var changeLine = (c) => `${c.types.join(",").padEnd(9)} ${c.id} ${c.title} \u2014 ${c.path}${c.previousPath ? " (\u2190 " + c.previousPath + ")" : ""}`;
|
|
29046
29526
|
var recordLine = (r, changes2) => {
|
|
@@ -29087,7 +29567,7 @@ var runChangesCommit = (options, controls = {}) => runCommand("changes", options
|
|
|
29087
29567
|
async function commitChanges(cwd, input2, dryRun) {
|
|
29088
29568
|
const request = object3(input2);
|
|
29089
29569
|
if (Object.keys(request).some((k) => !fields.includes(k))) fail3(t2("commit.unknownField"));
|
|
29090
|
-
const selected2 = paths(request.paths);
|
|
29570
|
+
const selected2 = paths(request.paths, request.migration === true ? MIGRATION_PATH_LIMIT : PATH_LIMIT);
|
|
29091
29571
|
const authorization = object3(request.authorization);
|
|
29092
29572
|
if (Object.keys(authorization).sort().join(",") !== "basis,evidence" || !["user-request", "project-policy"].includes(String(authorization.basis))) fail3(t2("commit.authorizationRequired"));
|
|
29093
29573
|
text3(authorization.evidence, 2e3);
|
|
@@ -29118,7 +29598,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29118
29598
|
...stdin ? { input: stdin } : {}
|
|
29119
29599
|
});
|
|
29120
29600
|
const staged = async (index) => (await git(["diff", "--cached", "--ita-visible-in-index", "--name-only", "--no-renames", "-z"], index)).toString("utf8").split("\0").filter(Boolean).sort();
|
|
29121
|
-
const busy =
|
|
29601
|
+
const busy = join20(gitDir, "gitifact-changes-commit.lock");
|
|
29122
29602
|
if (await info4(busy)) fail3(t2("commit.busy", { path: busy }));
|
|
29123
29603
|
if ((await staged()).length) fail3(t2("commit.existingStaging"));
|
|
29124
29604
|
const indexHash = hash2(await optional2(indexPath) ?? Buffer.alloc(0));
|
|
@@ -29157,7 +29637,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29157
29637
|
if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
|
|
29158
29638
|
return { outcome: "dry-run", committed: false, ...summary, commit: void 0 };
|
|
29159
29639
|
}
|
|
29160
|
-
const temporary =
|
|
29640
|
+
const temporary = join20(gitDir, "gitifact-commit-index-" + randomUUID5());
|
|
29161
29641
|
let owned = false;
|
|
29162
29642
|
let uncertain = false;
|
|
29163
29643
|
let commitStarted = false;
|
|
@@ -29171,16 +29651,16 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29171
29651
|
if (e.code === "EEXIST") fail3(t2("commit.busy", { path: busy }));
|
|
29172
29652
|
throw e;
|
|
29173
29653
|
}
|
|
29174
|
-
const lock = indexLock = await
|
|
29654
|
+
const lock = indexLock = await open5(indexPath + ".lock", "wx", 384);
|
|
29175
29655
|
const original = await optional2(indexPath);
|
|
29176
29656
|
if (hash2(original ?? Buffer.alloc(0)) !== indexHash || (await staged()).length) fail3(t2("commit.indexChanged"));
|
|
29177
29657
|
const before = await hashes([...started.keys()]);
|
|
29178
29658
|
for (const [path3, value] of started) if (before.get(path3) !== value) fail3(t2("commit.fileChangedRunning", { path: path3 }));
|
|
29179
|
-
await writeFile4(
|
|
29659
|
+
await writeFile4(join20(busy, "recovery.json"), JSON.stringify({ before: base, originalIndex: original?.toString("base64") ?? null, temporary, paths: selected2, message }));
|
|
29180
29660
|
const locked = await hashes([.../* @__PURE__ */ new Set([...selected2, ...contextPaths])]);
|
|
29181
29661
|
if (original) await writeFile4(temporary, original, { flag: "wx" });
|
|
29182
29662
|
else await git(["read-tree", "--empty"], temporary);
|
|
29183
|
-
await git(["add", "--",
|
|
29663
|
+
await git(["add", "--pathspec-from-file=-", "--pathspec-file-nul"], temporary, Buffer.from(stageable.join("\0")));
|
|
29184
29664
|
const actual = await staged(temporary);
|
|
29185
29665
|
if (!actual.length || actual.some((p) => !selected2.includes(p))) fail3(t2("commit.unexpectedFiles"));
|
|
29186
29666
|
const storeFiles = selected2.filter((p) => record3(p) && locked.get(p) !== null);
|
|
@@ -29193,18 +29673,18 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29193
29673
|
const size = Number(header[2]);
|
|
29194
29674
|
if (end < 0 || header[1] !== "blob" || !Number.isSafeInteger(size) || output2[end + 1 + size] !== 10) fail3(t2("commit.stagedUnreadable", { path: path3 }));
|
|
29195
29675
|
const blob = output2.subarray(end + 1, end + 1 + size);
|
|
29196
|
-
const file2 = await readFile16(
|
|
29676
|
+
const file2 = await readFile16(join20(root, path3));
|
|
29197
29677
|
if (!blob.equals(file2) && !blob.equals(Buffer.from(file2.toString("latin1").replace(/\r\n/g, "\n"), "latin1"))) fail3(t2("commit.filterChanged"));
|
|
29198
29678
|
offset = end + size + 2;
|
|
29199
29679
|
}
|
|
29200
29680
|
}
|
|
29201
29681
|
const again = await hashes([...locked.keys()]);
|
|
29202
29682
|
for (const [path3, value] of locked) if (again.get(path3) !== value) fail3(t2("commit.fileChangedPreparing", { path: path3 }));
|
|
29203
|
-
for (const name2 of integration) if (await info4(
|
|
29683
|
+
for (const name2 of integration) if (await info4(join20(gitDir, name2))) fail3(t2("commit.integrationStarted"));
|
|
29204
29684
|
if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
|
|
29205
29685
|
const tree = (await git(["write-tree"], temporary)).toString("utf8").trim();
|
|
29206
29686
|
commitStarted = true;
|
|
29207
|
-
await git(["commit", "-
|
|
29687
|
+
await git(["commit", "-F", "-"], temporary, Buffer.from(message.trim() + (trailers.length ? "\n\n" + trailers.join("\n") : "") + "\n"));
|
|
29208
29688
|
const after = await project.reader.baseline();
|
|
29209
29689
|
if (!after.head) return fail3(t2("commit.resultUnknown"));
|
|
29210
29690
|
const raw = (await git(["cat-file", "commit", after.head])).toString("utf8");
|
|
@@ -29240,7 +29720,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29240
29720
|
await unlink4(temporary).catch(() => {
|
|
29241
29721
|
});
|
|
29242
29722
|
if (owned) {
|
|
29243
|
-
await unlink4(
|
|
29723
|
+
await unlink4(join20(busy, "recovery.json")).catch(() => {
|
|
29244
29724
|
});
|
|
29245
29725
|
await rmdir(busy);
|
|
29246
29726
|
}
|
|
@@ -29251,7 +29731,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29251
29731
|
|
|
29252
29732
|
// src/commands/records.ts
|
|
29253
29733
|
import { readFile as readFile17 } from "node:fs/promises";
|
|
29254
|
-
import { join as
|
|
29734
|
+
import { join as join21 } from "node:path";
|
|
29255
29735
|
var runRecordsNew = (options) => runCommand("records", options.format, async () => {
|
|
29256
29736
|
if (/[\r\n]/.test(options.title)) throw new CommandError("INVALID_VALUE", t2("docs.singleLine", { field: "title" }));
|
|
29257
29737
|
const docs = [...new Set(options.docs.flatMap((d) => d.split(",")).map((d) => d.trim()).filter(Boolean))];
|
|
@@ -29287,7 +29767,7 @@ var runRecordsShow = (ids, options) => runCommand("records", options.format, asy
|
|
|
29287
29767
|
const shown = await Promise.all([...new Set(ids)].map(async (id) => {
|
|
29288
29768
|
const path3 = await findRecord(project.root, id);
|
|
29289
29769
|
if (!path3) return { id, missing: true };
|
|
29290
|
-
const source2 = await readFile17(
|
|
29770
|
+
const source2 = await readFile17(join21(project.root, ...path3.split("/")), "utf8");
|
|
29291
29771
|
const log = project.reader.decode(await project.reader.run(["log", "-1", "--diff-filter=A", "--format=%H%x00%aN%x00%aI", "--", path3])).trim();
|
|
29292
29772
|
const [commit, author, date5] = log.split("\0");
|
|
29293
29773
|
return { id, path: path3, text: source2, commit: commit ? { commit, author, date: date5 } : null };
|
|
@@ -29312,18 +29792,17 @@ var runRecordsList = (options) => runCommand("records", options.format, async ()
|
|
|
29312
29792
|
const fields2 = checkFields(options.fields, options.doc === void 0 ? recordColumns : eventColumns);
|
|
29313
29793
|
const project = await openProject(process.cwd());
|
|
29314
29794
|
const head = await project.head();
|
|
29315
|
-
const after = await since(project, options.since, head);
|
|
29316
|
-
const by = byAuthor(options.author);
|
|
29317
|
-
const has = contains(options.q);
|
|
29318
|
-
const limit = (rows2) => options.limit === void 0 ? rows2 : rows2.slice(0, options.limit);
|
|
29319
29795
|
const sectionText = (sections2) => sections2.map((s) => s.body);
|
|
29320
29796
|
if (options.doc !== void 0) {
|
|
29797
|
+
const after = await since(project, options.since, head);
|
|
29798
|
+
const by = byAuthor(options.author);
|
|
29799
|
+
const has2 = contains(options.q);
|
|
29321
29800
|
const id = options.doc;
|
|
29322
29801
|
const events = head ? await project.cache.history.ofDocument(head, id) : [];
|
|
29323
29802
|
const { documents } = await project.cache.documents.list();
|
|
29324
29803
|
const title = documents.find((d) => d.id === id)?.title ?? events.map((e) => (e.after ?? e.before)?.title).find(Boolean) ?? null;
|
|
29325
29804
|
if (!title && !events.length) throw new CommandError("UNKNOWN_DOCUMENT", t2("docs.unknownDocument", { ids: id }));
|
|
29326
|
-
const
|
|
29805
|
+
const page = paginate(events.filter((e) => after(e) && by(e) && has2(e.message, ...e.records.flatMap((r) => [r.title, ...sectionText(r.sections)]))).map((e) => ({
|
|
29327
29806
|
commit: e.commit,
|
|
29328
29807
|
date: e.date,
|
|
29329
29808
|
author: e.author,
|
|
@@ -29332,10 +29811,13 @@ var runRecordsList = (options) => runCommand("records", options.format, async ()
|
|
|
29332
29811
|
types: e.types,
|
|
29333
29812
|
path: (e.after ?? e.before)?.path ?? null,
|
|
29334
29813
|
records: e.records
|
|
29335
|
-
})));
|
|
29814
|
+
})), (e) => e.commit, options);
|
|
29815
|
+
const rows2 = page.rows;
|
|
29816
|
+
const paging2 = { total: page.total, next: page.next, unit: "commit" };
|
|
29817
|
+
const more2 = pageLine(page, 0, t2("list.commits"));
|
|
29336
29818
|
if (fields2) {
|
|
29337
29819
|
const picked = selected(rows2, fields2);
|
|
29338
|
-
return { json: { doc: { id, title }, events: picked.json }, text: picked.text };
|
|
29820
|
+
return { json: { doc: { id, title }, events: picked.json, page: paging2 }, text: picked.text + aside(options.format, more2) };
|
|
29339
29821
|
}
|
|
29340
29822
|
const out2 = [`${id} ${title ?? ""}`.trimEnd()];
|
|
29341
29823
|
for (const e of rows2) {
|
|
@@ -29348,20 +29830,35 @@ var runRecordsList = (options) => runCommand("records", options.format, async ()
|
|
|
29348
29830
|
out2.push(" " + t2("docs.commit") + ": " + e.message);
|
|
29349
29831
|
}
|
|
29350
29832
|
if (!rows2.length) out2.push(events.length ? t2("docs.noMatch") : t2("docs.noHistory"));
|
|
29351
|
-
|
|
29833
|
+
if (more2.length) out2.push("", ...more2);
|
|
29834
|
+
return { json: { doc: { id, title }, events: rows2, page: paging2 }, text: text2(out2) };
|
|
29352
29835
|
}
|
|
29353
|
-
const
|
|
29836
|
+
const has = contains(options.q);
|
|
29837
|
+
const pending = (options.author !== void 0 ? [] : [...(await project.pendingRecords()).files].flatMap(([path3, source2]) => {
|
|
29354
29838
|
try {
|
|
29355
29839
|
return [parseRecordFile(path3, source2)];
|
|
29356
29840
|
} catch {
|
|
29357
29841
|
return [];
|
|
29358
29842
|
}
|
|
29359
|
-
}).map((r) => ({ id: r.id, title: r.title, commit: null, date: null, author: null, docs: r.docs, sections: r.sections.map((s) => ({ key: s.key, body: s.body })) }));
|
|
29360
|
-
const
|
|
29361
|
-
const
|
|
29843
|
+
}).map((r) => ({ id: r.id, title: r.title, commit: null, date: null, author: null, docs: r.docs, sections: r.sections.map((s) => ({ key: s.key, body: s.body })) }))).filter((r) => has(r.title, ...sectionText(r.sections))).sort((a, b) => a.id < b.id ? -1 : 1);
|
|
29844
|
+
const size = options.all ? void 0 : options.limit ?? PAGE_SIZE;
|
|
29845
|
+
const inPending = options.after === void 0 ? 0 : pending.findIndex((r) => r.id === options.after) + 1;
|
|
29846
|
+
const afterCommitted = options.after !== void 0 && !inPending ? options.after : void 0;
|
|
29847
|
+
const pendingShown = afterCommitted !== void 0 ? [] : pending.slice(inPending, size === void 0 ? void 0 : inPending + size);
|
|
29848
|
+
const filter = { author: options.author, q: options.q, ...await sinceRange(project, options.since, head) };
|
|
29849
|
+
const room = size === void 0 ? void 0 : size - pendingShown.length;
|
|
29850
|
+
const found = head ? await project.cache.history.records(head, filter, afterCommitted, room) : { records: [], total: 0 };
|
|
29851
|
+
if (!found) throw new CommandError("INVALID_VALUE", t2("list.after", { cursor: options.after }));
|
|
29852
|
+
const committed2 = found.records.map((r) => ({ id: r.id, title: r.title, commit: r.commit, date: r.date, author: r.author, docs: r.docs, sections: r.sections }));
|
|
29853
|
+
const rows = [...pendingShown, ...committed2];
|
|
29854
|
+
const total = pending.length + found.total;
|
|
29855
|
+
const before = afterCommitted === void 0 ? inPending : pending.length + await readThrough(project, head, filter, afterCommitted);
|
|
29856
|
+
const next = before + rows.length < total && rows.length ? rows[rows.length - 1].id : null;
|
|
29857
|
+
const paging = { total, next, unit: "record" };
|
|
29858
|
+
const more = pageLine({ rows, total, next }, before, t2("list.records"));
|
|
29362
29859
|
if (fields2) {
|
|
29363
29860
|
const picked = selected(rows, fields2);
|
|
29364
|
-
return { json: { records: picked.json }, text: picked.text };
|
|
29861
|
+
return { json: { records: picked.json, page: paging }, text: picked.text + aside(options.format, more) };
|
|
29365
29862
|
}
|
|
29366
29863
|
const docs = (ids) => ids.slice(0, 5).join(", ") + (ids.length > 5 ? " " + t2("records.moreDocs", { count: ids.length - 5 }) : "");
|
|
29367
29864
|
const out = rows.flatMap((r) => [
|
|
@@ -29369,13 +29866,18 @@ var runRecordsList = (options) => runCommand("records", options.format, async ()
|
|
|
29369
29866
|
` ${r.commit ? `${r.date.slice(0, 10)} ${r.commit.slice(0, 7)} ${r.author}` : t2("records.uncommitted")} \xB7 ${docs(r.docs)}`
|
|
29370
29867
|
]);
|
|
29371
29868
|
const filtered = options.since !== void 0 || options.author !== void 0 || options.q !== void 0;
|
|
29372
|
-
return { json: { records: rows }, text: text2(out.length ? out : [filtered ? t2("docs.noMatch") : t2("records.empty")]) };
|
|
29869
|
+
return { json: { records: rows, page: paging }, text: text2(out.length ? [...out, ...more] : [filtered ? t2("docs.noMatch") : t2("records.empty")]) };
|
|
29373
29870
|
});
|
|
29871
|
+
async function readThrough(project, head, filter, id) {
|
|
29872
|
+
if (!head) return 0;
|
|
29873
|
+
const all = await project.cache.history.records(head, filter, void 0, void 0);
|
|
29874
|
+
return (all?.records.findIndex((r) => r.id === id) ?? -1) + 1;
|
|
29875
|
+
}
|
|
29374
29876
|
|
|
29375
29877
|
// src/commands/feedback.ts
|
|
29376
29878
|
import { readFile as readFile18 } from "node:fs/promises";
|
|
29377
29879
|
import { release as release3 } from "node:os";
|
|
29378
|
-
import { dirname as dirname3, join as
|
|
29880
|
+
import { dirname as dirname3, join as join22 } from "node:path";
|
|
29379
29881
|
|
|
29380
29882
|
// src/adapters/github/issue.ts
|
|
29381
29883
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -29442,7 +29944,7 @@ function parseFeedback(input2) {
|
|
|
29442
29944
|
}
|
|
29443
29945
|
async function schemaVersion(from) {
|
|
29444
29946
|
for (let folder = from; ; folder = dirname3(folder)) {
|
|
29445
|
-
const config2 = await readFile18(
|
|
29947
|
+
const config2 = await readFile18(join22(folder, ".gitifact", "config.json"), "utf8").catch(() => void 0);
|
|
29446
29948
|
if (config2 !== void 0) {
|
|
29447
29949
|
try {
|
|
29448
29950
|
const value = JSON.parse(config2).schemaVersion;
|
|
@@ -29520,7 +30022,7 @@ function agentDocsGit(root, inherited = process.env) {
|
|
|
29520
30022
|
|
|
29521
30023
|
// src/commands/update.ts
|
|
29522
30024
|
var blockCommitMessage = (version2) => "chore(gitifact): refresh GITIFACT block to v" + version2;
|
|
29523
|
-
var commitState = (
|
|
30025
|
+
var commitState = (state2, extra = {}) => ({ state: state2, commit: null, paths: [], message: null, reason: null, detail: null, ...extra });
|
|
29524
30026
|
async function refreshBlocks(cwd, version2, env, controls) {
|
|
29525
30027
|
const repo = initRepository(cwd, env);
|
|
29526
30028
|
let first;
|
|
@@ -29650,10 +30152,10 @@ async function runUpdateCheck(format2, version2) {
|
|
|
29650
30152
|
process.stdout.write(JSON.stringify(dto) + "\n");
|
|
29651
30153
|
return;
|
|
29652
30154
|
}
|
|
29653
|
-
const
|
|
30155
|
+
const state2 = dto.update;
|
|
29654
30156
|
const lines = [
|
|
29655
30157
|
t2("update.text.current", { version: dto.cliVersion }),
|
|
29656
|
-
|
|
30158
|
+
state2.status === "available" ? t2("update.text.available", { version: state2.latestVersion }) : state2.status === "up-to-date" ? t2("update.text.upToDate") : state2.status === "disabled" ? t2("update.text.disabled") : t2("update.text.unavailable")
|
|
29657
30159
|
];
|
|
29658
30160
|
if (dto.command) lines.push(t2("update.text.install", { command: dto.command }));
|
|
29659
30161
|
process.stdout.write(lines.join("\n") + "\n");
|
|
@@ -29668,15 +30170,15 @@ for (let i = 0; i < languageArgs.length; i++) {
|
|
|
29668
30170
|
if (value === "ko" || value === "en") selectedLanguage = value;
|
|
29669
30171
|
}
|
|
29670
30172
|
configureLanguage(selectedLanguage ?? environmentLanguage(), selectedLanguage);
|
|
29671
|
-
var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.8.
|
|
30173
|
+
var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.8.1").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
|
|
29672
30174
|
var format = () => new Option("--format <format>", t2("help.format")).choices(formats).default("text");
|
|
29673
|
-
program2.command("browser").description(t2("help.browser")).allowExcessArguments(false).option("--port <port>", t2("help.browserPort"), parsePort, 0).option("--dev", t2("help.browserDev")).action((options) => runBrowser(options, "0.8.
|
|
29674
|
-
program2.command("init").description(t2("help.init")).allowExcessArguments(false).option("--dry-run", t2("help.initDryRun")).addOption(new Option("--agent <agent>", t2("help.initAgent")).choices([...agentPresetNames])).addOption(new Option("--remove-agents", t2("help.initRemoveAgents")).conflicts("skipAgents")).option("--skip-agents", t2("help.initSkipAgents")).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).action((options) => runInit(options, "0.8.
|
|
29675
|
-
program2.command("update").description(t2("help.update")).allowExcessArguments(false).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).option("--commit", t2("help.updateCommit")).addOption(new Option("--check", t2("help.updateCheck")).conflicts("commit")).action((options) => options.check ? runUpdateCheck(options.format, "0.8.
|
|
30175
|
+
program2.command("browser").description(t2("help.browser")).allowExcessArguments(false).option("--port <port>", t2("help.browserPort"), parsePort, 0).option("--dev", t2("help.browserDev")).action((options) => runBrowser(options, "0.8.1"));
|
|
30176
|
+
program2.command("init").description(t2("help.init")).allowExcessArguments(false).option("--dry-run", t2("help.initDryRun")).addOption(new Option("--agent <agent>", t2("help.initAgent")).choices([...agentPresetNames])).addOption(new Option("--remove-agents", t2("help.initRemoveAgents")).conflicts("skipAgents")).option("--skip-agents", t2("help.initSkipAgents")).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).action((options) => runInit(options, "0.8.1"));
|
|
30177
|
+
program2.command("update").description(t2("help.update")).allowExcessArguments(false).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).option("--commit", t2("help.updateCommit")).addOption(new Option("--check", t2("help.updateCheck")).conflicts("commit")).action((options) => options.check ? runUpdateCheck(options.format, "0.8.1") : runUpdate(options, "0.8.1"));
|
|
29676
30178
|
var listed2 = (command, sorts) => {
|
|
29677
30179
|
command.allowExcessArguments(false).option("--q <words>", t2("help.listQ")).option("--author <name>", t2("help.listAuthor"));
|
|
29678
30180
|
if (sorts) command.addOption(new Option("--sort <key>", t2("help.listSort")).choices([...sorts]).default(sorts[0]));
|
|
29679
|
-
return command.
|
|
30181
|
+
return command.addOption(new Option("--limit <n>", t2("help.listLimit")).argParser(parseLimit).conflicts("all")).option("--all", t2("help.listAll")).option("--after <cursor>", t2("help.listAfter")).option("--fields <names>", t2("help.listFields"), parseFields).addOption(format());
|
|
29680
30182
|
};
|
|
29681
30183
|
var specs = program2.command("specs").description(t2("help.specs"));
|
|
29682
30184
|
listed2(specs.command("list").description(t2("help.specsList")).addOption(new Option("--type <kind>", t2("help.specsListType")).choices([...specKinds])).option("--feature <folder>", t2("help.specsListFeature")).option("--without-design", t2("help.specsListWithoutDesign")).option("--uncovered", t2("help.specsListUncovered")).option("--draft", t2("help.specsListDraft")).option("--changed-since <date|commit>", t2("help.specsListChangedSince")), specSorts).action((o) => runSpecsList(o));
|
|
@@ -29694,7 +30196,7 @@ program2.command("check").description(t2("help.check")).allowExcessArguments(fal
|
|
|
29694
30196
|
var changes = program2.command("changes").description(t2("help.changes"));
|
|
29695
30197
|
changes.command("list").description(t2("help.changesList")).allowExcessArguments(false).addOption(format()).action((o) => runChangesList(o));
|
|
29696
30198
|
changes.command("commit").description(t2("help.changesCommit")).allowExcessArguments(false).requiredOption("--file <path>", t2("help.changesCommitFile")).option("--dry-run", t2("help.changesCommitDryRun")).addOption(format()).action((o) => runChangesCommit(o));
|
|
29697
|
-
program2.command("feedback").description(t2("help.feedback")).allowExcessArguments(false).requiredOption("--file <path>", t2("help.feedbackFile")).option("--dry-run", t2("help.feedbackDryRun")).addOption(format()).action((o) => runFeedback(o, "0.8.
|
|
30199
|
+
program2.command("feedback").description(t2("help.feedback")).allowExcessArguments(false).requiredOption("--file <path>", t2("help.feedbackFile")).option("--dry-run", t2("help.feedbackDryRun")).addOption(format()).action((o) => runFeedback(o, "0.8.1"));
|
|
29698
30200
|
var guide = program2.command("guide").description(t2("help.guide"));
|
|
29699
30201
|
guide.command("list").description(t2("help.guideList")).allowExcessArguments(false).addOption(format()).action((o) => runGuideList(o));
|
|
29700
30202
|
guide.command("show").description(t2("help.guideShow")).argument("<topic>", t2("help.guideTopic")).allowExcessArguments(false).addOption(format()).action((topic, o) => runGuideShow(topic, o));
|