gitifact 0.8.0 → 0.8.2
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-DQ1G6Jkv.js +1 -0
- package/dist/browser/assets/{BottomSheet-7C5eiOji.js → BottomSheet-BFoM6syr.js} +1 -1
- package/dist/browser/assets/Grid-DCCm_m5g.js +1 -0
- package/dist/browser/assets/HoverCard-Bpt8LVFT.js +1 -0
- package/dist/browser/assets/{Icon-EsJf4C7A.js → Icon-DPaLmsmh.js} +3 -3
- package/dist/browser/assets/{IconButton-CgOx1Jog.js → IconButton-Br-4XhQr.js} +1 -1
- package/dist/browser/assets/{Markdown-K4Mne1wf.js → Markdown-B9QJ2Ao-.js} +4 -4
- package/dist/browser/assets/{MenuBottomSheet-BOGxMa05.js → MenuBottomSheet-Dgz4h1S7.js} +1 -1
- package/dist/browser/assets/{MetadataListItem-C3SqHyk0.js → MetadataListItem-BEwPg27H.js} +1 -1
- package/dist/browser/assets/ProgressBarMarkTooltip-BxGSd9KS.js +1 -0
- package/dist/browser/assets/{Section-BEbhTpxd.js → Section-Z2PuhE6c.js} +1 -1
- package/dist/browser/assets/Selector-BlDYK5Fb.js +2 -0
- package/dist/browser/assets/Tab-BoCyIuQw.js +1 -0
- package/dist/browser/assets/{Table-8qCDrsKI.js → Table-LdzcI4YS.js} +3 -3
- package/dist/browser/assets/TimestampHoverCard-B5JBaQDZ.js +1 -0
- package/dist/browser/assets/{Token-Dmavo-NE.js → Token-CYBSUyAu.js} +1 -1
- package/dist/browser/assets/{Tooltip-QZg2pB5o.js → Tooltip-MoU3alsv.js} +1 -1
- package/dist/browser/assets/{about-B8tYN8_7.js → about-ClxCgIMf.js} +2 -2
- package/dist/browser/assets/activity-timeline-BEeyw2GA.css +1 -0
- package/dist/browser/assets/activity-timeline-i0lmNZ1o.js +2 -0
- package/dist/browser/assets/{appearance-CDQ4fLdE.js → appearance-DgDbwXAm.js} +1 -1
- package/dist/browser/assets/changelog-bGtJx397.js +2 -0
- package/dist/browser/assets/commit-5TIiQrYh.js +4 -0
- package/dist/browser/assets/{commit-45NumYDT.css → commit-DVyOH45R.css} +1 -1
- package/dist/browser/assets/{container.stylex-6OsSOpKw.js → container.stylex-rwI38dw9.js} +1 -1
- package/dist/browser/assets/contributor-2mkEa2Wo.css +1 -0
- package/dist/browser/assets/contributor-C1R6Q471.js +1 -0
- package/dist/browser/assets/contributors-DjNETDIr.js +1 -0
- package/dist/browser/assets/{contributors._email-CW4qlA9y.js → contributors._email-DXxCGNZx.js} +1 -1
- package/dist/browser/assets/contributors.index-ByVvGuYC.js +1 -0
- package/dist/browser/assets/dashboard.index-RoAwRn4H.js +1 -0
- package/dist/browser/assets/document-BwoITy_-.js +2 -0
- package/dist/browser/assets/{document-D4osdtS6.js → document-CwA2dxtP.js} +5 -5
- package/dist/browser/assets/document-DEGaf2yT.css +1 -0
- package/dist/browser/assets/features-CttwIYBf.js +2 -0
- package/dist/browser/assets/features-ZYCEDoiE.css +1 -0
- package/dist/browser/assets/features._featureId-DPctVYNz.js +1 -0
- package/dist/browser/assets/features.index-lcNtlMn8.js +1 -0
- package/dist/browser/assets/getting-started-Chwezama.js +1 -0
- package/dist/browser/assets/git-CtsTwkaq.js +1 -0
- package/dist/browser/assets/{index-BER0M7UG.css → index-CmU7dwFQ.css} +1 -1
- package/dist/browser/assets/index-iSt1IRzY.js +48 -0
- package/dist/browser/assets/instructions-2OUMP-vh.css +1 -0
- package/dist/browser/assets/instructions-DULmclHB.js +1 -0
- package/dist/browser/assets/instructions._instructionId-CBz40hpF.js +1 -0
- package/dist/browser/assets/instructions.agents-CDpXylSu.js +1 -0
- package/dist/browser/assets/instructions.index-BZ5tCMZJ.js +1 -0
- package/dist/browser/assets/{lazyRouteComponent-Dbwmw-_u.js → lazyRouteComponent-DhTs_UEV.js} +1 -1
- package/dist/browser/assets/load-more-DxscRoh6.js +1 -0
- package/dist/browser/assets/{page-header-PYFVxi9j.js → page-header-B9LQWl7J.js} +1 -1
- package/dist/browser/assets/records-Dk3shbYl.css +1 -0
- package/dist/browser/assets/records-page-B4tDip2H.css +1 -0
- package/dist/browser/assets/records-page-BjyFUmMr.js +1 -0
- package/dist/browser/assets/records._recordId-vwjWdPRX.js +1 -0
- package/dist/browser/assets/records.commits._commit-CYYgrOxv.js +1 -0
- package/dist/browser/assets/records.index-N49ep6ay.js +1 -0
- package/dist/browser/assets/records.working-CcYY8eyI.js +1 -0
- package/dist/browser/assets/request-state-BeiYxf95.js +1 -0
- package/dist/browser/assets/search-CSSuO-jR.js +1 -0
- package/dist/browser/assets/search-palette-BCEUlRVv.js +561 -0
- package/dist/browser/assets/search-palette-CDmYAo36.css +1 -0
- package/dist/browser/assets/{settings-BdZ_rM9V.js → settings-I5Mn86wq.js} +1 -1
- package/dist/browser/assets/{themeProps-CfjLB1h4.js → themeProps-CkLe5zXY.js} +1 -1
- package/dist/browser/assets/{useClipboard-CTfBnLlg.js → useClipboard-Cqx6Upy7.js} +1 -1
- package/dist/browser/assets/useDevWarning-Dudfg3qF.js +1 -0
- package/dist/browser/assets/{useInfiniteQuery-Cs8d91AB.js → useInfiniteQuery-tqfloUvn.js} +1 -1
- package/dist/browser/assets/{useIsomorphicLayoutEffect-DByj3svz.js → useIsomorphicLayoutEffect-BiEJxDg_.js} +1 -1
- package/dist/browser/assets/{useKeyboardHint-CuvkDYsZ.js → useKeyboardHint-HHpn4XUs.js} +1 -1
- package/dist/browser/assets/{useMediaQuery-CqJr5z44.js → useMediaQuery-twbHF0pO.js} +1 -1
- package/dist/browser/assets/{useScrollLock-CmZKJxOo.js → useScrollLock-D-Y-zHSV.js} +1 -1
- package/dist/browser/index.html +25 -24
- package/dist/i18n/en/block.md +23 -23
- package/dist/i18n/en/changelog.md +30 -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 +30 -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 +1660 -699
- package/package.json +1 -1
- package/dist/browser/assets/Grid-BrUBBmhu.js +0 -1
- package/dist/browser/assets/HgiRefresh-DVzwwzGM.js +0 -1
- package/dist/browser/assets/ProgressBarMarkTooltip-C5Moep_o.js +0 -1
- package/dist/browser/assets/Selector-DuT8AsMB.js +0 -2
- 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/search-BCYi0CBz.js +0 -1
- package/dist/browser/assets/search-palette-C-XxJJ6I.js +0 -561
- package/dist/browser/assets/search-palette-DpmGOAIg.css +0 -1
- package/dist/browser/assets/useCollapsible-D7UZAUy-.js +0 -1
- package/dist/browser/assets/useDevWarning-gW8_bNZ_.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,26 +10402,26 @@ 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
|
}
|
|
10398
10413
|
var handoff;
|
|
10399
10414
|
var open = [];
|
|
10400
10415
|
var memo = {
|
|
10401
|
-
alloc(_inst, payload,
|
|
10416
|
+
alloc(_inst, payload, empty2) {
|
|
10402
10417
|
const bucket = handoff;
|
|
10403
10418
|
if (!bucket)
|
|
10404
|
-
return
|
|
10419
|
+
return empty2;
|
|
10405
10420
|
handoff = void 0;
|
|
10406
|
-
const entry2 = { value:
|
|
10421
|
+
const entry2 = { value: empty2, issues: null };
|
|
10407
10422
|
bucket.set(payload.value, entry2);
|
|
10408
10423
|
open.push(entry2);
|
|
10409
|
-
return
|
|
10424
|
+
return empty2;
|
|
10410
10425
|
},
|
|
10411
10426
|
guard(inst) {
|
|
10412
10427
|
var _a3;
|
|
@@ -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
|
}
|
|
@@ -13077,8 +13092,8 @@ var error20 = () => {
|
|
|
13077
13092
|
case "invalid_union":
|
|
13078
13093
|
return "\u05E7\u05DC\u05D8 \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF";
|
|
13079
13094
|
case "invalid_element": {
|
|
13080
|
-
const
|
|
13081
|
-
return `\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF \u05D1${
|
|
13095
|
+
const place3 = withDefinite(issue2.origin ?? "array");
|
|
13096
|
+
return `\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF \u05D1${place3}`;
|
|
13082
13097
|
}
|
|
13083
13098
|
default:
|
|
13084
13099
|
return `\u05E7\u05DC\u05D8 \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF`;
|
|
@@ -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,19 +25064,73 @@ 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
|
|
25048
|
-
|
|
25049
|
-
|
|
25067
|
+
var indexFeature = external_exports.strictObject({
|
|
25068
|
+
id: external_exports.string(),
|
|
25069
|
+
path: external_exports.string(),
|
|
25070
|
+
title: external_exports.string(),
|
|
25071
|
+
state,
|
|
25072
|
+
requirements: external_exports.array(external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string(), description: external_exports.string() })),
|
|
25073
|
+
designs: external_exports.array(external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string() }))
|
|
25074
|
+
});
|
|
25075
|
+
var indexInstruction = external_exports.strictObject({ id: external_exports.string(), name: external_exports.string(), path: external_exports.string(), title: external_exports.string() });
|
|
25076
|
+
var browserCheckoutV1 = external_exports.strictObject({
|
|
25077
|
+
contract: external_exports.literal("browser-checkout"),
|
|
25078
|
+
version: external_exports.literal(1),
|
|
25050
25079
|
sessionId: external_exports.string(),
|
|
25051
25080
|
head: oid2.nullable(),
|
|
25052
25081
|
observedAt: external_exports.string(),
|
|
25053
25082
|
working: external_exports.boolean(),
|
|
25054
|
-
|
|
25055
|
-
instructions: external_exports.array(instruction),
|
|
25056
|
-
agents: agents.nullable(),
|
|
25083
|
+
stamp: external_exports.string(),
|
|
25057
25084
|
problems: external_exports.array(problem),
|
|
25085
|
+
index: external_exports.strictObject({ features: external_exports.array(indexFeature), instructions: external_exports.array(indexInstruction), people: external_exports.array(external_exports.strictObject({ name: external_exports.string(), email: external_exports.string() })) })
|
|
25086
|
+
});
|
|
25087
|
+
var featureRow = external_exports.strictObject({
|
|
25088
|
+
id: external_exports.string(),
|
|
25089
|
+
path: external_exports.string(),
|
|
25090
|
+
title: external_exports.string(),
|
|
25091
|
+
description: external_exports.string(),
|
|
25092
|
+
state,
|
|
25093
|
+
designs: external_exports.number().int().nonnegative(),
|
|
25058
25094
|
contributors: external_exports.array(contributor),
|
|
25059
|
-
|
|
25095
|
+
updatedAt: external_exports.string().nullable(),
|
|
25096
|
+
requirementCount: external_exports.number().int().nonnegative(),
|
|
25097
|
+
// `acceptance`: the numbered items under the acceptance heading, null without one. `designed`: a design names it.
|
|
25098
|
+
requirements: external_exports.array(external_exports.strictObject({ id: external_exports.string(), title: external_exports.string(), description: external_exports.string(), state, acceptance: external_exports.number().int().positive().nullable(), designed: external_exports.boolean() })),
|
|
25099
|
+
hidden: external_exports.number().int().nonnegative()
|
|
25100
|
+
});
|
|
25101
|
+
var browserFeaturesV1 = external_exports.strictObject({
|
|
25102
|
+
contract: external_exports.literal("browser-features"),
|
|
25103
|
+
version: external_exports.literal(1),
|
|
25104
|
+
sessionId: external_exports.string(),
|
|
25105
|
+
total: external_exports.number().int().nonnegative(),
|
|
25106
|
+
all: external_exports.number().int().nonnegative(),
|
|
25107
|
+
requirements: external_exports.number().int().nonnegative(),
|
|
25108
|
+
mostRequirements: external_exports.number().int().nonnegative(),
|
|
25109
|
+
next: external_exports.string().nullable(),
|
|
25110
|
+
features: external_exports.array(featureRow)
|
|
25111
|
+
});
|
|
25112
|
+
var browserFeatureV1 = external_exports.strictObject({ contract: external_exports.literal("browser-feature"), version: external_exports.literal(1), sessionId: external_exports.string(), feature });
|
|
25113
|
+
var browserInstructionsV1 = external_exports.strictObject({
|
|
25114
|
+
contract: external_exports.literal("browser-instructions"),
|
|
25115
|
+
version: external_exports.literal(1),
|
|
25116
|
+
sessionId: external_exports.string(),
|
|
25117
|
+
instructions: external_exports.array(instruction),
|
|
25118
|
+
agents: agents.nullable()
|
|
25119
|
+
});
|
|
25120
|
+
var browserContributorsV1 = external_exports.strictObject({
|
|
25121
|
+
contract: external_exports.literal("browser-contributors"),
|
|
25122
|
+
version: external_exports.literal(1),
|
|
25123
|
+
sessionId: external_exports.string(),
|
|
25124
|
+
total: external_exports.number().int().nonnegative(),
|
|
25125
|
+
next: external_exports.string().nullable(),
|
|
25126
|
+
people: external_exports.array(external_exports.strictObject({ email: external_exports.string(), name: external_exports.string(), commits: external_exports.number().int().nonnegative(), latest: external_exports.string(), features: external_exports.number().int().nonnegative() }))
|
|
25127
|
+
});
|
|
25128
|
+
var browserContributorV1 = external_exports.strictObject({
|
|
25129
|
+
contract: external_exports.literal("browser-contributor"),
|
|
25130
|
+
version: external_exports.literal(1),
|
|
25131
|
+
sessionId: external_exports.string(),
|
|
25132
|
+
person: contributor,
|
|
25133
|
+
features: external_exports.array(external_exports.strictObject({ id: external_exports.string(), title: external_exports.string(), commits: external_exports.number().int().nonnegative(), requirements: external_exports.number().int().nonnegative() }))
|
|
25060
25134
|
});
|
|
25061
25135
|
var kind = external_exports.enum(["feature", "requirement", "design", "wiki", "instruction"]);
|
|
25062
25136
|
var reference = external_exports.strictObject({ id: external_exports.string(), title: external_exports.string(), specId: external_exports.string(), path: external_exports.string() }).nullable();
|
|
@@ -25090,21 +25164,24 @@ var event = external_exports.strictObject({
|
|
|
25090
25164
|
after: reference,
|
|
25091
25165
|
records: external_exports.array(record2)
|
|
25092
25166
|
});
|
|
25093
|
-
var
|
|
25167
|
+
var browserHistoryV6 = external_exports.strictObject({
|
|
25094
25168
|
contract: external_exports.literal("browser-history"),
|
|
25095
|
-
version: external_exports.literal(
|
|
25169
|
+
version: external_exports.literal(6),
|
|
25096
25170
|
sessionId: external_exports.string(),
|
|
25097
25171
|
head: oid2,
|
|
25098
|
-
// Matching changes in all of history, not in this page
|
|
25172
|
+
// Matching changes and the commits that hold them in all of history, not in this page.
|
|
25099
25173
|
total: external_exports.number().int().nonnegative(),
|
|
25100
|
-
|
|
25174
|
+
commits: external_exports.number().int().nonnegative(),
|
|
25175
|
+
next: oid2.nullable(),
|
|
25101
25176
|
events: external_exports.array(event)
|
|
25102
25177
|
});
|
|
25103
|
-
var
|
|
25178
|
+
var browserHistorySummaryV5 = external_exports.strictObject({
|
|
25104
25179
|
contract: external_exports.literal("browser-history-summary"),
|
|
25105
|
-
version: external_exports.literal(
|
|
25180
|
+
version: external_exports.literal(5),
|
|
25106
25181
|
sessionId: external_exports.string(),
|
|
25107
25182
|
head: oid2,
|
|
25183
|
+
// The three who committed most, and how many others there are and how many commits they made between them.
|
|
25184
|
+
people: external_exports.strictObject({ total: external_exports.number().int().nonnegative(), top: external_exports.array(contributor), rest: external_exports.strictObject({ count: external_exports.number().int().nonnegative(), commits: external_exports.number().int().nonnegative() }) }),
|
|
25108
25185
|
total: external_exports.number().int().nonnegative(),
|
|
25109
25186
|
byType: external_exports.strictObject({ created: external_exports.number().int().nonnegative(), modified: external_exports.number().int().nonnegative(), moved: external_exports.number().int().nonnegative(), deleted: external_exports.number().int().nonnegative() }),
|
|
25110
25187
|
// One entry per commit within three weeks of the newest change, so the reader can count by its own calendar day.
|
|
@@ -25112,29 +25189,30 @@ var browserHistorySummaryV4 = external_exports.strictObject({
|
|
|
25112
25189
|
// The newest commits: a few of their changes each and how many there are in all.
|
|
25113
25190
|
recent: external_exports.array(external_exports.strictObject({ commit: oid2, count: external_exports.number().int().positive(), events: external_exports.array(event) }))
|
|
25114
25191
|
});
|
|
25115
|
-
var
|
|
25192
|
+
var searchKind = external_exports.enum(["feature", "requirement", "design", "instruction", "record", "commit"]);
|
|
25193
|
+
var searchHit = external_exports.strictObject({
|
|
25194
|
+
id: external_exports.string(),
|
|
25195
|
+
kind: searchKind,
|
|
25196
|
+
title: external_exports.string(),
|
|
25197
|
+
where: external_exports.string(),
|
|
25198
|
+
line: external_exports.string(),
|
|
25199
|
+
// What the hit opens: a feature (with the requirement or design tab), an instruction, a record, or a commit.
|
|
25200
|
+
featureId: external_exports.string().optional(),
|
|
25201
|
+
commit: oid2.optional()
|
|
25202
|
+
});
|
|
25203
|
+
var browserSearchV3 = external_exports.strictObject({
|
|
25116
25204
|
contract: external_exports.literal("browser-search"),
|
|
25117
|
-
version: external_exports.literal(
|
|
25205
|
+
version: external_exports.literal(3),
|
|
25118
25206
|
sessionId: external_exports.string(),
|
|
25119
25207
|
query: external_exports.string(),
|
|
25120
|
-
|
|
25121
|
-
id: external_exports.string(),
|
|
25122
|
-
kind: external_exports.enum(["feature", "requirement", "design", "instruction", "history"]),
|
|
25123
|
-
title: external_exports.string(),
|
|
25124
|
-
where: external_exports.string(),
|
|
25125
|
-
line: external_exports.string(),
|
|
25126
|
-
// What the hit opens: a feature (with the requirement or design tab), an instruction, or one change in the activity.
|
|
25127
|
-
featureId: external_exports.string().optional(),
|
|
25128
|
-
documentId: external_exports.string().optional(),
|
|
25129
|
-
key: external_exports.string().optional()
|
|
25130
|
-
}))
|
|
25208
|
+
groups: external_exports.array(external_exports.strictObject({ group: external_exports.union([searchKind, external_exports.literal("recent")]), total: external_exports.number().int().nonnegative(), next: external_exports.string().nullable(), hits: external_exports.array(searchHit) }))
|
|
25131
25209
|
});
|
|
25132
25210
|
var headQuery = oid2;
|
|
25133
25211
|
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
|
|
25212
|
+
var browserHistoryQueryV4 = external_exports.strictObject({
|
|
25135
25213
|
head: headQuery,
|
|
25136
|
-
|
|
25137
|
-
limit: count(
|
|
25214
|
+
after: oid2.optional(),
|
|
25215
|
+
limit: count(50).pipe(external_exports.number().min(1)).optional(),
|
|
25138
25216
|
kind: changeType.optional(),
|
|
25139
25217
|
document: kind.optional(),
|
|
25140
25218
|
feature: external_exports.string().regex(/^S-[a-z2-7]{10}$/).optional(),
|
|
@@ -25142,7 +25220,26 @@ var browserHistoryQueryV3 = external_exports.strictObject({
|
|
|
25142
25220
|
q: external_exports.string().max(200).optional()
|
|
25143
25221
|
});
|
|
25144
25222
|
var browserHistorySummaryQueryV1 = external_exports.strictObject({ head: headQuery });
|
|
25145
|
-
var
|
|
25223
|
+
var featureId = external_exports.string().regex(/^S-[a-z2-7]{10}$/);
|
|
25224
|
+
var browserFeaturesQueryV1 = external_exports.strictObject({
|
|
25225
|
+
q: external_exports.string().max(200).optional(),
|
|
25226
|
+
design: external_exports.enum(["yes", "no"]).optional(),
|
|
25227
|
+
author: external_exports.string().min(1).max(320).optional(),
|
|
25228
|
+
sort: external_exports.enum(["title", "requirements", "updated"]).optional(),
|
|
25229
|
+
dir: external_exports.enum(["asc", "desc"]).optional(),
|
|
25230
|
+
after: featureId.optional(),
|
|
25231
|
+
limit: count(50).pipe(external_exports.number().min(1)).optional()
|
|
25232
|
+
});
|
|
25233
|
+
var browserFeatureQueryV1 = external_exports.strictObject({ id: featureId });
|
|
25234
|
+
var browserContributorsQueryV1 = external_exports.strictObject({ q: external_exports.string().max(200).optional(), after: external_exports.string().min(1).max(320).optional(), limit: count(50).pipe(external_exports.number().min(1)).optional() });
|
|
25235
|
+
var browserContributorQueryV1 = external_exports.strictObject({ email: external_exports.string().min(1).max(320) });
|
|
25236
|
+
var browserSearchQueryV2 = external_exports.strictObject({
|
|
25237
|
+
q: external_exports.string().max(200),
|
|
25238
|
+
head: headQuery.optional(),
|
|
25239
|
+
group: searchKind.optional(),
|
|
25240
|
+
after: external_exports.string().min(1).max(200).optional(),
|
|
25241
|
+
limit: count(50).pipe(external_exports.number().min(1)).optional()
|
|
25242
|
+
});
|
|
25146
25243
|
var browserInstructionFileQueryV1 = external_exports.strictObject({ id: external_exports.string().regex(/^I-[a-z2-7]{10}$/), path: external_exports.string().min(1).max(1e3) });
|
|
25147
25244
|
var changelogQueryV1 = external_exports.strictObject({ lang: external_exports.string().regex(/^[a-z]{2}(?:-[A-Z]{2})?$/).optional() });
|
|
25148
25245
|
var fileStatus = external_exports.enum(["added", "modified", "deleted", "renamed"]);
|
|
@@ -25154,12 +25251,13 @@ var commitFile = external_exports.strictObject({
|
|
|
25154
25251
|
additions: external_exports.number().int().nonnegative().nullable(),
|
|
25155
25252
|
deletions: external_exports.number().int().nonnegative().nullable()
|
|
25156
25253
|
});
|
|
25157
|
-
var
|
|
25254
|
+
var browserCommitFilesV2 = external_exports.strictObject({
|
|
25158
25255
|
contract: external_exports.literal("browser-commit-files"),
|
|
25159
|
-
version: external_exports.literal(
|
|
25256
|
+
version: external_exports.literal(2),
|
|
25160
25257
|
sessionId: external_exports.string(),
|
|
25161
25258
|
commit: oid2,
|
|
25162
25259
|
total: external_exports.number().int().nonnegative(),
|
|
25260
|
+
next: external_exports.string().nullable(),
|
|
25163
25261
|
files: external_exports.array(commitFile)
|
|
25164
25262
|
});
|
|
25165
25263
|
var browserCommitFileV1 = external_exports.strictObject({
|
|
@@ -25174,11 +25272,11 @@ var browserCommitFileV1 = external_exports.strictObject({
|
|
|
25174
25272
|
binary: external_exports.boolean(),
|
|
25175
25273
|
tooLarge: external_exports.boolean()
|
|
25176
25274
|
});
|
|
25177
|
-
var
|
|
25275
|
+
var browserCommitFilesQueryV2 = external_exports.strictObject({ commit: oid2, after: external_exports.string().min(1).max(4096).optional(), limit: count(100).pipe(external_exports.number().min(1)).optional() });
|
|
25178
25276
|
var browserCommitFileQueryV1 = external_exports.strictObject({ commit: oid2, path: external_exports.string().min(1).max(4096) });
|
|
25179
|
-
var
|
|
25277
|
+
var browserCommitV4 = external_exports.strictObject({
|
|
25180
25278
|
contract: external_exports.literal("browser-commit"),
|
|
25181
|
-
version: external_exports.literal(
|
|
25279
|
+
version: external_exports.literal(4),
|
|
25182
25280
|
sessionId: external_exports.string(),
|
|
25183
25281
|
commit: oid2,
|
|
25184
25282
|
author: external_exports.string(),
|
|
@@ -25186,17 +25284,31 @@ var browserCommitV3 = external_exports.strictObject({
|
|
|
25186
25284
|
committer: external_exports.string(),
|
|
25187
25285
|
date: external_exports.string(),
|
|
25188
25286
|
message: external_exports.string(),
|
|
25189
|
-
|
|
25287
|
+
total: external_exports.number().int().nonnegative(),
|
|
25288
|
+
next: external_exports.string().nullable(),
|
|
25289
|
+
changes: external_exports.array(event)
|
|
25190
25290
|
});
|
|
25191
|
-
var browserCommitQueryV1 = external_exports.strictObject({ commit: oid2 });
|
|
25192
25291
|
var recordId = external_exports.string().regex(/^(?:DR|H)-[A-Za-z0-9_-]{1,64}$/);
|
|
25193
|
-
var
|
|
25194
|
-
|
|
25292
|
+
var browserCommitQueryV3 = external_exports.strictObject({ commit: oid2, after: external_exports.string().min(1).max(200).optional(), limit: count(100).pipe(external_exports.number().min(1)).optional(), record: recordId.optional() });
|
|
25293
|
+
var browserCommitChangeV1 = external_exports.strictObject({
|
|
25294
|
+
contract: external_exports.literal("browser-commit-change"),
|
|
25195
25295
|
version: external_exports.literal(1),
|
|
25196
25296
|
sessionId: external_exports.string(),
|
|
25297
|
+
commit: oid2,
|
|
25298
|
+
event,
|
|
25299
|
+
before: snapshot,
|
|
25300
|
+
after: snapshot
|
|
25301
|
+
});
|
|
25302
|
+
var browserCommitChangeQueryV1 = external_exports.strictObject({ commit: oid2, id: external_exports.string().min(1).max(100) });
|
|
25303
|
+
var browserRecordV2 = external_exports.strictObject({
|
|
25304
|
+
contract: external_exports.literal("browser-record"),
|
|
25305
|
+
version: external_exports.literal(2),
|
|
25306
|
+
sessionId: external_exports.string(),
|
|
25197
25307
|
head: oid2,
|
|
25198
25308
|
id: recordId,
|
|
25199
|
-
commit: oid2
|
|
25309
|
+
commit: oid2,
|
|
25310
|
+
record: record2,
|
|
25311
|
+
others: external_exports.number().int().nonnegative()
|
|
25200
25312
|
});
|
|
25201
25313
|
var browserRecordQueryV1 = external_exports.strictObject({ head: oid2, id: recordId });
|
|
25202
25314
|
var browserInstructionFileV1 = external_exports.strictObject({
|
|
@@ -25210,6 +25322,27 @@ var browserInstructionFileV1 = external_exports.strictObject({
|
|
|
25210
25322
|
binary: external_exports.boolean(),
|
|
25211
25323
|
tooLarge: external_exports.boolean()
|
|
25212
25324
|
});
|
|
25325
|
+
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) });
|
|
25326
|
+
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() });
|
|
25327
|
+
var browserWorkingV1 = external_exports.strictObject({
|
|
25328
|
+
contract: external_exports.literal("browser-working"),
|
|
25329
|
+
version: external_exports.literal(1),
|
|
25330
|
+
sessionId: external_exports.string(),
|
|
25331
|
+
head: oid2.nullable(),
|
|
25332
|
+
records: external_exports.array(pendingRecord),
|
|
25333
|
+
changes: external_exports.array(workingChange),
|
|
25334
|
+
withoutRecord: external_exports.array(external_exports.string())
|
|
25335
|
+
});
|
|
25336
|
+
var browserWorkingChangeV1 = external_exports.strictObject({
|
|
25337
|
+
contract: external_exports.literal("browser-working-change"),
|
|
25338
|
+
version: external_exports.literal(1),
|
|
25339
|
+
sessionId: external_exports.string(),
|
|
25340
|
+
change: workingChange,
|
|
25341
|
+
before: snapshot,
|
|
25342
|
+
after: snapshot
|
|
25343
|
+
});
|
|
25344
|
+
var browserWorkingChangeQueryV1 = external_exports.strictObject({ id: external_exports.string().regex(/^[SRDWI]-[a-z2-7]{10}$/) });
|
|
25345
|
+
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
25346
|
|
|
25214
25347
|
// ../../packages/contracts/dist/versions/changelog-v1.js
|
|
25215
25348
|
var items = external_exports.array(external_exports.string().min(1));
|
|
@@ -25243,11 +25376,11 @@ var updateStateV1 = external_exports.strictObject({
|
|
|
25243
25376
|
latestVersion: release.nullable()
|
|
25244
25377
|
}).refine((value) => (value.status === "available" || value.status === "up-to-date") === (value.latestVersion !== null), "latestVersion is set exactly when the check produced a result.");
|
|
25245
25378
|
|
|
25246
|
-
// ../../packages/contracts/dist/versions/project-init-
|
|
25247
|
-
var
|
|
25379
|
+
// ../../packages/contracts/dist/versions/project-init-v8.js
|
|
25380
|
+
var projectInitV8 = external_exports.discriminatedUnion("ok", [
|
|
25248
25381
|
external_exports.strictObject({
|
|
25249
25382
|
contract: external_exports.literal("project-init"),
|
|
25250
|
-
version: external_exports.literal(
|
|
25383
|
+
version: external_exports.literal(8),
|
|
25251
25384
|
ok: external_exports.literal(true),
|
|
25252
25385
|
outcome: external_exports.enum(["planned", "created", "already-initialized"]),
|
|
25253
25386
|
rootPath: external_exports.string(),
|
|
@@ -25258,11 +25391,13 @@ var projectInitV7 = external_exports.discriminatedUnion("ok", [
|
|
|
25258
25391
|
external_exports.strictObject({ kind: external_exports.literal("commit"), objectFormat: external_exports.enum(["sha1", "sha256"]), commit: external_exports.string() })
|
|
25259
25392
|
]),
|
|
25260
25393
|
agentDocs: external_exports.strictObject({ mode: external_exports.enum(["install", "remove", "skip"]), paths: external_exports.array(external_exports.string()) }),
|
|
25394
|
+
// The file that keeps documents in LF on every OS. `created` is false when it was already there or the run is planned.
|
|
25395
|
+
lineEndings: external_exports.strictObject({ path: external_exports.literal(".gitifact/.gitattributes"), created: external_exports.boolean() }),
|
|
25261
25396
|
update: updateStateV1,
|
|
25262
25397
|
// Present only when a newer release exists. npx refreshes the project blocks without a global install; npmGlobal remains an alternative.
|
|
25263
25398
|
install: external_exports.strictObject({ npx: external_exports.string().min(1), npmGlobal: external_exports.string().min(1) }).nullable()
|
|
25264
25399
|
}),
|
|
25265
|
-
external_exports.strictObject({ contract: external_exports.literal("project-init"), version: external_exports.literal(
|
|
25400
|
+
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
25401
|
]);
|
|
25267
25402
|
|
|
25268
25403
|
// ../../packages/contracts/dist/versions/update-v5.js
|
|
@@ -25448,6 +25583,10 @@ var messages_default3 = {
|
|
|
25448
25583
|
"server.sessionChanged": "\uC11C\uBC84 \uC138\uC158\uC774 \uBCC0\uACBD\uB410\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC5F0\uACB0\uD558\uC138\uC694.",
|
|
25449
25584
|
"server.badRange": "\uC870\uD68C \uBC94\uC704\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
25450
25585
|
"server.commitNotFound": "\uC774 \uC800\uC7A5\uC18C\uC5D0 \uADF8 \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25586
|
+
"server.featureNotFound": "\uCCB4\uD06C\uC544\uC6C3\uC5D0 \uADF8 \uAE30\uB2A5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25587
|
+
"server.contributorNotFound": "HEAD\uC5D0 \uADF8 \uC774\uBA54\uC77C\uB85C \uCEE4\uBC0B\uD55C \uC0AC\uB78C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25588
|
+
"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.",
|
|
25589
|
+
"server.workingChangeNotFound": "\uCEE4\uBC0B \uC804 \uBCC0\uACBD\uC5D0 \uADF8 \uBB38\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25451
25590
|
"server.recordNotFound": "\uC774 \uC774\uB825\uC5D0 \uADF8 \uACB0\uC815\uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25452
25591
|
"server.commitFileNotFound": "\uADF8 \uCEE4\uBC0B\uC774 \uBC14\uAFBC \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4.",
|
|
25453
25592
|
"server.instructionFileNotFound": "\uC9C0\uCE68 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
@@ -25491,6 +25630,7 @@ var messages_default3 = {
|
|
|
25491
25630
|
"init.text.blockUpdated": "\uBE14\uB85D \uAC31\uC2E0",
|
|
25492
25631
|
"init.text.storage": "\uC800\uC7A5 \uADDC\uC57D",
|
|
25493
25632
|
"init.text.agentDocs": "\uC5D0\uC774\uC804\uD2B8 \uC9C0\uCE68",
|
|
25633
|
+
"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
25634
|
"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
25635
|
"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
25636
|
"config.storeNotDirectory": ".gitifact\uB294 \uB9C1\uD06C\uAC00 \uC544\uB2CC \uC77C\uBC18 \uB514\uB809\uD130\uB9AC\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
|
|
@@ -25511,7 +25651,6 @@ var messages_default3 = {
|
|
|
25511
25651
|
"agentBlock.duplicateStart": "GITIFACT \uC2DC\uC791 \uB9C8\uCEE4\uAC00 \uC5EC\uB7EC \uAC1C\uC785\uB2C8\uB2E4. \uC911\uBCF5 \uBE14\uB85D\uC744 \uC815\uB9AC\uD55C \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
|
|
25512
25652
|
"specReader.headUnreadable": "HEAD\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
25513
25653
|
"specReader.schemaRequired": "\uC0C8 \uC2A4\uD0A4\uB9C8 \uD504\uB85C\uC81D\uD2B8\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
|
|
25514
|
-
"specReader.authorUnreadable": "Git \uC791\uC131\uC790\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
25515
25654
|
"specReader.historyUnreadable": "Git \uC774\uB825 \uD615\uC2DD\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
25516
25655
|
"specReader.projectChanged": "\uC870\uD68C \uC911 \uD504\uB85C\uC81D\uD2B8\uAC00 \uBC14\uB00C\uC5C8\uC2B5\uB2C8\uB2E4. \uC0C8\uB85C\uACE0\uCE68\uD558\uC138\uC694.",
|
|
25517
25656
|
"browser.invalidPort": "\uD3EC\uD2B8\uB294 0\uBD80\uD130 65535 \uC0AC\uC774\uC758 \uC815\uC218\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
|
|
@@ -25534,7 +25673,7 @@ var messages_default3 = {
|
|
|
25534
25673
|
"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
25674
|
"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
25675
|
"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
|
|
25676
|
+
"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
25677
|
"update.text.committed": "\uC9C0\uCE68 \uBE14\uB85D \uCEE4\uBC0B: {commit} ({paths})",
|
|
25539
25678
|
"update.text.commitNothing": "\uCEE4\uBC0B\uD560 \uC9C0\uCE68 \uBE14\uB85D \uBCC0\uACBD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25540
25679
|
"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 +25700,9 @@ var messages_default3 = {
|
|
|
25561
25700
|
"input.size": "\uC785\uB825 \uD06C\uAE30\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.",
|
|
25562
25701
|
"input.json": "\uC785\uB825 \uD30C\uC77C\uC740 UTF-8 JSON\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
|
|
25563
25702
|
"docs.draft": "\uCD08\uC548",
|
|
25703
|
+
"docs.state.added": "\uCEE4\uBC0B \uC804: \uCD94\uAC00",
|
|
25704
|
+
"docs.state.modified": "\uCEE4\uBC0B \uC804: \uBCC0\uACBD",
|
|
25705
|
+
"docs.state.deleted": "\uCEE4\uBC0B \uC804: \uC0AD\uC81C \uC608\uC815",
|
|
25564
25706
|
"docs.feature": "\uAE30\uB2A5",
|
|
25565
25707
|
"docs.requirements": "\uC694\uAD6C\uC0AC\uD56D",
|
|
25566
25708
|
"docs.designs": "\uC124\uACC4",
|
|
@@ -25644,7 +25786,7 @@ var messages_default3 = {
|
|
|
25644
25786
|
"help.newTitle": "\uBB38\uC11C \uC81C\uBAA9",
|
|
25645
25787
|
"help.newDescription": "\uD55C \uC904 \uC124\uBA85",
|
|
25646
25788
|
"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 \
|
|
25789
|
+
"help.instructionsList": "AGENTS.md\uC640 \uC9C0\uCE68 \uBAA9\uB85D (\uC9C0\uCE68\uB9C8\uB2E4 \uB538\uB9B0 \uD30C\uC77C\uC758 \uC81C\uBAA9\uACFC \uC124\uBA85)",
|
|
25648
25790
|
"help.instructionsShow": "\uC9C0\uCE68 index.md \uC6D0\uBB38, \uB530\uB974\uB294 \uC124\uACC4, \uD3F4\uB354 \uD30C\uC77C \uBAA9\uB85D",
|
|
25649
25791
|
"help.instructionsShowTargets": "\uC9C0\uCE68 \uD3F4\uB354 \uC774\uB984\uC774\uB098 I- ID (\uC5EC\uB7EC \uAC1C \uAC00\uB2A5)",
|
|
25650
25792
|
"help.instructionsShowFile": "\uC9C0\uCE68 \uD3F4\uB354 \uC548\uC758 \uD30C\uC77C \uD558\uB098\uB97C \uC77D\uC74C (\uC608: references/code-style.md)",
|
|
@@ -25657,18 +25799,26 @@ var messages_default3 = {
|
|
|
25657
25799
|
"help.listQ": "\uC81C\uBAA9\xB7\uC124\uBA85\xB7\uBCF8\uBB38\uC5D0 \uC774 \uB9D0\uC774 \uC788\uB294 \uAC83\uB9CC",
|
|
25658
25800
|
"help.listAuthor": "\uC774 Git \uC791\uC131\uC790(\uC774\uB984\uC774\uB098 \uC774\uBA54\uC77C)\uAC00 \uBC14\uAFBC \uAC83\uB9CC",
|
|
25659
25801
|
"help.listSort": "\uC815\uB82C \uAE30\uC900",
|
|
25660
|
-
"help.listLimit": "\
|
|
25802
|
+
"help.listLimit": "\uD55C \uD398\uC774\uC9C0\uC5D0 n\uAC1C (\uAE30\uBCF8 20)",
|
|
25803
|
+
"help.listAll": "\uD398\uC774\uC9C0\uB85C \uB098\uB204\uC9C0 \uC54A\uACE0 \uC804\uBD80",
|
|
25804
|
+
"help.listAfter": "\uC774 \uD56D\uBAA9 \uB2E4\uC74C\uBD80\uD130 (\uC55E \uD398\uC774\uC9C0 \uB05D\uC5D0 \uB098\uC628 \uAC12)",
|
|
25661
25805
|
"help.listFields": "\uC774 \uC5F4\uB9CC \uC27C\uD45C\uB85C (\uD14D\uC2A4\uD2B8\uB294 \uD0ED\uC73C\uB85C \uB098\uB208 \uD55C \uC904)",
|
|
25662
25806
|
"list.limit": "1 \uC774\uC0C1\uC758 \uC815\uC218\uB97C \uC801\uC73C\uC138\uC694.",
|
|
25663
25807
|
"list.unknownFields": "\uC5C6\uB294 \uC5F4\uC785\uB2C8\uB2E4: {fields}. \uC4F8 \uC218 \uC788\uB294 \uC5F4: {columns}",
|
|
25664
25808
|
"list.since": "\uB0A0\uC9DC(YYYY-MM-DD)\uB098 \uCEE4\uBC0B\uC774 \uC544\uB2D9\uB2C8\uB2E4: {value}",
|
|
25809
|
+
"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.",
|
|
25810
|
+
"list.more": "{unit} {shown}/{total}\uAC1C. \uB2E4\uC74C \uD398\uC774\uC9C0: \uAC19\uC740 \uBA85\uB839\uC5D0 --after {cursor}",
|
|
25811
|
+
"list.items": "\uD56D\uBAA9",
|
|
25812
|
+
"list.features": "\uAE30\uB2A5",
|
|
25813
|
+
"list.commits": "\uCEE4\uBC0B",
|
|
25814
|
+
"list.records": "\uACB0\uC815\uAE30\uB85D",
|
|
25665
25815
|
"docs.otherResource": "\uC774 \uBA85\uB839\uC774 \uB2E4\uB8E8\uB294 \uBB38\uC11C\uAC00 \uC544\uB2D9\uB2C8\uB2E4: {ids}. {command}\uB85C \uC77D\uC73C\uC138\uC694.",
|
|
25666
25816
|
"docs.noMatch": "\uC870\uAC74\uC5D0 \uB9DE\uB294 \uAC83\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25667
25817
|
"docs.requirement": "\uC694\uAD6C\uC0AC\uD56D",
|
|
25668
25818
|
"docs.design": "\uC124\uACC4",
|
|
25669
25819
|
"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
25820
|
"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.
|
|
25821
|
+
"instructions.filesLimited": "(\uD30C\uC77C\uC774 \uB354 \uC788\uC5B4 \uC55E {count}\uAC1C\uB9CC \uBCF4\uC785\uB2C8\uB2E4)",
|
|
25672
25822
|
"instructions.empty": "\uC9C0\uCE68\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. instructions new\uB85C \uB9CC\uB4DC\uC138\uC694.",
|
|
25673
25823
|
"instructions.fileWithOne": "--file\uC740 \uC9C0\uCE68 \uD558\uB098\uC640 \uD568\uAED8 \uC4F0\uBA70 --ref\uC640 \uD568\uAED8 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25674
25824
|
"instructions.unknownFile": "\uC9C0\uCE68 {name} \uD3F4\uB354\uC5D0 \uADF8 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: {path}",
|
|
@@ -25738,6 +25888,10 @@ var messages_default4 = {
|
|
|
25738
25888
|
"server.sessionChanged": "The server session changed. Reconnect to continue.",
|
|
25739
25889
|
"server.badRange": "Check the requested range.",
|
|
25740
25890
|
"server.commitNotFound": "This repository has no such commit.",
|
|
25891
|
+
"server.featureNotFound": "The checkout has no such feature.",
|
|
25892
|
+
"server.contributorNotFound": "Nobody with this email committed to HEAD.",
|
|
25893
|
+
"server.cursorNotFound": "The place to read on from is no longer in the list. Load it again from the start.",
|
|
25894
|
+
"server.workingChangeNotFound": "No uncommitted change of that document.",
|
|
25741
25895
|
"server.recordNotFound": "This history has no such decision record.",
|
|
25742
25896
|
"server.commitFileNotFound": "That commit did not change this file.",
|
|
25743
25897
|
"server.instructionFileNotFound": "The instruction file was not found.",
|
|
@@ -25781,6 +25935,7 @@ var messages_default4 = {
|
|
|
25781
25935
|
"init.text.blockUpdated": "Block refreshed",
|
|
25782
25936
|
"init.text.storage": "Storage convention",
|
|
25783
25937
|
"init.text.agentDocs": "Agent instructions",
|
|
25938
|
+
"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
25939
|
"init.text.updateAvailable": "Version {version} is available. Run {command} to use it and refresh the project instructions.",
|
|
25785
25940
|
"init.failed": "Initialization failed. Check file permissions and the generated configuration and temporary files.",
|
|
25786
25941
|
"config.storeNotDirectory": ".gitifact must be a regular directory, not a link.",
|
|
@@ -25801,7 +25956,6 @@ var messages_default4 = {
|
|
|
25801
25956
|
"agentBlock.duplicateStart": "Multiple GITIFACT start markers found. Remove duplicate blocks before retrying.",
|
|
25802
25957
|
"specReader.headUnreadable": "Could not read HEAD.",
|
|
25803
25958
|
"specReader.schemaRequired": "A project using the supported schema is required.",
|
|
25804
|
-
"specReader.authorUnreadable": "Could not read Git authors.",
|
|
25805
25959
|
"specReader.historyUnreadable": "Could not read Git history.",
|
|
25806
25960
|
"specReader.projectChanged": "The project changed while reading. Refresh to continue.",
|
|
25807
25961
|
"browser.invalidPort": "Port must be an integer between 0 and 65535.",
|
|
@@ -25824,7 +25978,7 @@ var messages_default4 = {
|
|
|
25824
25978
|
"update.text.noBlock": "No instruction files contain a GITIFACT block. Run npx --yes gitifact@{version} init to install one.",
|
|
25825
25979
|
"update.text.notInitialized": "Only the version was checked because this project is not initialized.",
|
|
25826
25980
|
"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
|
|
25981
|
+
"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
25982
|
"update.text.committed": "Instruction blocks committed: {commit} ({paths})",
|
|
25829
25983
|
"update.text.commitNothing": "No instruction block changes to commit.",
|
|
25830
25984
|
"update.text.commitSkipped.untracked": "Not committed. Instruction files are untracked: {paths}",
|
|
@@ -25851,6 +26005,9 @@ var messages_default4 = {
|
|
|
25851
26005
|
"input.size": "The input is too large.",
|
|
25852
26006
|
"input.json": "The input file must be UTF-8 JSON.",
|
|
25853
26007
|
"docs.draft": "draft",
|
|
26008
|
+
"docs.state.added": "uncommitted: added",
|
|
26009
|
+
"docs.state.modified": "uncommitted: modified",
|
|
26010
|
+
"docs.state.deleted": "uncommitted: to be deleted",
|
|
25854
26011
|
"docs.feature": "Feature",
|
|
25855
26012
|
"docs.requirements": "Requirements",
|
|
25856
26013
|
"docs.designs": "Design",
|
|
@@ -25934,7 +26091,7 @@ var messages_default4 = {
|
|
|
25934
26091
|
"help.newTitle": "Document title",
|
|
25935
26092
|
"help.newDescription": "One-line description",
|
|
25936
26093
|
"help.instructions": "List, read and create project instructions",
|
|
25937
|
-
"help.instructionsList": "AGENTS.md and the instructions (with
|
|
26094
|
+
"help.instructionsList": "AGENTS.md and the instructions (with the title and description of each file in their folders)",
|
|
25938
26095
|
"help.instructionsShow": "An instruction's index.md as written, the designs that follow it and its folder's files",
|
|
25939
26096
|
"help.instructionsShowTargets": "Instruction folder names or I- IDs (one or more)",
|
|
25940
26097
|
"help.instructionsShowFile": "Read one file of the instruction folder (e.g. references/code-style.md)",
|
|
@@ -25947,18 +26104,26 @@ var messages_default4 = {
|
|
|
25947
26104
|
"help.listQ": "Only those whose title, description or text holds these words",
|
|
25948
26105
|
"help.listAuthor": "Only those this Git author (name or email) changed",
|
|
25949
26106
|
"help.listSort": "Sort key",
|
|
25950
|
-
"help.listLimit": "
|
|
26107
|
+
"help.listLimit": "n per page (20 by default)",
|
|
26108
|
+
"help.listAll": "Everything, not split into pages",
|
|
26109
|
+
"help.listAfter": "Start after this item (the value at the end of the previous page)",
|
|
25951
26110
|
"help.listFields": "Only these columns, comma-separated (text prints one tab-separated line each)",
|
|
25952
26111
|
"list.limit": "Give a whole number of 1 or more.",
|
|
25953
26112
|
"list.unknownFields": "Unknown columns: {fields}. Available columns: {columns}",
|
|
25954
26113
|
"list.since": "Not a date (YYYY-MM-DD) or a commit: {value}",
|
|
26114
|
+
"list.after": "No such cursor in the list: {cursor}. Read again from the start without --after.",
|
|
26115
|
+
"list.more": "{unit}: {shown} of {total}. Next page: the same command with --after {cursor}",
|
|
26116
|
+
"list.items": "Items",
|
|
26117
|
+
"list.features": "Features",
|
|
26118
|
+
"list.commits": "Commits",
|
|
26119
|
+
"list.records": "Records",
|
|
25955
26120
|
"docs.otherResource": "Not a document this command reads: {ids}. Use {command}.",
|
|
25956
26121
|
"docs.noMatch": "Nothing matches.",
|
|
25957
26122
|
"docs.requirement": "requirement",
|
|
25958
26123
|
"docs.design": "design",
|
|
25959
26124
|
"instructions.agents": "AGENTS.md \u2014 the index outside the block says which instruction to read for which work",
|
|
25960
26125
|
"instructions.noAgents": "No AGENTS.md \u2014 nothing tells when to read which instruction",
|
|
25961
|
-
"instructions.
|
|
26126
|
+
"instructions.filesLimited": "(the folder holds more; only the first {count} are shown)",
|
|
25962
26127
|
"instructions.empty": "No instructions yet. Create one with instructions new.",
|
|
25963
26128
|
"instructions.fileWithOne": "--file takes one instruction and does not go with --ref.",
|
|
25964
26129
|
"instructions.unknownFile": "The folder of instruction {name} has no such file: {path}",
|
|
@@ -26069,9 +26234,9 @@ async function dispatch(routes, sessionId, url2, path3, request, response, allow
|
|
|
26069
26234
|
if (found.query) {
|
|
26070
26235
|
const keys2 = [...url2.searchParams.keys()];
|
|
26071
26236
|
if (new Set(keys2).size !== keys2.length) return fail2(response, 400, "BAD_REQUEST", t2("server.badRange"));
|
|
26072
|
-
const
|
|
26073
|
-
if (!
|
|
26074
|
-
query =
|
|
26237
|
+
const parsed2 = found.query.safeParse(Object.fromEntries(url2.searchParams));
|
|
26238
|
+
if (!parsed2.success) return fail2(response, 400, "BAD_REQUEST", t2("server.badRange"));
|
|
26239
|
+
query = parsed2.data;
|
|
26075
26240
|
} else if (url2.search) return fail2(response, 400, "BAD_REQUEST", t2("server.noQuery"));
|
|
26076
26241
|
if (found.session && request.headers["x-gitifact-session"] !== sessionId) return fail2(response, 409, "SESSION_CHANGED", t2("server.sessionChanged"));
|
|
26077
26242
|
try {
|
|
@@ -26329,7 +26494,7 @@ function storeReader(cwd) {
|
|
|
26329
26494
|
import { DatabaseSync } from "node:sqlite";
|
|
26330
26495
|
import { lstat, mkdir, rm, writeFile } from "node:fs/promises";
|
|
26331
26496
|
import { join as join2 } from "node:path";
|
|
26332
|
-
var CACHE_FORMAT =
|
|
26497
|
+
var CACHE_FORMAT = 11;
|
|
26333
26498
|
var CACHE_DIR = ".gitifact/cache";
|
|
26334
26499
|
var SCHEMA = `
|
|
26335
26500
|
-- Working-tree files already read: the modification time and size decide whether a file is read again.
|
|
@@ -26344,18 +26509,32 @@ var SCHEMA = `
|
|
|
26344
26509
|
CREATE INDEX doc_references_to ON doc_references (to_id);
|
|
26345
26510
|
-- Commits already read, and with which parser: 'current', 'legacy' (0.7, before a migration) or 'migration' (hidden).
|
|
26346
26511
|
CREATE TABLE commits (oid TEXT PRIMARY KEY, reader TEXT NOT NULL);
|
|
26347
|
-
-- One row per change. 'row' is the list shape as JSON; 'detail'
|
|
26512
|
+
-- One row per change. 'row' is the list shape as JSON with its records by ID; 'detail' where each side's text is read
|
|
26513
|
+
-- again from Git (commit and path), or the text itself for 0.7 history, which has no files to read again.
|
|
26348
26514
|
CREATE TABLE changes (
|
|
26349
26515
|
key TEXT PRIMARY KEY, oid TEXT NOT NULL, ord INTEGER NOT NULL,
|
|
26350
26516
|
id TEXT NOT NULL, kind TEXT NOT NULL, types TEXT NOT NULL, email TEXT NOT NULL, date TEXT NOT NULL,
|
|
26351
26517
|
before_spec TEXT, after_spec TEXT, needle TEXT NOT NULL, row TEXT NOT NULL, detail TEXT NOT NULL);
|
|
26352
26518
|
CREATE INDEX changes_by_commit ON changes (oid, ord);
|
|
26353
26519
|
CREATE INDEX changes_by_id ON changes (id);
|
|
26520
|
+
-- The records each commit added, once each; change rows name them by ID.
|
|
26521
|
+
CREATE TABLE records (oid TEXT NOT NULL, id TEXT NOT NULL, title TEXT NOT NULL, sections TEXT NOT NULL, PRIMARY KEY (oid, id));
|
|
26522
|
+
CREATE INDEX records_by_id ON records (id);
|
|
26354
26523
|
-- All reachable record commits of one HEAD, children before parents (pos 0 is newest).
|
|
26355
26524
|
CREATE TABLE lineage (head TEXT NOT NULL, pos INTEGER NOT NULL, oid TEXT NOT NULL, PRIMARY KEY (head, pos));
|
|
26356
26525
|
CREATE INDEX lineage_by_commit ON lineage (head, oid);
|
|
26357
26526
|
CREATE TABLE heads (head TEXT PRIMARY KEY, seen INTEGER NOT NULL);
|
|
26358
|
-
--
|
|
26527
|
+
-- Every commit the log has met, code commits too: who made it and when, and whether it is a format migration.
|
|
26528
|
+
-- 'reach' is 1 for the commits of the HEAD the log follows now; contributors are counted over those.
|
|
26529
|
+
CREATE TABLE log (oid TEXT PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL, time INTEGER NOT NULL, date TEXT NOT NULL,
|
|
26530
|
+
subject TEXT NOT NULL, migration INTEGER NOT NULL, reach INTEGER NOT NULL);
|
|
26531
|
+
CREATE INDEX log_by_reach ON log (reach, email);
|
|
26532
|
+
-- The store folders a commit touched: a feature folder, an instruction folder, or AGENTS.md.
|
|
26533
|
+
CREATE TABLE touches (oid TEXT NOT NULL, folder TEXT NOT NULL, PRIMARY KEY (oid, folder));
|
|
26534
|
+
CREATE INDEX touches_by_folder ON touches (folder);
|
|
26535
|
+
-- What the log follows: the HEAD ('head') and the fingerprint of the .mailmap its names were read with ('mailmap').
|
|
26536
|
+
CREATE TABLE state (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
26537
|
+
-- What the search box finds among the working tree's documents (scope 'checkout'); records are searched in their table.
|
|
26359
26538
|
-- The searched columns hold lower-cased text; trigram lets LIKE '%...%' use the index from three characters on.
|
|
26360
26539
|
CREATE VIRTUAL TABLE search USING fts5(
|
|
26361
26540
|
scope UNINDEXED, kind UNINDEXED, ref UNINDEXED, oid UNINDEXED, payload UNINDEXED,
|
|
@@ -26377,6 +26556,10 @@ function prepare(db) {
|
|
|
26377
26556
|
db.exec("ROLLBACK");
|
|
26378
26557
|
throw error62;
|
|
26379
26558
|
}
|
|
26559
|
+
try {
|
|
26560
|
+
db.exec("VACUUM");
|
|
26561
|
+
} catch {
|
|
26562
|
+
}
|
|
26380
26563
|
}
|
|
26381
26564
|
function createCacheDatabase(root) {
|
|
26382
26565
|
let path3;
|
|
@@ -26472,24 +26655,23 @@ var place = (doc) => doc.path.replace(/^\.gitifact\//, "");
|
|
|
26472
26655
|
function createDocumentCache(root, database) {
|
|
26473
26656
|
let syncing;
|
|
26474
26657
|
async function walk() {
|
|
26475
|
-
const found =
|
|
26658
|
+
const found = [];
|
|
26476
26659
|
async function visit2(relative, depth) {
|
|
26477
26660
|
const info5 = await lstat2(join3(root, ...relative.split("/"))).catch((error62) => {
|
|
26478
26661
|
if (error62.code === "ENOENT") return void 0;
|
|
26479
26662
|
throw error62;
|
|
26480
26663
|
});
|
|
26481
|
-
if (!info5 || found.
|
|
26664
|
+
if (!info5 || found.length >= 2 * COUNT_LIMIT) return;
|
|
26482
26665
|
if (info5.isDirectory() && !info5.isSymbolicLink()) {
|
|
26483
26666
|
if (depth > 10) return;
|
|
26484
|
-
|
|
26667
|
+
await Promise.all((await readdir2(join3(root, ...relative.split("/")))).map((name2) => visit2(relative + "/" + name2, depth + 1)));
|
|
26485
26668
|
return;
|
|
26486
26669
|
}
|
|
26487
|
-
found.
|
|
26670
|
+
found.push([relative, { mtime: info5.mtimeMs, size: info5.size, link: info5.isSymbolicLink() || !info5.isFile() }]);
|
|
26488
26671
|
}
|
|
26489
|
-
await visit2(SPEC_ROOT, 0);
|
|
26490
|
-
|
|
26491
|
-
|
|
26492
|
-
return found;
|
|
26672
|
+
await Promise.all([visit2(SPEC_ROOT, 0), visit2(WIKI_ROOT, 0), visit2(INSTRUCTIONS_ROOT, 0)]);
|
|
26673
|
+
found.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
26674
|
+
return new Map(found.slice(0, COUNT_LIMIT));
|
|
26493
26675
|
}
|
|
26494
26676
|
async function read(path3, seen) {
|
|
26495
26677
|
try {
|
|
@@ -26513,7 +26695,7 @@ function createDocumentCache(root, database) {
|
|
|
26513
26695
|
});
|
|
26514
26696
|
const gone = [...known.keys()].filter((path3) => !seen.has(path3));
|
|
26515
26697
|
if (!changed.length && !gone.length) return;
|
|
26516
|
-
const
|
|
26698
|
+
const parsed2 = await Promise.all(changed.map(async ([path3, s]) => ({ path: path3, seen: s, ...await read(path3, s) })));
|
|
26517
26699
|
await database.with((db) => transaction(db, () => {
|
|
26518
26700
|
const forget = (path3) => {
|
|
26519
26701
|
const row = db.prepare("SELECT id FROM documents WHERE path = ?").get(path3);
|
|
@@ -26530,7 +26712,7 @@ function createDocumentCache(root, database) {
|
|
|
26530
26712
|
const document = db.prepare("INSERT INTO documents (path, id, kind, feature, title, description, ord, doc) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
|
26531
26713
|
const reference2 = db.prepare("INSERT INTO doc_references (from_id, to_id, type) VALUES (?, ?, ?)");
|
|
26532
26714
|
const search = db.prepare("INSERT INTO search (scope, kind, ref, oid, payload, title, place, body) VALUES (?,?,?,?,?,?,?,?)");
|
|
26533
|
-
for (const p of
|
|
26715
|
+
for (const p of parsed2) {
|
|
26534
26716
|
forget(p.path);
|
|
26535
26717
|
file2.run(p.path, p.seen.mtime, p.seen.size, p.problem ? JSON.stringify(p.problem) : null);
|
|
26536
26718
|
const doc = p.doc;
|
|
@@ -26589,28 +26771,23 @@ function createDocumentCache(root, database) {
|
|
|
26589
26771
|
throw error62;
|
|
26590
26772
|
});
|
|
26591
26773
|
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
|
-
}
|
|
26774
|
+
const read2 = await Promise.all([...seen].map(async ([path3, s]) => {
|
|
26775
|
+
if (!readable(path3)) return { path: path3 };
|
|
26776
|
+
if (instructionFile3(path3) && !path3.endsWith(".md")) return { path: path3, text: "" };
|
|
26777
|
+
if (s.link) return { path: path3, problem: docProblem("PATH_UNSUPPORTED", path3) };
|
|
26778
|
+
if (s.size > (path3 === HISTORY_PATH ? 64 * FILE_LIMIT : FILE_LIMIT)) return { path: path3, problem: docProblem("FILE_TOO_LARGE", path3) };
|
|
26608
26779
|
try {
|
|
26609
|
-
|
|
26780
|
+
return { path: path3, text: decode3(await readFile2(join3(root, ...path3.split("/")))) };
|
|
26610
26781
|
} catch (error62) {
|
|
26611
|
-
if (error62 instanceof TypeError)
|
|
26612
|
-
|
|
26782
|
+
if (error62 instanceof TypeError) return { path: path3, problem: docProblem("INVALID_CHARACTERS", path3) };
|
|
26783
|
+
throw error62;
|
|
26613
26784
|
}
|
|
26785
|
+
}));
|
|
26786
|
+
const files = /* @__PURE__ */ new Map();
|
|
26787
|
+
const problems = [];
|
|
26788
|
+
for (const r of read2) {
|
|
26789
|
+
if (r.text !== void 0) files.set(r.path, r.text);
|
|
26790
|
+
if (r.problem) problems.push(r.problem);
|
|
26614
26791
|
}
|
|
26615
26792
|
return { files, problems };
|
|
26616
26793
|
},
|
|
@@ -26641,6 +26818,40 @@ function createDocumentCache(root, database) {
|
|
|
26641
26818
|
}));
|
|
26642
26819
|
});
|
|
26643
26820
|
},
|
|
26821
|
+
/**
|
|
26822
|
+
* The working documents of one kind whose title, place or text holds the query, a page at a time: a title that
|
|
26823
|
+
* starts with it first, then a title, a place, and the text; by title within each. `after` is the ID of the last
|
|
26824
|
+
* hit of the page before; one that no longer matches answers undefined. Each hit names its feature's ID.
|
|
26825
|
+
*/
|
|
26826
|
+
async searchKind(kind2, raw, after, limit) {
|
|
26827
|
+
const query = raw.trim().toLowerCase();
|
|
26828
|
+
await sync();
|
|
26829
|
+
return database.with((db) => {
|
|
26830
|
+
const literal2 = /[\\%_]/.test(query);
|
|
26831
|
+
const match = (column) => literal2 ? `instr(${column}, :q) > 0` : `${column} LIKE :like`;
|
|
26832
|
+
const ordered = `WITH hits AS (
|
|
26833
|
+
SELECT json_extract(payload, '$.id') AS id, payload, title, ref,
|
|
26834
|
+
CASE WHEN substr(title, 1, length(:q)) = :q THEN 0 WHEN instr(title, :q) > 0 THEN 1 WHEN instr(place, :q) > 0 THEN 2 ELSE 3 END AS rank
|
|
26835
|
+
FROM search WHERE scope = 'checkout' AND kind = :kind AND (${match("title")} OR ${match("place")} OR ${match("body")})),
|
|
26836
|
+
ordered AS (SELECT *, row_number() OVER (ORDER BY rank, title, ref) AS n FROM hits)`;
|
|
26837
|
+
const params = { q: query, kind: kind2, ...literal2 ? {} : { like: containing(query) } };
|
|
26838
|
+
const total = Number(db.prepare(`${ordered} SELECT count(*) AS n FROM ordered`).get(params).n);
|
|
26839
|
+
let start = 0;
|
|
26840
|
+
if (after !== void 0) {
|
|
26841
|
+
const at = db.prepare(`${ordered} SELECT n FROM ordered WHERE id = :after`).get({ ...params, after });
|
|
26842
|
+
if (!at) return void 0;
|
|
26843
|
+
start = Number(at.n);
|
|
26844
|
+
}
|
|
26845
|
+
const rows = db.prepare(`${ordered} SELECT payload FROM ordered WHERE n > :start ORDER BY n LIMIT :limit`).all({ ...params, start, limit: limit + 1 });
|
|
26846
|
+
const features = new Map(db.prepare("SELECT feature, id FROM documents WHERE kind = 'feature'").all().map((r) => [r.feature, r.id]));
|
|
26847
|
+
const hits = rows.slice(0, limit).map((r) => {
|
|
26848
|
+
const p = JSON.parse(r.payload);
|
|
26849
|
+
const featureId2 = p.feature ? features.get(p.feature) : void 0;
|
|
26850
|
+
return { id: p.id, kind: kind2, title: p.title, where: p.where, line: snippet(p.body, query), ...featureId2 ? { featureId: featureId2 } : {} };
|
|
26851
|
+
});
|
|
26852
|
+
return { total, next: rows.length > limit ? hits[hits.length - 1].id : null, hits };
|
|
26853
|
+
});
|
|
26854
|
+
},
|
|
26644
26855
|
/** Documents that refer to `id` from their frontmatter: designs naming a requirement or a source. */
|
|
26645
26856
|
async referencing(id) {
|
|
26646
26857
|
await sync();
|
|
@@ -26866,7 +27077,8 @@ function createCommitChanges(git) {
|
|
|
26866
27077
|
types: change2.types,
|
|
26867
27078
|
before: change2.before,
|
|
26868
27079
|
after: change2.after,
|
|
26869
|
-
records: recordsFor(records2, change2.id)
|
|
27080
|
+
records: recordsFor(records2, change2.id),
|
|
27081
|
+
source: { before: c.parent ?? null, after: c.commit }
|
|
26870
27082
|
}));
|
|
26871
27083
|
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
27084
|
const recordsAt = (blobs, rev, paths2) => paths2.flatMap((path3) => {
|
|
@@ -26951,6 +27163,21 @@ function createCommitChanges(git) {
|
|
|
26951
27163
|
return {
|
|
26952
27164
|
/** Every document and reason file of a commit (path → text): what `specs show --ref` and `changes` compare against. */
|
|
26953
27165
|
tree,
|
|
27166
|
+
/**
|
|
27167
|
+
* Documents read again at the commits and paths a change was read at, by one `cat-file --batch`: the text a
|
|
27168
|
+
* change's detail shows, which the cache does not keep. A place that no longer reads is null.
|
|
27169
|
+
*/
|
|
27170
|
+
async sides(places) {
|
|
27171
|
+
const blobs = await readBlobs([...new Set(places.map((p) => p.rev + ":" + p.path))]);
|
|
27172
|
+
return places.map((p) => {
|
|
27173
|
+
const text4 = blobs.get(p.rev + ":" + p.path);
|
|
27174
|
+
try {
|
|
27175
|
+
return text4 === void 0 ? null : snapshot3(parseDocumentFile(p.path, text4), p.specId);
|
|
27176
|
+
} catch {
|
|
27177
|
+
return null;
|
|
27178
|
+
}
|
|
27179
|
+
});
|
|
27180
|
+
},
|
|
26954
27181
|
/** All reachable record commits, children before parents even when author clocks differ. */
|
|
26955
27182
|
async lineage(head) {
|
|
26956
27183
|
return git.decode(await git.run(["rev-list", "--full-history", "--date-order", head, "--", ...HISTORY_PATHSPECS])).split("\n").filter(Boolean);
|
|
@@ -26989,25 +27216,30 @@ function createCommitChanges(git) {
|
|
|
26989
27216
|
// src/adapters/cache/history.ts
|
|
26990
27217
|
var listed = (e) => {
|
|
26991
27218
|
const reference2 = (s) => s ? { id: s.id, title: s.title, specId: s.specId, path: s.path } : null;
|
|
26992
|
-
|
|
27219
|
+
const { source: _source, ...event2 } = e;
|
|
27220
|
+
return { ...event2, before: reference2(e.before), after: reference2(e.after) };
|
|
26993
27221
|
};
|
|
26994
|
-
var
|
|
27222
|
+
var sideOf3 = (snapshot5, rev) => !snapshot5 ? null : rev ? { rev, path: snapshot5.path } : snapshot5;
|
|
26995
27223
|
var DAY = 864e5;
|
|
26996
27224
|
var KEPT_HEADS = 4;
|
|
26997
27225
|
var RECENT_COMMITS = 3;
|
|
26998
27226
|
var RECENT_CHANGES = 12;
|
|
26999
|
-
function createHistory(database, git) {
|
|
27227
|
+
function createHistory(database, git, originals) {
|
|
27000
27228
|
const changes2 = createCommitChanges(git);
|
|
27229
|
+
const texts = originals ?? changes2;
|
|
27001
27230
|
const building = /* @__PURE__ */ new Map();
|
|
27002
27231
|
const built = /* @__PURE__ */ new Set();
|
|
27003
27232
|
function insert(db, commits, readers) {
|
|
27004
27233
|
const commit = db.prepare("INSERT OR IGNORE INTO commits (oid, reader) VALUES (?, ?)");
|
|
27005
27234
|
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 (?,?,?,?,?,?,?,?,?,?,?,?,?)");
|
|
27006
|
-
const
|
|
27235
|
+
const record4 = db.prepare("INSERT OR IGNORE INTO records (oid, id, title, sections) VALUES (?, ?, ?, ?)");
|
|
27007
27236
|
transaction(db, () => {
|
|
27008
27237
|
for (const c of commits) {
|
|
27009
27238
|
if (!Number(commit.run(c.commit, readers.get(c.commit) ?? "current").changes)) continue;
|
|
27010
27239
|
c.events.forEach((e, ord) => {
|
|
27240
|
+
for (const r of e.records) record4.run(e.commit, r.id, r.title, JSON.stringify(r.sections));
|
|
27241
|
+
const row = { ...listed(e), records: e.records.map((r) => r.id) };
|
|
27242
|
+
const detail = { before: sideOf3(e.before, e.source?.before), after: sideOf3(e.after, e.source?.after) };
|
|
27011
27243
|
change2.run(
|
|
27012
27244
|
e.key,
|
|
27013
27245
|
e.commit,
|
|
@@ -27020,26 +27252,22 @@ function createHistory(database, git) {
|
|
|
27020
27252
|
e.before?.specId ?? null,
|
|
27021
27253
|
e.after?.specId ?? null,
|
|
27022
27254
|
[e.id, e.before?.title ?? "", e.after?.title ?? ""].join(" ").toLowerCase(),
|
|
27023
|
-
JSON.stringify(
|
|
27024
|
-
JSON.stringify(
|
|
27025
|
-
);
|
|
27026
|
-
const line2 = e.records.length ? e.records.map((r) => [r.title, ...r.sections.map((s) => s.body)].join(" ")).join(" \xB7 ") : e.message;
|
|
27027
|
-
const where2 = e.commit.slice(0, 7) + " \xB7 " + e.author;
|
|
27028
|
-
search.run(
|
|
27029
|
-
"history",
|
|
27030
|
-
"history",
|
|
27031
|
-
e.key,
|
|
27032
|
-
e.commit,
|
|
27033
|
-
JSON.stringify({ title: titleOf(e), where: where2, line: line2 }),
|
|
27034
|
-
titleOf(e).toLowerCase(),
|
|
27035
|
-
where2.toLowerCase(),
|
|
27036
|
-
[line2, e.message, e.id].join(" ").toLowerCase()
|
|
27255
|
+
JSON.stringify(row),
|
|
27256
|
+
JSON.stringify(detail)
|
|
27037
27257
|
);
|
|
27038
27258
|
});
|
|
27039
27259
|
}
|
|
27040
27260
|
});
|
|
27041
27261
|
}
|
|
27042
27262
|
async function build(head) {
|
|
27263
|
+
const known = await database.with((db) => {
|
|
27264
|
+
const seen = !!db.prepare("SELECT 1 FROM heads WHERE head = ?").get(head);
|
|
27265
|
+
if (seen) db.prepare("UPDATE heads SET seen = ? WHERE head = ?").run(Date.now(), head);
|
|
27266
|
+
return seen;
|
|
27267
|
+
});
|
|
27268
|
+
if (!known) await read(head);
|
|
27269
|
+
}
|
|
27270
|
+
async function read(head) {
|
|
27043
27271
|
const lineage = await changes2.lineage(head);
|
|
27044
27272
|
const readers = await changes2.readers(head, lineage);
|
|
27045
27273
|
const known = await database.with((db) => new Map(db.prepare("SELECT oid, reader FROM commits").all().map((r) => [r.oid, r.reader])));
|
|
@@ -27048,13 +27276,15 @@ function createHistory(database, git) {
|
|
|
27048
27276
|
for (const oid3 of stale) {
|
|
27049
27277
|
db.prepare("DELETE FROM commits WHERE oid = ?").run(oid3);
|
|
27050
27278
|
db.prepare("DELETE FROM changes WHERE oid = ?").run(oid3);
|
|
27051
|
-
db.prepare("DELETE FROM
|
|
27279
|
+
db.prepare("DELETE FROM records WHERE oid = ?").run(oid3);
|
|
27052
27280
|
}
|
|
27281
|
+
db.prepare("DELETE FROM lineage WHERE head != ?").run(head);
|
|
27282
|
+
db.prepare("DELETE FROM heads WHERE head != ?").run(head);
|
|
27053
27283
|
}));
|
|
27054
27284
|
const missing2 = lineage.filter((oid3) => !known.has(oid3) || stale.includes(oid3));
|
|
27055
27285
|
for (let i = 0; i < missing2.length; i += 100) {
|
|
27056
|
-
const
|
|
27057
|
-
await database.with((db) => insert(db,
|
|
27286
|
+
const read2 = await changes2.of(missing2.slice(i, i + 100), readers);
|
|
27287
|
+
await database.with((db) => insert(db, read2, readers));
|
|
27058
27288
|
}
|
|
27059
27289
|
await database.with((db) => transaction(db, () => {
|
|
27060
27290
|
db.prepare("DELETE FROM lineage WHERE head = ?").run(head);
|
|
@@ -27078,6 +27308,45 @@ function createHistory(database, git) {
|
|
|
27078
27308
|
}
|
|
27079
27309
|
return pending;
|
|
27080
27310
|
}
|
|
27311
|
+
async function readCommit(commit) {
|
|
27312
|
+
const known = await database.with((db) => !!db.prepare("SELECT 1 FROM commits WHERE oid = ?").get(commit));
|
|
27313
|
+
if (known) return;
|
|
27314
|
+
const read2 = await changes2.of([commit]).catch(() => void 0);
|
|
27315
|
+
if (read2) await database.with((db) => insert(db, read2, /* @__PURE__ */ new Map()));
|
|
27316
|
+
}
|
|
27317
|
+
function sameRecord(db, commit, id) {
|
|
27318
|
+
if (!id.startsWith("H-")) return [id];
|
|
27319
|
+
const own2 = db.prepare("SELECT title, sections FROM records WHERE oid = ? AND id = ?").get(commit, id);
|
|
27320
|
+
if (!own2) return [id];
|
|
27321
|
+
return db.prepare("SELECT id FROM records WHERE oid = ? AND id LIKE 'H-%' AND title = ? AND sections = ?").all(commit, own2.title, own2.sections).map((r) => r.id);
|
|
27322
|
+
}
|
|
27323
|
+
function hydrate(db, rows) {
|
|
27324
|
+
const events = rows.map((row) => JSON.parse(row));
|
|
27325
|
+
const read2 = db.prepare("SELECT id, title, sections FROM records WHERE oid = ?");
|
|
27326
|
+
const byCommit = /* @__PURE__ */ new Map();
|
|
27327
|
+
for (const e of events) if (e.records.length && !byCommit.has(e.commit)) {
|
|
27328
|
+
byCommit.set(e.commit, new Map(read2.all(e.commit).map((r) => [r.id, { id: r.id, title: r.title, sections: JSON.parse(r.sections) }])));
|
|
27329
|
+
}
|
|
27330
|
+
return events.map((e) => ({ ...e, records: e.records.flatMap((id) => {
|
|
27331
|
+
const r = byCommit.get(e.commit)?.get(id);
|
|
27332
|
+
return r ? [r] : [];
|
|
27333
|
+
}) }));
|
|
27334
|
+
}
|
|
27335
|
+
async function detailOf(rows) {
|
|
27336
|
+
const events = await database.with((db) => hydrate(db, rows.map((r) => r.row)));
|
|
27337
|
+
const sides = rows.map((r) => JSON.parse(r.detail));
|
|
27338
|
+
const places = [];
|
|
27339
|
+
sides.forEach((side, i) => {
|
|
27340
|
+
for (const key of ["before", "after"]) {
|
|
27341
|
+
const value = side[key];
|
|
27342
|
+
if (value && "rev" in value) places.push({ rev: value.rev, path: value.path, specId: events[i][key]?.specId ?? "" });
|
|
27343
|
+
}
|
|
27344
|
+
});
|
|
27345
|
+
const read2 = places.length ? await texts.sides(places) : [];
|
|
27346
|
+
const text4 = new Map(places.map((p, i) => [p.rev + ":" + p.path, read2[i] ?? null]));
|
|
27347
|
+
const resolve = (value) => !value ? null : "rev" in value ? text4.get(value.rev + ":" + value.path) ?? null : value;
|
|
27348
|
+
return events.map((event2, i) => ({ event: event2, before: resolve(sides[i].before), after: resolve(sides[i].after) }));
|
|
27349
|
+
}
|
|
27081
27350
|
function where(filter) {
|
|
27082
27351
|
const clauses = [];
|
|
27083
27352
|
const params = [];
|
|
@@ -27106,22 +27375,60 @@ function createHistory(database, git) {
|
|
|
27106
27375
|
return {
|
|
27107
27376
|
ensure,
|
|
27108
27377
|
/** The document and reason files of one commit, read from Git and not kept. */
|
|
27109
|
-
filesAt: (rev) =>
|
|
27110
|
-
/**
|
|
27111
|
-
|
|
27378
|
+
filesAt: (rev) => texts.tree(rev),
|
|
27379
|
+
/**
|
|
27380
|
+
* The changes that match, newest first, a page of whole commits: `limit` commits after the commit `after`, each
|
|
27381
|
+
* with all of its matching changes, and how many changes and commits match in all. `next` is the last commit of the
|
|
27382
|
+
* page when more follow. A commit `after` that is not in `head`'s matching history answers undefined.
|
|
27383
|
+
*/
|
|
27384
|
+
async commits(head, filter, after, limit) {
|
|
27112
27385
|
await ensure(head);
|
|
27113
27386
|
return database.with((db) => {
|
|
27114
27387
|
const { sql, params } = where(filter);
|
|
27115
27388
|
const from = "FROM lineage l JOIN changes c ON c.oid = l.oid WHERE l.head = ?" + sql;
|
|
27116
|
-
const
|
|
27117
|
-
|
|
27118
|
-
|
|
27389
|
+
const counted = db.prepare("SELECT count(*) AS n, count(DISTINCT c.oid) AS commits " + from).get(head, ...params);
|
|
27390
|
+
let start = -1;
|
|
27391
|
+
if (after !== void 0) {
|
|
27392
|
+
const found = db.prepare("SELECT l.pos AS pos " + from + " AND l.oid = ? LIMIT 1").get(head, ...params, after);
|
|
27393
|
+
if (!found) return void 0;
|
|
27394
|
+
start = found.pos;
|
|
27395
|
+
}
|
|
27396
|
+
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);
|
|
27397
|
+
const shown = commits.slice(0, limit);
|
|
27398
|
+
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);
|
|
27399
|
+
return {
|
|
27400
|
+
total: Number(counted.n),
|
|
27401
|
+
commits: Number(counted.commits),
|
|
27402
|
+
next: commits.length > limit ? shown[shown.length - 1].oid : null,
|
|
27403
|
+
events: hydrate(db, rows.map((r) => r.row))
|
|
27404
|
+
};
|
|
27405
|
+
});
|
|
27406
|
+
},
|
|
27407
|
+
/**
|
|
27408
|
+
* The changes of one commit without their text, a page at a time after the change `after` (a key): the commit page's
|
|
27409
|
+
* list. With `record`, only the changes that record explains: the record page's documents.
|
|
27410
|
+
*/
|
|
27411
|
+
async commitChanges(commit, after, limit, record4) {
|
|
27412
|
+
await readCommit(commit);
|
|
27413
|
+
return database.with((db) => {
|
|
27414
|
+
const ids = record4 === void 0 ? void 0 : JSON.stringify(sameRecord(db, commit, record4));
|
|
27415
|
+
const all = ids === void 0 ? db.prepare("SELECT key, row FROM changes WHERE oid = ? ORDER BY ord").all(commit) : db.prepare("SELECT key, row FROM changes c WHERE oid = ? AND EXISTS (SELECT 1 FROM json_each(c.row, '$.records') j WHERE j.value IN (SELECT value FROM json_each(?))) ORDER BY ord").all(commit, ids);
|
|
27416
|
+
const start = after === void 0 ? 0 : all.findIndex((r) => r.key === after) + 1;
|
|
27417
|
+
if (after !== void 0 && !start) return void 0;
|
|
27418
|
+
const shown = all.slice(start, start + limit);
|
|
27419
|
+
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
27420
|
});
|
|
27120
27421
|
},
|
|
27422
|
+
/** One change of a commit with the text on both sides, read again from Git: the change the reader opened. */
|
|
27423
|
+
async commitChange(commit, id) {
|
|
27424
|
+
await readCommit(commit);
|
|
27425
|
+
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));
|
|
27426
|
+
return rows.length ? (await detailOf(rows))[0] : void 0;
|
|
27427
|
+
},
|
|
27121
27428
|
/** Every change of one document, newest first: `records list --doc`. */
|
|
27122
27429
|
async ofDocument(head, id) {
|
|
27123
27430
|
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) =>
|
|
27431
|
+
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
27432
|
},
|
|
27126
27433
|
/** Who changed which document in which commit of `head`'s history, newest first: what the lists filter and sort by. */
|
|
27127
27434
|
async changesOf(head) {
|
|
@@ -27130,25 +27437,60 @@ function createHistory(database, git) {
|
|
|
27130
27437
|
FROM lineage l JOIN changes c ON c.oid = l.oid WHERE l.head = ? ORDER BY l.pos, c.ord`).all(head));
|
|
27131
27438
|
},
|
|
27132
27439
|
/**
|
|
27133
|
-
*
|
|
27134
|
-
*
|
|
27440
|
+
* The records of `head`'s history that match, newest first, a page at a time: what `records list` shows after the
|
|
27441
|
+
* records not committed yet. The filters and the page are SQL over the records table, so a page reads only its
|
|
27442
|
+
* rows. `after` is the ID of the last record of the previous page; `limit` undefined is every match.
|
|
27135
27443
|
*/
|
|
27136
|
-
async
|
|
27444
|
+
async records(head, filter, after, limit) {
|
|
27137
27445
|
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;
|
|
27446
|
+
return database.with((db) => {
|
|
27447
|
+
const clauses = [];
|
|
27448
|
+
const params = [];
|
|
27449
|
+
if (filter.author) {
|
|
27450
|
+
clauses.push("(lower(json_extract(row, '$.author')) = ? OR lower(json_extract(row, '$.email')) = ?)");
|
|
27451
|
+
params.push(filter.author.toLowerCase(), filter.author.toLowerCase());
|
|
27148
27452
|
}
|
|
27149
|
-
|
|
27150
|
-
|
|
27151
|
-
|
|
27453
|
+
if (filter.q?.trim()) {
|
|
27454
|
+
clauses.push("instr(lower(title || ' ' || body || ' ' || json_extract(row, '$.message')), ?) > 0");
|
|
27455
|
+
params.push(filter.q.trim().toLowerCase());
|
|
27456
|
+
}
|
|
27457
|
+
if (filter.from !== void 0) {
|
|
27458
|
+
clauses.push("unixepoch(json_extract(row, '$.date')) * 1000 >= ?");
|
|
27459
|
+
params.push(filter.from);
|
|
27460
|
+
}
|
|
27461
|
+
if (filter.commits) {
|
|
27462
|
+
clauses.push("oid IN (SELECT value FROM json_each(?))");
|
|
27463
|
+
params.push(JSON.stringify(filter.commits));
|
|
27464
|
+
}
|
|
27465
|
+
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,
|
|
27466
|
+
(SELECT group_concat(json_extract(s.value, '$.body'), ' ') FROM json_each(r.sections) s) AS body,
|
|
27467
|
+
(SELECT c.row FROM changes c WHERE c.oid = r.oid ORDER BY c.ord LIMIT 1) AS row
|
|
27468
|
+
FROM lineage l JOIN records r ON r.oid = l.oid WHERE l.head = ?)
|
|
27469
|
+
SELECT * FROM meta WHERE 1 = 1${clauses.map((c) => " AND " + c).join("")}`;
|
|
27470
|
+
const total = Number(db.prepare(`SELECT count(*) AS n FROM (${matching})`).get(head, ...params).n);
|
|
27471
|
+
let from;
|
|
27472
|
+
if (after !== void 0) {
|
|
27473
|
+
from = db.prepare(`SELECT pos, id FROM (${matching}) WHERE id = ?`).get(head, ...params, after);
|
|
27474
|
+
if (!from) return void 0;
|
|
27475
|
+
}
|
|
27476
|
+
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);
|
|
27477
|
+
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");
|
|
27478
|
+
const records2 = rows.map((r) => {
|
|
27479
|
+
const commit = JSON.parse(r.row);
|
|
27480
|
+
return {
|
|
27481
|
+
id: r.id,
|
|
27482
|
+
title: r.title,
|
|
27483
|
+
sections: JSON.parse(r.sections),
|
|
27484
|
+
commit: r.oid,
|
|
27485
|
+
date: commit.date,
|
|
27486
|
+
author: commit.author,
|
|
27487
|
+
email: commit.email,
|
|
27488
|
+
message: commit.message,
|
|
27489
|
+
docs: docsOf.all(r.oid, r.id).map((d) => d.id)
|
|
27490
|
+
};
|
|
27491
|
+
});
|
|
27492
|
+
return { records: records2, total };
|
|
27493
|
+
});
|
|
27152
27494
|
},
|
|
27153
27495
|
/** Counts over all of history and its newest commits, for the overview. */
|
|
27154
27496
|
async summary(head) {
|
|
@@ -27160,110 +27502,299 @@ function createHistory(database, git) {
|
|
|
27160
27502
|
const commits = db.prepare(`SELECT l.oid AS oid, max(c.date) AS date, count(*) AS n ${from} GROUP BY l.pos, l.oid ORDER BY l.pos`).all(head);
|
|
27161
27503
|
const newest = Math.max(0, ...commits.map((c) => Date.parse(c.date)).filter(Number.isFinite));
|
|
27162
27504
|
const pulse = commits.filter((c) => Date.parse(c.date) >= newest - 22 * DAY).map((c) => ({ date: c.date, count: Number(c.n) }));
|
|
27163
|
-
const
|
|
27505
|
+
const recent2 = commits.slice(0, RECENT_COMMITS).map((c) => ({
|
|
27164
27506
|
commit: c.oid,
|
|
27165
27507
|
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) =>
|
|
27508
|
+
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
27509
|
}));
|
|
27168
|
-
return { total, byType: { created: count2("created"), modified: count2("modified"), moved: count2("moved"), deleted: count2("deleted") }, pulse, recent };
|
|
27510
|
+
return { total, byType: { created: count2("created"), modified: count2("modified"), moved: count2("moved"), deleted: count2("deleted") }, pulse, recent: recent2 };
|
|
27169
27511
|
});
|
|
27170
27512
|
},
|
|
27171
|
-
/**
|
|
27172
|
-
|
|
27513
|
+
/**
|
|
27514
|
+
* The record of `head`'s history with the commit that added it, and how many other records that commit holds (a
|
|
27515
|
+
* 0.7 reason written once over several documents counts once). Undefined when the history has no such record.
|
|
27516
|
+
*/
|
|
27517
|
+
async recordOf(head, id) {
|
|
27173
27518
|
await ensure(head);
|
|
27174
|
-
return database.with((db) =>
|
|
27175
|
-
WHERE l.head = ? AND
|
|
27176
|
-
|
|
27177
|
-
|
|
27178
|
-
|
|
27179
|
-
|
|
27180
|
-
|
|
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) })));
|
|
27519
|
+
return database.with((db) => {
|
|
27520
|
+
const found = db.prepare("SELECT r.oid AS oid, r.title AS title, r.sections AS sections 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);
|
|
27521
|
+
if (!found) return void 0;
|
|
27522
|
+
const all = db.prepare("SELECT id, title, sections FROM records WHERE oid = ?").all(found.oid);
|
|
27523
|
+
const groups = new Set(all.map((r) => r.id.startsWith("H-") ? "H\0" + r.title + "\0" + r.sections : r.id));
|
|
27524
|
+
return { commit: found.oid, record: { id, title: found.title, sections: JSON.parse(found.sections) }, others: groups.size - 1 };
|
|
27525
|
+
});
|
|
27187
27526
|
},
|
|
27188
27527
|
/**
|
|
27189
|
-
*
|
|
27190
|
-
*
|
|
27528
|
+
* The records of `head`'s history whose title, ID or sections hold the query, one hit per record file, a page at a
|
|
27529
|
+
* time: a title that starts with it first, then a title, the ID, and the sections; newest first within each. `after`
|
|
27530
|
+
* is the ID of the last hit of the page before; one that no longer matches answers undefined.
|
|
27191
27531
|
*/
|
|
27192
|
-
async
|
|
27532
|
+
async searchRecords(head, raw, after, limit) {
|
|
27193
27533
|
const query = raw.trim().toLowerCase();
|
|
27194
|
-
|
|
27195
|
-
if (head) await ensure(head);
|
|
27534
|
+
await ensure(head);
|
|
27196
27535
|
return database.with((db) => {
|
|
27197
|
-
const
|
|
27198
|
-
|
|
27199
|
-
|
|
27200
|
-
|
|
27201
|
-
|
|
27202
|
-
|
|
27203
|
-
|
|
27204
|
-
|
|
27205
|
-
|
|
27206
|
-
|
|
27207
|
-
|
|
27208
|
-
|
|
27209
|
-
|
|
27210
|
-
|
|
27211
|
-
|
|
27212
|
-
|
|
27213
|
-
|
|
27214
|
-
|
|
27215
|
-
|
|
27216
|
-
|
|
27217
|
-
const past = !head ? [] : db.prepare(`SELECT s.ref AS ref, s.payload AS payload FROM search s JOIN lineage l ON l.head = ? AND l.oid = s.oid
|
|
27218
|
-
WHERE s.scope = 'history' AND (${match("s.title")} OR ${match("s.body")}) ORDER BY l.pos LIMIT 8`).all(head, needle, needle).map((r) => {
|
|
27219
|
-
const p = JSON.parse(r.payload);
|
|
27220
|
-
return { id: r.ref, kind: "history", title: p.title, where: p.where, line: snippet(p.line, query), key: r.ref };
|
|
27536
|
+
const ordered = `WITH own AS (
|
|
27537
|
+
SELECT r.id AS id, r.title AS title, r.oid AS oid, min(l.pos) AS pos, r.sections AS sections,
|
|
27538
|
+
lower((SELECT group_concat(json_extract(s.value, '$.body'), ' ') FROM json_each(r.sections) s)) AS body
|
|
27539
|
+
FROM lineage l JOIN records r ON r.oid = l.oid WHERE l.head = :head GROUP BY r.id),
|
|
27540
|
+
hits AS (SELECT *, CASE WHEN substr(lower(title), 1, length(:q)) = :q THEN 0 WHEN instr(lower(title), :q) > 0 THEN 1
|
|
27541
|
+
WHEN instr(lower(id), :q) > 0 THEN 2 ELSE 3 END AS rank
|
|
27542
|
+
FROM own WHERE instr(lower(title), :q) > 0 OR instr(lower(id), :q) > 0 OR instr(body, :q) > 0),
|
|
27543
|
+
ordered AS (SELECT *, row_number() OVER (ORDER BY rank, pos, id) AS n FROM hits)`;
|
|
27544
|
+
const params = { head, q: query };
|
|
27545
|
+
const total = Number(db.prepare(`${ordered} SELECT count(*) AS n FROM ordered`).get(params).n);
|
|
27546
|
+
let start = 0;
|
|
27547
|
+
if (after !== void 0) {
|
|
27548
|
+
const at = db.prepare(`${ordered} SELECT n FROM ordered WHERE id = :after`).get({ ...params, after });
|
|
27549
|
+
if (!at) return void 0;
|
|
27550
|
+
start = Number(at.n);
|
|
27551
|
+
}
|
|
27552
|
+
const rows = db.prepare(`${ordered} SELECT id, title, oid, sections FROM ordered WHERE n > :start ORDER BY n LIMIT :limit`).all({ ...params, start, limit: limit + 1 });
|
|
27553
|
+
const hits = rows.slice(0, limit).map((r) => {
|
|
27554
|
+
const text4 = JSON.parse(r.sections).map((s) => s.body).join(" ");
|
|
27555
|
+
return { id: r.id, kind: "record", title: r.title, where: r.id + " \xB7 " + r.oid.slice(0, 7), line: snippet(text4, query), commit: r.oid };
|
|
27221
27556
|
});
|
|
27222
|
-
return [
|
|
27557
|
+
return { total, next: rows.length > limit ? hits[hits.length - 1].id : null, hits };
|
|
27558
|
+
});
|
|
27559
|
+
}
|
|
27560
|
+
};
|
|
27561
|
+
}
|
|
27562
|
+
|
|
27563
|
+
// src/adapters/cache/commit-log.ts
|
|
27564
|
+
import { lstat as lstat3 } from "node:fs/promises";
|
|
27565
|
+
import { join as join4 } from "node:path";
|
|
27566
|
+
var FORMAT2 = `%x1e%H%x00%aN%x00%aE%x00%at%x00%aI%x00%s%x00%(trailers:key=${MIGRATION_TRAILER},valueonly,separator=%x2C)`;
|
|
27567
|
+
var CHUNK = 1e5;
|
|
27568
|
+
var folderOf2 = (path3) => path3 === "AGENTS.md" ? path3 : new RegExp(`^(${SPEC_ROOT.replace(/\./g, "\\.")}/[^/]+|${INSTRUCTIONS_ROOT.replace(/\./g, "\\.")}/[^/]+)/`).exec(path3)?.[1];
|
|
27569
|
+
function createCommitLog(database, git, root) {
|
|
27570
|
+
let following;
|
|
27571
|
+
let followingHead;
|
|
27572
|
+
const read = async (args) => git.decode(await git.run(args));
|
|
27573
|
+
async function commits(range) {
|
|
27574
|
+
const out = [];
|
|
27575
|
+
for (let skip = 0; ; skip += CHUNK) {
|
|
27576
|
+
const text4 = await read(["log", `--format=${FORMAT2}`, `--max-count=${CHUNK}`, `--skip=${skip}`, ...range, "--"]);
|
|
27577
|
+
const chunk = text4.split("").filter(Boolean).map((entry2) => {
|
|
27578
|
+
const [oid3, name2, email3, time3, date5, subject, migration] = entry2.split("\0");
|
|
27579
|
+
return { oid: oid3, name: name2 ?? "", email: email3 ?? "", time: Number(time3), date: date5 ?? "", subject: subject ?? "", migration: !!migration?.trim() };
|
|
27580
|
+
});
|
|
27581
|
+
out.push(...chunk);
|
|
27582
|
+
if (chunk.length < CHUNK) return out;
|
|
27583
|
+
}
|
|
27584
|
+
}
|
|
27585
|
+
async function touches(range) {
|
|
27586
|
+
const text4 = await read(["log", "--format=%x1e%H", "-z", "--name-only", "--no-renames", ...range, "--", SPEC_ROOT, INSTRUCTIONS_ROOT, "AGENTS.md"]);
|
|
27587
|
+
const out = [];
|
|
27588
|
+
for (const entry2 of text4.split("").filter(Boolean)) {
|
|
27589
|
+
const [oid3, ...paths2] = entry2.split("\0");
|
|
27590
|
+
const folders = new Set(paths2.map((p) => folderOf2(p.replace(/^\n/, ""))).filter((f) => !!f));
|
|
27591
|
+
for (const folder of folders) out.push([oid3.trim(), folder]);
|
|
27592
|
+
}
|
|
27593
|
+
return out;
|
|
27594
|
+
}
|
|
27595
|
+
async function mailmap() {
|
|
27596
|
+
const info5 = await lstat3(join4(root, ".mailmap")).catch(() => void 0);
|
|
27597
|
+
return info5 ? info5.mtimeMs + ":" + info5.size : "-";
|
|
27598
|
+
}
|
|
27599
|
+
const stateOf = (db, key) => db.prepare("SELECT value FROM state WHERE key = ?").get(key)?.value;
|
|
27600
|
+
async function follow(head, attempts = 3) {
|
|
27601
|
+
const names = await mailmap();
|
|
27602
|
+
const [before, named] = await database.with((db) => [stateOf(db, "head"), stateOf(db, "mailmap")]);
|
|
27603
|
+
if (before === head && named === names) return;
|
|
27604
|
+
let change2;
|
|
27605
|
+
if (before && named === names) {
|
|
27606
|
+
try {
|
|
27607
|
+
const range = [head, "^" + before];
|
|
27608
|
+
const [added2, removed2, touched2] = await Promise.all([commits(range), read(["rev-list", before, "^" + head, "--"]), touches(range)]);
|
|
27609
|
+
change2 = { added: added2, removed: removed2.split("\n").filter(Boolean), touched: touched2, whole: false };
|
|
27610
|
+
} catch {
|
|
27611
|
+
change2 = void 0;
|
|
27612
|
+
}
|
|
27613
|
+
}
|
|
27614
|
+
if (!change2) {
|
|
27615
|
+
const [added2, touched2] = await Promise.all([commits([head]), touches([head])]);
|
|
27616
|
+
change2 = { added: added2, removed: [], touched: touched2, whole: true };
|
|
27617
|
+
}
|
|
27618
|
+
const { added, removed, touched, whole } = change2;
|
|
27619
|
+
const written = await database.with((db) => transaction(db, () => {
|
|
27620
|
+
if (stateOf(db, "head") !== before || stateOf(db, "mailmap") !== named) return false;
|
|
27621
|
+
if (whole) {
|
|
27622
|
+
db.exec("DELETE FROM log");
|
|
27623
|
+
db.exec("DELETE FROM touches");
|
|
27624
|
+
}
|
|
27625
|
+
const put = db.prepare(`INSERT INTO log (oid, name, email, time, date, subject, migration, reach) VALUES (?, ?, ?, ?, ?, ?, ?, 1)
|
|
27626
|
+
ON CONFLICT (oid) DO UPDATE SET name = excluded.name, email = excluded.email, reach = 1`);
|
|
27627
|
+
for (const c of added) put.run(c.oid, c.name, c.email, c.time, c.date, c.subject, c.migration ? 1 : 0);
|
|
27628
|
+
const leave = db.prepare("UPDATE log SET reach = 0 WHERE oid = ?");
|
|
27629
|
+
for (const oid3 of removed) leave.run(oid3);
|
|
27630
|
+
const touch = db.prepare("INSERT OR IGNORE INTO touches (oid, folder) VALUES (?, ?)");
|
|
27631
|
+
for (const [oid3, folder] of touched) touch.run(oid3, folder);
|
|
27632
|
+
const set2 = db.prepare("INSERT INTO state (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value = excluded.value");
|
|
27633
|
+
set2.run("head", head);
|
|
27634
|
+
set2.run("mailmap", names);
|
|
27635
|
+
return true;
|
|
27636
|
+
}));
|
|
27637
|
+
if (!written && attempts > 1) return follow(head, attempts - 1);
|
|
27638
|
+
}
|
|
27639
|
+
async function ensure(head) {
|
|
27640
|
+
while (following && followingHead !== head) await following.catch(() => void 0);
|
|
27641
|
+
if (!following) {
|
|
27642
|
+
followingHead = head;
|
|
27643
|
+
following = follow(head).finally(() => {
|
|
27644
|
+
following = void 0;
|
|
27645
|
+
followingHead = void 0;
|
|
27646
|
+
});
|
|
27647
|
+
}
|
|
27648
|
+
return following;
|
|
27649
|
+
}
|
|
27650
|
+
return {
|
|
27651
|
+
ensure,
|
|
27652
|
+
/** Everyone who committed to `head`, most commits first, format migrations left out. `latest` is their newest commit. */
|
|
27653
|
+
async contributors(head) {
|
|
27654
|
+
await ensure(head);
|
|
27655
|
+
return database.with((db) => db.prepare(`SELECT email, name, count(*) AS commits, max(time) AS time, date AS latest FROM log
|
|
27656
|
+
WHERE reach = 1 AND migration = 0 GROUP BY email ORDER BY commits DESC, name, email`).all().map(({ name: name2, email: email3, commits: commits2, latest }) => ({ name: name2, email: email3, commits: Number(commits2), latest })));
|
|
27657
|
+
},
|
|
27658
|
+
/**
|
|
27659
|
+
* The commits of `head` a hash names: the whole hash, or its start from seven characters on, as `git log --oneline`
|
|
27660
|
+
* prints it. Anything shorter or not a hash names none. Newest first, a page after the commit `after`.
|
|
27661
|
+
*/
|
|
27662
|
+
async commitsNamed(head, raw, after, limit) {
|
|
27663
|
+
const prefix = raw.trim().toLowerCase();
|
|
27664
|
+
if (!/^[0-9a-f]{7,64}$/.test(prefix)) return { total: 0, next: null, hits: [] };
|
|
27665
|
+
await ensure(head);
|
|
27666
|
+
return database.with((db) => {
|
|
27667
|
+
const rows = db.prepare("SELECT oid, name, date, subject FROM log WHERE reach = 1 AND substr(oid, 1, length(?)) = ? ORDER BY time DESC, oid").all(prefix, prefix);
|
|
27668
|
+
const start = after === void 0 ? 0 : rows.findIndex((r) => r.oid === after) + 1;
|
|
27669
|
+
if (after !== void 0 && !start) return void 0;
|
|
27670
|
+
const shown = rows.slice(start, start + limit);
|
|
27671
|
+
return {
|
|
27672
|
+
total: rows.length,
|
|
27673
|
+
next: start + shown.length < rows.length ? shown[shown.length - 1].oid : null,
|
|
27674
|
+
hits: shown.map((r) => ({ id: r.oid, kind: "commit", title: r.subject, where: r.oid.slice(0, 7) + " \xB7 " + r.name, line: r.date.slice(0, 10), commit: r.oid }))
|
|
27675
|
+
};
|
|
27676
|
+
});
|
|
27677
|
+
},
|
|
27678
|
+
/** Each store folder's authors, most commits first, and when it was last touched, format migrations left out. */
|
|
27679
|
+
async folders(head) {
|
|
27680
|
+
await ensure(head);
|
|
27681
|
+
return database.with((db) => {
|
|
27682
|
+
const rows = db.prepare(`SELECT t.folder AS folder, l.email AS email, l.name AS name, count(*) AS commits, max(l.time) AS time, l.date AS latest
|
|
27683
|
+
FROM touches t JOIN log l ON l.oid = t.oid WHERE l.reach = 1 AND l.migration = 0
|
|
27684
|
+
GROUP BY t.folder, l.email ORDER BY t.folder, commits DESC, name, email`).all();
|
|
27685
|
+
const out = /* @__PURE__ */ new Map();
|
|
27686
|
+
for (const r of rows) {
|
|
27687
|
+
let entry2 = out.get(r.folder);
|
|
27688
|
+
if (!entry2) out.set(r.folder, entry2 = { people: [], latest: r.latest, time: r.time });
|
|
27689
|
+
entry2.people.push({ name: r.name, email: r.email, commits: Number(r.commits), latest: r.latest });
|
|
27690
|
+
if (r.time > entry2.time) {
|
|
27691
|
+
entry2.time = r.time;
|
|
27692
|
+
entry2.latest = r.latest;
|
|
27693
|
+
}
|
|
27694
|
+
}
|
|
27695
|
+
return new Map([...out].map(([folder, { people, latest }]) => [folder, { people, latest }]));
|
|
27223
27696
|
});
|
|
27224
27697
|
}
|
|
27225
27698
|
};
|
|
27226
27699
|
}
|
|
27227
27700
|
|
|
27228
27701
|
// src/adapters/cache/index.ts
|
|
27229
|
-
function openCache(root, git) {
|
|
27702
|
+
function openCache(root, git, originals) {
|
|
27230
27703
|
const database = createCacheDatabase(root);
|
|
27231
27704
|
const documents = createDocumentCache(root, database);
|
|
27232
|
-
const history = createHistory(database, git);
|
|
27705
|
+
const history = createHistory(database, git, originals);
|
|
27706
|
+
const log = createCommitLog(database, git, root);
|
|
27233
27707
|
return {
|
|
27234
27708
|
documents,
|
|
27235
27709
|
history,
|
|
27236
|
-
|
|
27237
|
-
|
|
27238
|
-
async search(head, query) {
|
|
27239
|
-
await documents.sync();
|
|
27240
|
-
return history.search(head, query);
|
|
27241
|
-
}
|
|
27710
|
+
log,
|
|
27711
|
+
location: database.location
|
|
27242
27712
|
};
|
|
27243
27713
|
}
|
|
27244
27714
|
|
|
27715
|
+
// src/adapters/git/pending-records.ts
|
|
27716
|
+
import { lstat as lstat4, readdir as readdir3, readFile as readFile3 } from "node:fs/promises";
|
|
27717
|
+
import { join as join5 } from "node:path";
|
|
27718
|
+
var decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
27719
|
+
var info = (path3) => lstat4(path3).catch((error62) => {
|
|
27720
|
+
if (error62.code === "ENOENT") return void 0;
|
|
27721
|
+
throw error62;
|
|
27722
|
+
});
|
|
27723
|
+
async function recordFolders(root) {
|
|
27724
|
+
return (await readdir3(join5(root, ...RECORDS_ROOT.split("/"))).catch((error62) => {
|
|
27725
|
+
if (error62.code === "ENOENT") return [];
|
|
27726
|
+
throw error62;
|
|
27727
|
+
})).filter(isRecordFolder);
|
|
27728
|
+
}
|
|
27729
|
+
async function findRecord(root, id) {
|
|
27730
|
+
for (const folder of await recordFolders(root)) {
|
|
27731
|
+
const path3 = `${RECORDS_ROOT}/${folder}/${id}.md`;
|
|
27732
|
+
if ((await info(join5(root, ...path3.split("/"))))?.isFile()) return path3;
|
|
27733
|
+
}
|
|
27734
|
+
return void 0;
|
|
27735
|
+
}
|
|
27736
|
+
async function readPendingRecords(root, run) {
|
|
27737
|
+
const status = (await run(["status", "--porcelain=v1", "-z", "--untracked-files=all", "--no-renames", "--", RECORDS_ROOT])).toString("utf8").split("\0").filter(Boolean);
|
|
27738
|
+
const files = /* @__PURE__ */ new Map();
|
|
27739
|
+
const altered = [];
|
|
27740
|
+
const problems = [];
|
|
27741
|
+
for (const entry2 of status) {
|
|
27742
|
+
const state2 = entry2.slice(0, 2);
|
|
27743
|
+
const path3 = entry2.slice(3);
|
|
27744
|
+
if (state2 !== "??" && !state2.includes("A")) {
|
|
27745
|
+
altered.push(path3);
|
|
27746
|
+
continue;
|
|
27747
|
+
}
|
|
27748
|
+
const file2 = join5(root, ...path3.split("/"));
|
|
27749
|
+
const stat3 = await info(file2);
|
|
27750
|
+
if (!stat3?.isFile() || stat3.isSymbolicLink()) {
|
|
27751
|
+
problems.push(docProblem("PATH_UNSUPPORTED", path3));
|
|
27752
|
+
continue;
|
|
27753
|
+
}
|
|
27754
|
+
if (stat3.size > 1024 * 1024) {
|
|
27755
|
+
problems.push(docProblem("FILE_TOO_LARGE", path3));
|
|
27756
|
+
continue;
|
|
27757
|
+
}
|
|
27758
|
+
try {
|
|
27759
|
+
files.set(path3, decoder.decode(await readFile3(file2)));
|
|
27760
|
+
} catch {
|
|
27761
|
+
problems.push(docProblem("INVALID_CHARACTERS", path3));
|
|
27762
|
+
}
|
|
27763
|
+
}
|
|
27764
|
+
const folders = await recordFolders(root);
|
|
27765
|
+
for (const path3 of files.keys()) {
|
|
27766
|
+
const id = recordIdOf(path3);
|
|
27767
|
+
if (!id) continue;
|
|
27768
|
+
for (const folder of folders) {
|
|
27769
|
+
const other = `${RECORDS_ROOT}/${folder}/${id}.md`;
|
|
27770
|
+
if (other !== path3 && !files.has(other) && (await info(join5(root, ...other.split("/"))))?.isFile()) problems.push(docProblem("DUPLICATE_RECORD_ID", path3, { id, other }));
|
|
27771
|
+
}
|
|
27772
|
+
}
|
|
27773
|
+
return { files, altered: altered.sort(), problems };
|
|
27774
|
+
}
|
|
27775
|
+
|
|
27245
27776
|
// src/adapters/filesystem/config-file.ts
|
|
27246
|
-
import { lstat as
|
|
27247
|
-
import { join as
|
|
27777
|
+
import { lstat as lstat5, readFile as readFile4, realpath, mkdir as mkdir2, open as open2, link, rename, unlink } from "node:fs/promises";
|
|
27778
|
+
import { join as join6 } from "node:path";
|
|
27248
27779
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
27249
27780
|
var missing = (error62) => error62.code === "ENOENT";
|
|
27250
|
-
var fileInfo = (path3) =>
|
|
27781
|
+
var fileInfo = (path3) => lstat5(path3).catch((error62) => {
|
|
27251
27782
|
if (missing(error62)) return void 0;
|
|
27252
27783
|
throw error62;
|
|
27253
27784
|
});
|
|
27254
27785
|
async function configDirectory(root) {
|
|
27255
|
-
const path3 =
|
|
27786
|
+
const path3 = join6(root, ".gitifact");
|
|
27256
27787
|
const info5 = await fileInfo(path3);
|
|
27257
27788
|
if (info5 && (!info5.isDirectory() || info5.isSymbolicLink())) throw new InitError("PATH_CONFLICT", t2("config.storeNotDirectory"));
|
|
27258
27789
|
return info5;
|
|
27259
27790
|
}
|
|
27260
27791
|
async function readConfigFile(root) {
|
|
27261
27792
|
await configDirectory(root);
|
|
27262
|
-
const path3 =
|
|
27793
|
+
const path3 = join6(root, ".gitifact", "config.json");
|
|
27263
27794
|
const info5 = await fileInfo(path3);
|
|
27264
27795
|
if (!info5) return void 0;
|
|
27265
27796
|
if (!info5.isFile() || info5.isSymbolicLink() || info5.size > 65536) throw new InitError("PATH_CONFLICT", t2("config.fileShape"));
|
|
27266
|
-
const bytes = await
|
|
27797
|
+
const bytes = await readFile4(path3);
|
|
27267
27798
|
const after = await fileInfo(path3);
|
|
27268
27799
|
await configDirectory(root);
|
|
27269
27800
|
if (!after || after.ino !== info5.ino || after.dev !== info5.dev || after.mtimeMs !== info5.mtimeMs || bytes.length > 65536) {
|
|
@@ -27276,7 +27807,7 @@ async function readConfigFile(root) {
|
|
|
27276
27807
|
}
|
|
27277
27808
|
}
|
|
27278
27809
|
async function publishConfig(root, text4, recheck, beforePublish, replace = false) {
|
|
27279
|
-
const directory =
|
|
27810
|
+
const directory = join6(root, ".gitifact");
|
|
27280
27811
|
await configDirectory(root);
|
|
27281
27812
|
await mkdir2(directory).catch((error62) => {
|
|
27282
27813
|
if (error62.code !== "EEXIST") throw error62;
|
|
@@ -27289,7 +27820,7 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
|
|
|
27289
27820
|
throw new InitError("INPUT_CHANGED", t2("config.directoryChanged"));
|
|
27290
27821
|
}
|
|
27291
27822
|
};
|
|
27292
|
-
const temporary =
|
|
27823
|
+
const temporary = join6(directory, ".init-" + randomUUID2() + ".tmp");
|
|
27293
27824
|
const handle = await open2(temporary, "wx", 384);
|
|
27294
27825
|
const tempOwner = await handle.stat();
|
|
27295
27826
|
try {
|
|
@@ -27303,11 +27834,11 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
|
|
|
27303
27834
|
await unchanged();
|
|
27304
27835
|
await recheck();
|
|
27305
27836
|
await unchanged();
|
|
27306
|
-
const bytes = await
|
|
27837
|
+
const bytes = await readFile4(temporary, "utf8");
|
|
27307
27838
|
if (bytes !== text4) throw new InitError("INPUT_CHANGED", t2("config.temporaryChanged"));
|
|
27308
27839
|
parseManagedConfig(bytes);
|
|
27309
|
-
if (replace) await rename(temporary,
|
|
27310
|
-
else await link(temporary,
|
|
27840
|
+
if (replace) await rename(temporary, join6(directory, "config.json"));
|
|
27841
|
+
else await link(temporary, join6(directory, "config.json"));
|
|
27311
27842
|
} finally {
|
|
27312
27843
|
await unchanged();
|
|
27313
27844
|
const current2 = await fileInfo(temporary);
|
|
@@ -27316,42 +27847,64 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
|
|
|
27316
27847
|
}
|
|
27317
27848
|
|
|
27318
27849
|
// src/adapters/filesystem/instruction-folder.ts
|
|
27319
|
-
import { lstat as
|
|
27320
|
-
import { join as
|
|
27850
|
+
import { lstat as lstat6, open as open3, readdir as readdir4, readFile as readFile5 } from "node:fs/promises";
|
|
27851
|
+
import { join as join7 } from "node:path";
|
|
27321
27852
|
var INSTRUCTION_FILE_LIMIT = 500;
|
|
27322
27853
|
var INSTRUCTION_TEXT_LIMIT = 512 * 1024;
|
|
27323
|
-
var
|
|
27854
|
+
var folderOf3 = (root, instructionPath) => join7(root, ...instructionPath.split("/").slice(0, -1));
|
|
27324
27855
|
async function listInstructionFiles(root, instructionPath) {
|
|
27325
27856
|
const files = [];
|
|
27326
27857
|
let limited = false;
|
|
27327
27858
|
async function visit2(folder, prefix, depth) {
|
|
27328
27859
|
if (depth > 10) return;
|
|
27329
|
-
const entries = await
|
|
27860
|
+
const entries = await readdir4(folder, { withFileTypes: true }).catch(() => []);
|
|
27330
27861
|
for (const entry2 of entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
|
|
27331
27862
|
if (files.length >= INSTRUCTION_FILE_LIMIT) {
|
|
27332
27863
|
limited = true;
|
|
27333
27864
|
return;
|
|
27334
27865
|
}
|
|
27335
27866
|
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
|
|
27867
|
+
if (entry2.isDirectory()) await visit2(join7(folder, entry2.name), path3 + "/", depth + 1);
|
|
27868
|
+
else if (entry2.isFile() && path3 !== INSTRUCTION_FILE) files.push({ path: path3, size: (await lstat6(join7(folder, entry2.name))).size });
|
|
27338
27869
|
}
|
|
27339
27870
|
}
|
|
27340
|
-
await visit2(
|
|
27871
|
+
await visit2(folderOf3(root, instructionPath), "", 0);
|
|
27341
27872
|
return { files, limited };
|
|
27342
27873
|
}
|
|
27874
|
+
var FRONTMATTER_WINDOW = 8 * 1024;
|
|
27875
|
+
async function describeFiles(root, instructionPath, files) {
|
|
27876
|
+
const folder = folderOf3(root, instructionPath);
|
|
27877
|
+
return Promise.all(files.map(async (file2) => {
|
|
27878
|
+
if (!isInstructionReference(file2.path)) return file2;
|
|
27879
|
+
let handle;
|
|
27880
|
+
try {
|
|
27881
|
+
handle = await open3(join7(folder, ...file2.path.split("/")), "r");
|
|
27882
|
+
const buffer = Buffer.alloc(Math.min(FRONTMATTER_WINDOW, file2.size));
|
|
27883
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
27884
|
+
return { ...file2, ...parseInstructionFile(file2.path, buffer.subarray(0, bytesRead).toString("utf8")) };
|
|
27885
|
+
} catch {
|
|
27886
|
+
return file2;
|
|
27887
|
+
} finally {
|
|
27888
|
+
await handle?.close();
|
|
27889
|
+
}
|
|
27890
|
+
}));
|
|
27891
|
+
}
|
|
27892
|
+
async function readInstructionFiles(root, instructionPath) {
|
|
27893
|
+
const { files, limited } = await listInstructionFiles(root, instructionPath);
|
|
27894
|
+
return { files: await describeFiles(root, instructionPath, files), limited };
|
|
27895
|
+
}
|
|
27343
27896
|
async function readInstructionFile(root, instructionPath, path3) {
|
|
27344
27897
|
const parts = path3.split("/");
|
|
27345
27898
|
if (parts.some((p) => !p || p === "." || p === ".." || /[\\:\0]/.test(p))) return void 0;
|
|
27346
|
-
let full =
|
|
27899
|
+
let full = folderOf3(root, instructionPath);
|
|
27347
27900
|
for (const [index, part] of parts.entries()) {
|
|
27348
|
-
full =
|
|
27349
|
-
const info5 = await
|
|
27901
|
+
full = join7(full, part);
|
|
27902
|
+
const info5 = await lstat6(full).catch(() => void 0);
|
|
27350
27903
|
if (!info5 || info5.isSymbolicLink() || (index < parts.length - 1 ? !info5.isDirectory() : !info5.isFile())) return void 0;
|
|
27351
27904
|
}
|
|
27352
|
-
const size = (await
|
|
27905
|
+
const size = (await lstat6(full)).size;
|
|
27353
27906
|
if (size > INSTRUCTION_TEXT_LIMIT) return { text: null, size, binary: false, tooLarge: true };
|
|
27354
|
-
const bytes = await
|
|
27907
|
+
const bytes = await readFile5(full);
|
|
27355
27908
|
try {
|
|
27356
27909
|
if (bytes.includes(0)) throw new TypeError("binary");
|
|
27357
27910
|
return { text: new TextDecoder("utf-8", { fatal: true }).decode(bytes), size, binary: false, tooLarge: false };
|
|
@@ -27361,34 +27914,122 @@ async function readInstructionFile(root, instructionPath, path3) {
|
|
|
27361
27914
|
}
|
|
27362
27915
|
|
|
27363
27916
|
// src/adapters/filesystem/agents-file.ts
|
|
27364
|
-
import { lstat as
|
|
27365
|
-
import { join as
|
|
27917
|
+
import { lstat as lstat7, readFile as readFile6 } from "node:fs/promises";
|
|
27918
|
+
import { join as join8 } from "node:path";
|
|
27366
27919
|
var LIMIT = 1024 * 1024;
|
|
27367
27920
|
async function readAgentsFile(root) {
|
|
27368
|
-
const file2 =
|
|
27369
|
-
const info5 = await
|
|
27921
|
+
const file2 = join8(root, "AGENTS.md");
|
|
27922
|
+
const info5 = await lstat7(file2).catch(() => void 0);
|
|
27370
27923
|
if (!info5?.isFile() || info5.size > LIMIT) return null;
|
|
27371
27924
|
try {
|
|
27372
|
-
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: false }).decode(await
|
|
27925
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: false }).decode(await readFile6(file2));
|
|
27373
27926
|
} catch {
|
|
27374
27927
|
return null;
|
|
27375
27928
|
}
|
|
27376
27929
|
}
|
|
27377
27930
|
|
|
27378
|
-
// src/
|
|
27379
|
-
var
|
|
27380
|
-
|
|
27381
|
-
|
|
27382
|
-
|
|
27931
|
+
// src/queries/document-states.ts
|
|
27932
|
+
var isDocument = (path3) => {
|
|
27933
|
+
try {
|
|
27934
|
+
return classifyDocPath(path3).type === "doc";
|
|
27935
|
+
} catch {
|
|
27936
|
+
return false;
|
|
27937
|
+
}
|
|
27383
27938
|
};
|
|
27384
|
-
async function
|
|
27385
|
-
const
|
|
27386
|
-
const
|
|
27939
|
+
async function documentStates(project, working2) {
|
|
27940
|
+
const head = await project.head();
|
|
27941
|
+
const states = /* @__PURE__ */ new Map();
|
|
27942
|
+
const previous = /* @__PURE__ */ new Map();
|
|
27943
|
+
const result = (documents) => ({ documents, state: (id) => states.get(id) ?? "committed", previousPath: (id) => previous.get(id) });
|
|
27944
|
+
if (!head) {
|
|
27945
|
+
for (const doc of working2) states.set(doc.id, "added");
|
|
27946
|
+
return result(working2);
|
|
27947
|
+
}
|
|
27948
|
+
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);
|
|
27949
|
+
const byPath2 = new Map(working2.map((d) => [d.path, d]));
|
|
27950
|
+
const added = [];
|
|
27951
|
+
const deletedPaths = [];
|
|
27952
|
+
for (const entry2 of status) {
|
|
27953
|
+
const [x, y] = [entry2[0], entry2[1]];
|
|
27954
|
+
const path3 = entry2.slice(3);
|
|
27955
|
+
if (!isDocument(path3)) continue;
|
|
27956
|
+
const doc = byPath2.get(path3);
|
|
27957
|
+
const atHead = x !== "?" && x !== "A";
|
|
27958
|
+
if (!doc) {
|
|
27959
|
+
if (atHead && (x === "D" || y === "D")) deletedPaths.push(path3);
|
|
27960
|
+
continue;
|
|
27961
|
+
}
|
|
27962
|
+
if (!atHead) {
|
|
27963
|
+
added.push(doc);
|
|
27964
|
+
continue;
|
|
27965
|
+
}
|
|
27966
|
+
if (x !== " " || y !== " ") states.set(doc.id, "modified");
|
|
27967
|
+
}
|
|
27968
|
+
const gone = await documentsAtHead(project, head, deletedPaths);
|
|
27969
|
+
const goneById = new Map(gone.map((d) => [d.id, d]));
|
|
27970
|
+
for (const doc of added) {
|
|
27971
|
+
const before = goneById.get(doc.id);
|
|
27972
|
+
if (before) {
|
|
27973
|
+
states.set(doc.id, "modified");
|
|
27974
|
+
previous.set(doc.id, before.path);
|
|
27975
|
+
goneById.delete(doc.id);
|
|
27976
|
+
} else states.set(doc.id, "added");
|
|
27977
|
+
}
|
|
27978
|
+
const ids = new Set(working2.map((d) => d.id));
|
|
27979
|
+
const deleted = [...goneById.values()].filter((d) => !ids.has(d.id));
|
|
27980
|
+
for (const doc of deleted) states.set(doc.id, "deleted");
|
|
27981
|
+
return result([...working2, ...deleted]);
|
|
27982
|
+
}
|
|
27983
|
+
async function documentsAtHead(project, head, paths2) {
|
|
27984
|
+
if (!paths2.length) return [];
|
|
27985
|
+
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] }));
|
|
27986
|
+
const blobs = await project.reader.blobs([...new Set(listing.map((l) => l.blob))]).catch(() => /* @__PURE__ */ new Map());
|
|
27987
|
+
return listing.flatMap(({ blob, path: path3 }) => {
|
|
27988
|
+
const text4 = blobs.get(blob);
|
|
27989
|
+
try {
|
|
27990
|
+
return text4 === void 0 ? [] : [parseDocumentFile(path3, text4)];
|
|
27991
|
+
} catch {
|
|
27992
|
+
return [];
|
|
27993
|
+
}
|
|
27994
|
+
});
|
|
27995
|
+
}
|
|
27996
|
+
|
|
27997
|
+
// src/server/checkout/stamp.ts
|
|
27998
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
27999
|
+
import { lstat as lstat8 } from "node:fs/promises";
|
|
28000
|
+
import { join as join9 } from "node:path";
|
|
28001
|
+
function createStampReader(root, inherited = process.env) {
|
|
28002
|
+
const runner = createGitRunner();
|
|
28003
|
+
const env = { ...inherited, GIT_OPTIONAL_LOCKS: "0", GIT_NO_REPLACE_OBJECTS: "1", GIT_TERMINAL_PROMPT: "0", GIT_LITERAL_PATHSPECS: "0", LC_ALL: "C" };
|
|
28004
|
+
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");
|
|
28005
|
+
return async () => {
|
|
28006
|
+
const [head, status] = await Promise.all([
|
|
28007
|
+
git(["rev-parse", "--verify", "--quiet", "HEAD"], [0, 1]),
|
|
28008
|
+
git(["status", "--porcelain=v1", "-z", "--untracked-files=all", "--no-renames", "--", ":(top).gitifact", ":(top)AGENTS.md"])
|
|
28009
|
+
]);
|
|
28010
|
+
const paths2 = status.split("\0").filter(Boolean).map((entry2) => entry2.slice(3)).filter((path3) => !path3.startsWith(".gitifact/cache/"));
|
|
28011
|
+
const seen = await Promise.all(paths2.map(async (path3) => {
|
|
28012
|
+
const info5 = await lstat8(join9(root, ...path3.split("/"))).catch(() => void 0);
|
|
28013
|
+
return path3 + " " + (info5 ? info5.mtimeMs + ":" + info5.size : "-");
|
|
28014
|
+
}));
|
|
28015
|
+
return createHash2("sha256").update(head.trim() + "\n" + seen.sort().join("\n")).digest("hex").slice(0, 20);
|
|
28016
|
+
};
|
|
28017
|
+
}
|
|
28018
|
+
|
|
28019
|
+
// src/server/checkout/checkout-reader.ts
|
|
28020
|
+
async function settled(reads) {
|
|
28021
|
+
const results = await Promise.allSettled(reads);
|
|
28022
|
+
const failure2 = results.find((r) => r.status === "rejected");
|
|
27387
28023
|
if (failure2) throw failure2.reason;
|
|
27388
28024
|
return results.map((r) => r.value);
|
|
27389
28025
|
}
|
|
27390
|
-
var
|
|
27391
|
-
function checkoutDocuments(
|
|
28026
|
+
var folderOf4 = (path3) => path3.split("/").slice(0, 3).join("/");
|
|
28027
|
+
function checkoutDocuments(states, authors) {
|
|
28028
|
+
const documents = states.documents;
|
|
28029
|
+
const standing2 = (id) => {
|
|
28030
|
+
const previousPath = states.previousPath(id);
|
|
28031
|
+
return { state: states.state(id), ...previousPath ? { previousPath } : {} };
|
|
28032
|
+
};
|
|
27392
28033
|
const byId = new Map(documents.map((d) => [d.id, d]));
|
|
27393
28034
|
const source2 = (s) => {
|
|
27394
28035
|
if (!("id" in s)) return { title: s.title, url: s.url, ...s.note ? { note: s.note } : {} };
|
|
@@ -27396,28 +28037,45 @@ function checkoutDocuments(documents) {
|
|
|
27396
28037
|
return { id: s.id, ...target ? { title: target.title, path: target.path } : {}, ...s.note ? { note: s.note } : {} };
|
|
27397
28038
|
};
|
|
27398
28039
|
const arranged = arrangeDocuments(documents);
|
|
27399
|
-
const features = arranged.features.map(({ index, requirements, designs }) =>
|
|
27400
|
-
|
|
27401
|
-
|
|
27402
|
-
|
|
27403
|
-
|
|
27404
|
-
|
|
27405
|
-
|
|
27406
|
-
|
|
27407
|
-
|
|
27408
|
-
path:
|
|
27409
|
-
|
|
27410
|
-
|
|
27411
|
-
|
|
27412
|
-
|
|
27413
|
-
|
|
27414
|
-
|
|
27415
|
-
|
|
28040
|
+
const features = arranged.features.map(({ index, requirements, designs }) => {
|
|
28041
|
+
const entry2 = authors.get(folderOf4(index.path));
|
|
28042
|
+
return {
|
|
28043
|
+
id: index.id,
|
|
28044
|
+
path: index.path,
|
|
28045
|
+
title: index.title,
|
|
28046
|
+
description: index.description,
|
|
28047
|
+
body: index.body,
|
|
28048
|
+
...standing2(index.id),
|
|
28049
|
+
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) })),
|
|
28050
|
+
designs: designs.map((d) => ({
|
|
28051
|
+
id: d.id,
|
|
28052
|
+
path: d.path,
|
|
28053
|
+
title: d.title,
|
|
28054
|
+
description: d.description,
|
|
28055
|
+
order: d.order,
|
|
28056
|
+
body: d.body,
|
|
28057
|
+
requirements: d.requirements,
|
|
28058
|
+
sources: d.sources.map(source2),
|
|
28059
|
+
...standing2(d.id)
|
|
28060
|
+
})),
|
|
28061
|
+
// Who committed to the feature folder, most first, and when it was last touched; nobody until first committed.
|
|
28062
|
+
contributors: entry2?.people ?? [],
|
|
28063
|
+
updatedAt: entry2?.latest ?? null
|
|
28064
|
+
};
|
|
28065
|
+
});
|
|
28066
|
+
const instructions2 = arranged.instructions.map((k) => ({
|
|
28067
|
+
id: k.id,
|
|
28068
|
+
name: k.name,
|
|
28069
|
+
path: k.path,
|
|
28070
|
+
title: k.title,
|
|
28071
|
+
description: k.description,
|
|
28072
|
+
body: k.body,
|
|
28073
|
+
...standing2(k.id),
|
|
28074
|
+
updatedAt: authors.get(folderOf4(k.path))?.latest ?? null
|
|
27416
28075
|
}));
|
|
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 }));
|
|
27418
28076
|
return { features, instructions: instructions2, orphans: arranged.orphans };
|
|
27419
28077
|
}
|
|
27420
|
-
function createCheckoutReader(root,
|
|
28078
|
+
function createCheckoutReader(root, cache, inherited = process.env) {
|
|
27421
28079
|
const reader = storeReader(root);
|
|
27422
28080
|
const runner = createGitRunner();
|
|
27423
28081
|
const env = { ...inherited, GIT_OPTIONAL_LOCKS: "0", GIT_NO_REPLACE_OBJECTS: "1", GIT_TERMINAL_PROMPT: "0", LC_ALL: "C" };
|
|
@@ -27429,84 +28087,37 @@ function createCheckoutReader(root, sessionId, cache, inherited = process.env) {
|
|
|
27429
28087
|
return head || null;
|
|
27430
28088
|
};
|
|
27431
28089
|
const pending = /* @__PURE__ */ new Map();
|
|
27432
|
-
const
|
|
27433
|
-
async function storeAuthors(head) {
|
|
27434
|
-
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"]);
|
|
27435
|
-
const folders = /* @__PURE__ */ new Map();
|
|
27436
|
-
const instructions2 = /* @__PURE__ */ new Map();
|
|
27437
|
-
for (const chunk of text4.split("")) {
|
|
27438
|
-
if (!chunk) continue;
|
|
27439
|
-
const [name2, email3, date5, ...paths2] = chunk.split("\0");
|
|
27440
|
-
if (!name2 || !email3 || !date5) throw new StoreError(t2("specReader.authorUnreadable"));
|
|
27441
|
-
const seen = /* @__PURE__ */ new Set();
|
|
27442
|
-
for (const raw of paths2) {
|
|
27443
|
-
const path3 = raw.replace(/^\n/, "");
|
|
27444
|
-
if (!path3) continue;
|
|
27445
|
-
const folder = /^(\.gitifact\/spec\/[^/]+)\//.exec(path3)?.[1];
|
|
27446
|
-
if (folder && !seen.has(folder)) {
|
|
27447
|
-
seen.add(folder);
|
|
27448
|
-
let entry2 = folders.get(folder);
|
|
27449
|
-
if (!entry2) folders.set(folder, entry2 = { people: /* @__PURE__ */ new Map(), count: 0, latest: date5 });
|
|
27450
|
-
if (entry2.count < 2e3) {
|
|
27451
|
-
entry2.count++;
|
|
27452
|
-
tally(entry2.people, name2, email3, date5);
|
|
27453
|
-
}
|
|
27454
|
-
}
|
|
27455
|
-
const instruction2 = path3 === "AGENTS.md" ? path3 : /^(\.gitifact\/instructions\/[^/]+)\//.exec(path3)?.[1];
|
|
27456
|
-
if (instruction2 && !instructions2.has(instruction2)) instructions2.set(instruction2, date5);
|
|
27457
|
-
}
|
|
27458
|
-
}
|
|
27459
|
-
return { folders, instructions: instructions2 };
|
|
27460
|
-
}
|
|
28090
|
+
const readStamp = createStampReader(root, inherited);
|
|
27461
28091
|
async function read() {
|
|
27462
28092
|
const raw = await readConfigFile(root);
|
|
27463
28093
|
if (!raw) throw new StoreError(t2("specReader.schemaRequired"));
|
|
27464
28094
|
parseManagedConfig(raw);
|
|
27465
28095
|
const head = await readHead();
|
|
27466
|
-
const
|
|
28096
|
+
const stamp = await readStamp();
|
|
28097
|
+
const [current2, dirty, authors, people] = await settled([
|
|
27467
28098
|
cache.documents.list(),
|
|
27468
28099
|
head ? git(["status", "--porcelain=v1", "--", ".gitifact/spec", INSTRUCTIONS_ROOT]) : Promise.resolve(""),
|
|
27469
|
-
|
|
27470
|
-
|
|
27471
|
-
head ?
|
|
28100
|
+
// Who touched each folder and who committed at all, counted over the commit log in the cache.
|
|
28101
|
+
head ? cache.log.folders(head) : Promise.resolve(/* @__PURE__ */ new Map()),
|
|
28102
|
+
head ? cache.log.contributors(head) : Promise.resolve([])
|
|
27472
28103
|
]);
|
|
27473
|
-
const
|
|
27474
|
-
const
|
|
27475
|
-
|
|
27476
|
-
const [name2, email3, latest] = line2.split("\0");
|
|
27477
|
-
if (!name2 || !email3 || !latest) throw new StoreError(t2("specReader.authorUnreadable"));
|
|
27478
|
-
tally(people, name2, email3, latest);
|
|
27479
|
-
}
|
|
27480
|
-
const arranged = checkoutDocuments(current2.documents);
|
|
27481
|
-
const features = arranged.features.map((feature2) => {
|
|
27482
|
-
const entry2 = authors?.folders.get(folderOf3(feature2.path));
|
|
27483
|
-
return { ...feature2, contributors: entry2 ? [...entry2.people.values()].sort((a, b) => b.commits - a.commits) : [], updatedAt: entry2?.latest ?? null };
|
|
27484
|
-
});
|
|
27485
|
-
const instructions2 = await Promise.all(arranged.instructions.map(async (instruction2) => {
|
|
27486
|
-
const { files, limited } = await listInstructionFiles(root, instruction2.path);
|
|
27487
|
-
return { ...instruction2, files, filesLimited: limited, updatedAt: authors?.instructions.get(instruction2.path.split("/").slice(0, 3).join("/")) ?? null };
|
|
27488
|
-
}));
|
|
27489
|
-
const agentsText = await readAgentsFile(root);
|
|
27490
|
-
const agents2 = agentsText === null ? null : { path: "AGENTS.md", body: agentsText, updatedAt: authors?.instructions.get("AGENTS.md") ?? null };
|
|
27491
|
-
const problems = [...current2.problems, ...arranged.orphans.map((feature2) => docProblem("FEATURE_INDEX_REQUIRED", `.gitifact/spec/${feature2}/index.md`, { feature: feature2 }))];
|
|
28104
|
+
const states = await documentStates({ head: async () => head, reader }, current2.documents);
|
|
28105
|
+
const { features, instructions: instructions2, orphans } = checkoutDocuments(states, authors);
|
|
28106
|
+
const problems = [...current2.problems, ...orphans.map((feature2) => docProblem("FEATURE_INDEX_REQUIRED", `.gitifact/spec/${feature2}/index.md`, { feature: feature2 }))];
|
|
27492
28107
|
if (await readHead() !== head) throw new StoreError(t2("specReader.projectChanged"));
|
|
27493
|
-
|
|
27494
|
-
contract: "browser-specs",
|
|
27495
|
-
version: 6,
|
|
27496
|
-
sessionId,
|
|
28108
|
+
return {
|
|
27497
28109
|
head,
|
|
28110
|
+
stamp,
|
|
27498
28111
|
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27499
28112
|
working: head ? !!dirty.trim() : features.length > 0 || instructions2.length > 0,
|
|
27500
28113
|
features,
|
|
27501
28114
|
instructions: instructions2,
|
|
27502
|
-
agents: agents2,
|
|
27503
28115
|
problems,
|
|
27504
|
-
|
|
27505
|
-
|
|
27506
|
-
}
|
|
27507
|
-
return { checkout, head };
|
|
28116
|
+
people,
|
|
28117
|
+
folders: authors
|
|
28118
|
+
};
|
|
27508
28119
|
}
|
|
27509
|
-
|
|
28120
|
+
const base = () => {
|
|
27510
28121
|
const language = getLanguage();
|
|
27511
28122
|
let value = pending.get(language);
|
|
27512
28123
|
if (!value) {
|
|
@@ -27515,10 +28126,37 @@ function createCheckoutReader(root, sessionId, cache, inherited = process.env) {
|
|
|
27515
28126
|
}
|
|
27516
28127
|
return value;
|
|
27517
28128
|
};
|
|
28129
|
+
return {
|
|
28130
|
+
base,
|
|
28131
|
+
/** Every instruction by name with the other files of its folder, and AGENTS.md: few enough to read whole. */
|
|
28132
|
+
async instructions() {
|
|
28133
|
+
const checkout = await base();
|
|
28134
|
+
const instructions2 = await Promise.all([...checkout.instructions].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0).map(async (instruction2) => {
|
|
28135
|
+
const { files, limited } = instruction2.state === "deleted" ? { files: [], limited: false } : await readInstructionFiles(root, instruction2.path);
|
|
28136
|
+
return { ...instruction2, files, filesLimited: limited };
|
|
28137
|
+
}));
|
|
28138
|
+
const agentsText = await readAgentsFile(root);
|
|
28139
|
+
return { instructions: instructions2, agents: agentsText === null ? null : { path: "AGENTS.md", body: agentsText, updatedAt: checkout.folders.get("AGENTS.md")?.latest ?? null } };
|
|
28140
|
+
}
|
|
28141
|
+
};
|
|
28142
|
+
}
|
|
28143
|
+
|
|
28144
|
+
// src/queries/paging.ts
|
|
28145
|
+
var PAGE_SIZE = 20;
|
|
28146
|
+
function pageOf(rows, keyOf, request) {
|
|
28147
|
+
let start = 0;
|
|
28148
|
+
if (request.after !== void 0) {
|
|
28149
|
+
const at = rows.findIndex((row) => keyOf(row) === request.after);
|
|
28150
|
+
if (at < 0) return void 0;
|
|
28151
|
+
start = at + 1;
|
|
28152
|
+
}
|
|
28153
|
+
const size = request.all ? rows.length : request.limit ?? PAGE_SIZE;
|
|
28154
|
+
const shown = rows.slice(start, start + size);
|
|
28155
|
+
const more = start + shown.length < rows.length && shown.length > 0;
|
|
28156
|
+
return { rows: shown, total: rows.length, next: more ? keyOf(shown[shown.length - 1]) : null };
|
|
27518
28157
|
}
|
|
27519
28158
|
|
|
27520
28159
|
// src/server/commit/commit-files.ts
|
|
27521
|
-
var LIST_LIMIT = 500;
|
|
27522
28160
|
var TEXT_LIMIT = 512 * 1024;
|
|
27523
28161
|
var KEPT = 200;
|
|
27524
28162
|
var statusOf = (code) => code.startsWith("R") ? "renamed" : code === "A" ? "added" : code === "D" ? "deleted" : "modified";
|
|
@@ -27574,10 +28212,15 @@ function createCommitFiles(git) {
|
|
|
27574
28212
|
return git.run(["cat-file", "blob", `${rev}:${path3}`]);
|
|
27575
28213
|
}
|
|
27576
28214
|
return {
|
|
27577
|
-
/**
|
|
27578
|
-
|
|
28215
|
+
/**
|
|
28216
|
+
* A page of the source files after the file `after` (a path) and how many there are in all: undefined for a commit
|
|
28217
|
+
* the repository lacks, null for a cursor the commit does not have.
|
|
28218
|
+
*/
|
|
28219
|
+
async files(commit, request) {
|
|
27579
28220
|
const all = await list(commit);
|
|
27580
|
-
|
|
28221
|
+
if (!all) return void 0;
|
|
28222
|
+
const page = pageOf(all.files, (f) => f.path, request);
|
|
28223
|
+
return page ? { ...all, files: page.rows, next: page.next } : null;
|
|
27581
28224
|
},
|
|
27582
28225
|
/** One listed file on both sides; undefined when the commit did not change that path. */
|
|
27583
28226
|
async file(commit, path3) {
|
|
@@ -27606,42 +28249,297 @@ function isUtf8(bytes) {
|
|
|
27606
28249
|
}
|
|
27607
28250
|
}
|
|
27608
28251
|
|
|
28252
|
+
// src/queries/working-changes.ts
|
|
28253
|
+
async function workingChanges(source2, known) {
|
|
28254
|
+
const head = known !== void 0 ? known : await source2.head();
|
|
28255
|
+
const headFiles = head ? await source2.cache.history.filesAt(head) : /* @__PURE__ */ new Map();
|
|
28256
|
+
const [working2, records2] = await Promise.all([source2.cache.documents.files(), source2.pendingRecords()]);
|
|
28257
|
+
return { head, headFiles, working: working2, records: records2, ...compareDocumentSets(headFiles, working2.files) };
|
|
28258
|
+
}
|
|
28259
|
+
var uncovered = (changes2, records2) => {
|
|
28260
|
+
const covered = new Set(records2.flatMap((r) => r.docs));
|
|
28261
|
+
return changes2.filter((c) => c.types.some((type) => type !== "created") && !covered.has(c.id)).map((c) => c.id);
|
|
28262
|
+
};
|
|
28263
|
+
async function workingOverview(source2) {
|
|
28264
|
+
const found = await workingChanges(source2);
|
|
28265
|
+
const records2 = checkDocuments(new Map([...found.working.files, ...found.records.files])).records;
|
|
28266
|
+
return {
|
|
28267
|
+
head: found.head,
|
|
28268
|
+
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 })),
|
|
28269
|
+
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 })),
|
|
28270
|
+
withoutRecord: uncovered(found.changes, records2)
|
|
28271
|
+
};
|
|
28272
|
+
}
|
|
28273
|
+
function snapshot4(doc, docs) {
|
|
28274
|
+
const folder = doc.path.split("/").slice(0, 3).join("/");
|
|
28275
|
+
const specId = doc.kind === "wiki" || doc.kind === "instruction" ? doc.kind : docs.find((d) => d.kind === "feature" && d.path === folder + "/index.md")?.id ?? "";
|
|
28276
|
+
const base = { id: doc.id, kind: doc.kind, title: doc.title, description: doc.description, body: doc.body, specId, path: doc.path };
|
|
28277
|
+
if (doc.kind === "requirement") return { ...base, order: doc.order };
|
|
28278
|
+
if (doc.kind === "design") return { ...base, order: doc.order, requirements: doc.requirements, sources: doc.sources };
|
|
28279
|
+
return base;
|
|
28280
|
+
}
|
|
28281
|
+
var parsed = (files) => [...files].flatMap(([path3, text4]) => {
|
|
28282
|
+
try {
|
|
28283
|
+
return classifyDocPath(path3).type === "doc" ? [parseDocumentFile(path3, text4)] : [];
|
|
28284
|
+
} catch {
|
|
28285
|
+
return [];
|
|
28286
|
+
}
|
|
28287
|
+
});
|
|
28288
|
+
async function workingChange2(source2, id) {
|
|
28289
|
+
const found = await workingChanges(source2);
|
|
28290
|
+
const change2 = found.changes.find((c) => c.id === id);
|
|
28291
|
+
if (!change2) return void 0;
|
|
28292
|
+
const before = parsed(found.headFiles);
|
|
28293
|
+
const after = parsed(found.working.files);
|
|
28294
|
+
const side = (docs) => {
|
|
28295
|
+
const doc = docs.find((d) => d.id === id);
|
|
28296
|
+
return doc ? snapshot4(doc, docs) : null;
|
|
28297
|
+
};
|
|
28298
|
+
return {
|
|
28299
|
+
change: { id: change2.id, kind: change2.kind, title: change2.title, path: change2.path, ...change2.previousPath ? { previousPath: change2.previousPath } : {}, types: change2.types },
|
|
28300
|
+
before: side(before),
|
|
28301
|
+
after: side(after)
|
|
28302
|
+
};
|
|
28303
|
+
}
|
|
28304
|
+
|
|
28305
|
+
// src/queries/search.ts
|
|
28306
|
+
var SEARCH_GROUPS = ["feature", "requirement", "design", "instruction", "record", "commit"];
|
|
28307
|
+
var FIRST = 5;
|
|
28308
|
+
var RECENT = 6;
|
|
28309
|
+
var place2 = (path3) => path3.replace(/^\.gitifact\//, "");
|
|
28310
|
+
var folderOf5 = (path3) => path3.split("/").slice(0, 3).join("/");
|
|
28311
|
+
async function searchRecords(source2, request) {
|
|
28312
|
+
const head = request.head ?? await source2.head();
|
|
28313
|
+
const query = request.q.trim().toLowerCase();
|
|
28314
|
+
if (!query) return [await recent(source2, head)];
|
|
28315
|
+
const size = request.group ? request.limit ?? 20 : FIRST;
|
|
28316
|
+
const one = async (group) => {
|
|
28317
|
+
const found = group === "record" ? head ? await source2.cache.history.searchRecords(head, query, request.after, size) : empty() : group === "commit" ? head ? await source2.cache.log.commitsNamed(head, query, request.after, size) : empty() : await source2.cache.documents.searchKind(group, query, request.after, size);
|
|
28318
|
+
return found && { group, ...found };
|
|
28319
|
+
};
|
|
28320
|
+
const groups = await Promise.all((request.group ? [request.group] : SEARCH_GROUPS).map(one));
|
|
28321
|
+
if (groups.some((g) => !g)) return void 0;
|
|
28322
|
+
return groups.filter((g) => request.group || g.total > 0);
|
|
28323
|
+
}
|
|
28324
|
+
var empty = () => ({ total: 0, next: null, hits: [] });
|
|
28325
|
+
async function recent(source2, head) {
|
|
28326
|
+
const [{ documents }, folders] = await Promise.all([source2.cache.documents.list(), head ? source2.cache.log.folders(head) : /* @__PURE__ */ new Map()]);
|
|
28327
|
+
const hits = documents.filter((d) => d.kind === "feature" || d.kind === "instruction").map((d) => ({ doc: d, latest: folders.get(folderOf5(d.path))?.latest })).filter((d) => !!d.latest).sort((a, b) => Date.parse(b.latest) - Date.parse(a.latest) || a.doc.id.localeCompare(b.doc.id)).slice(0, RECENT).map(({ doc }) => ({
|
|
28328
|
+
id: doc.id,
|
|
28329
|
+
kind: doc.kind,
|
|
28330
|
+
title: doc.title,
|
|
28331
|
+
where: place2(doc.path),
|
|
28332
|
+
line: doc.description,
|
|
28333
|
+
...doc.kind === "feature" ? { featureId: doc.id } : {}
|
|
28334
|
+
}));
|
|
28335
|
+
return { group: "recent", total: hits.length, next: null, hits };
|
|
28336
|
+
}
|
|
28337
|
+
|
|
28338
|
+
// src/queries/checkout.ts
|
|
28339
|
+
var SHOWN_REQUIREMENTS = 12;
|
|
28340
|
+
function checkoutIndex(base) {
|
|
28341
|
+
return {
|
|
28342
|
+
features: base.features.map((f) => ({
|
|
28343
|
+
id: f.id,
|
|
28344
|
+
path: f.path,
|
|
28345
|
+
title: f.title,
|
|
28346
|
+
state: f.state,
|
|
28347
|
+
requirements: f.requirements.map((r) => ({ id: r.id, path: r.path, title: r.title, description: r.description })),
|
|
28348
|
+
designs: f.designs.map((d) => ({ id: d.id, path: d.path, title: d.title }))
|
|
28349
|
+
})),
|
|
28350
|
+
instructions: base.instructions.map((k) => ({ id: k.id, name: k.name, path: k.path, title: k.title })),
|
|
28351
|
+
people: base.people.map((p) => ({ name: p.name, email: p.email }))
|
|
28352
|
+
};
|
|
28353
|
+
}
|
|
28354
|
+
var acceptanceHeading = /^###\s+(?:수용 조건|acceptance criteria)\s*$/i;
|
|
28355
|
+
function acceptanceCount(body2) {
|
|
28356
|
+
const lines = body2.replace(/\r\n/g, "\n").split("\n");
|
|
28357
|
+
const start = lines.findIndex((line2) => acceptanceHeading.test(line2.trim()));
|
|
28358
|
+
if (start < 0) return null;
|
|
28359
|
+
let count2 = 0;
|
|
28360
|
+
for (const line2 of lines.slice(start + 1)) {
|
|
28361
|
+
if (/^#{1,6}\s/.test(line2)) break;
|
|
28362
|
+
if (/^\d+\.\s/.test(line2)) count2++;
|
|
28363
|
+
}
|
|
28364
|
+
return count2 || null;
|
|
28365
|
+
}
|
|
28366
|
+
function listFeatures(base, filter, request) {
|
|
28367
|
+
const query = filter.q?.trim().toLowerCase();
|
|
28368
|
+
const named = (f) => !query || (f.title + " " + f.id).toLowerCase().includes(query);
|
|
28369
|
+
const key = filter.sort ?? "updated";
|
|
28370
|
+
const opens = { title: "asc", requirements: "desc", updated: "desc" };
|
|
28371
|
+
const way = (filter.dir ?? opens[key]) === "asc" ? 1 : -1;
|
|
28372
|
+
const groups = base.features.filter((f) => !filter.design || filter.design === "yes" === f.designs.length > 0).filter((f) => !filter.author || f.contributors.some((p) => p.email === filter.author)).map((f) => ({ feature: f, requirements: named(f) ? f.requirements : f.requirements.filter((r) => (r.title + " " + r.id).toLowerCase().includes(query)) })).filter((g) => named(g.feature) || g.requirements.length).sort((a, b) => way * (key === "requirements" ? a.feature.requirements.length - b.feature.requirements.length : key === "title" ? a.feature.title.localeCompare(b.feature.title) : (a.feature.updatedAt ?? "").localeCompare(b.feature.updatedAt ?? "")) || a.feature.title.localeCompare(b.feature.title));
|
|
28373
|
+
const page = pageOf(groups, (g) => g.feature.id, request);
|
|
28374
|
+
if (!page) return void 0;
|
|
28375
|
+
const features = page.rows.map(({ feature: f, requirements }) => {
|
|
28376
|
+
const designed = new Set(f.designs.flatMap((d) => d.requirements));
|
|
28377
|
+
return {
|
|
28378
|
+
id: f.id,
|
|
28379
|
+
path: f.path,
|
|
28380
|
+
title: f.title,
|
|
28381
|
+
description: f.description,
|
|
28382
|
+
state: f.state,
|
|
28383
|
+
designs: f.designs.length,
|
|
28384
|
+
contributors: f.contributors,
|
|
28385
|
+
updatedAt: f.updatedAt,
|
|
28386
|
+
requirementCount: f.requirements.length,
|
|
28387
|
+
requirements: requirements.slice(0, SHOWN_REQUIREMENTS).map((r) => ({
|
|
28388
|
+
id: r.id,
|
|
28389
|
+
title: r.title,
|
|
28390
|
+
description: r.description,
|
|
28391
|
+
state: r.state,
|
|
28392
|
+
acceptance: acceptanceCount(r.body),
|
|
28393
|
+
designed: designed.has(r.id)
|
|
28394
|
+
})),
|
|
28395
|
+
hidden: Math.max(0, requirements.length - SHOWN_REQUIREMENTS)
|
|
28396
|
+
};
|
|
28397
|
+
});
|
|
28398
|
+
return {
|
|
28399
|
+
total: page.total,
|
|
28400
|
+
all: base.features.length,
|
|
28401
|
+
requirements: groups.reduce((sum, g) => sum + g.requirements.length, 0),
|
|
28402
|
+
mostRequirements: Math.max(0, ...base.features.map((f) => f.requirements.length)),
|
|
28403
|
+
next: page.next,
|
|
28404
|
+
features
|
|
28405
|
+
};
|
|
28406
|
+
}
|
|
28407
|
+
var featureOf = (base, id) => base.features.find((f) => f.id === id);
|
|
28408
|
+
function listContributors(base, q, request) {
|
|
28409
|
+
const query = q?.trim().toLowerCase();
|
|
28410
|
+
const people = base.people.filter((p) => !query || (p.name + " " + p.email).toLowerCase().includes(query));
|
|
28411
|
+
const page = pageOf(people, (p) => p.email, request);
|
|
28412
|
+
if (!page) return void 0;
|
|
28413
|
+
const touched = (email3) => base.features.filter((f) => f.contributors.some((c) => c.email === email3)).length;
|
|
28414
|
+
return { total: page.total, next: page.next, people: page.rows.map((p) => ({ ...p, features: touched(p.email) })) };
|
|
28415
|
+
}
|
|
28416
|
+
function contributorOf(base, email3) {
|
|
28417
|
+
const person = base.people.find((p) => p.email === email3);
|
|
28418
|
+
if (!person) return void 0;
|
|
28419
|
+
const features = base.features.flatMap((f) => {
|
|
28420
|
+
const share = f.contributors.find((c) => c.email === email3);
|
|
28421
|
+
return share ? [{ id: f.id, title: f.title, commits: share.commits, requirements: f.requirements.length }] : [];
|
|
28422
|
+
});
|
|
28423
|
+
return { person, features };
|
|
28424
|
+
}
|
|
28425
|
+
function topContributors(people) {
|
|
28426
|
+
const rest = people.slice(3);
|
|
28427
|
+
return { total: people.length, top: people.slice(0, 3), rest: { count: rest.length, commits: rest.reduce((sum, p) => sum + p.commits, 0) } };
|
|
28428
|
+
}
|
|
28429
|
+
|
|
27609
28430
|
// src/server/routes/record-routes.ts
|
|
27610
|
-
var PAGE =
|
|
28431
|
+
var PAGE = 20;
|
|
27611
28432
|
function recordRoutes(root, sessionId, env) {
|
|
27612
28433
|
const git = storeReader(root);
|
|
27613
28434
|
const cache = openCache(root, { run: (args, input2) => git.run(args, input2), decode: git.decode, legacyBundles: (oids) => git.readBundles(oids) });
|
|
27614
|
-
const
|
|
28435
|
+
const checkout = createCheckoutReader(root, cache, env);
|
|
28436
|
+
const readStamp = createStampReader(root, env);
|
|
27615
28437
|
const commitFiles = createCommitFiles(git);
|
|
28438
|
+
const source2 = { head: async () => (await git.baseline()).head, reader: git, cache };
|
|
28439
|
+
const working2 = { head: async () => (await git.baseline()).head, cache, pendingRecords: () => readPendingRecords(root, (args) => git.run(args)) };
|
|
27616
28440
|
const unreadable2 = () => t2("server.specsUnreadable");
|
|
28441
|
+
const cursorGone2 = () => new HttpError(404, "NOT_FOUND", t2("server.cursorNotFound"));
|
|
27617
28442
|
return [
|
|
27618
|
-
|
|
27619
|
-
|
|
27620
|
-
|
|
27621
|
-
const
|
|
27622
|
-
return ok(
|
|
28443
|
+
// The checkout a part at a time, each shaped by the query layer (`queries/checkout.ts`): the frame every screen reads
|
|
28444
|
+
// first, the feature list and one feature, the instructions, and the contributors.
|
|
28445
|
+
route({ method: "GET", path: "/api/v1/checkout", session: true, unreadable: unreadable2, handle: async () => {
|
|
28446
|
+
const base = await checkout.base();
|
|
28447
|
+
return ok(browserCheckoutV1.parse({
|
|
28448
|
+
contract: "browser-checkout",
|
|
28449
|
+
version: 1,
|
|
28450
|
+
sessionId,
|
|
28451
|
+
head: base.head,
|
|
28452
|
+
observedAt: base.observedAt,
|
|
28453
|
+
stamp: base.stamp,
|
|
28454
|
+
working: base.working,
|
|
28455
|
+
problems: base.problems,
|
|
28456
|
+
index: checkoutIndex(base)
|
|
28457
|
+
}));
|
|
27623
28458
|
} }),
|
|
27624
|
-
route({ method: "GET", path: "/api/v1/
|
|
27625
|
-
|
|
27626
|
-
|
|
27627
|
-
|
|
27628
|
-
|
|
28459
|
+
route({ method: "GET", path: "/api/v1/features", session: true, query: browserFeaturesQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28460
|
+
const page = listFeatures(await checkout.base(), query, { after: query.after, limit: query.limit ?? PAGE });
|
|
28461
|
+
if (!page) throw cursorGone2();
|
|
28462
|
+
return ok(browserFeaturesV1.parse({ contract: "browser-features", version: 1, sessionId, ...page }));
|
|
28463
|
+
} }),
|
|
28464
|
+
route({ method: "GET", path: "/api/v1/feature", session: true, query: browserFeatureQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28465
|
+
const feature2 = featureOf(await checkout.base(), query.id);
|
|
28466
|
+
if (!feature2) throw new HttpError(404, "NOT_FOUND", t2("server.featureNotFound"));
|
|
28467
|
+
return ok(browserFeatureV1.parse({ contract: "browser-feature", version: 1, sessionId, feature: feature2 }));
|
|
28468
|
+
} }),
|
|
28469
|
+
route({ method: "GET", path: "/api/v1/instructions", session: true, unreadable: unreadable2, handle: async () => ok(browserInstructionsV1.parse({ contract: "browser-instructions", version: 1, sessionId, ...await checkout.instructions() })) }),
|
|
28470
|
+
route({ method: "GET", path: "/api/v1/contributors", session: true, query: browserContributorsQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28471
|
+
const page = listContributors(await checkout.base(), query.q, { after: query.after, limit: query.limit ?? PAGE });
|
|
28472
|
+
if (!page) throw cursorGone2();
|
|
28473
|
+
return ok(browserContributorsV1.parse({ contract: "browser-contributors", version: 1, sessionId, ...page }));
|
|
28474
|
+
} }),
|
|
28475
|
+
route({ method: "GET", path: "/api/v1/contributor", session: true, query: browserContributorQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28476
|
+
const found = contributorOf(await checkout.base(), query.email);
|
|
28477
|
+
if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.contributorNotFound"));
|
|
28478
|
+
return ok(browserContributorV1.parse({ contract: "browser-contributor", version: 1, sessionId, ...found }));
|
|
28479
|
+
} }),
|
|
28480
|
+
// Whether the screen is behind, asked when the reader comes back to the tab; cheap enough to ask every time.
|
|
28481
|
+
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() })) }),
|
|
28482
|
+
route({ method: "GET", path: "/api/v1/history", session: true, query: browserHistoryQueryV4, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28483
|
+
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);
|
|
28484
|
+
if (!page) throw cursorGone2();
|
|
28485
|
+
return ok(browserHistoryV6.parse({ contract: "browser-history", version: 6, sessionId, head: query.head, ...page }));
|
|
28486
|
+
} }),
|
|
28487
|
+
route({ method: "GET", path: "/api/v1/history/summary", session: true, query: browserHistorySummaryQueryV1, unreadable: unreadable2, handle: async ({ query }) => ok(browserHistorySummaryV5.parse({
|
|
28488
|
+
contract: "browser-history-summary",
|
|
28489
|
+
version: 5,
|
|
28490
|
+
sessionId,
|
|
28491
|
+
head: query.head,
|
|
28492
|
+
...await cache.history.summary(query.head),
|
|
28493
|
+
people: topContributors(await cache.log.contributors(query.head))
|
|
28494
|
+
})) }),
|
|
28495
|
+
// One commit as its page reads it: who made it and a page of the documents it changed, even when it changed none.
|
|
28496
|
+
route({ method: "GET", path: "/api/v1/commit", session: true, query: browserCommitQueryV3, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28497
|
+
const page = await cache.history.commitChanges(query.commit, query.after, query.limit ?? PAGE, query.record);
|
|
28498
|
+
if (!page) throw cursorGone2();
|
|
28499
|
+
const first = page.events[0];
|
|
27629
28500
|
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
28501
|
if (!first && line2.length < 5) throw new HttpError(404, "NOT_FOUND", t2("server.commitNotFound"));
|
|
27631
28502
|
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(
|
|
28503
|
+
return ok(browserCommitV4.parse({
|
|
28504
|
+
contract: "browser-commit",
|
|
28505
|
+
version: 4,
|
|
28506
|
+
sessionId,
|
|
28507
|
+
commit: query.commit,
|
|
28508
|
+
author,
|
|
28509
|
+
email: email3,
|
|
28510
|
+
committer,
|
|
28511
|
+
date: date5,
|
|
28512
|
+
message,
|
|
28513
|
+
total: page.total,
|
|
28514
|
+
next: page.next,
|
|
28515
|
+
changes: page.events
|
|
28516
|
+
}));
|
|
28517
|
+
} }),
|
|
28518
|
+
// The text on both sides of one change, read from Git when the reader opens that document.
|
|
28519
|
+
route({ method: "GET", path: "/api/v1/commit/change", session: true, query: browserCommitChangeQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28520
|
+
const change2 = await cache.history.commitChange(query.commit, query.id);
|
|
28521
|
+
if (!change2) throw new HttpError(404, "NOT_FOUND", t2("server.commitNotFound"));
|
|
28522
|
+
return ok(browserCommitChangeV1.parse({ contract: "browser-commit-change", version: 1, sessionId, commit: query.commit, event: change2.event, before: change2.before, after: change2.after }));
|
|
27633
28523
|
} }),
|
|
27634
28524
|
// A record's page names the record; which commit added it is read from the history of the HEAD the reader is on.
|
|
27635
28525
|
route({ method: "GET", path: "/api/v1/record", session: true, query: browserRecordQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
27636
|
-
const
|
|
27637
|
-
if (!
|
|
27638
|
-
return ok(
|
|
28526
|
+
const found = await cache.history.recordOf(query.head, query.id);
|
|
28527
|
+
if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.recordNotFound"));
|
|
28528
|
+
return ok(browserRecordV2.parse({ contract: "browser-record", version: 2, sessionId, head: query.head, id: query.id, ...found }));
|
|
28529
|
+
} }),
|
|
28530
|
+
// What is not committed yet, worked out again on every request: the uncommitted entry of the records list.
|
|
28531
|
+
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) })) }),
|
|
28532
|
+
route({ method: "GET", path: "/api/v1/working/change", session: true, query: browserWorkingChangeQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28533
|
+
const change2 = await workingChange2(working2, query.id);
|
|
28534
|
+
if (!change2) throw new HttpError(404, "NOT_FOUND", t2("server.workingChangeNotFound"));
|
|
28535
|
+
return ok(browserWorkingChangeV1.parse({ contract: "browser-working-change", version: 1, sessionId, ...change2 }));
|
|
27639
28536
|
} }),
|
|
27640
28537
|
// The source a commit changed beside its documents, read from Git when the commit or record page asks for it.
|
|
27641
|
-
route({ method: "GET", path: "/api/v1/commit/files", session: true, query:
|
|
27642
|
-
const found = await commitFiles.files(query.commit);
|
|
27643
|
-
if (
|
|
27644
|
-
|
|
28538
|
+
route({ method: "GET", path: "/api/v1/commit/files", session: true, query: browserCommitFilesQueryV2, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28539
|
+
const found = await commitFiles.files(query.commit, { after: query.after, limit: query.limit ?? PAGE });
|
|
28540
|
+
if (found === void 0) throw new HttpError(404, "NOT_FOUND", t2("server.commitNotFound"));
|
|
28541
|
+
if (found === null) throw cursorGone2();
|
|
28542
|
+
return ok(browserCommitFilesV2.parse({ contract: "browser-commit-files", version: 2, sessionId, commit: found.commit, total: found.total, next: found.next, files: found.files }));
|
|
27645
28543
|
} }),
|
|
27646
28544
|
route({ method: "GET", path: "/api/v1/commit/file", session: true, query: browserCommitFileQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
27647
28545
|
const found = await commitFiles.file(query.commit, query.path);
|
|
@@ -27655,23 +28553,28 @@ function recordRoutes(root, sessionId, env) {
|
|
|
27655
28553
|
if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.instructionFileNotFound"));
|
|
27656
28554
|
return ok(browserInstructionFileV1.parse({ contract: "browser-instruction-file", version: 1, sessionId, id: query.id, path: query.path, ...found }));
|
|
27657
28555
|
} }),
|
|
27658
|
-
|
|
28556
|
+
// The search box's groups as the query layer works them out (`queries/search.ts`); the browser only draws them.
|
|
28557
|
+
route({ method: "GET", path: "/api/v1/search", session: true, query: browserSearchQueryV2, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28558
|
+
const groups = await searchRecords(source2, query);
|
|
28559
|
+
if (!groups) throw cursorGone2();
|
|
28560
|
+
return ok(browserSearchV3.parse({ contract: "browser-search", version: 3, sessionId, query: query.q, groups }));
|
|
28561
|
+
} })
|
|
27659
28562
|
];
|
|
27660
28563
|
}
|
|
27661
28564
|
|
|
27662
28565
|
// src/server/routes/asset-routes.ts
|
|
27663
|
-
import { lstat as
|
|
27664
|
-
import { join as
|
|
28566
|
+
import { lstat as lstat9, readFile as readFile7 } from "node:fs/promises";
|
|
28567
|
+
import { join as join10 } from "node:path";
|
|
27665
28568
|
function assetRoutes(root) {
|
|
27666
28569
|
return [
|
|
27667
28570
|
route({ method: "GET", path: "/api/v1/assets/*", session: false, handle: async ({ path: path3, response }) => {
|
|
27668
28571
|
const relative = path3.slice("/api/v1/assets/".length);
|
|
27669
28572
|
const assetPath = ASSETS_DIR + "/" + relative;
|
|
27670
28573
|
if (!isAssetPath(assetPath)) throw new HttpError(404, "NOT_FOUND", t2("server.assetNotFound"));
|
|
27671
|
-
const file2 =
|
|
27672
|
-
const stat3 = await
|
|
28574
|
+
const file2 = join10(root, ...assetPath.split("/"));
|
|
28575
|
+
const stat3 = await lstat9(file2).catch(() => void 0);
|
|
27673
28576
|
if (!stat3?.isFile() || stat3.isSymbolicLink() || stat3.size > 20 * 1024 * 1024) throw new HttpError(404, "NOT_FOUND", t2("server.assetNotFound"));
|
|
27674
|
-
const bytes = await
|
|
28577
|
+
const bytes = await readFile7(file2);
|
|
27675
28578
|
const extension = assetExtension(relative);
|
|
27676
28579
|
const inline = ["png", "jpg", "jpeg", "gif", "webp", "svg"].includes(extension);
|
|
27677
28580
|
response.writeHead(200, {
|
|
@@ -27688,7 +28591,7 @@ function assetRoutes(root) {
|
|
|
27688
28591
|
}
|
|
27689
28592
|
|
|
27690
28593
|
// src/server/browser-server.ts
|
|
27691
|
-
var readBundledChangelog = (language) =>
|
|
28594
|
+
var readBundledChangelog = (language) => readFile8(new URL("./i18n/" + language + "/changelog.md", import.meta.url), "utf8").catch((error62) => {
|
|
27692
28595
|
if (error62.code === "ENOENT") return null;
|
|
27693
28596
|
throw error62;
|
|
27694
28597
|
});
|
|
@@ -27818,14 +28721,14 @@ async function runBrowser(options, version2) {
|
|
|
27818
28721
|
}
|
|
27819
28722
|
|
|
27820
28723
|
// src/commands/spec-init.ts
|
|
27821
|
-
import { readdir as
|
|
27822
|
-
import { join as
|
|
28724
|
+
import { readdir as readdir5 } from "node:fs/promises";
|
|
28725
|
+
import { join as join14 } from "node:path";
|
|
27823
28726
|
|
|
27824
28727
|
// src/adapters/git/init-repository.ts
|
|
27825
|
-
import { createHash as
|
|
27826
|
-
import { lstat as
|
|
27827
|
-
import { join as
|
|
27828
|
-
var markerPresent = (path3) =>
|
|
28728
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
28729
|
+
import { lstat as lstat10, readFile as readFile9 } from "node:fs/promises";
|
|
28730
|
+
import { join as join11 } from "node:path";
|
|
28731
|
+
var markerPresent = (path3) => lstat10(path3).then(() => true, (error62) => {
|
|
27829
28732
|
if (error62.code === "ENOENT") return false;
|
|
27830
28733
|
throw error62;
|
|
27831
28734
|
});
|
|
@@ -27842,27 +28745,27 @@ function initRepository(cwd, inherited = process.env) {
|
|
|
27842
28745
|
});
|
|
27843
28746
|
return {
|
|
27844
28747
|
async inspect(observedState, includeTrackedConfig = true) {
|
|
27845
|
-
const
|
|
27846
|
-
const root =
|
|
28748
|
+
const state2 = observedState ?? await createRepositoryReader(cwd, { env }).read();
|
|
28749
|
+
const root = state2.repository.rootPath;
|
|
27847
28750
|
const locations = decodeGitLine(await git(["rev-parse", "--path-format=absolute", "--absolute-git-dir", "--git-path", "index"], root)).split("\n");
|
|
27848
28751
|
const gitDir = locations.length === 2 ? locations[0] : decodeGitLine(await git(["rev-parse", "--absolute-git-dir"], root));
|
|
27849
28752
|
const indexPath = locations.length === 2 ? locations[1] : decodeGitLine(await git(["rev-parse", "--path-format=absolute", "--git-path", "index"], root));
|
|
27850
28753
|
for (const marker2 of ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"]) {
|
|
27851
|
-
if (await markerPresent(
|
|
28754
|
+
if (await markerPresent(join11(gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
|
|
27852
28755
|
}
|
|
27853
|
-
if (
|
|
27854
|
-
const index = await
|
|
28756
|
+
if (state2.changes.some((change2) => change2.kind === "unmerged")) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
28757
|
+
const index = await readFile9(indexPath).catch((error62) => {
|
|
27855
28758
|
if (error62.code === "ENOENT") return Buffer.alloc(0);
|
|
27856
28759
|
throw error62;
|
|
27857
28760
|
});
|
|
27858
28761
|
const [staged, committed2] = includeTrackedConfig ? await Promise.all([
|
|
27859
28762
|
git(["ls-files", "--stage", "-z", "--", ".gitifact/config.json"], root),
|
|
27860
|
-
|
|
28763
|
+
state2.head.commit ? git(["ls-tree", "-z", state2.head.commit, "--", ".gitifact/config.json"], root) : Buffer.alloc(0)
|
|
27861
28764
|
]) : [Buffer.alloc(0), Buffer.alloc(0)];
|
|
27862
28765
|
return {
|
|
27863
|
-
state,
|
|
28766
|
+
state: state2,
|
|
27864
28767
|
trackedConfig: staged.length > 0 || committed2.length > 0,
|
|
27865
|
-
stamp: JSON.stringify({ repository:
|
|
28768
|
+
stamp: JSON.stringify({ repository: state2.repository, head: state2.head, index: createHash3("sha256").update(index).digest("hex") })
|
|
27866
28769
|
};
|
|
27867
28770
|
},
|
|
27868
28771
|
async checkIgnore(root, path3 = ".gitifact/config.json") {
|
|
@@ -27884,19 +28787,52 @@ function initRepository(cwd, inherited = process.env) {
|
|
|
27884
28787
|
};
|
|
27885
28788
|
}
|
|
27886
28789
|
|
|
28790
|
+
// src/adapters/filesystem/document-file.ts
|
|
28791
|
+
import { randomBytes } from "node:crypto";
|
|
28792
|
+
import { lstat as lstat11, mkdir as mkdir3, writeFile as writeFile2 } from "node:fs/promises";
|
|
28793
|
+
import { join as join12 } from "node:path";
|
|
28794
|
+
var generateId = (prefix) => prefix + "-" + [...randomBytes(10)].map((n) => "abcdefghijklmnopqrstuvwxyz234567"[n & 31]).join("");
|
|
28795
|
+
async function createDocumentFile(root, path3, text4) {
|
|
28796
|
+
const parts = path3.split("/");
|
|
28797
|
+
for (let i = 1; i < parts.length; i++) {
|
|
28798
|
+
const folder = join12(root, ...parts.slice(0, i));
|
|
28799
|
+
const info5 = await lstat11(folder).catch((error62) => {
|
|
28800
|
+
if (error62.code === "ENOENT") return void 0;
|
|
28801
|
+
throw error62;
|
|
28802
|
+
});
|
|
28803
|
+
if (!info5) {
|
|
28804
|
+
await mkdir3(folder);
|
|
28805
|
+
continue;
|
|
28806
|
+
}
|
|
28807
|
+
if (!info5.isDirectory() || info5.isSymbolicLink()) throw Object.assign(new Error(folder), { code: "ENOTDIR" });
|
|
28808
|
+
}
|
|
28809
|
+
await writeFile2(join12(root, ...parts), text4, { flag: "wx" });
|
|
28810
|
+
}
|
|
28811
|
+
var LINE_ENDINGS_PATH = ".gitifact/.gitattributes";
|
|
28812
|
+
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";
|
|
28813
|
+
async function writeLineEndings(root) {
|
|
28814
|
+
try {
|
|
28815
|
+
await createDocumentFile(root, LINE_ENDINGS_PATH, LINE_ENDINGS);
|
|
28816
|
+
return true;
|
|
28817
|
+
} catch (error62) {
|
|
28818
|
+
if (error62.code === "EEXIST") return false;
|
|
28819
|
+
throw error62;
|
|
28820
|
+
}
|
|
28821
|
+
}
|
|
28822
|
+
|
|
27887
28823
|
// src/adapters/filesystem/managed-file.ts
|
|
27888
|
-
import { open as
|
|
27889
|
-
import { join as
|
|
28824
|
+
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";
|
|
28825
|
+
import { join as join13, dirname } from "node:path";
|
|
27890
28826
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
27891
28827
|
var DEFAULT_LIMIT = 1024 * 1024;
|
|
27892
28828
|
async function managedPath(root, path3, create = false) {
|
|
27893
28829
|
let full = root;
|
|
27894
28830
|
const parts = path3.split("/");
|
|
27895
28831
|
for (let i = 0; i < parts.length; i++) {
|
|
27896
|
-
full =
|
|
28832
|
+
full = join13(full, parts[i]);
|
|
27897
28833
|
let info5 = await fileInfo(full);
|
|
27898
28834
|
if (!info5 && create && i < parts.length - 1) {
|
|
27899
|
-
await
|
|
28835
|
+
await mkdir4(full).catch((error62) => {
|
|
27900
28836
|
if (error62.code !== "EEXIST") throw error62;
|
|
27901
28837
|
});
|
|
27902
28838
|
info5 = await fileInfo(full);
|
|
@@ -27912,7 +28848,7 @@ async function managedRead(root, path3, limit = DEFAULT_LIMIT) {
|
|
|
27912
28848
|
const before = await fileInfo(full);
|
|
27913
28849
|
if (!before) return null;
|
|
27914
28850
|
if (before.size > limit) throw new InitError("FILE_TOO_LARGE", t2("managedFile.tooLarge", { kib: limit / 1024, path: path3 }));
|
|
27915
|
-
const bytes = await
|
|
28851
|
+
const bytes = await readFile10(full);
|
|
27916
28852
|
await managedPath(root, path3);
|
|
27917
28853
|
const after = await fileInfo(full);
|
|
27918
28854
|
if (!after || before.ino !== after.ino || before.dev !== after.dev || before.mtimeMs !== after.mtimeMs || bytes.length > limit) {
|
|
@@ -27941,8 +28877,8 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
27941
28877
|
if (previous !== null) await unlink2(full);
|
|
27942
28878
|
return;
|
|
27943
28879
|
}
|
|
27944
|
-
const temp =
|
|
27945
|
-
const handle = await
|
|
28880
|
+
const temp = join13(directory, ".gitifact-managed-" + randomUUID4() + ".tmp");
|
|
28881
|
+
const handle = await open4(temp, "wx", 384);
|
|
27946
28882
|
const tempOwner = await handle.stat();
|
|
27947
28883
|
try {
|
|
27948
28884
|
try {
|
|
@@ -27953,7 +28889,7 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
27953
28889
|
}
|
|
27954
28890
|
await recheck();
|
|
27955
28891
|
await unchanged();
|
|
27956
|
-
if (await managedRead(root, path3) !== previous || await
|
|
28892
|
+
if (await managedRead(root, path3) !== previous || await readFile10(temp, "utf8") !== next) throw new InitError("INPUT_CHANGED", t2("managedFile.publishTargetChanged", { path: path3 }));
|
|
27957
28893
|
if (previous === null) await link2(temp, full);
|
|
27958
28894
|
else await rename2(temp, full);
|
|
27959
28895
|
} finally {
|
|
@@ -27964,10 +28900,10 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
27964
28900
|
}
|
|
27965
28901
|
|
|
27966
28902
|
// src/commands/agent-block.ts
|
|
27967
|
-
import { readFile as
|
|
28903
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
27968
28904
|
|
|
27969
28905
|
// src/commands/guide.ts
|
|
27970
|
-
import { readFile as
|
|
28906
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
27971
28907
|
|
|
27972
28908
|
// src/commands/output.ts
|
|
27973
28909
|
var formats = ["text", "json"];
|
|
@@ -28004,7 +28940,7 @@ var text2 = (lines) => lines.join("\n") + "\n";
|
|
|
28004
28940
|
// src/commands/guide.ts
|
|
28005
28941
|
var guideTopics = ["workflow", "spec", "design", "instructions", "records", "writing", "commit", "migrate"];
|
|
28006
28942
|
var isGuideTopic = (value) => guideTopics.includes(value);
|
|
28007
|
-
var readBundledGuide = (name2, lang) =>
|
|
28943
|
+
var readBundledGuide = (name2, lang) => readFile11(new URL("./i18n/" + lang + "/docs/" + name2 + ".md", import.meta.url), "utf8");
|
|
28008
28944
|
function guideSummary(source2) {
|
|
28009
28945
|
const { fields: fields2 } = parseFrontmatterBlock(source2.replace(/\r\n/g, "\n"), "guide");
|
|
28010
28946
|
const value = (key) => {
|
|
@@ -28045,7 +28981,7 @@ function boilerplateFor(path3) {
|
|
|
28045
28981
|
const name2 = path3 === "AGENTS.md" ? path3 : path3.split("/").pop().replace(/^\./, "").replace(/\.md$/, "");
|
|
28046
28982
|
return "# " + name2 + "\n\nProject-specific guidance for AI coding agents.\n";
|
|
28047
28983
|
}
|
|
28048
|
-
var readBundledBlock = (lang) =>
|
|
28984
|
+
var readBundledBlock = (lang) => readFile12(new URL("./i18n/" + lang + "/block.md", import.meta.url), "utf8");
|
|
28049
28985
|
async function renderAgentBlock(version2, controls = {}, lang = getLanguage()) {
|
|
28050
28986
|
const body2 = await (controls.readBlock ?? readBundledBlock)(lang);
|
|
28051
28987
|
const filled = body2.trimEnd().replace(/\{(version|language|topics)\}/g, (_whole, name2) => name2 === "version" ? version2 : name2 === "language" ? lang : guideTopics.join(", "));
|
|
@@ -28226,11 +29162,11 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
28226
29162
|
const first = await repo.inspect();
|
|
28227
29163
|
const root = first.state.repository.rootPath;
|
|
28228
29164
|
const docsPlan = agentDocs ? await planAgentDocs(root, agentDocs) : skippedAgentDocs;
|
|
28229
|
-
const result = async (config3, outcome) => {
|
|
29165
|
+
const result = async (config3, outcome, lineEndings = false) => {
|
|
28230
29166
|
const checked = await update;
|
|
28231
|
-
return
|
|
29167
|
+
return projectInitV8.parse({
|
|
28232
29168
|
contract: "project-init",
|
|
28233
|
-
version:
|
|
29169
|
+
version: 8,
|
|
28234
29170
|
ok: true,
|
|
28235
29171
|
outcome,
|
|
28236
29172
|
rootPath: root,
|
|
@@ -28238,6 +29174,7 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
28238
29174
|
schemaVersion: config3.schemaVersion,
|
|
28239
29175
|
baseline: config3.baseline,
|
|
28240
29176
|
agentDocs: { mode: docsPlan.mode, paths: docsPlan.paths },
|
|
29177
|
+
lineEndings: { path: LINE_ENDINGS_PATH, created: lineEndings },
|
|
28241
29178
|
update: checked,
|
|
28242
29179
|
install: checked.status === "available" ? { npx: npxUpdate(checked.latestVersion), npmGlobal: npmGlobalInstall(checked.latestVersion) } : null
|
|
28243
29180
|
});
|
|
@@ -28249,8 +29186,9 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
28249
29186
|
if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !== text5) throw new InitError("INPUT_CHANGED", t2("init.inputChanged"));
|
|
28250
29187
|
};
|
|
28251
29188
|
await unchanged();
|
|
28252
|
-
if (
|
|
28253
|
-
|
|
29189
|
+
if (dryRun) return result(config3, "already-initialized");
|
|
29190
|
+
await applyAgentDocs(root, docsPlan, unchanged);
|
|
29191
|
+
return result(config3, "already-initialized", await writeLineEndings(root));
|
|
28254
29192
|
};
|
|
28255
29193
|
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
29194
|
const text4 = JSON.stringify(config2, null, 2) + "\n";
|
|
@@ -28260,13 +29198,13 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
28260
29198
|
};
|
|
28261
29199
|
await published();
|
|
28262
29200
|
await applyAgentDocs(root, docsPlan, published);
|
|
28263
|
-
return result(config2, outcome);
|
|
29201
|
+
return result(config2, outcome, await writeLineEndings(root));
|
|
28264
29202
|
};
|
|
28265
29203
|
const old = await readConfigFile(root);
|
|
28266
29204
|
if (old !== void 0) return existing(old);
|
|
28267
29205
|
if (first.trackedConfig) throw new InitError("CONFIG_DELETED", t2("init.configDeleted"));
|
|
28268
29206
|
const checkRecords = async () => {
|
|
28269
|
-
const entries = await
|
|
29207
|
+
const entries = await readdir5(join14(root, ".gitifact")).catch((e) => {
|
|
28270
29208
|
if (e.code === "ENOENT") return [];
|
|
28271
29209
|
throw e;
|
|
28272
29210
|
});
|
|
@@ -28318,117 +29256,33 @@ async function runInit(options, version2) {
|
|
|
28318
29256
|
);
|
|
28319
29257
|
if (!dto.ok) throw new Error("unreachable");
|
|
28320
29258
|
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") + ")");
|
|
29259
|
+
const lineEndings = dto.lineEndings.created ? t2("init.text.lineEndings", { path: dto.lineEndings.path }) + "\n" : "";
|
|
28321
29260
|
const newer = dto.install ? t2("init.text.updateAvailable", { version: dto.update.latestVersion, command: dto.install.npx }) + "\n" : "";
|
|
28322
29261
|
process.stdout.write(options.format === "text" ? `${dto.outcome}: ${dto.rootPath}/.gitifact/config.json
|
|
28323
29262
|
${t2("init.text.storage")}: schemaVersion ${dto.schemaVersion}
|
|
28324
29263
|
${t2("init.text.agentDocs")}: ${docs}
|
|
28325
|
-
${newer}` : JSON.stringify(dto) + "\n");
|
|
29264
|
+
${lineEndings}${newer}` : JSON.stringify(dto) + "\n");
|
|
28326
29265
|
} catch (error62) {
|
|
28327
29266
|
cancel.abort();
|
|
28328
29267
|
const known = error62 instanceof InitError || error62 instanceof RepositoryReadError;
|
|
28329
29268
|
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(
|
|
29269
|
+
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
29270
|
process.exitCode = 1;
|
|
28332
29271
|
}
|
|
28333
29272
|
}
|
|
28334
29273
|
|
|
28335
29274
|
// src/commands/documents.ts
|
|
28336
29275
|
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
|
-
}
|
|
29276
|
+
import { join as join16 } from "node:path";
|
|
28424
29277
|
|
|
28425
29278
|
// src/commands/project.ts
|
|
29279
|
+
import { join as join15 } from "node:path";
|
|
28426
29280
|
var operations = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"];
|
|
28427
29281
|
async function openProject(cwd, options = {}) {
|
|
28428
29282
|
const reader = storeReader(cwd);
|
|
28429
29283
|
const location = await reader.location();
|
|
28430
29284
|
if (options.writing) {
|
|
28431
|
-
for (const marker2 of operations) if (await fileInfo(
|
|
29285
|
+
for (const marker2 of operations) if (await fileInfo(join15(location.gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
|
|
28432
29286
|
if (await reader.hasUnmerged()) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
28433
29287
|
}
|
|
28434
29288
|
const raw = await readConfigFile(location.root);
|
|
@@ -28459,15 +29313,140 @@ function documentsOf(files) {
|
|
|
28459
29313
|
return docs;
|
|
28460
29314
|
}
|
|
28461
29315
|
|
|
29316
|
+
// src/queries/filters.ts
|
|
29317
|
+
function byAuthor(author) {
|
|
29318
|
+
if (author === void 0) return () => true;
|
|
29319
|
+
const wanted = author.trim().toLowerCase();
|
|
29320
|
+
return (change2) => change2.author.toLowerCase() === wanted || change2.email.toLowerCase() === wanted;
|
|
29321
|
+
}
|
|
29322
|
+
async function since(project, value, head) {
|
|
29323
|
+
if (value === void 0) return () => true;
|
|
29324
|
+
if (/^\d{4}-\d\d-\d\d/.test(value)) {
|
|
29325
|
+
const from = Date.parse(value);
|
|
29326
|
+
if (!Number.isFinite(from)) throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29327
|
+
return (change2) => Date.parse(change2.date) >= from;
|
|
29328
|
+
}
|
|
29329
|
+
let commit;
|
|
29330
|
+
try {
|
|
29331
|
+
commit = await project.reader.resolve(value);
|
|
29332
|
+
} catch {
|
|
29333
|
+
throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29334
|
+
}
|
|
29335
|
+
if (!head) return () => false;
|
|
29336
|
+
const after = new Set(project.reader.decode(await project.reader.run(["rev-list", `${commit}..${head}`, "--"])).split("\n").filter(Boolean));
|
|
29337
|
+
return (change2) => after.has(change2.commit);
|
|
29338
|
+
}
|
|
29339
|
+
async function sinceRange(project, value, head) {
|
|
29340
|
+
if (value === void 0) return {};
|
|
29341
|
+
if (/^\d{4}-\d\d-\d\d/.test(value)) {
|
|
29342
|
+
const from = Date.parse(value);
|
|
29343
|
+
if (!Number.isFinite(from)) throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29344
|
+
return { from };
|
|
29345
|
+
}
|
|
29346
|
+
let commit;
|
|
29347
|
+
try {
|
|
29348
|
+
commit = await project.reader.resolve(value);
|
|
29349
|
+
} catch {
|
|
29350
|
+
throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29351
|
+
}
|
|
29352
|
+
if (!head) return { commits: [] };
|
|
29353
|
+
return { commits: project.reader.decode(await project.reader.run(["rev-list", `${commit}..${head}`, "--"])).split("\n").filter(Boolean) };
|
|
29354
|
+
}
|
|
29355
|
+
var contains = (query) => {
|
|
29356
|
+
const wanted = query?.trim().toLowerCase();
|
|
29357
|
+
return (...texts) => !wanted || texts.some((value) => value?.toLowerCase().includes(wanted));
|
|
29358
|
+
};
|
|
29359
|
+
|
|
29360
|
+
// src/queries/specs.ts
|
|
29361
|
+
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);
|
|
29362
|
+
async function listSpecs(source2, filter, request) {
|
|
29363
|
+
const listed3 = await source2.cache.documents.list();
|
|
29364
|
+
const problems = listed3.problems;
|
|
29365
|
+
const states = await documentStates(source2, listed3.documents);
|
|
29366
|
+
const documents = states.documents;
|
|
29367
|
+
const byId = new Map(documents.map((d) => [d.id, d]));
|
|
29368
|
+
const arranged = arrangeDocuments(documents);
|
|
29369
|
+
let features = arranged.features;
|
|
29370
|
+
if (filter.feature !== void 0) {
|
|
29371
|
+
features = features.filter((f) => f.index.feature === filter.feature);
|
|
29372
|
+
if (!features.length) throw new CommandError("UNKNOWN_FEATURE", t2("docs.unknownFeature", { feature: filter.feature }));
|
|
29373
|
+
}
|
|
29374
|
+
const titled = (id) => ({ id, title: byId.get(id)?.title ?? null });
|
|
29375
|
+
const designsOf = /* @__PURE__ */ new Map();
|
|
29376
|
+
for (const f of arranged.features) for (const d of f.designs) for (const r of d.requirements) designsOf.set(r, [...designsOf.get(r) ?? [], titled(d.id)]);
|
|
29377
|
+
const historyNeeded = filter.sort === "updated" || filter.changedSince !== void 0 || filter.author !== void 0 || !!filter.withUpdated;
|
|
29378
|
+
const latest = /* @__PURE__ */ new Map();
|
|
29379
|
+
const newest = /* @__PURE__ */ new Map();
|
|
29380
|
+
let touched;
|
|
29381
|
+
if (historyNeeded) {
|
|
29382
|
+
const head = await source2.head();
|
|
29383
|
+
const changes2 = head ? await source2.cache.history.changesOf(head) : [];
|
|
29384
|
+
changes2.forEach((c, rank) => {
|
|
29385
|
+
if (!latest.has(c.id)) {
|
|
29386
|
+
latest.set(c.id, { commit: c.commit, date: c.date, author: c.author, email: c.email });
|
|
29387
|
+
newest.set(c.id, rank);
|
|
29388
|
+
}
|
|
29389
|
+
});
|
|
29390
|
+
if (filter.changedSince !== void 0 || filter.author !== void 0) {
|
|
29391
|
+
const after = await since(source2, filter.changedSince, head);
|
|
29392
|
+
const by = byAuthor(filter.author);
|
|
29393
|
+
touched = new Set(changes2.filter((c) => after(c) && by(c)).map((c) => c.id));
|
|
29394
|
+
}
|
|
29395
|
+
}
|
|
29396
|
+
const matched = filter.q !== void 0 ? await source2.cache.documents.matching(filter.q) : void 0;
|
|
29397
|
+
const rows = features.flatMap((f) => [f.index, ...f.requirements, ...f.designs].filter(
|
|
29398
|
+
(doc) => (filter.type === void 0 || doc.kind === filter.type) && (!filter.withoutDesign || !f.designs.length) && (!filter.uncovered || doc.kind === "requirement" && !designsOf.has(doc.id)) && (!filter.draft || doc.draft) && (!touched || touched.has(doc.id)) && (!matched || matched.has(doc.id))
|
|
29399
|
+
).map((doc) => ({
|
|
29400
|
+
id: doc.id,
|
|
29401
|
+
kind: doc.kind,
|
|
29402
|
+
feature: doc.feature,
|
|
29403
|
+
path: doc.path,
|
|
29404
|
+
title: doc.title,
|
|
29405
|
+
description: doc.description,
|
|
29406
|
+
...doc.kind === "requirement" || doc.kind === "design" ? { order: doc.order } : {},
|
|
29407
|
+
...doc.draft ? { draft: true } : {},
|
|
29408
|
+
state: states.state(doc.id),
|
|
29409
|
+
...states.previousPath(doc.id) ? { previousPath: states.previousPath(doc.id) } : {},
|
|
29410
|
+
...doc.kind === "design" ? { requirements: doc.requirements.map(titled), sources: titledSources(doc, byId) } : {},
|
|
29411
|
+
...doc.kind === "requirement" ? { designs: designsOf.get(doc.id) ?? [] } : {},
|
|
29412
|
+
...historyNeeded ? { updated: latest.get(doc.id) ?? null } : {},
|
|
29413
|
+
...matched ? { line: matched.get(doc.id) } : {}
|
|
29414
|
+
})));
|
|
29415
|
+
if (filter.sort === "title") rows.sort((a, b) => a.title.localeCompare(b.title));
|
|
29416
|
+
if (filter.sort === "updated") rows.sort((a, b) => (newest.get(a.id) ?? Infinity) - (newest.get(b.id) ?? Infinity));
|
|
29417
|
+
const grouped = filter.sort === "order";
|
|
29418
|
+
const groups = features.map((f) => ({ id: f.index.id, rows: rows.filter((r) => r.feature === f.index.feature) })).filter((g) => g.rows.length);
|
|
29419
|
+
const page = grouped ? pageOf(groups, (g) => g.id, request) : pageOf(rows, (r) => r.id, request);
|
|
29420
|
+
if (!page) return void 0;
|
|
29421
|
+
const shown = grouped ? page.rows.flatMap((g) => g.rows) : page.rows;
|
|
29422
|
+
return {
|
|
29423
|
+
rows: shown,
|
|
29424
|
+
features,
|
|
29425
|
+
byId,
|
|
29426
|
+
states,
|
|
29427
|
+
grouped,
|
|
29428
|
+
problems,
|
|
29429
|
+
page: { rows: page.rows, total: page.total, next: page.next },
|
|
29430
|
+
unit: grouped ? "feature" : "document",
|
|
29431
|
+
orphans: filter.feature === void 0 ? arranged.orphans : []
|
|
29432
|
+
};
|
|
29433
|
+
}
|
|
29434
|
+
|
|
28462
29435
|
// src/commands/documents.ts
|
|
28463
29436
|
var draftMark = (doc) => doc.draft ? " (" + t2("docs.draft") + ")" : "";
|
|
29437
|
+
var stateLabel = {
|
|
29438
|
+
added: () => t2("docs.state.added"),
|
|
29439
|
+
modified: () => t2("docs.state.modified"),
|
|
29440
|
+
deleted: () => t2("docs.state.deleted")
|
|
29441
|
+
};
|
|
29442
|
+
var stateMark = (state2) => state2 === "committed" ? "" : " (" + stateLabel[state2]() + ")";
|
|
28464
29443
|
var line = (doc) => `${doc.id} ${doc.title}${draftMark(doc)} \u2014 ${doc.description}`;
|
|
28465
|
-
var
|
|
29444
|
+
var fileLine = (f) => f.title ? `${f.path} ${f.title} \u2014 ${f.description}` : f.path;
|
|
28466
29445
|
async function working(project) {
|
|
28467
29446
|
const { documents } = await project.cache.documents.list();
|
|
28468
29447
|
return {
|
|
28469
29448
|
byId: new Map(documents.map((d) => [d.id, d])),
|
|
28470
|
-
read: (path3) => readFile13(
|
|
29449
|
+
read: (path3) => readFile13(join16(project.root, ...path3.split("/")), "utf8"),
|
|
28471
29450
|
referencing: async (id) => [...new Set((await project.cache.documents.referencing(id)).map((r) => r.from))]
|
|
28472
29451
|
};
|
|
28473
29452
|
}
|
|
@@ -28493,7 +29472,7 @@ async function showDocuments(project, ids, at, kinds, otherResource) {
|
|
|
28493
29472
|
...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
29473
|
] : [];
|
|
28495
29474
|
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
|
|
29475
|
+
const files = doc.kind === "instruction" && at === void 0 ? { files: (await readInstructionFiles(project.root, doc.path)).files } : {};
|
|
28497
29476
|
return { id, kind: doc.kind, path: doc.path, title: doc.title, text: await view.read(doc.path), references, referencedBy, ...files };
|
|
28498
29477
|
}));
|
|
28499
29478
|
const named = (r) => r.id ? r.id + " " + (r.title ?? "(" + t2("docs.missing") + ")") : r.title + " <" + r.url + ">";
|
|
@@ -28502,7 +29481,7 @@ async function showDocuments(project, ids, at, kinds, otherResource) {
|
|
|
28502
29481
|
d.text.replace(/\n$/, ""),
|
|
28503
29482
|
...section("-- " + t2("docs.references"), d.references.map(named)),
|
|
28504
29483
|
...section("-- " + t2("docs.referencedBy"), d.referencedBy.map(named)),
|
|
28505
|
-
...section("-- " + t2("docs.files"), d.files ?? [])
|
|
29484
|
+
...section("-- " + t2("docs.files"), (d.files ?? []).map(fileLine))
|
|
28506
29485
|
].join("\n"));
|
|
28507
29486
|
return { json: { ...at ? { commit: at } : {}, documents: shown }, text: blocks.join("\n\n") + "\n" };
|
|
28508
29487
|
}
|
|
@@ -28576,32 +29555,6 @@ function checkFields(fields2, columns3) {
|
|
|
28576
29555
|
if (unknown2.length || !fields2.length) throw new CommandError("INVALID_VALUE", t2("list.unknownFields", { fields: unknown2.join(", "), columns: columns3.join(", ") }));
|
|
28577
29556
|
return [...new Set(fields2)];
|
|
28578
29557
|
}
|
|
28579
|
-
function byAuthor(author) {
|
|
28580
|
-
if (author === void 0) return () => true;
|
|
28581
|
-
const wanted = author.trim().toLowerCase();
|
|
28582
|
-
return (change2) => change2.author.toLowerCase() === wanted || change2.email.toLowerCase() === wanted;
|
|
28583
|
-
}
|
|
28584
|
-
async function since(project, value, head) {
|
|
28585
|
-
if (value === void 0) return () => true;
|
|
28586
|
-
if (/^\d{4}-\d\d-\d\d/.test(value)) {
|
|
28587
|
-
const from = Date.parse(value);
|
|
28588
|
-
if (!Number.isFinite(from)) throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
28589
|
-
return (change2) => Date.parse(change2.date) >= from;
|
|
28590
|
-
}
|
|
28591
|
-
let commit;
|
|
28592
|
-
try {
|
|
28593
|
-
commit = await project.reader.resolve(value);
|
|
28594
|
-
} catch {
|
|
28595
|
-
throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
28596
|
-
}
|
|
28597
|
-
if (!head) return () => false;
|
|
28598
|
-
const after = new Set(project.reader.decode(await project.reader.run(["rev-list", `${commit}..${head}`, "--"])).split("\n").filter(Boolean));
|
|
28599
|
-
return (change2) => after.has(change2.commit);
|
|
28600
|
-
}
|
|
28601
|
-
var contains = (query) => {
|
|
28602
|
-
const wanted = query?.trim().toLowerCase();
|
|
28603
|
-
return (...texts) => !wanted || texts.some((value) => value?.toLowerCase().includes(wanted));
|
|
28604
|
-
};
|
|
28605
29558
|
function cell(value) {
|
|
28606
29559
|
if (value === void 0 || value === null || value === false) return "";
|
|
28607
29560
|
if (Array.isArray(value)) return value.map((v) => typeof v === "object" && v !== null ? "id" in v ? v.id : "title" in v ? v.title : JSON.stringify(v) : String(v)).join(",");
|
|
@@ -28620,69 +29573,36 @@ function selected(rows, fields2) {
|
|
|
28620
29573
|
text: rows.length ? text2(rows.map((row) => fields2.map((f) => cell(row[f])).join(" "))) : ""
|
|
28621
29574
|
};
|
|
28622
29575
|
}
|
|
29576
|
+
function paginate(rows, keyOf, options) {
|
|
29577
|
+
return pageOr(pageOf(rows, keyOf, options), options);
|
|
29578
|
+
}
|
|
29579
|
+
function pageOr(page, options) {
|
|
29580
|
+
return page ?? cursorGone(options);
|
|
29581
|
+
}
|
|
29582
|
+
function cursorGone(options) {
|
|
29583
|
+
throw new CommandError("INVALID_VALUE", t2("list.after", { cursor: options.after ?? "" }));
|
|
29584
|
+
}
|
|
29585
|
+
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 })];
|
|
29586
|
+
function aside(format2, lines) {
|
|
29587
|
+
if (format2 === "text" && lines.length) process.stderr.write(lines.join("\n") + "\n");
|
|
29588
|
+
return "";
|
|
29589
|
+
}
|
|
28623
29590
|
|
|
28624
29591
|
// src/commands/specs.ts
|
|
28625
29592
|
var specKinds = ["feature", "requirement", "design"];
|
|
28626
29593
|
var specSorts = ["order", "title", "updated"];
|
|
28627
|
-
var columns = ["id", "kind", "feature", "path", "title", "description", "order", "draft", "requirements", "sources", "designs", "updated", "line"];
|
|
29594
|
+
var columns = ["id", "kind", "feature", "path", "title", "description", "order", "draft", "state", "previousPath", "requirements", "sources", "designs", "updated", "line"];
|
|
28628
29595
|
var kindLabel = { feature: () => t2("docs.feature"), requirement: () => t2("docs.requirement"), design: () => t2("docs.design") };
|
|
28629
29596
|
var runSpecsList = (options) => runCommand("specs", options.format, async () => {
|
|
28630
29597
|
const fields2 = checkFields(options.fields, columns);
|
|
28631
29598
|
const project = await openProject(process.cwd());
|
|
28632
|
-
const {
|
|
28633
|
-
const
|
|
28634
|
-
const
|
|
28635
|
-
|
|
28636
|
-
if (options.feature !== void 0) {
|
|
28637
|
-
features = features.filter((f) => f.index.feature === options.feature);
|
|
28638
|
-
if (!features.length) throw new CommandError("UNKNOWN_FEATURE", t2("docs.unknownFeature", { feature: options.feature }));
|
|
28639
|
-
}
|
|
28640
|
-
const titled = (id) => ({ id, title: byId.get(id)?.title ?? null });
|
|
28641
|
-
const designsOf = /* @__PURE__ */ new Map();
|
|
28642
|
-
for (const f of arranged.features) for (const d of f.designs) for (const r of d.requirements) designsOf.set(r, [...designsOf.get(r) ?? [], titled(d.id)]);
|
|
28643
|
-
const historyNeeded = options.sort === "updated" || options.changedSince !== void 0 || options.author !== void 0 || !!fields2?.includes("updated");
|
|
28644
|
-
const latest = /* @__PURE__ */ new Map();
|
|
28645
|
-
const newest = /* @__PURE__ */ new Map();
|
|
28646
|
-
let touched;
|
|
28647
|
-
if (historyNeeded) {
|
|
28648
|
-
const head = await project.head();
|
|
28649
|
-
const changes2 = head ? await project.cache.history.changesOf(head) : [];
|
|
28650
|
-
changes2.forEach((c, rank) => {
|
|
28651
|
-
if (!latest.has(c.id)) {
|
|
28652
|
-
latest.set(c.id, { commit: c.commit, date: c.date, author: c.author, email: c.email });
|
|
28653
|
-
newest.set(c.id, rank);
|
|
28654
|
-
}
|
|
28655
|
-
});
|
|
28656
|
-
if (options.changedSince !== void 0 || options.author !== void 0) {
|
|
28657
|
-
const after = await since(project, options.changedSince, head);
|
|
28658
|
-
const by = byAuthor(options.author);
|
|
28659
|
-
touched = new Set(changes2.filter((c) => after(c) && by(c)).map((c) => c.id));
|
|
28660
|
-
}
|
|
28661
|
-
}
|
|
28662
|
-
const matched = options.q !== void 0 ? await project.cache.documents.matching(options.q) : void 0;
|
|
28663
|
-
const rows = features.flatMap((f) => [f.index, ...f.requirements, ...f.designs].filter(
|
|
28664
|
-
(doc) => (options.type === void 0 || doc.kind === options.type) && (!options.withoutDesign || !f.designs.length) && (!options.uncovered || doc.kind === "requirement" && !designsOf.has(doc.id)) && (!options.draft || doc.draft) && (!touched || touched.has(doc.id)) && (!matched || matched.has(doc.id))
|
|
28665
|
-
).map((doc) => ({
|
|
28666
|
-
id: doc.id,
|
|
28667
|
-
kind: doc.kind,
|
|
28668
|
-
feature: doc.feature,
|
|
28669
|
-
path: doc.path,
|
|
28670
|
-
title: doc.title,
|
|
28671
|
-
description: doc.description,
|
|
28672
|
-
...doc.kind === "requirement" || doc.kind === "design" ? { order: doc.order } : {},
|
|
28673
|
-
...doc.draft ? { draft: true } : {},
|
|
28674
|
-
...doc.kind === "design" ? { requirements: doc.requirements.map(titled), sources: titledSources(doc, byId) } : {},
|
|
28675
|
-
...doc.kind === "requirement" ? { designs: designsOf.get(doc.id) ?? [] } : {},
|
|
28676
|
-
...historyNeeded ? { updated: latest.get(doc.id) ?? null } : {},
|
|
28677
|
-
...matched ? { line: matched.get(doc.id) } : {}
|
|
28678
|
-
})));
|
|
28679
|
-
if (options.sort === "title") rows.sort((a, b) => a.title.localeCompare(b.title));
|
|
28680
|
-
if (options.sort === "updated") rows.sort((a, b) => (newest.get(a.id) ?? Infinity) - (newest.get(b.id) ?? Infinity));
|
|
28681
|
-
const shown = options.limit === void 0 ? rows : rows.slice(0, options.limit);
|
|
28682
|
-
const orphans = options.feature === void 0 ? arranged.orphans : [];
|
|
29599
|
+
const listed3 = await listSpecs(project, { ...options, withUpdated: !!fields2?.includes("updated") }, options) ?? cursorGone(options);
|
|
29600
|
+
const { rows: shown, features, byId, states, grouped, problems, orphans, page } = listed3;
|
|
29601
|
+
const more = pageLine(page, 0, grouped ? t2("list.features") : t2("list.items"));
|
|
29602
|
+
const paging = { total: page.total, next: page.next, unit: listed3.unit };
|
|
28683
29603
|
if (fields2) {
|
|
28684
29604
|
const picked = selected(shown, fields2);
|
|
28685
|
-
return { json: { documents: picked.json, orphans, problems }, text: picked.text };
|
|
29605
|
+
return { json: { documents: picked.json, orphans, problems, page: paging }, text: picked.text + aside(options.format, more) };
|
|
28686
29606
|
}
|
|
28687
29607
|
const refName = (r) => r.id + " " + (r.title ?? "(" + t2("docs.missing") + ")");
|
|
28688
29608
|
const excerpt = (row) => row.line ? [" " + row.line] : [];
|
|
@@ -28691,29 +29611,30 @@ var runSpecsList = (options) => runCommand("specs", options.format, async () =>
|
|
|
28691
29611
|
for (const f of features) {
|
|
28692
29612
|
const own2 = shown.filter((r) => r.feature === f.index.feature);
|
|
28693
29613
|
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}`);
|
|
29614
|
+
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
29615
|
const index = own2.find((r) => r.kind === "feature");
|
|
28696
29616
|
if (index) out.push(...excerpt(index));
|
|
28697
|
-
const requirement2 = (r) => [`${r.order} ${line(byId.get(r.id))}`, ...excerpt(r)];
|
|
29617
|
+
const requirement2 = (r) => [`${r.order} ${line(byId.get(r.id))}${stateMark(r.state)}`, ...excerpt(r)];
|
|
28698
29618
|
out.push(...section(" " + t2("docs.requirements"), own2.filter((r) => r.kind === "requirement").flatMap(requirement2), " "));
|
|
28699
29619
|
const designs = own2.filter((r) => r.kind === "design").flatMap((r) => {
|
|
28700
29620
|
const refs = r.requirements.length ? " [" + r.requirements.map(refName).join(", ") + "]" : "";
|
|
28701
29621
|
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)];
|
|
29622
|
+
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
29623
|
});
|
|
28704
29624
|
out.push(...section(" " + t2("docs.designs"), designs, " "));
|
|
28705
29625
|
}
|
|
28706
29626
|
} else {
|
|
28707
29627
|
for (const r of shown) {
|
|
28708
29628
|
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));
|
|
29629
|
+
out.push(`${r.id} ${r.title}${draftMark(r)}${stateMark(r.state)} (${kindLabel[r.kind]()} \xB7 ${r.feature}) \u2014 ${r.description}${when}`, ...excerpt(r));
|
|
28710
29630
|
}
|
|
28711
29631
|
}
|
|
28712
29632
|
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
29633
|
if (!out.length) out.push(filtered ? t2("docs.noMatch") : t2("docs.empty"));
|
|
29634
|
+
out.push(...more);
|
|
28714
29635
|
if (orphans.length) out.push(t2("docs.orphans", { folders: orphans.join(", ") }));
|
|
28715
29636
|
if (problems.length) out.push(t2("docs.unreadable", { count: problems.length }));
|
|
28716
|
-
return { json: { documents: shown, orphans, problems }, text: text2(out) };
|
|
29637
|
+
return { json: { documents: shown, orphans, problems, page: paging }, text: text2(out) };
|
|
28717
29638
|
});
|
|
28718
29639
|
var runSpecsShow = (ids, options) => runCommand("specs", options.format, async () => {
|
|
28719
29640
|
const project = await openProject(process.cwd());
|
|
@@ -28722,24 +29643,23 @@ var runSpecsShow = (ids, options) => runCommand("specs", options.format, async (
|
|
|
28722
29643
|
});
|
|
28723
29644
|
var runSpecsNew = (kind2, path3, options) => runCommand("specs", options.format, async () => createDocument(await openProject(process.cwd()), kind2, path3, options));
|
|
28724
29645
|
|
|
28725
|
-
// src/
|
|
28726
|
-
import { lstat as
|
|
28727
|
-
import { join as
|
|
28728
|
-
var instructionSorts = ["name", "updated"];
|
|
28729
|
-
var columns2 = ["id", "name", "path", "title", "description", "draft", "files", "updated", "line"];
|
|
29646
|
+
// src/queries/instructions.ts
|
|
29647
|
+
import { lstat as lstat12 } from "node:fs/promises";
|
|
29648
|
+
import { join as join17 } from "node:path";
|
|
28730
29649
|
var AGENTS = "AGENTS.md";
|
|
28731
|
-
|
|
28732
|
-
const
|
|
28733
|
-
const
|
|
28734
|
-
const
|
|
28735
|
-
const
|
|
28736
|
-
const
|
|
29650
|
+
async function listInstructions(source2, options, request) {
|
|
29651
|
+
const listed3 = await source2.cache.documents.list();
|
|
29652
|
+
const problems = listed3.problems;
|
|
29653
|
+
const states = await documentStates(source2, listed3.documents);
|
|
29654
|
+
const documents = states.documents;
|
|
29655
|
+
const agents2 = { path: AGENTS, exists: !!(await lstat12(join17(source2.root, AGENTS)).catch(() => void 0))?.isFile() };
|
|
29656
|
+
const historyNeeded = options.sort === "updated" || options.author !== void 0 || !!options.withUpdated;
|
|
28737
29657
|
const latest = /* @__PURE__ */ new Map();
|
|
28738
29658
|
const newest = /* @__PURE__ */ new Map();
|
|
28739
29659
|
let touched;
|
|
28740
29660
|
if (historyNeeded) {
|
|
28741
|
-
const head = await
|
|
28742
|
-
const changes2 = head ? await
|
|
29661
|
+
const head = await source2.head();
|
|
29662
|
+
const changes2 = head ? await source2.cache.history.changesOf(head) : [];
|
|
28743
29663
|
changes2.forEach((c, rank) => {
|
|
28744
29664
|
if (!latest.has(c.id)) {
|
|
28745
29665
|
latest.set(c.id, { commit: c.commit, date: c.date, author: c.author, email: c.email });
|
|
@@ -28751,35 +29671,60 @@ var runInstructionsList = (options) => runCommand("instructions", options.format
|
|
|
28751
29671
|
touched = new Set(changes2.filter(by).map((c) => c.id));
|
|
28752
29672
|
}
|
|
28753
29673
|
}
|
|
28754
|
-
const matched = options.q !== void 0 ? await
|
|
29674
|
+
const matched = options.q !== void 0 ? await source2.cache.documents.matching(options.q) : void 0;
|
|
28755
29675
|
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
|
-
|
|
29676
|
+
const rows = await Promise.all(instructions2.map(async (d) => {
|
|
29677
|
+
const state2 = states.state(d.id);
|
|
29678
|
+
const { files, limited } = state2 === "deleted" ? { files: [], limited: false } : await readInstructionFiles(source2.root, d.path);
|
|
29679
|
+
return {
|
|
29680
|
+
id: d.id,
|
|
29681
|
+
name: d.name,
|
|
29682
|
+
path: d.path,
|
|
29683
|
+
title: d.title,
|
|
29684
|
+
description: d.description,
|
|
29685
|
+
...d.draft ? { draft: true } : {},
|
|
29686
|
+
state: state2,
|
|
29687
|
+
...states.previousPath(d.id) ? { previousPath: states.previousPath(d.id) } : {},
|
|
29688
|
+
files,
|
|
29689
|
+
...limited ? { filesLimited: true } : {},
|
|
29690
|
+
...historyNeeded ? { updated: latest.get(d.id) ?? null } : {},
|
|
29691
|
+
...matched ? { line: matched.get(d.id) } : {}
|
|
29692
|
+
};
|
|
29693
|
+
}));
|
|
28767
29694
|
if (options.sort === "updated") rows.sort((a, b) => (newest.get(a.id) ?? Infinity) - (newest.get(b.id) ?? Infinity));
|
|
28768
|
-
const
|
|
29695
|
+
const page = pageOf(rows, (r) => r.id, request);
|
|
29696
|
+
if (!page) return void 0;
|
|
29697
|
+
return { agents: agents2, problems, page };
|
|
29698
|
+
}
|
|
29699
|
+
|
|
29700
|
+
// src/commands/instructions.ts
|
|
29701
|
+
var instructionSorts = ["name", "updated"];
|
|
29702
|
+
var columns2 = ["id", "name", "path", "title", "description", "draft", "state", "previousPath", "files", "updated", "line"];
|
|
29703
|
+
var runInstructionsList = (options) => runCommand("instructions", options.format, async () => {
|
|
29704
|
+
const fields2 = checkFields(options.fields, columns2);
|
|
29705
|
+
const project = await openProject(process.cwd());
|
|
29706
|
+
const listed3 = await listInstructions(project, { ...options, withUpdated: !!fields2?.includes("updated") }, options) ?? cursorGone(options);
|
|
29707
|
+
const { agents: agents2, problems, page } = listed3;
|
|
29708
|
+
const shown = page.rows;
|
|
29709
|
+
const paging = { total: page.total, next: page.next, unit: "instruction" };
|
|
28769
29710
|
const unreadable2 = problems.filter((p) => p.path.startsWith(INSTRUCTIONS_ROOT + "/"));
|
|
28770
29711
|
if (fields2) {
|
|
28771
29712
|
const picked = selected(shown, fields2);
|
|
28772
|
-
return {
|
|
29713
|
+
return {
|
|
29714
|
+
json: { agents: agents2, instructions: picked.json, problems: unreadable2, page: paging },
|
|
29715
|
+
text: selected(shown.map((r) => ({ ...r, files: r.files.map((f) => f.path) })), fields2).text + aside(options.format, pageLine(page))
|
|
29716
|
+
};
|
|
28773
29717
|
}
|
|
28774
29718
|
const out = [agents2.exists ? t2("instructions.agents") : t2("instructions.noAgents")];
|
|
28775
29719
|
for (const r of shown) {
|
|
28776
|
-
const files = r.files ? " \xB7 " + t2("instructions.files", { count: r.files }) : "";
|
|
28777
29720
|
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}${
|
|
29721
|
+
out.push(`${r.id} ${r.title}${draftMark(r)}${stateMark(r.state)} (${r.name}) \u2014 ${r.description}${when}`, ...r.line ? [" " + r.line] : []);
|
|
29722
|
+
out.push(...r.files.map((f) => " " + fileLine(f)), ...r.filesLimited ? [" " + t2("instructions.filesLimited", { count: r.files.length })] : []);
|
|
28779
29723
|
}
|
|
28780
29724
|
if (!shown.length) out.push(options.q !== void 0 || options.author !== void 0 ? t2("docs.noMatch") : t2("instructions.empty"));
|
|
29725
|
+
out.push(...pageLine(page));
|
|
28781
29726
|
if (unreadable2.length) out.push(t2("docs.unreadable", { count: unreadable2.length }));
|
|
28782
|
-
return { json: { agents: agents2, instructions: shown, problems: unreadable2 }, text: text2(out) };
|
|
29727
|
+
return { json: { agents: agents2, instructions: shown, problems: unreadable2, page: paging }, text: text2(out) };
|
|
28783
29728
|
});
|
|
28784
29729
|
async function resolveInstructions(project, targets, at) {
|
|
28785
29730
|
const view = at === void 0 ? await working(project) : await committed(project, at);
|
|
@@ -28805,9 +29750,9 @@ var runInstructionsShow = (targets, options) => runCommand("instructions", optio
|
|
|
28805
29750
|
var runInstructionsNew = (name2, options) => runCommand("instructions", options.format, async () => createDocument(await openProject(process.cwd()), "instruction", name2, options));
|
|
28806
29751
|
|
|
28807
29752
|
// src/adapters/filesystem/document-warnings.ts
|
|
28808
|
-
import { lstat as
|
|
28809
|
-
import { join as
|
|
28810
|
-
var info2 = async (path3) =>
|
|
29753
|
+
import { lstat as lstat13, readdir as readdir6 } from "node:fs/promises";
|
|
29754
|
+
import { join as join18 } from "node:path";
|
|
29755
|
+
var info2 = async (path3) => lstat13(path3).catch((e) => {
|
|
28811
29756
|
if (e.code === "ENOENT") return void 0;
|
|
28812
29757
|
throw e;
|
|
28813
29758
|
});
|
|
@@ -28815,11 +29760,11 @@ var posix = (path3) => path3.split("\\").join("/");
|
|
|
28815
29760
|
async function listAssets(root) {
|
|
28816
29761
|
const out = [];
|
|
28817
29762
|
async function visit2(relative, depth) {
|
|
28818
|
-
const stat3 = await info2(
|
|
29763
|
+
const stat3 = await info2(join18(root, relative));
|
|
28819
29764
|
if (!stat3 || stat3.isSymbolicLink()) return;
|
|
28820
29765
|
if (stat3.isDirectory()) {
|
|
28821
29766
|
if (depth > 8 || out.length > 4e3) return;
|
|
28822
|
-
for (const name2 of (await readdir6(
|
|
29767
|
+
for (const name2 of (await readdir6(join18(root, relative))).sort()) await visit2(relative + "/" + name2, depth + 1);
|
|
28823
29768
|
} else if (stat3.isFile()) out.push({ path: posix(relative), bytes: stat3.size });
|
|
28824
29769
|
}
|
|
28825
29770
|
await visit2(ASSETS_DIR, 0);
|
|
@@ -28828,7 +29773,7 @@ async function listAssets(root) {
|
|
|
28828
29773
|
async function readDocumentWarnings(root, documents) {
|
|
28829
29774
|
const outside = [...new Set(documentBodyLinks(documents).map((l) => l.target).filter((t3) => !t3.startsWith(".gitifact/") || t3.startsWith(INSTRUCTIONS_ROOT + "/")))];
|
|
28830
29775
|
const existing = /* @__PURE__ */ new Set();
|
|
28831
|
-
for (const target of outside) if ((await info2(
|
|
29776
|
+
for (const target of outside) if ((await info2(join18(root, ...target.split("/"))))?.isFile()) existing.add(target);
|
|
28832
29777
|
return documentWarnings(documents, await listAssets(root), existing);
|
|
28833
29778
|
}
|
|
28834
29779
|
|
|
@@ -28852,30 +29797,30 @@ var runCheck = (options) => runCommand("check", options.format, async () => {
|
|
|
28852
29797
|
|
|
28853
29798
|
// src/commands/changes.ts
|
|
28854
29799
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
28855
|
-
import { mkdir as mkdir6, open as
|
|
28856
|
-
import { join as
|
|
29800
|
+
import { mkdir as mkdir6, open as open5, readFile as readFile16, rename as rename3, rmdir, unlink as unlink4, writeFile as writeFile4 } from "node:fs/promises";
|
|
29801
|
+
import { join as join21 } from "node:path";
|
|
28857
29802
|
|
|
28858
29803
|
// src/adapters/filesystem/agent-inputs.ts
|
|
28859
|
-
import { createHash as
|
|
28860
|
-
import { lstat as
|
|
29804
|
+
import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
|
|
29805
|
+
import { lstat as lstat14, mkdir as mkdir5, readdir as readdir7, realpath as realpath3, unlink as unlink3, writeFile as writeFile3 } from "node:fs/promises";
|
|
28861
29806
|
import { tmpdir } from "node:os";
|
|
28862
|
-
import { basename, dirname as dirname2, join as
|
|
29807
|
+
import { basename, dirname as dirname2, join as join19 } from "node:path";
|
|
28863
29808
|
var maxAge = 7 * 24 * 60 * 60 * 1e3;
|
|
28864
|
-
var info3 = (path3) =>
|
|
29809
|
+
var info3 = (path3) => lstat14(path3).catch((e) => {
|
|
28865
29810
|
if (e.code === "ENOENT") return void 0;
|
|
28866
29811
|
throw e;
|
|
28867
29812
|
});
|
|
28868
29813
|
var comparable = (path3) => process.platform === "win32" ? path3.toLowerCase() : path3;
|
|
28869
29814
|
async function candidates(root, controls) {
|
|
28870
|
-
const key =
|
|
28871
|
-
return { system:
|
|
29815
|
+
const key = createHash4("sha256").update(comparable(await realpath3(root))).digest("hex").slice(0, 16);
|
|
29816
|
+
return { system: join19((controls.tmpdir ?? tmpdir)(), "gitifact", key), project: join19(root, ".gitifact", "tmp") };
|
|
28872
29817
|
}
|
|
28873
29818
|
async function writable(directory) {
|
|
28874
29819
|
try {
|
|
28875
29820
|
await mkdir5(directory, { recursive: true });
|
|
28876
29821
|
const stat3 = await info3(directory);
|
|
28877
29822
|
if (!stat3?.isDirectory() || stat3.isSymbolicLink()) return false;
|
|
28878
|
-
const probe =
|
|
29823
|
+
const probe = join19(directory, ".probe-" + randomBytes2(8).toString("hex"));
|
|
28879
29824
|
await writeFile3(probe, "", { flag: "wx" });
|
|
28880
29825
|
await unlink3(probe);
|
|
28881
29826
|
return true;
|
|
@@ -28889,7 +29834,7 @@ async function prepareAgentInputs(root, controls = {}) {
|
|
|
28889
29834
|
if (await writable(system)) directory = system;
|
|
28890
29835
|
else if (await writable(project)) {
|
|
28891
29836
|
directory = project;
|
|
28892
|
-
await writeFile3(
|
|
29837
|
+
await writeFile3(join19(project, ".gitignore"), "*\n", { flag: "wx" }).catch((e) => {
|
|
28893
29838
|
if (e.code !== "EEXIST") throw e;
|
|
28894
29839
|
});
|
|
28895
29840
|
}
|
|
@@ -28897,10 +29842,10 @@ async function prepareAgentInputs(root, controls = {}) {
|
|
|
28897
29842
|
const now = (controls.now ?? Date.now)();
|
|
28898
29843
|
for (const name2 of await readdir7(directory).catch(() => [])) {
|
|
28899
29844
|
if (name2 === ".gitignore") continue;
|
|
28900
|
-
const stat3 = await info3(
|
|
28901
|
-
if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await unlink3(
|
|
29845
|
+
const stat3 = await info3(join19(directory, name2)).catch(() => void 0);
|
|
29846
|
+
if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await unlink3(join19(directory, name2)).catch(() => void 0);
|
|
28902
29847
|
}
|
|
28903
|
-
return { commit:
|
|
29848
|
+
return { commit: join19(directory, "commit.json") };
|
|
28904
29849
|
}
|
|
28905
29850
|
async function discardAgentInput(root, file2, controls = {}) {
|
|
28906
29851
|
try {
|
|
@@ -28921,18 +29866,18 @@ async function discardAgentInput(root, file2, controls = {}) {
|
|
|
28921
29866
|
}
|
|
28922
29867
|
|
|
28923
29868
|
// src/commands/commit-files.ts
|
|
28924
|
-
import { createHash as
|
|
28925
|
-
import { lstat as
|
|
28926
|
-
import { join as
|
|
29869
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
29870
|
+
import { lstat as lstat15, readFile as readFile14 } from "node:fs/promises";
|
|
29871
|
+
import { join as join20 } from "node:path";
|
|
28927
29872
|
var fail3 = (message) => {
|
|
28928
29873
|
throw new CommandError("INVALID_COMMIT", message);
|
|
28929
29874
|
};
|
|
28930
|
-
var hash2 = (value) =>
|
|
29875
|
+
var hash2 = (value) => createHash5("sha256").update(value).digest("hex");
|
|
28931
29876
|
var optional2 = (path3) => readFile14(path3).catch((e) => {
|
|
28932
29877
|
if (e.code === "ENOENT") return null;
|
|
28933
29878
|
throw e;
|
|
28934
29879
|
});
|
|
28935
|
-
var info4 = (path3) =>
|
|
29880
|
+
var info4 = (path3) => lstat15(path3).catch((e) => {
|
|
28936
29881
|
if (e.code === "ENOENT") return void 0;
|
|
28937
29882
|
throw e;
|
|
28938
29883
|
});
|
|
@@ -28958,11 +29903,11 @@ async function fingerprint(root, path3) {
|
|
|
28958
29903
|
validPath2(path3);
|
|
28959
29904
|
const parts = path3.split("/");
|
|
28960
29905
|
for (let i = 1; i <= parts.length; i++) {
|
|
28961
|
-
const target =
|
|
29906
|
+
const target = join20(root, ...parts.slice(0, i));
|
|
28962
29907
|
const stat3 = await info4(target);
|
|
28963
29908
|
if (!stat3) return null;
|
|
28964
29909
|
if (stat3.isSymbolicLink() || (i < parts.length ? !stat3.isDirectory() : !stat3.isFile())) fail3(t2("commitFiles.linkOrDirectory"));
|
|
28965
|
-
if (i < parts.length && await info4(
|
|
29910
|
+
if (i < parts.length && await info4(join20(target, ".git"))) fail3(t2("commitFiles.nestedRepository"));
|
|
28966
29911
|
if (i === parts.length) {
|
|
28967
29912
|
if (stat3.nlink !== 1 || stat3.size > 16 * 1024 * 1024) fail3(t2("commitFiles.hardLinkOrSize"));
|
|
28968
29913
|
return hash2(Buffer.concat([Buffer.from(String(stat3.mode & 73) + "\0"), await readFile14(target)]));
|
|
@@ -29029,18 +29974,12 @@ async function readStdin(stream) {
|
|
|
29029
29974
|
var fields = ["paths", "message", "authorization", "migration"];
|
|
29030
29975
|
var integration = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply"];
|
|
29031
29976
|
var MIGRATION_TARGET = "0.8.0";
|
|
29977
|
+
var PATH_LIMIT = 128;
|
|
29978
|
+
var MIGRATION_PATH_LIMIT = 5e3;
|
|
29032
29979
|
var same2 = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
29033
29980
|
var problemLines2 = (problems) => problems.map((p) => " " + p.code + " " + p.message);
|
|
29034
29981
|
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
|
-
};
|
|
29982
|
+
var pendingChanges = (project, head) => workingChanges(project, head);
|
|
29044
29983
|
var shared = (changes2, records2) => changes2.map((c) => c.id).filter((id) => records2.filter((r) => r.docs.includes(id)).length > 1);
|
|
29045
29984
|
var changeLine = (c) => `${c.types.join(",").padEnd(9)} ${c.id} ${c.title} \u2014 ${c.path}${c.previousPath ? " (\u2190 " + c.previousPath + ")" : ""}`;
|
|
29046
29985
|
var recordLine = (r, changes2) => {
|
|
@@ -29087,7 +30026,7 @@ var runChangesCommit = (options, controls = {}) => runCommand("changes", options
|
|
|
29087
30026
|
async function commitChanges(cwd, input2, dryRun) {
|
|
29088
30027
|
const request = object3(input2);
|
|
29089
30028
|
if (Object.keys(request).some((k) => !fields.includes(k))) fail3(t2("commit.unknownField"));
|
|
29090
|
-
const selected2 = paths(request.paths);
|
|
30029
|
+
const selected2 = paths(request.paths, request.migration === true ? MIGRATION_PATH_LIMIT : PATH_LIMIT);
|
|
29091
30030
|
const authorization = object3(request.authorization);
|
|
29092
30031
|
if (Object.keys(authorization).sort().join(",") !== "basis,evidence" || !["user-request", "project-policy"].includes(String(authorization.basis))) fail3(t2("commit.authorizationRequired"));
|
|
29093
30032
|
text3(authorization.evidence, 2e3);
|
|
@@ -29118,7 +30057,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29118
30057
|
...stdin ? { input: stdin } : {}
|
|
29119
30058
|
});
|
|
29120
30059
|
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 =
|
|
30060
|
+
const busy = join21(gitDir, "gitifact-changes-commit.lock");
|
|
29122
30061
|
if (await info4(busy)) fail3(t2("commit.busy", { path: busy }));
|
|
29123
30062
|
if ((await staged()).length) fail3(t2("commit.existingStaging"));
|
|
29124
30063
|
const indexHash = hash2(await optional2(indexPath) ?? Buffer.alloc(0));
|
|
@@ -29157,7 +30096,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29157
30096
|
if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
|
|
29158
30097
|
return { outcome: "dry-run", committed: false, ...summary, commit: void 0 };
|
|
29159
30098
|
}
|
|
29160
|
-
const temporary =
|
|
30099
|
+
const temporary = join21(gitDir, "gitifact-commit-index-" + randomUUID5());
|
|
29161
30100
|
let owned = false;
|
|
29162
30101
|
let uncertain = false;
|
|
29163
30102
|
let commitStarted = false;
|
|
@@ -29171,16 +30110,16 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29171
30110
|
if (e.code === "EEXIST") fail3(t2("commit.busy", { path: busy }));
|
|
29172
30111
|
throw e;
|
|
29173
30112
|
}
|
|
29174
|
-
const lock = indexLock = await
|
|
30113
|
+
const lock = indexLock = await open5(indexPath + ".lock", "wx", 384);
|
|
29175
30114
|
const original = await optional2(indexPath);
|
|
29176
30115
|
if (hash2(original ?? Buffer.alloc(0)) !== indexHash || (await staged()).length) fail3(t2("commit.indexChanged"));
|
|
29177
30116
|
const before = await hashes([...started.keys()]);
|
|
29178
30117
|
for (const [path3, value] of started) if (before.get(path3) !== value) fail3(t2("commit.fileChangedRunning", { path: path3 }));
|
|
29179
|
-
await writeFile4(
|
|
30118
|
+
await writeFile4(join21(busy, "recovery.json"), JSON.stringify({ before: base, originalIndex: original?.toString("base64") ?? null, temporary, paths: selected2, message }));
|
|
29180
30119
|
const locked = await hashes([.../* @__PURE__ */ new Set([...selected2, ...contextPaths])]);
|
|
29181
30120
|
if (original) await writeFile4(temporary, original, { flag: "wx" });
|
|
29182
30121
|
else await git(["read-tree", "--empty"], temporary);
|
|
29183
|
-
await git(["add", "--",
|
|
30122
|
+
await git(["add", "--pathspec-from-file=-", "--pathspec-file-nul"], temporary, Buffer.from(stageable.join("\0")));
|
|
29184
30123
|
const actual = await staged(temporary);
|
|
29185
30124
|
if (!actual.length || actual.some((p) => !selected2.includes(p))) fail3(t2("commit.unexpectedFiles"));
|
|
29186
30125
|
const storeFiles = selected2.filter((p) => record3(p) && locked.get(p) !== null);
|
|
@@ -29193,18 +30132,18 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29193
30132
|
const size = Number(header[2]);
|
|
29194
30133
|
if (end < 0 || header[1] !== "blob" || !Number.isSafeInteger(size) || output2[end + 1 + size] !== 10) fail3(t2("commit.stagedUnreadable", { path: path3 }));
|
|
29195
30134
|
const blob = output2.subarray(end + 1, end + 1 + size);
|
|
29196
|
-
const file2 = await readFile16(
|
|
30135
|
+
const file2 = await readFile16(join21(root, path3));
|
|
29197
30136
|
if (!blob.equals(file2) && !blob.equals(Buffer.from(file2.toString("latin1").replace(/\r\n/g, "\n"), "latin1"))) fail3(t2("commit.filterChanged"));
|
|
29198
30137
|
offset = end + size + 2;
|
|
29199
30138
|
}
|
|
29200
30139
|
}
|
|
29201
30140
|
const again = await hashes([...locked.keys()]);
|
|
29202
30141
|
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(
|
|
30142
|
+
for (const name2 of integration) if (await info4(join21(gitDir, name2))) fail3(t2("commit.integrationStarted"));
|
|
29204
30143
|
if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
|
|
29205
30144
|
const tree = (await git(["write-tree"], temporary)).toString("utf8").trim();
|
|
29206
30145
|
commitStarted = true;
|
|
29207
|
-
await git(["commit", "-
|
|
30146
|
+
await git(["commit", "-F", "-"], temporary, Buffer.from(message.trim() + (trailers.length ? "\n\n" + trailers.join("\n") : "") + "\n"));
|
|
29208
30147
|
const after = await project.reader.baseline();
|
|
29209
30148
|
if (!after.head) return fail3(t2("commit.resultUnknown"));
|
|
29210
30149
|
const raw = (await git(["cat-file", "commit", after.head])).toString("utf8");
|
|
@@ -29240,7 +30179,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29240
30179
|
await unlink4(temporary).catch(() => {
|
|
29241
30180
|
});
|
|
29242
30181
|
if (owned) {
|
|
29243
|
-
await unlink4(
|
|
30182
|
+
await unlink4(join21(busy, "recovery.json")).catch(() => {
|
|
29244
30183
|
});
|
|
29245
30184
|
await rmdir(busy);
|
|
29246
30185
|
}
|
|
@@ -29251,7 +30190,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29251
30190
|
|
|
29252
30191
|
// src/commands/records.ts
|
|
29253
30192
|
import { readFile as readFile17 } from "node:fs/promises";
|
|
29254
|
-
import { join as
|
|
30193
|
+
import { join as join22 } from "node:path";
|
|
29255
30194
|
var runRecordsNew = (options) => runCommand("records", options.format, async () => {
|
|
29256
30195
|
if (/[\r\n]/.test(options.title)) throw new CommandError("INVALID_VALUE", t2("docs.singleLine", { field: "title" }));
|
|
29257
30196
|
const docs = [...new Set(options.docs.flatMap((d) => d.split(",")).map((d) => d.trim()).filter(Boolean))];
|
|
@@ -29287,7 +30226,7 @@ var runRecordsShow = (ids, options) => runCommand("records", options.format, asy
|
|
|
29287
30226
|
const shown = await Promise.all([...new Set(ids)].map(async (id) => {
|
|
29288
30227
|
const path3 = await findRecord(project.root, id);
|
|
29289
30228
|
if (!path3) return { id, missing: true };
|
|
29290
|
-
const source2 = await readFile17(
|
|
30229
|
+
const source2 = await readFile17(join22(project.root, ...path3.split("/")), "utf8");
|
|
29291
30230
|
const log = project.reader.decode(await project.reader.run(["log", "-1", "--diff-filter=A", "--format=%H%x00%aN%x00%aI", "--", path3])).trim();
|
|
29292
30231
|
const [commit, author, date5] = log.split("\0");
|
|
29293
30232
|
return { id, path: path3, text: source2, commit: commit ? { commit, author, date: date5 } : null };
|
|
@@ -29312,18 +30251,17 @@ var runRecordsList = (options) => runCommand("records", options.format, async ()
|
|
|
29312
30251
|
const fields2 = checkFields(options.fields, options.doc === void 0 ? recordColumns : eventColumns);
|
|
29313
30252
|
const project = await openProject(process.cwd());
|
|
29314
30253
|
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
30254
|
const sectionText = (sections2) => sections2.map((s) => s.body);
|
|
29320
30255
|
if (options.doc !== void 0) {
|
|
30256
|
+
const after = await since(project, options.since, head);
|
|
30257
|
+
const by = byAuthor(options.author);
|
|
30258
|
+
const has2 = contains(options.q);
|
|
29321
30259
|
const id = options.doc;
|
|
29322
30260
|
const events = head ? await project.cache.history.ofDocument(head, id) : [];
|
|
29323
30261
|
const { documents } = await project.cache.documents.list();
|
|
29324
30262
|
const title = documents.find((d) => d.id === id)?.title ?? events.map((e) => (e.after ?? e.before)?.title).find(Boolean) ?? null;
|
|
29325
30263
|
if (!title && !events.length) throw new CommandError("UNKNOWN_DOCUMENT", t2("docs.unknownDocument", { ids: id }));
|
|
29326
|
-
const
|
|
30264
|
+
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
30265
|
commit: e.commit,
|
|
29328
30266
|
date: e.date,
|
|
29329
30267
|
author: e.author,
|
|
@@ -29332,10 +30270,13 @@ var runRecordsList = (options) => runCommand("records", options.format, async ()
|
|
|
29332
30270
|
types: e.types,
|
|
29333
30271
|
path: (e.after ?? e.before)?.path ?? null,
|
|
29334
30272
|
records: e.records
|
|
29335
|
-
})));
|
|
30273
|
+
})), (e) => e.commit, options);
|
|
30274
|
+
const rows2 = page.rows;
|
|
30275
|
+
const paging2 = { total: page.total, next: page.next, unit: "commit" };
|
|
30276
|
+
const more2 = pageLine(page, 0, t2("list.commits"));
|
|
29336
30277
|
if (fields2) {
|
|
29337
30278
|
const picked = selected(rows2, fields2);
|
|
29338
|
-
return { json: { doc: { id, title }, events: picked.json }, text: picked.text };
|
|
30279
|
+
return { json: { doc: { id, title }, events: picked.json, page: paging2 }, text: picked.text + aside(options.format, more2) };
|
|
29339
30280
|
}
|
|
29340
30281
|
const out2 = [`${id} ${title ?? ""}`.trimEnd()];
|
|
29341
30282
|
for (const e of rows2) {
|
|
@@ -29348,20 +30289,35 @@ var runRecordsList = (options) => runCommand("records", options.format, async ()
|
|
|
29348
30289
|
out2.push(" " + t2("docs.commit") + ": " + e.message);
|
|
29349
30290
|
}
|
|
29350
30291
|
if (!rows2.length) out2.push(events.length ? t2("docs.noMatch") : t2("docs.noHistory"));
|
|
29351
|
-
|
|
30292
|
+
if (more2.length) out2.push("", ...more2);
|
|
30293
|
+
return { json: { doc: { id, title }, events: rows2, page: paging2 }, text: text2(out2) };
|
|
29352
30294
|
}
|
|
29353
|
-
const
|
|
30295
|
+
const has = contains(options.q);
|
|
30296
|
+
const pending = (options.author !== void 0 ? [] : [...(await project.pendingRecords()).files].flatMap(([path3, source2]) => {
|
|
29354
30297
|
try {
|
|
29355
30298
|
return [parseRecordFile(path3, source2)];
|
|
29356
30299
|
} catch {
|
|
29357
30300
|
return [];
|
|
29358
30301
|
}
|
|
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
|
|
30302
|
+
}).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);
|
|
30303
|
+
const size = options.all ? void 0 : options.limit ?? PAGE_SIZE;
|
|
30304
|
+
const inPending = options.after === void 0 ? 0 : pending.findIndex((r) => r.id === options.after) + 1;
|
|
30305
|
+
const afterCommitted = options.after !== void 0 && !inPending ? options.after : void 0;
|
|
30306
|
+
const pendingShown = afterCommitted !== void 0 ? [] : pending.slice(inPending, size === void 0 ? void 0 : inPending + size);
|
|
30307
|
+
const filter = { author: options.author, q: options.q, ...await sinceRange(project, options.since, head) };
|
|
30308
|
+
const room = size === void 0 ? void 0 : size - pendingShown.length;
|
|
30309
|
+
const found = head ? await project.cache.history.records(head, filter, afterCommitted, room) : { records: [], total: 0 };
|
|
30310
|
+
if (!found) throw new CommandError("INVALID_VALUE", t2("list.after", { cursor: options.after }));
|
|
30311
|
+
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 }));
|
|
30312
|
+
const rows = [...pendingShown, ...committed2];
|
|
30313
|
+
const total = pending.length + found.total;
|
|
30314
|
+
const before = afterCommitted === void 0 ? inPending : pending.length + await readThrough(project, head, filter, afterCommitted);
|
|
30315
|
+
const next = before + rows.length < total && rows.length ? rows[rows.length - 1].id : null;
|
|
30316
|
+
const paging = { total, next, unit: "record" };
|
|
30317
|
+
const more = pageLine({ rows, total, next }, before, t2("list.records"));
|
|
29362
30318
|
if (fields2) {
|
|
29363
30319
|
const picked = selected(rows, fields2);
|
|
29364
|
-
return { json: { records: picked.json }, text: picked.text };
|
|
30320
|
+
return { json: { records: picked.json, page: paging }, text: picked.text + aside(options.format, more) };
|
|
29365
30321
|
}
|
|
29366
30322
|
const docs = (ids) => ids.slice(0, 5).join(", ") + (ids.length > 5 ? " " + t2("records.moreDocs", { count: ids.length - 5 }) : "");
|
|
29367
30323
|
const out = rows.flatMap((r) => [
|
|
@@ -29369,13 +30325,18 @@ var runRecordsList = (options) => runCommand("records", options.format, async ()
|
|
|
29369
30325
|
` ${r.commit ? `${r.date.slice(0, 10)} ${r.commit.slice(0, 7)} ${r.author}` : t2("records.uncommitted")} \xB7 ${docs(r.docs)}`
|
|
29370
30326
|
]);
|
|
29371
30327
|
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")]) };
|
|
30328
|
+
return { json: { records: rows, page: paging }, text: text2(out.length ? [...out, ...more] : [filtered ? t2("docs.noMatch") : t2("records.empty")]) };
|
|
29373
30329
|
});
|
|
30330
|
+
async function readThrough(project, head, filter, id) {
|
|
30331
|
+
if (!head) return 0;
|
|
30332
|
+
const all = await project.cache.history.records(head, filter, void 0, void 0);
|
|
30333
|
+
return (all?.records.findIndex((r) => r.id === id) ?? -1) + 1;
|
|
30334
|
+
}
|
|
29374
30335
|
|
|
29375
30336
|
// src/commands/feedback.ts
|
|
29376
30337
|
import { readFile as readFile18 } from "node:fs/promises";
|
|
29377
30338
|
import { release as release3 } from "node:os";
|
|
29378
|
-
import { dirname as dirname3, join as
|
|
30339
|
+
import { dirname as dirname3, join as join23 } from "node:path";
|
|
29379
30340
|
|
|
29380
30341
|
// src/adapters/github/issue.ts
|
|
29381
30342
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -29442,7 +30403,7 @@ function parseFeedback(input2) {
|
|
|
29442
30403
|
}
|
|
29443
30404
|
async function schemaVersion(from) {
|
|
29444
30405
|
for (let folder = from; ; folder = dirname3(folder)) {
|
|
29445
|
-
const config2 = await readFile18(
|
|
30406
|
+
const config2 = await readFile18(join23(folder, ".gitifact", "config.json"), "utf8").catch(() => void 0);
|
|
29446
30407
|
if (config2 !== void 0) {
|
|
29447
30408
|
try {
|
|
29448
30409
|
const value = JSON.parse(config2).schemaVersion;
|
|
@@ -29520,7 +30481,7 @@ function agentDocsGit(root, inherited = process.env) {
|
|
|
29520
30481
|
|
|
29521
30482
|
// src/commands/update.ts
|
|
29522
30483
|
var blockCommitMessage = (version2) => "chore(gitifact): refresh GITIFACT block to v" + version2;
|
|
29523
|
-
var commitState = (
|
|
30484
|
+
var commitState = (state2, extra = {}) => ({ state: state2, commit: null, paths: [], message: null, reason: null, detail: null, ...extra });
|
|
29524
30485
|
async function refreshBlocks(cwd, version2, env, controls) {
|
|
29525
30486
|
const repo = initRepository(cwd, env);
|
|
29526
30487
|
let first;
|
|
@@ -29650,10 +30611,10 @@ async function runUpdateCheck(format2, version2) {
|
|
|
29650
30611
|
process.stdout.write(JSON.stringify(dto) + "\n");
|
|
29651
30612
|
return;
|
|
29652
30613
|
}
|
|
29653
|
-
const
|
|
30614
|
+
const state2 = dto.update;
|
|
29654
30615
|
const lines = [
|
|
29655
30616
|
t2("update.text.current", { version: dto.cliVersion }),
|
|
29656
|
-
|
|
30617
|
+
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
30618
|
];
|
|
29658
30619
|
if (dto.command) lines.push(t2("update.text.install", { command: dto.command }));
|
|
29659
30620
|
process.stdout.write(lines.join("\n") + "\n");
|
|
@@ -29668,15 +30629,15 @@ for (let i = 0; i < languageArgs.length; i++) {
|
|
|
29668
30629
|
if (value === "ko" || value === "en") selectedLanguage = value;
|
|
29669
30630
|
}
|
|
29670
30631
|
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.
|
|
30632
|
+
var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.8.2").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
|
|
29672
30633
|
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.
|
|
30634
|
+
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.2"));
|
|
30635
|
+
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.2"));
|
|
30636
|
+
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.2") : runUpdate(options, "0.8.2"));
|
|
29676
30637
|
var listed2 = (command, sorts) => {
|
|
29677
30638
|
command.allowExcessArguments(false).option("--q <words>", t2("help.listQ")).option("--author <name>", t2("help.listAuthor"));
|
|
29678
30639
|
if (sorts) command.addOption(new Option("--sort <key>", t2("help.listSort")).choices([...sorts]).default(sorts[0]));
|
|
29679
|
-
return command.
|
|
30640
|
+
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
30641
|
};
|
|
29681
30642
|
var specs = program2.command("specs").description(t2("help.specs"));
|
|
29682
30643
|
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 +30655,7 @@ program2.command("check").description(t2("help.check")).allowExcessArguments(fal
|
|
|
29694
30655
|
var changes = program2.command("changes").description(t2("help.changes"));
|
|
29695
30656
|
changes.command("list").description(t2("help.changesList")).allowExcessArguments(false).addOption(format()).action((o) => runChangesList(o));
|
|
29696
30657
|
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.
|
|
30658
|
+
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.2"));
|
|
29698
30659
|
var guide = program2.command("guide").description(t2("help.guide"));
|
|
29699
30660
|
guide.command("list").description(t2("help.guideList")).allowExcessArguments(false).addOption(format()).action((o) => runGuideList(o));
|
|
29700
30661
|
guide.command("show").description(t2("help.guideShow")).argument("<topic>", t2("help.guideTopic")).allowExcessArguments(false).addOption(format()).action((topic, o) => runGuideShow(topic, o));
|