gitifact 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/assets/{Banner-c1hFCLs8.js → Banner-DQ1G6Jkv.js} +1 -1
- package/dist/browser/assets/{BottomSheet-7C5eiOji.js → BottomSheet-BFoM6syr.js} +1 -1
- package/dist/browser/assets/Grid-DCCm_m5g.js +1 -0
- package/dist/browser/assets/{HoverCard-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-B9QJ2Ao-.js} +4 -4
- package/dist/browser/assets/{MenuBottomSheet-BOGxMa05.js → MenuBottomSheet-Dgz4h1S7.js} +1 -1
- package/dist/browser/assets/{MetadataListItem-C3SqHyk0.js → MetadataListItem-BEwPg27H.js} +1 -1
- package/dist/browser/assets/ProgressBarMarkTooltip-BxGSd9KS.js +1 -0
- package/dist/browser/assets/{Section-BEbhTpxd.js → Section-Z2PuhE6c.js} +1 -1
- package/dist/browser/assets/Selector-BlDYK5Fb.js +2 -0
- package/dist/browser/assets/{Tab-VVHagF2Z.js → Tab-BoCyIuQw.js} +1 -1
- package/dist/browser/assets/{Table-D4v8xCrw.js → Table-LdzcI4YS.js} +3 -3
- package/dist/browser/assets/{TimestampHoverCard-qfGbCkoP.js → TimestampHoverCard-B5JBaQDZ.js} +1 -1
- package/dist/browser/assets/{Token-BLd8-jfF.js → Token-CYBSUyAu.js} +1 -1
- package/dist/browser/assets/{Tooltip-QZg2pB5o.js → Tooltip-MoU3alsv.js} +1 -1
- package/dist/browser/assets/{about-DeUi-_2z.js → about-ClxCgIMf.js} +2 -2
- package/dist/browser/assets/{activity-timeline-bFoqFwVu.js → activity-timeline-i0lmNZ1o.js} +2 -2
- package/dist/browser/assets/{appearance-CDQ4fLdE.js → appearance-DgDbwXAm.js} +1 -1
- package/dist/browser/assets/changelog-bGtJx397.js +2 -0
- package/dist/browser/assets/commit-5TIiQrYh.js +4 -0
- package/dist/browser/assets/{container.stylex-6OsSOpKw.js → container.stylex-rwI38dw9.js} +1 -1
- package/dist/browser/assets/contributor-C1R6Q471.js +1 -0
- package/dist/browser/assets/contributors-DjNETDIr.js +1 -0
- package/dist/browser/assets/{contributors._email-BRfre3dw.js → contributors._email-DXxCGNZx.js} +1 -1
- package/dist/browser/assets/contributors.index-ByVvGuYC.js +1 -0
- package/dist/browser/assets/dashboard.index-RoAwRn4H.js +1 -0
- package/dist/browser/assets/document-BwoITy_-.js +2 -0
- package/dist/browser/assets/{document-JLY2-z8S.js → document-CwA2dxtP.js} +5 -5
- package/dist/browser/assets/features-CttwIYBf.js +2 -0
- package/dist/browser/assets/features-ZYCEDoiE.css +1 -0
- package/dist/browser/assets/{features._featureId-BgFHsRcy.js → features._featureId-DPctVYNz.js} +1 -1
- package/dist/browser/assets/features.index-lcNtlMn8.js +1 -0
- package/dist/browser/assets/getting-started-Chwezama.js +1 -0
- package/dist/browser/assets/git-CtsTwkaq.js +1 -0
- package/dist/browser/assets/index-iSt1IRzY.js +48 -0
- package/dist/browser/assets/instructions-DULmclHB.js +1 -0
- package/dist/browser/assets/instructions._instructionId-CBz40hpF.js +1 -0
- package/dist/browser/assets/instructions.agents-CDpXylSu.js +1 -0
- package/dist/browser/assets/instructions.index-BZ5tCMZJ.js +1 -0
- package/dist/browser/assets/{lazyRouteComponent-JkRa2CHo.js → lazyRouteComponent-DhTs_UEV.js} +1 -1
- package/dist/browser/assets/load-more-DxscRoh6.js +1 -0
- package/dist/browser/assets/{page-header-CPL7myjo.js → page-header-B9LQWl7J.js} +1 -1
- package/dist/browser/assets/records-page-B4tDip2H.css +1 -0
- package/dist/browser/assets/records-page-BjyFUmMr.js +1 -0
- package/dist/browser/assets/records._recordId-vwjWdPRX.js +1 -0
- package/dist/browser/assets/records.commits._commit-CYYgrOxv.js +1 -0
- package/dist/browser/assets/records.index-N49ep6ay.js +1 -0
- package/dist/browser/assets/records.working-CcYY8eyI.js +1 -0
- package/dist/browser/assets/request-state-BeiYxf95.js +1 -0
- package/dist/browser/assets/search-CSSuO-jR.js +1 -0
- package/dist/browser/assets/search-palette-BCEUlRVv.js +561 -0
- package/dist/browser/assets/search-palette-CDmYAo36.css +1 -0
- package/dist/browser/assets/{settings-BDVKHrS8.js → settings-I5Mn86wq.js} +1 -1
- package/dist/browser/assets/{themeProps-CfjLB1h4.js → themeProps-CkLe5zXY.js} +1 -1
- package/dist/browser/assets/{useClipboard-CTfBnLlg.js → useClipboard-Cqx6Upy7.js} +1 -1
- package/dist/browser/assets/useDevWarning-Dudfg3qF.js +1 -0
- package/dist/browser/assets/{useInfiniteQuery-Bn_R13Ih.js → useInfiniteQuery-tqfloUvn.js} +1 -1
- package/dist/browser/assets/{useIsomorphicLayoutEffect-DByj3svz.js → useIsomorphicLayoutEffect-BiEJxDg_.js} +1 -1
- package/dist/browser/assets/{useKeyboardHint-lSxUw5Qj.js → useKeyboardHint-HHpn4XUs.js} +1 -1
- package/dist/browser/assets/{useMediaQuery-CqJr5z44.js → useMediaQuery-twbHF0pO.js} +1 -1
- package/dist/browser/assets/{useScrollLock-CmZKJxOo.js → useScrollLock-D-Y-zHSV.js} +1 -1
- package/dist/browser/index.html +24 -24
- package/dist/i18n/en/changelog.md +10 -0
- package/dist/i18n/ko/changelog.md +10 -0
- package/dist/main.js +906 -447
- package/package.json +1 -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/changelog-Brd1Ninm.js +0 -2
- 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.index-CV84owW7.js +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/features-Cj41kcXf.js +0 -4
- package/dist/browser/assets/features-g3j2elTj.css +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/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/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/useDevWarning-gW8_bNZ_.js +0 -1
package/dist/main.js
CHANGED
|
@@ -10413,15 +10413,15 @@ function bucketFor(state2, inst) {
|
|
|
10413
10413
|
var handoff;
|
|
10414
10414
|
var open = [];
|
|
10415
10415
|
var memo = {
|
|
10416
|
-
alloc(_inst, payload,
|
|
10416
|
+
alloc(_inst, payload, empty2) {
|
|
10417
10417
|
const bucket = handoff;
|
|
10418
10418
|
if (!bucket)
|
|
10419
|
-
return
|
|
10419
|
+
return empty2;
|
|
10420
10420
|
handoff = void 0;
|
|
10421
|
-
const entry2 = { value:
|
|
10421
|
+
const entry2 = { value: empty2, issues: null };
|
|
10422
10422
|
bucket.set(payload.value, entry2);
|
|
10423
10423
|
open.push(entry2);
|
|
10424
|
-
return
|
|
10424
|
+
return empty2;
|
|
10425
10425
|
},
|
|
10426
10426
|
guard(inst) {
|
|
10427
10427
|
var _a3;
|
|
@@ -13092,8 +13092,8 @@ var error20 = () => {
|
|
|
13092
13092
|
case "invalid_union":
|
|
13093
13093
|
return "\u05E7\u05DC\u05D8 \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF";
|
|
13094
13094
|
case "invalid_element": {
|
|
13095
|
-
const
|
|
13096
|
-
return `\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF \u05D1${
|
|
13095
|
+
const place3 = withDefinite(issue2.origin ?? "array");
|
|
13096
|
+
return `\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF \u05D1${place3}`;
|
|
13097
13097
|
}
|
|
13098
13098
|
default:
|
|
13099
13099
|
return `\u05E7\u05DC\u05D8 \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF`;
|
|
@@ -25064,20 +25064,73 @@ var instruction = external_exports.strictObject({
|
|
|
25064
25064
|
var agents = external_exports.strictObject({ path: external_exports.string(), body: external_exports.string(), updatedAt: external_exports.string().nullable() });
|
|
25065
25065
|
var problem = external_exports.strictObject({ code: external_exports.string(), path: external_exports.string(), message: external_exports.string() });
|
|
25066
25066
|
var oid2 = external_exports.string().regex(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/);
|
|
25067
|
-
var
|
|
25068
|
-
|
|
25069
|
-
|
|
25067
|
+
var indexFeature = external_exports.strictObject({
|
|
25068
|
+
id: external_exports.string(),
|
|
25069
|
+
path: external_exports.string(),
|
|
25070
|
+
title: external_exports.string(),
|
|
25071
|
+
state,
|
|
25072
|
+
requirements: external_exports.array(external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string(), description: external_exports.string() })),
|
|
25073
|
+
designs: external_exports.array(external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string() }))
|
|
25074
|
+
});
|
|
25075
|
+
var indexInstruction = external_exports.strictObject({ id: external_exports.string(), name: external_exports.string(), path: external_exports.string(), title: external_exports.string() });
|
|
25076
|
+
var browserCheckoutV1 = external_exports.strictObject({
|
|
25077
|
+
contract: external_exports.literal("browser-checkout"),
|
|
25078
|
+
version: external_exports.literal(1),
|
|
25070
25079
|
sessionId: external_exports.string(),
|
|
25071
25080
|
head: oid2.nullable(),
|
|
25072
25081
|
observedAt: external_exports.string(),
|
|
25073
25082
|
working: external_exports.boolean(),
|
|
25074
25083
|
stamp: external_exports.string(),
|
|
25075
|
-
features: external_exports.array(feature),
|
|
25076
|
-
instructions: external_exports.array(instruction),
|
|
25077
|
-
agents: agents.nullable(),
|
|
25078
25084
|
problems: external_exports.array(problem),
|
|
25085
|
+
index: external_exports.strictObject({ features: external_exports.array(indexFeature), instructions: external_exports.array(indexInstruction), people: external_exports.array(external_exports.strictObject({ name: external_exports.string(), email: external_exports.string() })) })
|
|
25086
|
+
});
|
|
25087
|
+
var featureRow = external_exports.strictObject({
|
|
25088
|
+
id: external_exports.string(),
|
|
25089
|
+
path: external_exports.string(),
|
|
25090
|
+
title: external_exports.string(),
|
|
25091
|
+
description: external_exports.string(),
|
|
25092
|
+
state,
|
|
25093
|
+
designs: external_exports.number().int().nonnegative(),
|
|
25079
25094
|
contributors: external_exports.array(contributor),
|
|
25080
|
-
|
|
25095
|
+
updatedAt: external_exports.string().nullable(),
|
|
25096
|
+
requirementCount: external_exports.number().int().nonnegative(),
|
|
25097
|
+
// `acceptance`: the numbered items under the acceptance heading, null without one. `designed`: a design names it.
|
|
25098
|
+
requirements: external_exports.array(external_exports.strictObject({ id: external_exports.string(), title: external_exports.string(), description: external_exports.string(), state, acceptance: external_exports.number().int().positive().nullable(), designed: external_exports.boolean() })),
|
|
25099
|
+
hidden: external_exports.number().int().nonnegative()
|
|
25100
|
+
});
|
|
25101
|
+
var browserFeaturesV1 = external_exports.strictObject({
|
|
25102
|
+
contract: external_exports.literal("browser-features"),
|
|
25103
|
+
version: external_exports.literal(1),
|
|
25104
|
+
sessionId: external_exports.string(),
|
|
25105
|
+
total: external_exports.number().int().nonnegative(),
|
|
25106
|
+
all: external_exports.number().int().nonnegative(),
|
|
25107
|
+
requirements: external_exports.number().int().nonnegative(),
|
|
25108
|
+
mostRequirements: external_exports.number().int().nonnegative(),
|
|
25109
|
+
next: external_exports.string().nullable(),
|
|
25110
|
+
features: external_exports.array(featureRow)
|
|
25111
|
+
});
|
|
25112
|
+
var browserFeatureV1 = external_exports.strictObject({ contract: external_exports.literal("browser-feature"), version: external_exports.literal(1), sessionId: external_exports.string(), feature });
|
|
25113
|
+
var browserInstructionsV1 = external_exports.strictObject({
|
|
25114
|
+
contract: external_exports.literal("browser-instructions"),
|
|
25115
|
+
version: external_exports.literal(1),
|
|
25116
|
+
sessionId: external_exports.string(),
|
|
25117
|
+
instructions: external_exports.array(instruction),
|
|
25118
|
+
agents: agents.nullable()
|
|
25119
|
+
});
|
|
25120
|
+
var browserContributorsV1 = external_exports.strictObject({
|
|
25121
|
+
contract: external_exports.literal("browser-contributors"),
|
|
25122
|
+
version: external_exports.literal(1),
|
|
25123
|
+
sessionId: external_exports.string(),
|
|
25124
|
+
total: external_exports.number().int().nonnegative(),
|
|
25125
|
+
next: external_exports.string().nullable(),
|
|
25126
|
+
people: external_exports.array(external_exports.strictObject({ email: external_exports.string(), name: external_exports.string(), commits: external_exports.number().int().nonnegative(), latest: external_exports.string(), features: external_exports.number().int().nonnegative() }))
|
|
25127
|
+
});
|
|
25128
|
+
var browserContributorV1 = external_exports.strictObject({
|
|
25129
|
+
contract: external_exports.literal("browser-contributor"),
|
|
25130
|
+
version: external_exports.literal(1),
|
|
25131
|
+
sessionId: external_exports.string(),
|
|
25132
|
+
person: contributor,
|
|
25133
|
+
features: external_exports.array(external_exports.strictObject({ id: external_exports.string(), title: external_exports.string(), commits: external_exports.number().int().nonnegative(), requirements: external_exports.number().int().nonnegative() }))
|
|
25081
25134
|
});
|
|
25082
25135
|
var kind = external_exports.enum(["feature", "requirement", "design", "wiki", "instruction"]);
|
|
25083
25136
|
var reference = external_exports.strictObject({ id: external_exports.string(), title: external_exports.string(), specId: external_exports.string(), path: external_exports.string() }).nullable();
|
|
@@ -25122,11 +25175,13 @@ var browserHistoryV6 = external_exports.strictObject({
|
|
|
25122
25175
|
next: oid2.nullable(),
|
|
25123
25176
|
events: external_exports.array(event)
|
|
25124
25177
|
});
|
|
25125
|
-
var
|
|
25178
|
+
var browserHistorySummaryV5 = external_exports.strictObject({
|
|
25126
25179
|
contract: external_exports.literal("browser-history-summary"),
|
|
25127
|
-
version: external_exports.literal(
|
|
25180
|
+
version: external_exports.literal(5),
|
|
25128
25181
|
sessionId: external_exports.string(),
|
|
25129
25182
|
head: oid2,
|
|
25183
|
+
// The three who committed most, and how many others there are and how many commits they made between them.
|
|
25184
|
+
people: external_exports.strictObject({ total: external_exports.number().int().nonnegative(), top: external_exports.array(contributor), rest: external_exports.strictObject({ count: external_exports.number().int().nonnegative(), commits: external_exports.number().int().nonnegative() }) }),
|
|
25130
25185
|
total: external_exports.number().int().nonnegative(),
|
|
25131
25186
|
byType: external_exports.strictObject({ created: external_exports.number().int().nonnegative(), modified: external_exports.number().int().nonnegative(), moved: external_exports.number().int().nonnegative(), deleted: external_exports.number().int().nonnegative() }),
|
|
25132
25187
|
// One entry per commit within three weeks of the newest change, so the reader can count by its own calendar day.
|
|
@@ -25134,22 +25189,23 @@ var browserHistorySummaryV4 = external_exports.strictObject({
|
|
|
25134
25189
|
// The newest commits: a few of their changes each and how many there are in all.
|
|
25135
25190
|
recent: external_exports.array(external_exports.strictObject({ commit: oid2, count: external_exports.number().int().positive(), events: external_exports.array(event) }))
|
|
25136
25191
|
});
|
|
25137
|
-
var
|
|
25192
|
+
var searchKind = external_exports.enum(["feature", "requirement", "design", "instruction", "record", "commit"]);
|
|
25193
|
+
var searchHit = external_exports.strictObject({
|
|
25194
|
+
id: external_exports.string(),
|
|
25195
|
+
kind: searchKind,
|
|
25196
|
+
title: external_exports.string(),
|
|
25197
|
+
where: external_exports.string(),
|
|
25198
|
+
line: external_exports.string(),
|
|
25199
|
+
// What the hit opens: a feature (with the requirement or design tab), an instruction, a record, or a commit.
|
|
25200
|
+
featureId: external_exports.string().optional(),
|
|
25201
|
+
commit: oid2.optional()
|
|
25202
|
+
});
|
|
25203
|
+
var browserSearchV3 = external_exports.strictObject({
|
|
25138
25204
|
contract: external_exports.literal("browser-search"),
|
|
25139
|
-
version: external_exports.literal(
|
|
25205
|
+
version: external_exports.literal(3),
|
|
25140
25206
|
sessionId: external_exports.string(),
|
|
25141
25207
|
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
|
-
}))
|
|
25208
|
+
groups: external_exports.array(external_exports.strictObject({ group: external_exports.union([searchKind, external_exports.literal("recent")]), total: external_exports.number().int().nonnegative(), next: external_exports.string().nullable(), hits: external_exports.array(searchHit) }))
|
|
25153
25209
|
});
|
|
25154
25210
|
var headQuery = oid2;
|
|
25155
25211
|
var count = (max) => external_exports.string().regex(/^(0|[1-9]\d{0,6})$/).transform(Number).pipe(external_exports.number().int().min(0).max(max));
|
|
@@ -25164,7 +25220,26 @@ var browserHistoryQueryV4 = external_exports.strictObject({
|
|
|
25164
25220
|
q: external_exports.string().max(200).optional()
|
|
25165
25221
|
});
|
|
25166
25222
|
var browserHistorySummaryQueryV1 = external_exports.strictObject({ head: headQuery });
|
|
25167
|
-
var
|
|
25223
|
+
var featureId = external_exports.string().regex(/^S-[a-z2-7]{10}$/);
|
|
25224
|
+
var browserFeaturesQueryV1 = external_exports.strictObject({
|
|
25225
|
+
q: external_exports.string().max(200).optional(),
|
|
25226
|
+
design: external_exports.enum(["yes", "no"]).optional(),
|
|
25227
|
+
author: external_exports.string().min(1).max(320).optional(),
|
|
25228
|
+
sort: external_exports.enum(["title", "requirements", "updated"]).optional(),
|
|
25229
|
+
dir: external_exports.enum(["asc", "desc"]).optional(),
|
|
25230
|
+
after: featureId.optional(),
|
|
25231
|
+
limit: count(50).pipe(external_exports.number().min(1)).optional()
|
|
25232
|
+
});
|
|
25233
|
+
var browserFeatureQueryV1 = external_exports.strictObject({ id: featureId });
|
|
25234
|
+
var browserContributorsQueryV1 = external_exports.strictObject({ q: external_exports.string().max(200).optional(), after: external_exports.string().min(1).max(320).optional(), limit: count(50).pipe(external_exports.number().min(1)).optional() });
|
|
25235
|
+
var browserContributorQueryV1 = external_exports.strictObject({ email: external_exports.string().min(1).max(320) });
|
|
25236
|
+
var browserSearchQueryV2 = external_exports.strictObject({
|
|
25237
|
+
q: external_exports.string().max(200),
|
|
25238
|
+
head: headQuery.optional(),
|
|
25239
|
+
group: searchKind.optional(),
|
|
25240
|
+
after: external_exports.string().min(1).max(200).optional(),
|
|
25241
|
+
limit: count(50).pipe(external_exports.number().min(1)).optional()
|
|
25242
|
+
});
|
|
25168
25243
|
var browserInstructionFileQueryV1 = external_exports.strictObject({ id: external_exports.string().regex(/^I-[a-z2-7]{10}$/), path: external_exports.string().min(1).max(1e3) });
|
|
25169
25244
|
var changelogQueryV1 = external_exports.strictObject({ lang: external_exports.string().regex(/^[a-z]{2}(?:-[A-Z]{2})?$/).optional() });
|
|
25170
25245
|
var fileStatus = external_exports.enum(["added", "modified", "deleted", "renamed"]);
|
|
@@ -25176,12 +25251,13 @@ var commitFile = external_exports.strictObject({
|
|
|
25176
25251
|
additions: external_exports.number().int().nonnegative().nullable(),
|
|
25177
25252
|
deletions: external_exports.number().int().nonnegative().nullable()
|
|
25178
25253
|
});
|
|
25179
|
-
var
|
|
25254
|
+
var browserCommitFilesV2 = external_exports.strictObject({
|
|
25180
25255
|
contract: external_exports.literal("browser-commit-files"),
|
|
25181
|
-
version: external_exports.literal(
|
|
25256
|
+
version: external_exports.literal(2),
|
|
25182
25257
|
sessionId: external_exports.string(),
|
|
25183
25258
|
commit: oid2,
|
|
25184
25259
|
total: external_exports.number().int().nonnegative(),
|
|
25260
|
+
next: external_exports.string().nullable(),
|
|
25185
25261
|
files: external_exports.array(commitFile)
|
|
25186
25262
|
});
|
|
25187
25263
|
var browserCommitFileV1 = external_exports.strictObject({
|
|
@@ -25196,7 +25272,7 @@ var browserCommitFileV1 = external_exports.strictObject({
|
|
|
25196
25272
|
binary: external_exports.boolean(),
|
|
25197
25273
|
tooLarge: external_exports.boolean()
|
|
25198
25274
|
});
|
|
25199
|
-
var
|
|
25275
|
+
var browserCommitFilesQueryV2 = external_exports.strictObject({ commit: oid2, after: external_exports.string().min(1).max(4096).optional(), limit: count(100).pipe(external_exports.number().min(1)).optional() });
|
|
25200
25276
|
var browserCommitFileQueryV1 = external_exports.strictObject({ commit: oid2, path: external_exports.string().min(1).max(4096) });
|
|
25201
25277
|
var browserCommitV4 = external_exports.strictObject({
|
|
25202
25278
|
contract: external_exports.literal("browser-commit"),
|
|
@@ -25212,7 +25288,8 @@ var browserCommitV4 = external_exports.strictObject({
|
|
|
25212
25288
|
next: external_exports.string().nullable(),
|
|
25213
25289
|
changes: external_exports.array(event)
|
|
25214
25290
|
});
|
|
25215
|
-
var
|
|
25291
|
+
var recordId = external_exports.string().regex(/^(?:DR|H)-[A-Za-z0-9_-]{1,64}$/);
|
|
25292
|
+
var browserCommitQueryV3 = external_exports.strictObject({ commit: oid2, after: external_exports.string().min(1).max(200).optional(), limit: count(100).pipe(external_exports.number().min(1)).optional(), record: recordId.optional() });
|
|
25216
25293
|
var browserCommitChangeV1 = external_exports.strictObject({
|
|
25217
25294
|
contract: external_exports.literal("browser-commit-change"),
|
|
25218
25295
|
version: external_exports.literal(1),
|
|
@@ -25223,14 +25300,15 @@ var browserCommitChangeV1 = external_exports.strictObject({
|
|
|
25223
25300
|
after: snapshot
|
|
25224
25301
|
});
|
|
25225
25302
|
var browserCommitChangeQueryV1 = external_exports.strictObject({ commit: oid2, id: external_exports.string().min(1).max(100) });
|
|
25226
|
-
var
|
|
25227
|
-
var browserRecordV1 = external_exports.strictObject({
|
|
25303
|
+
var browserRecordV2 = external_exports.strictObject({
|
|
25228
25304
|
contract: external_exports.literal("browser-record"),
|
|
25229
|
-
version: external_exports.literal(
|
|
25305
|
+
version: external_exports.literal(2),
|
|
25230
25306
|
sessionId: external_exports.string(),
|
|
25231
25307
|
head: oid2,
|
|
25232
25308
|
id: recordId,
|
|
25233
|
-
commit: oid2
|
|
25309
|
+
commit: oid2,
|
|
25310
|
+
record: record2,
|
|
25311
|
+
others: external_exports.number().int().nonnegative()
|
|
25234
25312
|
});
|
|
25235
25313
|
var browserRecordQueryV1 = external_exports.strictObject({ head: oid2, id: recordId });
|
|
25236
25314
|
var browserInstructionFileV1 = external_exports.strictObject({
|
|
@@ -25505,6 +25583,8 @@ var messages_default3 = {
|
|
|
25505
25583
|
"server.sessionChanged": "\uC11C\uBC84 \uC138\uC158\uC774 \uBCC0\uACBD\uB410\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC5F0\uACB0\uD558\uC138\uC694.",
|
|
25506
25584
|
"server.badRange": "\uC870\uD68C \uBC94\uC704\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
25507
25585
|
"server.commitNotFound": "\uC774 \uC800\uC7A5\uC18C\uC5D0 \uADF8 \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25586
|
+
"server.featureNotFound": "\uCCB4\uD06C\uC544\uC6C3\uC5D0 \uADF8 \uAE30\uB2A5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25587
|
+
"server.contributorNotFound": "HEAD\uC5D0 \uADF8 \uC774\uBA54\uC77C\uB85C \uCEE4\uBC0B\uD55C \uC0AC\uB78C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25508
25588
|
"server.cursorNotFound": "\uC774\uC5B4 \uC77D\uC744 \uC704\uCE58\uB97C \uBAA9\uB85D\uC5D0\uC11C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uCC98\uC74C\uBD80\uD130 \uB2E4\uC2DC \uBD88\uB7EC\uC624\uC138\uC694.",
|
|
25509
25589
|
"server.workingChangeNotFound": "\uCEE4\uBC0B \uC804 \uBCC0\uACBD\uC5D0 \uADF8 \uBB38\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25510
25590
|
"server.recordNotFound": "\uC774 \uC774\uB825\uC5D0 \uADF8 \uACB0\uC815\uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
@@ -25571,7 +25651,6 @@ var messages_default3 = {
|
|
|
25571
25651
|
"agentBlock.duplicateStart": "GITIFACT \uC2DC\uC791 \uB9C8\uCEE4\uAC00 \uC5EC\uB7EC \uAC1C\uC785\uB2C8\uB2E4. \uC911\uBCF5 \uBE14\uB85D\uC744 \uC815\uB9AC\uD55C \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
|
|
25572
25652
|
"specReader.headUnreadable": "HEAD\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
25573
25653
|
"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
25654
|
"specReader.historyUnreadable": "Git \uC774\uB825 \uD615\uC2DD\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
25576
25655
|
"specReader.projectChanged": "\uC870\uD68C \uC911 \uD504\uB85C\uC81D\uD2B8\uAC00 \uBC14\uB00C\uC5C8\uC2B5\uB2C8\uB2E4. \uC0C8\uB85C\uACE0\uCE68\uD558\uC138\uC694.",
|
|
25577
25656
|
"browser.invalidPort": "\uD3EC\uD2B8\uB294 0\uBD80\uD130 65535 \uC0AC\uC774\uC758 \uC815\uC218\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
|
|
@@ -25809,6 +25888,8 @@ var messages_default4 = {
|
|
|
25809
25888
|
"server.sessionChanged": "The server session changed. Reconnect to continue.",
|
|
25810
25889
|
"server.badRange": "Check the requested range.",
|
|
25811
25890
|
"server.commitNotFound": "This repository has no such commit.",
|
|
25891
|
+
"server.featureNotFound": "The checkout has no such feature.",
|
|
25892
|
+
"server.contributorNotFound": "Nobody with this email committed to HEAD.",
|
|
25812
25893
|
"server.cursorNotFound": "The place to read on from is no longer in the list. Load it again from the start.",
|
|
25813
25894
|
"server.workingChangeNotFound": "No uncommitted change of that document.",
|
|
25814
25895
|
"server.recordNotFound": "This history has no such decision record.",
|
|
@@ -25875,7 +25956,6 @@ var messages_default4 = {
|
|
|
25875
25956
|
"agentBlock.duplicateStart": "Multiple GITIFACT start markers found. Remove duplicate blocks before retrying.",
|
|
25876
25957
|
"specReader.headUnreadable": "Could not read HEAD.",
|
|
25877
25958
|
"specReader.schemaRequired": "A project using the supported schema is required.",
|
|
25878
|
-
"specReader.authorUnreadable": "Could not read Git authors.",
|
|
25879
25959
|
"specReader.historyUnreadable": "Could not read Git history.",
|
|
25880
25960
|
"specReader.projectChanged": "The project changed while reading. Refresh to continue.",
|
|
25881
25961
|
"browser.invalidPort": "Port must be an integer between 0 and 65535.",
|
|
@@ -26414,7 +26494,7 @@ function storeReader(cwd) {
|
|
|
26414
26494
|
import { DatabaseSync } from "node:sqlite";
|
|
26415
26495
|
import { lstat, mkdir, rm, writeFile } from "node:fs/promises";
|
|
26416
26496
|
import { join as join2 } from "node:path";
|
|
26417
|
-
var CACHE_FORMAT =
|
|
26497
|
+
var CACHE_FORMAT = 11;
|
|
26418
26498
|
var CACHE_DIR = ".gitifact/cache";
|
|
26419
26499
|
var SCHEMA = `
|
|
26420
26500
|
-- Working-tree files already read: the modification time and size decide whether a file is read again.
|
|
@@ -26444,7 +26524,17 @@ var SCHEMA = `
|
|
|
26444
26524
|
CREATE TABLE lineage (head TEXT NOT NULL, pos INTEGER NOT NULL, oid TEXT NOT NULL, PRIMARY KEY (head, pos));
|
|
26445
26525
|
CREATE INDEX lineage_by_commit ON lineage (head, oid);
|
|
26446
26526
|
CREATE TABLE heads (head TEXT PRIMARY KEY, seen INTEGER NOT NULL);
|
|
26447
|
-
--
|
|
26527
|
+
-- Every commit the log has met, code commits too: who made it and when, and whether it is a format migration.
|
|
26528
|
+
-- 'reach' is 1 for the commits of the HEAD the log follows now; contributors are counted over those.
|
|
26529
|
+
CREATE TABLE log (oid TEXT PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL, time INTEGER NOT NULL, date TEXT NOT NULL,
|
|
26530
|
+
subject TEXT NOT NULL, migration INTEGER NOT NULL, reach INTEGER NOT NULL);
|
|
26531
|
+
CREATE INDEX log_by_reach ON log (reach, email);
|
|
26532
|
+
-- The store folders a commit touched: a feature folder, an instruction folder, or AGENTS.md.
|
|
26533
|
+
CREATE TABLE touches (oid TEXT NOT NULL, folder TEXT NOT NULL, PRIMARY KEY (oid, folder));
|
|
26534
|
+
CREATE INDEX touches_by_folder ON touches (folder);
|
|
26535
|
+
-- What the log follows: the HEAD ('head') and the fingerprint of the .mailmap its names were read with ('mailmap').
|
|
26536
|
+
CREATE TABLE state (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
26537
|
+
-- What the search box finds among the working tree's documents (scope 'checkout'); records are searched in their table.
|
|
26448
26538
|
-- The searched columns hold lower-cased text; trigram lets LIKE '%...%' use the index from three characters on.
|
|
26449
26539
|
CREATE VIRTUAL TABLE search USING fts5(
|
|
26450
26540
|
scope UNINDEXED, kind UNINDEXED, ref UNINDEXED, oid UNINDEXED, payload UNINDEXED,
|
|
@@ -26728,6 +26818,40 @@ function createDocumentCache(root, database) {
|
|
|
26728
26818
|
}));
|
|
26729
26819
|
});
|
|
26730
26820
|
},
|
|
26821
|
+
/**
|
|
26822
|
+
* The working documents of one kind whose title, place or text holds the query, a page at a time: a title that
|
|
26823
|
+
* starts with it first, then a title, a place, and the text; by title within each. `after` is the ID of the last
|
|
26824
|
+
* hit of the page before; one that no longer matches answers undefined. Each hit names its feature's ID.
|
|
26825
|
+
*/
|
|
26826
|
+
async searchKind(kind2, raw, after, limit) {
|
|
26827
|
+
const query = raw.trim().toLowerCase();
|
|
26828
|
+
await sync();
|
|
26829
|
+
return database.with((db) => {
|
|
26830
|
+
const literal2 = /[\\%_]/.test(query);
|
|
26831
|
+
const match = (column) => literal2 ? `instr(${column}, :q) > 0` : `${column} LIKE :like`;
|
|
26832
|
+
const ordered = `WITH hits AS (
|
|
26833
|
+
SELECT json_extract(payload, '$.id') AS id, payload, title, ref,
|
|
26834
|
+
CASE WHEN substr(title, 1, length(:q)) = :q THEN 0 WHEN instr(title, :q) > 0 THEN 1 WHEN instr(place, :q) > 0 THEN 2 ELSE 3 END AS rank
|
|
26835
|
+
FROM search WHERE scope = 'checkout' AND kind = :kind AND (${match("title")} OR ${match("place")} OR ${match("body")})),
|
|
26836
|
+
ordered AS (SELECT *, row_number() OVER (ORDER BY rank, title, ref) AS n FROM hits)`;
|
|
26837
|
+
const params = { q: query, kind: kind2, ...literal2 ? {} : { like: containing(query) } };
|
|
26838
|
+
const total = Number(db.prepare(`${ordered} SELECT count(*) AS n FROM ordered`).get(params).n);
|
|
26839
|
+
let start = 0;
|
|
26840
|
+
if (after !== void 0) {
|
|
26841
|
+
const at = db.prepare(`${ordered} SELECT n FROM ordered WHERE id = :after`).get({ ...params, after });
|
|
26842
|
+
if (!at) return void 0;
|
|
26843
|
+
start = Number(at.n);
|
|
26844
|
+
}
|
|
26845
|
+
const rows = db.prepare(`${ordered} SELECT payload FROM ordered WHERE n > :start ORDER BY n LIMIT :limit`).all({ ...params, start, limit: limit + 1 });
|
|
26846
|
+
const features = new Map(db.prepare("SELECT feature, id FROM documents WHERE kind = 'feature'").all().map((r) => [r.feature, r.id]));
|
|
26847
|
+
const hits = rows.slice(0, limit).map((r) => {
|
|
26848
|
+
const p = JSON.parse(r.payload);
|
|
26849
|
+
const featureId2 = p.feature ? features.get(p.feature) : void 0;
|
|
26850
|
+
return { id: p.id, kind: kind2, title: p.title, where: p.where, line: snippet(p.body, query), ...featureId2 ? { featureId: featureId2 } : {} };
|
|
26851
|
+
});
|
|
26852
|
+
return { total, next: rows.length > limit ? hits[hits.length - 1].id : null, hits };
|
|
26853
|
+
});
|
|
26854
|
+
},
|
|
26731
26855
|
/** Documents that refer to `id` from their frontmatter: designs naming a requirement or a source. */
|
|
26732
26856
|
async referencing(id) {
|
|
26733
26857
|
await sync();
|
|
@@ -27096,20 +27220,19 @@ var listed = (e) => {
|
|
|
27096
27220
|
return { ...event2, before: reference2(e.before), after: reference2(e.after) };
|
|
27097
27221
|
};
|
|
27098
27222
|
var sideOf3 = (snapshot5, rev) => !snapshot5 ? null : rev ? { rev, path: snapshot5.path } : snapshot5;
|
|
27099
|
-
var titleOf = (e) => (e.after ?? e.before)?.title ?? e.id;
|
|
27100
27223
|
var DAY = 864e5;
|
|
27101
27224
|
var KEPT_HEADS = 4;
|
|
27102
27225
|
var RECENT_COMMITS = 3;
|
|
27103
27226
|
var RECENT_CHANGES = 12;
|
|
27104
|
-
function createHistory(database, git) {
|
|
27227
|
+
function createHistory(database, git, originals) {
|
|
27105
27228
|
const changes2 = createCommitChanges(git);
|
|
27229
|
+
const texts = originals ?? changes2;
|
|
27106
27230
|
const building = /* @__PURE__ */ new Map();
|
|
27107
27231
|
const built = /* @__PURE__ */ new Set();
|
|
27108
27232
|
function insert(db, commits, readers) {
|
|
27109
27233
|
const commit = db.prepare("INSERT OR IGNORE INTO commits (oid, reader) VALUES (?, ?)");
|
|
27110
27234
|
const change2 = db.prepare("INSERT OR IGNORE INTO changes (key, oid, ord, id, kind, types, email, date, before_spec, after_spec, needle, row, detail) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)");
|
|
27111
27235
|
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
27236
|
transaction(db, () => {
|
|
27114
27237
|
for (const c of commits) {
|
|
27115
27238
|
if (!Number(commit.run(c.commit, readers.get(c.commit) ?? "current").changes)) continue;
|
|
@@ -27132,23 +27255,19 @@ function createHistory(database, git) {
|
|
|
27132
27255
|
JSON.stringify(row),
|
|
27133
27256
|
JSON.stringify(detail)
|
|
27134
27257
|
);
|
|
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
27258
|
});
|
|
27148
27259
|
}
|
|
27149
27260
|
});
|
|
27150
27261
|
}
|
|
27151
27262
|
async function build(head) {
|
|
27263
|
+
const known = await database.with((db) => {
|
|
27264
|
+
const seen = !!db.prepare("SELECT 1 FROM heads WHERE head = ?").get(head);
|
|
27265
|
+
if (seen) db.prepare("UPDATE heads SET seen = ? WHERE head = ?").run(Date.now(), head);
|
|
27266
|
+
return seen;
|
|
27267
|
+
});
|
|
27268
|
+
if (!known) await read(head);
|
|
27269
|
+
}
|
|
27270
|
+
async function read(head) {
|
|
27152
27271
|
const lineage = await changes2.lineage(head);
|
|
27153
27272
|
const readers = await changes2.readers(head, lineage);
|
|
27154
27273
|
const known = await database.with((db) => new Map(db.prepare("SELECT oid, reader FROM commits").all().map((r) => [r.oid, r.reader])));
|
|
@@ -27158,13 +27277,14 @@ function createHistory(database, git) {
|
|
|
27158
27277
|
db.prepare("DELETE FROM commits WHERE oid = ?").run(oid3);
|
|
27159
27278
|
db.prepare("DELETE FROM changes WHERE oid = ?").run(oid3);
|
|
27160
27279
|
db.prepare("DELETE FROM records WHERE oid = ?").run(oid3);
|
|
27161
|
-
db.prepare("DELETE FROM search WHERE scope = 'history' AND oid = ?").run(oid3);
|
|
27162
27280
|
}
|
|
27281
|
+
db.prepare("DELETE FROM lineage WHERE head != ?").run(head);
|
|
27282
|
+
db.prepare("DELETE FROM heads WHERE head != ?").run(head);
|
|
27163
27283
|
}));
|
|
27164
27284
|
const missing2 = lineage.filter((oid3) => !known.has(oid3) || stale.includes(oid3));
|
|
27165
27285
|
for (let i = 0; i < missing2.length; i += 100) {
|
|
27166
|
-
const
|
|
27167
|
-
await database.with((db) => insert(db,
|
|
27286
|
+
const read2 = await changes2.of(missing2.slice(i, i + 100), readers);
|
|
27287
|
+
await database.with((db) => insert(db, read2, readers));
|
|
27168
27288
|
}
|
|
27169
27289
|
await database.with((db) => transaction(db, () => {
|
|
27170
27290
|
db.prepare("DELETE FROM lineage WHERE head = ?").run(head);
|
|
@@ -27191,15 +27311,21 @@ function createHistory(database, git) {
|
|
|
27191
27311
|
async function readCommit(commit) {
|
|
27192
27312
|
const known = await database.with((db) => !!db.prepare("SELECT 1 FROM commits WHERE oid = ?").get(commit));
|
|
27193
27313
|
if (known) return;
|
|
27194
|
-
const
|
|
27195
|
-
if (
|
|
27314
|
+
const read2 = await changes2.of([commit]).catch(() => void 0);
|
|
27315
|
+
if (read2) await database.with((db) => insert(db, read2, /* @__PURE__ */ new Map()));
|
|
27316
|
+
}
|
|
27317
|
+
function sameRecord(db, commit, id) {
|
|
27318
|
+
if (!id.startsWith("H-")) return [id];
|
|
27319
|
+
const own2 = db.prepare("SELECT title, sections FROM records WHERE oid = ? AND id = ?").get(commit, id);
|
|
27320
|
+
if (!own2) return [id];
|
|
27321
|
+
return db.prepare("SELECT id FROM records WHERE oid = ? AND id LIKE 'H-%' AND title = ? AND sections = ?").all(commit, own2.title, own2.sections).map((r) => r.id);
|
|
27196
27322
|
}
|
|
27197
27323
|
function hydrate(db, rows) {
|
|
27198
27324
|
const events = rows.map((row) => JSON.parse(row));
|
|
27199
|
-
const
|
|
27325
|
+
const read2 = db.prepare("SELECT id, title, sections FROM records WHERE oid = ?");
|
|
27200
27326
|
const byCommit = /* @__PURE__ */ new Map();
|
|
27201
27327
|
for (const e of events) if (e.records.length && !byCommit.has(e.commit)) {
|
|
27202
|
-
byCommit.set(e.commit, new Map(
|
|
27328
|
+
byCommit.set(e.commit, new Map(read2.all(e.commit).map((r) => [r.id, { id: r.id, title: r.title, sections: JSON.parse(r.sections) }])));
|
|
27203
27329
|
}
|
|
27204
27330
|
return events.map((e) => ({ ...e, records: e.records.flatMap((id) => {
|
|
27205
27331
|
const r = byCommit.get(e.commit)?.get(id);
|
|
@@ -27216,8 +27342,8 @@ function createHistory(database, git) {
|
|
|
27216
27342
|
if (value && "rev" in value) places.push({ rev: value.rev, path: value.path, specId: events[i][key]?.specId ?? "" });
|
|
27217
27343
|
}
|
|
27218
27344
|
});
|
|
27219
|
-
const
|
|
27220
|
-
const text4 = new Map(places.map((p, i) => [p.rev + ":" + p.path,
|
|
27345
|
+
const read2 = places.length ? await texts.sides(places) : [];
|
|
27346
|
+
const text4 = new Map(places.map((p, i) => [p.rev + ":" + p.path, read2[i] ?? null]));
|
|
27221
27347
|
const resolve = (value) => !value ? null : "rev" in value ? text4.get(value.rev + ":" + value.path) ?? null : value;
|
|
27222
27348
|
return events.map((event2, i) => ({ event: event2, before: resolve(sides[i].before), after: resolve(sides[i].after) }));
|
|
27223
27349
|
}
|
|
@@ -27249,7 +27375,7 @@ function createHistory(database, git) {
|
|
|
27249
27375
|
return {
|
|
27250
27376
|
ensure,
|
|
27251
27377
|
/** The document and reason files of one commit, read from Git and not kept. */
|
|
27252
|
-
filesAt: (rev) =>
|
|
27378
|
+
filesAt: (rev) => texts.tree(rev),
|
|
27253
27379
|
/**
|
|
27254
27380
|
* The changes that match, newest first, a page of whole commits: `limit` commits after the commit `after`, each
|
|
27255
27381
|
* with all of its matching changes, and how many changes and commits match in all. `next` is the last commit of the
|
|
@@ -27278,11 +27404,15 @@ function createHistory(database, git) {
|
|
|
27278
27404
|
};
|
|
27279
27405
|
});
|
|
27280
27406
|
},
|
|
27281
|
-
/**
|
|
27282
|
-
|
|
27407
|
+
/**
|
|
27408
|
+
* The changes of one commit without their text, a page at a time after the change `after` (a key): the commit page's
|
|
27409
|
+
* list. With `record`, only the changes that record explains: the record page's documents.
|
|
27410
|
+
*/
|
|
27411
|
+
async commitChanges(commit, after, limit, record4) {
|
|
27283
27412
|
await readCommit(commit);
|
|
27284
27413
|
return database.with((db) => {
|
|
27285
|
-
const
|
|
27414
|
+
const ids = record4 === void 0 ? void 0 : JSON.stringify(sameRecord(db, commit, record4));
|
|
27415
|
+
const all = ids === void 0 ? db.prepare("SELECT key, row FROM changes WHERE oid = ? ORDER BY ord").all(commit) : db.prepare("SELECT key, row FROM changes c WHERE oid = ? AND EXISTS (SELECT 1 FROM json_each(c.row, '$.records') j WHERE j.value IN (SELECT value FROM json_each(?))) ORDER BY ord").all(commit, ids);
|
|
27286
27416
|
const start = after === void 0 ? 0 : all.findIndex((r) => r.key === after) + 1;
|
|
27287
27417
|
if (after !== void 0 && !start) return void 0;
|
|
27288
27418
|
const shown = all.slice(start, start + limit);
|
|
@@ -27372,86 +27502,226 @@ function createHistory(database, git) {
|
|
|
27372
27502
|
const commits = db.prepare(`SELECT l.oid AS oid, max(c.date) AS date, count(*) AS n ${from} GROUP BY l.pos, l.oid ORDER BY l.pos`).all(head);
|
|
27373
27503
|
const newest = Math.max(0, ...commits.map((c) => Date.parse(c.date)).filter(Number.isFinite));
|
|
27374
27504
|
const pulse = commits.filter((c) => Date.parse(c.date) >= newest - 22 * DAY).map((c) => ({ date: c.date, count: Number(c.n) }));
|
|
27375
|
-
const
|
|
27505
|
+
const recent2 = commits.slice(0, RECENT_COMMITS).map((c) => ({
|
|
27376
27506
|
commit: c.oid,
|
|
27377
27507
|
count: Number(c.n),
|
|
27378
27508
|
events: hydrate(db, db.prepare("SELECT row FROM changes WHERE oid = ? ORDER BY ord LIMIT ?").all(c.oid, RECENT_CHANGES).map((r) => r.row))
|
|
27379
27509
|
}));
|
|
27380
|
-
return { total, byType: { created: count2("created"), modified: count2("modified"), moved: count2("moved"), deleted: count2("deleted") }, pulse, recent };
|
|
27510
|
+
return { total, byType: { created: count2("created"), modified: count2("modified"), moved: count2("moved"), deleted: count2("deleted") }, pulse, recent: recent2 };
|
|
27381
27511
|
});
|
|
27382
27512
|
},
|
|
27383
|
-
/**
|
|
27384
|
-
|
|
27513
|
+
/**
|
|
27514
|
+
* The record of `head`'s history with the commit that added it, and how many other records that commit holds (a
|
|
27515
|
+
* 0.7 reason written once over several documents counts once). Undefined when the history has no such record.
|
|
27516
|
+
*/
|
|
27517
|
+
async recordOf(head, id) {
|
|
27385
27518
|
await ensure(head);
|
|
27386
|
-
return database.with((db) =>
|
|
27519
|
+
return database.with((db) => {
|
|
27520
|
+
const found = db.prepare("SELECT r.oid AS oid, r.title AS title, r.sections AS sections FROM lineage l JOIN records r ON r.oid = l.oid WHERE l.head = ? AND r.id = ? ORDER BY l.pos LIMIT 1").get(head, id);
|
|
27521
|
+
if (!found) return void 0;
|
|
27522
|
+
const all = db.prepare("SELECT id, title, sections FROM records WHERE oid = ?").all(found.oid);
|
|
27523
|
+
const groups = new Set(all.map((r) => r.id.startsWith("H-") ? "H\0" + r.title + "\0" + r.sections : r.id));
|
|
27524
|
+
return { commit: found.oid, record: { id, title: found.title, sections: JSON.parse(found.sections) }, others: groups.size - 1 };
|
|
27525
|
+
});
|
|
27387
27526
|
},
|
|
27388
27527
|
/**
|
|
27389
|
-
*
|
|
27390
|
-
*
|
|
27528
|
+
* The records of `head`'s history whose title, ID or sections hold the query, one hit per record file, a page at a
|
|
27529
|
+
* time: a title that starts with it first, then a title, the ID, and the sections; newest first within each. `after`
|
|
27530
|
+
* is the ID of the last hit of the page before; one that no longer matches answers undefined.
|
|
27391
27531
|
*/
|
|
27392
|
-
async
|
|
27532
|
+
async searchRecords(head, raw, after, limit) {
|
|
27393
27533
|
const query = raw.trim().toLowerCase();
|
|
27394
|
-
|
|
27395
|
-
if (head) await ensure(head);
|
|
27534
|
+
await ensure(head);
|
|
27396
27535
|
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 };
|
|
27536
|
+
const ordered = `WITH own AS (
|
|
27537
|
+
SELECT r.id AS id, r.title AS title, r.oid AS oid, min(l.pos) AS pos, r.sections AS sections,
|
|
27538
|
+
lower((SELECT group_concat(json_extract(s.value, '$.body'), ' ') FROM json_each(r.sections) s)) AS body
|
|
27539
|
+
FROM lineage l JOIN records r ON r.oid = l.oid WHERE l.head = :head GROUP BY r.id),
|
|
27540
|
+
hits AS (SELECT *, CASE WHEN substr(lower(title), 1, length(:q)) = :q THEN 0 WHEN instr(lower(title), :q) > 0 THEN 1
|
|
27541
|
+
WHEN instr(lower(id), :q) > 0 THEN 2 ELSE 3 END AS rank
|
|
27542
|
+
FROM own WHERE instr(lower(title), :q) > 0 OR instr(lower(id), :q) > 0 OR instr(body, :q) > 0),
|
|
27543
|
+
ordered AS (SELECT *, row_number() OVER (ORDER BY rank, pos, id) AS n FROM hits)`;
|
|
27544
|
+
const params = { head, q: query };
|
|
27545
|
+
const total = Number(db.prepare(`${ordered} SELECT count(*) AS n FROM ordered`).get(params).n);
|
|
27546
|
+
let start = 0;
|
|
27547
|
+
if (after !== void 0) {
|
|
27548
|
+
const at = db.prepare(`${ordered} SELECT n FROM ordered WHERE id = :after`).get({ ...params, after });
|
|
27549
|
+
if (!at) return void 0;
|
|
27550
|
+
start = Number(at.n);
|
|
27551
|
+
}
|
|
27552
|
+
const rows = db.prepare(`${ordered} SELECT id, title, oid, sections FROM ordered WHERE n > :start ORDER BY n LIMIT :limit`).all({ ...params, start, limit: limit + 1 });
|
|
27553
|
+
const hits = rows.slice(0, limit).map((r) => {
|
|
27554
|
+
const text4 = JSON.parse(r.sections).map((s) => s.body).join(" ");
|
|
27555
|
+
return { id: r.id, kind: "record", title: r.title, where: r.id + " \xB7 " + r.oid.slice(0, 7), line: snippet(text4, query), commit: r.oid };
|
|
27421
27556
|
});
|
|
27422
|
-
return [
|
|
27557
|
+
return { total, next: rows.length > limit ? hits[hits.length - 1].id : null, hits };
|
|
27558
|
+
});
|
|
27559
|
+
}
|
|
27560
|
+
};
|
|
27561
|
+
}
|
|
27562
|
+
|
|
27563
|
+
// src/adapters/cache/commit-log.ts
|
|
27564
|
+
import { lstat as lstat3 } from "node:fs/promises";
|
|
27565
|
+
import { join as join4 } from "node:path";
|
|
27566
|
+
var FORMAT2 = `%x1e%H%x00%aN%x00%aE%x00%at%x00%aI%x00%s%x00%(trailers:key=${MIGRATION_TRAILER},valueonly,separator=%x2C)`;
|
|
27567
|
+
var CHUNK = 1e5;
|
|
27568
|
+
var folderOf2 = (path3) => path3 === "AGENTS.md" ? path3 : new RegExp(`^(${SPEC_ROOT.replace(/\./g, "\\.")}/[^/]+|${INSTRUCTIONS_ROOT.replace(/\./g, "\\.")}/[^/]+)/`).exec(path3)?.[1];
|
|
27569
|
+
function createCommitLog(database, git, root) {
|
|
27570
|
+
let following;
|
|
27571
|
+
let followingHead;
|
|
27572
|
+
const read = async (args) => git.decode(await git.run(args));
|
|
27573
|
+
async function commits(range) {
|
|
27574
|
+
const out = [];
|
|
27575
|
+
for (let skip = 0; ; skip += CHUNK) {
|
|
27576
|
+
const text4 = await read(["log", `--format=${FORMAT2}`, `--max-count=${CHUNK}`, `--skip=${skip}`, ...range, "--"]);
|
|
27577
|
+
const chunk = text4.split("").filter(Boolean).map((entry2) => {
|
|
27578
|
+
const [oid3, name2, email3, time3, date5, subject, migration] = entry2.split("\0");
|
|
27579
|
+
return { oid: oid3, name: name2 ?? "", email: email3 ?? "", time: Number(time3), date: date5 ?? "", subject: subject ?? "", migration: !!migration?.trim() };
|
|
27580
|
+
});
|
|
27581
|
+
out.push(...chunk);
|
|
27582
|
+
if (chunk.length < CHUNK) return out;
|
|
27583
|
+
}
|
|
27584
|
+
}
|
|
27585
|
+
async function touches(range) {
|
|
27586
|
+
const text4 = await read(["log", "--format=%x1e%H", "-z", "--name-only", "--no-renames", ...range, "--", SPEC_ROOT, INSTRUCTIONS_ROOT, "AGENTS.md"]);
|
|
27587
|
+
const out = [];
|
|
27588
|
+
for (const entry2 of text4.split("").filter(Boolean)) {
|
|
27589
|
+
const [oid3, ...paths2] = entry2.split("\0");
|
|
27590
|
+
const folders = new Set(paths2.map((p) => folderOf2(p.replace(/^\n/, ""))).filter((f) => !!f));
|
|
27591
|
+
for (const folder of folders) out.push([oid3.trim(), folder]);
|
|
27592
|
+
}
|
|
27593
|
+
return out;
|
|
27594
|
+
}
|
|
27595
|
+
async function mailmap() {
|
|
27596
|
+
const info5 = await lstat3(join4(root, ".mailmap")).catch(() => void 0);
|
|
27597
|
+
return info5 ? info5.mtimeMs + ":" + info5.size : "-";
|
|
27598
|
+
}
|
|
27599
|
+
const stateOf = (db, key) => db.prepare("SELECT value FROM state WHERE key = ?").get(key)?.value;
|
|
27600
|
+
async function follow(head, attempts = 3) {
|
|
27601
|
+
const names = await mailmap();
|
|
27602
|
+
const [before, named] = await database.with((db) => [stateOf(db, "head"), stateOf(db, "mailmap")]);
|
|
27603
|
+
if (before === head && named === names) return;
|
|
27604
|
+
let change2;
|
|
27605
|
+
if (before && named === names) {
|
|
27606
|
+
try {
|
|
27607
|
+
const range = [head, "^" + before];
|
|
27608
|
+
const [added2, removed2, touched2] = await Promise.all([commits(range), read(["rev-list", before, "^" + head, "--"]), touches(range)]);
|
|
27609
|
+
change2 = { added: added2, removed: removed2.split("\n").filter(Boolean), touched: touched2, whole: false };
|
|
27610
|
+
} catch {
|
|
27611
|
+
change2 = void 0;
|
|
27612
|
+
}
|
|
27613
|
+
}
|
|
27614
|
+
if (!change2) {
|
|
27615
|
+
const [added2, touched2] = await Promise.all([commits([head]), touches([head])]);
|
|
27616
|
+
change2 = { added: added2, removed: [], touched: touched2, whole: true };
|
|
27617
|
+
}
|
|
27618
|
+
const { added, removed, touched, whole } = change2;
|
|
27619
|
+
const written = await database.with((db) => transaction(db, () => {
|
|
27620
|
+
if (stateOf(db, "head") !== before || stateOf(db, "mailmap") !== named) return false;
|
|
27621
|
+
if (whole) {
|
|
27622
|
+
db.exec("DELETE FROM log");
|
|
27623
|
+
db.exec("DELETE FROM touches");
|
|
27624
|
+
}
|
|
27625
|
+
const put = db.prepare(`INSERT INTO log (oid, name, email, time, date, subject, migration, reach) VALUES (?, ?, ?, ?, ?, ?, ?, 1)
|
|
27626
|
+
ON CONFLICT (oid) DO UPDATE SET name = excluded.name, email = excluded.email, reach = 1`);
|
|
27627
|
+
for (const c of added) put.run(c.oid, c.name, c.email, c.time, c.date, c.subject, c.migration ? 1 : 0);
|
|
27628
|
+
const leave = db.prepare("UPDATE log SET reach = 0 WHERE oid = ?");
|
|
27629
|
+
for (const oid3 of removed) leave.run(oid3);
|
|
27630
|
+
const touch = db.prepare("INSERT OR IGNORE INTO touches (oid, folder) VALUES (?, ?)");
|
|
27631
|
+
for (const [oid3, folder] of touched) touch.run(oid3, folder);
|
|
27632
|
+
const set2 = db.prepare("INSERT INTO state (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value = excluded.value");
|
|
27633
|
+
set2.run("head", head);
|
|
27634
|
+
set2.run("mailmap", names);
|
|
27635
|
+
return true;
|
|
27636
|
+
}));
|
|
27637
|
+
if (!written && attempts > 1) return follow(head, attempts - 1);
|
|
27638
|
+
}
|
|
27639
|
+
async function ensure(head) {
|
|
27640
|
+
while (following && followingHead !== head) await following.catch(() => void 0);
|
|
27641
|
+
if (!following) {
|
|
27642
|
+
followingHead = head;
|
|
27643
|
+
following = follow(head).finally(() => {
|
|
27644
|
+
following = void 0;
|
|
27645
|
+
followingHead = void 0;
|
|
27646
|
+
});
|
|
27647
|
+
}
|
|
27648
|
+
return following;
|
|
27649
|
+
}
|
|
27650
|
+
return {
|
|
27651
|
+
ensure,
|
|
27652
|
+
/** Everyone who committed to `head`, most commits first, format migrations left out. `latest` is their newest commit. */
|
|
27653
|
+
async contributors(head) {
|
|
27654
|
+
await ensure(head);
|
|
27655
|
+
return database.with((db) => db.prepare(`SELECT email, name, count(*) AS commits, max(time) AS time, date AS latest FROM log
|
|
27656
|
+
WHERE reach = 1 AND migration = 0 GROUP BY email ORDER BY commits DESC, name, email`).all().map(({ name: name2, email: email3, commits: commits2, latest }) => ({ name: name2, email: email3, commits: Number(commits2), latest })));
|
|
27657
|
+
},
|
|
27658
|
+
/**
|
|
27659
|
+
* The commits of `head` a hash names: the whole hash, or its start from seven characters on, as `git log --oneline`
|
|
27660
|
+
* prints it. Anything shorter or not a hash names none. Newest first, a page after the commit `after`.
|
|
27661
|
+
*/
|
|
27662
|
+
async commitsNamed(head, raw, after, limit) {
|
|
27663
|
+
const prefix = raw.trim().toLowerCase();
|
|
27664
|
+
if (!/^[0-9a-f]{7,64}$/.test(prefix)) return { total: 0, next: null, hits: [] };
|
|
27665
|
+
await ensure(head);
|
|
27666
|
+
return database.with((db) => {
|
|
27667
|
+
const rows = db.prepare("SELECT oid, name, date, subject FROM log WHERE reach = 1 AND substr(oid, 1, length(?)) = ? ORDER BY time DESC, oid").all(prefix, prefix);
|
|
27668
|
+
const start = after === void 0 ? 0 : rows.findIndex((r) => r.oid === after) + 1;
|
|
27669
|
+
if (after !== void 0 && !start) return void 0;
|
|
27670
|
+
const shown = rows.slice(start, start + limit);
|
|
27671
|
+
return {
|
|
27672
|
+
total: rows.length,
|
|
27673
|
+
next: start + shown.length < rows.length ? shown[shown.length - 1].oid : null,
|
|
27674
|
+
hits: shown.map((r) => ({ id: r.oid, kind: "commit", title: r.subject, where: r.oid.slice(0, 7) + " \xB7 " + r.name, line: r.date.slice(0, 10), commit: r.oid }))
|
|
27675
|
+
};
|
|
27676
|
+
});
|
|
27677
|
+
},
|
|
27678
|
+
/** Each store folder's authors, most commits first, and when it was last touched, format migrations left out. */
|
|
27679
|
+
async folders(head) {
|
|
27680
|
+
await ensure(head);
|
|
27681
|
+
return database.with((db) => {
|
|
27682
|
+
const rows = db.prepare(`SELECT t.folder AS folder, l.email AS email, l.name AS name, count(*) AS commits, max(l.time) AS time, l.date AS latest
|
|
27683
|
+
FROM touches t JOIN log l ON l.oid = t.oid WHERE l.reach = 1 AND l.migration = 0
|
|
27684
|
+
GROUP BY t.folder, l.email ORDER BY t.folder, commits DESC, name, email`).all();
|
|
27685
|
+
const out = /* @__PURE__ */ new Map();
|
|
27686
|
+
for (const r of rows) {
|
|
27687
|
+
let entry2 = out.get(r.folder);
|
|
27688
|
+
if (!entry2) out.set(r.folder, entry2 = { people: [], latest: r.latest, time: r.time });
|
|
27689
|
+
entry2.people.push({ name: r.name, email: r.email, commits: Number(r.commits), latest: r.latest });
|
|
27690
|
+
if (r.time > entry2.time) {
|
|
27691
|
+
entry2.time = r.time;
|
|
27692
|
+
entry2.latest = r.latest;
|
|
27693
|
+
}
|
|
27694
|
+
}
|
|
27695
|
+
return new Map([...out].map(([folder, { people, latest }]) => [folder, { people, latest }]));
|
|
27423
27696
|
});
|
|
27424
27697
|
}
|
|
27425
27698
|
};
|
|
27426
27699
|
}
|
|
27427
27700
|
|
|
27428
27701
|
// src/adapters/cache/index.ts
|
|
27429
|
-
function openCache(root, git) {
|
|
27702
|
+
function openCache(root, git, originals) {
|
|
27430
27703
|
const database = createCacheDatabase(root);
|
|
27431
27704
|
const documents = createDocumentCache(root, database);
|
|
27432
|
-
const history = createHistory(database, git);
|
|
27705
|
+
const history = createHistory(database, git, originals);
|
|
27706
|
+
const log = createCommitLog(database, git, root);
|
|
27433
27707
|
return {
|
|
27434
27708
|
documents,
|
|
27435
27709
|
history,
|
|
27436
|
-
|
|
27437
|
-
|
|
27438
|
-
async search(head, query) {
|
|
27439
|
-
await documents.sync();
|
|
27440
|
-
return history.search(head, query);
|
|
27441
|
-
}
|
|
27710
|
+
log,
|
|
27711
|
+
location: database.location
|
|
27442
27712
|
};
|
|
27443
27713
|
}
|
|
27444
27714
|
|
|
27445
27715
|
// src/adapters/git/pending-records.ts
|
|
27446
|
-
import { lstat as
|
|
27447
|
-
import { join as
|
|
27716
|
+
import { lstat as lstat4, readdir as readdir3, readFile as readFile3 } from "node:fs/promises";
|
|
27717
|
+
import { join as join5 } from "node:path";
|
|
27448
27718
|
var decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
27449
|
-
var info = (path3) =>
|
|
27719
|
+
var info = (path3) => lstat4(path3).catch((error62) => {
|
|
27450
27720
|
if (error62.code === "ENOENT") return void 0;
|
|
27451
27721
|
throw error62;
|
|
27452
27722
|
});
|
|
27453
27723
|
async function recordFolders(root) {
|
|
27454
|
-
return (await readdir3(
|
|
27724
|
+
return (await readdir3(join5(root, ...RECORDS_ROOT.split("/"))).catch((error62) => {
|
|
27455
27725
|
if (error62.code === "ENOENT") return [];
|
|
27456
27726
|
throw error62;
|
|
27457
27727
|
})).filter(isRecordFolder);
|
|
@@ -27459,7 +27729,7 @@ async function recordFolders(root) {
|
|
|
27459
27729
|
async function findRecord(root, id) {
|
|
27460
27730
|
for (const folder of await recordFolders(root)) {
|
|
27461
27731
|
const path3 = `${RECORDS_ROOT}/${folder}/${id}.md`;
|
|
27462
|
-
if ((await info(
|
|
27732
|
+
if ((await info(join5(root, ...path3.split("/"))))?.isFile()) return path3;
|
|
27463
27733
|
}
|
|
27464
27734
|
return void 0;
|
|
27465
27735
|
}
|
|
@@ -27475,7 +27745,7 @@ async function readPendingRecords(root, run) {
|
|
|
27475
27745
|
altered.push(path3);
|
|
27476
27746
|
continue;
|
|
27477
27747
|
}
|
|
27478
|
-
const file2 =
|
|
27748
|
+
const file2 = join5(root, ...path3.split("/"));
|
|
27479
27749
|
const stat3 = await info(file2);
|
|
27480
27750
|
if (!stat3?.isFile() || stat3.isSymbolicLink()) {
|
|
27481
27751
|
problems.push(docProblem("PATH_UNSUPPORTED", path3));
|
|
@@ -27497,30 +27767,30 @@ async function readPendingRecords(root, run) {
|
|
|
27497
27767
|
if (!id) continue;
|
|
27498
27768
|
for (const folder of folders) {
|
|
27499
27769
|
const other = `${RECORDS_ROOT}/${folder}/${id}.md`;
|
|
27500
|
-
if (other !== path3 && !files.has(other) && (await info(
|
|
27770
|
+
if (other !== path3 && !files.has(other) && (await info(join5(root, ...other.split("/"))))?.isFile()) problems.push(docProblem("DUPLICATE_RECORD_ID", path3, { id, other }));
|
|
27501
27771
|
}
|
|
27502
27772
|
}
|
|
27503
27773
|
return { files, altered: altered.sort(), problems };
|
|
27504
27774
|
}
|
|
27505
27775
|
|
|
27506
27776
|
// src/adapters/filesystem/config-file.ts
|
|
27507
|
-
import { lstat as
|
|
27508
|
-
import { join as
|
|
27777
|
+
import { lstat as lstat5, readFile as readFile4, realpath, mkdir as mkdir2, open as open2, link, rename, unlink } from "node:fs/promises";
|
|
27778
|
+
import { join as join6 } from "node:path";
|
|
27509
27779
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
27510
27780
|
var missing = (error62) => error62.code === "ENOENT";
|
|
27511
|
-
var fileInfo = (path3) =>
|
|
27781
|
+
var fileInfo = (path3) => lstat5(path3).catch((error62) => {
|
|
27512
27782
|
if (missing(error62)) return void 0;
|
|
27513
27783
|
throw error62;
|
|
27514
27784
|
});
|
|
27515
27785
|
async function configDirectory(root) {
|
|
27516
|
-
const path3 =
|
|
27786
|
+
const path3 = join6(root, ".gitifact");
|
|
27517
27787
|
const info5 = await fileInfo(path3);
|
|
27518
27788
|
if (info5 && (!info5.isDirectory() || info5.isSymbolicLink())) throw new InitError("PATH_CONFLICT", t2("config.storeNotDirectory"));
|
|
27519
27789
|
return info5;
|
|
27520
27790
|
}
|
|
27521
27791
|
async function readConfigFile(root) {
|
|
27522
27792
|
await configDirectory(root);
|
|
27523
|
-
const path3 =
|
|
27793
|
+
const path3 = join6(root, ".gitifact", "config.json");
|
|
27524
27794
|
const info5 = await fileInfo(path3);
|
|
27525
27795
|
if (!info5) return void 0;
|
|
27526
27796
|
if (!info5.isFile() || info5.isSymbolicLink() || info5.size > 65536) throw new InitError("PATH_CONFLICT", t2("config.fileShape"));
|
|
@@ -27537,7 +27807,7 @@ async function readConfigFile(root) {
|
|
|
27537
27807
|
}
|
|
27538
27808
|
}
|
|
27539
27809
|
async function publishConfig(root, text4, recheck, beforePublish, replace = false) {
|
|
27540
|
-
const directory =
|
|
27810
|
+
const directory = join6(root, ".gitifact");
|
|
27541
27811
|
await configDirectory(root);
|
|
27542
27812
|
await mkdir2(directory).catch((error62) => {
|
|
27543
27813
|
if (error62.code !== "EEXIST") throw error62;
|
|
@@ -27550,7 +27820,7 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
|
|
|
27550
27820
|
throw new InitError("INPUT_CHANGED", t2("config.directoryChanged"));
|
|
27551
27821
|
}
|
|
27552
27822
|
};
|
|
27553
|
-
const temporary =
|
|
27823
|
+
const temporary = join6(directory, ".init-" + randomUUID2() + ".tmp");
|
|
27554
27824
|
const handle = await open2(temporary, "wx", 384);
|
|
27555
27825
|
const tempOwner = await handle.stat();
|
|
27556
27826
|
try {
|
|
@@ -27567,8 +27837,8 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
|
|
|
27567
27837
|
const bytes = await readFile4(temporary, "utf8");
|
|
27568
27838
|
if (bytes !== text4) throw new InitError("INPUT_CHANGED", t2("config.temporaryChanged"));
|
|
27569
27839
|
parseManagedConfig(bytes);
|
|
27570
|
-
if (replace) await rename(temporary,
|
|
27571
|
-
else await link(temporary,
|
|
27840
|
+
if (replace) await rename(temporary, join6(directory, "config.json"));
|
|
27841
|
+
else await link(temporary, join6(directory, "config.json"));
|
|
27572
27842
|
} finally {
|
|
27573
27843
|
await unchanged();
|
|
27574
27844
|
const current2 = await fileInfo(temporary);
|
|
@@ -27577,11 +27847,11 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
|
|
|
27577
27847
|
}
|
|
27578
27848
|
|
|
27579
27849
|
// src/adapters/filesystem/instruction-folder.ts
|
|
27580
|
-
import { lstat as
|
|
27581
|
-
import { join as
|
|
27850
|
+
import { lstat as lstat6, open as open3, readdir as readdir4, readFile as readFile5 } from "node:fs/promises";
|
|
27851
|
+
import { join as join7 } from "node:path";
|
|
27582
27852
|
var INSTRUCTION_FILE_LIMIT = 500;
|
|
27583
27853
|
var INSTRUCTION_TEXT_LIMIT = 512 * 1024;
|
|
27584
|
-
var
|
|
27854
|
+
var folderOf3 = (root, instructionPath) => join7(root, ...instructionPath.split("/").slice(0, -1));
|
|
27585
27855
|
async function listInstructionFiles(root, instructionPath) {
|
|
27586
27856
|
const files = [];
|
|
27587
27857
|
let limited = false;
|
|
@@ -27594,21 +27864,21 @@ async function listInstructionFiles(root, instructionPath) {
|
|
|
27594
27864
|
return;
|
|
27595
27865
|
}
|
|
27596
27866
|
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
|
|
27867
|
+
if (entry2.isDirectory()) await visit2(join7(folder, entry2.name), path3 + "/", depth + 1);
|
|
27868
|
+
else if (entry2.isFile() && path3 !== INSTRUCTION_FILE) files.push({ path: path3, size: (await lstat6(join7(folder, entry2.name))).size });
|
|
27599
27869
|
}
|
|
27600
27870
|
}
|
|
27601
|
-
await visit2(
|
|
27871
|
+
await visit2(folderOf3(root, instructionPath), "", 0);
|
|
27602
27872
|
return { files, limited };
|
|
27603
27873
|
}
|
|
27604
27874
|
var FRONTMATTER_WINDOW = 8 * 1024;
|
|
27605
27875
|
async function describeFiles(root, instructionPath, files) {
|
|
27606
|
-
const folder =
|
|
27876
|
+
const folder = folderOf3(root, instructionPath);
|
|
27607
27877
|
return Promise.all(files.map(async (file2) => {
|
|
27608
27878
|
if (!isInstructionReference(file2.path)) return file2;
|
|
27609
27879
|
let handle;
|
|
27610
27880
|
try {
|
|
27611
|
-
handle = await open3(
|
|
27881
|
+
handle = await open3(join7(folder, ...file2.path.split("/")), "r");
|
|
27612
27882
|
const buffer = Buffer.alloc(Math.min(FRONTMATTER_WINDOW, file2.size));
|
|
27613
27883
|
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
27614
27884
|
return { ...file2, ...parseInstructionFile(file2.path, buffer.subarray(0, bytesRead).toString("utf8")) };
|
|
@@ -27626,13 +27896,13 @@ async function readInstructionFiles(root, instructionPath) {
|
|
|
27626
27896
|
async function readInstructionFile(root, instructionPath, path3) {
|
|
27627
27897
|
const parts = path3.split("/");
|
|
27628
27898
|
if (parts.some((p) => !p || p === "." || p === ".." || /[\\:\0]/.test(p))) return void 0;
|
|
27629
|
-
let full =
|
|
27899
|
+
let full = folderOf3(root, instructionPath);
|
|
27630
27900
|
for (const [index, part] of parts.entries()) {
|
|
27631
|
-
full =
|
|
27632
|
-
const info5 = await
|
|
27901
|
+
full = join7(full, part);
|
|
27902
|
+
const info5 = await lstat6(full).catch(() => void 0);
|
|
27633
27903
|
if (!info5 || info5.isSymbolicLink() || (index < parts.length - 1 ? !info5.isDirectory() : !info5.isFile())) return void 0;
|
|
27634
27904
|
}
|
|
27635
|
-
const size = (await
|
|
27905
|
+
const size = (await lstat6(full)).size;
|
|
27636
27906
|
if (size > INSTRUCTION_TEXT_LIMIT) return { text: null, size, binary: false, tooLarge: true };
|
|
27637
27907
|
const bytes = await readFile5(full);
|
|
27638
27908
|
try {
|
|
@@ -27644,12 +27914,12 @@ async function readInstructionFile(root, instructionPath, path3) {
|
|
|
27644
27914
|
}
|
|
27645
27915
|
|
|
27646
27916
|
// src/adapters/filesystem/agents-file.ts
|
|
27647
|
-
import { lstat as
|
|
27648
|
-
import { join as
|
|
27917
|
+
import { lstat as lstat7, readFile as readFile6 } from "node:fs/promises";
|
|
27918
|
+
import { join as join8 } from "node:path";
|
|
27649
27919
|
var LIMIT = 1024 * 1024;
|
|
27650
27920
|
async function readAgentsFile(root) {
|
|
27651
|
-
const file2 =
|
|
27652
|
-
const info5 = await
|
|
27921
|
+
const file2 = join8(root, "AGENTS.md");
|
|
27922
|
+
const info5 = await lstat7(file2).catch(() => void 0);
|
|
27653
27923
|
if (!info5?.isFile() || info5.size > LIMIT) return null;
|
|
27654
27924
|
try {
|
|
27655
27925
|
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: false }).decode(await readFile6(file2));
|
|
@@ -27726,8 +27996,8 @@ async function documentsAtHead(project, head, paths2) {
|
|
|
27726
27996
|
|
|
27727
27997
|
// src/server/checkout/stamp.ts
|
|
27728
27998
|
import { createHash as createHash2 } from "node:crypto";
|
|
27729
|
-
import { lstat as
|
|
27730
|
-
import { join as
|
|
27999
|
+
import { lstat as lstat8 } from "node:fs/promises";
|
|
28000
|
+
import { join as join9 } from "node:path";
|
|
27731
28001
|
function createStampReader(root, inherited = process.env) {
|
|
27732
28002
|
const runner = createGitRunner();
|
|
27733
28003
|
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 +28009,7 @@ function createStampReader(root, inherited = process.env) {
|
|
|
27739
28009
|
]);
|
|
27740
28010
|
const paths2 = status.split("\0").filter(Boolean).map((entry2) => entry2.slice(3)).filter((path3) => !path3.startsWith(".gitifact/cache/"));
|
|
27741
28011
|
const seen = await Promise.all(paths2.map(async (path3) => {
|
|
27742
|
-
const info5 = await
|
|
28012
|
+
const info5 = await lstat8(join9(root, ...path3.split("/"))).catch(() => void 0);
|
|
27743
28013
|
return path3 + " " + (info5 ? info5.mtimeMs + ":" + info5.size : "-");
|
|
27744
28014
|
}));
|
|
27745
28015
|
return createHash2("sha256").update(head.trim() + "\n" + seen.sort().join("\n")).digest("hex").slice(0, 20);
|
|
@@ -27747,19 +28017,14 @@ function createStampReader(root, inherited = process.env) {
|
|
|
27747
28017
|
}
|
|
27748
28018
|
|
|
27749
28019
|
// 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
28020
|
async function settled(reads) {
|
|
27756
28021
|
const results = await Promise.allSettled(reads);
|
|
27757
28022
|
const failure2 = results.find((r) => r.status === "rejected");
|
|
27758
28023
|
if (failure2) throw failure2.reason;
|
|
27759
28024
|
return results.map((r) => r.value);
|
|
27760
28025
|
}
|
|
27761
|
-
var
|
|
27762
|
-
function checkoutDocuments(states) {
|
|
28026
|
+
var folderOf4 = (path3) => path3.split("/").slice(0, 3).join("/");
|
|
28027
|
+
function checkoutDocuments(states, authors) {
|
|
27763
28028
|
const documents = states.documents;
|
|
27764
28029
|
const standing2 = (id) => {
|
|
27765
28030
|
const previousPath = states.previousPath(id);
|
|
@@ -27772,30 +28037,45 @@ function checkoutDocuments(states) {
|
|
|
27772
28037
|
return { id: s.id, ...target ? { title: target.title, path: target.path } : {}, ...s.note ? { note: s.note } : {} };
|
|
27773
28038
|
};
|
|
27774
28039
|
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
|
-
|
|
28040
|
+
const features = arranged.features.map(({ index, requirements, designs }) => {
|
|
28041
|
+
const entry2 = authors.get(folderOf4(index.path));
|
|
28042
|
+
return {
|
|
28043
|
+
id: index.id,
|
|
28044
|
+
path: index.path,
|
|
28045
|
+
title: index.title,
|
|
28046
|
+
description: index.description,
|
|
28047
|
+
body: index.body,
|
|
28048
|
+
...standing2(index.id),
|
|
28049
|
+
requirements: requirements.map((r) => ({ id: r.id, path: r.path, title: r.title, description: r.description, order: r.order, body: r.body, ...standing2(r.id) })),
|
|
28050
|
+
designs: designs.map((d) => ({
|
|
28051
|
+
id: d.id,
|
|
28052
|
+
path: d.path,
|
|
28053
|
+
title: d.title,
|
|
28054
|
+
description: d.description,
|
|
28055
|
+
order: d.order,
|
|
28056
|
+
body: d.body,
|
|
28057
|
+
requirements: d.requirements,
|
|
28058
|
+
sources: d.sources.map(source2),
|
|
28059
|
+
...standing2(d.id)
|
|
28060
|
+
})),
|
|
28061
|
+
// Who committed to the feature folder, most first, and when it was last touched; nobody until first committed.
|
|
28062
|
+
contributors: entry2?.people ?? [],
|
|
28063
|
+
updatedAt: entry2?.latest ?? null
|
|
28064
|
+
};
|
|
28065
|
+
});
|
|
28066
|
+
const instructions2 = arranged.instructions.map((k) => ({
|
|
28067
|
+
id: k.id,
|
|
28068
|
+
name: k.name,
|
|
28069
|
+
path: k.path,
|
|
28070
|
+
title: k.title,
|
|
28071
|
+
description: k.description,
|
|
28072
|
+
body: k.body,
|
|
28073
|
+
...standing2(k.id),
|
|
28074
|
+
updatedAt: authors.get(folderOf4(k.path))?.latest ?? null
|
|
27794
28075
|
}));
|
|
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
28076
|
return { features, instructions: instructions2, orphans: arranged.orphans };
|
|
27797
28077
|
}
|
|
27798
|
-
function createCheckoutReader(root,
|
|
28078
|
+
function createCheckoutReader(root, cache, inherited = process.env) {
|
|
27799
28079
|
const reader = storeReader(root);
|
|
27800
28080
|
const runner = createGitRunner();
|
|
27801
28081
|
const env = { ...inherited, GIT_OPTIONAL_LOCKS: "0", GIT_NO_REPLACE_OBJECTS: "1", GIT_TERMINAL_PROMPT: "0", LC_ALL: "C" };
|
|
@@ -27808,87 +28088,36 @@ function createCheckoutReader(root, sessionId, cache, inherited = process.env) {
|
|
|
27808
28088
|
};
|
|
27809
28089
|
const pending = /* @__PURE__ */ new Map();
|
|
27810
28090
|
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
28091
|
async function read() {
|
|
27841
28092
|
const raw = await readConfigFile(root);
|
|
27842
28093
|
if (!raw) throw new StoreError(t2("specReader.schemaRequired"));
|
|
27843
28094
|
parseManagedConfig(raw);
|
|
27844
28095
|
const head = await readHead();
|
|
27845
28096
|
const stamp = await readStamp();
|
|
27846
|
-
const [current2, dirty, authors,
|
|
28097
|
+
const [current2, dirty, authors, people] = await settled([
|
|
27847
28098
|
cache.documents.list(),
|
|
27848
28099
|
head ? git(["status", "--porcelain=v1", "--", ".gitifact/spec", INSTRUCTIONS_ROOT]) : Promise.resolve(""),
|
|
27849
|
-
|
|
27850
|
-
|
|
27851
|
-
head ?
|
|
28100
|
+
// Who touched each folder and who committed at all, counted over the commit log in the cache.
|
|
28101
|
+
head ? cache.log.folders(head) : Promise.resolve(/* @__PURE__ */ new Map()),
|
|
28102
|
+
head ? cache.log.contributors(head) : Promise.resolve([])
|
|
27852
28103
|
]);
|
|
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
28104
|
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 }))];
|
|
28105
|
+
const { features, instructions: instructions2, orphans } = checkoutDocuments(states, authors);
|
|
28106
|
+
const problems = [...current2.problems, ...orphans.map((feature2) => docProblem("FEATURE_INDEX_REQUIRED", `.gitifact/spec/${feature2}/index.md`, { feature: feature2 }))];
|
|
27873
28107
|
if (await readHead() !== head) throw new StoreError(t2("specReader.projectChanged"));
|
|
27874
|
-
|
|
27875
|
-
contract: "browser-specs",
|
|
27876
|
-
version: 7,
|
|
27877
|
-
sessionId,
|
|
28108
|
+
return {
|
|
27878
28109
|
head,
|
|
27879
|
-
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27880
28110
|
stamp,
|
|
28111
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27881
28112
|
working: head ? !!dirty.trim() : features.length > 0 || instructions2.length > 0,
|
|
27882
28113
|
features,
|
|
27883
28114
|
instructions: instructions2,
|
|
27884
|
-
agents: agents2,
|
|
27885
28115
|
problems,
|
|
27886
|
-
|
|
27887
|
-
|
|
27888
|
-
}
|
|
27889
|
-
return { checkout, head };
|
|
28116
|
+
people,
|
|
28117
|
+
folders: authors
|
|
28118
|
+
};
|
|
27890
28119
|
}
|
|
27891
|
-
|
|
28120
|
+
const base = () => {
|
|
27892
28121
|
const language = getLanguage();
|
|
27893
28122
|
let value = pending.get(language);
|
|
27894
28123
|
if (!value) {
|
|
@@ -27897,10 +28126,37 @@ function createCheckoutReader(root, sessionId, cache, inherited = process.env) {
|
|
|
27897
28126
|
}
|
|
27898
28127
|
return value;
|
|
27899
28128
|
};
|
|
28129
|
+
return {
|
|
28130
|
+
base,
|
|
28131
|
+
/** Every instruction by name with the other files of its folder, and AGENTS.md: few enough to read whole. */
|
|
28132
|
+
async instructions() {
|
|
28133
|
+
const checkout = await base();
|
|
28134
|
+
const instructions2 = await Promise.all([...checkout.instructions].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0).map(async (instruction2) => {
|
|
28135
|
+
const { files, limited } = instruction2.state === "deleted" ? { files: [], limited: false } : await readInstructionFiles(root, instruction2.path);
|
|
28136
|
+
return { ...instruction2, files, filesLimited: limited };
|
|
28137
|
+
}));
|
|
28138
|
+
const agentsText = await readAgentsFile(root);
|
|
28139
|
+
return { instructions: instructions2, agents: agentsText === null ? null : { path: "AGENTS.md", body: agentsText, updatedAt: checkout.folders.get("AGENTS.md")?.latest ?? null } };
|
|
28140
|
+
}
|
|
28141
|
+
};
|
|
28142
|
+
}
|
|
28143
|
+
|
|
28144
|
+
// src/queries/paging.ts
|
|
28145
|
+
var PAGE_SIZE = 20;
|
|
28146
|
+
function pageOf(rows, keyOf, request) {
|
|
28147
|
+
let start = 0;
|
|
28148
|
+
if (request.after !== void 0) {
|
|
28149
|
+
const at = rows.findIndex((row) => keyOf(row) === request.after);
|
|
28150
|
+
if (at < 0) return void 0;
|
|
28151
|
+
start = at + 1;
|
|
28152
|
+
}
|
|
28153
|
+
const size = request.all ? rows.length : request.limit ?? PAGE_SIZE;
|
|
28154
|
+
const shown = rows.slice(start, start + size);
|
|
28155
|
+
const more = start + shown.length < rows.length && shown.length > 0;
|
|
28156
|
+
return { rows: shown, total: rows.length, next: more ? keyOf(shown[shown.length - 1]) : null };
|
|
27900
28157
|
}
|
|
27901
28158
|
|
|
27902
28159
|
// src/server/commit/commit-files.ts
|
|
27903
|
-
var LIST_LIMIT = 500;
|
|
27904
28160
|
var TEXT_LIMIT = 512 * 1024;
|
|
27905
28161
|
var KEPT = 200;
|
|
27906
28162
|
var statusOf = (code) => code.startsWith("R") ? "renamed" : code === "A" ? "added" : code === "D" ? "deleted" : "modified";
|
|
@@ -27956,10 +28212,15 @@ function createCommitFiles(git) {
|
|
|
27956
28212
|
return git.run(["cat-file", "blob", `${rev}:${path3}`]);
|
|
27957
28213
|
}
|
|
27958
28214
|
return {
|
|
27959
|
-
/**
|
|
27960
|
-
|
|
28215
|
+
/**
|
|
28216
|
+
* A page of the source files after the file `after` (a path) and how many there are in all: undefined for a commit
|
|
28217
|
+
* the repository lacks, null for a cursor the commit does not have.
|
|
28218
|
+
*/
|
|
28219
|
+
async files(commit, request) {
|
|
27961
28220
|
const all = await list(commit);
|
|
27962
|
-
|
|
28221
|
+
if (!all) return void 0;
|
|
28222
|
+
const page = pageOf(all.files, (f) => f.path, request);
|
|
28223
|
+
return page ? { ...all, files: page.rows, next: page.next } : null;
|
|
27963
28224
|
},
|
|
27964
28225
|
/** One listed file on both sides; undefined when the commit did not change that path. */
|
|
27965
28226
|
async file(commit, path3) {
|
|
@@ -28041,31 +28302,200 @@ async function workingChange2(source2, id) {
|
|
|
28041
28302
|
};
|
|
28042
28303
|
}
|
|
28043
28304
|
|
|
28305
|
+
// src/queries/search.ts
|
|
28306
|
+
var SEARCH_GROUPS = ["feature", "requirement", "design", "instruction", "record", "commit"];
|
|
28307
|
+
var FIRST = 5;
|
|
28308
|
+
var RECENT = 6;
|
|
28309
|
+
var place2 = (path3) => path3.replace(/^\.gitifact\//, "");
|
|
28310
|
+
var folderOf5 = (path3) => path3.split("/").slice(0, 3).join("/");
|
|
28311
|
+
async function searchRecords(source2, request) {
|
|
28312
|
+
const head = request.head ?? await source2.head();
|
|
28313
|
+
const query = request.q.trim().toLowerCase();
|
|
28314
|
+
if (!query) return [await recent(source2, head)];
|
|
28315
|
+
const size = request.group ? request.limit ?? 20 : FIRST;
|
|
28316
|
+
const one = async (group) => {
|
|
28317
|
+
const found = group === "record" ? head ? await source2.cache.history.searchRecords(head, query, request.after, size) : empty() : group === "commit" ? head ? await source2.cache.log.commitsNamed(head, query, request.after, size) : empty() : await source2.cache.documents.searchKind(group, query, request.after, size);
|
|
28318
|
+
return found && { group, ...found };
|
|
28319
|
+
};
|
|
28320
|
+
const groups = await Promise.all((request.group ? [request.group] : SEARCH_GROUPS).map(one));
|
|
28321
|
+
if (groups.some((g) => !g)) return void 0;
|
|
28322
|
+
return groups.filter((g) => request.group || g.total > 0);
|
|
28323
|
+
}
|
|
28324
|
+
var empty = () => ({ total: 0, next: null, hits: [] });
|
|
28325
|
+
async function recent(source2, head) {
|
|
28326
|
+
const [{ documents }, folders] = await Promise.all([source2.cache.documents.list(), head ? source2.cache.log.folders(head) : /* @__PURE__ */ new Map()]);
|
|
28327
|
+
const hits = documents.filter((d) => d.kind === "feature" || d.kind === "instruction").map((d) => ({ doc: d, latest: folders.get(folderOf5(d.path))?.latest })).filter((d) => !!d.latest).sort((a, b) => Date.parse(b.latest) - Date.parse(a.latest) || a.doc.id.localeCompare(b.doc.id)).slice(0, RECENT).map(({ doc }) => ({
|
|
28328
|
+
id: doc.id,
|
|
28329
|
+
kind: doc.kind,
|
|
28330
|
+
title: doc.title,
|
|
28331
|
+
where: place2(doc.path),
|
|
28332
|
+
line: doc.description,
|
|
28333
|
+
...doc.kind === "feature" ? { featureId: doc.id } : {}
|
|
28334
|
+
}));
|
|
28335
|
+
return { group: "recent", total: hits.length, next: null, hits };
|
|
28336
|
+
}
|
|
28337
|
+
|
|
28338
|
+
// src/queries/checkout.ts
|
|
28339
|
+
var SHOWN_REQUIREMENTS = 12;
|
|
28340
|
+
function checkoutIndex(base) {
|
|
28341
|
+
return {
|
|
28342
|
+
features: base.features.map((f) => ({
|
|
28343
|
+
id: f.id,
|
|
28344
|
+
path: f.path,
|
|
28345
|
+
title: f.title,
|
|
28346
|
+
state: f.state,
|
|
28347
|
+
requirements: f.requirements.map((r) => ({ id: r.id, path: r.path, title: r.title, description: r.description })),
|
|
28348
|
+
designs: f.designs.map((d) => ({ id: d.id, path: d.path, title: d.title }))
|
|
28349
|
+
})),
|
|
28350
|
+
instructions: base.instructions.map((k) => ({ id: k.id, name: k.name, path: k.path, title: k.title })),
|
|
28351
|
+
people: base.people.map((p) => ({ name: p.name, email: p.email }))
|
|
28352
|
+
};
|
|
28353
|
+
}
|
|
28354
|
+
var acceptanceHeading = /^###\s+(?:수용 조건|acceptance criteria)\s*$/i;
|
|
28355
|
+
function acceptanceCount(body2) {
|
|
28356
|
+
const lines = body2.replace(/\r\n/g, "\n").split("\n");
|
|
28357
|
+
const start = lines.findIndex((line2) => acceptanceHeading.test(line2.trim()));
|
|
28358
|
+
if (start < 0) return null;
|
|
28359
|
+
let count2 = 0;
|
|
28360
|
+
for (const line2 of lines.slice(start + 1)) {
|
|
28361
|
+
if (/^#{1,6}\s/.test(line2)) break;
|
|
28362
|
+
if (/^\d+\.\s/.test(line2)) count2++;
|
|
28363
|
+
}
|
|
28364
|
+
return count2 || null;
|
|
28365
|
+
}
|
|
28366
|
+
function listFeatures(base, filter, request) {
|
|
28367
|
+
const query = filter.q?.trim().toLowerCase();
|
|
28368
|
+
const named = (f) => !query || (f.title + " " + f.id).toLowerCase().includes(query);
|
|
28369
|
+
const key = filter.sort ?? "updated";
|
|
28370
|
+
const opens = { title: "asc", requirements: "desc", updated: "desc" };
|
|
28371
|
+
const way = (filter.dir ?? opens[key]) === "asc" ? 1 : -1;
|
|
28372
|
+
const groups = base.features.filter((f) => !filter.design || filter.design === "yes" === f.designs.length > 0).filter((f) => !filter.author || f.contributors.some((p) => p.email === filter.author)).map((f) => ({ feature: f, requirements: named(f) ? f.requirements : f.requirements.filter((r) => (r.title + " " + r.id).toLowerCase().includes(query)) })).filter((g) => named(g.feature) || g.requirements.length).sort((a, b) => way * (key === "requirements" ? a.feature.requirements.length - b.feature.requirements.length : key === "title" ? a.feature.title.localeCompare(b.feature.title) : (a.feature.updatedAt ?? "").localeCompare(b.feature.updatedAt ?? "")) || a.feature.title.localeCompare(b.feature.title));
|
|
28373
|
+
const page = pageOf(groups, (g) => g.feature.id, request);
|
|
28374
|
+
if (!page) return void 0;
|
|
28375
|
+
const features = page.rows.map(({ feature: f, requirements }) => {
|
|
28376
|
+
const designed = new Set(f.designs.flatMap((d) => d.requirements));
|
|
28377
|
+
return {
|
|
28378
|
+
id: f.id,
|
|
28379
|
+
path: f.path,
|
|
28380
|
+
title: f.title,
|
|
28381
|
+
description: f.description,
|
|
28382
|
+
state: f.state,
|
|
28383
|
+
designs: f.designs.length,
|
|
28384
|
+
contributors: f.contributors,
|
|
28385
|
+
updatedAt: f.updatedAt,
|
|
28386
|
+
requirementCount: f.requirements.length,
|
|
28387
|
+
requirements: requirements.slice(0, SHOWN_REQUIREMENTS).map((r) => ({
|
|
28388
|
+
id: r.id,
|
|
28389
|
+
title: r.title,
|
|
28390
|
+
description: r.description,
|
|
28391
|
+
state: r.state,
|
|
28392
|
+
acceptance: acceptanceCount(r.body),
|
|
28393
|
+
designed: designed.has(r.id)
|
|
28394
|
+
})),
|
|
28395
|
+
hidden: Math.max(0, requirements.length - SHOWN_REQUIREMENTS)
|
|
28396
|
+
};
|
|
28397
|
+
});
|
|
28398
|
+
return {
|
|
28399
|
+
total: page.total,
|
|
28400
|
+
all: base.features.length,
|
|
28401
|
+
requirements: groups.reduce((sum, g) => sum + g.requirements.length, 0),
|
|
28402
|
+
mostRequirements: Math.max(0, ...base.features.map((f) => f.requirements.length)),
|
|
28403
|
+
next: page.next,
|
|
28404
|
+
features
|
|
28405
|
+
};
|
|
28406
|
+
}
|
|
28407
|
+
var featureOf = (base, id) => base.features.find((f) => f.id === id);
|
|
28408
|
+
function listContributors(base, q, request) {
|
|
28409
|
+
const query = q?.trim().toLowerCase();
|
|
28410
|
+
const people = base.people.filter((p) => !query || (p.name + " " + p.email).toLowerCase().includes(query));
|
|
28411
|
+
const page = pageOf(people, (p) => p.email, request);
|
|
28412
|
+
if (!page) return void 0;
|
|
28413
|
+
const touched = (email3) => base.features.filter((f) => f.contributors.some((c) => c.email === email3)).length;
|
|
28414
|
+
return { total: page.total, next: page.next, people: page.rows.map((p) => ({ ...p, features: touched(p.email) })) };
|
|
28415
|
+
}
|
|
28416
|
+
function contributorOf(base, email3) {
|
|
28417
|
+
const person = base.people.find((p) => p.email === email3);
|
|
28418
|
+
if (!person) return void 0;
|
|
28419
|
+
const features = base.features.flatMap((f) => {
|
|
28420
|
+
const share = f.contributors.find((c) => c.email === email3);
|
|
28421
|
+
return share ? [{ id: f.id, title: f.title, commits: share.commits, requirements: f.requirements.length }] : [];
|
|
28422
|
+
});
|
|
28423
|
+
return { person, features };
|
|
28424
|
+
}
|
|
28425
|
+
function topContributors(people) {
|
|
28426
|
+
const rest = people.slice(3);
|
|
28427
|
+
return { total: people.length, top: people.slice(0, 3), rest: { count: rest.length, commits: rest.reduce((sum, p) => sum + p.commits, 0) } };
|
|
28428
|
+
}
|
|
28429
|
+
|
|
28044
28430
|
// src/server/routes/record-routes.ts
|
|
28045
28431
|
var PAGE = 20;
|
|
28046
28432
|
function recordRoutes(root, sessionId, env) {
|
|
28047
28433
|
const git = storeReader(root);
|
|
28048
28434
|
const cache = openCache(root, { run: (args, input2) => git.run(args, input2), decode: git.decode, legacyBundles: (oids) => git.readBundles(oids) });
|
|
28049
|
-
const
|
|
28435
|
+
const checkout = createCheckoutReader(root, cache, env);
|
|
28050
28436
|
const readStamp = createStampReader(root, env);
|
|
28051
28437
|
const commitFiles = createCommitFiles(git);
|
|
28438
|
+
const source2 = { head: async () => (await git.baseline()).head, reader: git, cache };
|
|
28052
28439
|
const working2 = { head: async () => (await git.baseline()).head, cache, pendingRecords: () => readPendingRecords(root, (args) => git.run(args)) };
|
|
28053
28440
|
const unreadable2 = () => t2("server.specsUnreadable");
|
|
28054
|
-
const
|
|
28441
|
+
const cursorGone2 = () => new HttpError(404, "NOT_FOUND", t2("server.cursorNotFound"));
|
|
28055
28442
|
return [
|
|
28056
|
-
|
|
28443
|
+
// The checkout a part at a time, each shaped by the query layer (`queries/checkout.ts`): the frame every screen reads
|
|
28444
|
+
// first, the feature list and one feature, the instructions, and the contributors.
|
|
28445
|
+
route({ method: "GET", path: "/api/v1/checkout", session: true, unreadable: unreadable2, handle: async () => {
|
|
28446
|
+
const base = await checkout.base();
|
|
28447
|
+
return ok(browserCheckoutV1.parse({
|
|
28448
|
+
contract: "browser-checkout",
|
|
28449
|
+
version: 1,
|
|
28450
|
+
sessionId,
|
|
28451
|
+
head: base.head,
|
|
28452
|
+
observedAt: base.observedAt,
|
|
28453
|
+
stamp: base.stamp,
|
|
28454
|
+
working: base.working,
|
|
28455
|
+
problems: base.problems,
|
|
28456
|
+
index: checkoutIndex(base)
|
|
28457
|
+
}));
|
|
28458
|
+
} }),
|
|
28459
|
+
route({ method: "GET", path: "/api/v1/features", session: true, query: browserFeaturesQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28460
|
+
const page = listFeatures(await checkout.base(), query, { after: query.after, limit: query.limit ?? PAGE });
|
|
28461
|
+
if (!page) throw cursorGone2();
|
|
28462
|
+
return ok(browserFeaturesV1.parse({ contract: "browser-features", version: 1, sessionId, ...page }));
|
|
28463
|
+
} }),
|
|
28464
|
+
route({ method: "GET", path: "/api/v1/feature", session: true, query: browserFeatureQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28465
|
+
const feature2 = featureOf(await checkout.base(), query.id);
|
|
28466
|
+
if (!feature2) throw new HttpError(404, "NOT_FOUND", t2("server.featureNotFound"));
|
|
28467
|
+
return ok(browserFeatureV1.parse({ contract: "browser-feature", version: 1, sessionId, feature: feature2 }));
|
|
28468
|
+
} }),
|
|
28469
|
+
route({ method: "GET", path: "/api/v1/instructions", session: true, unreadable: unreadable2, handle: async () => ok(browserInstructionsV1.parse({ contract: "browser-instructions", version: 1, sessionId, ...await checkout.instructions() })) }),
|
|
28470
|
+
route({ method: "GET", path: "/api/v1/contributors", session: true, query: browserContributorsQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28471
|
+
const page = listContributors(await checkout.base(), query.q, { after: query.after, limit: query.limit ?? PAGE });
|
|
28472
|
+
if (!page) throw cursorGone2();
|
|
28473
|
+
return ok(browserContributorsV1.parse({ contract: "browser-contributors", version: 1, sessionId, ...page }));
|
|
28474
|
+
} }),
|
|
28475
|
+
route({ method: "GET", path: "/api/v1/contributor", session: true, query: browserContributorQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28476
|
+
const found = contributorOf(await checkout.base(), query.email);
|
|
28477
|
+
if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.contributorNotFound"));
|
|
28478
|
+
return ok(browserContributorV1.parse({ contract: "browser-contributor", version: 1, sessionId, ...found }));
|
|
28479
|
+
} }),
|
|
28057
28480
|
// Whether the screen is behind, asked when the reader comes back to the tab; cheap enough to ask every time.
|
|
28058
28481
|
route({ method: "GET", path: "/api/v1/stamp", session: true, unreadable: unreadable2, handle: async () => ok(browserStampV1.parse({ contract: "browser-stamp", version: 1, sessionId, stamp: await readStamp() })) }),
|
|
28059
28482
|
route({ method: "GET", path: "/api/v1/history", session: true, query: browserHistoryQueryV4, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28060
28483
|
const page = await cache.history.commits(query.head, { kind: query.kind, document: query.document, feature: query.feature, author: query.author, q: query.q }, query.after, query.limit ?? PAGE);
|
|
28061
|
-
if (!page) throw
|
|
28484
|
+
if (!page) throw cursorGone2();
|
|
28062
28485
|
return ok(browserHistoryV6.parse({ contract: "browser-history", version: 6, sessionId, head: query.head, ...page }));
|
|
28063
28486
|
} }),
|
|
28064
|
-
route({ method: "GET", path: "/api/v1/history/summary", session: true, query: browserHistorySummaryQueryV1, unreadable: unreadable2, handle: async ({ query }) => ok(
|
|
28487
|
+
route({ method: "GET", path: "/api/v1/history/summary", session: true, query: browserHistorySummaryQueryV1, unreadable: unreadable2, handle: async ({ query }) => ok(browserHistorySummaryV5.parse({
|
|
28488
|
+
contract: "browser-history-summary",
|
|
28489
|
+
version: 5,
|
|
28490
|
+
sessionId,
|
|
28491
|
+
head: query.head,
|
|
28492
|
+
...await cache.history.summary(query.head),
|
|
28493
|
+
people: topContributors(await cache.log.contributors(query.head))
|
|
28494
|
+
})) }),
|
|
28065
28495
|
// 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
|
|
28496
|
+
route({ method: "GET", path: "/api/v1/commit", session: true, query: browserCommitQueryV3, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28497
|
+
const page = await cache.history.commitChanges(query.commit, query.after, query.limit ?? PAGE, query.record);
|
|
28498
|
+
if (!page) throw cursorGone2();
|
|
28069
28499
|
const first = page.events[0];
|
|
28070
28500
|
const line2 = first ? [] : git.decode(await git.run(["log", "-1", "--format=%aN%x00%aE%x00%cN%x00%aI%x00%s", query.commit, "--"]).catch(() => Buffer.from(""))).split("\0");
|
|
28071
28501
|
if (!first && line2.length < 5) throw new HttpError(404, "NOT_FOUND", t2("server.commitNotFound"));
|
|
@@ -28093,9 +28523,9 @@ function recordRoutes(root, sessionId, env) {
|
|
|
28093
28523
|
} }),
|
|
28094
28524
|
// A record's page names the record; which commit added it is read from the history of the HEAD the reader is on.
|
|
28095
28525
|
route({ method: "GET", path: "/api/v1/record", session: true, query: browserRecordQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28096
|
-
const
|
|
28097
|
-
if (!
|
|
28098
|
-
return ok(
|
|
28526
|
+
const found = await cache.history.recordOf(query.head, query.id);
|
|
28527
|
+
if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.recordNotFound"));
|
|
28528
|
+
return ok(browserRecordV2.parse({ contract: "browser-record", version: 2, sessionId, head: query.head, id: query.id, ...found }));
|
|
28099
28529
|
} }),
|
|
28100
28530
|
// What is not committed yet, worked out again on every request: the uncommitted entry of the records list.
|
|
28101
28531
|
route({ method: "GET", path: "/api/v1/working", session: true, unreadable: unreadable2, handle: async () => ok(browserWorkingV1.parse({ contract: "browser-working", version: 1, sessionId, ...await workingOverview(working2) })) }),
|
|
@@ -28105,10 +28535,11 @@ function recordRoutes(root, sessionId, env) {
|
|
|
28105
28535
|
return ok(browserWorkingChangeV1.parse({ contract: "browser-working-change", version: 1, sessionId, ...change2 }));
|
|
28106
28536
|
} }),
|
|
28107
28537
|
// 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
|
-
|
|
28538
|
+
route({ method: "GET", path: "/api/v1/commit/files", session: true, query: browserCommitFilesQueryV2, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28539
|
+
const found = await commitFiles.files(query.commit, { after: query.after, limit: query.limit ?? PAGE });
|
|
28540
|
+
if (found === void 0) throw new HttpError(404, "NOT_FOUND", t2("server.commitNotFound"));
|
|
28541
|
+
if (found === null) throw cursorGone2();
|
|
28542
|
+
return ok(browserCommitFilesV2.parse({ contract: "browser-commit-files", version: 2, sessionId, commit: found.commit, total: found.total, next: found.next, files: found.files }));
|
|
28112
28543
|
} }),
|
|
28113
28544
|
route({ method: "GET", path: "/api/v1/commit/file", session: true, query: browserCommitFileQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28114
28545
|
const found = await commitFiles.file(query.commit, query.path);
|
|
@@ -28122,21 +28553,26 @@ function recordRoutes(root, sessionId, env) {
|
|
|
28122
28553
|
if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.instructionFileNotFound"));
|
|
28123
28554
|
return ok(browserInstructionFileV1.parse({ contract: "browser-instruction-file", version: 1, sessionId, id: query.id, path: query.path, ...found }));
|
|
28124
28555
|
} }),
|
|
28125
|
-
|
|
28556
|
+
// The search box's groups as the query layer works them out (`queries/search.ts`); the browser only draws them.
|
|
28557
|
+
route({ method: "GET", path: "/api/v1/search", session: true, query: browserSearchQueryV2, unreadable: unreadable2, handle: async ({ query }) => {
|
|
28558
|
+
const groups = await searchRecords(source2, query);
|
|
28559
|
+
if (!groups) throw cursorGone2();
|
|
28560
|
+
return ok(browserSearchV3.parse({ contract: "browser-search", version: 3, sessionId, query: query.q, groups }));
|
|
28561
|
+
} })
|
|
28126
28562
|
];
|
|
28127
28563
|
}
|
|
28128
28564
|
|
|
28129
28565
|
// src/server/routes/asset-routes.ts
|
|
28130
|
-
import { lstat as
|
|
28131
|
-
import { join as
|
|
28566
|
+
import { lstat as lstat9, readFile as readFile7 } from "node:fs/promises";
|
|
28567
|
+
import { join as join10 } from "node:path";
|
|
28132
28568
|
function assetRoutes(root) {
|
|
28133
28569
|
return [
|
|
28134
28570
|
route({ method: "GET", path: "/api/v1/assets/*", session: false, handle: async ({ path: path3, response }) => {
|
|
28135
28571
|
const relative = path3.slice("/api/v1/assets/".length);
|
|
28136
28572
|
const assetPath = ASSETS_DIR + "/" + relative;
|
|
28137
28573
|
if (!isAssetPath(assetPath)) throw new HttpError(404, "NOT_FOUND", t2("server.assetNotFound"));
|
|
28138
|
-
const file2 =
|
|
28139
|
-
const stat3 = await
|
|
28574
|
+
const file2 = join10(root, ...assetPath.split("/"));
|
|
28575
|
+
const stat3 = await lstat9(file2).catch(() => void 0);
|
|
28140
28576
|
if (!stat3?.isFile() || stat3.isSymbolicLink() || stat3.size > 20 * 1024 * 1024) throw new HttpError(404, "NOT_FOUND", t2("server.assetNotFound"));
|
|
28141
28577
|
const bytes = await readFile7(file2);
|
|
28142
28578
|
const extension = assetExtension(relative);
|
|
@@ -28286,13 +28722,13 @@ async function runBrowser(options, version2) {
|
|
|
28286
28722
|
|
|
28287
28723
|
// src/commands/spec-init.ts
|
|
28288
28724
|
import { readdir as readdir5 } from "node:fs/promises";
|
|
28289
|
-
import { join as
|
|
28725
|
+
import { join as join14 } from "node:path";
|
|
28290
28726
|
|
|
28291
28727
|
// src/adapters/git/init-repository.ts
|
|
28292
28728
|
import { createHash as createHash3 } from "node:crypto";
|
|
28293
|
-
import { lstat as
|
|
28294
|
-
import { join as
|
|
28295
|
-
var markerPresent = (path3) =>
|
|
28729
|
+
import { lstat as lstat10, readFile as readFile9 } from "node:fs/promises";
|
|
28730
|
+
import { join as join11 } from "node:path";
|
|
28731
|
+
var markerPresent = (path3) => lstat10(path3).then(() => true, (error62) => {
|
|
28296
28732
|
if (error62.code === "ENOENT") return false;
|
|
28297
28733
|
throw error62;
|
|
28298
28734
|
});
|
|
@@ -28315,7 +28751,7 @@ function initRepository(cwd, inherited = process.env) {
|
|
|
28315
28751
|
const gitDir = locations.length === 2 ? locations[0] : decodeGitLine(await git(["rev-parse", "--absolute-git-dir"], root));
|
|
28316
28752
|
const indexPath = locations.length === 2 ? locations[1] : decodeGitLine(await git(["rev-parse", "--path-format=absolute", "--git-path", "index"], root));
|
|
28317
28753
|
for (const marker2 of ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"]) {
|
|
28318
|
-
if (await markerPresent(
|
|
28754
|
+
if (await markerPresent(join11(gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
|
|
28319
28755
|
}
|
|
28320
28756
|
if (state2.changes.some((change2) => change2.kind === "unmerged")) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
28321
28757
|
const index = await readFile9(indexPath).catch((error62) => {
|
|
@@ -28353,14 +28789,14 @@ function initRepository(cwd, inherited = process.env) {
|
|
|
28353
28789
|
|
|
28354
28790
|
// src/adapters/filesystem/document-file.ts
|
|
28355
28791
|
import { randomBytes } from "node:crypto";
|
|
28356
|
-
import { lstat as
|
|
28357
|
-
import { join as
|
|
28792
|
+
import { lstat as lstat11, mkdir as mkdir3, writeFile as writeFile2 } from "node:fs/promises";
|
|
28793
|
+
import { join as join12 } from "node:path";
|
|
28358
28794
|
var generateId = (prefix) => prefix + "-" + [...randomBytes(10)].map((n) => "abcdefghijklmnopqrstuvwxyz234567"[n & 31]).join("");
|
|
28359
28795
|
async function createDocumentFile(root, path3, text4) {
|
|
28360
28796
|
const parts = path3.split("/");
|
|
28361
28797
|
for (let i = 1; i < parts.length; i++) {
|
|
28362
|
-
const folder =
|
|
28363
|
-
const info5 = await
|
|
28798
|
+
const folder = join12(root, ...parts.slice(0, i));
|
|
28799
|
+
const info5 = await lstat11(folder).catch((error62) => {
|
|
28364
28800
|
if (error62.code === "ENOENT") return void 0;
|
|
28365
28801
|
throw error62;
|
|
28366
28802
|
});
|
|
@@ -28370,7 +28806,7 @@ async function createDocumentFile(root, path3, text4) {
|
|
|
28370
28806
|
}
|
|
28371
28807
|
if (!info5.isDirectory() || info5.isSymbolicLink()) throw Object.assign(new Error(folder), { code: "ENOTDIR" });
|
|
28372
28808
|
}
|
|
28373
|
-
await writeFile2(
|
|
28809
|
+
await writeFile2(join12(root, ...parts), text4, { flag: "wx" });
|
|
28374
28810
|
}
|
|
28375
28811
|
var LINE_ENDINGS_PATH = ".gitifact/.gitattributes";
|
|
28376
28812
|
var LINE_ENDINGS = "# Written by gitifact init: documents keep LF on every OS, so what Git holds and the files read the same.\n* text=auto eol=lf\n";
|
|
@@ -28386,14 +28822,14 @@ async function writeLineEndings(root) {
|
|
|
28386
28822
|
|
|
28387
28823
|
// src/adapters/filesystem/managed-file.ts
|
|
28388
28824
|
import { open as open4, readFile as readFile10, mkdir as mkdir4, realpath as realpath2, rename as rename2, link as link2, unlink as unlink2 } from "node:fs/promises";
|
|
28389
|
-
import { join as
|
|
28825
|
+
import { join as join13, dirname } from "node:path";
|
|
28390
28826
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
28391
28827
|
var DEFAULT_LIMIT = 1024 * 1024;
|
|
28392
28828
|
async function managedPath(root, path3, create = false) {
|
|
28393
28829
|
let full = root;
|
|
28394
28830
|
const parts = path3.split("/");
|
|
28395
28831
|
for (let i = 0; i < parts.length; i++) {
|
|
28396
|
-
full =
|
|
28832
|
+
full = join13(full, parts[i]);
|
|
28397
28833
|
let info5 = await fileInfo(full);
|
|
28398
28834
|
if (!info5 && create && i < parts.length - 1) {
|
|
28399
28835
|
await mkdir4(full).catch((error62) => {
|
|
@@ -28441,7 +28877,7 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
28441
28877
|
if (previous !== null) await unlink2(full);
|
|
28442
28878
|
return;
|
|
28443
28879
|
}
|
|
28444
|
-
const temp =
|
|
28880
|
+
const temp = join13(directory, ".gitifact-managed-" + randomUUID4() + ".tmp");
|
|
28445
28881
|
const handle = await open4(temp, "wx", 384);
|
|
28446
28882
|
const tempOwner = await handle.stat();
|
|
28447
28883
|
try {
|
|
@@ -28768,7 +29204,7 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
28768
29204
|
if (old !== void 0) return existing(old);
|
|
28769
29205
|
if (first.trackedConfig) throw new InitError("CONFIG_DELETED", t2("init.configDeleted"));
|
|
28770
29206
|
const checkRecords = async () => {
|
|
28771
|
-
const entries = await readdir5(
|
|
29207
|
+
const entries = await readdir5(join14(root, ".gitifact")).catch((e) => {
|
|
28772
29208
|
if (e.code === "ENOENT") return [];
|
|
28773
29209
|
throw e;
|
|
28774
29210
|
});
|
|
@@ -28837,16 +29273,16 @@ ${lineEndings}${newer}` : JSON.stringify(dto) + "\n");
|
|
|
28837
29273
|
|
|
28838
29274
|
// src/commands/documents.ts
|
|
28839
29275
|
import { readFile as readFile13 } from "node:fs/promises";
|
|
28840
|
-
import { join as
|
|
29276
|
+
import { join as join16 } from "node:path";
|
|
28841
29277
|
|
|
28842
29278
|
// src/commands/project.ts
|
|
28843
|
-
import { join as
|
|
29279
|
+
import { join as join15 } from "node:path";
|
|
28844
29280
|
var operations = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"];
|
|
28845
29281
|
async function openProject(cwd, options = {}) {
|
|
28846
29282
|
const reader = storeReader(cwd);
|
|
28847
29283
|
const location = await reader.location();
|
|
28848
29284
|
if (options.writing) {
|
|
28849
|
-
for (const marker2 of operations) if (await fileInfo(
|
|
29285
|
+
for (const marker2 of operations) if (await fileInfo(join15(location.gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
|
|
28850
29286
|
if (await reader.hasUnmerged()) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
28851
29287
|
}
|
|
28852
29288
|
const raw = await readConfigFile(location.root);
|
|
@@ -28877,6 +29313,125 @@ function documentsOf(files) {
|
|
|
28877
29313
|
return docs;
|
|
28878
29314
|
}
|
|
28879
29315
|
|
|
29316
|
+
// src/queries/filters.ts
|
|
29317
|
+
function byAuthor(author) {
|
|
29318
|
+
if (author === void 0) return () => true;
|
|
29319
|
+
const wanted = author.trim().toLowerCase();
|
|
29320
|
+
return (change2) => change2.author.toLowerCase() === wanted || change2.email.toLowerCase() === wanted;
|
|
29321
|
+
}
|
|
29322
|
+
async function since(project, value, head) {
|
|
29323
|
+
if (value === void 0) return () => true;
|
|
29324
|
+
if (/^\d{4}-\d\d-\d\d/.test(value)) {
|
|
29325
|
+
const from = Date.parse(value);
|
|
29326
|
+
if (!Number.isFinite(from)) throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29327
|
+
return (change2) => Date.parse(change2.date) >= from;
|
|
29328
|
+
}
|
|
29329
|
+
let commit;
|
|
29330
|
+
try {
|
|
29331
|
+
commit = await project.reader.resolve(value);
|
|
29332
|
+
} catch {
|
|
29333
|
+
throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29334
|
+
}
|
|
29335
|
+
if (!head) return () => false;
|
|
29336
|
+
const after = new Set(project.reader.decode(await project.reader.run(["rev-list", `${commit}..${head}`, "--"])).split("\n").filter(Boolean));
|
|
29337
|
+
return (change2) => after.has(change2.commit);
|
|
29338
|
+
}
|
|
29339
|
+
async function sinceRange(project, value, head) {
|
|
29340
|
+
if (value === void 0) return {};
|
|
29341
|
+
if (/^\d{4}-\d\d-\d\d/.test(value)) {
|
|
29342
|
+
const from = Date.parse(value);
|
|
29343
|
+
if (!Number.isFinite(from)) throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29344
|
+
return { from };
|
|
29345
|
+
}
|
|
29346
|
+
let commit;
|
|
29347
|
+
try {
|
|
29348
|
+
commit = await project.reader.resolve(value);
|
|
29349
|
+
} catch {
|
|
29350
|
+
throw new CommandError("INVALID_VALUE", t2("list.since", { value }));
|
|
29351
|
+
}
|
|
29352
|
+
if (!head) return { commits: [] };
|
|
29353
|
+
return { commits: project.reader.decode(await project.reader.run(["rev-list", `${commit}..${head}`, "--"])).split("\n").filter(Boolean) };
|
|
29354
|
+
}
|
|
29355
|
+
var contains = (query) => {
|
|
29356
|
+
const wanted = query?.trim().toLowerCase();
|
|
29357
|
+
return (...texts) => !wanted || texts.some((value) => value?.toLowerCase().includes(wanted));
|
|
29358
|
+
};
|
|
29359
|
+
|
|
29360
|
+
// src/queries/specs.ts
|
|
29361
|
+
var titledSources = (d, byId) => d.sources.map((s) => "id" in s ? { id: s.id, title: byId.get(s.id)?.title ?? null, ...s.note ? { note: s.note } : {} } : s);
|
|
29362
|
+
async function listSpecs(source2, filter, request) {
|
|
29363
|
+
const listed3 = await source2.cache.documents.list();
|
|
29364
|
+
const problems = listed3.problems;
|
|
29365
|
+
const states = await documentStates(source2, listed3.documents);
|
|
29366
|
+
const documents = states.documents;
|
|
29367
|
+
const byId = new Map(documents.map((d) => [d.id, d]));
|
|
29368
|
+
const arranged = arrangeDocuments(documents);
|
|
29369
|
+
let features = arranged.features;
|
|
29370
|
+
if (filter.feature !== void 0) {
|
|
29371
|
+
features = features.filter((f) => f.index.feature === filter.feature);
|
|
29372
|
+
if (!features.length) throw new CommandError("UNKNOWN_FEATURE", t2("docs.unknownFeature", { feature: filter.feature }));
|
|
29373
|
+
}
|
|
29374
|
+
const titled = (id) => ({ id, title: byId.get(id)?.title ?? null });
|
|
29375
|
+
const designsOf = /* @__PURE__ */ new Map();
|
|
29376
|
+
for (const f of arranged.features) for (const d of f.designs) for (const r of d.requirements) designsOf.set(r, [...designsOf.get(r) ?? [], titled(d.id)]);
|
|
29377
|
+
const historyNeeded = filter.sort === "updated" || filter.changedSince !== void 0 || filter.author !== void 0 || !!filter.withUpdated;
|
|
29378
|
+
const latest = /* @__PURE__ */ new Map();
|
|
29379
|
+
const newest = /* @__PURE__ */ new Map();
|
|
29380
|
+
let touched;
|
|
29381
|
+
if (historyNeeded) {
|
|
29382
|
+
const head = await source2.head();
|
|
29383
|
+
const changes2 = head ? await source2.cache.history.changesOf(head) : [];
|
|
29384
|
+
changes2.forEach((c, rank) => {
|
|
29385
|
+
if (!latest.has(c.id)) {
|
|
29386
|
+
latest.set(c.id, { commit: c.commit, date: c.date, author: c.author, email: c.email });
|
|
29387
|
+
newest.set(c.id, rank);
|
|
29388
|
+
}
|
|
29389
|
+
});
|
|
29390
|
+
if (filter.changedSince !== void 0 || filter.author !== void 0) {
|
|
29391
|
+
const after = await since(source2, filter.changedSince, head);
|
|
29392
|
+
const by = byAuthor(filter.author);
|
|
29393
|
+
touched = new Set(changes2.filter((c) => after(c) && by(c)).map((c) => c.id));
|
|
29394
|
+
}
|
|
29395
|
+
}
|
|
29396
|
+
const matched = filter.q !== void 0 ? await source2.cache.documents.matching(filter.q) : void 0;
|
|
29397
|
+
const rows = features.flatMap((f) => [f.index, ...f.requirements, ...f.designs].filter(
|
|
29398
|
+
(doc) => (filter.type === void 0 || doc.kind === filter.type) && (!filter.withoutDesign || !f.designs.length) && (!filter.uncovered || doc.kind === "requirement" && !designsOf.has(doc.id)) && (!filter.draft || doc.draft) && (!touched || touched.has(doc.id)) && (!matched || matched.has(doc.id))
|
|
29399
|
+
).map((doc) => ({
|
|
29400
|
+
id: doc.id,
|
|
29401
|
+
kind: doc.kind,
|
|
29402
|
+
feature: doc.feature,
|
|
29403
|
+
path: doc.path,
|
|
29404
|
+
title: doc.title,
|
|
29405
|
+
description: doc.description,
|
|
29406
|
+
...doc.kind === "requirement" || doc.kind === "design" ? { order: doc.order } : {},
|
|
29407
|
+
...doc.draft ? { draft: true } : {},
|
|
29408
|
+
state: states.state(doc.id),
|
|
29409
|
+
...states.previousPath(doc.id) ? { previousPath: states.previousPath(doc.id) } : {},
|
|
29410
|
+
...doc.kind === "design" ? { requirements: doc.requirements.map(titled), sources: titledSources(doc, byId) } : {},
|
|
29411
|
+
...doc.kind === "requirement" ? { designs: designsOf.get(doc.id) ?? [] } : {},
|
|
29412
|
+
...historyNeeded ? { updated: latest.get(doc.id) ?? null } : {},
|
|
29413
|
+
...matched ? { line: matched.get(doc.id) } : {}
|
|
29414
|
+
})));
|
|
29415
|
+
if (filter.sort === "title") rows.sort((a, b) => a.title.localeCompare(b.title));
|
|
29416
|
+
if (filter.sort === "updated") rows.sort((a, b) => (newest.get(a.id) ?? Infinity) - (newest.get(b.id) ?? Infinity));
|
|
29417
|
+
const grouped = filter.sort === "order";
|
|
29418
|
+
const groups = features.map((f) => ({ id: f.index.id, rows: rows.filter((r) => r.feature === f.index.feature) })).filter((g) => g.rows.length);
|
|
29419
|
+
const page = grouped ? pageOf(groups, (g) => g.id, request) : pageOf(rows, (r) => r.id, request);
|
|
29420
|
+
if (!page) return void 0;
|
|
29421
|
+
const shown = grouped ? page.rows.flatMap((g) => g.rows) : page.rows;
|
|
29422
|
+
return {
|
|
29423
|
+
rows: shown,
|
|
29424
|
+
features,
|
|
29425
|
+
byId,
|
|
29426
|
+
states,
|
|
29427
|
+
grouped,
|
|
29428
|
+
problems,
|
|
29429
|
+
page: { rows: page.rows, total: page.total, next: page.next },
|
|
29430
|
+
unit: grouped ? "feature" : "document",
|
|
29431
|
+
orphans: filter.feature === void 0 ? arranged.orphans : []
|
|
29432
|
+
};
|
|
29433
|
+
}
|
|
29434
|
+
|
|
28880
29435
|
// src/commands/documents.ts
|
|
28881
29436
|
var draftMark = (doc) => doc.draft ? " (" + t2("docs.draft") + ")" : "";
|
|
28882
29437
|
var stateLabel = {
|
|
@@ -28887,12 +29442,11 @@ var stateLabel = {
|
|
|
28887
29442
|
var stateMark = (state2) => state2 === "committed" ? "" : " (" + stateLabel[state2]() + ")";
|
|
28888
29443
|
var line = (doc) => `${doc.id} ${doc.title}${draftMark(doc)} \u2014 ${doc.description}`;
|
|
28889
29444
|
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
29445
|
async function working(project) {
|
|
28892
29446
|
const { documents } = await project.cache.documents.list();
|
|
28893
29447
|
return {
|
|
28894
29448
|
byId: new Map(documents.map((d) => [d.id, d])),
|
|
28895
|
-
read: (path3) => readFile13(
|
|
29449
|
+
read: (path3) => readFile13(join16(project.root, ...path3.split("/")), "utf8"),
|
|
28896
29450
|
referencing: async (id) => [...new Set((await project.cache.documents.referencing(id)).map((r) => r.from))]
|
|
28897
29451
|
};
|
|
28898
29452
|
}
|
|
@@ -29001,48 +29555,6 @@ function checkFields(fields2, columns3) {
|
|
|
29001
29555
|
if (unknown2.length || !fields2.length) throw new CommandError("INVALID_VALUE", t2("list.unknownFields", { fields: unknown2.join(", "), columns: columns3.join(", ") }));
|
|
29002
29556
|
return [...new Set(fields2)];
|
|
29003
29557
|
}
|
|
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
29558
|
function cell(value) {
|
|
29047
29559
|
if (value === void 0 || value === null || value === false) return "";
|
|
29048
29560
|
if (Array.isArray(value)) return value.map((v) => typeof v === "object" && v !== null ? "id" in v ? v.id : "title" in v ? v.title : JSON.stringify(v) : String(v)).join(",");
|
|
@@ -29061,18 +29573,14 @@ function selected(rows, fields2) {
|
|
|
29061
29573
|
text: rows.length ? text2(rows.map((row) => fields2.map((f) => cell(row[f])).join(" "))) : ""
|
|
29062
29574
|
};
|
|
29063
29575
|
}
|
|
29064
|
-
var PAGE_SIZE = 20;
|
|
29065
29576
|
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 };
|
|
29577
|
+
return pageOr(pageOf(rows, keyOf, options), options);
|
|
29578
|
+
}
|
|
29579
|
+
function pageOr(page, options) {
|
|
29580
|
+
return page ?? cursorGone(options);
|
|
29581
|
+
}
|
|
29582
|
+
function cursorGone(options) {
|
|
29583
|
+
throw new CommandError("INVALID_VALUE", t2("list.after", { cursor: options.after ?? "" }));
|
|
29076
29584
|
}
|
|
29077
29585
|
var pageLine = (page, before = 0, unit = t2("list.items")) => page.next === null ? [] : [t2("list.more", { shown: before + page.rows.length, total: page.total, unit, cursor: page.next })];
|
|
29078
29586
|
function aside(format2, lines) {
|
|
@@ -29088,67 +29596,10 @@ var kindLabel = { feature: () => t2("docs.feature"), requirement: () => t2("docs
|
|
|
29088
29596
|
var runSpecsList = (options) => runCommand("specs", options.format, async () => {
|
|
29089
29597
|
const fields2 = checkFields(options.fields, columns);
|
|
29090
29598
|
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;
|
|
29599
|
+
const listed3 = await listSpecs(project, { ...options, withUpdated: !!fields2?.includes("updated") }, options) ?? cursorGone(options);
|
|
29600
|
+
const { rows: shown, features, byId, states, grouped, problems, orphans, page } = listed3;
|
|
29149
29601
|
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 : [];
|
|
29602
|
+
const paging = { total: page.total, next: page.next, unit: listed3.unit };
|
|
29152
29603
|
if (fields2) {
|
|
29153
29604
|
const picked = selected(shown, fields2);
|
|
29154
29605
|
return { json: { documents: picked.json, orphans, problems, page: paging }, text: picked.text + aside(options.format, more) };
|
|
@@ -29192,27 +29643,23 @@ var runSpecsShow = (ids, options) => runCommand("specs", options.format, async (
|
|
|
29192
29643
|
});
|
|
29193
29644
|
var runSpecsNew = (kind2, path3, options) => runCommand("specs", options.format, async () => createDocument(await openProject(process.cwd()), kind2, path3, options));
|
|
29194
29645
|
|
|
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"];
|
|
29646
|
+
// src/queries/instructions.ts
|
|
29647
|
+
import { lstat as lstat12 } from "node:fs/promises";
|
|
29648
|
+
import { join as join17 } from "node:path";
|
|
29200
29649
|
var AGENTS = "AGENTS.md";
|
|
29201
|
-
|
|
29202
|
-
const
|
|
29203
|
-
const project = await openProject(process.cwd());
|
|
29204
|
-
const listed3 = await project.cache.documents.list();
|
|
29650
|
+
async function listInstructions(source2, options, request) {
|
|
29651
|
+
const listed3 = await source2.cache.documents.list();
|
|
29205
29652
|
const problems = listed3.problems;
|
|
29206
|
-
const states = await documentStates(
|
|
29653
|
+
const states = await documentStates(source2, listed3.documents);
|
|
29207
29654
|
const documents = states.documents;
|
|
29208
|
-
const agents2 = { path: AGENTS, exists: !!(await
|
|
29209
|
-
const historyNeeded = options.sort === "updated" || options.author !== void 0 || !!
|
|
29655
|
+
const agents2 = { path: AGENTS, exists: !!(await lstat12(join17(source2.root, AGENTS)).catch(() => void 0))?.isFile() };
|
|
29656
|
+
const historyNeeded = options.sort === "updated" || options.author !== void 0 || !!options.withUpdated;
|
|
29210
29657
|
const latest = /* @__PURE__ */ new Map();
|
|
29211
29658
|
const newest = /* @__PURE__ */ new Map();
|
|
29212
29659
|
let touched;
|
|
29213
29660
|
if (historyNeeded) {
|
|
29214
|
-
const head = await
|
|
29215
|
-
const changes2 = head ? await
|
|
29661
|
+
const head = await source2.head();
|
|
29662
|
+
const changes2 = head ? await source2.cache.history.changesOf(head) : [];
|
|
29216
29663
|
changes2.forEach((c, rank) => {
|
|
29217
29664
|
if (!latest.has(c.id)) {
|
|
29218
29665
|
latest.set(c.id, { commit: c.commit, date: c.date, author: c.author, email: c.email });
|
|
@@ -29224,11 +29671,11 @@ var runInstructionsList = (options) => runCommand("instructions", options.format
|
|
|
29224
29671
|
touched = new Set(changes2.filter(by).map((c) => c.id));
|
|
29225
29672
|
}
|
|
29226
29673
|
}
|
|
29227
|
-
const matched = options.q !== void 0 ? await
|
|
29674
|
+
const matched = options.q !== void 0 ? await source2.cache.documents.matching(options.q) : void 0;
|
|
29228
29675
|
const instructions2 = documents.filter((d) => d.kind === "instruction").filter((d) => (!touched || touched.has(d.id)) && (!matched || matched.has(d.id))).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
29229
29676
|
const rows = await Promise.all(instructions2.map(async (d) => {
|
|
29230
29677
|
const state2 = states.state(d.id);
|
|
29231
|
-
const { files, limited } = state2 === "deleted" ? { files: [], limited: false } : await readInstructionFiles(
|
|
29678
|
+
const { files, limited } = state2 === "deleted" ? { files: [], limited: false } : await readInstructionFiles(source2.root, d.path);
|
|
29232
29679
|
return {
|
|
29233
29680
|
id: d.id,
|
|
29234
29681
|
name: d.name,
|
|
@@ -29245,7 +29692,19 @@ var runInstructionsList = (options) => runCommand("instructions", options.format
|
|
|
29245
29692
|
};
|
|
29246
29693
|
}));
|
|
29247
29694
|
if (options.sort === "updated") rows.sort((a, b) => (newest.get(a.id) ?? Infinity) - (newest.get(b.id) ?? Infinity));
|
|
29248
|
-
const page =
|
|
29695
|
+
const page = pageOf(rows, (r) => r.id, request);
|
|
29696
|
+
if (!page) return void 0;
|
|
29697
|
+
return { agents: agents2, problems, page };
|
|
29698
|
+
}
|
|
29699
|
+
|
|
29700
|
+
// src/commands/instructions.ts
|
|
29701
|
+
var instructionSorts = ["name", "updated"];
|
|
29702
|
+
var columns2 = ["id", "name", "path", "title", "description", "draft", "state", "previousPath", "files", "updated", "line"];
|
|
29703
|
+
var runInstructionsList = (options) => runCommand("instructions", options.format, async () => {
|
|
29704
|
+
const fields2 = checkFields(options.fields, columns2);
|
|
29705
|
+
const project = await openProject(process.cwd());
|
|
29706
|
+
const listed3 = await listInstructions(project, { ...options, withUpdated: !!fields2?.includes("updated") }, options) ?? cursorGone(options);
|
|
29707
|
+
const { agents: agents2, problems, page } = listed3;
|
|
29249
29708
|
const shown = page.rows;
|
|
29250
29709
|
const paging = { total: page.total, next: page.next, unit: "instruction" };
|
|
29251
29710
|
const unreadable2 = problems.filter((p) => p.path.startsWith(INSTRUCTIONS_ROOT + "/"));
|
|
@@ -29291,9 +29750,9 @@ var runInstructionsShow = (targets, options) => runCommand("instructions", optio
|
|
|
29291
29750
|
var runInstructionsNew = (name2, options) => runCommand("instructions", options.format, async () => createDocument(await openProject(process.cwd()), "instruction", name2, options));
|
|
29292
29751
|
|
|
29293
29752
|
// src/adapters/filesystem/document-warnings.ts
|
|
29294
|
-
import { lstat as
|
|
29295
|
-
import { join as
|
|
29296
|
-
var info2 = async (path3) =>
|
|
29753
|
+
import { lstat as lstat13, readdir as readdir6 } from "node:fs/promises";
|
|
29754
|
+
import { join as join18 } from "node:path";
|
|
29755
|
+
var info2 = async (path3) => lstat13(path3).catch((e) => {
|
|
29297
29756
|
if (e.code === "ENOENT") return void 0;
|
|
29298
29757
|
throw e;
|
|
29299
29758
|
});
|
|
@@ -29301,11 +29760,11 @@ var posix = (path3) => path3.split("\\").join("/");
|
|
|
29301
29760
|
async function listAssets(root) {
|
|
29302
29761
|
const out = [];
|
|
29303
29762
|
async function visit2(relative, depth) {
|
|
29304
|
-
const stat3 = await info2(
|
|
29763
|
+
const stat3 = await info2(join18(root, relative));
|
|
29305
29764
|
if (!stat3 || stat3.isSymbolicLink()) return;
|
|
29306
29765
|
if (stat3.isDirectory()) {
|
|
29307
29766
|
if (depth > 8 || out.length > 4e3) return;
|
|
29308
|
-
for (const name2 of (await readdir6(
|
|
29767
|
+
for (const name2 of (await readdir6(join18(root, relative))).sort()) await visit2(relative + "/" + name2, depth + 1);
|
|
29309
29768
|
} else if (stat3.isFile()) out.push({ path: posix(relative), bytes: stat3.size });
|
|
29310
29769
|
}
|
|
29311
29770
|
await visit2(ASSETS_DIR, 0);
|
|
@@ -29314,7 +29773,7 @@ async function listAssets(root) {
|
|
|
29314
29773
|
async function readDocumentWarnings(root, documents) {
|
|
29315
29774
|
const outside = [...new Set(documentBodyLinks(documents).map((l) => l.target).filter((t3) => !t3.startsWith(".gitifact/") || t3.startsWith(INSTRUCTIONS_ROOT + "/")))];
|
|
29316
29775
|
const existing = /* @__PURE__ */ new Set();
|
|
29317
|
-
for (const target of outside) if ((await info2(
|
|
29776
|
+
for (const target of outside) if ((await info2(join18(root, ...target.split("/"))))?.isFile()) existing.add(target);
|
|
29318
29777
|
return documentWarnings(documents, await listAssets(root), existing);
|
|
29319
29778
|
}
|
|
29320
29779
|
|
|
@@ -29339,29 +29798,29 @@ var runCheck = (options) => runCommand("check", options.format, async () => {
|
|
|
29339
29798
|
// src/commands/changes.ts
|
|
29340
29799
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
29341
29800
|
import { mkdir as mkdir6, open as open5, readFile as readFile16, rename as rename3, rmdir, unlink as unlink4, writeFile as writeFile4 } from "node:fs/promises";
|
|
29342
|
-
import { join as
|
|
29801
|
+
import { join as join21 } from "node:path";
|
|
29343
29802
|
|
|
29344
29803
|
// src/adapters/filesystem/agent-inputs.ts
|
|
29345
29804
|
import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
|
|
29346
|
-
import { lstat as
|
|
29805
|
+
import { lstat as lstat14, mkdir as mkdir5, readdir as readdir7, realpath as realpath3, unlink as unlink3, writeFile as writeFile3 } from "node:fs/promises";
|
|
29347
29806
|
import { tmpdir } from "node:os";
|
|
29348
|
-
import { basename, dirname as dirname2, join as
|
|
29807
|
+
import { basename, dirname as dirname2, join as join19 } from "node:path";
|
|
29349
29808
|
var maxAge = 7 * 24 * 60 * 60 * 1e3;
|
|
29350
|
-
var info3 = (path3) =>
|
|
29809
|
+
var info3 = (path3) => lstat14(path3).catch((e) => {
|
|
29351
29810
|
if (e.code === "ENOENT") return void 0;
|
|
29352
29811
|
throw e;
|
|
29353
29812
|
});
|
|
29354
29813
|
var comparable = (path3) => process.platform === "win32" ? path3.toLowerCase() : path3;
|
|
29355
29814
|
async function candidates(root, controls) {
|
|
29356
29815
|
const key = createHash4("sha256").update(comparable(await realpath3(root))).digest("hex").slice(0, 16);
|
|
29357
|
-
return { system:
|
|
29816
|
+
return { system: join19((controls.tmpdir ?? tmpdir)(), "gitifact", key), project: join19(root, ".gitifact", "tmp") };
|
|
29358
29817
|
}
|
|
29359
29818
|
async function writable(directory) {
|
|
29360
29819
|
try {
|
|
29361
29820
|
await mkdir5(directory, { recursive: true });
|
|
29362
29821
|
const stat3 = await info3(directory);
|
|
29363
29822
|
if (!stat3?.isDirectory() || stat3.isSymbolicLink()) return false;
|
|
29364
|
-
const probe =
|
|
29823
|
+
const probe = join19(directory, ".probe-" + randomBytes2(8).toString("hex"));
|
|
29365
29824
|
await writeFile3(probe, "", { flag: "wx" });
|
|
29366
29825
|
await unlink3(probe);
|
|
29367
29826
|
return true;
|
|
@@ -29375,7 +29834,7 @@ async function prepareAgentInputs(root, controls = {}) {
|
|
|
29375
29834
|
if (await writable(system)) directory = system;
|
|
29376
29835
|
else if (await writable(project)) {
|
|
29377
29836
|
directory = project;
|
|
29378
|
-
await writeFile3(
|
|
29837
|
+
await writeFile3(join19(project, ".gitignore"), "*\n", { flag: "wx" }).catch((e) => {
|
|
29379
29838
|
if (e.code !== "EEXIST") throw e;
|
|
29380
29839
|
});
|
|
29381
29840
|
}
|
|
@@ -29383,10 +29842,10 @@ async function prepareAgentInputs(root, controls = {}) {
|
|
|
29383
29842
|
const now = (controls.now ?? Date.now)();
|
|
29384
29843
|
for (const name2 of await readdir7(directory).catch(() => [])) {
|
|
29385
29844
|
if (name2 === ".gitignore") continue;
|
|
29386
|
-
const stat3 = await info3(
|
|
29387
|
-
if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await unlink3(
|
|
29845
|
+
const stat3 = await info3(join19(directory, name2)).catch(() => void 0);
|
|
29846
|
+
if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await unlink3(join19(directory, name2)).catch(() => void 0);
|
|
29388
29847
|
}
|
|
29389
|
-
return { commit:
|
|
29848
|
+
return { commit: join19(directory, "commit.json") };
|
|
29390
29849
|
}
|
|
29391
29850
|
async function discardAgentInput(root, file2, controls = {}) {
|
|
29392
29851
|
try {
|
|
@@ -29408,8 +29867,8 @@ async function discardAgentInput(root, file2, controls = {}) {
|
|
|
29408
29867
|
|
|
29409
29868
|
// src/commands/commit-files.ts
|
|
29410
29869
|
import { createHash as createHash5 } from "node:crypto";
|
|
29411
|
-
import { lstat as
|
|
29412
|
-
import { join as
|
|
29870
|
+
import { lstat as lstat15, readFile as readFile14 } from "node:fs/promises";
|
|
29871
|
+
import { join as join20 } from "node:path";
|
|
29413
29872
|
var fail3 = (message) => {
|
|
29414
29873
|
throw new CommandError("INVALID_COMMIT", message);
|
|
29415
29874
|
};
|
|
@@ -29418,7 +29877,7 @@ var optional2 = (path3) => readFile14(path3).catch((e) => {
|
|
|
29418
29877
|
if (e.code === "ENOENT") return null;
|
|
29419
29878
|
throw e;
|
|
29420
29879
|
});
|
|
29421
|
-
var info4 = (path3) =>
|
|
29880
|
+
var info4 = (path3) => lstat15(path3).catch((e) => {
|
|
29422
29881
|
if (e.code === "ENOENT") return void 0;
|
|
29423
29882
|
throw e;
|
|
29424
29883
|
});
|
|
@@ -29444,11 +29903,11 @@ async function fingerprint(root, path3) {
|
|
|
29444
29903
|
validPath2(path3);
|
|
29445
29904
|
const parts = path3.split("/");
|
|
29446
29905
|
for (let i = 1; i <= parts.length; i++) {
|
|
29447
|
-
const target =
|
|
29906
|
+
const target = join20(root, ...parts.slice(0, i));
|
|
29448
29907
|
const stat3 = await info4(target);
|
|
29449
29908
|
if (!stat3) return null;
|
|
29450
29909
|
if (stat3.isSymbolicLink() || (i < parts.length ? !stat3.isDirectory() : !stat3.isFile())) fail3(t2("commitFiles.linkOrDirectory"));
|
|
29451
|
-
if (i < parts.length && await info4(
|
|
29910
|
+
if (i < parts.length && await info4(join20(target, ".git"))) fail3(t2("commitFiles.nestedRepository"));
|
|
29452
29911
|
if (i === parts.length) {
|
|
29453
29912
|
if (stat3.nlink !== 1 || stat3.size > 16 * 1024 * 1024) fail3(t2("commitFiles.hardLinkOrSize"));
|
|
29454
29913
|
return hash2(Buffer.concat([Buffer.from(String(stat3.mode & 73) + "\0"), await readFile14(target)]));
|
|
@@ -29598,7 +30057,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29598
30057
|
...stdin ? { input: stdin } : {}
|
|
29599
30058
|
});
|
|
29600
30059
|
const staged = async (index) => (await git(["diff", "--cached", "--ita-visible-in-index", "--name-only", "--no-renames", "-z"], index)).toString("utf8").split("\0").filter(Boolean).sort();
|
|
29601
|
-
const busy =
|
|
30060
|
+
const busy = join21(gitDir, "gitifact-changes-commit.lock");
|
|
29602
30061
|
if (await info4(busy)) fail3(t2("commit.busy", { path: busy }));
|
|
29603
30062
|
if ((await staged()).length) fail3(t2("commit.existingStaging"));
|
|
29604
30063
|
const indexHash = hash2(await optional2(indexPath) ?? Buffer.alloc(0));
|
|
@@ -29637,7 +30096,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29637
30096
|
if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
|
|
29638
30097
|
return { outcome: "dry-run", committed: false, ...summary, commit: void 0 };
|
|
29639
30098
|
}
|
|
29640
|
-
const temporary =
|
|
30099
|
+
const temporary = join21(gitDir, "gitifact-commit-index-" + randomUUID5());
|
|
29641
30100
|
let owned = false;
|
|
29642
30101
|
let uncertain = false;
|
|
29643
30102
|
let commitStarted = false;
|
|
@@ -29656,7 +30115,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29656
30115
|
if (hash2(original ?? Buffer.alloc(0)) !== indexHash || (await staged()).length) fail3(t2("commit.indexChanged"));
|
|
29657
30116
|
const before = await hashes([...started.keys()]);
|
|
29658
30117
|
for (const [path3, value] of started) if (before.get(path3) !== value) fail3(t2("commit.fileChangedRunning", { path: path3 }));
|
|
29659
|
-
await writeFile4(
|
|
30118
|
+
await writeFile4(join21(busy, "recovery.json"), JSON.stringify({ before: base, originalIndex: original?.toString("base64") ?? null, temporary, paths: selected2, message }));
|
|
29660
30119
|
const locked = await hashes([.../* @__PURE__ */ new Set([...selected2, ...contextPaths])]);
|
|
29661
30120
|
if (original) await writeFile4(temporary, original, { flag: "wx" });
|
|
29662
30121
|
else await git(["read-tree", "--empty"], temporary);
|
|
@@ -29673,14 +30132,14 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29673
30132
|
const size = Number(header[2]);
|
|
29674
30133
|
if (end < 0 || header[1] !== "blob" || !Number.isSafeInteger(size) || output2[end + 1 + size] !== 10) fail3(t2("commit.stagedUnreadable", { path: path3 }));
|
|
29675
30134
|
const blob = output2.subarray(end + 1, end + 1 + size);
|
|
29676
|
-
const file2 = await readFile16(
|
|
30135
|
+
const file2 = await readFile16(join21(root, path3));
|
|
29677
30136
|
if (!blob.equals(file2) && !blob.equals(Buffer.from(file2.toString("latin1").replace(/\r\n/g, "\n"), "latin1"))) fail3(t2("commit.filterChanged"));
|
|
29678
30137
|
offset = end + size + 2;
|
|
29679
30138
|
}
|
|
29680
30139
|
}
|
|
29681
30140
|
const again = await hashes([...locked.keys()]);
|
|
29682
30141
|
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(
|
|
30142
|
+
for (const name2 of integration) if (await info4(join21(gitDir, name2))) fail3(t2("commit.integrationStarted"));
|
|
29684
30143
|
if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
|
|
29685
30144
|
const tree = (await git(["write-tree"], temporary)).toString("utf8").trim();
|
|
29686
30145
|
commitStarted = true;
|
|
@@ -29720,7 +30179,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29720
30179
|
await unlink4(temporary).catch(() => {
|
|
29721
30180
|
});
|
|
29722
30181
|
if (owned) {
|
|
29723
|
-
await unlink4(
|
|
30182
|
+
await unlink4(join21(busy, "recovery.json")).catch(() => {
|
|
29724
30183
|
});
|
|
29725
30184
|
await rmdir(busy);
|
|
29726
30185
|
}
|
|
@@ -29731,7 +30190,7 @@ async function commitChanges(cwd, input2, dryRun) {
|
|
|
29731
30190
|
|
|
29732
30191
|
// src/commands/records.ts
|
|
29733
30192
|
import { readFile as readFile17 } from "node:fs/promises";
|
|
29734
|
-
import { join as
|
|
30193
|
+
import { join as join22 } from "node:path";
|
|
29735
30194
|
var runRecordsNew = (options) => runCommand("records", options.format, async () => {
|
|
29736
30195
|
if (/[\r\n]/.test(options.title)) throw new CommandError("INVALID_VALUE", t2("docs.singleLine", { field: "title" }));
|
|
29737
30196
|
const docs = [...new Set(options.docs.flatMap((d) => d.split(",")).map((d) => d.trim()).filter(Boolean))];
|
|
@@ -29767,7 +30226,7 @@ var runRecordsShow = (ids, options) => runCommand("records", options.format, asy
|
|
|
29767
30226
|
const shown = await Promise.all([...new Set(ids)].map(async (id) => {
|
|
29768
30227
|
const path3 = await findRecord(project.root, id);
|
|
29769
30228
|
if (!path3) return { id, missing: true };
|
|
29770
|
-
const source2 = await readFile17(
|
|
30229
|
+
const source2 = await readFile17(join22(project.root, ...path3.split("/")), "utf8");
|
|
29771
30230
|
const log = project.reader.decode(await project.reader.run(["log", "-1", "--diff-filter=A", "--format=%H%x00%aN%x00%aI", "--", path3])).trim();
|
|
29772
30231
|
const [commit, author, date5] = log.split("\0");
|
|
29773
30232
|
return { id, path: path3, text: source2, commit: commit ? { commit, author, date: date5 } : null };
|
|
@@ -29877,7 +30336,7 @@ async function readThrough(project, head, filter, id) {
|
|
|
29877
30336
|
// src/commands/feedback.ts
|
|
29878
30337
|
import { readFile as readFile18 } from "node:fs/promises";
|
|
29879
30338
|
import { release as release3 } from "node:os";
|
|
29880
|
-
import { dirname as dirname3, join as
|
|
30339
|
+
import { dirname as dirname3, join as join23 } from "node:path";
|
|
29881
30340
|
|
|
29882
30341
|
// src/adapters/github/issue.ts
|
|
29883
30342
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -29944,7 +30403,7 @@ function parseFeedback(input2) {
|
|
|
29944
30403
|
}
|
|
29945
30404
|
async function schemaVersion(from) {
|
|
29946
30405
|
for (let folder = from; ; folder = dirname3(folder)) {
|
|
29947
|
-
const config2 = await readFile18(
|
|
30406
|
+
const config2 = await readFile18(join23(folder, ".gitifact", "config.json"), "utf8").catch(() => void 0);
|
|
29948
30407
|
if (config2 !== void 0) {
|
|
29949
30408
|
try {
|
|
29950
30409
|
const value = JSON.parse(config2).schemaVersion;
|
|
@@ -30170,11 +30629,11 @@ for (let i = 0; i < languageArgs.length; i++) {
|
|
|
30170
30629
|
if (value === "ko" || value === "en") selectedLanguage = value;
|
|
30171
30630
|
}
|
|
30172
30631
|
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.
|
|
30632
|
+
var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.8.2").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
|
|
30174
30633
|
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.
|
|
30634
|
+
program2.command("browser").description(t2("help.browser")).allowExcessArguments(false).option("--port <port>", t2("help.browserPort"), parsePort, 0).option("--dev", t2("help.browserDev")).action((options) => runBrowser(options, "0.8.2"));
|
|
30635
|
+
program2.command("init").description(t2("help.init")).allowExcessArguments(false).option("--dry-run", t2("help.initDryRun")).addOption(new Option("--agent <agent>", t2("help.initAgent")).choices([...agentPresetNames])).addOption(new Option("--remove-agents", t2("help.initRemoveAgents")).conflicts("skipAgents")).option("--skip-agents", t2("help.initSkipAgents")).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).action((options) => runInit(options, "0.8.2"));
|
|
30636
|
+
program2.command("update").description(t2("help.update")).allowExcessArguments(false).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).option("--commit", t2("help.updateCommit")).addOption(new Option("--check", t2("help.updateCheck")).conflicts("commit")).action((options) => options.check ? runUpdateCheck(options.format, "0.8.2") : runUpdate(options, "0.8.2"));
|
|
30178
30637
|
var listed2 = (command, sorts) => {
|
|
30179
30638
|
command.allowExcessArguments(false).option("--q <words>", t2("help.listQ")).option("--author <name>", t2("help.listAuthor"));
|
|
30180
30639
|
if (sorts) command.addOption(new Option("--sort <key>", t2("help.listSort")).choices([...sorts]).default(sorts[0]));
|
|
@@ -30196,7 +30655,7 @@ program2.command("check").description(t2("help.check")).allowExcessArguments(fal
|
|
|
30196
30655
|
var changes = program2.command("changes").description(t2("help.changes"));
|
|
30197
30656
|
changes.command("list").description(t2("help.changesList")).allowExcessArguments(false).addOption(format()).action((o) => runChangesList(o));
|
|
30198
30657
|
changes.command("commit").description(t2("help.changesCommit")).allowExcessArguments(false).requiredOption("--file <path>", t2("help.changesCommitFile")).option("--dry-run", t2("help.changesCommitDryRun")).addOption(format()).action((o) => runChangesCommit(o));
|
|
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.
|
|
30658
|
+
program2.command("feedback").description(t2("help.feedback")).allowExcessArguments(false).requiredOption("--file <path>", t2("help.feedbackFile")).option("--dry-run", t2("help.feedbackDryRun")).addOption(format()).action((o) => runFeedback(o, "0.8.2"));
|
|
30200
30659
|
var guide = program2.command("guide").description(t2("help.guide"));
|
|
30201
30660
|
guide.command("list").description(t2("help.guideList")).allowExcessArguments(false).addOption(format()).action((o) => runGuideList(o));
|
|
30202
30661
|
guide.command("show").description(t2("help.guideShow")).argument("<topic>", t2("help.guideTopic")).allowExcessArguments(false).addOption(format()).action((topic, o) => runGuideShow(topic, o));
|