gitifact 0.8.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -37
- package/dist/THIRD_PARTY_NOTICES.txt +26 -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/HgiRefresh-UIdBFt6N.js +1 -0
- package/dist/browser/assets/{HoverCard-D6keobP0.js → HoverCard-Bpt8LVFT.js} +1 -1
- 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-Droxhk-G.js → Markdown-CR_AHfxJ.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-CPFOT7Wj.js +2 -0
- package/dist/browser/assets/Tab-DL11wx1W.js +1 -0
- package/dist/browser/assets/{Table-D4v8xCrw.js → Table-BK5793GP.js} +3 -3
- package/dist/browser/assets/{TimestampHoverCard-qfGbCkoP.js → TimestampHoverCard-B5JBaQDZ.js} +1 -1
- package/dist/browser/assets/Token-BnQGlhSv.js +2 -0
- package/dist/browser/assets/Token-Bqkc8eyB.css +1 -0
- package/dist/browser/assets/{Tooltip-QZg2pB5o.js → Tooltip-MoU3alsv.js} +1 -1
- package/dist/browser/assets/about-3BiGgi3V.js +3 -0
- package/dist/browser/assets/activity-timeline-CCrOFWbb.js +2 -0
- package/dist/browser/assets/{appearance-CDQ4fLdE.js → appearance-DyJA06C4.js} +1 -1
- package/dist/browser/assets/changelog-DMiOCVuF.js +1 -0
- package/dist/browser/assets/commit-CMw0y1Oj.js +4 -0
- package/dist/browser/assets/{container.stylex-6OsSOpKw.js → container.stylex-rwI38dw9.js} +1 -1
- package/dist/browser/assets/contributor-Cg_cxulE.js +1 -0
- package/dist/browser/assets/contributors-ol5bZm_T.js +1 -0
- package/dist/browser/assets/contributors._email-BoTYVUTP.js +1 -0
- package/dist/browser/assets/contributors.index-DrdKDOYt.js +1 -0
- package/dist/browser/assets/dashboard.index-BUzgcF91.js +1 -0
- package/dist/browser/assets/document-C2htw6u5.js +2 -0
- package/dist/browser/assets/document-Cmx0e6kz.css +1 -0
- package/dist/browser/assets/document-CteWuasx.js +19 -0
- package/dist/browser/assets/features-DUHxz0_L.css +1 -0
- package/dist/browser/assets/features-haTu80rD.js +2 -0
- package/dist/browser/assets/features._featureId-CHZA4Y9C.js +1 -0
- package/dist/browser/assets/features.index-BY8ScP1y.js +1 -0
- package/dist/browser/assets/getting-started-GU6t7LF-.js +1 -0
- package/dist/browser/assets/git-D2zVLj_w.js +1 -0
- package/dist/browser/assets/index-D1JeDnJc.css +1 -0
- package/dist/browser/assets/index-DaKOj2qL.js +49 -0
- package/dist/browser/assets/instructions-mU0nkIyN.js +1 -0
- package/dist/browser/assets/instructions._instructionId-D-pztPg5.js +1 -0
- package/dist/browser/assets/instructions.agents-WcmyZITb.js +1 -0
- package/dist/browser/assets/instructions.index-BEJLVyZD.js +1 -0
- package/dist/browser/assets/lazyRouteComponent-DcCLQOpn.js +1 -0
- package/dist/browser/assets/load-more-YVDkQmcp.js +1 -0
- package/dist/browser/assets/lottie_light-DSIvQuvH.js +2 -0
- package/dist/browser/assets/page-header-DTEyUWBS.css +1 -0
- package/dist/browser/assets/page-header-rHElc4Gv.js +246 -0
- package/dist/browser/assets/records._recordId-CQAy7eOv.js +1 -0
- package/dist/browser/assets/records.commits._commit-BP1KDLvt.js +1 -0
- package/dist/browser/assets/records.index-CZ6iNU57.js +1 -0
- package/dist/browser/assets/records.working-Bv9veUIm.js +1 -0
- package/dist/browser/assets/rocket-loader-CMSP4Jri.js +1 -0
- package/dist/browser/assets/search-CSSuO-jR.js +1 -0
- package/dist/browser/assets/settings-D2ZdL-Vu.js +1 -0
- 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-guh_cfSb.js +1 -0
- package/dist/browser/assets/{useIsomorphicLayoutEffect-DByj3svz.js → useIsomorphicLayoutEffect-BiEJxDg_.js} +1 -1
- package/dist/browser/assets/useKeyboardHint-BD_Jlpwp.js +1 -0
- 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 +46 -25
- package/dist/browser/licenses/rocket-in-space-lottie.txt +29 -0
- package/dist/i18n/en/block.md +4 -6
- package/dist/i18n/en/changelog.md +27 -0
- package/dist/i18n/en/docs/commit.md +1 -1
- package/dist/i18n/en/docs/workflow.md +7 -7
- package/dist/i18n/ko/block.md +4 -6
- package/dist/i18n/ko/changelog.md +27 -0
- package/dist/i18n/ko/docs/commit.md +1 -1
- package/dist/i18n/ko/docs/workflow.md +5 -5
- package/dist/main.js +1279 -559
- package/package.json +1 -1
- package/dist/browser/assets/Banner-c1hFCLs8.js +0 -1
- package/dist/browser/assets/Grid-BrUBBmhu.js +0 -1
- package/dist/browser/assets/ProgressBarMarkTooltip-C5Moep_o.js +0 -1
- package/dist/browser/assets/Selector-D_xYW7R_.js +0 -2
- package/dist/browser/assets/Tab-VVHagF2Z.js +0 -1
- package/dist/browser/assets/Token-BLd8-jfF.js +0 -1
- package/dist/browser/assets/about-DeUi-_2z.js +0 -3
- package/dist/browser/assets/activity-timeline-bFoqFwVu.js +0 -2
- package/dist/browser/assets/changelog-Brd1Ninm.js +0 -2
- package/dist/browser/assets/changelog-CxmMo2wk.css +0 -1
- package/dist/browser/assets/commit-CiMtFlZl.js +0 -4
- package/dist/browser/assets/contributor-C_IskSHq.js +0 -1
- package/dist/browser/assets/contributors-CbwXLc7M.js +0 -1
- package/dist/browser/assets/contributors._email-BRfre3dw.js +0 -1
- package/dist/browser/assets/contributors.index-CV84owW7.js +0 -1
- package/dist/browser/assets/dashboard-CMSj2wu3.css +0 -1
- package/dist/browser/assets/dashboard.index-CQdr7hsf.js +0 -1
- package/dist/browser/assets/document-BZmiLx-e.js +0 -2
- package/dist/browser/assets/document-DEGaf2yT.css +0 -1
- package/dist/browser/assets/document-JLY2-z8S.js +0 -19
- package/dist/browser/assets/features-Cj41kcXf.js +0 -4
- package/dist/browser/assets/features-g3j2elTj.css +0 -1
- package/dist/browser/assets/features._featureId-BgFHsRcy.js +0 -1
- package/dist/browser/assets/features.index-Ca0rzFW6.js +0 -1
- package/dist/browser/assets/getting-started-CgL3Vi9o.js +0 -1
- package/dist/browser/assets/git-B7oxggGB.js +0 -1
- package/dist/browser/assets/git-D_wcK2vC.css +0 -1
- package/dist/browser/assets/index-CmU7dwFQ.css +0 -1
- package/dist/browser/assets/index-DWRGm9YO.js +0 -48
- package/dist/browser/assets/instructions-D3cdAPD3.js +0 -1
- package/dist/browser/assets/instructions._instructionId-BkT14646.js +0 -1
- package/dist/browser/assets/instructions.agents-B3jLRl1u.js +0 -1
- package/dist/browser/assets/instructions.index-8qlhSXoy.js +0 -1
- package/dist/browser/assets/lazyRouteComponent-JkRa2CHo.js +0 -1
- package/dist/browser/assets/page-header-CPL7myjo.js +0 -1
- package/dist/browser/assets/page-header-atX8Nsmd.css +0 -1
- package/dist/browser/assets/records-Dk3shbYl.css +0 -1
- package/dist/browser/assets/records-page-B4Al3fIQ.js +0 -1
- package/dist/browser/assets/records-page-D2F1WJrh.css +0 -1
- package/dist/browser/assets/records._recordId-Chn1SO5R.js +0 -1
- package/dist/browser/assets/records.commits._commit-D4Wk4i_m.js +0 -1
- package/dist/browser/assets/records.index-CViTeD08.js +0 -1
- package/dist/browser/assets/records.working-BsHKMIz-.js +0 -1
- package/dist/browser/assets/request-state-oiWyP9c-.js +0 -1
- package/dist/browser/assets/search-BCYi0CBz.js +0 -1
- package/dist/browser/assets/search-palette-D0ctICJy.js +0 -561
- package/dist/browser/assets/search-palette-DpmGOAIg.css +0 -1
- package/dist/browser/assets/settings-BDVKHrS8.js +0 -1
- package/dist/browser/assets/useDevWarning-gW8_bNZ_.js +0 -1
- package/dist/browser/assets/useInfiniteQuery-Bn_R13Ih.js +0 -1
- package/dist/browser/assets/useKeyboardHint-lSxUw5Qj.js +0 -1
package/dist/main.js
CHANGED
|
@@ -3407,6 +3407,8 @@ var messages_default = {
|
|
|
3407
3407
|
"config.invalidFields": "\uD504\uB85C\uC81D\uD2B8 \uC124\uC815\uC758 \uD544\uB4DC\uB098 \uAC12\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
3408
3408
|
"config.jsonUnreadableShort": "\uC124\uC815 JSON\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
3409
3409
|
"config.schemaFields": "schemaVersion\uACFC baseline \uC124\uC815 \uD544\uB4DC\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
3410
|
+
"config.cliVersion": "\uC124\uC815\uC758 cli\uB294 0.8.3 \uAC19\uC740 \uBC30\uD3EC \uBC84\uC804\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
|
|
3411
|
+
"config.language": "\uC124\uC815\uC758 language\uB294 {languages} \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
|
|
3410
3412
|
"document.pathTooLong": "\uC704\uD0A4 \uD398\uC774\uC9C0 \uACBD\uB85C \uAE4A\uC774 \uB610\uB294 \uAE38\uC774 \uD55C\uB3C4\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4: {path}",
|
|
3411
3413
|
"document.fileName": "\uC704\uD0A4 \uD398\uC774\uC9C0 \uD30C\uC77C \uC774\uB984\uC740 \uC18C\uBB38\uC790\xB7\uC22B\uC790\xB7\uD558\uC774\uD508\uACFC .md\uC5EC\uC57C \uD569\uB2C8\uB2E4(\uB8E8\uD2B8\uC758 README.md\xB7ARCHITECTURE.md \uAC19\uC740 \uB300\uBB38\uC790 \uC774\uB984 \uC608\uC678): {path}",
|
|
3412
3414
|
"document.folderName": "\uC704\uD0A4 \uD3F4\uB354 \uC774\uB984\uC740 \uC18C\uBB38\uC790\xB7\uC22B\uC790\xB7\uD558\uC774\uD508\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4: {path}",
|
|
@@ -3524,6 +3526,8 @@ var messages_default2 = {
|
|
|
3524
3526
|
"config.invalidFields": "Invalid project configuration fields or values.",
|
|
3525
3527
|
"config.jsonUnreadableShort": "Could not read configuration JSON.",
|
|
3526
3528
|
"config.schemaFields": "Check the schemaVersion and baseline fields.",
|
|
3529
|
+
"config.cliVersion": "The config field cli must be a release version such as 0.8.3.",
|
|
3530
|
+
"config.language": "The config field language must be one of {languages}.",
|
|
3527
3531
|
"document.pathTooLong": "Wiki page path exceeds the depth or length limit: {path}",
|
|
3528
3532
|
"document.fileName": "Wiki filenames must use lowercase letters, digits, hyphens and .md; uppercase root names such as README.md and ARCHITECTURE.md are allowed: {path}",
|
|
3529
3533
|
"document.folderName": "Wiki folder names must use lowercase letters, digits and hyphens: {path}",
|
|
@@ -3682,6 +3686,8 @@ var InitError = class extends Error {
|
|
|
3682
3686
|
};
|
|
3683
3687
|
var SCHEMA_VERSION = 3;
|
|
3684
3688
|
var PREVIOUS_SCHEMA_VERSION = 2;
|
|
3689
|
+
var PROJECT_LANGUAGES = ["ko", "en"];
|
|
3690
|
+
var CLI_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
3685
3691
|
var object = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3686
3692
|
var keys = (value, expected) => Object.keys(value).sort().join(",") === expected.sort().join(",");
|
|
3687
3693
|
function parseBaseline(value) {
|
|
@@ -3714,7 +3720,7 @@ function parseManagedConfig(text4) {
|
|
|
3714
3720
|
}
|
|
3715
3721
|
if (!object(value) || !("schemaVersion" in value))
|
|
3716
3722
|
throw new InitError("UNSUPPORTED_FORMAT", t("config.unsupportedFormat"));
|
|
3717
|
-
if (!
|
|
3723
|
+
if (!("baseline" in value))
|
|
3718
3724
|
throw new InitError("INVALID_CONFIG", t("config.schemaFields"));
|
|
3719
3725
|
if (value.schemaVersion !== SCHEMA_VERSION) {
|
|
3720
3726
|
const found = value.schemaVersion;
|
|
@@ -3723,7 +3729,35 @@ function parseManagedConfig(text4) {
|
|
|
3723
3729
|
throw new InitError("UNSUPPORTED_SCHEMA", t("config.migrationRequired"));
|
|
3724
3730
|
throw new InitError("UNSUPPORTED_SCHEMA", newer ? t("config.newerSchema", { version: String(found) }) : t("config.unsupportedSchema", { version: String(found) }));
|
|
3725
3731
|
}
|
|
3726
|
-
|
|
3732
|
+
const config2 = { schemaVersion: SCHEMA_VERSION, baseline: parseBaseline(value.baseline) };
|
|
3733
|
+
if ("cli" in value) {
|
|
3734
|
+
if (typeof value.cli !== "string" || !CLI_VERSION_PATTERN.test(value.cli))
|
|
3735
|
+
throw new InitError("INVALID_CONFIG", t("config.cliVersion"));
|
|
3736
|
+
config2.cli = value.cli;
|
|
3737
|
+
}
|
|
3738
|
+
if ("language" in value) {
|
|
3739
|
+
if (!PROJECT_LANGUAGES.includes(value.language))
|
|
3740
|
+
throw new InitError("INVALID_CONFIG", t("config.language", { languages: PROJECT_LANGUAGES.join(", ") }));
|
|
3741
|
+
config2.language = value.language;
|
|
3742
|
+
}
|
|
3743
|
+
return config2;
|
|
3744
|
+
}
|
|
3745
|
+
function formatManagedConfig(config2, previous) {
|
|
3746
|
+
let unknown2 = {};
|
|
3747
|
+
if (previous !== void 0) {
|
|
3748
|
+
const value = JSON.parse(previous);
|
|
3749
|
+
if (object(value)) {
|
|
3750
|
+
const { schemaVersion: _s, baseline: _b, cli: _c, language: _l, ...rest } = value;
|
|
3751
|
+
unknown2 = rest;
|
|
3752
|
+
}
|
|
3753
|
+
}
|
|
3754
|
+
const known = {
|
|
3755
|
+
schemaVersion: config2.schemaVersion,
|
|
3756
|
+
baseline: config2.baseline,
|
|
3757
|
+
...config2.cli === void 0 ? {} : { cli: config2.cli },
|
|
3758
|
+
...config2.language === void 0 ? {} : { language: config2.language }
|
|
3759
|
+
};
|
|
3760
|
+
return JSON.stringify({ ...known, ...unknown2 }, null, 2) + "\n";
|
|
3727
3761
|
}
|
|
3728
3762
|
|
|
3729
3763
|
// ../../packages/core/dist/formats/links.js
|
|
@@ -10413,15 +10447,15 @@ function bucketFor(state2, inst) {
|
|
|
10413
10447
|
var handoff;
|
|
10414
10448
|
var open = [];
|
|
10415
10449
|
var memo = {
|
|
10416
|
-
alloc(_inst, payload,
|
|
10450
|
+
alloc(_inst, payload, empty2) {
|
|
10417
10451
|
const bucket = handoff;
|
|
10418
10452
|
if (!bucket)
|
|
10419
|
-
return
|
|
10453
|
+
return empty2;
|
|
10420
10454
|
handoff = void 0;
|
|
10421
|
-
const entry2 = { value:
|
|
10455
|
+
const entry2 = { value: empty2, issues: null };
|
|
10422
10456
|
bucket.set(payload.value, entry2);
|
|
10423
10457
|
open.push(entry2);
|
|
10424
|
-
return
|
|
10458
|
+
return empty2;
|
|
10425
10459
|
},
|
|
10426
10460
|
guard(inst) {
|
|
10427
10461
|
var _a3;
|
|
@@ -13092,8 +13126,8 @@ var error20 = () => {
|
|
|
13092
13126
|
case "invalid_union":
|
|
13093
13127
|
return "\u05E7\u05DC\u05D8 \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF";
|
|
13094
13128
|
case "invalid_element": {
|
|
13095
|
-
const
|
|
13096
|
-
return `\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF \u05D1${
|
|
13129
|
+
const place3 = withDefinite(issue2.origin ?? "array");
|
|
13130
|
+
return `\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF \u05D1${place3}`;
|
|
13097
13131
|
}
|
|
13098
13132
|
default:
|
|
13099
13133
|
return `\u05E7\u05DC\u05D8 \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF`;
|
|
@@ -25064,20 +25098,73 @@ var instruction = external_exports.strictObject({
|
|
|
25064
25098
|
var agents = external_exports.strictObject({ path: external_exports.string(), body: external_exports.string(), updatedAt: external_exports.string().nullable() });
|
|
25065
25099
|
var problem = external_exports.strictObject({ code: external_exports.string(), path: external_exports.string(), message: external_exports.string() });
|
|
25066
25100
|
var oid2 = external_exports.string().regex(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/);
|
|
25067
|
-
var
|
|
25068
|
-
|
|
25069
|
-
|
|
25101
|
+
var indexFeature = external_exports.strictObject({
|
|
25102
|
+
id: external_exports.string(),
|
|
25103
|
+
path: external_exports.string(),
|
|
25104
|
+
title: external_exports.string(),
|
|
25105
|
+
state,
|
|
25106
|
+
requirements: external_exports.array(external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string(), description: external_exports.string() })),
|
|
25107
|
+
designs: external_exports.array(external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string() }))
|
|
25108
|
+
});
|
|
25109
|
+
var indexInstruction = external_exports.strictObject({ id: external_exports.string(), name: external_exports.string(), path: external_exports.string(), title: external_exports.string() });
|
|
25110
|
+
var browserCheckoutV1 = external_exports.strictObject({
|
|
25111
|
+
contract: external_exports.literal("browser-checkout"),
|
|
25112
|
+
version: external_exports.literal(1),
|
|
25070
25113
|
sessionId: external_exports.string(),
|
|
25071
25114
|
head: oid2.nullable(),
|
|
25072
25115
|
observedAt: external_exports.string(),
|
|
25073
25116
|
working: external_exports.boolean(),
|
|
25074
25117
|
stamp: external_exports.string(),
|
|
25075
|
-
features: external_exports.array(feature),
|
|
25076
|
-
instructions: external_exports.array(instruction),
|
|
25077
|
-
agents: agents.nullable(),
|
|
25078
25118
|
problems: external_exports.array(problem),
|
|
25119
|
+
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() })) })
|
|
25120
|
+
});
|
|
25121
|
+
var featureRow = external_exports.strictObject({
|
|
25122
|
+
id: external_exports.string(),
|
|
25123
|
+
path: external_exports.string(),
|
|
25124
|
+
title: external_exports.string(),
|
|
25125
|
+
description: external_exports.string(),
|
|
25126
|
+
state,
|
|
25127
|
+
designs: external_exports.number().int().nonnegative(),
|
|
25079
25128
|
contributors: external_exports.array(contributor),
|
|
25080
|
-
|
|
25129
|
+
updatedAt: external_exports.string().nullable(),
|
|
25130
|
+
requirementCount: external_exports.number().int().nonnegative(),
|
|
25131
|
+
// `acceptance`: the numbered items under the acceptance heading, null without one. `designed`: a design names it.
|
|
25132
|
+
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() })),
|
|
25133
|
+
hidden: external_exports.number().int().nonnegative()
|
|
25134
|
+
});
|
|
25135
|
+
var browserFeaturesV1 = external_exports.strictObject({
|
|
25136
|
+
contract: external_exports.literal("browser-features"),
|
|
25137
|
+
version: external_exports.literal(1),
|
|
25138
|
+
sessionId: external_exports.string(),
|
|
25139
|
+
total: external_exports.number().int().nonnegative(),
|
|
25140
|
+
all: external_exports.number().int().nonnegative(),
|
|
25141
|
+
requirements: external_exports.number().int().nonnegative(),
|
|
25142
|
+
mostRequirements: external_exports.number().int().nonnegative(),
|
|
25143
|
+
next: external_exports.string().nullable(),
|
|
25144
|
+
features: external_exports.array(featureRow)
|
|
25145
|
+
});
|
|
25146
|
+
var browserFeatureV1 = external_exports.strictObject({ contract: external_exports.literal("browser-feature"), version: external_exports.literal(1), sessionId: external_exports.string(), feature });
|
|
25147
|
+
var browserInstructionsV1 = external_exports.strictObject({
|
|
25148
|
+
contract: external_exports.literal("browser-instructions"),
|
|
25149
|
+
version: external_exports.literal(1),
|
|
25150
|
+
sessionId: external_exports.string(),
|
|
25151
|
+
instructions: external_exports.array(instruction),
|
|
25152
|
+
agents: agents.nullable()
|
|
25153
|
+
});
|
|
25154
|
+
var browserContributorsV1 = external_exports.strictObject({
|
|
25155
|
+
contract: external_exports.literal("browser-contributors"),
|
|
25156
|
+
version: external_exports.literal(1),
|
|
25157
|
+
sessionId: external_exports.string(),
|
|
25158
|
+
total: external_exports.number().int().nonnegative(),
|
|
25159
|
+
next: external_exports.string().nullable(),
|
|
25160
|
+
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() }))
|
|
25161
|
+
});
|
|
25162
|
+
var browserContributorV1 = external_exports.strictObject({
|
|
25163
|
+
contract: external_exports.literal("browser-contributor"),
|
|
25164
|
+
version: external_exports.literal(1),
|
|
25165
|
+
sessionId: external_exports.string(),
|
|
25166
|
+
person: contributor,
|
|
25167
|
+
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() }))
|
|
25081
25168
|
});
|
|
25082
25169
|
var kind = external_exports.enum(["feature", "requirement", "design", "wiki", "instruction"]);
|
|
25083
25170
|
var reference = external_exports.strictObject({ id: external_exports.string(), title: external_exports.string(), specId: external_exports.string(), path: external_exports.string() }).nullable();
|
|
@@ -25122,11 +25209,13 @@ var browserHistoryV6 = external_exports.strictObject({
|
|
|
25122
25209
|
next: oid2.nullable(),
|
|
25123
25210
|
events: external_exports.array(event)
|
|
25124
25211
|
});
|
|
25125
|
-
var
|
|
25212
|
+
var browserHistorySummaryV5 = external_exports.strictObject({
|
|
25126
25213
|
contract: external_exports.literal("browser-history-summary"),
|
|
25127
|
-
version: external_exports.literal(
|
|
25214
|
+
version: external_exports.literal(5),
|
|
25128
25215
|
sessionId: external_exports.string(),
|
|
25129
25216
|
head: oid2,
|
|
25217
|
+
// The three who committed most, and how many others there are and how many commits they made between them.
|
|
25218
|
+
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() }) }),
|
|
25130
25219
|
total: external_exports.number().int().nonnegative(),
|
|
25131
25220
|
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() }),
|
|
25132
25221
|
// One entry per commit within three weeks of the newest change, so the reader can count by its own calendar day.
|
|
@@ -25134,22 +25223,23 @@ var browserHistorySummaryV4 = external_exports.strictObject({
|
|
|
25134
25223
|
// The newest commits: a few of their changes each and how many there are in all.
|
|
25135
25224
|
recent: external_exports.array(external_exports.strictObject({ commit: oid2, count: external_exports.number().int().positive(), events: external_exports.array(event) }))
|
|
25136
25225
|
});
|
|
25137
|
-
var
|
|
25226
|
+
var searchKind = external_exports.enum(["feature", "requirement", "design", "instruction", "record", "commit"]);
|
|
25227
|
+
var searchHit = external_exports.strictObject({
|
|
25228
|
+
id: external_exports.string(),
|
|
25229
|
+
kind: searchKind,
|
|
25230
|
+
title: external_exports.string(),
|
|
25231
|
+
where: external_exports.string(),
|
|
25232
|
+
line: external_exports.string(),
|
|
25233
|
+
// What the hit opens: a feature (with the requirement or design tab), an instruction, a record, or a commit.
|
|
25234
|
+
featureId: external_exports.string().optional(),
|
|
25235
|
+
commit: oid2.optional()
|
|
25236
|
+
});
|
|
25237
|
+
var browserSearchV3 = external_exports.strictObject({
|
|
25138
25238
|
contract: external_exports.literal("browser-search"),
|
|
25139
|
-
version: external_exports.literal(
|
|
25239
|
+
version: external_exports.literal(3),
|
|
25140
25240
|
sessionId: external_exports.string(),
|
|
25141
25241
|
query: external_exports.string(),
|
|
25142
|
-
|
|
25143
|
-
id: external_exports.string(),
|
|
25144
|
-
kind: external_exports.enum(["feature", "requirement", "design", "instruction", "history"]),
|
|
25145
|
-
title: external_exports.string(),
|
|
25146
|
-
where: external_exports.string(),
|
|
25147
|
-
line: external_exports.string(),
|
|
25148
|
-
// What the hit opens: a feature (with the requirement or design tab), an instruction, or one change in the activity.
|
|
25149
|
-
featureId: external_exports.string().optional(),
|
|
25150
|
-
documentId: external_exports.string().optional(),
|
|
25151
|
-
key: external_exports.string().optional()
|
|
25152
|
-
}))
|
|
25242
|
+
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) }))
|
|
25153
25243
|
});
|
|
25154
25244
|
var headQuery = oid2;
|
|
25155
25245
|
var count = (max) => external_exports.string().regex(/^(0|[1-9]\d{0,6})$/).transform(Number).pipe(external_exports.number().int().min(0).max(max));
|
|
@@ -25164,7 +25254,26 @@ var browserHistoryQueryV4 = external_exports.strictObject({
|
|
|
25164
25254
|
q: external_exports.string().max(200).optional()
|
|
25165
25255
|
});
|
|
25166
25256
|
var browserHistorySummaryQueryV1 = external_exports.strictObject({ head: headQuery });
|
|
25167
|
-
var
|
|
25257
|
+
var featureId = external_exports.string().regex(/^S-[a-z2-7]{10}$/);
|
|
25258
|
+
var browserFeaturesQueryV1 = external_exports.strictObject({
|
|
25259
|
+
q: external_exports.string().max(200).optional(),
|
|
25260
|
+
design: external_exports.enum(["yes", "no"]).optional(),
|
|
25261
|
+
author: external_exports.string().min(1).max(320).optional(),
|
|
25262
|
+
sort: external_exports.enum(["title", "requirements", "updated"]).optional(),
|
|
25263
|
+
dir: external_exports.enum(["asc", "desc"]).optional(),
|
|
25264
|
+
after: featureId.optional(),
|
|
25265
|
+
limit: count(50).pipe(external_exports.number().min(1)).optional()
|
|
25266
|
+
});
|
|
25267
|
+
var browserFeatureQueryV1 = external_exports.strictObject({ id: featureId });
|
|
25268
|
+
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() });
|
|
25269
|
+
var browserContributorQueryV1 = external_exports.strictObject({ email: external_exports.string().min(1).max(320) });
|
|
25270
|
+
var browserSearchQueryV2 = external_exports.strictObject({
|
|
25271
|
+
q: external_exports.string().max(200),
|
|
25272
|
+
head: headQuery.optional(),
|
|
25273
|
+
group: searchKind.optional(),
|
|
25274
|
+
after: external_exports.string().min(1).max(200).optional(),
|
|
25275
|
+
limit: count(50).pipe(external_exports.number().min(1)).optional()
|
|
25276
|
+
});
|
|
25168
25277
|
var browserInstructionFileQueryV1 = external_exports.strictObject({ id: external_exports.string().regex(/^I-[a-z2-7]{10}$/), path: external_exports.string().min(1).max(1e3) });
|
|
25169
25278
|
var changelogQueryV1 = external_exports.strictObject({ lang: external_exports.string().regex(/^[a-z]{2}(?:-[A-Z]{2})?$/).optional() });
|
|
25170
25279
|
var fileStatus = external_exports.enum(["added", "modified", "deleted", "renamed"]);
|
|
@@ -25176,12 +25285,13 @@ var commitFile = external_exports.strictObject({
|
|
|
25176
25285
|
additions: external_exports.number().int().nonnegative().nullable(),
|
|
25177
25286
|
deletions: external_exports.number().int().nonnegative().nullable()
|
|
25178
25287
|
});
|
|
25179
|
-
var
|
|
25288
|
+
var browserCommitFilesV2 = external_exports.strictObject({
|
|
25180
25289
|
contract: external_exports.literal("browser-commit-files"),
|
|
25181
|
-
version: external_exports.literal(
|
|
25290
|
+
version: external_exports.literal(2),
|
|
25182
25291
|
sessionId: external_exports.string(),
|
|
25183
25292
|
commit: oid2,
|
|
25184
25293
|
total: external_exports.number().int().nonnegative(),
|
|
25294
|
+
next: external_exports.string().nullable(),
|
|
25185
25295
|
files: external_exports.array(commitFile)
|
|
25186
25296
|
});
|
|
25187
25297
|
var browserCommitFileV1 = external_exports.strictObject({
|
|
@@ -25196,7 +25306,7 @@ var browserCommitFileV1 = external_exports.strictObject({
|
|
|
25196
25306
|
binary: external_exports.boolean(),
|
|
25197
25307
|
tooLarge: external_exports.boolean()
|
|
25198
25308
|
});
|
|
25199
|
-
var
|
|
25309
|
+
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() });
|
|
25200
25310
|
var browserCommitFileQueryV1 = external_exports.strictObject({ commit: oid2, path: external_exports.string().min(1).max(4096) });
|
|
25201
25311
|
var browserCommitV4 = external_exports.strictObject({
|
|
25202
25312
|
contract: external_exports.literal("browser-commit"),
|
|
@@ -25212,7 +25322,8 @@ var browserCommitV4 = external_exports.strictObject({
|
|
|
25212
25322
|
next: external_exports.string().nullable(),
|
|
25213
25323
|
changes: external_exports.array(event)
|
|
25214
25324
|
});
|
|
25215
|
-
var
|
|
25325
|
+
var recordId = external_exports.string().regex(/^(?:DR|H)-[A-Za-z0-9_-]{1,64}$/);
|
|
25326
|
+
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() });
|
|
25216
25327
|
var browserCommitChangeV1 = external_exports.strictObject({
|
|
25217
25328
|
contract: external_exports.literal("browser-commit-change"),
|
|
25218
25329
|
version: external_exports.literal(1),
|
|
@@ -25223,14 +25334,15 @@ var browserCommitChangeV1 = external_exports.strictObject({
|
|
|
25223
25334
|
after: snapshot
|
|
25224
25335
|
});
|
|
25225
25336
|
var browserCommitChangeQueryV1 = external_exports.strictObject({ commit: oid2, id: external_exports.string().min(1).max(100) });
|
|
25226
|
-
var
|
|
25227
|
-
var browserRecordV1 = external_exports.strictObject({
|
|
25337
|
+
var browserRecordV2 = external_exports.strictObject({
|
|
25228
25338
|
contract: external_exports.literal("browser-record"),
|
|
25229
|
-
version: external_exports.literal(
|
|
25339
|
+
version: external_exports.literal(2),
|
|
25230
25340
|
sessionId: external_exports.string(),
|
|
25231
25341
|
head: oid2,
|
|
25232
25342
|
id: recordId,
|
|
25233
|
-
commit: oid2
|
|
25343
|
+
commit: oid2,
|
|
25344
|
+
record: record2,
|
|
25345
|
+
others: external_exports.number().int().nonnegative()
|
|
25234
25346
|
});
|
|
25235
25347
|
var browserRecordQueryV1 = external_exports.strictObject({ head: oid2, id: recordId });
|
|
25236
25348
|
var browserInstructionFileV1 = external_exports.strictObject({
|
|
@@ -25322,9 +25434,9 @@ var projectInitV8 = external_exports.discriminatedUnion("ok", [
|
|
|
25322
25434
|
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() }) })
|
|
25323
25435
|
]);
|
|
25324
25436
|
|
|
25325
|
-
// ../../packages/contracts/dist/versions/update-
|
|
25437
|
+
// ../../packages/contracts/dist/versions/update-v6.js
|
|
25326
25438
|
var commitReason = external_exports.enum(["untracked", "other-changes", "commit-failed"]);
|
|
25327
|
-
var
|
|
25439
|
+
var updateCommit = external_exports.strictObject({
|
|
25328
25440
|
state: external_exports.enum(["not-requested", "nothing", "committed", "skipped"]),
|
|
25329
25441
|
commit: external_exports.string().nullable(),
|
|
25330
25442
|
paths: external_exports.array(external_exports.string()),
|
|
@@ -25332,27 +25444,40 @@ var agentDocsCommit = external_exports.strictObject({
|
|
|
25332
25444
|
reason: commitReason.nullable(),
|
|
25333
25445
|
detail: external_exports.string().nullable()
|
|
25334
25446
|
}).refine((value) => value.state === "committed" === (value.commit !== null) && value.state === "skipped" === (value.reason !== null), "commit is set exactly when committed, reason exactly when skipped.");
|
|
25335
|
-
var
|
|
25447
|
+
var updateV6 = external_exports.discriminatedUnion("ok", [
|
|
25336
25448
|
external_exports.strictObject({
|
|
25337
25449
|
contract: external_exports.literal("update"),
|
|
25338
|
-
version: external_exports.literal(
|
|
25450
|
+
version: external_exports.literal(6),
|
|
25339
25451
|
ok: external_exports.literal(true),
|
|
25340
25452
|
cliVersion: external_exports.string().min(1),
|
|
25341
25453
|
update: updateStateV1,
|
|
25342
|
-
// Present only when a newer release exists
|
|
25343
|
-
install: external_exports.strictObject({
|
|
25344
|
-
// refreshed: blocks whose
|
|
25454
|
+
// Present only when a newer release exists; after installing it, `gitifact update` sets the project to it.
|
|
25455
|
+
install: external_exports.strictObject({ npmGlobal: external_exports.string().min(1) }).nullable(),
|
|
25456
|
+
// refreshed: blocks whose text differed were rewritten. current: every block already matched. kept: the project is on a
|
|
25457
|
+
// release newer than this CLI, so its blocks were left as they are.
|
|
25345
25458
|
// not-initialized: no .gitifact config here, so only the version check ran. no-block: no file carries a block.
|
|
25346
25459
|
// missing: files `gitifact init` would add and update does not create, e.g. CLAUDE.md when only AGENTS.md has the block.
|
|
25347
|
-
agentDocs: external_exports.strictObject({ state: external_exports.enum(["refreshed", "current", "not-initialized", "no-block"]), paths: external_exports.array(external_exports.string()), missing: external_exports.array(external_exports.string()) }),
|
|
25348
|
-
|
|
25460
|
+
agentDocs: external_exports.strictObject({ state: external_exports.enum(["refreshed", "current", "kept", "not-initialized", "no-block"]), paths: external_exports.array(external_exports.string()), missing: external_exports.array(external_exports.string()) }),
|
|
25461
|
+
// written: .gitifact/config.json now names this release as `cli` and the block language. current: it already did.
|
|
25462
|
+
// cli is the project's release afterwards; one newer than cliVersion was kept, since an older CLI does not lower it.
|
|
25463
|
+
project: external_exports.strictObject({ state: external_exports.enum(["written", "current", "not-initialized"]), cli: external_exports.string().min(1).nullable() }),
|
|
25464
|
+
commit: updateCommit,
|
|
25349
25465
|
// The project still uses the 0.7 storage convention (schemaVersion 2): the blocks now describe commands that will refuse it
|
|
25350
25466
|
// until the documents are moved with `gitifact guide show migrate`.
|
|
25351
25467
|
migrationRequired: external_exports.boolean()
|
|
25352
25468
|
}),
|
|
25353
|
-
external_exports.strictObject({ contract: external_exports.literal("update"), version: external_exports.literal(
|
|
25469
|
+
external_exports.strictObject({ contract: external_exports.literal("update"), version: external_exports.literal(6), ok: external_exports.literal(false), error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }) })
|
|
25354
25470
|
]);
|
|
25355
25471
|
|
|
25472
|
+
// ../../packages/contracts/dist/versions/update-later-v1.js
|
|
25473
|
+
var updateLaterV1 = external_exports.strictObject({
|
|
25474
|
+
contract: external_exports.literal("update-later"),
|
|
25475
|
+
version: external_exports.literal(1),
|
|
25476
|
+
ok: external_exports.literal(true),
|
|
25477
|
+
cliVersion: external_exports.string().min(1),
|
|
25478
|
+
later: external_exports.strictObject({ version: external_exports.string().min(1), until: external_exports.iso.datetime() }).nullable()
|
|
25479
|
+
});
|
|
25480
|
+
|
|
25356
25481
|
// ../../packages/contracts/dist/versions/browser-session-v3.js
|
|
25357
25482
|
var browserSessionV3 = external_exports.strictObject({
|
|
25358
25483
|
contract: external_exports.literal("browser-session"),
|
|
@@ -25505,6 +25630,8 @@ var messages_default3 = {
|
|
|
25505
25630
|
"server.sessionChanged": "\uC11C\uBC84 \uC138\uC158\uC774 \uBCC0\uACBD\uB410\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC5F0\uACB0\uD558\uC138\uC694.",
|
|
25506
25631
|
"server.badRange": "\uC870\uD68C \uBC94\uC704\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
25507
25632
|
"server.commitNotFound": "\uC774 \uC800\uC7A5\uC18C\uC5D0 \uADF8 \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25633
|
+
"server.featureNotFound": "\uCCB4\uD06C\uC544\uC6C3\uC5D0 \uADF8 \uAE30\uB2A5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25634
|
+
"server.contributorNotFound": "HEAD\uC5D0 \uADF8 \uC774\uBA54\uC77C\uB85C \uCEE4\uBC0B\uD55C \uC0AC\uB78C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25508
25635
|
"server.cursorNotFound": "\uC774\uC5B4 \uC77D\uC744 \uC704\uCE58\uB97C \uBAA9\uB85D\uC5D0\uC11C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uCC98\uC74C\uBD80\uD130 \uB2E4\uC2DC \uBD88\uB7EC\uC624\uC138\uC694.",
|
|
25509
25636
|
"server.workingChangeNotFound": "\uCEE4\uBC0B \uC804 \uBCC0\uACBD\uC5D0 \uADF8 \uBB38\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25510
25637
|
"server.recordNotFound": "\uC774 \uC774\uB825\uC5D0 \uADF8 \uACB0\uC815\uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
@@ -25551,7 +25678,7 @@ var messages_default3 = {
|
|
|
25551
25678
|
"init.text.storage": "\uC800\uC7A5 \uADDC\uC57D",
|
|
25552
25679
|
"init.text.agentDocs": "\uC5D0\uC774\uC804\uD2B8 \uC9C0\uCE68",
|
|
25553
25680
|
"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.",
|
|
25554
|
-
"init.text.updateAvailable": "\uC0C8 \uBC84\uC804 {version}\uC774 \uC788\uC2B5\uB2C8\uB2E4. {command}\uB85C \
|
|
25681
|
+
"init.text.updateAvailable": "\uC0C8 \uBC84\uC804 {version}\uC774 \uC788\uC2B5\uB2C8\uB2E4. {command}\uB85C \uC124\uCE58\uD55C \uB4A4 gitifact update\uB85C \uD504\uB85C\uC81D\uD2B8\uB97C \uC0C8 \uBC84\uC804\uC5D0 \uB9DE\uCD94\uC138\uC694.",
|
|
25555
25682
|
"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.",
|
|
25556
25683
|
"config.storeNotDirectory": ".gitifact\uB294 \uB9C1\uD06C\uAC00 \uC544\uB2CC \uC77C\uBC18 \uB514\uB809\uD130\uB9AC\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
|
|
25557
25684
|
"config.fileShape": "config.json\uC740 64 KiB \uC774\uD558\uC758 \uC77C\uBC18 \uD30C\uC77C\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
|
|
@@ -25571,7 +25698,6 @@ var messages_default3 = {
|
|
|
25571
25698
|
"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.",
|
|
25572
25699
|
"specReader.headUnreadable": "HEAD\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
25573
25700
|
"specReader.schemaRequired": "\uC0C8 \uC2A4\uD0A4\uB9C8 \uD504\uB85C\uC81D\uD2B8\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
|
|
25574
|
-
"specReader.authorUnreadable": "Git \uC791\uC131\uC790\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
25575
25701
|
"specReader.historyUnreadable": "Git \uC774\uB825 \uD615\uC2DD\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
25576
25702
|
"specReader.projectChanged": "\uC870\uD68C \uC911 \uD504\uB85C\uC81D\uD2B8\uAC00 \uBC14\uB00C\uC5C8\uC2B5\uB2C8\uB2E4. \uC0C8\uB85C\uACE0\uCE68\uD558\uC138\uC694.",
|
|
25577
25703
|
"browser.invalidPort": "\uD3EC\uD2B8\uB294 0\uBD80\uD130 65535 \uC0AC\uC774\uC758 \uC815\uC218\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
|
|
@@ -25587,13 +25713,13 @@ var messages_default3 = {
|
|
|
25587
25713
|
"update.text.upToDate": "\uCD5C\uC2E0 \uBC84\uC804\uC744 \uC0AC\uC6A9 \uC911\uC785\uB2C8\uB2E4.",
|
|
25588
25714
|
"update.text.disabled": "\uC0C8 \uBC84\uC804 \uD655\uC778\uC774 \uAEBC\uC838 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
25589
25715
|
"update.text.unavailable": "\uC0C8 \uBC84\uC804\uC744 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uB124\uD2B8\uC6CC\uD06C \uC5F0\uACB0\uC744 \uD655\uC778\uD558\uC138\uC694.",
|
|
25590
|
-
"update.text.install": "\
|
|
25591
|
-
"update.text.afterInstall": "\
|
|
25716
|
+
"update.text.install": "\uC124\uCE58 \uBA85\uB839: {command}",
|
|
25717
|
+
"update.text.afterInstall": "\uC124\uCE58\uD55C \uB4A4 gitifact update\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 \uD504\uB85C\uC81D\uD2B8 \uAE30\uC900\uACFC \uC9C0\uCE68\uC774 \uC0C8 \uBC84\uC804\uC73C\uB85C \uBC14\uB01D\uB2C8\uB2E4. \uBE0C\uB77C\uC6B0\uC800\uB3C4 \uC0C8 \uBC84\uC804\uC73C\uB85C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
|
|
25592
25718
|
"update.text.blockRefreshed": "\uC9C0\uCE68 \uBE14\uB85D \uAC31\uC2E0: {paths}",
|
|
25593
25719
|
"update.text.blockCurrent": "\uC9C0\uCE68 \uBE14\uB85D\uC740 \uC774\uBBF8 \uD604\uC7AC \uBC84\uC804\uC785\uB2C8\uB2E4: {paths}",
|
|
25594
|
-
"update.text.noBlock": "GITIFACT \uBE14\uB85D\uC774 \uC788\uB294 \uC9C0\uCE68 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.
|
|
25720
|
+
"update.text.noBlock": "GITIFACT \uBE14\uB85D\uC774 \uC788\uB294 \uC9C0\uCE68 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. gitifact init\uC73C\uB85C \uC124\uCE58\uD558\uC138\uC694.",
|
|
25595
25721
|
"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.",
|
|
25596
|
-
"update.text.missing": "{paths} \uD30C\uC77C\uC774 \uC5C6\uC5B4 Claude Code\uAC00 AGENTS.md\uC758 \uC9C0\uCE68\uC744 \uC77D\uC9C0 \uBABB\uD569\uB2C8\uB2E4.
|
|
25722
|
+
"update.text.missing": "{paths} \uD30C\uC77C\uC774 \uC5C6\uC5B4 Claude Code\uAC00 AGENTS.md\uC758 \uC9C0\uCE68\uC744 \uC77D\uC9C0 \uBABB\uD569\uB2C8\uB2E4. gitifact init\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 AGENTS.md\uB97C \uAC00\uC838\uC624\uB294 \uD30C\uC77C\uC744 \uB9CC\uB4ED\uB2C8\uB2E4.",
|
|
25597
25723
|
"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.",
|
|
25598
25724
|
"update.text.committed": "\uC9C0\uCE68 \uBE14\uB85D \uCEE4\uBC0B: {commit} ({paths})",
|
|
25599
25725
|
"update.text.commitNothing": "\uCEE4\uBC0B\uD560 \uC9C0\uCE68 \uBE14\uB85D \uBCC0\uACBD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
@@ -25605,7 +25731,7 @@ var messages_default3 = {
|
|
|
25605
25731
|
"server.assetNotFound": "\uC5D0\uC14B\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25606
25732
|
"help.lang": "\uCD9C\uB825 \uC5B8\uC5B4 (ko \uB610\uB294 en)",
|
|
25607
25733
|
"help.updateCheck": "\uD30C\uC77C\uC744 \uBC14\uAFB8\uC9C0 \uC54A\uACE0 \uC0C8 \uBC84\uC804\uB9CC \uD655\uC778",
|
|
25608
|
-
"commitFiles.notRecord": ".gitifact \uC548\uC5D0\uC11C\uB294 \uAE30\uB85D\xB7\uC124\uC815\xB7\uC5D0\uC14B\uB9CC \uCEE4\uBC0B\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4: {path}",
|
|
25734
|
+
"commitFiles.notRecord": ".gitifact \uC548\uC5D0\uC11C\uB294 \uAE30\uB85D\xB7\uC124\uC815(config.json\xB7.gitattributes)\xB7\uC5D0\uC14B\uB9CC \uCEE4\uBC0B\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4: {path}",
|
|
25609
25735
|
"help.changes": "\uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC740 \uBB38\uC11C \uBCC0\uACBD \uC870\uD68C\uC640 \uCEE4\uBC0B",
|
|
25610
25736
|
"help.changesList": "HEAD \uB300\uBE44 \uBC14\uB010 \uBB38\uC11C, \uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC740 \uACB0\uC815\uAE30\uB85D, \uAE30\uB85D\uC774 \uC5C6\uB294 \uBCC0\uACBD, \uCEE4\uBC0B \uC785\uB825 \uD30C\uC77C \uACBD\uB85C",
|
|
25611
25737
|
"help.changesCommit": "check\uC640 \uAC19\uC740 \uAC80\uC0AC \uB4A4 \uC120\uD0DD\uD55C \uD30C\uC77C\uACFC \uACB0\uC815\uAE30\uB85D\uC744 \uCEE4\uBC0B",
|
|
@@ -25758,7 +25884,16 @@ var messages_default3 = {
|
|
|
25758
25884
|
"feedback.openUrl": "gh\uB97C \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC544\uB798 \uC8FC\uC18C\uB97C \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uC5B4 \uC774\uC288\uB97C \uC81C\uCD9C\uD558\uC138\uC694.",
|
|
25759
25885
|
"feedback.ghFailed": "gh\uB85C \uC774\uC288\uB97C \uB9CC\uB4E4\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uC544\uB798 \uC8FC\uC18C\uB97C \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uC5B4 \uC774\uC288\uB97C \uC81C\uCD9C\uD558\uC138\uC694.",
|
|
25760
25886
|
"feedback.truncated": "\uBCF8\uBB38\uC774 \uAE38\uC5B4 \uC8FC\uC18C\uC5D0\uB294 \uC55E\uBD80\uBD84\uB9CC \uB2F4\uACBC\uC2B5\uB2C8\uB2E4. \uC544\uB798 \uC804\uCCB4 \uBCF8\uBB38\uC744 \uC791\uC131 \uD398\uC774\uC9C0\uC5D0 \uBD99\uC5EC \uB123\uC73C\uC138\uC694.",
|
|
25761
|
-
"feedback.title": "\uC81C\uBAA9: {title}"
|
|
25887
|
+
"feedback.title": "\uC81C\uBAA9: {title}",
|
|
25888
|
+
"update.text.projectWritten": "{path}\uC758 \uD504\uB85C\uC81D\uD2B8 \uAE30\uC900\uC744 gitifact {version}\uC73C\uB85C \uC801\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
25889
|
+
"update.text.projectAhead": "\uD504\uB85C\uC81D\uD2B8 \uAE30\uC900\uC740 gitifact {project}\uC785\uB2C8\uB2E4. \uC774 CLI({version})\uAC00 \uB354 \uC624\uB798\uB418\uC5B4 \uB0AE\uCD94\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. {command}\uB85C \uC62C\uB9AC\uC138\uC694.",
|
|
25890
|
+
"update.later.postponed": "{latest} \uC548\uB0B4\uB97C {until}\uAE4C\uC9C0 \uC27D\uB2C8\uB2E4. \uB354 \uC0C8 \uBC84\uC804\uC774 \uB098\uC624\uBA74 \uBC14\uB85C \uB2E4\uC2DC \uC548\uB0B4\uD569\uB2C8\uB2E4.",
|
|
25891
|
+
"update.later.nothing": "\uBBF8\uB8F0 \uC0C8 \uBC84\uC804 \uC548\uB0B4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4(\uC9C0\uAE08 {version}).",
|
|
25892
|
+
"notice.release": "\uC0C8 \uBC84\uC804 {latest}\uAC00 \uC788\uC2B5\uB2C8\uB2E4(\uC9C0\uAE08 {version}). \uC5C5\uB370\uC774\uD2B8: {command} \uD6C4 gitifact update \xB7 \uB098\uC911\uC5D0: gitifact update --later",
|
|
25893
|
+
"notice.projectNewer": "\uC774 \uD504\uB85C\uC81D\uD2B8\uB294 gitifact {project} \uAE30\uC900\uC785\uB2C8\uB2E4(\uC2E4\uD589 \uC911 {version}). {command}\uB85C \uC62C\uB9AC\uC138\uC694.",
|
|
25894
|
+
"notice.projectOlder": "\uC774 CLI({version})\uAC00 \uD504\uB85C\uC81D\uD2B8 \uAE30\uC900({project})\uBCF4\uB2E4 \uC0C8 \uBC84\uC804\uC785\uB2C8\uB2E4. gitifact update\uB85C \uD504\uB85C\uC81D\uD2B8 \uAE30\uC900\uC744 \uC62C\uB9B4 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
25895
|
+
"help.updateLater": "\uC9C0\uAE08 \uC548\uB0B4\uB41C \uC0C8 \uBC84\uC804\uC744 24\uC2DC\uAC04 \uB3D9\uC548 \uC548\uB0B4\uD558\uC9C0 \uC54A\uC74C. \uB354 \uC0C8 \uBC84\uC804\uC740 \uBC14\uB85C \uC548\uB0B4",
|
|
25896
|
+
"update.text.blockKept": "\uD504\uB85C\uC81D\uD2B8\uAC00 \uC774 CLI\uBCF4\uB2E4 \uC0C8 \uBC84\uC804 \uAE30\uC900\uC774\uB77C \uC9C0\uCE68 \uBE14\uB85D\uC744 \uADF8\uB300\uB85C \uB450\uC5C8\uC2B5\uB2C8\uB2E4: {paths}"
|
|
25762
25897
|
};
|
|
25763
25898
|
|
|
25764
25899
|
// src/shared/i18n/en/messages.json
|
|
@@ -25809,6 +25944,8 @@ var messages_default4 = {
|
|
|
25809
25944
|
"server.sessionChanged": "The server session changed. Reconnect to continue.",
|
|
25810
25945
|
"server.badRange": "Check the requested range.",
|
|
25811
25946
|
"server.commitNotFound": "This repository has no such commit.",
|
|
25947
|
+
"server.featureNotFound": "The checkout has no such feature.",
|
|
25948
|
+
"server.contributorNotFound": "Nobody with this email committed to HEAD.",
|
|
25812
25949
|
"server.cursorNotFound": "The place to read on from is no longer in the list. Load it again from the start.",
|
|
25813
25950
|
"server.workingChangeNotFound": "No uncommitted change of that document.",
|
|
25814
25951
|
"server.recordNotFound": "This history has no such decision record.",
|
|
@@ -25855,7 +25992,7 @@ var messages_default4 = {
|
|
|
25855
25992
|
"init.text.storage": "Storage convention",
|
|
25856
25993
|
"init.text.agentDocs": "Agent instructions",
|
|
25857
25994
|
"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.",
|
|
25858
|
-
"init.text.updateAvailable": "Version {version} is available.
|
|
25995
|
+
"init.text.updateAvailable": "Version {version} is available. Install it with {command}, then run gitifact update to move the project to it.",
|
|
25859
25996
|
"init.failed": "Initialization failed. Check file permissions and the generated configuration and temporary files.",
|
|
25860
25997
|
"config.storeNotDirectory": ".gitifact must be a regular directory, not a link.",
|
|
25861
25998
|
"config.fileShape": "config.json must be a regular file no larger than 64 KiB.",
|
|
@@ -25875,7 +26012,6 @@ var messages_default4 = {
|
|
|
25875
26012
|
"agentBlock.duplicateStart": "Multiple GITIFACT start markers found. Remove duplicate blocks before retrying.",
|
|
25876
26013
|
"specReader.headUnreadable": "Could not read HEAD.",
|
|
25877
26014
|
"specReader.schemaRequired": "A project using the supported schema is required.",
|
|
25878
|
-
"specReader.authorUnreadable": "Could not read Git authors.",
|
|
25879
26015
|
"specReader.historyUnreadable": "Could not read Git history.",
|
|
25880
26016
|
"specReader.projectChanged": "The project changed while reading. Refresh to continue.",
|
|
25881
26017
|
"browser.invalidPort": "Port must be an integer between 0 and 65535.",
|
|
@@ -25891,13 +26027,13 @@ var messages_default4 = {
|
|
|
25891
26027
|
"update.text.upToDate": "You are using the latest version.",
|
|
25892
26028
|
"update.text.disabled": "Update checks are disabled.",
|
|
25893
26029
|
"update.text.unavailable": "Could not check for updates. Check your network connection.",
|
|
25894
|
-
"update.text.install": "
|
|
25895
|
-
"update.text.afterInstall": "
|
|
26030
|
+
"update.text.install": "Install command: {command}",
|
|
26031
|
+
"update.text.afterInstall": "After installing, run gitifact update again to move the project and its instructions to the new version. Restart the browser with the new version too.",
|
|
25896
26032
|
"update.text.blockRefreshed": "Instruction blocks refreshed: {paths}",
|
|
25897
26033
|
"update.text.blockCurrent": "Instruction blocks are already current: {paths}",
|
|
25898
|
-
"update.text.noBlock": "No instruction files contain a GITIFACT block. Run
|
|
26034
|
+
"update.text.noBlock": "No instruction files contain a GITIFACT block. Run gitifact init to install one.",
|
|
25899
26035
|
"update.text.notInitialized": "Only the version was checked because this project is not initialized.",
|
|
25900
|
-
"update.text.missing": "{paths} is missing, so Claude Code cannot load the AGENTS.md instructions. Rerun
|
|
26036
|
+
"update.text.missing": "{paths} is missing, so Claude Code cannot load the AGENTS.md instructions. Rerun gitifact init to create the import file.",
|
|
25901
26037
|
"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.",
|
|
25902
26038
|
"update.text.committed": "Instruction blocks committed: {commit} ({paths})",
|
|
25903
26039
|
"update.text.commitNothing": "No instruction block changes to commit.",
|
|
@@ -25909,7 +26045,7 @@ var messages_default4 = {
|
|
|
25909
26045
|
"server.assetNotFound": "Asset not found.",
|
|
25910
26046
|
"help.lang": "Output language (ko or en)",
|
|
25911
26047
|
"help.updateCheck": "Check for a newer version without changing files",
|
|
25912
|
-
"commitFiles.notRecord": "Inside .gitifact only records, the configuration and assets can be committed: {path}",
|
|
26048
|
+
"commitFiles.notRecord": "Inside .gitifact only records, the configuration (config.json, .gitattributes) and assets can be committed: {path}",
|
|
25913
26049
|
"help.changes": "Inspect and commit uncommitted document changes",
|
|
25914
26050
|
"help.changesList": "Documents changed since HEAD, uncommitted records, changes without a record, and the commit input path",
|
|
25915
26051
|
"help.changesCommit": "Check like the check command, then commit the selected files and records",
|
|
@@ -26062,7 +26198,16 @@ var messages_default4 = {
|
|
|
26062
26198
|
"feedback.openUrl": "gh is not available. Open the address below in a browser and submit the issue.",
|
|
26063
26199
|
"feedback.ghFailed": "gh could not create the issue. Open the address below in a browser and submit the issue.",
|
|
26064
26200
|
"feedback.truncated": "The body is long, so the address holds only its beginning. Paste the full body below into the page.",
|
|
26065
|
-
"feedback.title": "Title: {title}"
|
|
26201
|
+
"feedback.title": "Title: {title}",
|
|
26202
|
+
"update.text.projectWritten": "Set the project to gitifact {version} in {path}.",
|
|
26203
|
+
"update.text.projectAhead": "The project is set to gitifact {project}. This CLI ({version}) is older, so it was not lowered. Install it with {command}.",
|
|
26204
|
+
"update.later.postponed": "The notice for {latest} is quiet until {until}. A newer release is announced right away.",
|
|
26205
|
+
"update.later.nothing": "No newer release to postpone (running {version}).",
|
|
26206
|
+
"notice.release": "Version {latest} is available (running {version}). Update: {command}, then gitifact update \xB7 Later: gitifact update --later",
|
|
26207
|
+
"notice.projectNewer": "This project is set to gitifact {project} (running {version}). Install it with {command}.",
|
|
26208
|
+
"notice.projectOlder": "This CLI ({version}) is newer than the project ({project}). Run gitifact update to move the project to it.",
|
|
26209
|
+
"help.updateLater": "Quiet the announced newer release for 24 hours; a release newer than it is announced right away",
|
|
26210
|
+
"update.text.blockKept": "The project is set to a newer release than this CLI, so the instruction blocks were left as they are: {paths}"
|
|
26066
26211
|
};
|
|
26067
26212
|
|
|
26068
26213
|
// src/shared/i18n/index.ts
|
|
@@ -26414,7 +26559,7 @@ function storeReader(cwd) {
|
|
|
26414
26559
|
import { DatabaseSync } from "node:sqlite";
|
|
26415
26560
|
import { lstat, mkdir, rm, writeFile } from "node:fs/promises";
|
|
26416
26561
|
import { join as join2 } from "node:path";
|
|
26417
|
-
var CACHE_FORMAT =
|
|
26562
|
+
var CACHE_FORMAT = 11;
|
|
26418
26563
|
var CACHE_DIR = ".gitifact/cache";
|
|
26419
26564
|
var SCHEMA = `
|
|
26420
26565
|
-- Working-tree files already read: the modification time and size decide whether a file is read again.
|
|
@@ -26444,7 +26589,17 @@ var SCHEMA = `
|
|
|
26444
26589
|
CREATE TABLE lineage (head TEXT NOT NULL, pos INTEGER NOT NULL, oid TEXT NOT NULL, PRIMARY KEY (head, pos));
|
|
26445
26590
|
CREATE INDEX lineage_by_commit ON lineage (head, oid);
|
|
26446
26591
|
CREATE TABLE heads (head TEXT PRIMARY KEY, seen INTEGER NOT NULL);
|
|
26447
|
-
--
|
|
26592
|
+
-- Every commit the log has met, code commits too: who made it and when, and whether it is a format migration.
|
|
26593
|
+
-- 'reach' is 1 for the commits of the HEAD the log follows now; contributors are counted over those.
|
|
26594
|
+
CREATE TABLE log (oid TEXT PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL, time INTEGER NOT NULL, date TEXT NOT NULL,
|
|
26595
|
+
subject TEXT NOT NULL, migration INTEGER NOT NULL, reach INTEGER NOT NULL);
|
|
26596
|
+
CREATE INDEX log_by_reach ON log (reach, email);
|
|
26597
|
+
-- The store folders a commit touched: a feature folder, an instruction folder, or AGENTS.md.
|
|
26598
|
+
CREATE TABLE touches (oid TEXT NOT NULL, folder TEXT NOT NULL, PRIMARY KEY (oid, folder));
|
|
26599
|
+
CREATE INDEX touches_by_folder ON touches (folder);
|
|
26600
|
+
-- What the log follows: the HEAD ('head') and the fingerprint of the .mailmap its names were read with ('mailmap').
|
|
26601
|
+
CREATE TABLE state (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
26602
|
+
-- What the search box finds among the working tree's documents (scope 'checkout'); records are searched in their table.
|
|
26448
26603
|
-- The searched columns hold lower-cased text; trigram lets LIKE '%...%' use the index from three characters on.
|
|
26449
26604
|
CREATE VIRTUAL TABLE search USING fts5(
|
|
26450
26605
|
scope UNINDEXED, kind UNINDEXED, ref UNINDEXED, oid UNINDEXED, payload UNINDEXED,
|
|
@@ -26728,6 +26883,40 @@ function createDocumentCache(root, database) {
|
|
|
26728
26883
|
}));
|
|
26729
26884
|
});
|
|
26730
26885
|
},
|
|
26886
|
+
/**
|
|
26887
|
+
* The working documents of one kind whose title, place or text holds the query, a page at a time: a title that
|
|
26888
|
+
* starts with it first, then a title, a place, and the text; by title within each. `after` is the ID of the last
|
|
26889
|
+
* hit of the page before; one that no longer matches answers undefined. Each hit names its feature's ID.
|
|
26890
|
+
*/
|
|
26891
|
+
async searchKind(kind2, raw, after, limit) {
|
|
26892
|
+
const query = raw.trim().toLowerCase();
|
|
26893
|
+
await sync();
|
|
26894
|
+
return database.with((db) => {
|
|
26895
|
+
const literal2 = /[\\%_]/.test(query);
|
|
26896
|
+
const match = (column) => literal2 ? `instr(${column}, :q) > 0` : `${column} LIKE :like`;
|
|
26897
|
+
const ordered = `WITH hits AS (
|
|
26898
|
+
SELECT json_extract(payload, '$.id') AS id, payload, title, ref,
|
|
26899
|
+
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
|
|
26900
|
+
FROM search WHERE scope = 'checkout' AND kind = :kind AND (${match("title")} OR ${match("place")} OR ${match("body")})),
|
|
26901
|
+
ordered AS (SELECT *, row_number() OVER (ORDER BY rank, title, ref) AS n FROM hits)`;
|
|
26902
|
+
const params = { q: query, kind: kind2, ...literal2 ? {} : { like: containing(query) } };
|
|
26903
|
+
const total = Number(db.prepare(`${ordered} SELECT count(*) AS n FROM ordered`).get(params).n);
|
|
26904
|
+
let start = 0;
|
|
26905
|
+
if (after !== void 0) {
|
|
26906
|
+
const at = db.prepare(`${ordered} SELECT n FROM ordered WHERE id = :after`).get({ ...params, after });
|
|
26907
|
+
if (!at) return void 0;
|
|
26908
|
+
start = Number(at.n);
|
|
26909
|
+
}
|
|
26910
|
+
const rows = db.prepare(`${ordered} SELECT payload FROM ordered WHERE n > :start ORDER BY n LIMIT :limit`).all({ ...params, start, limit: limit + 1 });
|
|
26911
|
+
const features = new Map(db.prepare("SELECT feature, id FROM documents WHERE kind = 'feature'").all().map((r) => [r.feature, r.id]));
|
|
26912
|
+
const hits = rows.slice(0, limit).map((r) => {
|
|
26913
|
+
const p = JSON.parse(r.payload);
|
|
26914
|
+
const featureId2 = p.feature ? features.get(p.feature) : void 0;
|
|
26915
|
+
return { id: p.id, kind: kind2, title: p.title, where: p.where, line: snippet(p.body, query), ...featureId2 ? { featureId: featureId2 } : {} };
|
|
26916
|
+
});
|
|
26917
|
+
return { total, next: rows.length > limit ? hits[hits.length - 1].id : null, hits };
|
|
26918
|
+
});
|
|
26919
|
+
},
|
|
26731
26920
|
/** Documents that refer to `id` from their frontmatter: designs naming a requirement or a source. */
|
|
26732
26921
|
async referencing(id) {
|
|
26733
26922
|
await sync();
|
|
@@ -27096,20 +27285,19 @@ var listed = (e) => {
|
|
|
27096
27285
|
return { ...event2, before: reference2(e.before), after: reference2(e.after) };
|
|
27097
27286
|
};
|
|
27098
27287
|
var sideOf3 = (snapshot5, rev) => !snapshot5 ? null : rev ? { rev, path: snapshot5.path } : snapshot5;
|
|
27099
|
-
var titleOf = (e) => (e.after ?? e.before)?.title ?? e.id;
|
|
27100
27288
|
var DAY = 864e5;
|
|
27101
27289
|
var KEPT_HEADS = 4;
|
|
27102
27290
|
var RECENT_COMMITS = 3;
|
|
27103
27291
|
var RECENT_CHANGES = 12;
|
|
27104
|
-
function createHistory(database, git) {
|
|
27292
|
+
function createHistory(database, git, originals) {
|
|
27105
27293
|
const changes2 = createCommitChanges(git);
|
|
27294
|
+
const texts = originals ?? changes2;
|
|
27106
27295
|
const building = /* @__PURE__ */ new Map();
|
|
27107
27296
|
const built = /* @__PURE__ */ new Set();
|
|
27108
27297
|
function insert(db, commits, readers) {
|
|
27109
27298
|
const commit = db.prepare("INSERT OR IGNORE INTO commits (oid, reader) VALUES (?, ?)");
|
|
27110
27299
|
const change2 = db.prepare("INSERT OR IGNORE INTO changes (key, oid, ord, id, kind, types, email, date, before_spec, after_spec, needle, row, detail) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)");
|
|
27111
27300
|
const record4 = db.prepare("INSERT OR IGNORE INTO records (oid, id, title, sections) VALUES (?, ?, ?, ?)");
|
|
27112
|
-
const search = db.prepare("INSERT INTO search (scope, kind, ref, oid, payload, title, place, body) VALUES (?,?,?,?,?,?,?,?)");
|
|
27113
27301
|
transaction(db, () => {
|
|
27114
27302
|
for (const c of commits) {
|
|
27115
27303
|
if (!Number(commit.run(c.commit, readers.get(c.commit) ?? "current").changes)) continue;
|
|
@@ -27132,23 +27320,19 @@ function createHistory(database, git) {
|
|
|
27132
27320
|
JSON.stringify(row),
|
|
27133
27321
|
JSON.stringify(detail)
|
|
27134
27322
|
);
|
|
27135
|
-
const line2 = e.records.length ? e.records.map((r) => [r.title, ...r.sections.map((s) => s.body)].join(" ")).join(" \xB7 ") : e.message;
|
|
27136
|
-
const where2 = e.commit.slice(0, 7) + " \xB7 " + e.author;
|
|
27137
|
-
search.run(
|
|
27138
|
-
"history",
|
|
27139
|
-
"history",
|
|
27140
|
-
e.key,
|
|
27141
|
-
e.commit,
|
|
27142
|
-
JSON.stringify({ title: titleOf(e), where: where2, line: line2 }),
|
|
27143
|
-
titleOf(e).toLowerCase(),
|
|
27144
|
-
where2.toLowerCase(),
|
|
27145
|
-
[line2, e.message, e.id].join(" ").toLowerCase()
|
|
27146
|
-
);
|
|
27147
27323
|
});
|
|
27148
27324
|
}
|
|
27149
27325
|
});
|
|
27150
27326
|
}
|
|
27151
27327
|
async function build(head) {
|
|
27328
|
+
const known = await database.with((db) => {
|
|
27329
|
+
const seen = !!db.prepare("SELECT 1 FROM heads WHERE head = ?").get(head);
|
|
27330
|
+
if (seen) db.prepare("UPDATE heads SET seen = ? WHERE head = ?").run(Date.now(), head);
|
|
27331
|
+
return seen;
|
|
27332
|
+
});
|
|
27333
|
+
if (!known) await read(head);
|
|
27334
|
+
}
|
|
27335
|
+
async function read(head) {
|
|
27152
27336
|
const lineage = await changes2.lineage(head);
|
|
27153
27337
|
const readers = await changes2.readers(head, lineage);
|
|
27154
27338
|
const known = await database.with((db) => new Map(db.prepare("SELECT oid, reader FROM commits").all().map((r) => [r.oid, r.reader])));
|
|
@@ -27158,13 +27342,14 @@ function createHistory(database, git) {
|
|
|
27158
27342
|
db.prepare("DELETE FROM commits WHERE oid = ?").run(oid3);
|
|
27159
27343
|
db.prepare("DELETE FROM changes WHERE oid = ?").run(oid3);
|
|
27160
27344
|
db.prepare("DELETE FROM records WHERE oid = ?").run(oid3);
|
|
27161
|
-
db.prepare("DELETE FROM search WHERE scope = 'history' AND oid = ?").run(oid3);
|
|
27162
27345
|
}
|
|
27346
|
+
db.prepare("DELETE FROM lineage WHERE head != ?").run(head);
|
|
27347
|
+
db.prepare("DELETE FROM heads WHERE head != ?").run(head);
|
|
27163
27348
|
}));
|
|
27164
27349
|
const missing2 = lineage.filter((oid3) => !known.has(oid3) || stale.includes(oid3));
|
|
27165
27350
|
for (let i = 0; i < missing2.length; i += 100) {
|
|
27166
|
-
const
|
|
27167
|
-
await database.with((db) => insert(db,
|
|
27351
|
+
const read2 = await changes2.of(missing2.slice(i, i + 100), readers);
|
|
27352
|
+
await database.with((db) => insert(db, read2, readers));
|
|
27168
27353
|
}
|
|
27169
27354
|
await database.with((db) => transaction(db, () => {
|
|
27170
27355
|
db.prepare("DELETE FROM lineage WHERE head = ?").run(head);
|
|
@@ -27191,15 +27376,21 @@ function createHistory(database, git) {
|
|
|
27191
27376
|
async function readCommit(commit) {
|
|
27192
27377
|
const known = await database.with((db) => !!db.prepare("SELECT 1 FROM commits WHERE oid = ?").get(commit));
|
|
27193
27378
|
if (known) return;
|
|
27194
|
-
const
|
|
27195
|
-
if (
|
|
27379
|
+
const read2 = await changes2.of([commit]).catch(() => void 0);
|
|
27380
|
+
if (read2) await database.with((db) => insert(db, read2, /* @__PURE__ */ new Map()));
|
|
27381
|
+
}
|
|
27382
|
+
function sameRecord(db, commit, id) {
|
|
27383
|
+
if (!id.startsWith("H-")) return [id];
|
|
27384
|
+
const own2 = db.prepare("SELECT title, sections FROM records WHERE oid = ? AND id = ?").get(commit, id);
|
|
27385
|
+
if (!own2) return [id];
|
|
27386
|
+
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);
|
|
27196
27387
|
}
|
|
27197
27388
|
function hydrate(db, rows) {
|
|
27198
27389
|
const events = rows.map((row) => JSON.parse(row));
|
|
27199
|
-
const
|
|
27390
|
+
const read2 = db.prepare("SELECT id, title, sections FROM records WHERE oid = ?");
|
|
27200
27391
|
const byCommit = /* @__PURE__ */ new Map();
|
|
27201
27392
|
for (const e of events) if (e.records.length && !byCommit.has(e.commit)) {
|
|
27202
|
-
byCommit.set(e.commit, new Map(
|
|
27393
|
+
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) }])));
|
|
27203
27394
|
}
|
|
27204
27395
|
return events.map((e) => ({ ...e, records: e.records.flatMap((id) => {
|
|
27205
27396
|
const r = byCommit.get(e.commit)?.get(id);
|
|
@@ -27216,8 +27407,8 @@ function createHistory(database, git) {
|
|
|
27216
27407
|
if (value && "rev" in value) places.push({ rev: value.rev, path: value.path, specId: events[i][key]?.specId ?? "" });
|
|
27217
27408
|
}
|
|
27218
27409
|
});
|
|
27219
|
-
const
|
|
27220
|
-
const text4 = new Map(places.map((p, i) => [p.rev + ":" + p.path,
|
|
27410
|
+
const read2 = places.length ? await texts.sides(places) : [];
|
|
27411
|
+
const text4 = new Map(places.map((p, i) => [p.rev + ":" + p.path, read2[i] ?? null]));
|
|
27221
27412
|
const resolve = (value) => !value ? null : "rev" in value ? text4.get(value.rev + ":" + value.path) ?? null : value;
|
|
27222
27413
|
return events.map((event2, i) => ({ event: event2, before: resolve(sides[i].before), after: resolve(sides[i].after) }));
|
|
27223
27414
|
}
|
|
@@ -27249,7 +27440,7 @@ function createHistory(database, git) {
|
|
|
27249
27440
|
return {
|
|
27250
27441
|
ensure,
|
|
27251
27442
|
/** The document and reason files of one commit, read from Git and not kept. */
|
|
27252
|
-
filesAt: (rev) =>
|
|
27443
|
+
filesAt: (rev) => texts.tree(rev),
|
|
27253
27444
|
/**
|
|
27254
27445
|
* The changes that match, newest first, a page of whole commits: `limit` commits after the commit `after`, each
|
|
27255
27446
|
* with all of its matching changes, and how many changes and commits match in all. `next` is the last commit of the
|
|
@@ -27278,11 +27469,15 @@ function createHistory(database, git) {
|
|
|
27278
27469
|
};
|
|
27279
27470
|
});
|
|
27280
27471
|
},
|
|
27281
|
-
/**
|
|
27282
|
-
|
|
27472
|
+
/**
|
|
27473
|
+
* The changes of one commit without their text, a page at a time after the change `after` (a key): the commit page's
|
|
27474
|
+
* list. With `record`, only the changes that record explains: the record page's documents.
|
|
27475
|
+
*/
|
|
27476
|
+
async commitChanges(commit, after, limit, record4) {
|
|
27283
27477
|
await readCommit(commit);
|
|
27284
27478
|
return database.with((db) => {
|
|
27285
|
-
const
|
|
27479
|
+
const ids = record4 === void 0 ? void 0 : JSON.stringify(sameRecord(db, commit, record4));
|
|
27480
|
+
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);
|
|
27286
27481
|
const start = after === void 0 ? 0 : all.findIndex((r) => r.key === after) + 1;
|
|
27287
27482
|
if (after !== void 0 && !start) return void 0;
|
|
27288
27483
|
const shown = all.slice(start, start + limit);
|
|
@@ -27372,86 +27567,226 @@ function createHistory(database, git) {
|
|
|
27372
27567
|
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);
|
|
27373
27568
|
const newest = Math.max(0, ...commits.map((c) => Date.parse(c.date)).filter(Number.isFinite));
|
|
27374
27569
|
const pulse = commits.filter((c) => Date.parse(c.date) >= newest - 22 * DAY).map((c) => ({ date: c.date, count: Number(c.n) }));
|
|
27375
|
-
const
|
|
27570
|
+
const recent2 = commits.slice(0, RECENT_COMMITS).map((c) => ({
|
|
27376
27571
|
commit: c.oid,
|
|
27377
27572
|
count: Number(c.n),
|
|
27378
27573
|
events: hydrate(db, db.prepare("SELECT row FROM changes WHERE oid = ? ORDER BY ord LIMIT ?").all(c.oid, RECENT_CHANGES).map((r) => r.row))
|
|
27379
27574
|
}));
|
|
27380
|
-
return { total, byType: { created: count2("created"), modified: count2("modified"), moved: count2("moved"), deleted: count2("deleted") }, pulse, recent };
|
|
27575
|
+
return { total, byType: { created: count2("created"), modified: count2("modified"), moved: count2("moved"), deleted: count2("deleted") }, pulse, recent: recent2 };
|
|
27381
27576
|
});
|
|
27382
27577
|
},
|
|
27383
|
-
/**
|
|
27384
|
-
|
|
27578
|
+
/**
|
|
27579
|
+
* The record of `head`'s history with the commit that added it, and how many other records that commit holds (a
|
|
27580
|
+
* 0.7 reason written once over several documents counts once). Undefined when the history has no such record.
|
|
27581
|
+
*/
|
|
27582
|
+
async recordOf(head, id) {
|
|
27385
27583
|
await ensure(head);
|
|
27386
|
-
return database.with((db) =>
|
|
27584
|
+
return database.with((db) => {
|
|
27585
|
+
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);
|
|
27586
|
+
if (!found) return void 0;
|
|
27587
|
+
const all = db.prepare("SELECT id, title, sections FROM records WHERE oid = ?").all(found.oid);
|
|
27588
|
+
const groups = new Set(all.map((r) => r.id.startsWith("H-") ? "H\0" + r.title + "\0" + r.sections : r.id));
|
|
27589
|
+
return { commit: found.oid, record: { id, title: found.title, sections: JSON.parse(found.sections) }, others: groups.size - 1 };
|
|
27590
|
+
});
|
|
27387
27591
|
},
|
|
27388
27592
|
/**
|
|
27389
|
-
*
|
|
27390
|
-
*
|
|
27593
|
+
* The records of `head`'s history whose title, ID or sections hold the query, one hit per record file, a page at a
|
|
27594
|
+
* time: a title that starts with it first, then a title, the ID, and the sections; newest first within each. `after`
|
|
27595
|
+
* is the ID of the last hit of the page before; one that no longer matches answers undefined.
|
|
27391
27596
|
*/
|
|
27392
|
-
async
|
|
27597
|
+
async searchRecords(head, raw, after, limit) {
|
|
27393
27598
|
const query = raw.trim().toLowerCase();
|
|
27394
|
-
|
|
27395
|
-
if (head) await ensure(head);
|
|
27599
|
+
await ensure(head);
|
|
27396
27600
|
return database.with((db) => {
|
|
27397
|
-
const
|
|
27398
|
-
|
|
27399
|
-
|
|
27400
|
-
|
|
27401
|
-
|
|
27402
|
-
|
|
27403
|
-
|
|
27404
|
-
|
|
27405
|
-
|
|
27406
|
-
|
|
27407
|
-
|
|
27408
|
-
|
|
27409
|
-
|
|
27410
|
-
|
|
27411
|
-
|
|
27412
|
-
|
|
27413
|
-
|
|
27414
|
-
|
|
27415
|
-
|
|
27416
|
-
|
|
27417
|
-
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
|
|
27418
|
-
WHERE s.scope = 'history' AND (${match("s.title")} OR ${match("s.body")}) ORDER BY l.pos LIMIT 8`).all(head, needle, needle).map((r) => {
|
|
27419
|
-
const p = JSON.parse(r.payload);
|
|
27420
|
-
return { id: r.ref, kind: "history", title: p.title, where: p.where, line: snippet(p.line, query), key: r.ref };
|
|
27601
|
+
const ordered = `WITH own AS (
|
|
27602
|
+
SELECT r.id AS id, r.title AS title, r.oid AS oid, min(l.pos) AS pos, r.sections AS sections,
|
|
27603
|
+
lower((SELECT group_concat(json_extract(s.value, '$.body'), ' ') FROM json_each(r.sections) s)) AS body
|
|
27604
|
+
FROM lineage l JOIN records r ON r.oid = l.oid WHERE l.head = :head GROUP BY r.id),
|
|
27605
|
+
hits AS (SELECT *, CASE WHEN substr(lower(title), 1, length(:q)) = :q THEN 0 WHEN instr(lower(title), :q) > 0 THEN 1
|
|
27606
|
+
WHEN instr(lower(id), :q) > 0 THEN 2 ELSE 3 END AS rank
|
|
27607
|
+
FROM own WHERE instr(lower(title), :q) > 0 OR instr(lower(id), :q) > 0 OR instr(body, :q) > 0),
|
|
27608
|
+
ordered AS (SELECT *, row_number() OVER (ORDER BY rank, pos, id) AS n FROM hits)`;
|
|
27609
|
+
const params = { head, q: query };
|
|
27610
|
+
const total = Number(db.prepare(`${ordered} SELECT count(*) AS n FROM ordered`).get(params).n);
|
|
27611
|
+
let start = 0;
|
|
27612
|
+
if (after !== void 0) {
|
|
27613
|
+
const at = db.prepare(`${ordered} SELECT n FROM ordered WHERE id = :after`).get({ ...params, after });
|
|
27614
|
+
if (!at) return void 0;
|
|
27615
|
+
start = Number(at.n);
|
|
27616
|
+
}
|
|
27617
|
+
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 });
|
|
27618
|
+
const hits = rows.slice(0, limit).map((r) => {
|
|
27619
|
+
const text4 = JSON.parse(r.sections).map((s) => s.body).join(" ");
|
|
27620
|
+
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 };
|
|
27421
27621
|
});
|
|
27422
|
-
return [
|
|
27622
|
+
return { total, next: rows.length > limit ? hits[hits.length - 1].id : null, hits };
|
|
27623
|
+
});
|
|
27624
|
+
}
|
|
27625
|
+
};
|
|
27626
|
+
}
|
|
27627
|
+
|
|
27628
|
+
// src/adapters/cache/commit-log.ts
|
|
27629
|
+
import { lstat as lstat3 } from "node:fs/promises";
|
|
27630
|
+
import { join as join4 } from "node:path";
|
|
27631
|
+
var FORMAT2 = `%x1e%H%x00%aN%x00%aE%x00%at%x00%aI%x00%s%x00%(trailers:key=${MIGRATION_TRAILER},valueonly,separator=%x2C)`;
|
|
27632
|
+
var CHUNK = 1e5;
|
|
27633
|
+
var folderOf2 = (path3) => path3 === "AGENTS.md" ? path3 : new RegExp(`^(${SPEC_ROOT.replace(/\./g, "\\.")}/[^/]+|${INSTRUCTIONS_ROOT.replace(/\./g, "\\.")}/[^/]+)/`).exec(path3)?.[1];
|
|
27634
|
+
function createCommitLog(database, git, root) {
|
|
27635
|
+
let following;
|
|
27636
|
+
let followingHead;
|
|
27637
|
+
const read = async (args) => git.decode(await git.run(args));
|
|
27638
|
+
async function commits(range) {
|
|
27639
|
+
const out = [];
|
|
27640
|
+
for (let skip = 0; ; skip += CHUNK) {
|
|
27641
|
+
const text4 = await read(["log", `--format=${FORMAT2}`, `--max-count=${CHUNK}`, `--skip=${skip}`, ...range, "--"]);
|
|
27642
|
+
const chunk = text4.split("").filter(Boolean).map((entry2) => {
|
|
27643
|
+
const [oid3, name2, email3, time3, date5, subject, migration] = entry2.split("\0");
|
|
27644
|
+
return { oid: oid3, name: name2 ?? "", email: email3 ?? "", time: Number(time3), date: date5 ?? "", subject: subject ?? "", migration: !!migration?.trim() };
|
|
27645
|
+
});
|
|
27646
|
+
out.push(...chunk);
|
|
27647
|
+
if (chunk.length < CHUNK) return out;
|
|
27648
|
+
}
|
|
27649
|
+
}
|
|
27650
|
+
async function touches(range) {
|
|
27651
|
+
const text4 = await read(["log", "--format=%x1e%H", "-z", "--name-only", "--no-renames", ...range, "--", SPEC_ROOT, INSTRUCTIONS_ROOT, "AGENTS.md"]);
|
|
27652
|
+
const out = [];
|
|
27653
|
+
for (const entry2 of text4.split("").filter(Boolean)) {
|
|
27654
|
+
const [oid3, ...paths2] = entry2.split("\0");
|
|
27655
|
+
const folders = new Set(paths2.map((p) => folderOf2(p.replace(/^\n/, ""))).filter((f) => !!f));
|
|
27656
|
+
for (const folder of folders) out.push([oid3.trim(), folder]);
|
|
27657
|
+
}
|
|
27658
|
+
return out;
|
|
27659
|
+
}
|
|
27660
|
+
async function mailmap() {
|
|
27661
|
+
const info5 = await lstat3(join4(root, ".mailmap")).catch(() => void 0);
|
|
27662
|
+
return info5 ? info5.mtimeMs + ":" + info5.size : "-";
|
|
27663
|
+
}
|
|
27664
|
+
const stateOf = (db, key) => db.prepare("SELECT value FROM state WHERE key = ?").get(key)?.value;
|
|
27665
|
+
async function follow(head, attempts = 3) {
|
|
27666
|
+
const names = await mailmap();
|
|
27667
|
+
const [before, named] = await database.with((db) => [stateOf(db, "head"), stateOf(db, "mailmap")]);
|
|
27668
|
+
if (before === head && named === names) return;
|
|
27669
|
+
let change2;
|
|
27670
|
+
if (before && named === names) {
|
|
27671
|
+
try {
|
|
27672
|
+
const range = [head, "^" + before];
|
|
27673
|
+
const [added2, removed2, touched2] = await Promise.all([commits(range), read(["rev-list", before, "^" + head, "--"]), touches(range)]);
|
|
27674
|
+
change2 = { added: added2, removed: removed2.split("\n").filter(Boolean), touched: touched2, whole: false };
|
|
27675
|
+
} catch {
|
|
27676
|
+
change2 = void 0;
|
|
27677
|
+
}
|
|
27678
|
+
}
|
|
27679
|
+
if (!change2) {
|
|
27680
|
+
const [added2, touched2] = await Promise.all([commits([head]), touches([head])]);
|
|
27681
|
+
change2 = { added: added2, removed: [], touched: touched2, whole: true };
|
|
27682
|
+
}
|
|
27683
|
+
const { added, removed, touched, whole } = change2;
|
|
27684
|
+
const written = await database.with((db) => transaction(db, () => {
|
|
27685
|
+
if (stateOf(db, "head") !== before || stateOf(db, "mailmap") !== named) return false;
|
|
27686
|
+
if (whole) {
|
|
27687
|
+
db.exec("DELETE FROM log");
|
|
27688
|
+
db.exec("DELETE FROM touches");
|
|
27689
|
+
}
|
|
27690
|
+
const put = db.prepare(`INSERT INTO log (oid, name, email, time, date, subject, migration, reach) VALUES (?, ?, ?, ?, ?, ?, ?, 1)
|
|
27691
|
+
ON CONFLICT (oid) DO UPDATE SET name = excluded.name, email = excluded.email, reach = 1`);
|
|
27692
|
+
for (const c of added) put.run(c.oid, c.name, c.email, c.time, c.date, c.subject, c.migration ? 1 : 0);
|
|
27693
|
+
const leave = db.prepare("UPDATE log SET reach = 0 WHERE oid = ?");
|
|
27694
|
+
for (const oid3 of removed) leave.run(oid3);
|
|
27695
|
+
const touch = db.prepare("INSERT OR IGNORE INTO touches (oid, folder) VALUES (?, ?)");
|
|
27696
|
+
for (const [oid3, folder] of touched) touch.run(oid3, folder);
|
|
27697
|
+
const set2 = db.prepare("INSERT INTO state (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value = excluded.value");
|
|
27698
|
+
set2.run("head", head);
|
|
27699
|
+
set2.run("mailmap", names);
|
|
27700
|
+
return true;
|
|
27701
|
+
}));
|
|
27702
|
+
if (!written && attempts > 1) return follow(head, attempts - 1);
|
|
27703
|
+
}
|
|
27704
|
+
async function ensure(head) {
|
|
27705
|
+
while (following && followingHead !== head) await following.catch(() => void 0);
|
|
27706
|
+
if (!following) {
|
|
27707
|
+
followingHead = head;
|
|
27708
|
+
following = follow(head).finally(() => {
|
|
27709
|
+
following = void 0;
|
|
27710
|
+
followingHead = void 0;
|
|
27711
|
+
});
|
|
27712
|
+
}
|
|
27713
|
+
return following;
|
|
27714
|
+
}
|
|
27715
|
+
return {
|
|
27716
|
+
ensure,
|
|
27717
|
+
/** Everyone who committed to `head`, most commits first, format migrations left out. `latest` is their newest commit. */
|
|
27718
|
+
async contributors(head) {
|
|
27719
|
+
await ensure(head);
|
|
27720
|
+
return database.with((db) => db.prepare(`SELECT email, name, count(*) AS commits, max(time) AS time, date AS latest FROM log
|
|
27721
|
+
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 })));
|
|
27722
|
+
},
|
|
27723
|
+
/**
|
|
27724
|
+
* The commits of `head` a hash names: the whole hash, or its start from seven characters on, as `git log --oneline`
|
|
27725
|
+
* prints it. Anything shorter or not a hash names none. Newest first, a page after the commit `after`.
|
|
27726
|
+
*/
|
|
27727
|
+
async commitsNamed(head, raw, after, limit) {
|
|
27728
|
+
const prefix = raw.trim().toLowerCase();
|
|
27729
|
+
if (!/^[0-9a-f]{7,64}$/.test(prefix)) return { total: 0, next: null, hits: [] };
|
|
27730
|
+
await ensure(head);
|
|
27731
|
+
return database.with((db) => {
|
|
27732
|
+
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);
|
|
27733
|
+
const start = after === void 0 ? 0 : rows.findIndex((r) => r.oid === after) + 1;
|
|
27734
|
+
if (after !== void 0 && !start) return void 0;
|
|
27735
|
+
const shown = rows.slice(start, start + limit);
|
|
27736
|
+
return {
|
|
27737
|
+
total: rows.length,
|
|
27738
|
+
next: start + shown.length < rows.length ? shown[shown.length - 1].oid : null,
|
|
27739
|
+
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 }))
|
|
27740
|
+
};
|
|
27741
|
+
});
|
|
27742
|
+
},
|
|
27743
|
+
/** Each store folder's authors, most commits first, and when it was last touched, format migrations left out. */
|
|
27744
|
+
async folders(head) {
|
|
27745
|
+
await ensure(head);
|
|
27746
|
+
return database.with((db) => {
|
|
27747
|
+
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
|
|
27748
|
+
FROM touches t JOIN log l ON l.oid = t.oid WHERE l.reach = 1 AND l.migration = 0
|
|
27749
|
+
GROUP BY t.folder, l.email ORDER BY t.folder, commits DESC, name, email`).all();
|
|
27750
|
+
const out = /* @__PURE__ */ new Map();
|
|
27751
|
+
for (const r of rows) {
|
|
27752
|
+
let entry2 = out.get(r.folder);
|
|
27753
|
+
if (!entry2) out.set(r.folder, entry2 = { people: [], latest: r.latest, time: r.time });
|
|
27754
|
+
entry2.people.push({ name: r.name, email: r.email, commits: Number(r.commits), latest: r.latest });
|
|
27755
|
+
if (r.time > entry2.time) {
|
|
27756
|
+
entry2.time = r.time;
|
|
27757
|
+
entry2.latest = r.latest;
|
|
27758
|
+
}
|
|
27759
|
+
}
|
|
27760
|
+
return new Map([...out].map(([folder, { people, latest }]) => [folder, { people, latest }]));
|
|
27423
27761
|
});
|
|
27424
27762
|
}
|
|
27425
27763
|
};
|
|
27426
27764
|
}
|
|
27427
27765
|
|
|
27428
27766
|
// src/adapters/cache/index.ts
|
|
27429
|
-
function openCache(root, git) {
|
|
27767
|
+
function openCache(root, git, originals) {
|
|
27430
27768
|
const database = createCacheDatabase(root);
|
|
27431
27769
|
const documents = createDocumentCache(root, database);
|
|
27432
|
-
const history = createHistory(database, git);
|
|
27770
|
+
const history = createHistory(database, git, originals);
|
|
27771
|
+
const log = createCommitLog(database, git, root);
|
|
27433
27772
|
return {
|
|
27434
27773
|
documents,
|
|
27435
27774
|
history,
|
|
27436
|
-
|
|
27437
|
-
|
|
27438
|
-
async search(head, query) {
|
|
27439
|
-
await documents.sync();
|
|
27440
|
-
return history.search(head, query);
|
|
27441
|
-
}
|
|
27775
|
+
log,
|
|
27776
|
+
location: database.location
|
|
27442
27777
|
};
|
|
27443
27778
|
}
|
|
27444
27779
|
|
|
27445
27780
|
// src/adapters/git/pending-records.ts
|
|
27446
|
-
import { lstat as
|
|
27447
|
-
import { join as
|
|
27781
|
+
import { lstat as lstat4, readdir as readdir3, readFile as readFile3 } from "node:fs/promises";
|
|
27782
|
+
import { join as join5 } from "node:path";
|
|
27448
27783
|
var decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
27449
|
-
var info = (path3) =>
|
|
27784
|
+
var info = (path3) => lstat4(path3).catch((error62) => {
|
|
27450
27785
|
if (error62.code === "ENOENT") return void 0;
|
|
27451
27786
|
throw error62;
|
|
27452
27787
|
});
|
|
27453
27788
|
async function recordFolders(root) {
|
|
27454
|
-
return (await readdir3(
|
|
27789
|
+
return (await readdir3(join5(root, ...RECORDS_ROOT.split("/"))).catch((error62) => {
|
|
27455
27790
|
if (error62.code === "ENOENT") return [];
|
|
27456
27791
|
throw error62;
|
|
27457
27792
|
})).filter(isRecordFolder);
|
|
@@ -27459,7 +27794,7 @@ async function recordFolders(root) {
|
|
|
27459
27794
|
async function findRecord(root, id) {
|
|
27460
27795
|
for (const folder of await recordFolders(root)) {
|
|
27461
27796
|
const path3 = `${RECORDS_ROOT}/${folder}/${id}.md`;
|
|
27462
|
-
if ((await info(
|
|
27797
|
+
if ((await info(join5(root, ...path3.split("/"))))?.isFile()) return path3;
|
|
27463
27798
|
}
|
|
27464
27799
|
return void 0;
|
|
27465
27800
|
}
|
|
@@ -27475,7 +27810,7 @@ async function readPendingRecords(root, run) {
|
|
|
27475
27810
|
altered.push(path3);
|
|
27476
27811
|
continue;
|
|
27477
27812
|
}
|
|
27478
|
-
const file2 =
|
|
27813
|
+
const file2 = join5(root, ...path3.split("/"));
|
|
27479
27814
|
const stat3 = await info(file2);
|
|
27480
27815
|
if (!stat3?.isFile() || stat3.isSymbolicLink()) {
|
|
27481
27816
|
problems.push(docProblem("PATH_UNSUPPORTED", path3));
|
|
@@ -27497,30 +27832,30 @@ async function readPendingRecords(root, run) {
|
|
|
27497
27832
|
if (!id) continue;
|
|
27498
27833
|
for (const folder of folders) {
|
|
27499
27834
|
const other = `${RECORDS_ROOT}/${folder}/${id}.md`;
|
|
27500
|
-
if (other !== path3 && !files.has(other) && (await info(
|
|
27835
|
+
if (other !== path3 && !files.has(other) && (await info(join5(root, ...other.split("/"))))?.isFile()) problems.push(docProblem("DUPLICATE_RECORD_ID", path3, { id, other }));
|
|
27501
27836
|
}
|
|
27502
27837
|
}
|
|
27503
27838
|
return { files, altered: altered.sort(), problems };
|
|
27504
27839
|
}
|
|
27505
27840
|
|
|
27506
27841
|
// src/adapters/filesystem/config-file.ts
|
|
27507
|
-
import { lstat as
|
|
27508
|
-
import { join as
|
|
27842
|
+
import { lstat as lstat5, readFile as readFile4, realpath, mkdir as mkdir2, open as open2, link, rename, unlink } from "node:fs/promises";
|
|
27843
|
+
import { join as join6 } from "node:path";
|
|
27509
27844
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
27510
27845
|
var missing = (error62) => error62.code === "ENOENT";
|
|
27511
|
-
var fileInfo = (path3) =>
|
|
27846
|
+
var fileInfo = (path3) => lstat5(path3).catch((error62) => {
|
|
27512
27847
|
if (missing(error62)) return void 0;
|
|
27513
27848
|
throw error62;
|
|
27514
27849
|
});
|
|
27515
27850
|
async function configDirectory(root) {
|
|
27516
|
-
const path3 =
|
|
27851
|
+
const path3 = join6(root, ".gitifact");
|
|
27517
27852
|
const info5 = await fileInfo(path3);
|
|
27518
27853
|
if (info5 && (!info5.isDirectory() || info5.isSymbolicLink())) throw new InitError("PATH_CONFLICT", t2("config.storeNotDirectory"));
|
|
27519
27854
|
return info5;
|
|
27520
27855
|
}
|
|
27521
27856
|
async function readConfigFile(root) {
|
|
27522
27857
|
await configDirectory(root);
|
|
27523
|
-
const path3 =
|
|
27858
|
+
const path3 = join6(root, ".gitifact", "config.json");
|
|
27524
27859
|
const info5 = await fileInfo(path3);
|
|
27525
27860
|
if (!info5) return void 0;
|
|
27526
27861
|
if (!info5.isFile() || info5.isSymbolicLink() || info5.size > 65536) throw new InitError("PATH_CONFLICT", t2("config.fileShape"));
|
|
@@ -27537,7 +27872,7 @@ async function readConfigFile(root) {
|
|
|
27537
27872
|
}
|
|
27538
27873
|
}
|
|
27539
27874
|
async function publishConfig(root, text4, recheck, beforePublish, replace = false) {
|
|
27540
|
-
const directory =
|
|
27875
|
+
const directory = join6(root, ".gitifact");
|
|
27541
27876
|
await configDirectory(root);
|
|
27542
27877
|
await mkdir2(directory).catch((error62) => {
|
|
27543
27878
|
if (error62.code !== "EEXIST") throw error62;
|
|
@@ -27550,7 +27885,7 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
|
|
|
27550
27885
|
throw new InitError("INPUT_CHANGED", t2("config.directoryChanged"));
|
|
27551
27886
|
}
|
|
27552
27887
|
};
|
|
27553
|
-
const temporary =
|
|
27888
|
+
const temporary = join6(directory, ".init-" + randomUUID2() + ".tmp");
|
|
27554
27889
|
const handle = await open2(temporary, "wx", 384);
|
|
27555
27890
|
const tempOwner = await handle.stat();
|
|
27556
27891
|
try {
|
|
@@ -27567,8 +27902,8 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
|
|
|
27567
27902
|
const bytes = await readFile4(temporary, "utf8");
|
|
27568
27903
|
if (bytes !== text4) throw new InitError("INPUT_CHANGED", t2("config.temporaryChanged"));
|
|
27569
27904
|
parseManagedConfig(bytes);
|
|
27570
|
-
if (replace) await rename(temporary,
|
|
27571
|
-
else await link(temporary,
|
|
27905
|
+
if (replace) await rename(temporary, join6(directory, "config.json"));
|
|
27906
|
+
else await link(temporary, join6(directory, "config.json"));
|
|
27572
27907
|
} finally {
|
|
27573
27908
|
await unchanged();
|
|
27574
27909
|
const current2 = await fileInfo(temporary);
|
|
@@ -27577,11 +27912,11 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
|
|
|
27577
27912
|
}
|
|
27578
27913
|
|
|
27579
27914
|
// src/adapters/filesystem/instruction-folder.ts
|
|
27580
|
-
import { lstat as
|
|
27581
|
-
import { join as
|
|
27915
|
+
import { lstat as lstat6, open as open3, readdir as readdir4, readFile as readFile5 } from "node:fs/promises";
|
|
27916
|
+
import { join as join7 } from "node:path";
|
|
27582
27917
|
var INSTRUCTION_FILE_LIMIT = 500;
|
|
27583
27918
|
var INSTRUCTION_TEXT_LIMIT = 512 * 1024;
|
|
27584
|
-
var
|
|
27919
|
+
var folderOf3 = (root, instructionPath) => join7(root, ...instructionPath.split("/").slice(0, -1));
|
|
27585
27920
|
async function listInstructionFiles(root, instructionPath) {
|
|
27586
27921
|
const files = [];
|
|
27587
27922
|
let limited = false;
|
|
@@ -27594,21 +27929,21 @@ async function listInstructionFiles(root, instructionPath) {
|
|
|
27594
27929
|
return;
|
|
27595
27930
|
}
|
|
27596
27931
|
const path3 = prefix + entry2.name;
|
|
27597
|
-
if (entry2.isDirectory()) await visit2(
|
|
27598
|
-
else if (entry2.isFile() && path3 !== INSTRUCTION_FILE) files.push({ path: path3, size: (await
|
|
27932
|
+
if (entry2.isDirectory()) await visit2(join7(folder, entry2.name), path3 + "/", depth + 1);
|
|
27933
|
+
else if (entry2.isFile() && path3 !== INSTRUCTION_FILE) files.push({ path: path3, size: (await lstat6(join7(folder, entry2.name))).size });
|
|
27599
27934
|
}
|
|
27600
27935
|
}
|
|
27601
|
-
await visit2(
|
|
27936
|
+
await visit2(folderOf3(root, instructionPath), "", 0);
|
|
27602
27937
|
return { files, limited };
|
|
27603
27938
|
}
|
|
27604
27939
|
var FRONTMATTER_WINDOW = 8 * 1024;
|
|
27605
27940
|
async function describeFiles(root, instructionPath, files) {
|
|
27606
|
-
const folder =
|
|
27941
|
+
const folder = folderOf3(root, instructionPath);
|
|
27607
27942
|
return Promise.all(files.map(async (file2) => {
|
|
27608
27943
|
if (!isInstructionReference(file2.path)) return file2;
|
|
27609
27944
|
let handle;
|
|
27610
27945
|
try {
|
|
27611
|
-
handle = await open3(
|
|
27946
|
+
handle = await open3(join7(folder, ...file2.path.split("/")), "r");
|
|
27612
27947
|
const buffer = Buffer.alloc(Math.min(FRONTMATTER_WINDOW, file2.size));
|
|
27613
27948
|
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
27614
27949
|
return { ...file2, ...parseInstructionFile(file2.path, buffer.subarray(0, bytesRead).toString("utf8")) };
|
|
@@ -27626,13 +27961,13 @@ async function readInstructionFiles(root, instructionPath) {
|
|
|
27626
27961
|
async function readInstructionFile(root, instructionPath, path3) {
|
|
27627
27962
|
const parts = path3.split("/");
|
|
27628
27963
|
if (parts.some((p) => !p || p === "." || p === ".." || /[\\:\0]/.test(p))) return void 0;
|
|
27629
|
-
let full =
|
|
27964
|
+
let full = folderOf3(root, instructionPath);
|
|
27630
27965
|
for (const [index, part] of parts.entries()) {
|
|
27631
|
-
full =
|
|
27632
|
-
const info5 = await
|
|
27966
|
+
full = join7(full, part);
|
|
27967
|
+
const info5 = await lstat6(full).catch(() => void 0);
|
|
27633
27968
|
if (!info5 || info5.isSymbolicLink() || (index < parts.length - 1 ? !info5.isDirectory() : !info5.isFile())) return void 0;
|
|
27634
27969
|
}
|
|
27635
|
-
const size = (await
|
|
27970
|
+
const size = (await lstat6(full)).size;
|
|
27636
27971
|
if (size > INSTRUCTION_TEXT_LIMIT) return { text: null, size, binary: false, tooLarge: true };
|
|
27637
27972
|
const bytes = await readFile5(full);
|
|
27638
27973
|
try {
|
|
@@ -27644,12 +27979,12 @@ async function readInstructionFile(root, instructionPath, path3) {
|
|
|
27644
27979
|
}
|
|
27645
27980
|
|
|
27646
27981
|
// src/adapters/filesystem/agents-file.ts
|
|
27647
|
-
import { lstat as
|
|
27648
|
-
import { join as
|
|
27982
|
+
import { lstat as lstat7, readFile as readFile6 } from "node:fs/promises";
|
|
27983
|
+
import { join as join8 } from "node:path";
|
|
27649
27984
|
var LIMIT = 1024 * 1024;
|
|
27650
27985
|
async function readAgentsFile(root) {
|
|
27651
|
-
const file2 =
|
|
27652
|
-
const info5 = await
|
|
27986
|
+
const file2 = join8(root, "AGENTS.md");
|
|
27987
|
+
const info5 = await lstat7(file2).catch(() => void 0);
|
|
27653
27988
|
if (!info5?.isFile() || info5.size > LIMIT) return null;
|
|
27654
27989
|
try {
|
|
27655
27990
|
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: false }).decode(await readFile6(file2));
|
|
@@ -27726,8 +28061,8 @@ async function documentsAtHead(project, head, paths2) {
|
|
|
27726
28061
|
|
|
27727
28062
|
// src/server/checkout/stamp.ts
|
|
27728
28063
|
import { createHash as createHash2 } from "node:crypto";
|
|
27729
|
-
import { lstat as
|
|
27730
|
-
import { join as
|
|
28064
|
+
import { lstat as lstat8 } from "node:fs/promises";
|
|
28065
|
+
import { join as join9 } from "node:path";
|
|
27731
28066
|
function createStampReader(root, inherited = process.env) {
|
|
27732
28067
|
const runner = createGitRunner();
|
|
27733
28068
|
const env = { ...inherited, GIT_OPTIONAL_LOCKS: "0", GIT_NO_REPLACE_OBJECTS: "1", GIT_TERMINAL_PROMPT: "0", GIT_LITERAL_PATHSPECS: "0", LC_ALL: "C" };
|
|
@@ -27739,7 +28074,7 @@ function createStampReader(root, inherited = process.env) {
|
|
|
27739
28074
|
]);
|
|
27740
28075
|
const paths2 = status.split("\0").filter(Boolean).map((entry2) => entry2.slice(3)).filter((path3) => !path3.startsWith(".gitifact/cache/"));
|
|
27741
28076
|
const seen = await Promise.all(paths2.map(async (path3) => {
|
|
27742
|
-
const info5 = await
|
|
28077
|
+
const info5 = await lstat8(join9(root, ...path3.split("/"))).catch(() => void 0);
|
|
27743
28078
|
return path3 + " " + (info5 ? info5.mtimeMs + ":" + info5.size : "-");
|
|
27744
28079
|
}));
|
|
27745
28080
|
return createHash2("sha256").update(head.trim() + "\n" + seen.sort().join("\n")).digest("hex").slice(0, 20);
|
|
@@ -27747,19 +28082,14 @@ function createStampReader(root, inherited = process.env) {
|
|
|
27747
28082
|
}
|
|
27748
28083
|
|
|
27749
28084
|
// src/server/checkout/checkout-reader.ts
|
|
27750
|
-
var tally = (people, name2, email3, latest) => {
|
|
27751
|
-
const person = people.get(email3);
|
|
27752
|
-
if (person) person.commits++;
|
|
27753
|
-
else people.set(email3, { name: name2, email: email3, latest, commits: 1 });
|
|
27754
|
-
};
|
|
27755
28085
|
async function settled(reads) {
|
|
27756
28086
|
const results = await Promise.allSettled(reads);
|
|
27757
28087
|
const failure2 = results.find((r) => r.status === "rejected");
|
|
27758
28088
|
if (failure2) throw failure2.reason;
|
|
27759
28089
|
return results.map((r) => r.value);
|
|
27760
28090
|
}
|
|
27761
|
-
var
|
|
27762
|
-
function checkoutDocuments(states) {
|
|
28091
|
+
var folderOf4 = (path3) => path3.split("/").slice(0, 3).join("/");
|
|
28092
|
+
function checkoutDocuments(states, authors) {
|
|
27763
28093
|
const documents = states.documents;
|
|
27764
28094
|
const standing2 = (id) => {
|
|
27765
28095
|
const previousPath = states.previousPath(id);
|
|
@@ -27772,30 +28102,45 @@ function checkoutDocuments(states) {
|
|
|
27772
28102
|
return { id: s.id, ...target ? { title: target.title, path: target.path } : {}, ...s.note ? { note: s.note } : {} };
|
|
27773
28103
|
};
|
|
27774
28104
|
const arranged = arrangeDocuments(documents);
|
|
27775
|
-
const features = arranged.features.map(({ index, requirements, designs }) =>
|
|
27776
|
-
|
|
27777
|
-
|
|
27778
|
-
|
|
27779
|
-
|
|
27780
|
-
|
|
27781
|
-
|
|
27782
|
-
|
|
27783
|
-
|
|
27784
|
-
id:
|
|
27785
|
-
|
|
27786
|
-
|
|
27787
|
-
|
|
27788
|
-
|
|
27789
|
-
|
|
27790
|
-
|
|
27791
|
-
|
|
27792
|
-
|
|
27793
|
-
|
|
28105
|
+
const features = arranged.features.map(({ index, requirements, designs }) => {
|
|
28106
|
+
const entry2 = authors.get(folderOf4(index.path));
|
|
28107
|
+
return {
|
|
28108
|
+
id: index.id,
|
|
28109
|
+
path: index.path,
|
|
28110
|
+
title: index.title,
|
|
28111
|
+
description: index.description,
|
|
28112
|
+
body: index.body,
|
|
28113
|
+
...standing2(index.id),
|
|
28114
|
+
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) })),
|
|
28115
|
+
designs: designs.map((d) => ({
|
|
28116
|
+
id: d.id,
|
|
28117
|
+
path: d.path,
|
|
28118
|
+
title: d.title,
|
|
28119
|
+
description: d.description,
|
|
28120
|
+
order: d.order,
|
|
28121
|
+
body: d.body,
|
|
28122
|
+
requirements: d.requirements,
|
|
28123
|
+
sources: d.sources.map(source2),
|
|
28124
|
+
...standing2(d.id)
|
|
28125
|
+
})),
|
|
28126
|
+
// Who committed to the feature folder, most first, and when it was last touched; nobody until first committed.
|
|
28127
|
+
contributors: entry2?.people ?? [],
|
|
28128
|
+
updatedAt: entry2?.latest ?? null
|
|
28129
|
+
};
|
|
28130
|
+
});
|
|
28131
|
+
const instructions2 = arranged.instructions.map((k) => ({
|
|
28132
|
+
id: k.id,
|
|
28133
|
+
name: k.name,
|
|
28134
|
+
path: k.path,
|
|
28135
|
+
title: k.title,
|
|
28136
|
+
description: k.description,
|
|
28137
|
+
body: k.body,
|
|
28138
|
+
...standing2(k.id),
|
|
28139
|
+
updatedAt: authors.get(folderOf4(k.path))?.latest ?? null
|
|
27794
28140
|
}));
|
|
27795
|
-
const instructions2 = arranged.instructions.map((k) => ({ id: k.id, name: k.name, path: k.path, title: k.title, description: k.description, body: k.body, ...standing2(k.id) }));
|
|
27796
28141
|
return { features, instructions: instructions2, orphans: arranged.orphans };
|
|
27797
28142
|
}
|
|
27798
|
-
function createCheckoutReader(root,
|
|
28143
|
+
function createCheckoutReader(root, cache, inherited = process.env) {
|
|
27799
28144
|
const reader = storeReader(root);
|
|
27800
28145
|
const runner = createGitRunner();
|
|
27801
28146
|
const env = { ...inherited, GIT_OPTIONAL_LOCKS: "0", GIT_NO_REPLACE_OBJECTS: "1", GIT_TERMINAL_PROMPT: "0", LC_ALL: "C" };
|
|
@@ -27808,87 +28153,36 @@ function createCheckoutReader(root, sessionId, cache, inherited = process.env) {
|
|
|
27808
28153
|
};
|
|
27809
28154
|
const pending = /* @__PURE__ */ new Map();
|
|
27810
28155
|
const readStamp = createStampReader(root, inherited);
|
|
27811
|
-
const notMigration = ["-E", "--invert-grep", `--grep=^${MIGRATION_TRAILER}: `];
|
|
27812
|
-
async function storeAuthors(head) {
|
|
27813
|
-
const text4 = await git(["log", ...notMigration, "--format=%x1e%aN%x00%aE%x00%aI", "-z", "--name-only", "--no-renames", "--max-count=20000", head, "--", ".gitifact/spec", INSTRUCTIONS_ROOT, "AGENTS.md"]);
|
|
27814
|
-
const folders = /* @__PURE__ */ new Map();
|
|
27815
|
-
const instructions2 = /* @__PURE__ */ new Map();
|
|
27816
|
-
for (const chunk of text4.split("")) {
|
|
27817
|
-
if (!chunk) continue;
|
|
27818
|
-
const [name2, email3, date5, ...paths2] = chunk.split("\0");
|
|
27819
|
-
if (!name2 || !email3 || !date5) throw new StoreError(t2("specReader.authorUnreadable"));
|
|
27820
|
-
const seen = /* @__PURE__ */ new Set();
|
|
27821
|
-
for (const raw of paths2) {
|
|
27822
|
-
const path3 = raw.replace(/^\n/, "");
|
|
27823
|
-
if (!path3) continue;
|
|
27824
|
-
const folder = /^(\.gitifact\/spec\/[^/]+)\//.exec(path3)?.[1];
|
|
27825
|
-
if (folder && !seen.has(folder)) {
|
|
27826
|
-
seen.add(folder);
|
|
27827
|
-
let entry2 = folders.get(folder);
|
|
27828
|
-
if (!entry2) folders.set(folder, entry2 = { people: /* @__PURE__ */ new Map(), count: 0, latest: date5 });
|
|
27829
|
-
if (entry2.count < 2e3) {
|
|
27830
|
-
entry2.count++;
|
|
27831
|
-
tally(entry2.people, name2, email3, date5);
|
|
27832
|
-
}
|
|
27833
|
-
}
|
|
27834
|
-
const instruction2 = path3 === "AGENTS.md" ? path3 : /^(\.gitifact\/instructions\/[^/]+)\//.exec(path3)?.[1];
|
|
27835
|
-
if (instruction2 && !instructions2.has(instruction2)) instructions2.set(instruction2, date5);
|
|
27836
|
-
}
|
|
27837
|
-
}
|
|
27838
|
-
return { folders, instructions: instructions2 };
|
|
27839
|
-
}
|
|
27840
28156
|
async function read() {
|
|
27841
28157
|
const raw = await readConfigFile(root);
|
|
27842
28158
|
if (!raw) throw new StoreError(t2("specReader.schemaRequired"));
|
|
27843
28159
|
parseManagedConfig(raw);
|
|
27844
28160
|
const head = await readHead();
|
|
27845
28161
|
const stamp = await readStamp();
|
|
27846
|
-
const [current2, dirty, authors,
|
|
28162
|
+
const [current2, dirty, authors, people] = await settled([
|
|
27847
28163
|
cache.documents.list(),
|
|
27848
28164
|
head ? git(["status", "--porcelain=v1", "--", ".gitifact/spec", INSTRUCTIONS_ROOT]) : Promise.resolve(""),
|
|
27849
|
-
|
|
27850
|
-
|
|
27851
|
-
head ?
|
|
28165
|
+
// Who touched each folder and who committed at all, counted over the commit log in the cache.
|
|
28166
|
+
head ? cache.log.folders(head) : Promise.resolve(/* @__PURE__ */ new Map()),
|
|
28167
|
+
head ? cache.log.contributors(head) : Promise.resolve([])
|
|
27852
28168
|
]);
|
|
27853
|
-
const people = /* @__PURE__ */ new Map();
|
|
27854
|
-
const lines = everyone.trim().split("\n").filter(Boolean);
|
|
27855
|
-
for (const line2 of lines.slice(0, 1e4)) {
|
|
27856
|
-
const [name2, email3, latest] = line2.split("\0");
|
|
27857
|
-
if (!name2 || !email3 || !latest) throw new StoreError(t2("specReader.authorUnreadable"));
|
|
27858
|
-
tally(people, name2, email3, latest);
|
|
27859
|
-
}
|
|
27860
28169
|
const states = await documentStates({ head: async () => head, reader }, current2.documents);
|
|
27861
|
-
const
|
|
27862
|
-
const
|
|
27863
|
-
const entry2 = authors?.folders.get(folderOf3(feature2.path));
|
|
27864
|
-
return { ...feature2, contributors: entry2 ? [...entry2.people.values()].sort((a, b) => b.commits - a.commits) : [], updatedAt: entry2?.latest ?? null };
|
|
27865
|
-
});
|
|
27866
|
-
const instructions2 = await Promise.all(arranged.instructions.map(async (instruction2) => {
|
|
27867
|
-
const { files, limited } = instruction2.state === "deleted" ? { files: [], limited: false } : await readInstructionFiles(root, instruction2.path);
|
|
27868
|
-
return { ...instruction2, files, filesLimited: limited, updatedAt: authors?.instructions.get(instruction2.path.split("/").slice(0, 3).join("/")) ?? null };
|
|
27869
|
-
}));
|
|
27870
|
-
const agentsText = await readAgentsFile(root);
|
|
27871
|
-
const agents2 = agentsText === null ? null : { path: "AGENTS.md", body: agentsText, updatedAt: authors?.instructions.get("AGENTS.md") ?? null };
|
|
27872
|
-
const problems = [...current2.problems, ...arranged.orphans.map((feature2) => docProblem("FEATURE_INDEX_REQUIRED", `.gitifact/spec/${feature2}/index.md`, { feature: feature2 }))];
|
|
28170
|
+
const { features, instructions: instructions2, orphans } = checkoutDocuments(states, authors);
|
|
28171
|
+
const problems = [...current2.problems, ...orphans.map((feature2) => docProblem("FEATURE_INDEX_REQUIRED", `.gitifact/spec/${feature2}/index.md`, { feature: feature2 }))];
|
|
27873
28172
|
if (await readHead() !== head) throw new StoreError(t2("specReader.projectChanged"));
|
|
27874
|
-
|
|
27875
|
-
contract: "browser-specs",
|
|
27876
|
-
version: 7,
|
|
27877
|
-
sessionId,
|
|
28173
|
+
return {
|
|
27878
28174
|
head,
|
|
27879
|
-
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27880
28175
|
stamp,
|
|
28176
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27881
28177
|
working: head ? !!dirty.trim() : features.length > 0 || instructions2.length > 0,
|
|
27882
28178
|
features,
|
|
27883
28179
|
instructions: instructions2,
|
|
27884
|
-
agents: agents2,
|
|
27885
28180
|
problems,
|
|
27886
|
-
|
|
27887
|
-
|
|
27888
|
-
}
|
|
27889
|
-
return { checkout, head };
|
|
28181
|
+
people,
|
|
28182
|
+
folders: authors
|
|
28183
|
+
};
|
|
27890
28184
|
}
|
|
27891
|
-
|
|
28185
|
+
const base = () => {
|
|
27892
28186
|
const language = getLanguage();
|
|
27893
28187
|
let value = pending.get(language);
|
|
27894
28188
|
if (!value) {
|
|
@@ -27897,10 +28191,37 @@ function createCheckoutReader(root, sessionId, cache, inherited = process.env) {
|
|
|
27897
28191
|
}
|
|
27898
28192
|
return value;
|
|
27899
28193
|
};
|
|
28194
|
+
return {
|
|
28195
|
+
base,
|
|
28196
|
+
/** Every instruction by name with the other files of its folder, and AGENTS.md: few enough to read whole. */
|
|
28197
|
+
async instructions() {
|
|
28198
|
+
const checkout = await base();
|
|
28199
|
+
const instructions2 = await Promise.all([...checkout.instructions].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0).map(async (instruction2) => {
|
|
28200
|
+
const { files, limited } = instruction2.state === "deleted" ? { files: [], limited: false } : await readInstructionFiles(root, instruction2.path);
|
|
28201
|
+
return { ...instruction2, files, filesLimited: limited };
|
|
28202
|
+
}));
|
|
28203
|
+
const agentsText = await readAgentsFile(root);
|
|
28204
|
+
return { instructions: instructions2, agents: agentsText === null ? null : { path: "AGENTS.md", body: agentsText, updatedAt: checkout.folders.get("AGENTS.md")?.latest ?? null } };
|
|
28205
|
+
}
|
|
28206
|
+
};
|
|
28207
|
+
}
|
|
28208
|
+
|
|
28209
|
+
// src/queries/paging.ts
|
|
28210
|
+
var PAGE_SIZE = 20;
|
|
28211
|
+
function pageOf(rows, keyOf, request) {
|
|
28212
|
+
let start = 0;
|
|
28213
|
+
if (request.after !== void 0) {
|
|
28214
|
+
const at = rows.findIndex((row) => keyOf(row) === request.after);
|
|
28215
|
+
if (at < 0) return void 0;
|
|
28216
|
+
start = at + 1;
|
|
28217
|
+
}
|
|
28218
|
+
const size = request.all ? rows.length : request.limit ?? PAGE_SIZE;
|
|
28219
|
+
const shown = rows.slice(start, start + size);
|
|
28220
|
+
const more = start + shown.length < rows.length && shown.length > 0;
|
|
28221
|
+
return { rows: shown, total: rows.length, next: more ? keyOf(shown[shown.length - 1]) : null };
|
|
27900
28222
|
}
|
|
27901
28223
|
|
|
27902
28224
|
// src/server/commit/commit-files.ts
|
|
27903
|
-
var LIST_LIMIT = 500;
|
|
27904
28225
|
var TEXT_LIMIT = 512 * 1024;
|
|
27905
28226
|
var KEPT = 200;
|
|
27906
28227
|
var statusOf = (code) => code.startsWith("R") ? "renamed" : code === "A" ? "added" : code === "D" ? "deleted" : "modified";
|
|
@@ -27956,10 +28277,15 @@ function createCommitFiles(git) {
|
|
|
27956
28277
|
return git.run(["cat-file", "blob", `${rev}:${path3}`]);
|
|
27957
28278
|
}
|
|
27958
28279
|
return {
|
|
27959
|
-
/**
|
|
27960
|
-
|
|
28280
|
+
/**
|
|
28281
|
+
* A page of the source files after the file `after` (a path) and how many there are in all: undefined for a commit
|
|
28282
|
+
* the repository lacks, null for a cursor the commit does not have.
|
|
28283
|
+
*/
|
|
28284
|
+
async files(commit, request) {
|
|
27961
28285
|
const all = await list(commit);
|
|
27962
|
-
|
|
28286
|
+
if (!all) return void 0;
|
|
28287
|
+
const page = pageOf(all.files, (f) => f.path, request);
|
|
28288
|
+
return page ? { ...all, files: page.rows, next: page.next } : null;
|
|
27963
28289
|
},
|
|
27964
28290
|
/** One listed file on both sides; undefined when the commit did not change that path. */
|
|
27965
28291
|
async file(commit, path3) {
|
|
@@ -28041,31 +28367,200 @@ async function workingChange2(source2, id) {
|
|
|
28041
28367
|
};
|
|
28042
28368
|
}
|
|
28043
28369
|
|
|
28370
|
+
// src/queries/search.ts
|
|
28371
|
+
var SEARCH_GROUPS = ["feature", "requirement", "design", "instruction", "record", "commit"];
|
|
28372
|
+
var FIRST = 5;
|
|
28373
|
+
var RECENT = 6;
|
|
28374
|
+
var place2 = (path3) => path3.replace(/^\.gitifact\//, "");
|
|
28375
|
+
var folderOf5 = (path3) => path3.split("/").slice(0, 3).join("/");
|
|
28376
|
+
async function searchRecords(source2, request) {
|
|
28377
|
+
const head = request.head ?? await source2.head();
|
|
28378
|
+
const query = request.q.trim().toLowerCase();
|
|
28379
|
+
if (!query) return [await recent(source2, head)];
|
|
28380
|
+
const size = request.group ? request.limit ?? 20 : FIRST;
|
|
28381
|
+
const one = async (group) => {
|
|
28382
|
+
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);
|
|
28383
|
+
return found && { group, ...found };
|
|
28384
|
+
};
|
|
28385
|
+
const groups = await Promise.all((request.group ? [request.group] : SEARCH_GROUPS).map(one));
|
|
28386
|
+
if (groups.some((g) => !g)) return void 0;
|
|
28387
|
+
return groups.filter((g) => request.group || g.total > 0);
|
|
28388
|
+
}
|
|
28389
|
+
var empty = () => ({ total: 0, next: null, hits: [] });
|
|
28390
|
+
async function recent(source2, head) {
|
|
28391
|
+
const [{ documents }, folders] = await Promise.all([source2.cache.documents.list(), head ? source2.cache.log.folders(head) : /* @__PURE__ */ new Map()]);
|
|
28392
|
+
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 }) => ({
|
|
28393
|
+
id: doc.id,
|
|
28394
|
+
kind: doc.kind,
|
|
28395
|
+
title: doc.title,
|
|
28396
|
+
where: place2(doc.path),
|
|
28397
|
+
line: doc.description,
|
|
28398
|
+
...doc.kind === "feature" ? { featureId: doc.id } : {}
|
|
28399
|
+
}));
|
|
28400
|
+
return { group: "recent", total: hits.length, next: null, hits };
|
|
28401
|
+
}
|
|
28402
|
+
|
|
28403
|
+
// src/queries/checkout.ts
|
|
28404
|
+
var SHOWN_REQUIREMENTS = 12;
|
|
28405
|
+
function checkoutIndex(base) {
|
|
28406
|
+
return {
|
|
28407
|
+
features: base.features.map((f) => ({
|
|
28408
|
+
id: f.id,
|
|
28409
|
+
path: f.path,
|
|
28410
|
+
title: f.title,
|
|
28411
|
+
state: f.state,
|
|
28412
|
+
requirements: f.requirements.map((r) => ({ id: r.id, path: r.path, title: r.title, description: r.description })),
|
|
28413
|
+
designs: f.designs.map((d) => ({ id: d.id, path: d.path, title: d.title }))
|
|
28414
|
+
})),
|
|
28415
|
+
instructions: base.instructions.map((k) => ({ id: k.id, name: k.name, path: k.path, title: k.title })),
|
|
28416
|
+
people: base.people.map((p) => ({ name: p.name, email: p.email }))
|
|
28417
|
+
};
|
|
28418
|
+
}
|
|
28419
|
+
var acceptanceHeading = /^###\s+(?:수용 조건|acceptance criteria)\s*$/i;
|
|
28420
|
+
function acceptanceCount(body2) {
|
|
28421
|
+
const lines = body2.replace(/\r\n/g, "\n").split("\n");
|
|
28422
|
+
const start = lines.findIndex((line2) => acceptanceHeading.test(line2.trim()));
|
|
28423
|
+
if (start < 0) return null;
|
|
28424
|
+
let count2 = 0;
|
|
28425
|
+
for (const line2 of lines.slice(start + 1)) {
|
|
28426
|
+
if (/^#{1,6}\s/.test(line2)) break;
|
|
28427
|
+
if (/^\d+\.\s/.test(line2)) count2++;
|
|
28428
|
+
}
|
|
28429
|
+
return count2 || null;
|
|
28430
|
+
}
|
|
28431
|
+
function listFeatures(base, filter, request) {
|
|
28432
|
+
const query = filter.q?.trim().toLowerCase();
|
|
28433
|
+
const named = (f) => !query || (f.title + " " + f.id).toLowerCase().includes(query);
|
|
28434
|
+
const key = filter.sort ?? "updated";
|
|
28435
|
+
const opens = { title: "asc", requirements: "desc", updated: "desc" };
|
|
28436
|
+
const way = (filter.dir ?? opens[key]) === "asc" ? 1 : -1;
|
|
28437
|
+
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));
|
|
28438
|
+
const page = pageOf(groups, (g) => g.feature.id, request);
|
|
28439
|
+
if (!page) return void 0;
|
|
28440
|
+
const features = page.rows.map(({ feature: f, requirements }) => {
|
|
28441
|
+
const designed = new Set(f.designs.flatMap((d) => d.requirements));
|
|
28442
|
+
return {
|
|
28443
|
+
id: f.id,
|
|
28444
|
+
path: f.path,
|
|
28445
|
+
title: f.title,
|
|
28446
|
+
description: f.description,
|
|
28447
|
+
state: f.state,
|
|
28448
|
+
designs: f.designs.length,
|
|
28449
|
+
contributors: f.contributors,
|
|
28450
|
+
updatedAt: f.updatedAt,
|
|
28451
|
+
requirementCount: f.requirements.length,
|
|
28452
|
+
requirements: requirements.slice(0, SHOWN_REQUIREMENTS).map((r) => ({
|
|
28453
|
+
id: r.id,
|
|
28454
|
+
title: r.title,
|
|
28455
|
+
description: r.description,
|
|
28456
|
+
state: r.state,
|
|
28457
|
+
acceptance: acceptanceCount(r.body),
|
|
28458
|
+
designed: designed.has(r.id)
|
|
28459
|
+
})),
|
|
28460
|
+
hidden: Math.max(0, requirements.length - SHOWN_REQUIREMENTS)
|
|
28461
|
+
};
|
|
28462
|
+
});
|
|
28463
|
+
return {
|
|
28464
|
+
total: page.total,
|
|
28465
|
+
all: base.features.length,
|
|
28466
|
+
requirements: groups.reduce((sum, g) => sum + g.requirements.length, 0),
|
|
28467
|
+
mostRequirements: Math.max(0, ...base.features.map((f) => f.requirements.length)),
|
|
28468
|
+
next: page.next,
|
|
28469
|
+
features
|
|
28470
|
+
};
|
|
28471
|
+
}
|
|
28472
|
+
var featureOf = (base, id) => base.features.find((f) => f.id === id);
|
|
28473
|
+
function listContributors(base, q, request) {
|
|
28474
|
+
const query = q?.trim().toLowerCase();
|
|
28475
|
+
const people = base.people.filter((p) => !query || (p.name + " " + p.email).toLowerCase().includes(query));
|
|
28476
|
+
const page = pageOf(people, (p) => p.email, request);
|
|
28477
|
+
if (!page) return void 0;
|
|
28478
|
+
const touched = (email3) => base.features.filter((f) => f.contributors.some((c) => c.email === email3)).length;
|
|
28479
|
+
return { total: page.total, next: page.next, people: page.rows.map((p) => ({ ...p, features: touched(p.email) })) };
|
|
28480
|
+
}
|
|
28481
|
+
function contributorOf(base, email3) {
|
|
28482
|
+
const person = base.people.find((p) => p.email === email3);
|
|
28483
|
+
if (!person) return void 0;
|
|
28484
|
+
const features = base.features.flatMap((f) => {
|
|
28485
|
+
const share = f.contributors.find((c) => c.email === email3);
|
|
28486
|
+
return share ? [{ id: f.id, title: f.title, commits: share.commits, requirements: f.requirements.length }] : [];
|
|
28487
|
+
});
|
|
28488
|
+
return { person, features };
|
|
28489
|
+
}
|
|
28490
|
+
function topContributors(people) {
|
|
28491
|
+
const rest = people.slice(3);
|
|
28492
|
+
return { total: people.length, top: people.slice(0, 3), rest: { count: rest.length, commits: rest.reduce((sum, p) => sum + p.commits, 0) } };
|
|
28493
|
+
}
|
|
28494
|
+
|
|
28044
28495
|
// src/server/routes/record-routes.ts
|
|
28045
28496
|
var PAGE = 20;
|
|
28046
28497
|
function recordRoutes(root, sessionId, env) {
|
|
28047
28498
|
const git = storeReader(root);
|
|
28048
28499
|
const cache = openCache(root, { run: (args, input2) => git.run(args, input2), decode: git.decode, legacyBundles: (oids) => git.readBundles(oids) });
|
|
28049
|
-
const
|
|
28500
|
+
const checkout = createCheckoutReader(root, cache, env);
|
|
28050
28501
|
const readStamp = createStampReader(root, env);
|
|
28051
28502
|
const commitFiles = createCommitFiles(git);
|
|
28503
|
+
const source2 = { head: async () => (await git.baseline()).head, reader: git, cache };
|
|
28052
28504
|
const working2 = { head: async () => (await git.baseline()).head, cache, pendingRecords: () => readPendingRecords(root, (args) => git.run(args)) };
|
|
28053
28505
|
const unreadable2 = () => t2("server.specsUnreadable");
|
|
28054
|
-
const
|
|
28506
|
+
const cursorGone2 = () => new HttpError(404, "NOT_FOUND", t2("server.cursorNotFound"));
|
|
28055
28507
|
return [
|
|
28056
|
-
|
|
28508
|
+
// The checkout a part at a time, each shaped by the query layer (`queries/checkout.ts`): the frame every screen reads
|
|
28509
|
+
// first, the feature list and one feature, the instructions, and the contributors.
|
|
28510
|
+
route({ method: "GET", path: "/api/v1/checkout", session: true, unreadable: unreadable2, handle: async () => {
|
|
28511
|
+
const base = await checkout.base();
|
|
28512
|
+
return ok(browserCheckoutV1.parse({
|
|
28513
|
+
contract: "browser-checkout",
|
|
28514
|
+
version: 1,
|
|
28515
|
+
sessionId,
|
|
28516
|
+
head: base.head,
|
|
28517
|
+
observedAt: base.observedAt,
|
|
28518
|
+
stamp: base.stamp,
|
|
28519
|
+
working: base.working,
|
|
28520
|
+
problems: base.problems,
|
|
28521
|
+
index: checkoutIndex(base)
|
|
28522
|
+
}));
|
|
28523
|
+
} }),
|
|
28524
|
+
route({ method: "GET", path: "/api/v1/features", session: true, query: browserFeaturesQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28525
|
+
const page = listFeatures(await checkout.base(), query, { after: query.after, limit: query.limit ?? PAGE });
|
|
28526
|
+
if (!page) throw cursorGone2();
|
|
28527
|
+
return ok(browserFeaturesV1.parse({ contract: "browser-features", version: 1, sessionId, ...page }));
|
|
28528
|
+
} }),
|
|
28529
|
+
route({ method: "GET", path: "/api/v1/feature", session: true, query: browserFeatureQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28530
|
+
const feature2 = featureOf(await checkout.base(), query.id);
|
|
28531
|
+
if (!feature2) throw new HttpError(404, "NOT_FOUND", t2("server.featureNotFound"));
|
|
28532
|
+
return ok(browserFeatureV1.parse({ contract: "browser-feature", version: 1, sessionId, feature: feature2 }));
|
|
28533
|
+
} }),
|
|
28534
|
+
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() })) }),
|
|
28535
|
+
route({ method: "GET", path: "/api/v1/contributors", session: true, query: browserContributorsQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28536
|
+
const page = listContributors(await checkout.base(), query.q, { after: query.after, limit: query.limit ?? PAGE });
|
|
28537
|
+
if (!page) throw cursorGone2();
|
|
28538
|
+
return ok(browserContributorsV1.parse({ contract: "browser-contributors", version: 1, sessionId, ...page }));
|
|
28539
|
+
} }),
|
|
28540
|
+
route({ method: "GET", path: "/api/v1/contributor", session: true, query: browserContributorQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28541
|
+
const found = contributorOf(await checkout.base(), query.email);
|
|
28542
|
+
if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.contributorNotFound"));
|
|
28543
|
+
return ok(browserContributorV1.parse({ contract: "browser-contributor", version: 1, sessionId, ...found }));
|
|
28544
|
+
} }),
|
|
28057
28545
|
// Whether the screen is behind, asked when the reader comes back to the tab; cheap enough to ask every time.
|
|
28058
28546
|
route({ method: "GET", path: "/api/v1/stamp", session: true, unreadable: unreadable2, handle: async () => ok(browserStampV1.parse({ contract: "browser-stamp", version: 1, sessionId, stamp: await readStamp() })) }),
|
|
28059
28547
|
route({ method: "GET", path: "/api/v1/history", session: true, query: browserHistoryQueryV4, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28060
28548
|
const page = await cache.history.commits(query.head, { kind: query.kind, document: query.document, feature: query.feature, author: query.author, q: query.q }, query.after, query.limit ?? PAGE);
|
|
28061
|
-
if (!page) throw
|
|
28549
|
+
if (!page) throw cursorGone2();
|
|
28062
28550
|
return ok(browserHistoryV6.parse({ contract: "browser-history", version: 6, sessionId, head: query.head, ...page }));
|
|
28063
28551
|
} }),
|
|
28064
|
-
route({ method: "GET", path: "/api/v1/history/summary", session: true, query: browserHistorySummaryQueryV1, unreadable: unreadable2, handle: async ({ query }) => ok(
|
|
28552
|
+
route({ method: "GET", path: "/api/v1/history/summary", session: true, query: browserHistorySummaryQueryV1, unreadable: unreadable2, handle: async ({ query }) => ok(browserHistorySummaryV5.parse({
|
|
28553
|
+
contract: "browser-history-summary",
|
|
28554
|
+
version: 5,
|
|
28555
|
+
sessionId,
|
|
28556
|
+
head: query.head,
|
|
28557
|
+
...await cache.history.summary(query.head),
|
|
28558
|
+
people: topContributors(await cache.log.contributors(query.head))
|
|
28559
|
+
})) }),
|
|
28065
28560
|
// One commit as its page reads it: who made it and a page of the documents it changed, even when it changed none.
|
|
28066
|
-
route({ method: "GET", path: "/api/v1/commit", session: true, query:
|
|
28067
|
-
const page = await cache.history.commitChanges(query.commit, query.after, query.limit ?? PAGE);
|
|
28068
|
-
if (!page) throw
|
|
28561
|
+
route({ method: "GET", path: "/api/v1/commit", session: true, query: browserCommitQueryV3, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28562
|
+
const page = await cache.history.commitChanges(query.commit, query.after, query.limit ?? PAGE, query.record);
|
|
28563
|
+
if (!page) throw cursorGone2();
|
|
28069
28564
|
const first = page.events[0];
|
|
28070
28565
|
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");
|
|
28071
28566
|
if (!first && line2.length < 5) throw new HttpError(404, "NOT_FOUND", t2("server.commitNotFound"));
|
|
@@ -28093,9 +28588,9 @@ function recordRoutes(root, sessionId, env) {
|
|
|
28093
28588
|
} }),
|
|
28094
28589
|
// A record's page names the record; which commit added it is read from the history of the HEAD the reader is on.
|
|
28095
28590
|
route({ method: "GET", path: "/api/v1/record", session: true, query: browserRecordQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28096
|
-
const
|
|
28097
|
-
if (!
|
|
28098
|
-
return ok(
|
|
28591
|
+
const found = await cache.history.recordOf(query.head, query.id);
|
|
28592
|
+
if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.recordNotFound"));
|
|
28593
|
+
return ok(browserRecordV2.parse({ contract: "browser-record", version: 2, sessionId, head: query.head, id: query.id, ...found }));
|
|
28099
28594
|
} }),
|
|
28100
28595
|
// What is not committed yet, worked out again on every request: the uncommitted entry of the records list.
|
|
28101
28596
|
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) })) }),
|
|
@@ -28105,10 +28600,11 @@ function recordRoutes(root, sessionId, env) {
|
|
|
28105
28600
|
return ok(browserWorkingChangeV1.parse({ contract: "browser-working-change", version: 1, sessionId, ...change2 }));
|
|
28106
28601
|
} }),
|
|
28107
28602
|
// The source a commit changed beside its documents, read from Git when the commit or record page asks for it.
|
|
28108
|
-
route({ method: "GET", path: "/api/v1/commit/files", session: true, query:
|
|
28109
|
-
const found = await commitFiles.files(query.commit);
|
|
28110
|
-
if (
|
|
28111
|
-
|
|
28603
|
+
route({ method: "GET", path: "/api/v1/commit/files", session: true, query: browserCommitFilesQueryV2, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28604
|
+
const found = await commitFiles.files(query.commit, { after: query.after, limit: query.limit ?? PAGE });
|
|
28605
|
+
if (found === void 0) throw new HttpError(404, "NOT_FOUND", t2("server.commitNotFound"));
|
|
28606
|
+
if (found === null) throw cursorGone2();
|
|
28607
|
+
return ok(browserCommitFilesV2.parse({ contract: "browser-commit-files", version: 2, sessionId, commit: found.commit, total: found.total, next: found.next, files: found.files }));
|
|
28112
28608
|
} }),
|
|
28113
28609
|
route({ method: "GET", path: "/api/v1/commit/file", session: true, query: browserCommitFileQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28114
28610
|
const found = await commitFiles.file(query.commit, query.path);
|
|
@@ -28122,21 +28618,26 @@ function recordRoutes(root, sessionId, env) {
|
|
|
28122
28618
|
if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.instructionFileNotFound"));
|
|
28123
28619
|
return ok(browserInstructionFileV1.parse({ contract: "browser-instruction-file", version: 1, sessionId, id: query.id, path: query.path, ...found }));
|
|
28124
28620
|
} }),
|
|
28125
|
-
|
|
28621
|
+
// The search box's groups as the query layer works them out (`queries/search.ts`); the browser only draws them.
|
|
28622
|
+
route({ method: "GET", path: "/api/v1/search", session: true, query: browserSearchQueryV2, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28623
|
+
const groups = await searchRecords(source2, query);
|
|
28624
|
+
if (!groups) throw cursorGone2();
|
|
28625
|
+
return ok(browserSearchV3.parse({ contract: "browser-search", version: 3, sessionId, query: query.q, groups }));
|
|
28626
|
+
} })
|
|
28126
28627
|
];
|
|
28127
28628
|
}
|
|
28128
28629
|
|
|
28129
28630
|
// src/server/routes/asset-routes.ts
|
|
28130
|
-
import { lstat as
|
|
28131
|
-
import { join as
|
|
28631
|
+
import { lstat as lstat9, readFile as readFile7 } from "node:fs/promises";
|
|
28632
|
+
import { join as join10 } from "node:path";
|
|
28132
28633
|
function assetRoutes(root) {
|
|
28133
28634
|
return [
|
|
28134
28635
|
route({ method: "GET", path: "/api/v1/assets/*", session: false, handle: async ({ path: path3, response }) => {
|
|
28135
28636
|
const relative = path3.slice("/api/v1/assets/".length);
|
|
28136
28637
|
const assetPath = ASSETS_DIR + "/" + relative;
|
|
28137
28638
|
if (!isAssetPath(assetPath)) throw new HttpError(404, "NOT_FOUND", t2("server.assetNotFound"));
|
|
28138
|
-
const file2 =
|
|
28139
|
-
const stat3 = await
|
|
28639
|
+
const file2 = join10(root, ...assetPath.split("/"));
|
|
28640
|
+
const stat3 = await lstat9(file2).catch(() => void 0);
|
|
28140
28641
|
if (!stat3?.isFile() || stat3.isSymbolicLink() || stat3.size > 20 * 1024 * 1024) throw new HttpError(404, "NOT_FOUND", t2("server.assetNotFound"));
|
|
28141
28642
|
const bytes = await readFile7(file2);
|
|
28142
28643
|
const extension = assetExtension(relative);
|
|
@@ -28286,13 +28787,13 @@ async function runBrowser(options, version2) {
|
|
|
28286
28787
|
|
|
28287
28788
|
// src/commands/spec-init.ts
|
|
28288
28789
|
import { readdir as readdir5 } from "node:fs/promises";
|
|
28289
|
-
import { join as
|
|
28790
|
+
import { join as join14 } from "node:path";
|
|
28290
28791
|
|
|
28291
28792
|
// src/adapters/git/init-repository.ts
|
|
28292
28793
|
import { createHash as createHash3 } from "node:crypto";
|
|
28293
|
-
import { lstat as
|
|
28294
|
-
import { join as
|
|
28295
|
-
var markerPresent = (path3) =>
|
|
28794
|
+
import { lstat as lstat10, readFile as readFile9 } from "node:fs/promises";
|
|
28795
|
+
import { join as join11 } from "node:path";
|
|
28796
|
+
var markerPresent = (path3) => lstat10(path3).then(() => true, (error62) => {
|
|
28296
28797
|
if (error62.code === "ENOENT") return false;
|
|
28297
28798
|
throw error62;
|
|
28298
28799
|
});
|
|
@@ -28315,7 +28816,7 @@ function initRepository(cwd, inherited = process.env) {
|
|
|
28315
28816
|
const gitDir = locations.length === 2 ? locations[0] : decodeGitLine(await git(["rev-parse", "--absolute-git-dir"], root));
|
|
28316
28817
|
const indexPath = locations.length === 2 ? locations[1] : decodeGitLine(await git(["rev-parse", "--path-format=absolute", "--git-path", "index"], root));
|
|
28317
28818
|
for (const marker2 of ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"]) {
|
|
28318
|
-
if (await markerPresent(
|
|
28819
|
+
if (await markerPresent(join11(gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
|
|
28319
28820
|
}
|
|
28320
28821
|
if (state2.changes.some((change2) => change2.kind === "unmerged")) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
28321
28822
|
const index = await readFile9(indexPath).catch((error62) => {
|
|
@@ -28353,14 +28854,14 @@ function initRepository(cwd, inherited = process.env) {
|
|
|
28353
28854
|
|
|
28354
28855
|
// src/adapters/filesystem/document-file.ts
|
|
28355
28856
|
import { randomBytes } from "node:crypto";
|
|
28356
|
-
import { lstat as
|
|
28357
|
-
import { join as
|
|
28857
|
+
import { lstat as lstat11, mkdir as mkdir3, writeFile as writeFile2 } from "node:fs/promises";
|
|
28858
|
+
import { join as join12 } from "node:path";
|
|
28358
28859
|
var generateId = (prefix) => prefix + "-" + [...randomBytes(10)].map((n) => "abcdefghijklmnopqrstuvwxyz234567"[n & 31]).join("");
|
|
28359
28860
|
async function createDocumentFile(root, path3, text4) {
|
|
28360
28861
|
const parts = path3.split("/");
|
|
28361
28862
|
for (let i = 1; i < parts.length; i++) {
|
|
28362
|
-
const folder =
|
|
28363
|
-
const info5 = await
|
|
28863
|
+
const folder = join12(root, ...parts.slice(0, i));
|
|
28864
|
+
const info5 = await lstat11(folder).catch((error62) => {
|
|
28364
28865
|
if (error62.code === "ENOENT") return void 0;
|
|
28365
28866
|
throw error62;
|
|
28366
28867
|
});
|
|
@@ -28370,7 +28871,7 @@ async function createDocumentFile(root, path3, text4) {
|
|
|
28370
28871
|
}
|
|
28371
28872
|
if (!info5.isDirectory() || info5.isSymbolicLink()) throw Object.assign(new Error(folder), { code: "ENOTDIR" });
|
|
28372
28873
|
}
|
|
28373
|
-
await writeFile2(
|
|
28874
|
+
await writeFile2(join12(root, ...parts), text4, { flag: "wx" });
|
|
28374
28875
|
}
|
|
28375
28876
|
var LINE_ENDINGS_PATH = ".gitifact/.gitattributes";
|
|
28376
28877
|
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";
|
|
@@ -28386,14 +28887,14 @@ async function writeLineEndings(root) {
|
|
|
28386
28887
|
|
|
28387
28888
|
// src/adapters/filesystem/managed-file.ts
|
|
28388
28889
|
import { open as open4, readFile as readFile10, mkdir as mkdir4, realpath as realpath2, rename as rename2, link as link2, unlink as unlink2 } from "node:fs/promises";
|
|
28389
|
-
import { join as
|
|
28890
|
+
import { join as join13, dirname } from "node:path";
|
|
28390
28891
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
28391
28892
|
var DEFAULT_LIMIT = 1024 * 1024;
|
|
28392
28893
|
async function managedPath(root, path3, create = false) {
|
|
28393
28894
|
let full = root;
|
|
28394
28895
|
const parts = path3.split("/");
|
|
28395
28896
|
for (let i = 0; i < parts.length; i++) {
|
|
28396
|
-
full =
|
|
28897
|
+
full = join13(full, parts[i]);
|
|
28397
28898
|
let info5 = await fileInfo(full);
|
|
28398
28899
|
if (!info5 && create && i < parts.length - 1) {
|
|
28399
28900
|
await mkdir4(full).catch((error62) => {
|
|
@@ -28441,7 +28942,7 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
28441
28942
|
if (previous !== null) await unlink2(full);
|
|
28442
28943
|
return;
|
|
28443
28944
|
}
|
|
28444
|
-
const temp =
|
|
28945
|
+
const temp = join13(directory, ".gitifact-managed-" + randomUUID4() + ".tmp");
|
|
28445
28946
|
const handle = await open4(temp, "wx", 384);
|
|
28446
28947
|
const tempOwner = await handle.stat();
|
|
28447
28948
|
try {
|
|
@@ -28546,15 +29047,15 @@ function boilerplateFor(path3) {
|
|
|
28546
29047
|
return "# " + name2 + "\n\nProject-specific guidance for AI coding agents.\n";
|
|
28547
29048
|
}
|
|
28548
29049
|
var readBundledBlock = (lang) => readFile12(new URL("./i18n/" + lang + "/block.md", import.meta.url), "utf8");
|
|
28549
|
-
async function renderAgentBlock(
|
|
29050
|
+
async function renderAgentBlock(controls = {}, lang = getLanguage()) {
|
|
28550
29051
|
const body2 = await (controls.readBlock ?? readBundledBlock)(lang);
|
|
28551
|
-
|
|
28552
|
-
return AGENT_START + "\n" + filled + "\n" + AGENT_END;
|
|
29052
|
+
return AGENT_START + "\n" + body2.trimEnd().replace(/\{topics\}/g, guideTopics.join(", ")) + "\n" + AGENT_END;
|
|
28553
29053
|
}
|
|
28554
|
-
function
|
|
28555
|
-
const match = /^gitifact v
|
|
28556
|
-
if (!match) return
|
|
28557
|
-
|
|
29054
|
+
function legacyBlockLanguage(text4) {
|
|
29055
|
+
const match = /^gitifact v\S+(?: · ([a-z][a-z-]*))? · .*schemaVersion \d+$/m.exec(text4);
|
|
29056
|
+
if (!match) return void 0;
|
|
29057
|
+
const language = match[1] ?? "ko";
|
|
29058
|
+
return language === "ko" || language === "en" ? language : void 0;
|
|
28558
29059
|
}
|
|
28559
29060
|
function findBlock(text4) {
|
|
28560
29061
|
const start = text4.indexOf(AGENT_START);
|
|
@@ -28616,14 +29117,26 @@ function removeBlock(text4, boilerplate) {
|
|
|
28616
29117
|
}
|
|
28617
29118
|
|
|
28618
29119
|
// src/commands/agent-docs.ts
|
|
28619
|
-
var skippedAgentDocs = { mode: "skip", paths: [], writes: [], missing: [] };
|
|
29120
|
+
var skippedAgentDocs = (language = explicitLanguage() ?? getLanguage()) => ({ mode: "skip", paths: [], writes: [], missing: [], language });
|
|
29121
|
+
var blockOf = (text4) => {
|
|
29122
|
+
try {
|
|
29123
|
+
const found = findBlock(text4);
|
|
29124
|
+
return found ? text4.slice(found.start, found.end) : void 0;
|
|
29125
|
+
} catch {
|
|
29126
|
+
return void 0;
|
|
29127
|
+
}
|
|
29128
|
+
};
|
|
28620
29129
|
async function planAgentDocs(root, options) {
|
|
28621
|
-
if (options.skip) return skippedAgentDocs;
|
|
29130
|
+
if (options.skip) return skippedAgentDocs(explicitLanguage() ?? options.language ?? getLanguage());
|
|
28622
29131
|
const existing = /* @__PURE__ */ new Map();
|
|
28623
29132
|
for (const path3 of candidatePaths) {
|
|
28624
29133
|
const text4 = await managedRead(root, path3);
|
|
28625
29134
|
if (text4 !== null) existing.set(path3, text4);
|
|
28626
29135
|
}
|
|
29136
|
+
const language = explicitLanguage() ?? options.language ?? [...existing.values()].map((text4) => {
|
|
29137
|
+
const block2 = blockOf(text4);
|
|
29138
|
+
return block2 === void 0 ? void 0 : legacyBlockLanguage(block2);
|
|
29139
|
+
}).find(Boolean) ?? getLanguage();
|
|
28627
29140
|
const writes = [];
|
|
28628
29141
|
const paths2 = [];
|
|
28629
29142
|
if (options.remove) {
|
|
@@ -28639,19 +29152,14 @@ async function planAgentDocs(root, options) {
|
|
|
28639
29152
|
paths2.push(CLAUDE_WRAPPER_PATH);
|
|
28640
29153
|
writes.push({ path: CLAUDE_WRAPPER_PATH, previous: wrapper, next: null });
|
|
28641
29154
|
}
|
|
28642
|
-
return { mode: "remove", paths: paths2, writes, missing: [] };
|
|
29155
|
+
return { mode: "remove", paths: paths2, writes, missing: [], language };
|
|
28643
29156
|
}
|
|
28644
|
-
|
|
28645
|
-
const blockFor = async (
|
|
28646
|
-
const stored = previous ? parseAgentBlock(previous)?.language : void 0;
|
|
28647
|
-
const language = explicitLanguage() ?? (stored === "ko" || stored === "en" ? stored : getLanguage());
|
|
28648
|
-
if (!blocks.has(language)) blocks.set(language, await renderAgentBlock(options.version, options, language));
|
|
28649
|
-
return blocks.get(language);
|
|
28650
|
-
};
|
|
29157
|
+
let block;
|
|
29158
|
+
const blockFor = async () => block ??= await renderAgentBlock(options, language);
|
|
28651
29159
|
const { inject, create } = options.onlyExisting ? { inject: candidatePaths.filter((path3) => existing.has(path3) && findBlock(existing.get(path3)) && !isWrapperFile(existing.get(path3))), create: null } : resolveAgentPaths(options.agent, existing);
|
|
28652
29160
|
for (const path3 of inject) {
|
|
28653
29161
|
const previous = existing.get(path3);
|
|
28654
|
-
const next = injectBlock(previous, await blockFor(
|
|
29162
|
+
const next = injectBlock(previous, await blockFor(), boilerplateFor(path3));
|
|
28655
29163
|
paths2.push(path3);
|
|
28656
29164
|
if (next !== previous) writes.push({ path: path3, previous, next });
|
|
28657
29165
|
}
|
|
@@ -28671,7 +29179,7 @@ async function planAgentDocs(root, options) {
|
|
|
28671
29179
|
if (paths2.includes(path3) || !isWrapperFile(text4) || !findBlock(text4)) continue;
|
|
28672
29180
|
writes.push({ path: path3, previous: text4, next: removeBlock(text4, boilerplateFor(path3)) });
|
|
28673
29181
|
}
|
|
28674
|
-
return { mode: "install", paths: paths2, writes, missing: missing2 };
|
|
29182
|
+
return { mode: "install", paths: paths2, writes, missing: missing2, language };
|
|
28675
29183
|
}
|
|
28676
29184
|
async function applyAgentDocs(root, plan, recheck) {
|
|
28677
29185
|
for (const write of plan.writes) await managedWrite(root, write.path, write.previous, write.next, recheck);
|
|
@@ -28720,12 +29228,32 @@ async function resolveUpdate(current2, fetchLatest, signal, timeoutMs = updateCh
|
|
|
28720
29228
|
var npmGlobalInstall = (version2) => "npm install -g gitifact@" + version2;
|
|
28721
29229
|
var npxUpdate = (version2) => "npx --yes gitifact@" + version2 + " update";
|
|
28722
29230
|
|
|
29231
|
+
// src/commands/project-settings.ts
|
|
29232
|
+
function settleConfig(config2, version2, language) {
|
|
29233
|
+
const cli = !CLI_VERSION_PATTERN.test(version2) || config2.cli !== void 0 && !isNewerRelease(version2, config2.cli) ? config2.cli : version2;
|
|
29234
|
+
const next = { ...config2, ...cli === void 0 ? {} : { cli }, language };
|
|
29235
|
+
return { config: next, changed: next.cli !== config2.cli || next.language !== config2.language };
|
|
29236
|
+
}
|
|
29237
|
+
function projectSettings(text4) {
|
|
29238
|
+
if (text4 === void 0) return {};
|
|
29239
|
+
try {
|
|
29240
|
+
const value = JSON.parse(text4);
|
|
29241
|
+
return {
|
|
29242
|
+
...typeof value.cli === "string" && CLI_VERSION_PATTERN.test(value.cli) ? { cli: value.cli } : {},
|
|
29243
|
+
...value.language === "ko" || value.language === "en" ? { language: value.language } : {}
|
|
29244
|
+
};
|
|
29245
|
+
} catch {
|
|
29246
|
+
return {};
|
|
29247
|
+
}
|
|
29248
|
+
}
|
|
29249
|
+
|
|
28723
29250
|
// src/commands/spec-init.ts
|
|
28724
29251
|
async function initializeSpecProject(cwd, dryRun = false, env = process.env, beforePublish, agentDocs, update = disabledUpdate) {
|
|
28725
29252
|
const repo = initRepository(cwd, env);
|
|
28726
29253
|
const first = await repo.inspect();
|
|
28727
29254
|
const root = first.state.repository.rootPath;
|
|
28728
|
-
const
|
|
29255
|
+
const language = projectSettings(await readConfigFile(root).catch(() => void 0)).language;
|
|
29256
|
+
let docsPlan = agentDocs ? await planAgentDocs(root, { ...agentDocs, language }) : skippedAgentDocs(language);
|
|
28729
29257
|
const result = async (config3, outcome, lineEndings = false) => {
|
|
28730
29258
|
const checked = await update;
|
|
28731
29259
|
return projectInitV8.parse({
|
|
@@ -28746,16 +29274,25 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
28746
29274
|
const existing = async (text5) => {
|
|
28747
29275
|
const config3 = parseManagedConfig(text5);
|
|
28748
29276
|
await repo.validateBaseline(config3, root, first.state.head.commit, first.state.repository.objectFormat);
|
|
29277
|
+
let current2 = text5;
|
|
28749
29278
|
const unchanged = async () => {
|
|
28750
|
-
if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !==
|
|
29279
|
+
if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !== current2) throw new InitError("INPUT_CHANGED", t2("init.inputChanged"));
|
|
28751
29280
|
};
|
|
28752
29281
|
await unchanged();
|
|
29282
|
+
if (agentDocs && config3.cli && isNewerRelease(config3.cli, agentDocs.version) && docsPlan.mode === "install") docsPlan = skippedAgentDocs(docsPlan.language);
|
|
28753
29283
|
if (dryRun) return result(config3, "already-initialized");
|
|
29284
|
+
const settled2 = agentDocs ? settleConfig(config3, agentDocs.version, docsPlan.language) : { config: config3, changed: false };
|
|
29285
|
+
if (settled2.changed) {
|
|
29286
|
+
const next = formatManagedConfig(settled2.config, text5);
|
|
29287
|
+
await publishConfig(root, next, unchanged, void 0, true);
|
|
29288
|
+
current2 = next;
|
|
29289
|
+
}
|
|
28754
29290
|
await applyAgentDocs(root, docsPlan, unchanged);
|
|
28755
|
-
return result(
|
|
29291
|
+
return result(settled2.config, "already-initialized", await writeLineEndings(root));
|
|
28756
29292
|
};
|
|
28757
|
-
const
|
|
28758
|
-
const
|
|
29293
|
+
const base = { schemaVersion: SCHEMA_VERSION, baseline: first.state.head.commit ? { kind: "commit", objectFormat: first.state.repository.objectFormat, commit: first.state.head.commit } : { kind: "empty" } };
|
|
29294
|
+
const config2 = agentDocs ? settleConfig(base, agentDocs.version, docsPlan.language).config : base;
|
|
29295
|
+
const text4 = formatManagedConfig(config2);
|
|
28759
29296
|
const finish = async (outcome) => {
|
|
28760
29297
|
const published = async () => {
|
|
28761
29298
|
if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !== text4) throw new InitError("INPUT_CHANGED_AFTER_WRITE", t2("init.changedAfterWrite"));
|
|
@@ -28768,7 +29305,7 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
28768
29305
|
if (old !== void 0) return existing(old);
|
|
28769
29306
|
if (first.trackedConfig) throw new InitError("CONFIG_DELETED", t2("init.configDeleted"));
|
|
28770
29307
|
const checkRecords = async () => {
|
|
28771
|
-
const entries = await readdir5(
|
|
29308
|
+
const entries = await readdir5(join14(root, ".gitifact")).catch((e) => {
|
|
28772
29309
|
if (e.code === "ENOENT") return [];
|
|
28773
29310
|
throw e;
|
|
28774
29311
|
});
|
|
@@ -28805,6 +29342,53 @@ var fetchLatestVersion = async (signal) => {
|
|
|
28805
29342
|
return latest;
|
|
28806
29343
|
};
|
|
28807
29344
|
|
|
29345
|
+
// src/adapters/filesystem/user-cache.ts
|
|
29346
|
+
import { mkdir as mkdir5, readFile as readFile13, rename as rename3, unlink as unlink3, writeFile as writeFile3 } from "node:fs/promises";
|
|
29347
|
+
import { homedir } from "node:os";
|
|
29348
|
+
import { join as join15 } from "node:path";
|
|
29349
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
29350
|
+
function userCacheDirectory(env = process.env, platform = process.platform, home = homedir()) {
|
|
29351
|
+
if (env.GITIFACT_CACHE_DIR) return env.GITIFACT_CACHE_DIR;
|
|
29352
|
+
if (platform === "win32") return join15(env.LOCALAPPDATA || join15(home, "AppData", "Local"), "gitifact");
|
|
29353
|
+
if (platform === "darwin") return join15(home, "Library", "Caches", "gitifact");
|
|
29354
|
+
return join15(env.XDG_CACHE_HOME || join15(home, ".cache"), "gitifact");
|
|
29355
|
+
}
|
|
29356
|
+
var cacheFile = (env) => join15(userCacheDirectory(env), "update.json");
|
|
29357
|
+
var isTime = (value) => typeof value === "string" && !Number.isNaN(Date.parse(value));
|
|
29358
|
+
async function readUpdateCache(env = process.env) {
|
|
29359
|
+
let value;
|
|
29360
|
+
try {
|
|
29361
|
+
value = JSON.parse(await readFile13(cacheFile(env), "utf8"));
|
|
29362
|
+
} catch {
|
|
29363
|
+
return {};
|
|
29364
|
+
}
|
|
29365
|
+
if (typeof value !== "object" || value === null) return {};
|
|
29366
|
+
const later = value.later;
|
|
29367
|
+
return {
|
|
29368
|
+
...isTime(value.checkedAt) ? { checkedAt: value.checkedAt } : {},
|
|
29369
|
+
...typeof value.latest === "string" ? { latest: value.latest } : {},
|
|
29370
|
+
...isTime(value.checkingSince) ? { checkingSince: value.checkingSince } : {},
|
|
29371
|
+
...later && typeof later.version === "string" && isTime(later.until) ? { later: { version: later.version, until: later.until } } : {}
|
|
29372
|
+
};
|
|
29373
|
+
}
|
|
29374
|
+
async function changeUpdateCache(env, change2) {
|
|
29375
|
+
const directory = userCacheDirectory(env);
|
|
29376
|
+
const temporary = join15(directory, "update-" + randomUUID5() + ".tmp");
|
|
29377
|
+
try {
|
|
29378
|
+
const next = change2(await readUpdateCache(env));
|
|
29379
|
+
await mkdir5(directory, { recursive: true });
|
|
29380
|
+
await writeFile3(temporary, JSON.stringify(next) + "\n", "utf8");
|
|
29381
|
+
await rename3(temporary, cacheFile(env));
|
|
29382
|
+
} catch {
|
|
29383
|
+
await unlink3(temporary).catch(() => {
|
|
29384
|
+
});
|
|
29385
|
+
}
|
|
29386
|
+
}
|
|
29387
|
+
async function recordUpdateCheck(env, update, now = Date.now()) {
|
|
29388
|
+
if (update.status === "disabled" || update.status === "checking") return;
|
|
29389
|
+
await changeUpdateCache(env, ({ checkingSince: _done, ...cache }) => ({ ...cache, checkedAt: new Date(now).toISOString(), ...update.latestVersion ? { latest: update.latestVersion } : {} }));
|
|
29390
|
+
}
|
|
29391
|
+
|
|
28808
29392
|
// src/commands/init.ts
|
|
28809
29393
|
async function runInit(options, version2) {
|
|
28810
29394
|
const cancel = new AbortController();
|
|
@@ -28819,9 +29403,10 @@ async function runInit(options, version2) {
|
|
|
28819
29403
|
update
|
|
28820
29404
|
);
|
|
28821
29405
|
if (!dto.ok) throw new Error("unreachable");
|
|
29406
|
+
await recordUpdateCheck(process.env, dto.update);
|
|
28822
29407
|
const docs = dto.agentDocs.mode === "skip" ? t2("init.text.skipped") : dto.agentDocs.paths.join(", ") + (dto.agentDocs.mode === "remove" ? " (" + t2("init.text.blockRemoved") + ")" : dto.outcome === "planned" ? " (" + t2("init.text.blockPlanned") + ")" : " (" + t2("init.text.blockUpdated") + ")");
|
|
28823
29408
|
const lineEndings = dto.lineEndings.created ? t2("init.text.lineEndings", { path: dto.lineEndings.path }) + "\n" : "";
|
|
28824
|
-
const newer = dto.install ? t2("init.text.updateAvailable", { version: dto.update.latestVersion, command: dto.install.
|
|
29409
|
+
const newer = dto.install ? t2("init.text.updateAvailable", { version: dto.update.latestVersion, command: dto.install.npmGlobal }) + "\n" : "";
|
|
28825
29410
|
process.stdout.write(options.format === "text" ? `${dto.outcome}: ${dto.rootPath}/.gitifact/config.json
|
|
28826
29411
|
${t2("init.text.storage")}: schemaVersion ${dto.schemaVersion}
|
|
28827
29412
|
${t2("init.text.agentDocs")}: ${docs}
|
|
@@ -28836,17 +29421,17 @@ ${lineEndings}${newer}` : JSON.stringify(dto) + "\n");
|
|
|
28836
29421
|
}
|
|
28837
29422
|
|
|
28838
29423
|
// src/commands/documents.ts
|
|
28839
|
-
import { readFile as
|
|
28840
|
-
import { join as
|
|
29424
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
29425
|
+
import { join as join17 } from "node:path";
|
|
28841
29426
|
|
|
28842
29427
|
// src/commands/project.ts
|
|
28843
|
-
import { join as
|
|
29428
|
+
import { join as join16 } from "node:path";
|
|
28844
29429
|
var operations = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"];
|
|
28845
29430
|
async function openProject(cwd, options = {}) {
|
|
28846
29431
|
const reader = storeReader(cwd);
|
|
28847
29432
|
const location = await reader.location();
|
|
28848
29433
|
if (options.writing) {
|
|
28849
|
-
for (const marker2 of operations) if (await fileInfo(
|
|
29434
|
+
for (const marker2 of operations) if (await fileInfo(join16(location.gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
|
|
28850
29435
|
if (await reader.hasUnmerged()) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
28851
29436
|
}
|
|
28852
29437
|
const raw = await readConfigFile(location.root);
|
|
@@ -28877,6 +29462,125 @@ function documentsOf(files) {
|
|
|
28877
29462
|
return docs;
|
|
28878
29463
|
}
|
|
28879
29464
|
|
|
29465
|
+
// src/queries/filters.ts
|
|
29466
|
+
function byAuthor(author) {
|
|
29467
|
+
if (author === void 0) return () => true;
|
|
29468
|
+
const wanted = author.trim().toLowerCase();
|
|
29469
|
+
return (change2) => change2.author.toLowerCase() === wanted || change2.email.toLowerCase() === wanted;
|
|
29470
|
+
}
|
|
29471
|
+
async function since(project, value, head) {
|
|
29472
|
+
if (value === void 0) return () => true;
|
|
29473
|
+
if (/^\d{4}-\d\d-\d\d/.test(value)) {
|
|
29474
|
+
const from = Date.parse(value);
|
|
29475
|
+
if (!Number.isFinite(from)) throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29476
|
+
return (change2) => Date.parse(change2.date) >= from;
|
|
29477
|
+
}
|
|
29478
|
+
let commit;
|
|
29479
|
+
try {
|
|
29480
|
+
commit = await project.reader.resolve(value);
|
|
29481
|
+
} catch {
|
|
29482
|
+
throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29483
|
+
}
|
|
29484
|
+
if (!head) return () => false;
|
|
29485
|
+
const after = new Set(project.reader.decode(await project.reader.run(["rev-list", `${commit}..${head}`, "--"])).split("\n").filter(Boolean));
|
|
29486
|
+
return (change2) => after.has(change2.commit);
|
|
29487
|
+
}
|
|
29488
|
+
async function sinceRange(project, value, head) {
|
|
29489
|
+
if (value === void 0) return {};
|
|
29490
|
+
if (/^\d{4}-\d\d-\d\d/.test(value)) {
|
|
29491
|
+
const from = Date.parse(value);
|
|
29492
|
+
if (!Number.isFinite(from)) throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29493
|
+
return { from };
|
|
29494
|
+
}
|
|
29495
|
+
let commit;
|
|
29496
|
+
try {
|
|
29497
|
+
commit = await project.reader.resolve(value);
|
|
29498
|
+
} catch {
|
|
29499
|
+
throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29500
|
+
}
|
|
29501
|
+
if (!head) return { commits: [] };
|
|
29502
|
+
return { commits: project.reader.decode(await project.reader.run(["rev-list", `${commit}..${head}`, "--"])).split("\n").filter(Boolean) };
|
|
29503
|
+
}
|
|
29504
|
+
var contains = (query) => {
|
|
29505
|
+
const wanted = query?.trim().toLowerCase();
|
|
29506
|
+
return (...texts) => !wanted || texts.some((value) => value?.toLowerCase().includes(wanted));
|
|
29507
|
+
};
|
|
29508
|
+
|
|
29509
|
+
// src/queries/specs.ts
|
|
29510
|
+
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);
|
|
29511
|
+
async function listSpecs(source2, filter, request) {
|
|
29512
|
+
const listed3 = await source2.cache.documents.list();
|
|
29513
|
+
const problems = listed3.problems;
|
|
29514
|
+
const states = await documentStates(source2, listed3.documents);
|
|
29515
|
+
const documents = states.documents;
|
|
29516
|
+
const byId = new Map(documents.map((d) => [d.id, d]));
|
|
29517
|
+
const arranged = arrangeDocuments(documents);
|
|
29518
|
+
let features = arranged.features;
|
|
29519
|
+
if (filter.feature !== void 0) {
|
|
29520
|
+
features = features.filter((f) => f.index.feature === filter.feature);
|
|
29521
|
+
if (!features.length) throw new CommandError("UNKNOWN_FEATURE", t2("docs.unknownFeature", { feature: filter.feature }));
|
|
29522
|
+
}
|
|
29523
|
+
const titled = (id) => ({ id, title: byId.get(id)?.title ?? null });
|
|
29524
|
+
const designsOf = /* @__PURE__ */ new Map();
|
|
29525
|
+
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)]);
|
|
29526
|
+
const historyNeeded = filter.sort === "updated" || filter.changedSince !== void 0 || filter.author !== void 0 || !!filter.withUpdated;
|
|
29527
|
+
const latest = /* @__PURE__ */ new Map();
|
|
29528
|
+
const newest = /* @__PURE__ */ new Map();
|
|
29529
|
+
let touched;
|
|
29530
|
+
if (historyNeeded) {
|
|
29531
|
+
const head = await source2.head();
|
|
29532
|
+
const changes2 = head ? await source2.cache.history.changesOf(head) : [];
|
|
29533
|
+
changes2.forEach((c, rank) => {
|
|
29534
|
+
if (!latest.has(c.id)) {
|
|
29535
|
+
latest.set(c.id, { commit: c.commit, date: c.date, author: c.author, email: c.email });
|
|
29536
|
+
newest.set(c.id, rank);
|
|
29537
|
+
}
|
|
29538
|
+
});
|
|
29539
|
+
if (filter.changedSince !== void 0 || filter.author !== void 0) {
|
|
29540
|
+
const after = await since(source2, filter.changedSince, head);
|
|
29541
|
+
const by = byAuthor(filter.author);
|
|
29542
|
+
touched = new Set(changes2.filter((c) => after(c) && by(c)).map((c) => c.id));
|
|
29543
|
+
}
|
|
29544
|
+
}
|
|
29545
|
+
const matched = filter.q !== void 0 ? await source2.cache.documents.matching(filter.q) : void 0;
|
|
29546
|
+
const rows = features.flatMap((f) => [f.index, ...f.requirements, ...f.designs].filter(
|
|
29547
|
+
(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))
|
|
29548
|
+
).map((doc) => ({
|
|
29549
|
+
id: doc.id,
|
|
29550
|
+
kind: doc.kind,
|
|
29551
|
+
feature: doc.feature,
|
|
29552
|
+
path: doc.path,
|
|
29553
|
+
title: doc.title,
|
|
29554
|
+
description: doc.description,
|
|
29555
|
+
...doc.kind === "requirement" || doc.kind === "design" ? { order: doc.order } : {},
|
|
29556
|
+
...doc.draft ? { draft: true } : {},
|
|
29557
|
+
state: states.state(doc.id),
|
|
29558
|
+
...states.previousPath(doc.id) ? { previousPath: states.previousPath(doc.id) } : {},
|
|
29559
|
+
...doc.kind === "design" ? { requirements: doc.requirements.map(titled), sources: titledSources(doc, byId) } : {},
|
|
29560
|
+
...doc.kind === "requirement" ? { designs: designsOf.get(doc.id) ?? [] } : {},
|
|
29561
|
+
...historyNeeded ? { updated: latest.get(doc.id) ?? null } : {},
|
|
29562
|
+
...matched ? { line: matched.get(doc.id) } : {}
|
|
29563
|
+
})));
|
|
29564
|
+
if (filter.sort === "title") rows.sort((a, b) => a.title.localeCompare(b.title));
|
|
29565
|
+
if (filter.sort === "updated") rows.sort((a, b) => (newest.get(a.id) ?? Infinity) - (newest.get(b.id) ?? Infinity));
|
|
29566
|
+
const grouped = filter.sort === "order";
|
|
29567
|
+
const groups = features.map((f) => ({ id: f.index.id, rows: rows.filter((r) => r.feature === f.index.feature) })).filter((g) => g.rows.length);
|
|
29568
|
+
const page = grouped ? pageOf(groups, (g) => g.id, request) : pageOf(rows, (r) => r.id, request);
|
|
29569
|
+
if (!page) return void 0;
|
|
29570
|
+
const shown = grouped ? page.rows.flatMap((g) => g.rows) : page.rows;
|
|
29571
|
+
return {
|
|
29572
|
+
rows: shown,
|
|
29573
|
+
features,
|
|
29574
|
+
byId,
|
|
29575
|
+
states,
|
|
29576
|
+
grouped,
|
|
29577
|
+
problems,
|
|
29578
|
+
page: { rows: page.rows, total: page.total, next: page.next },
|
|
29579
|
+
unit: grouped ? "feature" : "document",
|
|
29580
|
+
orphans: filter.feature === void 0 ? arranged.orphans : []
|
|
29581
|
+
};
|
|
29582
|
+
}
|
|
29583
|
+
|
|
28880
29584
|
// src/commands/documents.ts
|
|
28881
29585
|
var draftMark = (doc) => doc.draft ? " (" + t2("docs.draft") + ")" : "";
|
|
28882
29586
|
var stateLabel = {
|
|
@@ -28887,12 +29591,11 @@ var stateLabel = {
|
|
|
28887
29591
|
var stateMark = (state2) => state2 === "committed" ? "" : " (" + stateLabel[state2]() + ")";
|
|
28888
29592
|
var line = (doc) => `${doc.id} ${doc.title}${draftMark(doc)} \u2014 ${doc.description}`;
|
|
28889
29593
|
var fileLine = (f) => f.title ? `${f.path} ${f.title} \u2014 ${f.description}` : f.path;
|
|
28890
|
-
var titledSources = (d, byId) => d.sources.map((s) => "id" in s ? { id: s.id, title: byId.get(s.id)?.title ?? null, ...s.note ? { note: s.note } : {} } : s);
|
|
28891
29594
|
async function working(project) {
|
|
28892
29595
|
const { documents } = await project.cache.documents.list();
|
|
28893
29596
|
return {
|
|
28894
29597
|
byId: new Map(documents.map((d) => [d.id, d])),
|
|
28895
|
-
read: (path3) =>
|
|
29598
|
+
read: (path3) => readFile14(join17(project.root, ...path3.split("/")), "utf8"),
|
|
28896
29599
|
referencing: async (id) => [...new Set((await project.cache.documents.referencing(id)).map((r) => r.from))]
|
|
28897
29600
|
};
|
|
28898
29601
|
}
|
|
@@ -29001,48 +29704,6 @@ function checkFields(fields2, columns3) {
|
|
|
29001
29704
|
if (unknown2.length || !fields2.length) throw new CommandError("INVALID_VALUE", t2("list.unknownFields", { fields: unknown2.join(", "), columns: columns3.join(", ") }));
|
|
29002
29705
|
return [...new Set(fields2)];
|
|
29003
29706
|
}
|
|
29004
|
-
function byAuthor(author) {
|
|
29005
|
-
if (author === void 0) return () => true;
|
|
29006
|
-
const wanted = author.trim().toLowerCase();
|
|
29007
|
-
return (change2) => change2.author.toLowerCase() === wanted || change2.email.toLowerCase() === wanted;
|
|
29008
|
-
}
|
|
29009
|
-
async function since(project, value, head) {
|
|
29010
|
-
if (value === void 0) return () => true;
|
|
29011
|
-
if (/^\d{4}-\d\d-\d\d/.test(value)) {
|
|
29012
|
-
const from = Date.parse(value);
|
|
29013
|
-
if (!Number.isFinite(from)) throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29014
|
-
return (change2) => Date.parse(change2.date) >= from;
|
|
29015
|
-
}
|
|
29016
|
-
let commit;
|
|
29017
|
-
try {
|
|
29018
|
-
commit = await project.reader.resolve(value);
|
|
29019
|
-
} catch {
|
|
29020
|
-
throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29021
|
-
}
|
|
29022
|
-
if (!head) return () => false;
|
|
29023
|
-
const after = new Set(project.reader.decode(await project.reader.run(["rev-list", `${commit}..${head}`, "--"])).split("\n").filter(Boolean));
|
|
29024
|
-
return (change2) => after.has(change2.commit);
|
|
29025
|
-
}
|
|
29026
|
-
async function sinceRange(project, value, head) {
|
|
29027
|
-
if (value === void 0) return {};
|
|
29028
|
-
if (/^\d{4}-\d\d-\d\d/.test(value)) {
|
|
29029
|
-
const from = Date.parse(value);
|
|
29030
|
-
if (!Number.isFinite(from)) throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29031
|
-
return { from };
|
|
29032
|
-
}
|
|
29033
|
-
let commit;
|
|
29034
|
-
try {
|
|
29035
|
-
commit = await project.reader.resolve(value);
|
|
29036
|
-
} catch {
|
|
29037
|
-
throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29038
|
-
}
|
|
29039
|
-
if (!head) return { commits: [] };
|
|
29040
|
-
return { commits: project.reader.decode(await project.reader.run(["rev-list", `${commit}..${head}`, "--"])).split("\n").filter(Boolean) };
|
|
29041
|
-
}
|
|
29042
|
-
var contains = (query) => {
|
|
29043
|
-
const wanted = query?.trim().toLowerCase();
|
|
29044
|
-
return (...texts) => !wanted || texts.some((value) => value?.toLowerCase().includes(wanted));
|
|
29045
|
-
};
|
|
29046
29707
|
function cell(value) {
|
|
29047
29708
|
if (value === void 0 || value === null || value === false) return "";
|
|
29048
29709
|
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(",");
|
|
@@ -29061,18 +29722,14 @@ function selected(rows, fields2) {
|
|
|
29061
29722
|
text: rows.length ? text2(rows.map((row) => fields2.map((f) => cell(row[f])).join(" "))) : ""
|
|
29062
29723
|
};
|
|
29063
29724
|
}
|
|
29064
|
-
var PAGE_SIZE = 20;
|
|
29065
29725
|
function paginate(rows, keyOf, options) {
|
|
29066
|
-
|
|
29067
|
-
|
|
29068
|
-
|
|
29069
|
-
|
|
29070
|
-
|
|
29071
|
-
|
|
29072
|
-
|
|
29073
|
-
const shown = rows.slice(start, start + size);
|
|
29074
|
-
const more = start + shown.length < rows.length && shown.length > 0;
|
|
29075
|
-
return { rows: shown, total: rows.length, next: more ? keyOf(shown[shown.length - 1]) : null };
|
|
29726
|
+
return pageOr(pageOf(rows, keyOf, options), options);
|
|
29727
|
+
}
|
|
29728
|
+
function pageOr(page, options) {
|
|
29729
|
+
return page ?? cursorGone(options);
|
|
29730
|
+
}
|
|
29731
|
+
function cursorGone(options) {
|
|
29732
|
+
throw new CommandError("INVALID_VALUE", t2("list.after", { cursor: options.after ?? "" }));
|
|
29076
29733
|
}
|
|
29077
29734
|
var pageLine = (page, before = 0, unit = t2("list.items")) => page.next === null ? [] : [t2("list.more", { shown: before + page.rows.length, total: page.total, unit, cursor: page.next })];
|
|
29078
29735
|
function aside(format2, lines) {
|
|
@@ -29088,67 +29745,10 @@ var kindLabel = { feature: () => t2("docs.feature"), requirement: () => t2("docs
|
|
|
29088
29745
|
var runSpecsList = (options) => runCommand("specs", options.format, async () => {
|
|
29089
29746
|
const fields2 = checkFields(options.fields, columns);
|
|
29090
29747
|
const project = await openProject(process.cwd());
|
|
29091
|
-
const listed3 = await project
|
|
29092
|
-
const problems = listed3
|
|
29093
|
-
const states = await documentStates(project, listed3.documents);
|
|
29094
|
-
const documents = states.documents;
|
|
29095
|
-
const byId = new Map(documents.map((d) => [d.id, d]));
|
|
29096
|
-
const arranged = arrangeDocuments(documents);
|
|
29097
|
-
let features = arranged.features;
|
|
29098
|
-
if (options.feature !== void 0) {
|
|
29099
|
-
features = features.filter((f) => f.index.feature === options.feature);
|
|
29100
|
-
if (!features.length) throw new CommandError("UNKNOWN_FEATURE", t2("docs.unknownFeature", { feature: options.feature }));
|
|
29101
|
-
}
|
|
29102
|
-
const titled = (id) => ({ id, title: byId.get(id)?.title ?? null });
|
|
29103
|
-
const designsOf = /* @__PURE__ */ new Map();
|
|
29104
|
-
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)]);
|
|
29105
|
-
const historyNeeded = options.sort === "updated" || options.changedSince !== void 0 || options.author !== void 0 || !!fields2?.includes("updated");
|
|
29106
|
-
const latest = /* @__PURE__ */ new Map();
|
|
29107
|
-
const newest = /* @__PURE__ */ new Map();
|
|
29108
|
-
let touched;
|
|
29109
|
-
if (historyNeeded) {
|
|
29110
|
-
const head = await project.head();
|
|
29111
|
-
const changes2 = head ? await project.cache.history.changesOf(head) : [];
|
|
29112
|
-
changes2.forEach((c, rank) => {
|
|
29113
|
-
if (!latest.has(c.id)) {
|
|
29114
|
-
latest.set(c.id, { commit: c.commit, date: c.date, author: c.author, email: c.email });
|
|
29115
|
-
newest.set(c.id, rank);
|
|
29116
|
-
}
|
|
29117
|
-
});
|
|
29118
|
-
if (options.changedSince !== void 0 || options.author !== void 0) {
|
|
29119
|
-
const after = await since(project, options.changedSince, head);
|
|
29120
|
-
const by = byAuthor(options.author);
|
|
29121
|
-
touched = new Set(changes2.filter((c) => after(c) && by(c)).map((c) => c.id));
|
|
29122
|
-
}
|
|
29123
|
-
}
|
|
29124
|
-
const matched = options.q !== void 0 ? await project.cache.documents.matching(options.q) : void 0;
|
|
29125
|
-
const rows = features.flatMap((f) => [f.index, ...f.requirements, ...f.designs].filter(
|
|
29126
|
-
(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))
|
|
29127
|
-
).map((doc) => ({
|
|
29128
|
-
id: doc.id,
|
|
29129
|
-
kind: doc.kind,
|
|
29130
|
-
feature: doc.feature,
|
|
29131
|
-
path: doc.path,
|
|
29132
|
-
title: doc.title,
|
|
29133
|
-
description: doc.description,
|
|
29134
|
-
...doc.kind === "requirement" || doc.kind === "design" ? { order: doc.order } : {},
|
|
29135
|
-
...doc.draft ? { draft: true } : {},
|
|
29136
|
-
state: states.state(doc.id),
|
|
29137
|
-
...states.previousPath(doc.id) ? { previousPath: states.previousPath(doc.id) } : {},
|
|
29138
|
-
...doc.kind === "design" ? { requirements: doc.requirements.map(titled), sources: titledSources(doc, byId) } : {},
|
|
29139
|
-
...doc.kind === "requirement" ? { designs: designsOf.get(doc.id) ?? [] } : {},
|
|
29140
|
-
...historyNeeded ? { updated: latest.get(doc.id) ?? null } : {},
|
|
29141
|
-
...matched ? { line: matched.get(doc.id) } : {}
|
|
29142
|
-
})));
|
|
29143
|
-
if (options.sort === "title") rows.sort((a, b) => a.title.localeCompare(b.title));
|
|
29144
|
-
if (options.sort === "updated") rows.sort((a, b) => (newest.get(a.id) ?? Infinity) - (newest.get(b.id) ?? Infinity));
|
|
29145
|
-
const grouped = options.sort === "order";
|
|
29146
|
-
const groups = features.map((f) => ({ id: f.index.id, rows: rows.filter((r) => r.feature === f.index.feature) })).filter((g) => g.rows.length);
|
|
29147
|
-
const page = grouped ? paginate(groups, (g) => g.id, options) : paginate(rows, (r) => r.id, options);
|
|
29148
|
-
const shown = grouped ? page.rows.flatMap((g) => g.rows) : page.rows;
|
|
29748
|
+
const listed3 = await listSpecs(project, { ...options, withUpdated: !!fields2?.includes("updated") }, options) ?? cursorGone(options);
|
|
29749
|
+
const { rows: shown, features, byId, states, grouped, problems, orphans, page } = listed3;
|
|
29149
29750
|
const more = pageLine(page, 0, grouped ? t2("list.features") : t2("list.items"));
|
|
29150
|
-
const paging = { total: page.total, next: page.next, unit:
|
|
29151
|
-
const orphans = options.feature === void 0 ? arranged.orphans : [];
|
|
29751
|
+
const paging = { total: page.total, next: page.next, unit: listed3.unit };
|
|
29152
29752
|
if (fields2) {
|
|
29153
29753
|
const picked = selected(shown, fields2);
|
|
29154
29754
|
return { json: { documents: picked.json, orphans, problems, page: paging }, text: picked.text + aside(options.format, more) };
|
|
@@ -29192,27 +29792,23 @@ var runSpecsShow = (ids, options) => runCommand("specs", options.format, async (
|
|
|
29192
29792
|
});
|
|
29193
29793
|
var runSpecsNew = (kind2, path3, options) => runCommand("specs", options.format, async () => createDocument(await openProject(process.cwd()), kind2, path3, options));
|
|
29194
29794
|
|
|
29195
|
-
// src/
|
|
29196
|
-
import { lstat as
|
|
29197
|
-
import { join as
|
|
29198
|
-
var instructionSorts = ["name", "updated"];
|
|
29199
|
-
var columns2 = ["id", "name", "path", "title", "description", "draft", "state", "previousPath", "files", "updated", "line"];
|
|
29795
|
+
// src/queries/instructions.ts
|
|
29796
|
+
import { lstat as lstat12 } from "node:fs/promises";
|
|
29797
|
+
import { join as join18 } from "node:path";
|
|
29200
29798
|
var AGENTS = "AGENTS.md";
|
|
29201
|
-
|
|
29202
|
-
const
|
|
29203
|
-
const project = await openProject(process.cwd());
|
|
29204
|
-
const listed3 = await project.cache.documents.list();
|
|
29799
|
+
async function listInstructions(source2, options, request) {
|
|
29800
|
+
const listed3 = await source2.cache.documents.list();
|
|
29205
29801
|
const problems = listed3.problems;
|
|
29206
|
-
const states = await documentStates(
|
|
29802
|
+
const states = await documentStates(source2, listed3.documents);
|
|
29207
29803
|
const documents = states.documents;
|
|
29208
|
-
const agents2 = { path: AGENTS, exists: !!(await
|
|
29209
|
-
const historyNeeded = options.sort === "updated" || options.author !== void 0 || !!
|
|
29804
|
+
const agents2 = { path: AGENTS, exists: !!(await lstat12(join18(source2.root, AGENTS)).catch(() => void 0))?.isFile() };
|
|
29805
|
+
const historyNeeded = options.sort === "updated" || options.author !== void 0 || !!options.withUpdated;
|
|
29210
29806
|
const latest = /* @__PURE__ */ new Map();
|
|
29211
29807
|
const newest = /* @__PURE__ */ new Map();
|
|
29212
29808
|
let touched;
|
|
29213
29809
|
if (historyNeeded) {
|
|
29214
|
-
const head = await
|
|
29215
|
-
const changes2 = head ? await
|
|
29810
|
+
const head = await source2.head();
|
|
29811
|
+
const changes2 = head ? await source2.cache.history.changesOf(head) : [];
|
|
29216
29812
|
changes2.forEach((c, rank) => {
|
|
29217
29813
|
if (!latest.has(c.id)) {
|
|
29218
29814
|
latest.set(c.id, { commit: c.commit, date: c.date, author: c.author, email: c.email });
|
|
@@ -29224,11 +29820,11 @@ var runInstructionsList = (options) => runCommand("instructions", options.format
|
|
|
29224
29820
|
touched = new Set(changes2.filter(by).map((c) => c.id));
|
|
29225
29821
|
}
|
|
29226
29822
|
}
|
|
29227
|
-
const matched = options.q !== void 0 ? await
|
|
29823
|
+
const matched = options.q !== void 0 ? await source2.cache.documents.matching(options.q) : void 0;
|
|
29228
29824
|
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);
|
|
29229
29825
|
const rows = await Promise.all(instructions2.map(async (d) => {
|
|
29230
29826
|
const state2 = states.state(d.id);
|
|
29231
|
-
const { files, limited } = state2 === "deleted" ? { files: [], limited: false } : await readInstructionFiles(
|
|
29827
|
+
const { files, limited } = state2 === "deleted" ? { files: [], limited: false } : await readInstructionFiles(source2.root, d.path);
|
|
29232
29828
|
return {
|
|
29233
29829
|
id: d.id,
|
|
29234
29830
|
name: d.name,
|
|
@@ -29245,7 +29841,19 @@ var runInstructionsList = (options) => runCommand("instructions", options.format
|
|
|
29245
29841
|
};
|
|
29246
29842
|
}));
|
|
29247
29843
|
if (options.sort === "updated") rows.sort((a, b) => (newest.get(a.id) ?? Infinity) - (newest.get(b.id) ?? Infinity));
|
|
29248
|
-
const page =
|
|
29844
|
+
const page = pageOf(rows, (r) => r.id, request);
|
|
29845
|
+
if (!page) return void 0;
|
|
29846
|
+
return { agents: agents2, problems, page };
|
|
29847
|
+
}
|
|
29848
|
+
|
|
29849
|
+
// src/commands/instructions.ts
|
|
29850
|
+
var instructionSorts = ["name", "updated"];
|
|
29851
|
+
var columns2 = ["id", "name", "path", "title", "description", "draft", "state", "previousPath", "files", "updated", "line"];
|
|
29852
|
+
var runInstructionsList = (options) => runCommand("instructions", options.format, async () => {
|
|
29853
|
+
const fields2 = checkFields(options.fields, columns2);
|
|
29854
|
+
const project = await openProject(process.cwd());
|
|
29855
|
+
const listed3 = await listInstructions(project, { ...options, withUpdated: !!fields2?.includes("updated") }, options) ?? cursorGone(options);
|
|
29856
|
+
const { agents: agents2, problems, page } = listed3;
|
|
29249
29857
|
const shown = page.rows;
|
|
29250
29858
|
const paging = { total: page.total, next: page.next, unit: "instruction" };
|
|
29251
29859
|
const unreadable2 = problems.filter((p) => p.path.startsWith(INSTRUCTIONS_ROOT + "/"));
|
|
@@ -29291,9 +29899,9 @@ var runInstructionsShow = (targets, options) => runCommand("instructions", optio
|
|
|
29291
29899
|
var runInstructionsNew = (name2, options) => runCommand("instructions", options.format, async () => createDocument(await openProject(process.cwd()), "instruction", name2, options));
|
|
29292
29900
|
|
|
29293
29901
|
// src/adapters/filesystem/document-warnings.ts
|
|
29294
|
-
import { lstat as
|
|
29295
|
-
import { join as
|
|
29296
|
-
var info2 = async (path3) =>
|
|
29902
|
+
import { lstat as lstat13, readdir as readdir6 } from "node:fs/promises";
|
|
29903
|
+
import { join as join19 } from "node:path";
|
|
29904
|
+
var info2 = async (path3) => lstat13(path3).catch((e) => {
|
|
29297
29905
|
if (e.code === "ENOENT") return void 0;
|
|
29298
29906
|
throw e;
|
|
29299
29907
|
});
|
|
@@ -29301,11 +29909,11 @@ var posix = (path3) => path3.split("\\").join("/");
|
|
|
29301
29909
|
async function listAssets(root) {
|
|
29302
29910
|
const out = [];
|
|
29303
29911
|
async function visit2(relative, depth) {
|
|
29304
|
-
const stat3 = await info2(
|
|
29912
|
+
const stat3 = await info2(join19(root, relative));
|
|
29305
29913
|
if (!stat3 || stat3.isSymbolicLink()) return;
|
|
29306
29914
|
if (stat3.isDirectory()) {
|
|
29307
29915
|
if (depth > 8 || out.length > 4e3) return;
|
|
29308
|
-
for (const name2 of (await readdir6(
|
|
29916
|
+
for (const name2 of (await readdir6(join19(root, relative))).sort()) await visit2(relative + "/" + name2, depth + 1);
|
|
29309
29917
|
} else if (stat3.isFile()) out.push({ path: posix(relative), bytes: stat3.size });
|
|
29310
29918
|
}
|
|
29311
29919
|
await visit2(ASSETS_DIR, 0);
|
|
@@ -29314,7 +29922,7 @@ async function listAssets(root) {
|
|
|
29314
29922
|
async function readDocumentWarnings(root, documents) {
|
|
29315
29923
|
const outside = [...new Set(documentBodyLinks(documents).map((l) => l.target).filter((t3) => !t3.startsWith(".gitifact/") || t3.startsWith(INSTRUCTIONS_ROOT + "/")))];
|
|
29316
29924
|
const existing = /* @__PURE__ */ new Set();
|
|
29317
|
-
for (const target of outside) if ((await info2(
|
|
29925
|
+
for (const target of outside) if ((await info2(join19(root, ...target.split("/"))))?.isFile()) existing.add(target);
|
|
29318
29926
|
return documentWarnings(documents, await listAssets(root), existing);
|
|
29319
29927
|
}
|
|
29320
29928
|
|
|
@@ -29337,33 +29945,33 @@ var runCheck = (options) => runCommand("check", options.format, async () => {
|
|
|
29337
29945
|
});
|
|
29338
29946
|
|
|
29339
29947
|
// src/commands/changes.ts
|
|
29340
|
-
import { randomUUID as
|
|
29341
|
-
import { mkdir as
|
|
29342
|
-
import { join as
|
|
29948
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
29949
|
+
import { mkdir as mkdir7, open as open5, readFile as readFile17, rename as rename4, rmdir, unlink as unlink5, writeFile as writeFile5 } from "node:fs/promises";
|
|
29950
|
+
import { join as join22 } from "node:path";
|
|
29343
29951
|
|
|
29344
29952
|
// src/adapters/filesystem/agent-inputs.ts
|
|
29345
29953
|
import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
|
|
29346
|
-
import { lstat as
|
|
29954
|
+
import { lstat as lstat14, mkdir as mkdir6, readdir as readdir7, realpath as realpath3, unlink as unlink4, writeFile as writeFile4 } from "node:fs/promises";
|
|
29347
29955
|
import { tmpdir } from "node:os";
|
|
29348
|
-
import { basename, dirname as dirname2, join as
|
|
29956
|
+
import { basename, dirname as dirname2, join as join20 } from "node:path";
|
|
29349
29957
|
var maxAge = 7 * 24 * 60 * 60 * 1e3;
|
|
29350
|
-
var info3 = (path3) =>
|
|
29958
|
+
var info3 = (path3) => lstat14(path3).catch((e) => {
|
|
29351
29959
|
if (e.code === "ENOENT") return void 0;
|
|
29352
29960
|
throw e;
|
|
29353
29961
|
});
|
|
29354
29962
|
var comparable = (path3) => process.platform === "win32" ? path3.toLowerCase() : path3;
|
|
29355
29963
|
async function candidates(root, controls) {
|
|
29356
29964
|
const key = createHash4("sha256").update(comparable(await realpath3(root))).digest("hex").slice(0, 16);
|
|
29357
|
-
return { system:
|
|
29965
|
+
return { system: join20((controls.tmpdir ?? tmpdir)(), "gitifact", key), project: join20(root, ".gitifact", "tmp") };
|
|
29358
29966
|
}
|
|
29359
29967
|
async function writable(directory) {
|
|
29360
29968
|
try {
|
|
29361
|
-
await
|
|
29969
|
+
await mkdir6(directory, { recursive: true });
|
|
29362
29970
|
const stat3 = await info3(directory);
|
|
29363
29971
|
if (!stat3?.isDirectory() || stat3.isSymbolicLink()) return false;
|
|
29364
|
-
const probe =
|
|
29365
|
-
await
|
|
29366
|
-
await
|
|
29972
|
+
const probe = join20(directory, ".probe-" + randomBytes2(8).toString("hex"));
|
|
29973
|
+
await writeFile4(probe, "", { flag: "wx" });
|
|
29974
|
+
await unlink4(probe);
|
|
29367
29975
|
return true;
|
|
29368
29976
|
} catch {
|
|
29369
29977
|
return false;
|
|
@@ -29375,7 +29983,7 @@ async function prepareAgentInputs(root, controls = {}) {
|
|
|
29375
29983
|
if (await writable(system)) directory = system;
|
|
29376
29984
|
else if (await writable(project)) {
|
|
29377
29985
|
directory = project;
|
|
29378
|
-
await
|
|
29986
|
+
await writeFile4(join20(project, ".gitignore"), "*\n", { flag: "wx" }).catch((e) => {
|
|
29379
29987
|
if (e.code !== "EEXIST") throw e;
|
|
29380
29988
|
});
|
|
29381
29989
|
}
|
|
@@ -29383,10 +29991,10 @@ async function prepareAgentInputs(root, controls = {}) {
|
|
|
29383
29991
|
const now = (controls.now ?? Date.now)();
|
|
29384
29992
|
for (const name2 of await readdir7(directory).catch(() => [])) {
|
|
29385
29993
|
if (name2 === ".gitignore") continue;
|
|
29386
|
-
const stat3 = await info3(
|
|
29387
|
-
if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await
|
|
29994
|
+
const stat3 = await info3(join20(directory, name2)).catch(() => void 0);
|
|
29995
|
+
if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await unlink4(join20(directory, name2)).catch(() => void 0);
|
|
29388
29996
|
}
|
|
29389
|
-
return { commit:
|
|
29997
|
+
return { commit: join20(directory, "commit.json") };
|
|
29390
29998
|
}
|
|
29391
29999
|
async function discardAgentInput(root, file2, controls = {}) {
|
|
29392
30000
|
try {
|
|
@@ -29399,7 +30007,7 @@ async function discardAgentInput(root, file2, controls = {}) {
|
|
|
29399
30007
|
if (await info3(directory) && comparable(await realpath3(directory)) === parent) managed = true;
|
|
29400
30008
|
}
|
|
29401
30009
|
if (!managed || basename(file2) === ".gitignore") return false;
|
|
29402
|
-
await
|
|
30010
|
+
await unlink4(file2);
|
|
29403
30011
|
return true;
|
|
29404
30012
|
} catch {
|
|
29405
30013
|
return false;
|
|
@@ -29408,17 +30016,17 @@ async function discardAgentInput(root, file2, controls = {}) {
|
|
|
29408
30016
|
|
|
29409
30017
|
// src/commands/commit-files.ts
|
|
29410
30018
|
import { createHash as createHash5 } from "node:crypto";
|
|
29411
|
-
import { lstat as
|
|
29412
|
-
import { join as
|
|
30019
|
+
import { lstat as lstat15, readFile as readFile15 } from "node:fs/promises";
|
|
30020
|
+
import { join as join21 } from "node:path";
|
|
29413
30021
|
var fail3 = (message) => {
|
|
29414
30022
|
throw new CommandError("INVALID_COMMIT", message);
|
|
29415
30023
|
};
|
|
29416
30024
|
var hash2 = (value) => createHash5("sha256").update(value).digest("hex");
|
|
29417
|
-
var optional2 = (path3) =>
|
|
30025
|
+
var optional2 = (path3) => readFile15(path3).catch((e) => {
|
|
29418
30026
|
if (e.code === "ENOENT") return null;
|
|
29419
30027
|
throw e;
|
|
29420
30028
|
});
|
|
29421
|
-
var info4 = (path3) =>
|
|
30029
|
+
var info4 = (path3) => lstat15(path3).catch((e) => {
|
|
29422
30030
|
if (e.code === "ENOENT") return void 0;
|
|
29423
30031
|
throw e;
|
|
29424
30032
|
});
|
|
@@ -29444,14 +30052,14 @@ async function fingerprint(root, path3) {
|
|
|
29444
30052
|
validPath2(path3);
|
|
29445
30053
|
const parts = path3.split("/");
|
|
29446
30054
|
for (let i = 1; i <= parts.length; i++) {
|
|
29447
|
-
const target =
|
|
30055
|
+
const target = join21(root, ...parts.slice(0, i));
|
|
29448
30056
|
const stat3 = await info4(target);
|
|
29449
30057
|
if (!stat3) return null;
|
|
29450
30058
|
if (stat3.isSymbolicLink() || (i < parts.length ? !stat3.isDirectory() : !stat3.isFile())) fail3(t2("commitFiles.linkOrDirectory"));
|
|
29451
|
-
if (i < parts.length && await info4(
|
|
30059
|
+
if (i < parts.length && await info4(join21(target, ".git"))) fail3(t2("commitFiles.nestedRepository"));
|
|
29452
30060
|
if (i === parts.length) {
|
|
29453
30061
|
if (stat3.nlink !== 1 || stat3.size > 16 * 1024 * 1024) fail3(t2("commitFiles.hardLinkOrSize"));
|
|
29454
|
-
return hash2(Buffer.concat([Buffer.from(String(stat3.mode & 73) + "\0"), await
|
|
30062
|
+
return hash2(Buffer.concat([Buffer.from(String(stat3.mode & 73) + "\0"), await readFile15(target)]));
|
|
29455
30063
|
}
|
|
29456
30064
|
}
|
|
29457
30065
|
return null;
|
|
@@ -29478,14 +30086,15 @@ function policyPaths(files) {
|
|
|
29478
30086
|
}
|
|
29479
30087
|
return [...all].sort();
|
|
29480
30088
|
}
|
|
30089
|
+
var storeFiles = /* @__PURE__ */ new Set([".gitifact/config.json", LINE_ENDINGS_PATH]);
|
|
29481
30090
|
function checkStoreSelection(selected2) {
|
|
29482
|
-
for (const p of selected2.filter((p2) => p2.startsWith(".gitifact/") && !record3(p2) && p2
|
|
30091
|
+
for (const p of selected2.filter((p2) => p2.startsWith(".gitifact/") && !record3(p2) && !storeFiles.has(p2))) {
|
|
29483
30092
|
if (!isAssetPath(p) && !instructionFile2(p)) fail3(t2("commitFiles.notRecord", { path: p }));
|
|
29484
30093
|
}
|
|
29485
30094
|
}
|
|
29486
30095
|
|
|
29487
30096
|
// src/commands/input.ts
|
|
29488
|
-
import { readFile as
|
|
30097
|
+
import { readFile as readFile16, stat as stat2 } from "node:fs/promises";
|
|
29489
30098
|
var LIMIT2 = 1024 * 1024;
|
|
29490
30099
|
async function readJsonInput(file2, stdin = process.stdin) {
|
|
29491
30100
|
const bytes = file2 === "-" ? await readStdin(stdin) : await readInputFile(file2);
|
|
@@ -29498,7 +30107,7 @@ async function readJsonInput(file2, stdin = process.stdin) {
|
|
|
29498
30107
|
async function readInputFile(path3) {
|
|
29499
30108
|
const source2 = await stat2(path3).catch(() => void 0);
|
|
29500
30109
|
if (!source2?.isFile() || source2.size > LIMIT2) throw new CommandError("INVALID_INPUT", t2("input.file"));
|
|
29501
|
-
return
|
|
30110
|
+
return readFile16(path3);
|
|
29502
30111
|
}
|
|
29503
30112
|
async function readStdin(stream) {
|
|
29504
30113
|
const chunks = [];
|
|
@@ -29598,7 +30207,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29598
30207
|
...stdin ? { input: stdin } : {}
|
|
29599
30208
|
});
|
|
29600
30209
|
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();
|
|
29601
|
-
const busy =
|
|
30210
|
+
const busy = join22(gitDir, "gitifact-changes-commit.lock");
|
|
29602
30211
|
if (await info4(busy)) fail3(t2("commit.busy", { path: busy }));
|
|
29603
30212
|
if ((await staged()).length) fail3(t2("commit.existingStaging"));
|
|
29604
30213
|
const indexHash = hash2(await optional2(indexPath) ?? Buffer.alloc(0));
|
|
@@ -29637,7 +30246,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29637
30246
|
if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
|
|
29638
30247
|
return { outcome: "dry-run", committed: false, ...summary, commit: void 0 };
|
|
29639
30248
|
}
|
|
29640
|
-
const temporary =
|
|
30249
|
+
const temporary = join22(gitDir, "gitifact-commit-index-" + randomUUID6());
|
|
29641
30250
|
let owned = false;
|
|
29642
30251
|
let uncertain = false;
|
|
29643
30252
|
let commitStarted = false;
|
|
@@ -29645,7 +30254,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29645
30254
|
let committed2;
|
|
29646
30255
|
try {
|
|
29647
30256
|
try {
|
|
29648
|
-
await
|
|
30257
|
+
await mkdir7(busy);
|
|
29649
30258
|
owned = true;
|
|
29650
30259
|
} catch (e) {
|
|
29651
30260
|
if (e.code === "EEXIST") fail3(t2("commit.busy", { path: busy }));
|
|
@@ -29656,31 +30265,31 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29656
30265
|
if (hash2(original ?? Buffer.alloc(0)) !== indexHash || (await staged()).length) fail3(t2("commit.indexChanged"));
|
|
29657
30266
|
const before = await hashes([...started.keys()]);
|
|
29658
30267
|
for (const [path3, value] of started) if (before.get(path3) !== value) fail3(t2("commit.fileChangedRunning", { path: path3 }));
|
|
29659
|
-
await
|
|
30268
|
+
await writeFile5(join22(busy, "recovery.json"), JSON.stringify({ before: base, originalIndex: original?.toString("base64") ?? null, temporary, paths: selected2, message }));
|
|
29660
30269
|
const locked = await hashes([.../* @__PURE__ */ new Set([...selected2, ...contextPaths])]);
|
|
29661
|
-
if (original) await
|
|
30270
|
+
if (original) await writeFile5(temporary, original, { flag: "wx" });
|
|
29662
30271
|
else await git(["read-tree", "--empty"], temporary);
|
|
29663
30272
|
await git(["add", "--pathspec-from-file=-", "--pathspec-file-nul"], temporary, Buffer.from(stageable.join("\0")));
|
|
29664
30273
|
const actual = await staged(temporary);
|
|
29665
30274
|
if (!actual.length || actual.some((p) => !selected2.includes(p))) fail3(t2("commit.unexpectedFiles"));
|
|
29666
|
-
const
|
|
29667
|
-
if (
|
|
29668
|
-
const output2 = await git(["cat-file", "--batch"], temporary, Buffer.from(
|
|
30275
|
+
const storeFiles2 = selected2.filter((p) => record3(p) && locked.get(p) !== null);
|
|
30276
|
+
if (storeFiles2.length) {
|
|
30277
|
+
const output2 = await git(["cat-file", "--batch"], temporary, Buffer.from(storeFiles2.map((p) => ":" + p).join("\n") + "\n"));
|
|
29669
30278
|
let offset = 0;
|
|
29670
|
-
for (const path3 of
|
|
30279
|
+
for (const path3 of storeFiles2) {
|
|
29671
30280
|
const end = output2.indexOf(10, offset);
|
|
29672
30281
|
const header = output2.subarray(offset, end).toString("utf8").split(" ");
|
|
29673
30282
|
const size = Number(header[2]);
|
|
29674
30283
|
if (end < 0 || header[1] !== "blob" || !Number.isSafeInteger(size) || output2[end + 1 + size] !== 10) fail3(t2("commit.stagedUnreadable", { path: path3 }));
|
|
29675
30284
|
const blob = output2.subarray(end + 1, end + 1 + size);
|
|
29676
|
-
const file2 = await
|
|
30285
|
+
const file2 = await readFile17(join22(root, path3));
|
|
29677
30286
|
if (!blob.equals(file2) && !blob.equals(Buffer.from(file2.toString("latin1").replace(/\r\n/g, "\n"), "latin1"))) fail3(t2("commit.filterChanged"));
|
|
29678
30287
|
offset = end + size + 2;
|
|
29679
30288
|
}
|
|
29680
30289
|
}
|
|
29681
30290
|
const again = await hashes([...locked.keys()]);
|
|
29682
30291
|
for (const [path3, value] of locked) if (again.get(path3) !== value) fail3(t2("commit.fileChangedPreparing", { path: path3 }));
|
|
29683
|
-
for (const name2 of integration) if (await info4(
|
|
30292
|
+
for (const name2 of integration) if (await info4(join22(gitDir, name2))) fail3(t2("commit.integrationStarted"));
|
|
29684
30293
|
if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
|
|
29685
30294
|
const tree = (await git(["write-tree"], temporary)).toString("utf8").trim();
|
|
29686
30295
|
commitStarted = true;
|
|
@@ -29695,10 +30304,10 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29695
30304
|
if (headers[0] !== "tree " + tree || after.branch !== base.branch || !same2(parents, base.head ? [base.head] : []) || trailers.some((line2) => !body2.split(/\r?\n/).includes(line2))) fail3(t2("commit.resultAltered"));
|
|
29696
30305
|
if (hash2(await optional2(indexPath) ?? Buffer.alloc(0)) !== indexHash) fail3(t2("commit.indexChangedAfter"));
|
|
29697
30306
|
if ((await git(["write-tree"], temporary)).toString("utf8").trim() !== tree) fail3(t2("commit.hookChangedStaging"));
|
|
29698
|
-
await lock.writeFile(await
|
|
30307
|
+
await lock.writeFile(await readFile17(temporary));
|
|
29699
30308
|
await lock.sync();
|
|
29700
30309
|
await lock.close();
|
|
29701
|
-
await
|
|
30310
|
+
await rename4(indexPath + ".lock", indexPath);
|
|
29702
30311
|
indexLock = void 0;
|
|
29703
30312
|
committed2 = { commit: after.head, paths: actual };
|
|
29704
30313
|
} catch (error62) {
|
|
@@ -29715,12 +30324,12 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29715
30324
|
await indexLock?.close().catch(() => {
|
|
29716
30325
|
});
|
|
29717
30326
|
if (!uncertain) {
|
|
29718
|
-
if (indexLock) await
|
|
30327
|
+
if (indexLock) await unlink5(indexPath + ".lock").catch(() => {
|
|
29719
30328
|
});
|
|
29720
|
-
await
|
|
30329
|
+
await unlink5(temporary).catch(() => {
|
|
29721
30330
|
});
|
|
29722
30331
|
if (owned) {
|
|
29723
|
-
await
|
|
30332
|
+
await unlink5(join22(busy, "recovery.json")).catch(() => {
|
|
29724
30333
|
});
|
|
29725
30334
|
await rmdir(busy);
|
|
29726
30335
|
}
|
|
@@ -29730,8 +30339,8 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29730
30339
|
}
|
|
29731
30340
|
|
|
29732
30341
|
// src/commands/records.ts
|
|
29733
|
-
import { readFile as
|
|
29734
|
-
import { join as
|
|
30342
|
+
import { readFile as readFile18 } from "node:fs/promises";
|
|
30343
|
+
import { join as join23 } from "node:path";
|
|
29735
30344
|
var runRecordsNew = (options) => runCommand("records", options.format, async () => {
|
|
29736
30345
|
if (/[\r\n]/.test(options.title)) throw new CommandError("INVALID_VALUE", t2("docs.singleLine", { field: "title" }));
|
|
29737
30346
|
const docs = [...new Set(options.docs.flatMap((d) => d.split(",")).map((d) => d.trim()).filter(Boolean))];
|
|
@@ -29767,7 +30376,7 @@ var runRecordsShow = (ids, options) => runCommand("records", options.format, asy
|
|
|
29767
30376
|
const shown = await Promise.all([...new Set(ids)].map(async (id) => {
|
|
29768
30377
|
const path3 = await findRecord(project.root, id);
|
|
29769
30378
|
if (!path3) return { id, missing: true };
|
|
29770
|
-
const source2 = await
|
|
30379
|
+
const source2 = await readFile18(join23(project.root, ...path3.split("/")), "utf8");
|
|
29771
30380
|
const log = project.reader.decode(await project.reader.run(["log", "-1", "--diff-filter=A", "--format=%H%x00%aN%x00%aI", "--", path3])).trim();
|
|
29772
30381
|
const [commit, author, date5] = log.split("\0");
|
|
29773
30382
|
return { id, path: path3, text: source2, commit: commit ? { commit, author, date: date5 } : null };
|
|
@@ -29875,9 +30484,7 @@ async function readThrough(project, head, filter, id) {
|
|
|
29875
30484
|
}
|
|
29876
30485
|
|
|
29877
30486
|
// src/commands/feedback.ts
|
|
29878
|
-
import { readFile as readFile18 } from "node:fs/promises";
|
|
29879
30487
|
import { release as release3 } from "node:os";
|
|
29880
|
-
import { dirname as dirname3, join as join22 } from "node:path";
|
|
29881
30488
|
|
|
29882
30489
|
// src/adapters/github/issue.ts
|
|
29883
30490
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -29931,6 +30538,17 @@ function newIssueUrl(title, body2, marker2) {
|
|
|
29931
30538
|
return { url: address(characters.slice(0, low).join("") + marker2), truncated: true };
|
|
29932
30539
|
}
|
|
29933
30540
|
|
|
30541
|
+
// src/shared/project-config.ts
|
|
30542
|
+
import { readFile as readFile19 } from "node:fs/promises";
|
|
30543
|
+
import { dirname as dirname3, join as join24 } from "node:path";
|
|
30544
|
+
async function findProjectConfig(from) {
|
|
30545
|
+
for (let folder = from; ; folder = dirname3(folder)) {
|
|
30546
|
+
const text4 = await readFile19(join24(folder, ".gitifact", "config.json"), "utf8").catch(() => void 0);
|
|
30547
|
+
if (text4 !== void 0) return { root: folder, text: text4 };
|
|
30548
|
+
if (dirname3(folder) === folder) return void 0;
|
|
30549
|
+
}
|
|
30550
|
+
}
|
|
30551
|
+
|
|
29934
30552
|
// src/commands/feedback.ts
|
|
29935
30553
|
var types = ["bug", "idea"];
|
|
29936
30554
|
function parseFeedback(input2) {
|
|
@@ -29943,17 +30561,13 @@ function parseFeedback(input2) {
|
|
|
29943
30561
|
return { type, title: title.trim(), body: body2.trim() };
|
|
29944
30562
|
}
|
|
29945
30563
|
async function schemaVersion(from) {
|
|
29946
|
-
|
|
29947
|
-
|
|
29948
|
-
|
|
29949
|
-
|
|
29950
|
-
|
|
29951
|
-
|
|
29952
|
-
|
|
29953
|
-
return "none";
|
|
29954
|
-
}
|
|
29955
|
-
}
|
|
29956
|
-
if (dirname3(folder) === folder) return "none";
|
|
30564
|
+
const config2 = (await findProjectConfig(from))?.text;
|
|
30565
|
+
if (config2 === void 0) return "none";
|
|
30566
|
+
try {
|
|
30567
|
+
const value = JSON.parse(config2).schemaVersion;
|
|
30568
|
+
return typeof value === "number" ? String(value) : "none";
|
|
30569
|
+
} catch {
|
|
30570
|
+
return "none";
|
|
29957
30571
|
}
|
|
29958
30572
|
}
|
|
29959
30573
|
async function issueBody(feedback, version2, cwd) {
|
|
@@ -29990,6 +30604,11 @@ var runFeedback = (options, version2, run) => runCommand("feedback", options.for
|
|
|
29990
30604
|
return { json: { ...sent, title: feedback.title, body: body2 }, text: text2(out) };
|
|
29991
30605
|
});
|
|
29992
30606
|
|
|
30607
|
+
// src/commands/update.ts
|
|
30608
|
+
import { readFile as readFile20 } from "node:fs/promises";
|
|
30609
|
+
import { join as join25 } from "node:path";
|
|
30610
|
+
import { isDeepStrictEqual } from "node:util";
|
|
30611
|
+
|
|
29993
30612
|
// src/adapters/git/agent-docs-commit.ts
|
|
29994
30613
|
function agentDocsGit(root, inherited = process.env) {
|
|
29995
30614
|
const env = { ...inherited, GIT_OPTIONAL_LOCKS: "0", GIT_NO_LAZY_FETCH: "1", GIT_TERMINAL_PROMPT: "0", GIT_LITERAL_PATHSPECS: "1", LC_ALL: "C" };
|
|
@@ -30021,9 +30640,11 @@ function agentDocsGit(root, inherited = process.env) {
|
|
|
30021
30640
|
}
|
|
30022
30641
|
|
|
30023
30642
|
// src/commands/update.ts
|
|
30024
|
-
var
|
|
30643
|
+
var CONFIG_PATH = ".gitifact/config.json";
|
|
30644
|
+
var updateCommitMessage = (version2) => "chore(gitifact): update project to gitifact v" + version2;
|
|
30025
30645
|
var commitState = (state2, extra = {}) => ({ state: state2, commit: null, paths: [], message: null, reason: null, detail: null, ...extra });
|
|
30026
|
-
|
|
30646
|
+
var notInitialized = { docs: { state: "not-initialized", paths: [], missing: [] }, project: { state: "not-initialized", cli: null } };
|
|
30647
|
+
async function refreshProject(cwd, version2, env, controls) {
|
|
30027
30648
|
const repo = initRepository(cwd, env);
|
|
30028
30649
|
let first;
|
|
30029
30650
|
let config2;
|
|
@@ -30031,20 +30652,28 @@ async function refreshBlocks(cwd, version2, env, controls) {
|
|
|
30031
30652
|
first = await repo.inspect();
|
|
30032
30653
|
config2 = await readConfigFile(first.state.repository.rootPath);
|
|
30033
30654
|
} catch (error62) {
|
|
30034
|
-
if (error62 instanceof RepositoryReadError && error62.code === "NOT_A_REPOSITORY") return
|
|
30655
|
+
if (error62 instanceof RepositoryReadError && error62.code === "NOT_A_REPOSITORY") return notInitialized;
|
|
30035
30656
|
throw error62;
|
|
30036
30657
|
}
|
|
30037
|
-
if (config2 === void 0) return
|
|
30658
|
+
if (config2 === void 0) return notInitialized;
|
|
30038
30659
|
const root = first.state.repository.rootPath;
|
|
30039
|
-
const
|
|
30660
|
+
const parsed2 = needsMigration(config2) ? void 0 : parseManagedConfig(config2);
|
|
30661
|
+
const plan = await planAgentDocs(root, { version: version2, onlyExisting: true, language: parsed2?.language, ...controls });
|
|
30040
30662
|
const { missing: missing2 } = plan;
|
|
30041
|
-
if (
|
|
30042
|
-
|
|
30663
|
+
if (parsed2?.cli && isNewerRelease(parsed2.cli, version2)) return { docs: { state: "kept", paths: plan.paths, missing: missing2, root }, project: { state: "current", cli: parsed2.cli } };
|
|
30664
|
+
let current2 = config2;
|
|
30043
30665
|
const unchanged = async () => {
|
|
30044
|
-
if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !==
|
|
30666
|
+
if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !== current2) throw new InitError("INPUT_CHANGED", t2("init.inputChanged"));
|
|
30045
30667
|
};
|
|
30046
|
-
const
|
|
30047
|
-
return {
|
|
30668
|
+
const docs = plan.paths.length === 0 ? { state: "no-block", paths: [], missing: missing2, root } : plan.writes.length === 0 ? { state: "current", paths: plan.paths, missing: missing2, root, candidates: plan.paths } : { state: "refreshed", paths: await applyAgentDocs(root, plan, unchanged), missing: missing2, root, candidates: plan.paths };
|
|
30669
|
+
if (!parsed2) return { docs, project: { state: "current", cli: null } };
|
|
30670
|
+
const settled2 = settleConfig(parsed2, version2, plan.language);
|
|
30671
|
+
if (settled2.changed) {
|
|
30672
|
+
const next = formatManagedConfig(settled2.config, config2);
|
|
30673
|
+
await publishConfig(root, next, unchanged, void 0, true);
|
|
30674
|
+
current2 = next;
|
|
30675
|
+
}
|
|
30676
|
+
return { docs: { ...docs, root }, project: { state: settled2.changed ? "written" : "current", cli: settled2.config.cli ?? null } };
|
|
30048
30677
|
}
|
|
30049
30678
|
function outsideBlock(text4) {
|
|
30050
30679
|
const normalized2 = text4.replace(/\r\n/g, "\n");
|
|
@@ -30055,12 +30684,20 @@ function outsideBlock(text4) {
|
|
|
30055
30684
|
return null;
|
|
30056
30685
|
}
|
|
30057
30686
|
}
|
|
30058
|
-
|
|
30059
|
-
|
|
30687
|
+
function withoutSettings(text4) {
|
|
30688
|
+
try {
|
|
30689
|
+
const { cli: _c, language: _l, ...rest } = JSON.parse(text4);
|
|
30690
|
+
return rest;
|
|
30691
|
+
} catch {
|
|
30692
|
+
return null;
|
|
30693
|
+
}
|
|
30694
|
+
}
|
|
30695
|
+
async function commitUpdate(docs, version2, env) {
|
|
30696
|
+
if (!docs.root) return commitState("nothing");
|
|
30060
30697
|
const git = agentDocsGit(docs.root, env);
|
|
30061
30698
|
const head = await git.head();
|
|
30062
30699
|
const changed = [];
|
|
30063
|
-
for (const path3 of docs.candidates) {
|
|
30700
|
+
for (const path3 of docs.candidates ?? []) {
|
|
30064
30701
|
const working2 = await managedRead(docs.root, path3);
|
|
30065
30702
|
const committed2 = head ? await git.committed(head, path3) : null;
|
|
30066
30703
|
if (working2 === null) continue;
|
|
@@ -30070,8 +30707,16 @@ async function commitBlocks(docs, version2, env) {
|
|
|
30070
30707
|
if (before === null || before !== outsideBlock(working2)) return commitState("skipped", { paths: [path3], reason: "other-changes" });
|
|
30071
30708
|
changed.push(path3);
|
|
30072
30709
|
}
|
|
30710
|
+
const workingConfig = await readFile20(join25(docs.root, CONFIG_PATH), "utf8").catch(() => null);
|
|
30711
|
+
const committedConfig = head ? await git.committed(head, CONFIG_PATH) : null;
|
|
30712
|
+
if (workingConfig !== null && committedConfig?.replace(/\r\n/g, "\n") !== workingConfig.replace(/\r\n/g, "\n")) {
|
|
30713
|
+
if (committedConfig === null) return commitState("skipped", { paths: [CONFIG_PATH], reason: "untracked" });
|
|
30714
|
+
const before = withoutSettings(committedConfig);
|
|
30715
|
+
if (before === null || !isDeepStrictEqual(before, withoutSettings(workingConfig))) return commitState("skipped", { paths: [CONFIG_PATH], reason: "other-changes" });
|
|
30716
|
+
changed.push(CONFIG_PATH);
|
|
30717
|
+
}
|
|
30073
30718
|
if (changed.length === 0) return commitState("nothing");
|
|
30074
|
-
const message =
|
|
30719
|
+
const message = updateCommitMessage(version2);
|
|
30075
30720
|
let failure2;
|
|
30076
30721
|
try {
|
|
30077
30722
|
await git.commitOnly(changed, message);
|
|
@@ -30085,20 +30730,23 @@ async function commitBlocks(docs, version2, env) {
|
|
|
30085
30730
|
return commitState("committed", { commit: after, paths: await git.changedPaths(after), message });
|
|
30086
30731
|
}
|
|
30087
30732
|
async function updateCommand(cwd, version2, env = process.env, controls = {}) {
|
|
30088
|
-
const [update,
|
|
30733
|
+
const [update, refreshed] = await Promise.all([
|
|
30089
30734
|
updateCheckDisabled(env) ? disabledUpdate : resolveUpdate(version2, controls.fetchLatest ?? fetchLatestVersion, void 0, controls.timeoutMs),
|
|
30090
|
-
|
|
30735
|
+
refreshProject(cwd, version2, env, controls)
|
|
30091
30736
|
]);
|
|
30092
|
-
|
|
30093
|
-
const
|
|
30094
|
-
|
|
30737
|
+
await recordUpdateCheck(env, update, controls.now?.());
|
|
30738
|
+
const { docs, project } = refreshed;
|
|
30739
|
+
const commit = controls.commit ? await commitUpdate(docs, version2, env) : commitState("not-requested");
|
|
30740
|
+
const config2 = await readConfigFile(docs.root ?? cwd).catch(() => void 0);
|
|
30741
|
+
return updateV6.parse({
|
|
30095
30742
|
contract: "update",
|
|
30096
|
-
version:
|
|
30743
|
+
version: 6,
|
|
30097
30744
|
ok: true,
|
|
30098
30745
|
cliVersion: version2,
|
|
30099
30746
|
update,
|
|
30100
|
-
install: update.status === "available" ? {
|
|
30101
|
-
agentDocs: { state:
|
|
30747
|
+
install: update.status === "available" ? { npmGlobal: npmGlobalInstall(update.latestVersion) } : null,
|
|
30748
|
+
agentDocs: { state: docs.state, paths: docs.paths, missing: docs.missing },
|
|
30749
|
+
project,
|
|
30102
30750
|
commit,
|
|
30103
30751
|
migrationRequired: config2 !== void 0 && needsMigration(config2)
|
|
30104
30752
|
});
|
|
@@ -30113,9 +30761,11 @@ async function runUpdate(options, version2) {
|
|
|
30113
30761
|
}
|
|
30114
30762
|
const lines = [t2("update.text.current", { version: dto.cliVersion })];
|
|
30115
30763
|
lines.push(dto.update.status === "available" ? t2("update.text.available", { version: dto.update.latestVersion }) : dto.update.status === "up-to-date" ? t2("update.text.upToDate") : dto.update.status === "disabled" ? t2("update.text.disabled") : t2("update.text.unavailable"));
|
|
30116
|
-
if (dto.install) lines.push(t2("update.text.install", { command: dto.install.
|
|
30117
|
-
lines.push(dto.agentDocs.state === "refreshed" ? t2("update.text.blockRefreshed", { paths: dto.agentDocs.paths.join(", ") }) : dto.agentDocs.state === "current" ? t2("update.text.blockCurrent", { paths: dto.agentDocs.paths.join(", ") }) : dto.agentDocs.state === "
|
|
30118
|
-
if (dto.agentDocs.missing.length) lines.push(t2("update.text.missing", { paths: dto.agentDocs.missing.join(", ")
|
|
30764
|
+
if (dto.install) lines.push(t2("update.text.install", { command: dto.install.npmGlobal }), t2("update.text.afterInstall"));
|
|
30765
|
+
lines.push(dto.agentDocs.state === "refreshed" ? t2("update.text.blockRefreshed", { paths: dto.agentDocs.paths.join(", ") }) : dto.agentDocs.state === "current" ? t2("update.text.blockCurrent", { paths: dto.agentDocs.paths.join(", ") }) : dto.agentDocs.state === "kept" ? t2("update.text.blockKept", { paths: dto.agentDocs.paths.join(", ") || "-" }) : dto.agentDocs.state === "no-block" ? t2("update.text.noBlock") : t2("update.text.notInitialized"));
|
|
30766
|
+
if (dto.agentDocs.missing.length) lines.push(t2("update.text.missing", { paths: dto.agentDocs.missing.join(", ") }));
|
|
30767
|
+
if (dto.project.state === "written") lines.push(t2("update.text.projectWritten", { path: CONFIG_PATH, version: dto.project.cli ?? dto.cliVersion }));
|
|
30768
|
+
if (dto.project.cli && isNewerRelease(dto.project.cli, dto.cliVersion)) lines.push(t2("update.text.projectAhead", { project: dto.project.cli, version: dto.cliVersion, command: npmGlobalInstall(dto.project.cli) }));
|
|
30119
30769
|
if (dto.migrationRequired) lines.push(t2("update.text.migrationRequired", { version: dto.cliVersion }));
|
|
30120
30770
|
const { commit } = dto;
|
|
30121
30771
|
if (commit.state === "committed") lines.push(t2("update.text.committed", { commit: commit.commit.slice(0, 12), paths: commit.paths.join(", ") }));
|
|
@@ -30129,7 +30779,7 @@ async function runUpdate(options, version2) {
|
|
|
30129
30779
|
} catch (error62) {
|
|
30130
30780
|
const known = error62 instanceof InitError || error62 instanceof RepositoryReadError;
|
|
30131
30781
|
const failure2 = { code: known ? error62.code : "UPDATE_FAILED", message: known ? error62.message : t2("update.failed") };
|
|
30132
|
-
process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(
|
|
30782
|
+
process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(updateV6.parse({ contract: "update", version: 6, ok: false, error: failure2 })) + "\n");
|
|
30133
30783
|
process.exitCode = 1;
|
|
30134
30784
|
}
|
|
30135
30785
|
}
|
|
@@ -30137,6 +30787,7 @@ async function runUpdate(options, version2) {
|
|
|
30137
30787
|
// src/commands/update-check.ts
|
|
30138
30788
|
async function checkUpdateCommand(version2, env = process.env, controls = {}) {
|
|
30139
30789
|
const update = updateCheckDisabled(env) ? disabledUpdate : await resolveUpdate(version2, controls.fetchLatest ?? fetchLatestVersion, void 0, controls.timeoutMs);
|
|
30790
|
+
await recordUpdateCheck(env, update, controls.now?.());
|
|
30140
30791
|
return updateCheckV1.parse({
|
|
30141
30792
|
contract: "update-check",
|
|
30142
30793
|
version: 1,
|
|
@@ -30157,10 +30808,73 @@ async function runUpdateCheck(format2, version2) {
|
|
|
30157
30808
|
t2("update.text.current", { version: dto.cliVersion }),
|
|
30158
30809
|
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")
|
|
30159
30810
|
];
|
|
30160
|
-
if (
|
|
30811
|
+
if (state2.status === "available") lines.push(t2("update.text.install", { command: npmGlobalInstall(state2.latestVersion) }), t2("update.text.afterInstall"));
|
|
30161
30812
|
process.stdout.write(lines.join("\n") + "\n");
|
|
30162
30813
|
}
|
|
30163
30814
|
|
|
30815
|
+
// src/commands/version-notice.ts
|
|
30816
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
30817
|
+
import { homedir as homedir2 } from "node:os";
|
|
30818
|
+
var REFRESH_AFTER_MS = 60 * 60 * 1e3;
|
|
30819
|
+
var CHECKING_WINDOW_MS = 5 * 60 * 1e3;
|
|
30820
|
+
var LATER_MS = 24 * 60 * 60 * 1e3;
|
|
30821
|
+
var REFRESH_COMMAND = "__refresh-update";
|
|
30822
|
+
var noticeScope = (command) => ({ release: command !== "init" && command !== "update", projectOlder: command !== "update" });
|
|
30823
|
+
function chooseNotice(running2, project, cache, now, scope = { release: true, projectOlder: true }) {
|
|
30824
|
+
if (project && isNewerRelease(project, running2)) return { kind: "project-newer", project };
|
|
30825
|
+
const latest = cache.latest;
|
|
30826
|
+
const postponed = cache.later !== void 0 && cache.later.version === latest && now < Date.parse(cache.later.until);
|
|
30827
|
+
if (scope.release && latest && isNewerRelease(latest, running2) && !postponed) return { kind: "release", latest };
|
|
30828
|
+
if (scope.projectOlder && project && isNewerRelease(running2, project)) return { kind: "project-older", project };
|
|
30829
|
+
return null;
|
|
30830
|
+
}
|
|
30831
|
+
function noticeText(notice, running2) {
|
|
30832
|
+
if (notice.kind === "project-newer") return t2("notice.projectNewer", { project: notice.project, version: running2, command: npmGlobalInstall(notice.project) });
|
|
30833
|
+
if (notice.kind === "release") return t2("notice.release", { latest: notice.latest, version: running2, command: npmGlobalInstall(notice.latest) });
|
|
30834
|
+
return t2("notice.projectOlder", { project: notice.project, version: running2 });
|
|
30835
|
+
}
|
|
30836
|
+
var needsRefresh = (cache, now) => !(cache.checkedAt && now - Date.parse(cache.checkedAt) < REFRESH_AFTER_MS) && !(cache.checkingSince && now - Date.parse(cache.checkingSince) < CHECKING_WINDOW_MS);
|
|
30837
|
+
var spawnRefresh = () => {
|
|
30838
|
+
const child = spawn3(process.execPath, [...process.execArgv, process.argv[1], REFRESH_COMMAND], { cwd: homedir2(), detached: true, stdio: "ignore", windowsHide: true });
|
|
30839
|
+
child.on("error", () => {
|
|
30840
|
+
});
|
|
30841
|
+
child.unref();
|
|
30842
|
+
};
|
|
30843
|
+
async function noticeVersion(version2, command, controls = {}) {
|
|
30844
|
+
const env = controls.env ?? process.env;
|
|
30845
|
+
const now = controls.now ?? Date.now();
|
|
30846
|
+
try {
|
|
30847
|
+
const project = projectSettings((await findProjectConfig(controls.cwd ?? process.cwd()))?.text).cli;
|
|
30848
|
+
const scope = noticeScope(command);
|
|
30849
|
+
let cache = {};
|
|
30850
|
+
if (!updateCheckDisabled(env)) {
|
|
30851
|
+
cache = await readUpdateCache(env);
|
|
30852
|
+
if (scope.release && needsRefresh(cache, now)) {
|
|
30853
|
+
await changeUpdateCache(env, (current2) => ({ ...current2, checkingSince: new Date(now).toISOString() }));
|
|
30854
|
+
(controls.refresh ?? spawnRefresh)();
|
|
30855
|
+
}
|
|
30856
|
+
}
|
|
30857
|
+
const notice = chooseNotice(version2, project, cache, now, scope);
|
|
30858
|
+
if (notice) (controls.write ?? ((line2) => process.stderr.write(line2 + "\n")))(noticeText(notice, version2));
|
|
30859
|
+
} catch {
|
|
30860
|
+
}
|
|
30861
|
+
}
|
|
30862
|
+
async function refreshUpdateCache(version2, env = process.env, fetchLatest = fetchLatestVersion, now) {
|
|
30863
|
+
if (updateCheckDisabled(env)) return;
|
|
30864
|
+
const update = await resolveUpdate(version2, fetchLatest);
|
|
30865
|
+
await recordUpdateCheck(env, update, now?.());
|
|
30866
|
+
}
|
|
30867
|
+
async function postponeRelease(version2, env = process.env, now = Date.now()) {
|
|
30868
|
+
const cache = await readUpdateCache(env);
|
|
30869
|
+
const later = cache.latest && isNewerRelease(cache.latest, version2) ? { version: cache.latest, until: new Date(now + LATER_MS).toISOString() } : null;
|
|
30870
|
+
if (later) await changeUpdateCache(env, (current2) => ({ ...current2, later }));
|
|
30871
|
+
return updateLaterV1.parse({ contract: "update-later", version: 1, ok: true, cliVersion: version2, later });
|
|
30872
|
+
}
|
|
30873
|
+
async function runUpdateLater(format2, version2) {
|
|
30874
|
+
const dto = await postponeRelease(version2);
|
|
30875
|
+
process.stdout.write(format2 === "json" ? JSON.stringify(dto) + "\n" : (dto.later ? t2("update.later.postponed", { latest: dto.later.version, until: new Date(dto.later.until).toLocaleString() }) : t2("update.later.nothing", { version: version2 })) + "\n");
|
|
30876
|
+
}
|
|
30877
|
+
|
|
30164
30878
|
// src/main.ts
|
|
30165
30879
|
var languageArgs = process.argv.slice(2).slice(0, process.argv.slice(2).indexOf("--") < 0 ? void 0 : process.argv.slice(2).indexOf("--"));
|
|
30166
30880
|
var selectedLanguage;
|
|
@@ -30170,11 +30884,17 @@ for (let i = 0; i < languageArgs.length; i++) {
|
|
|
30170
30884
|
if (value === "ko" || value === "en") selectedLanguage = value;
|
|
30171
30885
|
}
|
|
30172
30886
|
configureLanguage(selectedLanguage ?? environmentLanguage(), selectedLanguage);
|
|
30173
|
-
var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.8.
|
|
30887
|
+
var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.8.3").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
|
|
30888
|
+
program2.hook("preAction", async (_program, action) => {
|
|
30889
|
+
let top = action;
|
|
30890
|
+
while (top.parent && top.parent !== program2) top = top.parent;
|
|
30891
|
+
if (top.name() !== REFRESH_COMMAND) await noticeVersion("0.8.3", top.name());
|
|
30892
|
+
});
|
|
30893
|
+
program2.command(REFRESH_COMMAND, { hidden: true }).allowExcessArguments(false).action(() => refreshUpdateCache("0.8.3"));
|
|
30174
30894
|
var format = () => new Option("--format <format>", t2("help.format")).choices(formats).default("text");
|
|
30175
|
-
program2.command("browser").description(t2("help.browser")).allowExcessArguments(false).option("--port <port>", t2("help.browserPort"), parsePort, 0).option("--dev", t2("help.browserDev")).action((options) => runBrowser(options, "0.8.
|
|
30176
|
-
program2.command("init").description(t2("help.init")).allowExcessArguments(false).option("--dry-run", t2("help.initDryRun")).addOption(new Option("--agent <agent>", t2("help.initAgent")).choices([...agentPresetNames])).addOption(new Option("--remove-agents", t2("help.initRemoveAgents")).conflicts("skipAgents")).option("--skip-agents", t2("help.initSkipAgents")).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).action((options) => runInit(options, "0.8.
|
|
30177
|
-
program2.command("update").description(t2("help.update")).allowExcessArguments(false).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).option("--commit", t2("help.updateCommit")).addOption(new Option("--check", t2("help.updateCheck")).conflicts("commit")).action((options) => options.check ? runUpdateCheck(options.format, "0.8.
|
|
30895
|
+
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.3"));
|
|
30896
|
+
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.3"));
|
|
30897
|
+
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")).addOption(new Option("--later", t2("help.updateLater")).conflicts(["check", "commit"])).action((options) => options.later ? runUpdateLater(options.format, "0.8.3") : options.check ? runUpdateCheck(options.format, "0.8.3") : runUpdate(options, "0.8.3"));
|
|
30178
30898
|
var listed2 = (command, sorts) => {
|
|
30179
30899
|
command.allowExcessArguments(false).option("--q <words>", t2("help.listQ")).option("--author <name>", t2("help.listAuthor"));
|
|
30180
30900
|
if (sorts) command.addOption(new Option("--sort <key>", t2("help.listSort")).choices([...sorts]).default(sorts[0]));
|
|
@@ -30196,7 +30916,7 @@ program2.command("check").description(t2("help.check")).allowExcessArguments(fal
|
|
|
30196
30916
|
var changes = program2.command("changes").description(t2("help.changes"));
|
|
30197
30917
|
changes.command("list").description(t2("help.changesList")).allowExcessArguments(false).addOption(format()).action((o) => runChangesList(o));
|
|
30198
30918
|
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));
|
|
30199
|
-
program2.command("feedback").description(t2("help.feedback")).allowExcessArguments(false).requiredOption("--file <path>", t2("help.feedbackFile")).option("--dry-run", t2("help.feedbackDryRun")).addOption(format()).action((o) => runFeedback(o, "0.8.
|
|
30919
|
+
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.3"));
|
|
30200
30920
|
var guide = program2.command("guide").description(t2("help.guide"));
|
|
30201
30921
|
guide.command("list").description(t2("help.guideList")).allowExcessArguments(false).addOption(format()).action((o) => runGuideList(o));
|
|
30202
30922
|
guide.command("show").description(t2("help.guideShow")).argument("<topic>", t2("help.guideTopic")).allowExcessArguments(false).addOption(format()).action((topic, o) => runGuideShow(topic, o));
|