gitifact 0.4.4 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/dist/browser/assets/{Markdown-Drk5rJF_.js → Markdown-CqZ4w2H_.js} +3 -3
- package/dist/browser/assets/{MetadataListItem-o7NlLubs.js → MetadataListItem-B6xUZEC3.js} +1 -1
- package/dist/browser/assets/Table-DDKWqCdD.js +3 -0
- package/dist/browser/assets/about-Chl-XBQg.js +3 -0
- package/dist/browser/assets/{routes-D_2lOqSD.js → activity-F2_AhgBS.js} +1 -1
- package/dist/browser/assets/changelog-DrJfyXDZ.js +2 -0
- package/dist/browser/assets/{contributors._email-CsIeETiA.js → contributors._email-CINLTSX2.js} +1 -1
- package/dist/browser/assets/{contributors.index-BVn-RzjO.js → contributors.index-ChB89wcL.js} +1 -1
- package/dist/browser/assets/document-Jy6Buepz.js +9 -0
- package/dist/browser/assets/document-QMuypGH5.css +1 -0
- package/dist/browser/assets/features._featureId-FySsJ2tl.js +1 -0
- package/dist/browser/assets/{requirements-C5TAa2tx.js → features.index-DWfPG8WV.js} +1 -1
- package/dist/browser/assets/git-AnH-8SBT.js +1 -0
- package/dist/browser/assets/index-DXK4gUTr.js +48 -0
- package/dist/browser/assets/page-header-N7A_M6-B.js +124 -0
- package/dist/browser/assets/product-BKOKDrzZ.js +8 -0
- package/dist/browser/assets/product-DNlxFjU7.css +1 -0
- package/dist/browser/assets/product.index-BohDJK9z.js +1 -0
- package/dist/browser/assets/request-state-CO3OLoay.js +1 -0
- package/dist/browser/assets/{features.index-Cf-ct7tk.js → requirements-Dh2NEqNw.js} +1 -1
- package/dist/browser/assets/{settings-Dzak94qo.js → settings-DylPhHVJ.js} +1 -1
- package/dist/browser/assets/wiki-DYxjYiPl.js +1 -0
- package/dist/browser/assets/wiki._documentId-DJ7LAi8J.js +1 -0
- package/dist/browser/assets/wiki.index-DJ7LAi8J.js +1 -0
- package/dist/browser/index.html +11 -11
- package/dist/i18n/ko/block.md +6 -4
- package/dist/i18n/ko/changelog.md +29 -0
- package/dist/i18n/ko/docs/commit.md +15 -5
- package/dist/i18n/ko/docs/design.md +26 -18
- package/dist/i18n/ko/docs/spec.md +22 -16
- package/dist/i18n/ko/docs/wiki.default.md +27 -0
- package/dist/i18n/ko/docs/wiki.md +41 -0
- package/dist/i18n/ko/docs/workflow.md +20 -10
- package/dist/main.js +824 -495
- package/package.json +3 -3
- package/dist/browser/assets/Table-DmxmodQG.js +0 -3
- package/dist/browser/assets/about-BFpb4QWu.js +0 -3
- package/dist/browser/assets/changelog-rtqqVzIW.js +0 -2
- package/dist/browser/assets/document-BGXuvcs6.js +0 -1
- package/dist/browser/assets/document-D41OCO3e.css +0 -1
- package/dist/browser/assets/features._featureId-Bw_4HsoD.js +0 -1
- package/dist/browser/assets/git-E_25yRrM.js +0 -1
- package/dist/browser/assets/guides._documentId-CJIzrRob.js +0 -1
- package/dist/browser/assets/guides.index-Bh0g2XhD.js +0 -1
- package/dist/browser/assets/index-Bmk5GeZg.js +0 -56
- package/dist/browser/assets/page-header-Cmn2UulE.js +0 -126
- package/dist/browser/assets/product-BrvAWtBo.css +0 -1
- package/dist/browser/assets/product-lcmaeBnU.js +0 -8
- package/dist/browser/assets/product.document-BH-jLWyC.js +0 -1
- package/dist/browser/assets/product.index-3Hnsh67g.js +0 -1
- package/dist/browser/assets/request-state-hT3U3nPp.js +0 -1
- package/dist/i18n/ko/docs/product.md +0 -21
package/dist/main.js
CHANGED
|
@@ -1926,13 +1926,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1926
1926
|
* @param {string} source - expected values are default/config/env/cli/implied
|
|
1927
1927
|
* @return {Command} `this` command for chaining
|
|
1928
1928
|
*/
|
|
1929
|
-
setOptionValueWithSource(key, value,
|
|
1929
|
+
setOptionValueWithSource(key, value, source2) {
|
|
1930
1930
|
if (this._storeOptionsAsProperties) {
|
|
1931
1931
|
this[key] = value;
|
|
1932
1932
|
} else {
|
|
1933
1933
|
this._optionValues[key] = value;
|
|
1934
1934
|
}
|
|
1935
|
-
this._optionValueSources[key] =
|
|
1935
|
+
this._optionValueSources[key] = source2;
|
|
1936
1936
|
return this;
|
|
1937
1937
|
}
|
|
1938
1938
|
/**
|
|
@@ -1953,13 +1953,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1953
1953
|
* @return {string}
|
|
1954
1954
|
*/
|
|
1955
1955
|
getOptionValueSourceWithGlobals(key) {
|
|
1956
|
-
let
|
|
1956
|
+
let source2;
|
|
1957
1957
|
this._getCommandAndAncestors().forEach((cmd) => {
|
|
1958
1958
|
if (cmd.getOptionValueSource(key) !== void 0) {
|
|
1959
|
-
|
|
1959
|
+
source2 = cmd.getOptionValueSource(key);
|
|
1960
1960
|
}
|
|
1961
1961
|
});
|
|
1962
|
-
return
|
|
1962
|
+
return source2;
|
|
1963
1963
|
}
|
|
1964
1964
|
/**
|
|
1965
1965
|
* Get user arguments from implied or explicit arguments.
|
|
@@ -2839,8 +2839,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2839
2839
|
const getErrorMessage = (option2) => {
|
|
2840
2840
|
const bestOption = findBestOptionFromValue(option2);
|
|
2841
2841
|
const optionKey = bestOption.attributeName();
|
|
2842
|
-
const
|
|
2843
|
-
if (
|
|
2842
|
+
const source2 = this.getOptionValueSource(optionKey);
|
|
2843
|
+
if (source2 === "env") {
|
|
2844
2844
|
return `environment variable '${bestOption.envVar}'`;
|
|
2845
2845
|
}
|
|
2846
2846
|
return `option '${bestOption.flags}'`;
|
|
@@ -3405,9 +3405,9 @@ var messages_default = {
|
|
|
3405
3405
|
"config.invalidFields": "\uD504\uB85C\uC81D\uD2B8 \uC124\uC815\uC758 \uD544\uB4DC\uB098 \uAC12\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
3406
3406
|
"config.jsonUnreadableShort": "\uC124\uC815 JSON\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
3407
3407
|
"config.schemaFields": "schemaVersion\uACFC baseline \uC124\uC815 \uD544\uB4DC\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
3408
|
-
"document.pathTooLong": "\
|
|
3409
|
-
"document.fileName": "\
|
|
3410
|
-
"document.folderName": "\
|
|
3408
|
+
"document.pathTooLong": "\uC704\uD0A4 \uD398\uC774\uC9C0 \uACBD\uB85C \uAE4A\uC774 \uB610\uB294 \uAE38\uC774 \uD55C\uB3C4\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4: {path}",
|
|
3409
|
+
"document.fileName": "\uC704\uD0A4 \uD398\uC774\uC9C0 \uD30C\uC77C \uC774\uB984\uC740 \uC18C\uBB38\uC790\xB7\uC22B\uC790\xB7\uD558\uC774\uD508\uACFC .md\uC5EC\uC57C \uD569\uB2C8\uB2E4(\uB8E8\uD2B8\uC758 README.md\xB7ARCHITECTURE.md \uAC19\uC740 \uB300\uBB38\uC790 \uC774\uB984 \uC608\uC678): {path}",
|
|
3410
|
+
"document.folderName": "\uC704\uD0A4 \uD3F4\uB354 \uC774\uB984\uC740 \uC18C\uBB38\uC790\xB7\uC22B\uC790\xB7\uD558\uC774\uD508\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4: {path}",
|
|
3411
3411
|
"design.invalidCharacters": "\uC798\uBABB\uB41C \uC124\uACC4 \uBB38\uC790\uC785\uB2C8\uB2E4.",
|
|
3412
3412
|
"design.idMismatch": "\uC124\uACC4\uC758 gitifact-design ID\uB294 \uAC19\uC740 \uAE30\uB2A5\uC758 S-ID\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
|
|
3413
3413
|
"design.titleRequired": "\uC124\uACC4 \uC81C\uBAA9\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
|
|
@@ -3415,24 +3415,19 @@ var messages_default = {
|
|
|
3415
3415
|
"design.singleTopHeading": "\uC124\uACC4 \uCD5C\uC0C1\uC704 \uC81C\uBAA9\uC740 \uD558\uB098\uB9CC \uC501\uB2C8\uB2E4.",
|
|
3416
3416
|
"design.referenceFormat": "\uC124\uACC4\uC758 \uCC38\uC870 \uC8FC\uC11D \uD615\uC2DD\uC744 \uD655\uC778\uD558\uC138\uC694.",
|
|
3417
3417
|
"design.unclosedFence": "\uC124\uACC4 \uCF54\uB4DC \uBE14\uB85D\uC774 \uB2EB\uD788\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.",
|
|
3418
|
-
"document.unsupportedPath": "\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \
|
|
3419
|
-
"document.productOnly": "\uC81C\uD488 \uD3F4\uB354\uC5D0\uB294 PRODUCT.md \uD558\uB098\uB9CC \uB461\uB2C8\uB2E4: {path}",
|
|
3418
|
+
"document.unsupportedPath": "\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC704\uD0A4 \uACBD\uB85C: {path}",
|
|
3420
3419
|
"document.invalidCharacters": "\uC798\uBABB\uB41C \uBB38\uC11C \uBB38\uC790: {path}",
|
|
3421
|
-
"document.idRequired": "\uCCAB \uC904\uC5D0 \uC720\uD6A8\uD55C gitifact-{kind} \uC8FC\uC11D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4: {path}",
|
|
3422
3420
|
"document.titleRequired": "\uBB38\uC11C \uC81C\uBAA9\uC774 \uD544\uC694\uD569\uB2C8\uB2E4: {path}",
|
|
3423
3421
|
"document.bodyRequired": "\uBB38\uC11C \uBCF8\uBB38\uC774 \uD544\uC694\uD569\uB2C8\uB2E4: {path}",
|
|
3424
3422
|
"document.singleTopHeading": "\uBB38\uC11C \uCD5C\uC0C1\uC704 \uC81C\uBAA9\uC740 \uD558\uB098\uB9CC \uC501\uB2C8\uB2E4: {path}",
|
|
3425
3423
|
"document.noMarkerInBody": "\uBB38\uC11C \uBCF8\uBB38\uC5D0\uB294 gitifact \uC8FC\uC11D\uC744 \uC4F0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: {path}",
|
|
3426
3424
|
"document.unclosedFence": "\uBB38\uC11C \uCF54\uB4DC \uBE14\uB85D\uC774 \uB2EB\uD788\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4: {path}",
|
|
3427
|
-
"document.
|
|
3428
|
-
"document.kindMismatch": "\uBB38\uC11C \uC885\uB958\uAC00 \uD3F4\uB354\uC640 \uB2E4\uB985\uB2C8\uB2E4: {path}",
|
|
3429
|
-
"document.duplicateId": "\uC800\uC7A5\uC18C \uB0B4 \uC911\uBCF5 \uBB38\uC11C ID: {id}",
|
|
3425
|
+
"document.duplicateId": "\uC800\uC7A5\uC18C \uB0B4 \uC911\uBCF5 \uC704\uD0A4 \uD398\uC774\uC9C0 ID: {id}",
|
|
3430
3426
|
"document.caseOnlyPath": "\uB300\uC18C\uBB38\uC790\uB9CC \uB2E4\uB978 \uBB38\uC11C \uACBD\uB85C: {path}",
|
|
3431
3427
|
"history.duplicateId": "\uC800\uC7A5\uC18C \uB0B4 \uC911\uBCF5 \uBCF4\uC870 \uAE30\uB85D ID: {id}",
|
|
3432
3428
|
"spec.orphanFile": "\uC694\uAD6C\uC0AC\uD56D \uBB38\uC11C \uC5C6\uB294 \uC124\uACC4 \uB610\uB294 \uC774\uC720 \uD30C\uC77C: {path}",
|
|
3433
3429
|
"spec.unsupportedPath": "\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uBA85\uC138 \uACBD\uB85C: {path}",
|
|
3434
3430
|
"spec.invalidCharacters": "\uC798\uBABB\uB41C \uBA85\uC138 \uBB38\uC790: {path}",
|
|
3435
|
-
"spec.idRequired": "\uCCAB \uC904\uC5D0 \uC720\uD6A8\uD55C gitifact-spec \uC8FC\uC11D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4: {path}",
|
|
3436
3431
|
"spec.requirementIdRequired": "\uC694\uAD6C\uC0AC\uD56D \uC81C\uBAA9 \uBC14\uB85C \uC544\uB798\uC5D0 \uC720\uD6A8\uD55C ID\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4: {path}",
|
|
3437
3432
|
"spec.invalidFence": "\uC798\uBABB\uB41C \uCF54\uB4DC \uBE14\uB85D\uC785\uB2C8\uB2E4.",
|
|
3438
3433
|
"spec.titleOnce": "\uBB38\uC11C \uC81C\uBAA9\uC740 \uCC98\uC74C\uC5D0 \uD55C \uBC88\uB9CC \uC501\uB2C8\uB2E4: {path}",
|
|
@@ -3460,10 +3455,8 @@ var messages_default = {
|
|
|
3460
3455
|
"edit.emptyField": "\uBE48 \uD3B8\uC9D1 \uD544\uB4DC: {field}",
|
|
3461
3456
|
"edit.invalidCharacters": "\uC798\uBABB\uB41C \uD3B8\uC9D1 \uBB38\uC790: {field}",
|
|
3462
3457
|
"edit.specNotFound": "\uB300\uC0C1 \uBA85\uC138\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4: {path}",
|
|
3463
|
-
"edit.noProductToDelete": "\uC0AD\uC81C\uD560 \uC81C\uD488 \uBB38\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
3464
3458
|
"edit.samePath": "\uC774\uBBF8 \uAC19\uC740 \uACBD\uB85C\uC785\uB2C8\uB2E4.",
|
|
3465
3459
|
"edit.documentPathExists": "\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uBB38\uC11C \uACBD\uB85C\uC785\uB2C8\uB2E4: {path}",
|
|
3466
|
-
"edit.guideIdNotFound": "\uC9C0\uCE68 \uBB38\uC11C ID\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4: {id}",
|
|
3467
3460
|
"edit.featureExists": "\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uAE30\uB2A5\uC785\uB2C8\uB2E4.",
|
|
3468
3461
|
"edit.noDesignToDelete": "\uC0AD\uC81C\uD560 \uC124\uACC4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
3469
3462
|
"edit.specIdNotFound": "\uBA85\uC138 ID\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
@@ -3473,7 +3466,7 @@ var messages_default = {
|
|
|
3473
3466
|
"edit.bodyChangesDocumentStructure": "\uBCF8\uBB38\uC774 \uBB38\uC11C \uAD6C\uC870\uB97C \uBCC0\uACBD\uD569\uB2C8\uB2E4: {path}",
|
|
3474
3467
|
"reason.keepEmptySpec": "\uAE30\uC874 \uC774\uC720\uB97C \uBCF4\uC874\uD560 \uBE48 \uBA85\uC138\uB97C \uB0A8\uACA8\uC8FC\uC138\uC694: {id}",
|
|
3475
3468
|
"reason.committedImmutable": "\uCEE4\uBC0B\uB41C \uC774\uC720\uC758 \uC218\uC815\xB7\uC0AD\uC81C\xB7\uC7AC\uBC30\uCE58\uB294 \uD5C8\uC6A9\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: {id}",
|
|
3476
|
-
"reason.committedDocumentImmutable": "\uCEE4\uBC0B\uB41C \
|
|
3469
|
+
"reason.committedDocumentImmutable": "\uCEE4\uBC0B\uB41C \uC704\uD0A4 \uC774\uC720\uC758 \uC218\uC815\xB7\uC0AD\uC81C\xB7\uC7AC\uBC30\uCE58\uB294 \uD5C8\uC6A9\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
3477
3470
|
"reason.count": "reasons\uB294 \uCD5C\uB300 100\uAC1C\uC758 \uBC30\uC5F4\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
|
|
3478
3471
|
"reason.idGenerated": "\uC798\uBABB\uB41C \uC774\uC720 ID \uC0DD\uC131 \uACB0\uACFC\uC785\uB2C8\uB2E4.",
|
|
3479
3472
|
"reason.idUnavailable": "\uACE0\uC720 \uC774\uC720 ID\uB97C \uBC1C\uAE09\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
@@ -3482,7 +3475,6 @@ var messages_default = {
|
|
|
3482
3475
|
"reason.documentWithRequirements": "\uBB38\uC11C \uC774\uC720\uC5D0\uB294 \uC694\uAD6C\uC0AC\uD56D\uC744 \uD568\uAED8 \uC9C0\uC815\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
3483
3476
|
"reason.documentTargetInvalid": "\uBB38\uC11C \uC774\uC720 \uB300\uC0C1\uC774 \uC798\uBABB\uB410\uAC70\uB098 \uC911\uBCF5\uB429\uB2C8\uB2E4.",
|
|
3484
3477
|
"reason.documentUnchanged": "\uCD5C\uC885 \uBCC0\uACBD\uC774 \uC5C6\uB294 \uBB38\uC11C\uC5D0 \uC774\uC720\uB97C \uBD99\uC77C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: {id}",
|
|
3485
|
-
"reason.singleDocumentKind": "\uD55C \uC774\uC720\uB294 \uD55C \uC885\uB958\uC758 \uBB38\uC11C\uB9CC \uB300\uC0C1\uC73C\uB85C \uD569\uB2C8\uB2E4.",
|
|
3486
3478
|
"reason.invalidTarget": "\uC798\uBABB\uB41C \uC774\uC720 \uB300\uC0C1\uC785\uB2C8\uB2E4.",
|
|
3487
3479
|
"reason.targetKind": "\uC774\uC720 \uB300\uC0C1 \uC885\uB958\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
3488
3480
|
"reason.targetInvalid": "\uC774\uC720 \uB300\uC0C1\uC774 \uC798\uBABB\uB410\uAC70\uB098 \uC911\uBCF5\uB429\uB2C8\uB2E4.",
|
|
@@ -3506,7 +3498,20 @@ var messages_default = {
|
|
|
3506
3498
|
"changelog.duplicateSection": "\uAC19\uC740 \uBC84\uC804\uC5D0 {section} \uC808\uC774 \uB450 \uBC88 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
3507
3499
|
"changelog.itemOutsideSection": "\uC808 \uC81C\uBAA9(### Added|Changed|Removed|Fixed) \uC544\uB798\uC5D0\uB9CC \uD56D\uBAA9\uC744 \uC4F8 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
3508
3500
|
"changelog.unknownLine": '\uC778\uC2DD\uD560 \uC218 \uC5C6\uB294 \uC904\uC785\uB2C8\uB2E4. \uBC84\uC804\uC740 "## X.Y.Z - YYYY-MM-DD", \uC808\uC740 "### Added|Changed|Removed|Fixed", \uD56D\uBAA9\uC740 "- "\uB85C \uC501\uB2C8\uB2E4: {text}',
|
|
3509
|
-
"changelog.noVersions": "\uBC84\uC804\uC774 \uD558\uB098\uB3C4 \uC5C6\uC2B5\uB2C8\uB2E4."
|
|
3501
|
+
"changelog.noVersions": "\uBC84\uC804\uC774 \uD558\uB098\uB3C4 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
3502
|
+
"frontmatter.required": "\uCCAB \uC904\uC5D0 --- \uB85C \uC2DC\uC791\uD558\uB294 frontmatter\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
|
|
3503
|
+
"frontmatter.unclosed": "frontmatter\uB97C \uB2EB\uB294 --- \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
3504
|
+
"frontmatter.line": "\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 frontmatter \uC904\uC785\uB2C8\uB2E4: {line}",
|
|
3505
|
+
"frontmatter.scalar": "frontmatter \uAC12\uC740 \uB530\uC634\uD45C \uC5C6\uB294 \uB2E8\uC21C \uBB38\uC790\uC5F4\uC774\uAC70\uB098 \uD070\uB530\uC634\uD45C \uBB38\uC790\uC5F4\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4: {value}",
|
|
3506
|
+
"frontmatter.id": "frontmatter\uC758 id\uAC00 \uC720\uD6A8\uD55C {kind} ID\uAC00 \uC544\uB2D9\uB2C8\uB2E4.",
|
|
3507
|
+
"frontmatter.unknownKey": "\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 frontmatter \uD0A4\uC785\uB2C8\uB2E4: {key}",
|
|
3508
|
+
"source.invalid": "\uCC38\uACE0 \uBB38\uC11C \uD56D\uBAA9\uC740 title\uACFC path \uB610\uB294 url \uC911 \uD558\uB098, \uC120\uD0DD\uC801 note\uB85C \uAD6C\uC131\uD569\uB2C8\uB2E4.",
|
|
3509
|
+
"source.url": "\uCC38\uACE0 \uBB38\uC11C url\uC740 http(s)\uC5EC\uC57C \uD569\uB2C8\uB2E4: {url}",
|
|
3510
|
+
"source.path": "\uCC38\uACE0 \uBB38\uC11C path\uB294 .md\uB85C \uB05D\uB098\uB294 \uC0C1\uB300 \uACBD\uB85C\uC5EC\uC57C \uD569\uB2C8\uB2E4: {path}",
|
|
3511
|
+
"edit.documentIdNotFound": "\uC704\uD0A4 \uD398\uC774\uC9C0 ID\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4: {id}",
|
|
3512
|
+
"edit.invalidSources": "sources\uB294 \uCD5C\uB300 50\uAC1C\uC758 \uBC30\uC5F4\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
|
|
3513
|
+
"config.unsupportedSchema": "\uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uC800\uC7A5 \uADDC\uC57D(schemaVersion {version})\uC740 \uC774\uC804 \uBC84\uC804\uC758 \uAC83\uC774\uB77C \uC774 CLI\uAC00 \uC77D\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC815\uC2DD \uBC84\uC804 \uC804 \uADDC\uC57D\uC740 \uC804\uD658\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. .gitifact\uC5D0 config.json\uB9CC \uC788\uC73C\uBA74 gitifact init\uC774 \uC0C8 \uADDC\uC57D\uC73C\uB85C \uBC14\uAFC9\uB2C8\uB2E4.",
|
|
3514
|
+
"config.newerSchema": "\uC774 \uD504\uB85C\uC81D\uD2B8\uB294 \uB354 \uC0C8\uB85C\uC6B4 \uC800\uC7A5 \uADDC\uC57D(schemaVersion {version})\uC744 \uC501\uB2C8\uB2E4. npm install -g gitifact@latest\uB85C CLI\uB97C \uC62C\uB9B0 \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694."
|
|
3510
3515
|
};
|
|
3511
3516
|
|
|
3512
3517
|
// ../../packages/core/dist/shared/i18n/index.js
|
|
@@ -3578,6 +3583,7 @@ function parseProjectConfig(text2) {
|
|
|
3578
3583
|
}
|
|
3579
3584
|
|
|
3580
3585
|
// ../../packages/core/dist/formats/spec-project.js
|
|
3586
|
+
var SCHEMA_VERSION = 2;
|
|
3581
3587
|
function parseManagedConfig(text2) {
|
|
3582
3588
|
let value;
|
|
3583
3589
|
try {
|
|
@@ -3588,41 +3594,53 @@ function parseManagedConfig(text2) {
|
|
|
3588
3594
|
if (!value || typeof value !== "object" || Array.isArray(value) || !("schemaVersion" in value))
|
|
3589
3595
|
return parseProjectConfig(text2);
|
|
3590
3596
|
const config2 = value;
|
|
3591
|
-
if (Object.keys(config2).sort().join(",") !== "baseline,schemaVersion"
|
|
3597
|
+
if (Object.keys(config2).sort().join(",") !== "baseline,schemaVersion")
|
|
3592
3598
|
throw new InitError("INVALID_CONFIG", t("config.schemaFields"));
|
|
3599
|
+
if (config2.schemaVersion !== SCHEMA_VERSION) {
|
|
3600
|
+
const found = config2.schemaVersion;
|
|
3601
|
+
const newer = typeof found === "number" && found > SCHEMA_VERSION;
|
|
3602
|
+
throw new InitError("UNSUPPORTED_SCHEMA", newer ? t("config.newerSchema", { version: String(found) }) : t("config.unsupportedSchema", { version: String(found) }));
|
|
3603
|
+
}
|
|
3593
3604
|
const checked = parseProjectConfig(JSON.stringify({ kind: "tryce-project", format: "workflow-1", mode: "auto", baseline: config2.baseline }));
|
|
3594
|
-
return { schemaVersion:
|
|
3605
|
+
return { schemaVersion: SCHEMA_VERSION, baseline: checked.baseline };
|
|
3595
3606
|
}
|
|
3596
3607
|
|
|
3597
3608
|
// ../../packages/core/dist/formats/spec-preview.js
|
|
3598
3609
|
var SpecPreviewError = class extends Error {
|
|
3599
3610
|
code = "INVALID_SPEC_PREVIEW";
|
|
3600
3611
|
};
|
|
3601
|
-
var
|
|
3602
|
-
var
|
|
3603
|
-
var
|
|
3604
|
-
var
|
|
3605
|
-
var
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
}
|
|
3612
|
+
var WIKI_DIR = ".gitifact/wiki";
|
|
3613
|
+
var WIKI_HISTORY_PATH = WIKI_DIR + "/history.jsonl";
|
|
3614
|
+
var WIKI_ENTRY = "README.md";
|
|
3615
|
+
var WIKI_ENTRY_PATH = WIKI_DIR + "/" + WIKI_ENTRY;
|
|
3616
|
+
var ASSETS_DIR = ".gitifact/assets";
|
|
3617
|
+
var RECOMMENDED_ASSET_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "webp", "svg", "pdf"];
|
|
3618
|
+
var ASSET_SIZE_LIMIT = 1024 * 1024;
|
|
3619
|
+
var ASSETS_TOTAL_LIMIT = 50 * 1024 * 1024;
|
|
3610
3620
|
var token = "[a-z2-7]{10}";
|
|
3611
3621
|
var STORE_DIR = ".gitifact";
|
|
3612
3622
|
var LEGACY_STORE_DIR = ".tryce";
|
|
3613
3623
|
var STORE_DIRS = [STORE_DIR, LEGACY_STORE_DIR];
|
|
3614
3624
|
var marker = "(?:gitifact|tryce)";
|
|
3615
|
-
var recordPathPattern = /^(?:\.(?:gitifact|tryce)\/spec\/[^/]+\/(?:requirements\.md|design\.md|history\.jsonl)|\.gitifact\/
|
|
3616
|
-
var documentPathPattern = /^\.gitifact\/
|
|
3617
|
-
var documentHistoryPattern = /^\.gitifact\/
|
|
3618
|
-
var kindOfDir = (dir) => dir === "product" ? "product" : "guide";
|
|
3625
|
+
var recordPathPattern = /^(?:\.(?:gitifact|tryce)\/spec\/[^/]+\/(?:requirements\.md|design\.md|history\.jsonl)|\.gitifact\/wiki\/(?:(?:[^/]+\/)*[^/]+\.md|history\.jsonl))$/;
|
|
3626
|
+
var documentPathPattern = /^\.gitifact\/wiki\/((?:[^/]+\/)*[^/]+\.md)$/;
|
|
3627
|
+
var documentHistoryPattern = /^\.gitifact\/wiki\/history\.jsonl$/;
|
|
3619
3628
|
var nameToken = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
3629
|
+
var segmentOk = (part) => !!part && part !== "." && part !== ".." && !/[\\:\0]/.test(part);
|
|
3630
|
+
function isAssetPath(path3) {
|
|
3631
|
+
if (!path3.startsWith(ASSETS_DIR + "/"))
|
|
3632
|
+
return false;
|
|
3633
|
+
const parts = path3.slice(ASSETS_DIR.length + 1).split("/");
|
|
3634
|
+
return parts.every(segmentOk) && parts.length <= 8 && path3.length <= 300;
|
|
3635
|
+
}
|
|
3636
|
+
var assetExtension = (path3) => (/\.([A-Za-z0-9]+)$/.exec(path3)?.[1] ?? "").toLowerCase();
|
|
3637
|
+
var rootUpperName = /^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)*\.md$/;
|
|
3620
3638
|
function validateDocumentRelativePath(relative) {
|
|
3621
3639
|
const parts = relative.split("/");
|
|
3622
3640
|
if (relative.length > 200 || parts.length > 8)
|
|
3623
3641
|
fail(t("document.pathTooLong", { path: relative }));
|
|
3624
3642
|
const file2 = parts.pop();
|
|
3625
|
-
if (!file2.endsWith(".md") || !nameToken.test(file2.slice(0, -3)))
|
|
3643
|
+
if (!(parts.length === 0 && rootUpperName.test(file2)) && (!file2.endsWith(".md") || !nameToken.test(file2.slice(0, -3))))
|
|
3626
3644
|
fail(t("document.fileName", { path: relative }));
|
|
3627
3645
|
for (const part of parts)
|
|
3628
3646
|
if (!nameToken.test(part) || part.length > 80)
|
|
@@ -3634,19 +3652,115 @@ var fail = (message) => {
|
|
|
3634
3652
|
throw new SpecPreviewError(message);
|
|
3635
3653
|
};
|
|
3636
3654
|
var normalized = (value) => value.replace(/\r\n/g, "\n").trim();
|
|
3655
|
+
var scalar = (value) => {
|
|
3656
|
+
const quoted = /^"((?:[^"\\]|\\["\\])*)"$/.exec(value);
|
|
3657
|
+
if (quoted)
|
|
3658
|
+
return quoted[1].replace(/\\(["\\])/g, "$1");
|
|
3659
|
+
if (!value || /^["'#&*!|>%@`[{-]/.test(value) || /:\s|\s#/.test(value) || value !== value.trim())
|
|
3660
|
+
fail(t("frontmatter.scalar", { value }));
|
|
3661
|
+
return value;
|
|
3662
|
+
};
|
|
3663
|
+
var quote = (value) => /^[A-Za-z0-9가-힣./_][^"#:\\]*$/.test(value) && value === value.trim() && !/\s#|:\s/.test(value) && !/^(true|false|null|yes|no|~)$/i.test(value) && !/^\d/.test(value) ? value : '"' + value.replace(/[\\"]/g, "\\$&") + '"';
|
|
3664
|
+
function parseFrontmatter(lines, kind, legacyIds) {
|
|
3665
|
+
const legacy = new RegExp(`^<!-- ${marker}-(?:${kind === "wiki" ? "product|guide" : kind}): (${legacyIds}-${token}) -->$`).exec(lines[0] ?? "");
|
|
3666
|
+
if (legacy) {
|
|
3667
|
+
lines.shift();
|
|
3668
|
+
return { id: legacy[1] };
|
|
3669
|
+
}
|
|
3670
|
+
if (lines[0] !== "---")
|
|
3671
|
+
fail(t("frontmatter.required"));
|
|
3672
|
+
lines.shift();
|
|
3673
|
+
const end = lines.indexOf("---");
|
|
3674
|
+
if (end === -1)
|
|
3675
|
+
fail(t("frontmatter.unclosed"));
|
|
3676
|
+
const block = lines.splice(0, end);
|
|
3677
|
+
lines.shift();
|
|
3678
|
+
const fields2 = /* @__PURE__ */ new Map();
|
|
3679
|
+
for (let i = 0; i < block.length; i++) {
|
|
3680
|
+
const line = block[i];
|
|
3681
|
+
if (!line.trim())
|
|
3682
|
+
continue;
|
|
3683
|
+
const entry2 = /^([a-z]+):(?: (.*))?$/.exec(line);
|
|
3684
|
+
if (!entry2 || fields2.has(entry2[1]))
|
|
3685
|
+
fail(t("frontmatter.line", { line }));
|
|
3686
|
+
const [, key, value] = entry2;
|
|
3687
|
+
if (key !== "sources") {
|
|
3688
|
+
if (value === void 0)
|
|
3689
|
+
fail(t("frontmatter.line", { line }));
|
|
3690
|
+
fields2.set(key, scalar(value));
|
|
3691
|
+
continue;
|
|
3692
|
+
}
|
|
3693
|
+
if (value !== void 0)
|
|
3694
|
+
fail(t("frontmatter.line", { line }));
|
|
3695
|
+
const sources2 = [];
|
|
3696
|
+
while (i + 1 < block.length && /^ /.test(block[i + 1])) {
|
|
3697
|
+
const item = block[++i];
|
|
3698
|
+
const head = /^ - ([a-z]+): (.*)$/.exec(item);
|
|
3699
|
+
if (!head)
|
|
3700
|
+
fail(t("frontmatter.line", { line: item }));
|
|
3701
|
+
const map2 = { [head[1]]: scalar(head[2]) };
|
|
3702
|
+
while (i + 1 < block.length && /^ [a-z]+: /.test(block[i + 1])) {
|
|
3703
|
+
const more = /^ ([a-z]+): (.*)$/.exec(block[++i]);
|
|
3704
|
+
if (more[1] in map2)
|
|
3705
|
+
fail(t("frontmatter.line", { line: block[i] }));
|
|
3706
|
+
map2[more[1]] = scalar(more[2]);
|
|
3707
|
+
}
|
|
3708
|
+
sources2.push(validateSource(map2));
|
|
3709
|
+
}
|
|
3710
|
+
fields2.set("sources", sources2);
|
|
3711
|
+
}
|
|
3712
|
+
const id = fields2.get("id");
|
|
3713
|
+
if (typeof id !== "string" || !new RegExp(`^(?:${legacyIds})-${token}$`).test(id))
|
|
3714
|
+
fail(t("frontmatter.id", { kind }));
|
|
3715
|
+
for (const key of fields2.keys())
|
|
3716
|
+
if (key !== "id" && !(key === "sources" && kind === "design"))
|
|
3717
|
+
fail(t("frontmatter.unknownKey", { key }));
|
|
3718
|
+
const sources = fields2.get("sources");
|
|
3719
|
+
return { id, ...Array.isArray(sources) ? { sources } : {} };
|
|
3720
|
+
}
|
|
3721
|
+
function validateSource(value) {
|
|
3722
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
3723
|
+
fail(t("source.invalid"));
|
|
3724
|
+
const map2 = value;
|
|
3725
|
+
const keys2 = Object.keys(map2).sort();
|
|
3726
|
+
if (keys2.some((k) => !["title", "path", "url", "note"].includes(k)) || typeof map2.title !== "string" || !map2.title.trim() || "path" in map2 === "url" in map2)
|
|
3727
|
+
fail(t("source.invalid"));
|
|
3728
|
+
for (const key of keys2) {
|
|
3729
|
+
const v = map2[key];
|
|
3730
|
+
if (typeof v !== "string" || !v.trim() || /[\r\n\0]/.test(v) || v.length > 500)
|
|
3731
|
+
fail(t("source.invalid"));
|
|
3732
|
+
}
|
|
3733
|
+
if (typeof map2.url === "string" && !/^https?:\/\/\S+$/.test(map2.url))
|
|
3734
|
+
fail(t("source.url", { url: map2.url }));
|
|
3735
|
+
if (typeof map2.path === "string" && (/^[a-z]+:/i.test(map2.path) || map2.path.startsWith("/") || !map2.path.endsWith(".md")))
|
|
3736
|
+
fail(t("source.path", { path: map2.path }));
|
|
3737
|
+
return { title: map2.title.trim(), ...map2.path !== void 0 ? { path: map2.path.trim() } : {}, ...map2.url !== void 0 ? { url: map2.url.trim() } : {}, ...map2.note !== void 0 ? { note: map2.note.trim() } : {} };
|
|
3738
|
+
}
|
|
3739
|
+
function renderFrontmatter(front) {
|
|
3740
|
+
const lines = ["---", "id: " + front.id];
|
|
3741
|
+
if (front.sources?.length) {
|
|
3742
|
+
lines.push("sources:");
|
|
3743
|
+
for (const s of front.sources) {
|
|
3744
|
+
const entries = ["title", "path", "url", "note"].filter((k) => s[k] !== void 0).map((k) => k + ": " + quote(s[k]));
|
|
3745
|
+
lines.push(" - " + entries[0], ...entries.slice(1).map((e) => " " + e));
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
lines.push("---");
|
|
3749
|
+
return lines.join("\n") + "\n";
|
|
3750
|
+
}
|
|
3637
3751
|
function renderDesignPreview(id, design) {
|
|
3638
|
-
return
|
|
3639
|
-
|
|
3752
|
+
return renderFrontmatter({ id, ...design.sources?.length ? { sources: design.sources } : {} }) + `
|
|
3640
3753
|
# ${design.title}
|
|
3641
3754
|
|
|
3642
3755
|
${design.body}
|
|
3643
3756
|
`;
|
|
3644
3757
|
}
|
|
3645
|
-
function parseDesignPreview(
|
|
3646
|
-
if (
|
|
3758
|
+
function parseDesignPreview(source2, specId) {
|
|
3759
|
+
if (source2.includes("\0") || /\r(?!\n)/.test(source2))
|
|
3647
3760
|
fail(t("design.invalidCharacters"));
|
|
3648
|
-
const lines = normalized(
|
|
3649
|
-
|
|
3761
|
+
const lines = normalized(source2).split("\n");
|
|
3762
|
+
const front = parseFrontmatter(lines, "design", "S");
|
|
3763
|
+
if (front.id !== specId)
|
|
3650
3764
|
fail(t("design.idMismatch"));
|
|
3651
3765
|
while (lines[0] === "")
|
|
3652
3766
|
lines.shift();
|
|
@@ -3684,32 +3798,24 @@ function parseDesignPreview(source, specId) {
|
|
|
3684
3798
|
}).join("\n");
|
|
3685
3799
|
if (fence)
|
|
3686
3800
|
fail(t("design.unclosedFence"));
|
|
3687
|
-
return { title, body, requirements: [...references] };
|
|
3801
|
+
return { title, body, requirements: [...references], sources: front.sources ?? [] };
|
|
3688
3802
|
}
|
|
3689
3803
|
function renderDocument(doc) {
|
|
3690
|
-
return
|
|
3691
|
-
|
|
3804
|
+
return renderFrontmatter({ id: doc.id }) + `
|
|
3692
3805
|
# ${doc.title}
|
|
3693
3806
|
|
|
3694
3807
|
${doc.body}
|
|
3695
3808
|
`;
|
|
3696
3809
|
}
|
|
3697
|
-
function parseDocument(path3,
|
|
3810
|
+
function parseDocument(path3, source2) {
|
|
3698
3811
|
const match = documentPathPattern.exec(path3);
|
|
3699
3812
|
if (!match)
|
|
3700
3813
|
fail(t("document.unsupportedPath", { path: path3 }));
|
|
3701
|
-
|
|
3702
|
-
if (
|
|
3703
|
-
if (path3 !== PRODUCT_PATH)
|
|
3704
|
-
fail(t("document.productOnly", { path: path3 }));
|
|
3705
|
-
} else
|
|
3706
|
-
validateDocumentRelativePath(match[2]);
|
|
3707
|
-
if (source.includes("\0") || /\r(?!\n)/.test(source))
|
|
3814
|
+
validateDocumentRelativePath(match[1]);
|
|
3815
|
+
if (source2.includes("\0") || /\r(?!\n)/.test(source2))
|
|
3708
3816
|
fail(t("document.invalidCharacters", { path: path3 }));
|
|
3709
|
-
const lines = normalized(
|
|
3710
|
-
const id
|
|
3711
|
-
if (!id || id[0] !== (kind === "product" ? "P" : "G"))
|
|
3712
|
-
return fail(t("document.idRequired", { kind, path: path3 }));
|
|
3817
|
+
const lines = normalized(source2).split("\n");
|
|
3818
|
+
const { id } = parseFrontmatter(lines, "wiki", "W");
|
|
3713
3819
|
while (lines[0] === "")
|
|
3714
3820
|
lines.shift();
|
|
3715
3821
|
const heading = lines.shift();
|
|
@@ -3738,58 +3844,49 @@ function parseDocument(path3, source) {
|
|
|
3738
3844
|
}
|
|
3739
3845
|
if (fence)
|
|
3740
3846
|
fail(t("document.unclosedFence", { path: path3 }));
|
|
3741
|
-
return { id,
|
|
3742
|
-
}
|
|
3743
|
-
function
|
|
3744
|
-
const
|
|
3745
|
-
for (const [path3,
|
|
3746
|
-
if (
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
validateDocumentSets(sets);
|
|
3754
|
-
return sets;
|
|
3755
|
-
}
|
|
3756
|
-
function validateDocumentSets(sets) {
|
|
3847
|
+
return { id, path: path3, title, body };
|
|
3848
|
+
}
|
|
3849
|
+
function parseWikiFiles(files) {
|
|
3850
|
+
const wiki = { documents: [], history: parsePreviewHistory(files.get(WIKI_HISTORY_PATH) ?? "", "documents") };
|
|
3851
|
+
for (const [path3, source2] of files)
|
|
3852
|
+
if (documentPathPattern.test(path3))
|
|
3853
|
+
wiki.documents.push(parseDocument(path3, source2));
|
|
3854
|
+
wiki.documents.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
3855
|
+
validateWiki(wiki);
|
|
3856
|
+
return wiki;
|
|
3857
|
+
}
|
|
3858
|
+
function validateWiki(wiki) {
|
|
3757
3859
|
const ids = /* @__PURE__ */ new Set();
|
|
3758
3860
|
const paths2 = /* @__PURE__ */ new Set();
|
|
3759
3861
|
const historyIds = /* @__PURE__ */ new Set();
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
}
|
|
3774
|
-
for (const h of set2.history) {
|
|
3775
|
-
if (historyIds.has(h.id))
|
|
3776
|
-
fail(t("history.duplicateId", { id: h.id }));
|
|
3777
|
-
historyIds.add(h.id);
|
|
3778
|
-
}
|
|
3862
|
+
for (const d of wiki.documents) {
|
|
3863
|
+
if (ids.has(d.id))
|
|
3864
|
+
fail(t("document.duplicateId", { id: d.id }));
|
|
3865
|
+
ids.add(d.id);
|
|
3866
|
+
const lower = d.path.toLowerCase();
|
|
3867
|
+
if (paths2.has(lower))
|
|
3868
|
+
fail(t("document.caseOnlyPath", { path: d.path }));
|
|
3869
|
+
paths2.add(lower);
|
|
3870
|
+
}
|
|
3871
|
+
for (const h of wiki.history) {
|
|
3872
|
+
if (historyIds.has(h.id))
|
|
3873
|
+
fail(t("history.duplicateId", { id: h.id }));
|
|
3874
|
+
historyIds.add(h.id);
|
|
3779
3875
|
}
|
|
3780
3876
|
}
|
|
3781
|
-
var
|
|
3782
|
-
var
|
|
3877
|
+
var emptyWiki = () => ({ documents: [], history: [] });
|
|
3878
|
+
var emptyBundle = () => ({ specs: [], wiki: emptyWiki() });
|
|
3879
|
+
var asBundle = (value) => Array.isArray(value) ? { specs: value, wiki: emptyWiki() } : value;
|
|
3783
3880
|
function parsePreviewBundle(files) {
|
|
3784
|
-
const bundle = { specs: parsePreviewFiles(files),
|
|
3881
|
+
const bundle = { specs: parsePreviewFiles(files), wiki: parseWikiFiles(files) };
|
|
3785
3882
|
validateBundle(bundle);
|
|
3786
3883
|
return bundle;
|
|
3787
3884
|
}
|
|
3788
3885
|
function validateBundle(bundle) {
|
|
3789
3886
|
validatePreviewSnapshot(bundle.specs);
|
|
3790
|
-
|
|
3887
|
+
validateWiki(bundle.wiki);
|
|
3791
3888
|
const ids = /* @__PURE__ */ new Set();
|
|
3792
|
-
for (const h of [...bundle.specs.flatMap((s) => s.history), ...bundle.
|
|
3889
|
+
for (const h of [...bundle.specs.flatMap((s) => s.history), ...bundle.wiki.history]) {
|
|
3793
3890
|
if (ids.has(h.id))
|
|
3794
3891
|
fail(t("history.duplicateId", { id: h.id }));
|
|
3795
3892
|
ids.add(h.id);
|
|
@@ -3799,28 +3896,92 @@ function designReferenceWarnings(specs) {
|
|
|
3799
3896
|
const known = new Set(specs.flatMap((s) => s.requirements.map((r) => r.id)));
|
|
3800
3897
|
return specs.flatMap((s) => (s.design?.requirements ?? []).filter((id) => !known.has(id)).map((id) => ({ specId: s.id, requirement: id, code: "MISSING_DESIGN_REFERENCE" })));
|
|
3801
3898
|
}
|
|
3899
|
+
function resolveLink(from, link3) {
|
|
3900
|
+
const bare = link3.replace(/[?#].*$/, "");
|
|
3901
|
+
if (!bare || /^[a-z][a-z0-9+.-]*:/i.test(bare) || bare.startsWith("/") || bare.startsWith("//"))
|
|
3902
|
+
return null;
|
|
3903
|
+
const parts = from.split("/").slice(0, -1);
|
|
3904
|
+
for (const part of decodeSafe(bare).split("/")) {
|
|
3905
|
+
if (part === "" || part === ".")
|
|
3906
|
+
continue;
|
|
3907
|
+
if (part === "..") {
|
|
3908
|
+
if (!parts.length)
|
|
3909
|
+
return null;
|
|
3910
|
+
parts.pop();
|
|
3911
|
+
continue;
|
|
3912
|
+
}
|
|
3913
|
+
if (/[\\:\0]/.test(part))
|
|
3914
|
+
return null;
|
|
3915
|
+
parts.push(part);
|
|
3916
|
+
}
|
|
3917
|
+
return parts.join("/");
|
|
3918
|
+
}
|
|
3919
|
+
var decodeSafe = (value) => {
|
|
3920
|
+
try {
|
|
3921
|
+
return decodeURIComponent(value);
|
|
3922
|
+
} catch {
|
|
3923
|
+
return value;
|
|
3924
|
+
}
|
|
3925
|
+
};
|
|
3926
|
+
function extractLinks(body) {
|
|
3927
|
+
const links = [];
|
|
3928
|
+
let fence;
|
|
3929
|
+
for (const line of body.split("\n")) {
|
|
3930
|
+
if (fence) {
|
|
3931
|
+
if (new RegExp(`^ {0,3}${fence.char}{${fence.size},}\\s*$`).test(line))
|
|
3932
|
+
fence = void 0;
|
|
3933
|
+
continue;
|
|
3934
|
+
}
|
|
3935
|
+
const open6 = /^ {0,3}(`{3,}|~{3,})/.exec(line);
|
|
3936
|
+
if (open6) {
|
|
3937
|
+
fence = { char: open6[1][0], size: open6[1].length };
|
|
3938
|
+
continue;
|
|
3939
|
+
}
|
|
3940
|
+
for (const m of line.replace(/`[^`]*`/g, "").matchAll(/!?\[[^\]]*\]\((?:<([^>]+)>|([^)\s]+))(?:\s+"[^"]*")?\)/g))
|
|
3941
|
+
links.push(m[1] ?? m[2]);
|
|
3942
|
+
}
|
|
3943
|
+
return links;
|
|
3944
|
+
}
|
|
3945
|
+
function documentLinks(bundle) {
|
|
3946
|
+
const out = [];
|
|
3947
|
+
const collect = (from, links) => {
|
|
3948
|
+
for (const link3 of links) {
|
|
3949
|
+
const target = resolveLink(from, link3);
|
|
3950
|
+
if (target !== null)
|
|
3951
|
+
out.push({ from, link: link3, target });
|
|
3952
|
+
}
|
|
3953
|
+
};
|
|
3954
|
+
for (const s of bundle.specs) {
|
|
3955
|
+
collect(s.path, extractLinks(s.description + "\n" + s.requirements.map((r) => r.body).join("\n")));
|
|
3956
|
+
if (s.design) {
|
|
3957
|
+
const path3 = s.path.replace(/requirements\.md$/, "design.md");
|
|
3958
|
+
collect(path3, [...extractLinks(s.design.body), ...s.design.sources.flatMap((x) => x.path ? [x.path] : [])]);
|
|
3959
|
+
}
|
|
3960
|
+
}
|
|
3961
|
+
for (const d of bundle.wiki.documents)
|
|
3962
|
+
collect(d.path, extractLinks(d.body));
|
|
3963
|
+
return out;
|
|
3964
|
+
}
|
|
3802
3965
|
function parsePreviewFiles(files) {
|
|
3803
3966
|
const specs = [];
|
|
3804
|
-
for (const [path3,
|
|
3967
|
+
for (const [path3, source2] of files) {
|
|
3805
3968
|
if (documentPathPattern.test(path3) || documentHistoryPattern.test(path3))
|
|
3806
3969
|
continue;
|
|
3807
3970
|
if (path3.endsWith("/requirements.md")) {
|
|
3808
|
-
specs.push(parseSpecPreview(path3,
|
|
3971
|
+
specs.push(parseSpecPreview(path3, source2, files.get(path3.replace(/requirements\.md$/, "history.jsonl")) ?? "", files.get(path3.replace(/requirements\.md$/, "design.md"))));
|
|
3809
3972
|
} else if (!files.has(path3.replace(/(?:history\.jsonl|design\.md)$/, "requirements.md")))
|
|
3810
3973
|
fail(t("spec.orphanFile", { path: path3 }));
|
|
3811
3974
|
}
|
|
3812
3975
|
validatePreviewSnapshot(specs);
|
|
3813
3976
|
return specs;
|
|
3814
3977
|
}
|
|
3815
|
-
function parseSpecPreview(path3,
|
|
3978
|
+
function parseSpecPreview(path3, source2, history = "", designSource) {
|
|
3816
3979
|
if (!/^\.(?:gitifact|tryce)\/spec\/[^/]+\/requirements\.md$/.test(path3))
|
|
3817
3980
|
fail(t("spec.unsupportedPath", { path: path3 }));
|
|
3818
|
-
if (
|
|
3981
|
+
if (source2.includes("\0") || source2.includes("\r") && /\r(?!\n)/.test(source2))
|
|
3819
3982
|
fail(t("spec.invalidCharacters", { path: path3 }));
|
|
3820
|
-
const lines = normalized(
|
|
3821
|
-
const
|
|
3822
|
-
if (!specMatch)
|
|
3823
|
-
fail(t("spec.idRequired", { path: path3 }));
|
|
3983
|
+
const lines = normalized(source2).split("\n");
|
|
3984
|
+
const specId = parseFrontmatter(lines, "spec", "S").id;
|
|
3824
3985
|
let title = "";
|
|
3825
3986
|
let description = "";
|
|
3826
3987
|
let current;
|
|
@@ -3833,7 +3994,7 @@ function parseSpecPreview(path3, source, history = "", designSource) {
|
|
|
3833
3994
|
else
|
|
3834
3995
|
description += line + "\n";
|
|
3835
3996
|
};
|
|
3836
|
-
for (let i =
|
|
3997
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3837
3998
|
const line = lines[i];
|
|
3838
3999
|
if (waitingId) {
|
|
3839
4000
|
const match = new RegExp(`^<!-- ${marker}-req: (R-${token}) -->$`).exec(line);
|
|
@@ -3890,13 +4051,13 @@ function parseSpecPreview(path3, source, history = "", designSource) {
|
|
|
3890
4051
|
fail(t("spec.emptyOrDuplicateRequirement", { id: r.id }));
|
|
3891
4052
|
ids.add(r.id);
|
|
3892
4053
|
}
|
|
3893
|
-
return { id:
|
|
4054
|
+
return { id: specId, path: path3, title, description: normalized(description), requirements, history: parsePreviewHistory(history), ...designSource === void 0 ? {} : { design: parseDesignPreview(designSource, specId) } };
|
|
3894
4055
|
}
|
|
3895
|
-
function parsePreviewHistory(
|
|
4056
|
+
function parsePreviewHistory(source2, scope = "spec") {
|
|
3896
4057
|
const records = [];
|
|
3897
4058
|
const ids = /* @__PURE__ */ new Set();
|
|
3898
4059
|
const shapes = scope === "spec" ? ["id,reason,requirements", "designs,id,reason,requirements"] : ["documents,id,reason,requirements"];
|
|
3899
|
-
for (const line of
|
|
4060
|
+
for (const line of source2.replace(/\r\n/g, "\n").split("\n")) {
|
|
3900
4061
|
if (!line.trim())
|
|
3901
4062
|
continue;
|
|
3902
4063
|
let value;
|
|
@@ -3908,7 +4069,7 @@ function parsePreviewHistory(source, scope = "spec") {
|
|
|
3908
4069
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
3909
4070
|
fail(t("history.invalidRecord"));
|
|
3910
4071
|
const r = value;
|
|
3911
|
-
if (!shapes.includes(Object.keys(r).sort().join(",")) || typeof r.id !== "string" || !new RegExp(`^H-${token}$`).test(r.id) || ids.has(r.id) || typeof r.reason !== "string" || !r.reason.trim() || !Array.isArray(r.requirements) || !r.requirements.length && !Array.isArray(r.designs) && !Array.isArray(r.documents) || r.documents !== void 0 && (r.requirements.length || !Array.isArray(r.documents) || !r.documents.length || r.documents.some((id) => typeof id !== "string" || !/^
|
|
4072
|
+
if (!shapes.includes(Object.keys(r).sort().join(",")) || typeof r.id !== "string" || !new RegExp(`^H-${token}$`).test(r.id) || ids.has(r.id) || typeof r.reason !== "string" || !r.reason.trim() || !Array.isArray(r.requirements) || !r.requirements.length && !Array.isArray(r.designs) && !Array.isArray(r.documents) || r.documents !== void 0 && (r.requirements.length || !Array.isArray(r.documents) || !r.documents.length || r.documents.some((id) => typeof id !== "string" || !/^W-[a-z2-7]{10}$/.test(id)) || new Set(r.documents).size !== r.documents.length) || r.designs !== void 0 && (!Array.isArray(r.designs) || !r.designs.length || r.designs.some((id) => typeof id !== "string" || !/^S-[a-z2-7]{10}$/.test(id)) || new Set(r.designs).size !== r.designs.length) || r.requirements.some((id) => typeof id !== "string" || !reqId.test(id)) || new Set(r.requirements).size !== r.requirements.length)
|
|
3912
4073
|
fail(t("history.invalidFields"));
|
|
3913
4074
|
ids.add(r.id);
|
|
3914
4075
|
records.push({ id: r.id, requirements: r.requirements, ...r.designs === void 0 ? {} : { designs: r.designs }, ...r.documents === void 0 ? {} : { documents: r.documents }, reason: r.reason });
|
|
@@ -3935,6 +4096,7 @@ function validatePreviewSnapshot(specs) {
|
|
|
3935
4096
|
}
|
|
3936
4097
|
}
|
|
3937
4098
|
}
|
|
4099
|
+
var sameEntry = (a, b) => a.title === b.title && a.body === b.body && JSON.stringify(a.sources ?? []) === JSON.stringify(b.sources ?? []);
|
|
3938
4100
|
function compareSpecPreviews(before, after) {
|
|
3939
4101
|
validatePreviewSnapshot(before);
|
|
3940
4102
|
validatePreviewSnapshot(after);
|
|
@@ -3944,7 +4106,7 @@ function compareSpecPreviews(before, after) {
|
|
|
3944
4106
|
for (const [specs, map2] of [[before, prev], [after, next]])
|
|
3945
4107
|
for (const s of specs)
|
|
3946
4108
|
if (s.design)
|
|
3947
|
-
map2.set(s.id, { id: s.id, title: s.design.title, body: s.design.body, specId: s.id, path: s.path.replace(/requirements\.md$/, "design.md") });
|
|
4109
|
+
map2.set(s.id, { id: s.id, title: s.design.title, body: s.design.body, specId: s.id, path: s.path.replace(/requirements\.md$/, "design.md"), sources: s.design.sources });
|
|
3948
4110
|
const previousHistory = new Map(before.flatMap((s) => s.history.map((h) => [h.id, h])));
|
|
3949
4111
|
const reasons = after.flatMap((s) => s.history).filter((h) => {
|
|
3950
4112
|
const old = previousHistory.get(h.id);
|
|
@@ -3963,7 +4125,7 @@ function compareSpecPreviews(before, after) {
|
|
|
3963
4125
|
else {
|
|
3964
4126
|
if (from.specId !== to.specId || id.startsWith("S-") && storeRelative(from.path) !== storeRelative(to.path))
|
|
3965
4127
|
types.push("moved");
|
|
3966
|
-
if (from
|
|
4128
|
+
if (!sameEntry(from, to))
|
|
3967
4129
|
types.push("modified");
|
|
3968
4130
|
}
|
|
3969
4131
|
return types.length ? [{ id, types, before: from, after: to, kind: id.startsWith("S-") ? "design" : "requirement", reasons: reasons.filter((h) => [...h.requirements, ...h.designs ?? []].includes(id)) }] : [];
|
|
@@ -3985,14 +4147,14 @@ function compareSpecPreviews(before, after) {
|
|
|
3985
4147
|
});
|
|
3986
4148
|
return { specChanges, changes };
|
|
3987
4149
|
}
|
|
3988
|
-
function
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
const index = (
|
|
4150
|
+
function compareWikis(before, after) {
|
|
4151
|
+
validateWiki(before);
|
|
4152
|
+
validateWiki(after);
|
|
4153
|
+
const index = (wiki) => new Map(wiki.documents.map((d) => [d.id, { id: d.id, title: d.title, body: d.body, specId: "wiki", path: d.path }]));
|
|
3992
4154
|
const prev = index(before);
|
|
3993
4155
|
const next = index(after);
|
|
3994
|
-
const previousHistory = new Map(before.
|
|
3995
|
-
const reasons = after.
|
|
4156
|
+
const previousHistory = new Map(before.history.map((h) => [h.id, h]));
|
|
4157
|
+
const reasons = after.history.filter((h) => {
|
|
3996
4158
|
const old = previousHistory.get(h.id);
|
|
3997
4159
|
if (old && JSON.stringify(old) !== JSON.stringify(h))
|
|
3998
4160
|
fail(t("history.changed", { id: h.id }));
|
|
@@ -4012,8 +4174,7 @@ function compareDocumentSets(before, after) {
|
|
|
4012
4174
|
if (from.title !== to.title || from.body !== to.body)
|
|
4013
4175
|
types.push("modified");
|
|
4014
4176
|
}
|
|
4015
|
-
|
|
4016
|
-
return types.length ? [{ id, types, before: from, after: to, kind, reasons: reasons.filter((h) => (h.documents ?? []).includes(id)) }] : [];
|
|
4177
|
+
return types.length ? [{ id, types, before: from, after: to, kind: "wiki", reasons: reasons.filter((h) => (h.documents ?? []).includes(id)) }] : [];
|
|
4017
4178
|
});
|
|
4018
4179
|
const changed = new Set(changes.map((c) => c.id));
|
|
4019
4180
|
for (const h of reasons)
|
|
@@ -4027,7 +4188,7 @@ function comparePreviewBundles(before, after) {
|
|
|
4027
4188
|
validateBundle(a);
|
|
4028
4189
|
validateBundle(b);
|
|
4029
4190
|
const specs = compareSpecPreviews(a.specs, b.specs);
|
|
4030
|
-
return { specChanges: specs.specChanges, changes: [...specs.changes, ...
|
|
4191
|
+
return { specChanges: specs.specChanges, changes: [...specs.changes, ...compareWikis(a.wiki, b.wiki)] };
|
|
4031
4192
|
}
|
|
4032
4193
|
|
|
4033
4194
|
// ../../packages/core/dist/formats/changelog.js
|
|
@@ -4106,8 +4267,7 @@ function previewPath(feature2) {
|
|
|
4106
4267
|
return `.gitifact/spec/${feature2}/requirements.md`;
|
|
4107
4268
|
}
|
|
4108
4269
|
function renderSpecPreview(spec2) {
|
|
4109
|
-
return
|
|
4110
|
-
|
|
4270
|
+
return renderFrontmatter({ id: spec2.id }) + `
|
|
4111
4271
|
# ${spec2.title}
|
|
4112
4272
|
|
|
4113
4273
|
` + (spec2.description ? spec2.description + "\n\n" : "") + spec2.requirements.map((r) => `## ${r.title}
|
|
@@ -4119,11 +4279,11 @@ ${r.body}
|
|
|
4119
4279
|
function editSpecPreview(original, input2, generate) {
|
|
4120
4280
|
if (!Array.isArray(input2) || !input2.length || input2.length > 100)
|
|
4121
4281
|
fail2(t("edit.operationCount"));
|
|
4122
|
-
const
|
|
4123
|
-
const specs =
|
|
4124
|
-
const
|
|
4125
|
-
validateBundle({ specs,
|
|
4126
|
-
const used = /* @__PURE__ */ new Set([...specs.flatMap((s) => [s.id, ...s.requirements.map((r) => r.id)]), ...
|
|
4282
|
+
const source2 = asBundle(original);
|
|
4283
|
+
const specs = source2.specs.map((s) => ({ ...s, requirements: s.requirements.map((r) => ({ ...r })), history: s.history.map((h) => ({ ...h, requirements: [...h.requirements] })) }));
|
|
4284
|
+
const wiki = { documents: source2.wiki.documents.map((d) => ({ ...d })), history: source2.wiki.history.map((h) => ({ ...h })) };
|
|
4285
|
+
validateBundle({ specs, wiki });
|
|
4286
|
+
const used = /* @__PURE__ */ new Set([...specs.flatMap((s) => [s.id, ...s.requirements.map((r) => r.id)]), ...wiki.documents.map((x) => x.id)]);
|
|
4127
4287
|
const allocate = (prefix) => {
|
|
4128
4288
|
for (let attempt = 0; attempt < 100; attempt++) {
|
|
4129
4289
|
const id = generate(prefix);
|
|
@@ -4147,14 +4307,12 @@ function editSpecPreview(original, input2, generate) {
|
|
|
4147
4307
|
update: ["type", "id", "title", "body"],
|
|
4148
4308
|
move: ["type", "id", "feature"],
|
|
4149
4309
|
"rename-spec": ["type", "id", "title"],
|
|
4150
|
-
"set-design": ["type", "feature", "title", "body"],
|
|
4310
|
+
"set-design": ["type", "feature", "title", "body", "sources"],
|
|
4151
4311
|
"delete-design": ["type", "feature"],
|
|
4152
4312
|
"create-doc": ["type", "path", "title", "body"],
|
|
4153
4313
|
"update-doc": ["type", "id", "title", "body"],
|
|
4154
4314
|
"move-doc": ["type", "id", "path"],
|
|
4155
|
-
"delete-doc": ["type", "id"]
|
|
4156
|
-
"set-product": ["type", "title", "body"],
|
|
4157
|
-
"delete-product": ["type"]
|
|
4315
|
+
"delete-doc": ["type", "id"]
|
|
4158
4316
|
};
|
|
4159
4317
|
const type = String(op.type);
|
|
4160
4318
|
const fields2 = schemas[type];
|
|
@@ -4174,41 +4332,24 @@ function editSpecPreview(original, input2, generate) {
|
|
|
4174
4332
|
const path3 = previewPath(str("feature"));
|
|
4175
4333
|
return specs.find((s) => s.path === path3) ?? fail2(t("edit.specNotFound", { path: path3 }));
|
|
4176
4334
|
};
|
|
4177
|
-
if (type === "set-product" || type === "delete-product") {
|
|
4178
|
-
const set2 = documents.find((s) => s.kind === "product");
|
|
4179
|
-
const existing = set2.documents[0];
|
|
4180
|
-
if (type === "delete-product") {
|
|
4181
|
-
if (!existing)
|
|
4182
|
-
fail2(t("edit.noProductToDelete"));
|
|
4183
|
-
set2.documents = [];
|
|
4184
|
-
results.push({ type, id: existing.id });
|
|
4185
|
-
} else {
|
|
4186
|
-
const doc = { id: existing?.id ?? allocate("P"), kind: "product", path: PRODUCT_PATH, title: str("title"), body: str("body") };
|
|
4187
|
-
set2.documents = [doc];
|
|
4188
|
-
results.push({ type, id: doc.id });
|
|
4189
|
-
}
|
|
4190
|
-
continue;
|
|
4191
|
-
}
|
|
4192
4335
|
if (type.endsWith("-doc")) {
|
|
4193
|
-
const
|
|
4194
|
-
const documentPath = (kind, self) => {
|
|
4336
|
+
const documentPath = (self) => {
|
|
4195
4337
|
const relative = str("path");
|
|
4196
4338
|
validateDocumentRelativePath(relative);
|
|
4197
|
-
const path3 = `${
|
|
4339
|
+
const path3 = `${WIKI_DIR}/${relative}`;
|
|
4198
4340
|
if (self && self.path === path3)
|
|
4199
4341
|
fail2(t("edit.samePath"));
|
|
4200
|
-
if (
|
|
4342
|
+
if (wiki.documents.some((d) => d !== self && d.path.toLowerCase() === path3.toLowerCase()))
|
|
4201
4343
|
fail2(t("edit.documentPathExists", { path: path3 }));
|
|
4202
4344
|
return path3;
|
|
4203
4345
|
};
|
|
4204
4346
|
const find = () => {
|
|
4205
4347
|
const id = str("id");
|
|
4206
|
-
return
|
|
4348
|
+
return wiki.documents.find((d) => d.id === id) ?? fail2(t("edit.documentIdNotFound", { id }));
|
|
4207
4349
|
};
|
|
4208
4350
|
if (type === "create-doc") {
|
|
4209
|
-
const
|
|
4210
|
-
|
|
4211
|
-
documents.find((s) => s.kind === kind).documents.push(doc);
|
|
4351
|
+
const doc = { id: allocate("W"), path: documentPath(), title: str("title"), body: str("body") };
|
|
4352
|
+
wiki.documents.push(doc);
|
|
4212
4353
|
results.push({ type, id: doc.id });
|
|
4213
4354
|
} else if (type === "update-doc") {
|
|
4214
4355
|
const doc = find();
|
|
@@ -4217,12 +4358,11 @@ function editSpecPreview(original, input2, generate) {
|
|
|
4217
4358
|
results.push({ type, id: doc.id });
|
|
4218
4359
|
} else if (type === "move-doc") {
|
|
4219
4360
|
const doc = find();
|
|
4220
|
-
doc.path = documentPath(doc
|
|
4361
|
+
doc.path = documentPath(doc);
|
|
4221
4362
|
results.push({ type, id: doc.id });
|
|
4222
4363
|
} else {
|
|
4223
4364
|
const doc = find();
|
|
4224
|
-
|
|
4225
|
-
set2.documents = set2.documents.filter((d) => d.id !== doc.id);
|
|
4365
|
+
wiki.documents = wiki.documents.filter((d) => d.id !== doc.id);
|
|
4226
4366
|
results.push({ type, id: doc.id });
|
|
4227
4367
|
}
|
|
4228
4368
|
continue;
|
|
@@ -4245,8 +4385,16 @@ function editSpecPreview(original, input2, generate) {
|
|
|
4245
4385
|
if (!spec2.design)
|
|
4246
4386
|
fail2(t("edit.noDesignToDelete"));
|
|
4247
4387
|
delete spec2.design;
|
|
4248
|
-
} else
|
|
4249
|
-
|
|
4388
|
+
} else {
|
|
4389
|
+
let sources = [];
|
|
4390
|
+
const list = op.sources;
|
|
4391
|
+
if (list !== void 0) {
|
|
4392
|
+
if (!Array.isArray(list) || list.length > 50)
|
|
4393
|
+
return fail2(t("edit.invalidSources"));
|
|
4394
|
+
sources = list.map(validateSource);
|
|
4395
|
+
}
|
|
4396
|
+
spec2.design = parseDesignPreview(renderDesignPreview(spec2.id, { title: str("title"), body: str("body"), sources }), spec2.id);
|
|
4397
|
+
}
|
|
4250
4398
|
results.push({ type, id: spec2.id });
|
|
4251
4399
|
} else if (type === "rename-spec") {
|
|
4252
4400
|
const spec2 = specs.find((s) => s.id === str("id")) ?? fail2(t("edit.specIdNotFound"));
|
|
@@ -4254,13 +4402,13 @@ function editSpecPreview(original, input2, generate) {
|
|
|
4254
4402
|
results.push({ type, id: spec2.id });
|
|
4255
4403
|
} else {
|
|
4256
4404
|
const id = str("id");
|
|
4257
|
-
const
|
|
4258
|
-
const req =
|
|
4405
|
+
const source3 = specs.find((s) => s.requirements.some((r) => r.id === id)) ?? fail2(t("edit.requirementIdNotFound"));
|
|
4406
|
+
const req = source3.requirements.find((r) => r.id === id);
|
|
4259
4407
|
if (type === "move") {
|
|
4260
4408
|
const destination = target();
|
|
4261
|
-
if (
|
|
4409
|
+
if (source3 === destination)
|
|
4262
4410
|
fail2(t("edit.sameSpec"));
|
|
4263
|
-
|
|
4411
|
+
source3.requirements = source3.requirements.filter((r) => r.id !== id);
|
|
4264
4412
|
destination.requirements.push(req);
|
|
4265
4413
|
} else {
|
|
4266
4414
|
req.title = str("title");
|
|
@@ -4274,14 +4422,12 @@ function editSpecPreview(original, input2, generate) {
|
|
|
4274
4422
|
if (JSON.stringify({ ...parsed, history: spec2.history }) !== JSON.stringify(spec2))
|
|
4275
4423
|
fail2(t("edit.bodyChangesSpecStructure"));
|
|
4276
4424
|
}
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
}
|
|
4283
|
-
validateBundle({ specs, documents });
|
|
4284
|
-
return { specs, documents, results };
|
|
4425
|
+
wiki.documents.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
4426
|
+
for (const doc of wiki.documents)
|
|
4427
|
+
if (JSON.stringify(parseDocument(doc.path, renderDocument(doc))) !== JSON.stringify(doc))
|
|
4428
|
+
fail2(t("edit.bodyChangesDocumentStructure", { path: doc.path }));
|
|
4429
|
+
validateBundle({ specs, wiki });
|
|
4430
|
+
return { specs, wiki, results };
|
|
4285
4431
|
}
|
|
4286
4432
|
|
|
4287
4433
|
// ../../packages/core/dist/use-cases/prepare-spec-preview.js
|
|
@@ -4295,7 +4441,7 @@ function pendingPreviewReasons(before, current) {
|
|
|
4295
4441
|
const b = asBundle(current);
|
|
4296
4442
|
return [
|
|
4297
4443
|
...b.specs.flatMap((s) => s.history.slice(a.specs.find((p) => p.id === s.id)?.history.length ?? 0).map((h) => ({ specId: s.id, path: historyPath(s), ...h }))),
|
|
4298
|
-
...b.
|
|
4444
|
+
...b.wiki.history.slice(a.wiki.history.length).map((h) => ({ kind: "wiki", path: WIKI_HISTORY_PATH, ...h }))
|
|
4299
4445
|
];
|
|
4300
4446
|
}
|
|
4301
4447
|
function finalSpecPreviewChanges(beforeValue, currentValue) {
|
|
@@ -4310,14 +4456,11 @@ function finalSpecPreviewChanges(beforeValue, currentValue) {
|
|
|
4310
4456
|
if (next && !equal(next.history.slice(0, previous.history.length), previous.history))
|
|
4311
4457
|
fail3(t("reason.committedImmutable", { id: previous.id }));
|
|
4312
4458
|
}
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
if (!equal(next.history.slice(0, previous.history.length), previous.history))
|
|
4316
|
-
fail3(t("reason.committedDocumentImmutable", { kind: previous.kind }));
|
|
4317
|
-
}
|
|
4459
|
+
if (!equal(current.wiki.history.slice(0, before.wiki.history.length), before.wiki.history))
|
|
4460
|
+
fail3(t("reason.committedDocumentImmutable"));
|
|
4318
4461
|
const clean = {
|
|
4319
4462
|
specs: current.specs.map((s) => ({ ...s, history: before.specs.find((p) => p.id === s.id)?.history ?? [] })),
|
|
4320
|
-
documents: current.documents
|
|
4463
|
+
wiki: { documents: current.wiki.documents, history: before.wiki.history }
|
|
4321
4464
|
};
|
|
4322
4465
|
return comparePreviewBundles(before, clean);
|
|
4323
4466
|
}
|
|
@@ -4332,8 +4475,8 @@ function prepareSpecPreview(beforeValue, currentValue, baseFiles, currentFiles,
|
|
|
4332
4475
|
const writes = /* @__PURE__ */ new Map();
|
|
4333
4476
|
const pending = /* @__PURE__ */ new Map();
|
|
4334
4477
|
const covered = /* @__PURE__ */ new Set();
|
|
4335
|
-
const used = new Set([...before.flatMap((s) => s.history), ...current.flatMap((s) => s.history), ...beforeBundle.
|
|
4336
|
-
const
|
|
4478
|
+
const used = new Set([...before.flatMap((s) => s.history), ...current.flatMap((s) => s.history), ...beforeBundle.wiki.history, ...currentBundle.wiki.history].map((h) => h.id));
|
|
4479
|
+
const wikiPending = [];
|
|
4337
4480
|
const allocate = () => {
|
|
4338
4481
|
for (let attempt = 0; attempt < 100; attempt++) {
|
|
4339
4482
|
const id = generate();
|
|
@@ -4359,21 +4502,16 @@ function prepareSpecPreview(beforeValue, currentValue, baseFiles, currentFiles,
|
|
|
4359
4502
|
if (raw.requirements.length)
|
|
4360
4503
|
fail3(t("reason.documentWithRequirements"));
|
|
4361
4504
|
const ids = raw.documents;
|
|
4362
|
-
let kind;
|
|
4363
4505
|
for (const value2 of ids) {
|
|
4364
|
-
if (typeof value2 !== "string" || !/^
|
|
4506
|
+
if (typeof value2 !== "string" || !/^W-[a-z2-7]{10}$/.test(value2) || covered.has(value2))
|
|
4365
4507
|
return fail3(t("reason.documentTargetInvalid"));
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
fail3(t("reason.singleDocumentKind"));
|
|
4369
|
-
kind = change2.kind;
|
|
4508
|
+
if (!delta.changes.some((c) => c.id === value2))
|
|
4509
|
+
fail3(t("reason.documentUnchanged", { id: value2 }));
|
|
4370
4510
|
covered.add(value2);
|
|
4371
4511
|
}
|
|
4372
4512
|
const documents = [...ids].sort();
|
|
4373
|
-
const
|
|
4374
|
-
|
|
4375
|
-
const reusable = set2.history.slice(oldCount).find((h) => h.reason === reason && equal([...h.documents ?? []].sort(), documents));
|
|
4376
|
-
documentPending.set(kind, [...documentPending.get(kind) ?? [], { id: reusable?.id ?? allocate(), requirements: [], documents, reason }]);
|
|
4513
|
+
const reusable = currentBundle.wiki.history.slice(beforeBundle.wiki.history.length).find((h) => h.reason === reason && equal([...h.documents ?? []].sort(), documents));
|
|
4514
|
+
wikiPending.push({ id: reusable?.id ?? allocate(), requirements: [], documents, reason });
|
|
4377
4515
|
continue;
|
|
4378
4516
|
}
|
|
4379
4517
|
for (const value2 of [...raw.requirements, ...raw.designs ?? []]) {
|
|
@@ -4401,34 +4539,32 @@ function prepareSpecPreview(beforeValue, currentValue, baseFiles, currentFiles,
|
|
|
4401
4539
|
pending.set(specId, [...pending.get(specId) ?? [], record3]);
|
|
4402
4540
|
}
|
|
4403
4541
|
}
|
|
4542
|
+
const historyText = (base, records) => {
|
|
4543
|
+
let text2 = base ?? "";
|
|
4544
|
+
if (records.length)
|
|
4545
|
+
text2 += (text2 && !text2.endsWith("\n") ? "\n" : "") + records.map((h) => JSON.stringify(h) + "\n").join("");
|
|
4546
|
+
return base !== void 0 || records.length ? text2 : null;
|
|
4547
|
+
};
|
|
4404
4548
|
for (const spec2 of current) {
|
|
4405
4549
|
const old = before.find((s) => s.id === spec2.id);
|
|
4406
4550
|
const base = old ? baseFiles.get(historyPath(old)) : void 0;
|
|
4407
4551
|
const records = (pending.get(spec2.id) ?? []).sort((a, b) => (a.requirements[0] ?? a.designs[0]) < (b.requirements[0] ?? b.designs[0]) ? -1 : 1);
|
|
4408
|
-
let text2 = base ?? "";
|
|
4409
|
-
if (records.length)
|
|
4410
|
-
text2 += (text2 && !text2.endsWith("\n") ? "\n" : "") + records.map((h) => JSON.stringify(h) + "\n").join("");
|
|
4411
4552
|
const path3 = historyPath(spec2);
|
|
4412
|
-
const next = base
|
|
4553
|
+
const next = historyText(base, records);
|
|
4413
4554
|
if ((currentFiles.get(path3) ?? null) !== next)
|
|
4414
4555
|
writes.set(path3, next);
|
|
4415
4556
|
}
|
|
4416
|
-
|
|
4417
|
-
const
|
|
4418
|
-
const
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
if (records.length)
|
|
4422
|
-
text2 += (text2 && !text2.endsWith("\n") ? "\n" : "") + records.map((h) => JSON.stringify(h) + "\n").join("");
|
|
4423
|
-
const next = base !== void 0 || records.length ? text2 : null;
|
|
4424
|
-
if ((currentFiles.get(path3) ?? null) !== next)
|
|
4425
|
-
writes.set(path3, next);
|
|
4557
|
+
{
|
|
4558
|
+
const records = wikiPending.sort((a, b) => a.documents[0] < b.documents[0] ? -1 : 1);
|
|
4559
|
+
const next = historyText(baseFiles.get(WIKI_HISTORY_PATH), records);
|
|
4560
|
+
if ((currentFiles.get(WIKI_HISTORY_PATH) ?? null) !== next)
|
|
4561
|
+
writes.set(WIKI_HISTORY_PATH, next);
|
|
4426
4562
|
}
|
|
4427
4563
|
return {
|
|
4428
4564
|
writes,
|
|
4429
4565
|
changes: delta.changes,
|
|
4430
4566
|
specChanges: delta.specChanges,
|
|
4431
|
-
reasons: [...[...pending].flatMap(([specId, records]) => records.map((record3) => ({ specId, ...record3 }))), ...
|
|
4567
|
+
reasons: [...[...pending].flatMap(([specId, records]) => records.map((record3) => ({ specId, ...record3 }))), ...wikiPending.map((record3) => ({ kind: "wiki", ...record3 }))],
|
|
4432
4568
|
withoutReason: delta.changes.filter((c) => !covered.has(c.id)).map((c) => c.id)
|
|
4433
4569
|
};
|
|
4434
4570
|
}
|
|
@@ -4629,7 +4765,7 @@ function createRepositoryReader(cwd, options = {}) {
|
|
|
4629
4765
|
return output2;
|
|
4630
4766
|
};
|
|
4631
4767
|
const discover = async () => {
|
|
4632
|
-
const
|
|
4768
|
+
const scalar2 = async (flag) => decodeGitLine(await invoke(["rev-parse", "--path-format=absolute", flag]));
|
|
4633
4769
|
const flags = ["--is-bare-repository", "--is-inside-git-dir", "--is-inside-work-tree"];
|
|
4634
4770
|
const paths2 = ["--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-object-format"];
|
|
4635
4771
|
let combined;
|
|
@@ -4646,7 +4782,7 @@ function createRepositoryReader(cwd, options = {}) {
|
|
|
4646
4782
|
}
|
|
4647
4783
|
if (bare === "true" || internal === "true" || inside !== "true") throw new RepositoryReadError("UNSUPPORTED_REPOSITORY");
|
|
4648
4784
|
let values = combined.slice(3);
|
|
4649
|
-
if (values.length !== paths2.length) values = await Promise.all(paths2.map(
|
|
4785
|
+
if (values.length !== paths2.length) values = await Promise.all(paths2.map(scalar2));
|
|
4650
4786
|
const [rootPath, gitDir, commonDir, objectFormat] = values;
|
|
4651
4787
|
if (objectFormat !== "sha1" && objectFormat !== "sha256") throw new RepositoryReadError("GIT_UNSUPPORTED");
|
|
4652
4788
|
return { rootPath, gitDir, commonDir, objectFormat };
|
|
@@ -5381,10 +5517,10 @@ function cached(getter) {
|
|
|
5381
5517
|
function nullish(input2) {
|
|
5382
5518
|
return input2 === null || input2 === void 0;
|
|
5383
5519
|
}
|
|
5384
|
-
function cleanRegex(
|
|
5385
|
-
const start =
|
|
5386
|
-
const end =
|
|
5387
|
-
return
|
|
5520
|
+
function cleanRegex(source2) {
|
|
5521
|
+
const start = source2.startsWith("^") ? 1 : 0;
|
|
5522
|
+
const end = source2.endsWith("$") ? source2.length - 1 : source2.length;
|
|
5523
|
+
return source2.slice(start, end);
|
|
5388
5524
|
}
|
|
5389
5525
|
function floatSafeRemainder(val, step) {
|
|
5390
5526
|
const ratio = val / step;
|
|
@@ -5452,28 +5588,28 @@ function putProp(target, key, value) {
|
|
|
5452
5588
|
else
|
|
5453
5589
|
target[key] = value;
|
|
5454
5590
|
}
|
|
5455
|
-
function mirrorShape(target,
|
|
5456
|
-
const raw = sourceShape(
|
|
5591
|
+
function mirrorShape(target, source2, keys2, wrap) {
|
|
5592
|
+
const raw = sourceShape(source2);
|
|
5457
5593
|
for (const key of keys2) {
|
|
5458
5594
|
const desc = Object.getOwnPropertyDescriptor(raw, key);
|
|
5459
5595
|
if (!desc.enumerable)
|
|
5460
5596
|
continue;
|
|
5461
5597
|
if (desc.get) {
|
|
5462
5598
|
deferProp(target, key, () => {
|
|
5463
|
-
const value =
|
|
5599
|
+
const value = source2._zod.def.shape[key];
|
|
5464
5600
|
return wrap ? wrap(value, key) : value;
|
|
5465
5601
|
});
|
|
5466
5602
|
} else
|
|
5467
5603
|
putProp(target, key, wrap ? wrap(desc.value, key) : desc.value);
|
|
5468
5604
|
}
|
|
5469
5605
|
}
|
|
5470
|
-
function mirrorProps(target,
|
|
5471
|
-
for (const key of Reflect.ownKeys(
|
|
5472
|
-
const desc = Object.getOwnPropertyDescriptor(
|
|
5606
|
+
function mirrorProps(target, source2) {
|
|
5607
|
+
for (const key of Reflect.ownKeys(source2)) {
|
|
5608
|
+
const desc = Object.getOwnPropertyDescriptor(source2, key);
|
|
5473
5609
|
if (!desc.enumerable)
|
|
5474
5610
|
continue;
|
|
5475
5611
|
if (desc.get)
|
|
5476
|
-
deferProp(target, key, () =>
|
|
5612
|
+
deferProp(target, key, () => source2[key]);
|
|
5477
5613
|
else
|
|
5478
5614
|
putProp(target, key, desc.value);
|
|
5479
5615
|
}
|
|
@@ -6686,8 +6822,8 @@ var creditCard = /^\d(?:[ -]?\d){11,18}$/;
|
|
|
6686
6822
|
var currencyCode = /^(?:AED|AFN|ALL|AMD|AOA|ARS|AUD|AWG|AZN|BAM|BBD|BDT|BHD|BIF|BMD|BND|BOB|BOV|BRL|BSD|BTN|BWP|BYN|BZD|CAD|CDF|CHE|CHF|CHW|CLF|CLP|CNY|COP|COU|CRC|CUP|CVE|CZK|DJF|DKK|DOP|DZD|EGP|ERN|ETB|EUR|FJD|FKP|GBP|GEL|GHS|GIP|GMD|GNF|GTQ|GYD|HKD|HNL|HTG|HUF|IDR|ILS|INR|IQD|IRR|ISK|JMD|JOD|JPY|KES|KGS|KHR|KMF|KPW|KRW|KWD|KYD|KZT|LAK|LBP|LKR|LRD|LSL|LYD|MAD|MDL|MGA|MKD|MMK|MNT|MOP|MRU|MUR|MVR|MWK|MXN|MXV|MYR|MZN|NAD|NGN|NIO|NOK|NPR|NZD|OMR|PAB|PEN|PGK|PHP|PKR|PLN|PYG|QAR|RON|RSD|RUB|RWF|SAR|SBD|SCR|SDG|SEK|SGD|SHP|SLE|SOS|SRD|SSP|STN|SVC|SYP|SZL|THB|TJS|TMT|TND|TOP|TRY|TTD|TWD|TZS|UAH|UGX|USD|USN|UYI|UYU|UYW|UZS|VED|VES|VND|VUV|WST|XAD|XAF|XAG|XAU|XBA|XBB|XBC|XBD|XCD|XCG|XDR|XOF|XPD|XPF|XPT|XSU|XTS|XUA|XXX|YER|ZAR|ZMW|ZWG)$/;
|
|
6687
6823
|
var iban = /^[A-Z]{2}(?!00|01|99)\d{2}[A-Z0-9]{11,30}$/;
|
|
6688
6824
|
var dateSource = `(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`;
|
|
6689
|
-
function anchor(
|
|
6690
|
-
return new RegExp(`^${
|
|
6825
|
+
function anchor(source2) {
|
|
6826
|
+
return new RegExp(`^${source2}$`);
|
|
6691
6827
|
}
|
|
6692
6828
|
var date = /* @__PURE__ */ anchor(dateSource);
|
|
6693
6829
|
function timeSource(args) {
|
|
@@ -9510,11 +9646,11 @@ var $ZodTemplateLiteral = /* @__PURE__ */ $constructor("$ZodTemplateLiteral", (i
|
|
|
9510
9646
|
const regexParts = [];
|
|
9511
9647
|
for (const part of def.parts) {
|
|
9512
9648
|
if (typeof part === "object" && part !== null) {
|
|
9513
|
-
const
|
|
9514
|
-
if (!
|
|
9649
|
+
const source2 = partPattern(part);
|
|
9650
|
+
if (!source2) {
|
|
9515
9651
|
throw new Error(`Invalid template literal part, no pattern found: ${[...part._zod.traits].shift()}`);
|
|
9516
9652
|
}
|
|
9517
|
-
regexParts.push(cleanRegex(
|
|
9653
|
+
regexParts.push(cleanRegex(source2));
|
|
9518
9654
|
} else if (part === null || primitiveTypes.has(typeof part)) {
|
|
9519
9655
|
regexParts.push(escapeRegex(`${part}`));
|
|
9520
9656
|
} else {
|
|
@@ -17640,9 +17776,9 @@ function withParser(schema, parser) {
|
|
|
17640
17776
|
installCompiledUserMethods(clone2, schema, parser);
|
|
17641
17777
|
return clone2;
|
|
17642
17778
|
}
|
|
17643
|
-
function installCompiledUserMethods(target,
|
|
17779
|
+
function installCompiledUserMethods(target, source2, parser) {
|
|
17644
17780
|
const targetAny = target;
|
|
17645
|
-
const sourceAny =
|
|
17781
|
+
const sourceAny = source2;
|
|
17646
17782
|
if (typeof sourceAny.safeParse === "function") {
|
|
17647
17783
|
const originalSafeParse = sourceAny.safeParse;
|
|
17648
17784
|
targetAny.safeParse = (data, params) => {
|
|
@@ -20237,10 +20373,10 @@ function _stringFormat(Class2, format, fnOrRegex, _params = {}) {
|
|
|
20237
20373
|
|
|
20238
20374
|
// ../../node_modules/.pnpm/zod@4.6.4/node_modules/zod/v4/core/to-json-schema.js
|
|
20239
20375
|
function assignProps(target, ...sources) {
|
|
20240
|
-
for (const
|
|
20241
|
-
for (const key of Reflect.ownKeys(
|
|
20242
|
-
if (Object.prototype.propertyIsEnumerable.call(
|
|
20243
|
-
assignProp(target, key,
|
|
20376
|
+
for (const source2 of sources) {
|
|
20377
|
+
for (const key of Reflect.ownKeys(source2)) {
|
|
20378
|
+
if (Object.prototype.propertyIsEnumerable.call(source2, key)) {
|
|
20379
|
+
assignProp(target, key, source2[key]);
|
|
20244
20380
|
}
|
|
20245
20381
|
}
|
|
20246
20382
|
}
|
|
@@ -24454,44 +24590,67 @@ var browserHttpErrorV1 = external_exports.strictObject({
|
|
|
24454
24590
|
})
|
|
24455
24591
|
});
|
|
24456
24592
|
|
|
24457
|
-
// ../../packages/contracts/dist/versions/
|
|
24458
|
-
var
|
|
24593
|
+
// ../../packages/contracts/dist/versions/browser-session-v2.js
|
|
24594
|
+
var release = external_exports.string().regex(/^\d+\.\d+\.\d+$/);
|
|
24595
|
+
var updateStateV1 = external_exports.strictObject({
|
|
24596
|
+
status: external_exports.enum(["checking", "available", "up-to-date", "unavailable", "disabled"]),
|
|
24597
|
+
latestVersion: release.nullable()
|
|
24598
|
+
}).refine((value) => (value.status === "available" || value.status === "up-to-date") === (value.latestVersion !== null), "latestVersion is set exactly when the check produced a result.");
|
|
24599
|
+
var browserSessionV2 = external_exports.strictObject({
|
|
24600
|
+
contract: external_exports.literal("browser-session"),
|
|
24601
|
+
version: external_exports.literal(2),
|
|
24602
|
+
sessionId: external_exports.uuid(),
|
|
24603
|
+
repository: external_exports.strictObject({
|
|
24604
|
+
key: external_exports.string().regex(/^repo:[0-9a-f]{64}$/),
|
|
24605
|
+
worktreeKey: external_exports.string().regex(/^worktree:[0-9a-f]{64}$/)
|
|
24606
|
+
}),
|
|
24607
|
+
// The running CLI, the same value as `gitifact --version`. A development build may carry a pre-release suffix.
|
|
24608
|
+
cliVersion: external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/),
|
|
24609
|
+
update: updateStateV1
|
|
24610
|
+
});
|
|
24611
|
+
|
|
24612
|
+
// ../../packages/contracts/dist/versions/project-init-v5.js
|
|
24613
|
+
var projectInitV5 = external_exports.discriminatedUnion("ok", [
|
|
24459
24614
|
external_exports.strictObject({
|
|
24460
24615
|
contract: external_exports.literal("project-init"),
|
|
24461
|
-
version: external_exports.literal(
|
|
24616
|
+
version: external_exports.literal(5),
|
|
24462
24617
|
ok: external_exports.literal(true),
|
|
24463
|
-
outcome: external_exports.enum(["planned", "created", "already-initialized"]),
|
|
24618
|
+
outcome: external_exports.enum(["planned", "created", "replaced", "already-initialized"]),
|
|
24464
24619
|
rootPath: external_exports.string(),
|
|
24465
24620
|
configPath: external_exports.literal(".gitifact/config.json"),
|
|
24466
|
-
schemaVersion: external_exports.literal(
|
|
24621
|
+
schemaVersion: external_exports.literal(2),
|
|
24467
24622
|
baseline: external_exports.discriminatedUnion("kind", [
|
|
24468
24623
|
external_exports.strictObject({ kind: external_exports.literal("empty") }),
|
|
24469
24624
|
external_exports.strictObject({ kind: external_exports.literal("commit"), objectFormat: external_exports.enum(["sha1", "sha256"]), commit: external_exports.string() })
|
|
24470
24625
|
]),
|
|
24471
|
-
agentDocs: external_exports.strictObject({ mode: external_exports.enum(["install", "remove", "skip"]), paths: external_exports.array(external_exports.string()) })
|
|
24626
|
+
agentDocs: external_exports.strictObject({ mode: external_exports.enum(["install", "remove", "skip"]), paths: external_exports.array(external_exports.string()) }),
|
|
24627
|
+
update: updateStateV1,
|
|
24628
|
+
// Present only when a newer release exists. The command is for npm global installs; other installs differ.
|
|
24629
|
+
install: external_exports.strictObject({ npmGlobal: external_exports.string().min(1) }).nullable()
|
|
24472
24630
|
}),
|
|
24473
|
-
external_exports.strictObject({ contract: external_exports.literal("project-init"), version: external_exports.literal(
|
|
24631
|
+
external_exports.strictObject({ contract: external_exports.literal("project-init"), version: external_exports.literal(5), ok: external_exports.literal(false), error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }) })
|
|
24474
24632
|
]);
|
|
24475
24633
|
|
|
24476
|
-
// ../../packages/contracts/dist/versions/browser-specs-
|
|
24634
|
+
// ../../packages/contracts/dist/versions/browser-specs-v2.js
|
|
24477
24635
|
var requirement = external_exports.strictObject({ id: external_exports.string(), title: external_exports.string(), body: external_exports.string() });
|
|
24478
24636
|
var contributor = external_exports.strictObject({ email: external_exports.string(), name: external_exports.string(), commits: external_exports.number().int().nonnegative(), latest: external_exports.string() });
|
|
24637
|
+
var source = external_exports.strictObject({ title: external_exports.string(), path: external_exports.string().optional(), url: external_exports.string().optional(), note: external_exports.string().optional() });
|
|
24479
24638
|
var feature = external_exports.strictObject({
|
|
24480
24639
|
id: external_exports.string(),
|
|
24481
24640
|
path: external_exports.string(),
|
|
24482
24641
|
title: external_exports.string(),
|
|
24483
24642
|
description: external_exports.string(),
|
|
24484
24643
|
requirements: external_exports.array(requirement),
|
|
24485
|
-
design: external_exports.strictObject({ title: external_exports.string(), body: external_exports.string(), requirements: external_exports.array(external_exports.string()) }).optional(),
|
|
24644
|
+
design: external_exports.strictObject({ title: external_exports.string(), body: external_exports.string(), requirements: external_exports.array(external_exports.string()), sources: external_exports.array(source) }).optional(),
|
|
24486
24645
|
// Authors of commits touching the feature folder (current or legacy store) and the latest such commit; empty until first committed.
|
|
24487
24646
|
contributors: external_exports.array(contributor),
|
|
24488
24647
|
updatedAt: external_exports.string().nullable()
|
|
24489
24648
|
});
|
|
24490
|
-
var snapshot = requirement.extend({ specId: external_exports.string(), path: external_exports.string() }).nullable();
|
|
24491
|
-
var document = external_exports.strictObject({ id: external_exports.string(),
|
|
24492
|
-
var
|
|
24649
|
+
var snapshot = requirement.extend({ specId: external_exports.string(), path: external_exports.string(), sources: external_exports.array(source).optional() }).nullable();
|
|
24650
|
+
var document = external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string(), body: external_exports.string(), updatedAt: external_exports.string().nullable() });
|
|
24651
|
+
var browserSpecsV2 = external_exports.strictObject({
|
|
24493
24652
|
contract: external_exports.literal("browser-specs"),
|
|
24494
|
-
version: external_exports.literal(
|
|
24653
|
+
version: external_exports.literal(2),
|
|
24495
24654
|
sessionId: external_exports.string(),
|
|
24496
24655
|
head: external_exports.string().nullable(),
|
|
24497
24656
|
observedAt: external_exports.string(),
|
|
@@ -24507,7 +24666,7 @@ var browserSpecsV1 = external_exports.strictObject({
|
|
|
24507
24666
|
committer: external_exports.string(),
|
|
24508
24667
|
message: external_exports.string(),
|
|
24509
24668
|
id: external_exports.string(),
|
|
24510
|
-
kind: external_exports.enum(["requirement", "design", "
|
|
24669
|
+
kind: external_exports.enum(["requirement", "design", "wiki"]).optional(),
|
|
24511
24670
|
types: external_exports.array(external_exports.enum(["created", "modified", "moved", "deleted"])),
|
|
24512
24671
|
before: snapshot,
|
|
24513
24672
|
after: snapshot,
|
|
@@ -24544,26 +24703,7 @@ var changelogV1 = external_exports.strictObject({
|
|
|
24544
24703
|
}
|
|
24545
24704
|
});
|
|
24546
24705
|
|
|
24547
|
-
// ../../packages/contracts/dist/versions/
|
|
24548
|
-
var release = external_exports.string().regex(/^\d+\.\d+\.\d+$/);
|
|
24549
|
-
var updateStateV1 = external_exports.strictObject({
|
|
24550
|
-
status: external_exports.enum(["checking", "available", "up-to-date", "unavailable", "disabled"]),
|
|
24551
|
-
latestVersion: release.nullable()
|
|
24552
|
-
}).refine((value) => (value.status === "available" || value.status === "up-to-date") === (value.latestVersion !== null), "latestVersion is set exactly when the check produced a result.");
|
|
24553
|
-
var browserSessionV2 = external_exports.strictObject({
|
|
24554
|
-
contract: external_exports.literal("browser-session"),
|
|
24555
|
-
version: external_exports.literal(2),
|
|
24556
|
-
sessionId: external_exports.uuid(),
|
|
24557
|
-
repository: external_exports.strictObject({
|
|
24558
|
-
key: external_exports.string().regex(/^repo:[0-9a-f]{64}$/),
|
|
24559
|
-
worktreeKey: external_exports.string().regex(/^worktree:[0-9a-f]{64}$/)
|
|
24560
|
-
}),
|
|
24561
|
-
// The running CLI, the same value as `gitifact --version`. A development build may carry a pre-release suffix.
|
|
24562
|
-
cliVersion: external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/),
|
|
24563
|
-
update: updateStateV1
|
|
24564
|
-
});
|
|
24565
|
-
|
|
24566
|
-
// ../../packages/contracts/dist/versions/update-v2.js
|
|
24706
|
+
// ../../packages/contracts/dist/versions/update-v3.js
|
|
24567
24707
|
var commitReason = external_exports.enum(["untracked", "other-changes", "commit-failed"]);
|
|
24568
24708
|
var agentDocsCommit = external_exports.strictObject({
|
|
24569
24709
|
state: external_exports.enum(["not-requested", "nothing", "committed", "skipped"]),
|
|
@@ -24573,10 +24713,10 @@ var agentDocsCommit = external_exports.strictObject({
|
|
|
24573
24713
|
reason: commitReason.nullable(),
|
|
24574
24714
|
detail: external_exports.string().nullable()
|
|
24575
24715
|
}).refine((value) => value.state === "committed" === (value.commit !== null) && value.state === "skipped" === (value.reason !== null), "commit is set exactly when committed, reason exactly when skipped.");
|
|
24576
|
-
var
|
|
24716
|
+
var updateV3 = external_exports.discriminatedUnion("ok", [
|
|
24577
24717
|
external_exports.strictObject({
|
|
24578
24718
|
contract: external_exports.literal("update"),
|
|
24579
|
-
version: external_exports.literal(
|
|
24719
|
+
version: external_exports.literal(3),
|
|
24580
24720
|
ok: external_exports.literal(true),
|
|
24581
24721
|
cliVersion: external_exports.string().min(1),
|
|
24582
24722
|
update: updateStateV1,
|
|
@@ -24584,10 +24724,11 @@ var updateV2 = external_exports.discriminatedUnion("ok", [
|
|
|
24584
24724
|
install: external_exports.strictObject({ npmGlobal: external_exports.string().min(1) }).nullable(),
|
|
24585
24725
|
// refreshed: blocks whose version line differed were rewritten. current: every block already matched.
|
|
24586
24726
|
// not-initialized: no .gitifact config here, so only the version check ran. no-block: no file carries a block.
|
|
24587
|
-
|
|
24727
|
+
// missing: files `gitifact init` would add and update does not create, e.g. CLAUDE.md when only AGENTS.md has the block.
|
|
24728
|
+
agentDocs: external_exports.strictObject({ state: external_exports.enum(["refreshed", "current", "not-initialized", "no-block"]), paths: external_exports.array(external_exports.string()), missing: external_exports.array(external_exports.string()) }),
|
|
24588
24729
|
commit: agentDocsCommit
|
|
24589
24730
|
}),
|
|
24590
|
-
external_exports.strictObject({ contract: external_exports.literal("update"), version: external_exports.literal(
|
|
24731
|
+
external_exports.strictObject({ contract: external_exports.literal("update"), version: external_exports.literal(3), ok: external_exports.literal(false), error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }) })
|
|
24591
24732
|
]);
|
|
24592
24733
|
|
|
24593
24734
|
// src/shared/i18n/ko/messages.json
|
|
@@ -24605,7 +24746,7 @@ var messages_default2 = {
|
|
|
24605
24746
|
"help.initRemoveAgents": "GITIFACT \uBE14\uB85D\uC744 \uC81C\uAC70",
|
|
24606
24747
|
"help.initSkipAgents": "\uC9C0\uCE68 \uD30C\uC77C\uC744 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uC74C",
|
|
24607
24748
|
"help.docs": "\uC5D0\uC774\uC804\uD2B8\uC6A9 \uC791\uC5C5 \uC9C0\uCE68 \uCD9C\uB825 (\uC8FC\uC81C \uC5C6\uC774 \uC2E4\uD589\uD558\uBA74 \uBAA9\uB85D)",
|
|
24608
|
-
"help.docsTopic": "workflow, spec, design,
|
|
24749
|
+
"help.docsTopic": "workflow, spec, design, wiki, commit \uC911 \uD558\uB098",
|
|
24609
24750
|
"help.migrate": ".tryce \uC800\uC7A5\uC18C\uB97C .gitifact\uB85C \uC804\uD658 (\uACBD\uB85C\uC640 \uB9C8\uCEE4\uB9CC \uBC14\uAFB8\uACE0 ID\xB7\uC774\uC720\xB7\uC774\uB825\uC740 \uBCF4\uC874)",
|
|
24610
24751
|
"help.migrateDryRun": "\uD30C\uC77C\uC744 \uBC14\uAFB8\uC9C0 \uC54A\uACE0 \uC804\uD658 \uACC4\uD68D \uD655\uC778",
|
|
24611
24752
|
"help.spec": "Markdown \uBA85\uC138 \uC791\uC131\xB7\uC870\uD68C\xB7Git \uAE30\uB85D",
|
|
@@ -24622,11 +24763,11 @@ var messages_default2 = {
|
|
|
24622
24763
|
"help.specDiffFrom": "\uC774\uC804 \uCEE4\uBC0B",
|
|
24623
24764
|
"help.specDiffTo": "\uC774\uD6C4 \uCEE4\uBC0B",
|
|
24624
24765
|
"help.specChanges": "HEAD \uB300\uBE44 \uCD5C\uC885 \uBA85\uC138 \uCC28\uC774\uC640 \uBBF8\uCEE4\uBC0B \uC774\uC720 \uC870\uD68C",
|
|
24625
|
-
"help.specWorking": "\uD604\uC7AC \uBA85\uC138\xB7\
|
|
24766
|
+
"help.specWorking": "\uD604\uC7AC \uBA85\uC138\xB7\uC704\uD0A4 \uC804\uCCB4\uC640 stamp, \uC785\uB825 \uD30C\uC77C \uACBD\uB85C \uC870\uD68C",
|
|
24626
24767
|
"help.specWorkingStamp": "stamp\uC640 \uC785\uB825 \uD30C\uC77C \uACBD\uB85C\uB9CC \uCD9C\uB825",
|
|
24627
|
-
"help.specWorkingFeature": "\uD55C \uAE30\uB2A5 \uD3F4\uB354\uC758 \uBA85\uC138\uB9CC \uCD9C\uB825 (\
|
|
24768
|
+
"help.specWorkingFeature": "\uD55C \uAE30\uB2A5 \uD3F4\uB354\uC758 \uBA85\uC138\uB9CC \uCD9C\uB825 (\uC704\uD0A4 \uC81C\uC678)",
|
|
24628
24769
|
"help.specWorkingIds": "\uBCF8\uBB38 \uC5C6\uC774 ID\xB7\uC81C\uBAA9\xB7\uACBD\uB85C\uB9CC \uCD9C\uB825",
|
|
24629
|
-
"help.specSave": "\uC694\uAD6C\uC0AC\uD56D\xB7\uC124\uACC4\xB7\
|
|
24770
|
+
"help.specSave": "\uC694\uAD6C\uC0AC\uD56D\xB7\uC124\uACC4\xB7\uC704\uD0A4 \uC800\uC7A5",
|
|
24630
24771
|
"help.specRead": "\uCEE4\uBC0B \uC2DC\uC810\uC758 \uBA85\uC138 \uC870\uD68C",
|
|
24631
24772
|
"help.specDiff": "\uB450 \uCEE4\uBC0B \uC0AC\uC774\uC758 \uBA85\uC138 \uBCC0\uACBD \uC870\uD68C",
|
|
24632
24773
|
"git.operationInProgress": "Git \uC791\uC5C5\uC774 \uC9C4\uD589 \uC911\uC785\uB2C8\uB2E4: {marker}",
|
|
@@ -24722,7 +24863,6 @@ var messages_default2 = {
|
|
|
24722
24863
|
"server.crossSite": "\uC678\uBD80 \uD398\uC774\uC9C0\uC758 \uC694\uCCAD\uC744 \uD5C8\uC6A9\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
24723
24864
|
"server.noQuery": "\uC870\uD68C \uC778\uC790\uB97C \uBC1B\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
24724
24865
|
"server.methodNotAllowed": "\uD5C8\uC6A9\uD558\uC9C0 \uC54A\uB294 \uBA54\uC11C\uB4DC\uC785\uB2C8\uB2E4.",
|
|
24725
|
-
"server.productImageNotFound": "\uC81C\uD488 \uC774\uBBF8\uC9C0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
24726
24866
|
"server.apiNotFound": "API\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
24727
24867
|
"server.sessionChanged": "\uC11C\uBC84 \uC138\uC158\uC774 \uBCC0\uACBD\uB410\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC5F0\uACB0\uD558\uC138\uC694.",
|
|
24728
24868
|
"server.badRange": "\uC870\uD68C \uBC94\uC704\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
@@ -24777,6 +24917,7 @@ var messages_default2 = {
|
|
|
24777
24917
|
"init.configDeleted": "\uAE30\uC874 \uC124\uC815\uC758 \uC0AD\uC81C\uB97C \uC2E0\uADDC \uB3C4\uC785\uC73C\uB85C \uCC98\uB9AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
24778
24918
|
"init.existingSpecs": "\uAE30\uC874 specs \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
24779
24919
|
"init.orphanStore": "\uC124\uC815 \uC5C6\uB294 .gitifact \uC790\uB8CC\uB97C \uC790\uB3D9 \uCC44\uD0DD\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
24920
|
+
"init.legacyRecords": "\uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0\uB294 \uC774\uC804 \uC800\uC7A5 \uADDC\uC57D(schemaVersion {version})\uC73C\uB85C \uC4F4 \uBA85\uC138\uB098 \uAE30\uB85D\uC774 \uC788\uC5B4 \uC124\uC815\uC744 \uBC14\uAFB8\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC774 CLI\uB294 \uC774\uC804 \uADDC\uC57D\uC744 \uC804\uD658\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uAE30\uB85D\uC744 \uB2E4\uB978 \uACF3\uC5D0 \uBCF4\uAD00\uD558\uACE0 .gitifact\uC5D0 config.json\uB9CC \uB0A8\uAE34 \uB4A4 \uB2E4\uC2DC init \uD558\uC138\uC694.",
|
|
24780
24921
|
"init.headOrIndexChanged": "HEAD \uB610\uB294 index\uAC00 \uBCC0\uACBD\uB410\uC2B5\uB2C8\uB2E4.",
|
|
24781
24922
|
"init.configAppeared": "\uB2E4\uB978 \uC2E4\uD589\uC774 \uC124\uC815\uC744 \uB9CC\uB4E4\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
24782
24923
|
"init.changedAfterWrite": "\uC124\uC815 \uC0DD\uC131 \uD6C4 \uC785\uB825\uC774 \uBCC0\uACBD\uB410\uC2B5\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C\uC744 \uD655\uC778\uD558\uC138\uC694.",
|
|
@@ -24786,6 +24927,7 @@ var messages_default2 = {
|
|
|
24786
24927
|
"init.text.blockUpdated": "\uBE14\uB85D \uAC31\uC2E0",
|
|
24787
24928
|
"init.text.storage": "\uC800\uC7A5 \uADDC\uC57D",
|
|
24788
24929
|
"init.text.agentDocs": "\uC5D0\uC774\uC804\uD2B8 \uC9C0\uCE68",
|
|
24930
|
+
"init.text.updateAvailable": "\uC0C8 \uBC84\uC804 {version}\uC774 \uC788\uC2B5\uB2C8\uB2E4. {command}\uB85C \uC124\uCE58\uD55C \uB4A4 gitifact init\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 \uC9C0\uCE68 \uBE14\uB85D\uC774 \uC0C8 \uBC84\uC804\uC73C\uB85C \uAC31\uC2E0\uB429\uB2C8\uB2E4.",
|
|
24789
24931
|
"init.failed": "\uCD08\uAE30\uD654\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uD30C\uC77C \uC811\uADFC \uAD8C\uD55C\uACFC \uC0DD\uC131\uB41C \uC124\uC815\xB7\uC784\uC2DC \uD30C\uC77C\uC744 \uD655\uC778\uD558\uC138\uC694.",
|
|
24790
24932
|
"config.storeNotDirectory": ".gitifact\uB294 \uB9C1\uD06C\uAC00 \uC544\uB2CC \uC77C\uBC18 \uB514\uB809\uD130\uB9AC\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
|
|
24791
24933
|
"config.migrateFirst": ".tryce \uC800\uC7A5\uC18C\uC785\uB2C8\uB2E4. gitifact migrate\uB85C .gitifact\uC5D0 \uC804\uD658\uD558\uC138\uC694.",
|
|
@@ -24820,8 +24962,7 @@ var messages_default2 = {
|
|
|
24820
24962
|
"browser.assetsUnreadable": "\uBE0C\uB77C\uC6B0\uC800 \uBE4C\uB4DC \uC790\uC0B0\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. pnpm build\uB85C \uB2E4\uC2DC \uBE4C\uB4DC\uD558\uC138\uC694.",
|
|
24821
24963
|
"docs.summary.workflow": "\uC2DC\uC791 \uC2DC \uD655\uC778\uD560 \uAC83, \uC694\uAD6C\uC0AC\uD56D\uC73C\uB85C \uB0A8\uAE38 \uC694\uCCAD\uC758 \uAD6C\uBD84, \uB9C8\uBB34\uB9AC \uBCF4\uACE0",
|
|
24822
24964
|
"docs.summary.spec": "\uC0AC\uC6A9\uC790 \uC2A4\uD1A0\uB9AC\xB7\uC218\uC6A9 \uC870\uAC74 \uD615\uC2DD, ID \uADDC\uCE59, spec save \uC785\uB825",
|
|
24823
|
-
"docs.summary.design": "design.md \uBAA9\uCC28, \uCC38\uC870 \uC8FC\uC11D, \uAC1C\uC815 \uBC29\uC2DD",
|
|
24824
|
-
"docs.summary.product": "PRODUCT.md\uC640 guides \uBB38\uC11C\uC758 \uC5ED\uD560\uACFC \uC800\uC7A5 \uBA85\uB839",
|
|
24965
|
+
"docs.summary.design": "design.md \uBAA9\uCC28, \uCC38\uC870 \uC8FC\uC11D\uACFC \uCC38\uACE0 \uBB38\uC11C, \uAC1C\uC815 \uBC29\uC2DD",
|
|
24825
24966
|
"docs.summary.commit": "spec commit \uC785\uB825, \uC774\uC720 \uAE30\uB85D, \uC2E4\uD328 \uD6C4 \uBCF5\uAD6C",
|
|
24826
24967
|
"help.browserNoUpdateCheck": "\uC0C8 \uBC84\uC804 \uD655\uC778\uC744 \uC704\uD55C npm \uB808\uC9C0\uC2A4\uD2B8\uB9AC \uC870\uD68C\uB97C \uB054 (GITIFACT_NO_UPDATE_CHECK\uC640 \uAC19\uC74C)",
|
|
24827
24968
|
"help.updateCommit": "\uAC31\uC2E0\uD55C GITIFACT \uBE14\uB85D\uC774 HEAD\uC640 \uBE14\uB85D \uC548\uC5D0\uC11C\uB9CC \uB2E4\uB974\uBA74 \uADF8 \uD30C\uC77C\uB9CC \uACE0\uC815 \uBA54\uC2DC\uC9C0\uB85C \uCEE4\uBC0B",
|
|
@@ -24840,13 +24981,19 @@ var messages_default2 = {
|
|
|
24840
24981
|
"update.text.blockCurrent": "\uC9C0\uCE68 \uBE14\uB85D\uC740 \uC774\uBBF8 \uD604\uC7AC \uBC84\uC804\uC785\uB2C8\uB2E4: {paths}",
|
|
24841
24982
|
"update.text.noBlock": "GITIFACT \uBE14\uB85D\uC774 \uC788\uB294 \uC9C0\uCE68 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. gitifact init\uC73C\uB85C \uC124\uCE58\uD558\uC138\uC694.",
|
|
24842
24983
|
"update.text.notInitialized": "\uCD08\uAE30\uD654\uB41C \uD504\uB85C\uC81D\uD2B8\uAC00 \uC544\uB2C8\uC5B4\uC11C \uBC84\uC804\uB9CC \uD655\uC778\uD588\uC2B5\uB2C8\uB2E4.",
|
|
24984
|
+
"update.text.missing": "{paths} \uD30C\uC77C\uC774 \uC5C6\uC5B4 Claude Code\uAC00 AGENTS.md\uC758 \uC9C0\uCE68\uC744 \uC77D\uC9C0 \uBABB\uD569\uB2C8\uB2E4. gitifact init\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 AGENTS.md\uB97C \uAC00\uC838\uC624\uB294 \uD30C\uC77C\uC744 \uB9CC\uB4ED\uB2C8\uB2E4.",
|
|
24843
24985
|
"update.text.committed": "\uC9C0\uCE68 \uBE14\uB85D \uCEE4\uBC0B: {commit} ({paths})",
|
|
24844
24986
|
"update.text.commitNothing": "\uCEE4\uBC0B\uD560 \uC9C0\uCE68 \uBE14\uB85D \uBCC0\uACBD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
24845
24987
|
"update.text.commitSkipped.untracked": "\uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. Git\uC774 \uCD94\uC801\uD558\uC9C0 \uC54A\uB294 \uC9C0\uCE68 \uD30C\uC77C\uC785\uB2C8\uB2E4: {paths}",
|
|
24846
24988
|
"update.text.commitSkipped.other-changes": "\uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uBE14\uB85D \uBC16\uC5D0\uB3C4 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC740 \uC218\uC815\uC774 \uC788\uC2B5\uB2C8\uB2E4: {paths}",
|
|
24847
24989
|
"update.text.commitSkipped.commit-failed": "\uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. git commit\uC774 \uAC70\uBD80\uB410\uACE0 \uAC31\uC2E0\uD55C \uD30C\uC77C\uC740 \uADF8\uB300\uB85C \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4: {paths}",
|
|
24848
24990
|
"update.commitResultUnknown": "\uCEE4\uBC0B \uACB0\uACFC\uB97C \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. HEAD {commit}\uC758 \uBD80\uBAA8\uC640 \uBCC0\uACBD \uD30C\uC77C\uC744 \uC9C1\uC811 \uD655\uC778\uD558\uC138\uC694.",
|
|
24849
|
-
"update.failed": "\uC5C5\uB370\uC774\uD2B8 \uD655\uC778\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4."
|
|
24991
|
+
"update.failed": "\uC5C5\uB370\uC774\uD2B8 \uD655\uC778\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.",
|
|
24992
|
+
"docs.summary.wiki": "\uD504\uB85C\uC81D\uD2B8 \uC704\uD0A4\uC758 \uD615\uC2DD\uACFC \uC800\uC7A5 \uBA85\uB839, \uC5D0\uC14B \uADDC\uCE59, \uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uC704\uD0A4 \uC6B4\uC601 \uBC29\uCE68(wiki/README.md)",
|
|
24993
|
+
"server.assetNotFound": "\uC5D0\uC14B\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
24994
|
+
"docs.wikiPolicyProject": "\uC6B4\uC601 \uBC29\uCE68 (.gitifact/wiki/README.md)",
|
|
24995
|
+
"docs.wikiPolicyDefault": "\uC6B4\uC601 \uBC29\uCE68 (\uAE30\uBCF8\uAC12. .gitifact/wiki/README.md\uAC00 \uC5C6\uC5B4 \uB0B4\uC7A5 \uBC29\uCE68\uC744 \uC2E3\uB294\uB2E4)",
|
|
24996
|
+
"init.wikiReadmeTitle": "\uC704\uD0A4 \uC6B4\uC601 \uBC29\uCE68"
|
|
24850
24997
|
};
|
|
24851
24998
|
|
|
24852
24999
|
// src/shared/i18n/index.ts
|
|
@@ -24922,8 +25069,8 @@ async function runStatus(format) {
|
|
|
24922
25069
|
import { createServer } from "node:http";
|
|
24923
25070
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
24924
25071
|
import { fileURLToPath } from "node:url";
|
|
24925
|
-
import { lstat as
|
|
24926
|
-
import { join as
|
|
25072
|
+
import { lstat as lstat4, readFile as readFile4 } from "node:fs/promises";
|
|
25073
|
+
import { join as join5 } from "node:path";
|
|
24927
25074
|
|
|
24928
25075
|
// src/server/assets.ts
|
|
24929
25076
|
import { readdir, readFile } from "node:fs/promises";
|
|
@@ -24956,6 +25103,7 @@ function contentType(path3) {
|
|
|
24956
25103
|
if (/\.jpe?g$/.test(path3)) return "image/jpeg";
|
|
24957
25104
|
if (path3.endsWith(".gif")) return "image/gif";
|
|
24958
25105
|
if (path3.endsWith(".webp")) return "image/webp";
|
|
25106
|
+
if (path3.endsWith(".pdf")) return "application/pdf";
|
|
24959
25107
|
return "application/octet-stream";
|
|
24960
25108
|
}
|
|
24961
25109
|
|
|
@@ -25062,7 +25210,7 @@ function commandScoped(key, load) {
|
|
|
25062
25210
|
}
|
|
25063
25211
|
|
|
25064
25212
|
// src/adapters/git/spec-preview-reader.ts
|
|
25065
|
-
var RECORD_PATHSPECS = [...STORE_DIRS.map((d) => d + "/spec/"),
|
|
25213
|
+
var RECORD_PATHSPECS = [...STORE_DIRS.map((d) => d + "/spec/"), WIKI_DIR + "/"];
|
|
25066
25214
|
var LegacyBaselineError = class extends SpecPreviewError {
|
|
25067
25215
|
};
|
|
25068
25216
|
function specPreviewReader(cwd) {
|
|
@@ -25096,7 +25244,7 @@ function specPreviewReader(cwd) {
|
|
|
25096
25244
|
const [, mode, oid2, stage, path3] = match;
|
|
25097
25245
|
if (!["100644", "100755"].includes(mode) || stage !== "0") throw new SpecPreviewError(t2("reader.stagingConflictOrLink"));
|
|
25098
25246
|
if (!recordPathPattern.test(path3)) {
|
|
25099
|
-
if (
|
|
25247
|
+
if (path3.startsWith(WIKI_DIR + "/")) continue;
|
|
25100
25248
|
throw new SpecPreviewError(t2("reader.stagingUnsupported"));
|
|
25101
25249
|
}
|
|
25102
25250
|
files.set(path3, oid2);
|
|
@@ -25192,38 +25340,85 @@ function specPreviewReader(cwd) {
|
|
|
25192
25340
|
}
|
|
25193
25341
|
|
|
25194
25342
|
// src/adapters/filesystem/spec-preview-store.ts
|
|
25195
|
-
import { lstat as
|
|
25196
|
-
import { join as
|
|
25343
|
+
import { lstat as lstat3, readdir as readdir3, readFile as readFile3, mkdir as mkdir2, writeFile, rename as rename2, unlink as unlink2, rmdir } from "node:fs/promises";
|
|
25344
|
+
import { join as join4, dirname } from "node:path";
|
|
25197
25345
|
import { createHash as createHash2, randomBytes } from "node:crypto";
|
|
25198
25346
|
|
|
25199
|
-
// src/adapters/filesystem/
|
|
25200
|
-
import { lstat,
|
|
25347
|
+
// src/adapters/filesystem/working-warnings.ts
|
|
25348
|
+
import { lstat, readdir as readdir2 } from "node:fs/promises";
|
|
25201
25349
|
import { join as join2 } from "node:path";
|
|
25350
|
+
var info = async (path3) => lstat(path3).catch((e) => {
|
|
25351
|
+
if (e.code === "ENOENT") return void 0;
|
|
25352
|
+
throw e;
|
|
25353
|
+
});
|
|
25354
|
+
var posix = (path3) => path3.split("\\").join("/");
|
|
25355
|
+
async function listAssets(root) {
|
|
25356
|
+
const out = [];
|
|
25357
|
+
async function visit2(relative, depth) {
|
|
25358
|
+
const stat3 = await info(join2(root, relative));
|
|
25359
|
+
if (!stat3 || stat3.isSymbolicLink()) return;
|
|
25360
|
+
if (stat3.isDirectory()) {
|
|
25361
|
+
if (depth > 8 || out.length > 4e3) return;
|
|
25362
|
+
for (const name of (await readdir2(join2(root, relative))).sort()) await visit2(relative + "/" + name, depth + 1);
|
|
25363
|
+
} else if (stat3.isFile()) out.push({ path: posix(relative), bytes: stat3.size });
|
|
25364
|
+
}
|
|
25365
|
+
await visit2(ASSETS_DIR, 0);
|
|
25366
|
+
return out;
|
|
25367
|
+
}
|
|
25368
|
+
async function workingWarnings(root, bundle) {
|
|
25369
|
+
const warnings = designReferenceWarnings(bundle.specs);
|
|
25370
|
+
const records = /* @__PURE__ */ new Set([...bundle.specs.flatMap((s) => [s.path, ...s.design ? [s.path.replace(/requirements\.md$/, "design.md")] : []]), ...bundle.wiki.documents.map((d) => d.path)]);
|
|
25371
|
+
const assets = await listAssets(root);
|
|
25372
|
+
const assetPaths = new Set(assets.map((a) => a.path));
|
|
25373
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
25374
|
+
for (const { from, link: link3, target } of documentLinks(bundle)) {
|
|
25375
|
+
if (records.has(target)) continue;
|
|
25376
|
+
if (assetPaths.has(target)) {
|
|
25377
|
+
referenced.add(target);
|
|
25378
|
+
continue;
|
|
25379
|
+
}
|
|
25380
|
+
const stat3 = target.startsWith(".gitifact/") ? void 0 : await info(join2(root, ...target.split("/")));
|
|
25381
|
+
if (!stat3?.isFile()) warnings.push({ code: "MISSING_LINK_TARGET", path: from, target, link: link3 });
|
|
25382
|
+
}
|
|
25383
|
+
let total = 0;
|
|
25384
|
+
for (const asset of assets) {
|
|
25385
|
+
total += asset.bytes;
|
|
25386
|
+
if (asset.bytes > ASSET_SIZE_LIMIT) warnings.push({ code: "ASSET_SIZE", path: asset.path, bytes: asset.bytes });
|
|
25387
|
+
if (!RECOMMENDED_ASSET_EXTENSIONS.includes(assetExtension(asset.path))) warnings.push({ code: "ASSET_EXTENSION", path: asset.path });
|
|
25388
|
+
if (!referenced.has(asset.path)) warnings.push({ code: "UNREFERENCED_ASSET", path: asset.path });
|
|
25389
|
+
}
|
|
25390
|
+
if (total > ASSETS_TOTAL_LIMIT) warnings.push({ code: "ASSETS_TOTAL_SIZE", bytes: total });
|
|
25391
|
+
return warnings;
|
|
25392
|
+
}
|
|
25393
|
+
|
|
25394
|
+
// src/adapters/filesystem/config-file.ts
|
|
25395
|
+
import { lstat as lstat2, readFile as readFile2, realpath, mkdir, open as open2, link, rename, unlink } from "node:fs/promises";
|
|
25396
|
+
import { join as join3 } from "node:path";
|
|
25202
25397
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
25203
25398
|
var missing = (error62) => error62.code === "ENOENT";
|
|
25204
|
-
var fileInfo = (path3) =>
|
|
25399
|
+
var fileInfo = (path3) => lstat2(path3).catch((error62) => {
|
|
25205
25400
|
if (missing(error62)) return void 0;
|
|
25206
25401
|
throw error62;
|
|
25207
25402
|
});
|
|
25208
25403
|
async function configDirectory(root) {
|
|
25209
|
-
const path3 =
|
|
25210
|
-
const
|
|
25211
|
-
if (
|
|
25212
|
-
return
|
|
25404
|
+
const path3 = join3(root, ".gitifact");
|
|
25405
|
+
const info7 = await fileInfo(path3);
|
|
25406
|
+
if (info7 && (!info7.isDirectory() || info7.isSymbolicLink())) throw new InitError("PATH_CONFLICT", t2("config.storeNotDirectory"));
|
|
25407
|
+
return info7;
|
|
25213
25408
|
}
|
|
25214
25409
|
async function readConfigFile(root) {
|
|
25215
25410
|
await configDirectory(root);
|
|
25216
|
-
const path3 =
|
|
25217
|
-
const
|
|
25218
|
-
if (!
|
|
25219
|
-
if (await fileInfo(
|
|
25411
|
+
const path3 = join3(root, ".gitifact", "config.json");
|
|
25412
|
+
const info7 = await fileInfo(path3);
|
|
25413
|
+
if (!info7) {
|
|
25414
|
+
if (await fileInfo(join3(root, ".tryce", "config.json"))) throw new InitError("MIGRATION_REQUIRED", t2("config.migrateFirst"));
|
|
25220
25415
|
return void 0;
|
|
25221
25416
|
}
|
|
25222
|
-
if (!
|
|
25417
|
+
if (!info7.isFile() || info7.isSymbolicLink() || info7.size > 65536) throw new InitError("PATH_CONFLICT", t2("config.fileShape"));
|
|
25223
25418
|
const bytes = await readFile2(path3);
|
|
25224
25419
|
const after = await fileInfo(path3);
|
|
25225
25420
|
await configDirectory(root);
|
|
25226
|
-
if (!after || after.ino !==
|
|
25421
|
+
if (!after || after.ino !== info7.ino || after.dev !== info7.dev || after.mtimeMs !== info7.mtimeMs || bytes.length > 65536) {
|
|
25227
25422
|
throw new InitError("INPUT_CHANGED", t2("config.changedWhileReading"));
|
|
25228
25423
|
}
|
|
25229
25424
|
try {
|
|
@@ -25232,8 +25427,8 @@ async function readConfigFile(root) {
|
|
|
25232
25427
|
throw new InitError("INVALID_CONFIG", t2("config.notUtf8Json"));
|
|
25233
25428
|
}
|
|
25234
25429
|
}
|
|
25235
|
-
async function publishConfig(root, text2, recheck, beforePublish) {
|
|
25236
|
-
const directory =
|
|
25430
|
+
async function publishConfig(root, text2, recheck, beforePublish, replace = false) {
|
|
25431
|
+
const directory = join3(root, ".gitifact");
|
|
25237
25432
|
await configDirectory(root);
|
|
25238
25433
|
await mkdir(directory).catch((error62) => {
|
|
25239
25434
|
if (error62.code !== "EEXIST") throw error62;
|
|
@@ -25246,7 +25441,7 @@ async function publishConfig(root, text2, recheck, beforePublish) {
|
|
|
25246
25441
|
throw new InitError("INPUT_CHANGED", t2("config.directoryChanged"));
|
|
25247
25442
|
}
|
|
25248
25443
|
};
|
|
25249
|
-
const temporary =
|
|
25444
|
+
const temporary = join3(directory, ".init-" + randomUUID3() + ".tmp");
|
|
25250
25445
|
const handle = await open2(temporary, "wx", 384);
|
|
25251
25446
|
const tempOwner = await handle.stat();
|
|
25252
25447
|
try {
|
|
@@ -25263,7 +25458,8 @@ async function publishConfig(root, text2, recheck, beforePublish) {
|
|
|
25263
25458
|
const bytes = await readFile2(temporary, "utf8");
|
|
25264
25459
|
if (bytes !== text2) throw new InitError("INPUT_CHANGED", t2("config.temporaryChanged"));
|
|
25265
25460
|
parseManagedConfig(bytes);
|
|
25266
|
-
await
|
|
25461
|
+
if (replace) await rename(temporary, join3(directory, "config.json"));
|
|
25462
|
+
else await link(temporary, join3(directory, "config.json"));
|
|
25267
25463
|
} finally {
|
|
25268
25464
|
await unchanged();
|
|
25269
25465
|
const current = await fileInfo(temporary);
|
|
@@ -25275,14 +25471,14 @@ async function publishConfig(root, text2, recheck, beforePublish) {
|
|
|
25275
25471
|
var fail4 = (message) => {
|
|
25276
25472
|
throw new SpecPreviewError(message);
|
|
25277
25473
|
};
|
|
25278
|
-
var
|
|
25474
|
+
var info2 = async (path3) => lstat3(path3).catch((e) => {
|
|
25279
25475
|
if (e.code === "ENOENT") return void 0;
|
|
25280
25476
|
throw e;
|
|
25281
25477
|
});
|
|
25282
25478
|
var digest = (value) => createHash2("sha256").update(value).digest("hex");
|
|
25283
25479
|
var LEGACY_LOCKS = ["tryce-spec-preview.lock", "tryce-spec-commit.lock"];
|
|
25284
25480
|
async function failOnLegacyLock(gitDir) {
|
|
25285
|
-
for (const name of LEGACY_LOCKS) if (await
|
|
25481
|
+
for (const name of LEGACY_LOCKS) if (await info2(join4(gitDir, name))) fail4(t2("store.legacyLock", { path: join4(gitDir, name) }));
|
|
25286
25482
|
}
|
|
25287
25483
|
var decode3 = (bytes) => new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
25288
25484
|
var generatePreviewId = (prefix) => prefix + "-" + [...randomBytes(10)].map((n) => "abcdefghijklmnopqrstuvwxyz234567"[n & 31]).join("");
|
|
@@ -25292,50 +25488,50 @@ async function snapshot2(root) {
|
|
|
25292
25488
|
const files = /* @__PURE__ */ new Map();
|
|
25293
25489
|
let count = 0;
|
|
25294
25490
|
let bytes = 0;
|
|
25295
|
-
const parent = await
|
|
25491
|
+
const parent = await info2(join4(root, ".gitifact"));
|
|
25296
25492
|
if (parent && (!parent.isDirectory() || parent.isSymbolicLink())) fail4(t2("store.storeNotDirectory"));
|
|
25297
|
-
if (!parent && await
|
|
25493
|
+
if (!parent && await info2(join4(root, ".tryce"))) fail4(t2("store.migrateFirst"));
|
|
25298
25494
|
const config2 = await readConfigFile(root);
|
|
25299
|
-
if (config2 !== void 0 && !("schemaVersion" in parseManagedConfig(config2)) || await
|
|
25495
|
+
if (config2 !== void 0 && !("schemaVersion" in parseManagedConfig(config2)) || await info2(join4(root, "specs"))) fail4(t2("store.legacyProject"));
|
|
25300
25496
|
async function visit2(path3, depth, documents) {
|
|
25301
|
-
const stat3 = await
|
|
25497
|
+
const stat3 = await info2(join4(root, path3));
|
|
25302
25498
|
if (!stat3) return;
|
|
25303
25499
|
if (stat3.isSymbolicLink() || !stat3.isDirectory() && !stat3.isFile()) fail4(t2("store.linkOrSpecial", { path: path3 }));
|
|
25304
25500
|
if (++count > 4e3) fail4(t2("store.tooManyFiles"));
|
|
25305
25501
|
if (stat3.isDirectory()) {
|
|
25306
25502
|
if (depth > (documents ? 8 : 1)) fail4(t2("store.tooDeep", { path: path3 }));
|
|
25307
|
-
for (const name of (await
|
|
25503
|
+
for (const name of (await readdir3(join4(root, path3))).sort()) await visit2(path3 + "/" + name, depth + 1, documents);
|
|
25308
25504
|
} else {
|
|
25309
25505
|
if (path3.endsWith("/tryce.json")) fail4(t2("store.legacyJson"));
|
|
25310
25506
|
if (documents ? !(path3.endsWith(".md") || depth === 1 && path3.endsWith("/history.jsonl")) : !/\/(requirements\.md|design\.md|history\.jsonl)$/.test(path3)) return;
|
|
25311
25507
|
if (!recordPathPattern.test(path3) || path3.startsWith(".tryce/")) fail4(t2("store.unsupportedPath", { path: path3 }));
|
|
25312
25508
|
if (stat3.nlink !== 1 || stat3.size > 1024 * 1024) fail4(t2("store.hardLinkOrSize"));
|
|
25313
|
-
const raw = await readFile3(
|
|
25509
|
+
const raw = await readFile3(join4(root, path3));
|
|
25314
25510
|
bytes += raw.length;
|
|
25315
25511
|
if (raw.length > 1024 * 1024 || bytes > 16 * 1024 * 1024) fail4(t2("store.sizeLimit"));
|
|
25316
25512
|
files.set(path3, decode3(raw));
|
|
25317
25513
|
}
|
|
25318
25514
|
}
|
|
25319
25515
|
await visit2(".gitifact/spec", 0, false);
|
|
25320
|
-
|
|
25516
|
+
await visit2(WIKI_DIR, 0, true);
|
|
25321
25517
|
const bundle = parsePreviewBundle(files);
|
|
25322
|
-
return { files, specs: bundle.specs,
|
|
25518
|
+
return { root, files, specs: bundle.specs, wiki: bundle.wiki, bundle, config: config2, stamp: digest(JSON.stringify([...files].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) + (config2 ?? "")) };
|
|
25323
25519
|
}
|
|
25324
25520
|
async function readWorkingPreviewState(cwd) {
|
|
25325
25521
|
const { root, gitDir } = await specPreviewReader(cwd).location();
|
|
25326
25522
|
await failOnLegacyLock(gitDir);
|
|
25327
|
-
if (await
|
|
25523
|
+
if (await info2(join4(gitDir, "gitifact-spec-preview.lock"))) fail4(t2("store.locked"));
|
|
25328
25524
|
const first = await snapshot2(root);
|
|
25329
25525
|
const second = await snapshot2(root);
|
|
25330
25526
|
if (first.stamp !== second.stamp) fail4(t2("store.changedWhileReading"));
|
|
25331
|
-
if (await
|
|
25527
|
+
if (await info2(join4(gitDir, "gitifact-spec-preview.lock"))) fail4(t2("store.lockedWhileReading"));
|
|
25332
25528
|
return first;
|
|
25333
25529
|
}
|
|
25334
25530
|
async function readWorkingPreview(cwd) {
|
|
25335
|
-
const { stamp, specs,
|
|
25336
|
-
return { stamp, specs,
|
|
25531
|
+
const { root, stamp, specs, wiki, bundle } = await readWorkingPreviewState(cwd);
|
|
25532
|
+
return { stamp, specs, wiki, warnings: await workingWarnings(root, bundle) };
|
|
25337
25533
|
}
|
|
25338
|
-
async function saveWorkingPreview(cwd, input2, publish =
|
|
25534
|
+
async function saveWorkingPreview(cwd, input2, publish = rename2) {
|
|
25339
25535
|
if (!input2 || typeof input2 !== "object" || Array.isArray(input2)) fail4(t2("store.invalidInput"));
|
|
25340
25536
|
const request = input2;
|
|
25341
25537
|
if (Object.keys(request).sort().join(",") !== "expected,operations" || typeof request.expected !== "string") fail4(t2("store.expectedOperations"));
|
|
@@ -25347,8 +25543,8 @@ async function saveWorkingPreview(cwd, input2, publish = rename) {
|
|
|
25347
25543
|
if (!old || renderSpecPreview(old) !== renderSpecPreview(spec2)) writes.set(spec2.path, renderSpecPreview(spec2));
|
|
25348
25544
|
if (JSON.stringify(old?.design) !== JSON.stringify(spec2.design)) writes.set(spec2.path.replace(/requirements\.md$/, "design.md"), spec2.design ? renderDesignPreview(spec2.id, spec2.design) : null);
|
|
25349
25545
|
}
|
|
25350
|
-
const previousDocs = before.
|
|
25351
|
-
const nextDocs = result.
|
|
25546
|
+
const previousDocs = before.wiki.documents;
|
|
25547
|
+
const nextDocs = result.wiki.documents;
|
|
25352
25548
|
for (const doc of nextDocs) {
|
|
25353
25549
|
const old = previousDocs.find((d) => d.id === doc.id);
|
|
25354
25550
|
if (old && old.path !== doc.path) writes.set(old.path, null);
|
|
@@ -25359,10 +25555,10 @@ async function saveWorkingPreview(cwd, input2, publish = rename) {
|
|
|
25359
25555
|
}, publish);
|
|
25360
25556
|
}
|
|
25361
25557
|
var readLockedPreviewState = (root) => snapshot2(root);
|
|
25362
|
-
async function previewTransaction(cwd, expected2, build, publish =
|
|
25558
|
+
async function previewTransaction(cwd, expected2, build, publish = rename2, afterPublish) {
|
|
25363
25559
|
const { root, gitDir } = await specPreviewReader(cwd).location();
|
|
25364
25560
|
await failOnLegacyLock(gitDir);
|
|
25365
|
-
const lock =
|
|
25561
|
+
const lock = join4(gitDir, "gitifact-spec-preview.lock");
|
|
25366
25562
|
try {
|
|
25367
25563
|
await mkdir2(lock);
|
|
25368
25564
|
} catch (e) {
|
|
@@ -25375,7 +25571,7 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25375
25571
|
const published = [];
|
|
25376
25572
|
let keepRecovery = false;
|
|
25377
25573
|
async function directory(path3) {
|
|
25378
|
-
const stat3 = await
|
|
25574
|
+
const stat3 = await info2(path3);
|
|
25379
25575
|
if (stat3) {
|
|
25380
25576
|
if (!stat3.isDirectory() || stat3.isSymbolicLink()) fail4(t2("store.notDirectory", { path: path3 }));
|
|
25381
25577
|
return;
|
|
@@ -25399,14 +25595,14 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25399
25595
|
else finalFiles.set(c.path, c.after);
|
|
25400
25596
|
}
|
|
25401
25597
|
if (finalFiles.size > 2e3 || [...finalFiles.values()].reduce((n, s) => n + Buffer.byteLength(s), 0) > 16 * 1024 * 1024) fail4(t2("store.totalLimit"));
|
|
25402
|
-
await writeFile(
|
|
25598
|
+
await writeFile(join4(lock, "recovery.json"), JSON.stringify({ root, changed }), { flag: "wx" });
|
|
25403
25599
|
for (const c of changed) {
|
|
25404
25600
|
if (c.after === null) {
|
|
25405
25601
|
temporary.push("");
|
|
25406
25602
|
continue;
|
|
25407
25603
|
}
|
|
25408
|
-
await directory(dirname(
|
|
25409
|
-
const temp =
|
|
25604
|
+
await directory(dirname(join4(root, c.path)));
|
|
25605
|
+
const temp = join4(dirname(join4(root, c.path)), ".gitifact-save-" + randomBytes(12).toString("hex"));
|
|
25410
25606
|
await writeFile(temp, c.after, { flag: "wx" });
|
|
25411
25607
|
temporary.push(temp);
|
|
25412
25608
|
}
|
|
@@ -25414,8 +25610,8 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25414
25610
|
await result.recheck?.();
|
|
25415
25611
|
for (let i = 0; i < changed.length; i++) {
|
|
25416
25612
|
const c = changed[i];
|
|
25417
|
-
const path3 =
|
|
25418
|
-
const stat3 = await
|
|
25613
|
+
const path3 = join4(root, c.path);
|
|
25614
|
+
const stat3 = await info2(path3);
|
|
25419
25615
|
if (stat3?.isSymbolicLink() || stat3 && !stat3.isFile() || (stat3 ? decode3(await readFile3(path3)) : null) !== c.before) fail4(t2("store.changedBeforeWrite"));
|
|
25420
25616
|
if (c.after === null) await unlink2(path3);
|
|
25421
25617
|
else await publish(temporary[i], path3);
|
|
@@ -25425,7 +25621,7 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25425
25621
|
if (after.stamp !== digest(JSON.stringify([...finalFiles].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) + (before.config ?? ""))) fail4(t2("store.changedWhileWriting"));
|
|
25426
25622
|
await result.recheck?.();
|
|
25427
25623
|
await afterPublish?.(after);
|
|
25428
|
-
return { ...result.data, stamp: after.stamp, paths: changed.map((c) => c.path), specs: after.specs,
|
|
25624
|
+
return { ...result.data, stamp: after.stamp, paths: changed.map((c) => c.path), specs: after.specs, wiki: after.wiki, warnings: await workingWarnings(root, after.bundle) };
|
|
25429
25625
|
} catch (error62) {
|
|
25430
25626
|
if (error62 instanceof PreservedPreviewError) {
|
|
25431
25627
|
keepRecovery = true;
|
|
@@ -25433,18 +25629,18 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25433
25629
|
}
|
|
25434
25630
|
for (const c of [...published].reverse()) {
|
|
25435
25631
|
try {
|
|
25436
|
-
const path3 =
|
|
25437
|
-
const stat3 = await
|
|
25632
|
+
const path3 = join4(root, c.path);
|
|
25633
|
+
const stat3 = await info2(path3);
|
|
25438
25634
|
if (c.after === null ? !!stat3 : !stat3?.isFile() || stat3.isSymbolicLink() || decode3(await readFile3(path3)) !== c.after) {
|
|
25439
25635
|
keepRecovery = true;
|
|
25440
25636
|
continue;
|
|
25441
25637
|
}
|
|
25442
25638
|
if (c.before === null) await unlink2(path3);
|
|
25443
25639
|
else {
|
|
25444
|
-
const temp =
|
|
25640
|
+
const temp = join4(dirname(path3), ".gitifact-restore-" + randomBytes(12).toString("hex"));
|
|
25445
25641
|
temporary.push(temp);
|
|
25446
25642
|
await writeFile(temp, c.before, { flag: "wx" });
|
|
25447
|
-
await
|
|
25643
|
+
await rename2(temp, path3);
|
|
25448
25644
|
}
|
|
25449
25645
|
} catch {
|
|
25450
25646
|
keepRecovery = true;
|
|
@@ -25459,7 +25655,7 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25459
25655
|
for (const path3 of [...createdDirs].reverse()) await rmdir(path3).catch(() => {
|
|
25460
25656
|
});
|
|
25461
25657
|
if (!keepRecovery) {
|
|
25462
|
-
await unlink2(
|
|
25658
|
+
await unlink2(join4(lock, "recovery.json")).catch((e) => {
|
|
25463
25659
|
if (e.code !== "ENOENT") throw e;
|
|
25464
25660
|
});
|
|
25465
25661
|
await rmdir(lock);
|
|
@@ -25501,8 +25697,8 @@ function createSpecBrowserReader(root, sessionId, inherited = process.env) {
|
|
|
25501
25697
|
if (cursor > 0 && expectedHead !== base.head) throw new SpecPreviewError(t2("specReader.historyChanged"));
|
|
25502
25698
|
const current = await readWorkingPreviewState(root);
|
|
25503
25699
|
const bare = current.specs.map(({ history: _history, ...s }) => ({ ...s, contributors: [], updatedAt: null }));
|
|
25504
|
-
const bareDocuments = current.
|
|
25505
|
-
if (!base.head) return
|
|
25700
|
+
const bareDocuments = current.wiki.documents.map((d) => ({ ...d, updatedAt: null }));
|
|
25701
|
+
if (!base.head) return browserSpecsV2.parse({ contract: "browser-specs", version: 2, sessionId, head: null, observedAt: (/* @__PURE__ */ new Date()).toISOString(), working: bare.length > 0 || bareDocuments.length > 0, features: bare, documents: bareDocuments, events: [], contributors: [], contributorsLimited: false, nextCursor: null, boundary: false });
|
|
25506
25702
|
const head = base.head;
|
|
25507
25703
|
const features = await Promise.all(bare.map(async (feature2) => {
|
|
25508
25704
|
const folder = feature2.path.replace(/^\.(?:gitifact|tryce)\/(spec\/[^/]+)\/requirements\.md$/, "$1");
|
|
@@ -25518,7 +25714,7 @@ function createSpecBrowserReader(root, sessionId, inherited = process.env) {
|
|
|
25518
25714
|
return { ...feature2, contributors: [...people.values()].sort((a, b) => b.commits - a.commits), updatedAt: lines[0]?.split("\0")[2] ?? null };
|
|
25519
25715
|
}));
|
|
25520
25716
|
const documents = await Promise.all(bareDocuments.map(async (doc) => ({ ...doc, updatedAt: (await git(["log", "--format=%aI", "--max-count=1", head, "--", doc.path])).trim() || null })));
|
|
25521
|
-
const documentDirs =
|
|
25717
|
+
const documentDirs = [WIKI_DIR];
|
|
25522
25718
|
const [rows, dirty] = await Promise.all([
|
|
25523
25719
|
git([
|
|
25524
25720
|
"log",
|
|
@@ -25552,7 +25748,7 @@ function createSpecBrowserReader(root, sessionId, inherited = process.env) {
|
|
|
25552
25748
|
const [commit, parents, author, email3, date5, committer, message] = row.split("\0");
|
|
25553
25749
|
if (!commit || author === void 0 || email3 === void 0 || !date5 || committer === void 0 || message === void 0) throw new SpecPreviewError(t2("specReader.historyUnreadable"));
|
|
25554
25750
|
const after = await snapshot3(commit);
|
|
25555
|
-
let before =
|
|
25751
|
+
let before = emptyBundle();
|
|
25556
25752
|
const parent = parents?.split(" ")[0];
|
|
25557
25753
|
if (parent) {
|
|
25558
25754
|
try {
|
|
@@ -25579,9 +25775,9 @@ function createSpecBrowserReader(root, sessionId, inherited = process.env) {
|
|
|
25579
25775
|
}));
|
|
25580
25776
|
}));
|
|
25581
25777
|
if (await readHead() !== head || (await readWorkingPreviewState(root)).stamp !== current.stamp) throw new SpecPreviewError(t2("specReader.projectChanged"));
|
|
25582
|
-
return
|
|
25778
|
+
return browserSpecsV2.parse({
|
|
25583
25779
|
contract: "browser-specs",
|
|
25584
|
-
version:
|
|
25780
|
+
version: 2,
|
|
25585
25781
|
sessionId,
|
|
25586
25782
|
head,
|
|
25587
25783
|
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -25675,18 +25871,27 @@ async function startBrowserServer(options) {
|
|
|
25675
25871
|
if (api) {
|
|
25676
25872
|
if (request.headers["sec-fetch-site"] === "cross-site" && !allowedOrigin) return fail8(response, 403, "FORBIDDEN", t2("server.crossSite"));
|
|
25677
25873
|
if (url2.search && path3 !== "/api/v1/specs" && path3 !== "/api/v1/changelog") return fail8(response, 400, "BAD_REQUEST", t2("server.noQuery"));
|
|
25678
|
-
if (path3.startsWith("/api/v1/
|
|
25679
|
-
const
|
|
25874
|
+
if (path3.startsWith("/api/v1/assets/")) {
|
|
25875
|
+
const relative = path3.slice("/api/v1/assets/".length);
|
|
25876
|
+
const assetPath2 = ASSETS_DIR + "/" + relative;
|
|
25680
25877
|
if (request.method !== "GET") {
|
|
25681
25878
|
response.setHeader("Allow", "GET");
|
|
25682
25879
|
return fail8(response, 405, "METHOD_NOT_ALLOWED", t2("server.methodNotAllowed"));
|
|
25683
25880
|
}
|
|
25684
|
-
if (!
|
|
25685
|
-
const file2 =
|
|
25686
|
-
const stat3 = await
|
|
25687
|
-
if (!stat3?.isFile() || stat3.isSymbolicLink() || stat3.size >
|
|
25881
|
+
if (!isAssetPath(assetPath2)) return fail8(response, 404, "NOT_FOUND", t2("server.assetNotFound"));
|
|
25882
|
+
const file2 = join5(initial.repository.rootPath, ...assetPath2.split("/"));
|
|
25883
|
+
const stat3 = await lstat4(file2).catch(() => void 0);
|
|
25884
|
+
if (!stat3?.isFile() || stat3.isSymbolicLink() || stat3.size > 20 * 1024 * 1024) return fail8(response, 404, "NOT_FOUND", t2("server.assetNotFound"));
|
|
25688
25885
|
const bytes = await readFile4(file2);
|
|
25689
|
-
|
|
25886
|
+
const extension = assetExtension(relative);
|
|
25887
|
+
const inline = ["png", "jpg", "jpeg", "gif", "webp", "svg"].includes(extension);
|
|
25888
|
+
response.writeHead(200, {
|
|
25889
|
+
"Content-Type": contentType("." + extension),
|
|
25890
|
+
"Cache-Control": "no-cache",
|
|
25891
|
+
"Content-Length": bytes.length,
|
|
25892
|
+
"Content-Security-Policy": "default-src 'none'; sandbox",
|
|
25893
|
+
"Content-Disposition": inline ? "inline" : 'attachment; filename="' + encodeURIComponent(relative.split("/").pop()) + '"'
|
|
25894
|
+
});
|
|
25690
25895
|
response.end(bytes);
|
|
25691
25896
|
return;
|
|
25692
25897
|
}
|
|
@@ -25850,13 +26055,13 @@ async function runBrowser(options, version2) {
|
|
|
25850
26055
|
}
|
|
25851
26056
|
|
|
25852
26057
|
// src/commands/spec-init.ts
|
|
25853
|
-
import { readdir as
|
|
25854
|
-
import { join as
|
|
26058
|
+
import { mkdir as mkdir4, readdir as readdir4, writeFile as writeFile2 } from "node:fs/promises";
|
|
26059
|
+
import { join as join9 } from "node:path";
|
|
25855
26060
|
|
|
25856
26061
|
// src/adapters/git/init-repository.ts
|
|
25857
26062
|
import { createHash as createHash3 } from "node:crypto";
|
|
25858
26063
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
25859
|
-
import { join as
|
|
26064
|
+
import { join as join6 } from "node:path";
|
|
25860
26065
|
function initRepository(cwd, inherited = process.env) {
|
|
25861
26066
|
const env = { ...inherited, GIT_OPTIONAL_LOCKS: "0", GIT_NO_LAZY_FETCH: "1", GIT_TERMINAL_PROMPT: "0", GIT_LITERAL_PATHSPECS: "1", LC_ALL: "C" };
|
|
25862
26067
|
const runner = createGitRunner();
|
|
@@ -25876,7 +26081,7 @@ function initRepository(cwd, inherited = process.env) {
|
|
|
25876
26081
|
const gitDir = locations.length === 2 ? locations[0] : decodeGitLine(await git(["rev-parse", "--absolute-git-dir"], root));
|
|
25877
26082
|
const indexPath = locations.length === 2 ? locations[1] : decodeGitLine(await git(["rev-parse", "--path-format=absolute", "--git-path", "index"], root));
|
|
25878
26083
|
for (const marker2 of ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"]) {
|
|
25879
|
-
if (await fileInfo(
|
|
26084
|
+
if (await fileInfo(join6(gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
|
|
25880
26085
|
}
|
|
25881
26086
|
if (state.changes.some((change2) => change2.kind === "unmerged")) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
25882
26087
|
const index = await readFile5(indexPath).catch((error62) => {
|
|
@@ -25913,23 +26118,23 @@ function initRepository(cwd, inherited = process.env) {
|
|
|
25913
26118
|
}
|
|
25914
26119
|
|
|
25915
26120
|
// src/adapters/filesystem/managed-file.ts
|
|
25916
|
-
import { open as open3, readFile as readFile6, mkdir as mkdir3, realpath as realpath2, rename as
|
|
25917
|
-
import { join as
|
|
26121
|
+
import { open as open3, readFile as readFile6, mkdir as mkdir3, realpath as realpath2, rename as rename3, link as link2, unlink as unlink3 } from "node:fs/promises";
|
|
26122
|
+
import { join as join7, dirname as dirname2 } from "node:path";
|
|
25918
26123
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
25919
26124
|
var DEFAULT_LIMIT = 1024 * 1024;
|
|
25920
26125
|
async function managedPath(root, path3, create = false) {
|
|
25921
26126
|
let full = root;
|
|
25922
26127
|
const parts = path3.split("/");
|
|
25923
26128
|
for (let i = 0; i < parts.length; i++) {
|
|
25924
|
-
full =
|
|
25925
|
-
let
|
|
25926
|
-
if (!
|
|
26129
|
+
full = join7(full, parts[i]);
|
|
26130
|
+
let info7 = await fileInfo(full);
|
|
26131
|
+
if (!info7 && create && i < parts.length - 1) {
|
|
25927
26132
|
await mkdir3(full).catch((error62) => {
|
|
25928
26133
|
if (error62.code !== "EEXIST") throw error62;
|
|
25929
26134
|
});
|
|
25930
|
-
|
|
26135
|
+
info7 = await fileInfo(full);
|
|
25931
26136
|
}
|
|
25932
|
-
if (
|
|
26137
|
+
if (info7 && (info7.isSymbolicLink() || (i < parts.length - 1 ? !info7.isDirectory() : !info7.isFile()))) {
|
|
25933
26138
|
throw new InitError("PATH_CONFLICT", t2("managedFile.notRegularPath", { path: path3 }));
|
|
25934
26139
|
}
|
|
25935
26140
|
}
|
|
@@ -25969,7 +26174,7 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
25969
26174
|
if (previous !== null) await unlink3(full);
|
|
25970
26175
|
return;
|
|
25971
26176
|
}
|
|
25972
|
-
const temp =
|
|
26177
|
+
const temp = join7(directory, ".gitifact-managed-" + randomUUID5() + ".tmp");
|
|
25973
26178
|
const handle = await open3(temp, "wx", 384);
|
|
25974
26179
|
const tempOwner = await handle.stat();
|
|
25975
26180
|
try {
|
|
@@ -25983,7 +26188,7 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
25983
26188
|
await unchanged();
|
|
25984
26189
|
if (await managedRead(root, path3) !== previous || await readFile6(temp, "utf8") !== next) throw new InitError("INPUT_CHANGED", t2("managedFile.publishTargetChanged", { path: path3 }));
|
|
25985
26190
|
if (previous === null) await link2(temp, full);
|
|
25986
|
-
else await
|
|
26191
|
+
else await rename3(temp, full);
|
|
25987
26192
|
} finally {
|
|
25988
26193
|
await unchanged();
|
|
25989
26194
|
const now = await fileInfo(temp);
|
|
@@ -25995,14 +26200,53 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
25995
26200
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
25996
26201
|
|
|
25997
26202
|
// src/commands/docs.ts
|
|
25998
|
-
import { readFile as readFile7 } from "node:fs/promises";
|
|
25999
|
-
|
|
26203
|
+
import { lstat as lstat5, readFile as readFile7 } from "node:fs/promises";
|
|
26204
|
+
import { dirname as dirname3, join as join8 } from "node:path";
|
|
26205
|
+
var docTopics = ["workflow", "spec", "design", "wiki", "commit"];
|
|
26000
26206
|
var isDocTopic = (value) => docTopics.includes(value);
|
|
26001
|
-
var
|
|
26207
|
+
var readBundledDoc = (name, lang) => readFile7(new URL("./i18n/" + lang + "/docs/" + name + ".md", import.meta.url), "utf8");
|
|
26208
|
+
var info3 = (path3) => lstat5(path3).catch((e) => {
|
|
26209
|
+
if (e.code === "ENOENT") return void 0;
|
|
26210
|
+
throw e;
|
|
26211
|
+
});
|
|
26002
26212
|
function listDocTopics(lang = defaultLanguage2) {
|
|
26003
26213
|
const width = Math.max(...docTopics.map((topic) => topic.length));
|
|
26004
26214
|
return docTopics.map((topic) => topic.padEnd(width) + " " + t2(`docs.summary.${topic}`, {}, lang)).join("\n") + "\n";
|
|
26005
26215
|
}
|
|
26216
|
+
async function findProjectRoot(cwd) {
|
|
26217
|
+
for (let dir = cwd; ; dir = dirname3(dir)) {
|
|
26218
|
+
if ((await info3(join8(dir, ".gitifact", "config.json")))?.isFile()) return dir;
|
|
26219
|
+
if (dirname3(dir) === dir) return void 0;
|
|
26220
|
+
}
|
|
26221
|
+
}
|
|
26222
|
+
async function readWikiPolicy(root) {
|
|
26223
|
+
if (!root) return void 0;
|
|
26224
|
+
const path3 = join8(root, ...WIKI_ENTRY_PATH.split("/"));
|
|
26225
|
+
const stat3 = await info3(path3);
|
|
26226
|
+
if (!stat3?.isFile() || stat3.size > 1024 * 1024) return void 0;
|
|
26227
|
+
const lines = (await readFile7(path3, "utf8")).replace(/\r\n/g, "\n").split("\n");
|
|
26228
|
+
if (lines[0] === "---") {
|
|
26229
|
+
const end = lines.indexOf("---", 1);
|
|
26230
|
+
if (end > 0) lines.splice(0, end + 1);
|
|
26231
|
+
}
|
|
26232
|
+
while (lines.length && !lines[0].trim()) lines.shift();
|
|
26233
|
+
if (lines[0]?.startsWith("# ")) lines.shift();
|
|
26234
|
+
let fence = false;
|
|
26235
|
+
const body = lines.map((line) => {
|
|
26236
|
+
if (/^ {0,3}(`{3,}|~{3,})/.test(line)) fence = !fence;
|
|
26237
|
+
return !fence && /^#{1,5} /.test(line) ? "#" + line : line;
|
|
26238
|
+
}).join("\n").trim();
|
|
26239
|
+
return body || void 0;
|
|
26240
|
+
}
|
|
26241
|
+
async function renderDoc(topic, controls = {}, lang = defaultLanguage2) {
|
|
26242
|
+
const read = controls.readDoc ?? readBundledDoc;
|
|
26243
|
+
const format = (await read(topic, lang)).trimEnd();
|
|
26244
|
+
if (topic !== "wiki") return format + "\n";
|
|
26245
|
+
const own2 = await readWikiPolicy(await findProjectRoot(controls.cwd ?? process.cwd()));
|
|
26246
|
+
const policy = own2 ?? (await read("wiki.default", lang)).trim().replace(/^(#{1,5}) /gm, "#$1 ");
|
|
26247
|
+
const heading = own2 ? t2("docs.wikiPolicyProject", {}, lang) : t2("docs.wikiPolicyDefault", {}, lang);
|
|
26248
|
+
return format + "\n\n## " + heading + "\n\n" + policy + "\n";
|
|
26249
|
+
}
|
|
26006
26250
|
async function runDocs(topic, controls = {}) {
|
|
26007
26251
|
if (topic === void 0) {
|
|
26008
26252
|
process.stdout.write(listDocTopics());
|
|
@@ -26014,7 +26258,7 @@ async function runDocs(topic, controls = {}) {
|
|
|
26014
26258
|
process.exitCode = 1;
|
|
26015
26259
|
return;
|
|
26016
26260
|
}
|
|
26017
|
-
process.stdout.write(await (
|
|
26261
|
+
process.stdout.write(await renderDoc(topic, controls));
|
|
26018
26262
|
}
|
|
26019
26263
|
|
|
26020
26264
|
// src/commands/agent-block.ts
|
|
@@ -26055,6 +26299,16 @@ function isWrapperFile(text2) {
|
|
|
26055
26299
|
const lines = own2.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
26056
26300
|
return lines.length > 0 && lines.every((line) => /^@(?:\.\/)?[^\s]+$/.test(line) && candidatePaths.includes(line.replace(/^@(?:\.\/)?/, "")));
|
|
26057
26301
|
}
|
|
26302
|
+
var CLAUDE_WRAPPER_PATH = "CLAUDE.md";
|
|
26303
|
+
function claudeWrapperFor(agents) {
|
|
26304
|
+
return "@AGENTS.md" + eolOf(agents);
|
|
26305
|
+
}
|
|
26306
|
+
function isGeneratedClaudeWrapper(text2) {
|
|
26307
|
+
return text2.trim() === "@AGENTS.md";
|
|
26308
|
+
}
|
|
26309
|
+
function lacksClaudeFile(existing) {
|
|
26310
|
+
return !existing.has("CLAUDE.md") && !existing.has(".claude/CLAUDE.md");
|
|
26311
|
+
}
|
|
26058
26312
|
function resolveAgentPaths(preset, existing) {
|
|
26059
26313
|
if (preset && preset !== "all") {
|
|
26060
26314
|
const order = agentPresets[preset];
|
|
@@ -26088,7 +26342,7 @@ function removeBlock(text2, boilerplate) {
|
|
|
26088
26342
|
}
|
|
26089
26343
|
|
|
26090
26344
|
// src/commands/agent-docs.ts
|
|
26091
|
-
var skippedAgentDocs = { mode: "skip", paths: [], writes: [] };
|
|
26345
|
+
var skippedAgentDocs = { mode: "skip", paths: [], writes: [], missing: [] };
|
|
26092
26346
|
async function planAgentDocs(root, options) {
|
|
26093
26347
|
if (options.skip) return skippedAgentDocs;
|
|
26094
26348
|
const existing = /* @__PURE__ */ new Map();
|
|
@@ -26106,7 +26360,12 @@ async function planAgentDocs(root, options) {
|
|
|
26106
26360
|
paths2.push(path3);
|
|
26107
26361
|
writes.push({ path: path3, previous: text2, next: removeBlock(text2, boilerplateFor(path3)) });
|
|
26108
26362
|
}
|
|
26109
|
-
|
|
26363
|
+
const wrapper = existing.get(CLAUDE_WRAPPER_PATH);
|
|
26364
|
+
if (writes.some((write) => write.path === "AGENTS.md" && write.next === null) && wrapper !== void 0 && isGeneratedClaudeWrapper(wrapper)) {
|
|
26365
|
+
paths2.push(CLAUDE_WRAPPER_PATH);
|
|
26366
|
+
writes.push({ path: CLAUDE_WRAPPER_PATH, previous: wrapper, next: null });
|
|
26367
|
+
}
|
|
26368
|
+
return { mode: "remove", paths: paths2, writes, missing: [] };
|
|
26110
26369
|
}
|
|
26111
26370
|
const block = await renderAgentBlock(options.version, options);
|
|
26112
26371
|
const { inject, create } = options.onlyExisting ? { inject: candidatePaths.filter((path3) => existing.has(path3) && findBlock(existing.get(path3)) && !isWrapperFile(existing.get(path3))), create: null } : resolveAgentPaths(options.agent, existing);
|
|
@@ -26120,11 +26379,19 @@ async function planAgentDocs(root, options) {
|
|
|
26120
26379
|
paths2.push(create);
|
|
26121
26380
|
writes.push({ path: create, previous: null, next: injectBlock(null, block, boilerplateFor(create)) });
|
|
26122
26381
|
}
|
|
26382
|
+
const missing2 = [];
|
|
26383
|
+
if (paths2.includes("AGENTS.md") && lacksClaudeFile(existing)) {
|
|
26384
|
+
if (options.onlyExisting) missing2.push(CLAUDE_WRAPPER_PATH);
|
|
26385
|
+
else {
|
|
26386
|
+
paths2.push(CLAUDE_WRAPPER_PATH);
|
|
26387
|
+
writes.push({ path: CLAUDE_WRAPPER_PATH, previous: null, next: claudeWrapperFor(existing.get("AGENTS.md") ?? null) });
|
|
26388
|
+
}
|
|
26389
|
+
}
|
|
26123
26390
|
for (const [path3, text2] of existing) {
|
|
26124
26391
|
if (paths2.includes(path3) || !isWrapperFile(text2) || !findBlock(text2)) continue;
|
|
26125
26392
|
writes.push({ path: path3, previous: text2, next: removeBlock(text2, boilerplateFor(path3)) });
|
|
26126
26393
|
}
|
|
26127
|
-
return { mode: "install", paths: paths2, writes };
|
|
26394
|
+
return { mode: "install", paths: paths2, writes, missing: missing2 };
|
|
26128
26395
|
}
|
|
26129
26396
|
async function applyAgentDocs(root, plan, recheck) {
|
|
26130
26397
|
for (const write of plan.writes) await managedWrite(root, write.path, write.previous, write.next, recheck);
|
|
@@ -26132,22 +26399,27 @@ async function applyAgentDocs(root, plan, recheck) {
|
|
|
26132
26399
|
}
|
|
26133
26400
|
|
|
26134
26401
|
// src/commands/spec-init.ts
|
|
26135
|
-
async function initializeSpecProject(cwd, dryRun = false, env = process.env, beforePublish, agentDocs) {
|
|
26402
|
+
async function initializeSpecProject(cwd, dryRun = false, env = process.env, beforePublish, agentDocs, readDoc = (name) => readBundledDoc(name, defaultLanguage2), update = disabledUpdate) {
|
|
26136
26403
|
const repo = initRepository(cwd, env);
|
|
26137
26404
|
const first = await repo.inspect();
|
|
26138
26405
|
const root = first.state.repository.rootPath;
|
|
26139
26406
|
const docsPlan = agentDocs ? await planAgentDocs(root, agentDocs) : skippedAgentDocs;
|
|
26140
|
-
const result = (config3, outcome) =>
|
|
26141
|
-
|
|
26142
|
-
|
|
26143
|
-
|
|
26144
|
-
|
|
26145
|
-
|
|
26146
|
-
|
|
26147
|
-
|
|
26148
|
-
|
|
26149
|
-
|
|
26150
|
-
|
|
26407
|
+
const result = async (config3, outcome) => {
|
|
26408
|
+
const checked = await update;
|
|
26409
|
+
return projectInitV5.parse({
|
|
26410
|
+
contract: "project-init",
|
|
26411
|
+
version: 5,
|
|
26412
|
+
ok: true,
|
|
26413
|
+
outcome,
|
|
26414
|
+
rootPath: root,
|
|
26415
|
+
configPath: ".gitifact/config.json",
|
|
26416
|
+
schemaVersion: config3.schemaVersion,
|
|
26417
|
+
baseline: config3.baseline,
|
|
26418
|
+
agentDocs: { mode: docsPlan.mode, paths: docsPlan.paths },
|
|
26419
|
+
update: checked,
|
|
26420
|
+
install: checked.status === "available" ? { npmGlobal: npmGlobalInstall(checked.latestVersion) } : null
|
|
26421
|
+
});
|
|
26422
|
+
};
|
|
26151
26423
|
const existing = async (text3) => {
|
|
26152
26424
|
const config3 = parseManagedConfig(text3);
|
|
26153
26425
|
if (!("schemaVersion" in config3)) throw new InitError("MIGRATION_REQUIRED", t2("init.migrationRequired"));
|
|
@@ -26159,19 +26431,49 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
26159
26431
|
if (!dryRun) await applyAgentDocs(root, docsPlan, unchanged);
|
|
26160
26432
|
return result(config3, "already-initialized");
|
|
26161
26433
|
};
|
|
26434
|
+
const config2 = { schemaVersion: SCHEMA_VERSION, baseline: first.state.head.commit ? { kind: "commit", objectFormat: first.state.repository.objectFormat, commit: first.state.head.commit } : { kind: "empty" } };
|
|
26435
|
+
const text2 = JSON.stringify(config2, null, 2) + "\n";
|
|
26436
|
+
const replaceLegacy = async (before, version2) => {
|
|
26437
|
+
const onlyConfig = async () => {
|
|
26438
|
+
const entries = await readdir4(join9(root, ".gitifact"));
|
|
26439
|
+
if (await fileInfo(join9(root, "specs")) || entries.some((name) => name !== "config.json" && !/^\.init-[a-f0-9-]+\.tmp$/.test(name))) {
|
|
26440
|
+
throw new InitError("UNSUPPORTED_SCHEMA", t2("init.legacyRecords", { version: String(version2) }));
|
|
26441
|
+
}
|
|
26442
|
+
};
|
|
26443
|
+
const recheck2 = async () => {
|
|
26444
|
+
await onlyConfig();
|
|
26445
|
+
await repo.checkIgnore(root);
|
|
26446
|
+
if ((await repo.inspect()).stamp !== first.stamp) throw new InitError("INPUT_CHANGED", t2("init.headOrIndexChanged"));
|
|
26447
|
+
if (await readConfigFile(root) !== before) throw new InitError("INPUT_CHANGED", t2("init.inputChanged"));
|
|
26448
|
+
};
|
|
26449
|
+
await recheck2();
|
|
26450
|
+
if (dryRun) return result(config2, "planned");
|
|
26451
|
+
await publishConfig(root, text2, recheck2, beforePublish, true);
|
|
26452
|
+
return finish("replaced");
|
|
26453
|
+
};
|
|
26454
|
+
const finish = async (outcome) => {
|
|
26455
|
+
const published = async () => {
|
|
26456
|
+
if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !== text2) throw new InitError("INPUT_CHANGED_AFTER_WRITE", t2("init.changedAfterWrite"));
|
|
26457
|
+
};
|
|
26458
|
+
await published();
|
|
26459
|
+
await applyAgentDocs(root, docsPlan, published);
|
|
26460
|
+
await writeWikiPolicy(root, readDoc);
|
|
26461
|
+
return result(config2, outcome);
|
|
26462
|
+
};
|
|
26162
26463
|
const old = await readConfigFile(root);
|
|
26163
|
-
if (old !== void 0)
|
|
26464
|
+
if (old !== void 0) {
|
|
26465
|
+
const legacy = legacySchemaVersion(old);
|
|
26466
|
+
return legacy === void 0 ? existing(old) : replaceLegacy(old, legacy);
|
|
26467
|
+
}
|
|
26164
26468
|
if (first.trackedConfig) throw new InitError("CONFIG_DELETED", t2("init.configDeleted"));
|
|
26165
26469
|
const checkRecords = async () => {
|
|
26166
|
-
if (await fileInfo(
|
|
26167
|
-
const entries = await
|
|
26470
|
+
if (await fileInfo(join9(root, "specs"))) throw new InitError("EXISTING_RECORDS", t2("init.existingSpecs"));
|
|
26471
|
+
const entries = await readdir4(join9(root, ".gitifact")).catch((e) => {
|
|
26168
26472
|
if (e.code === "ENOENT") return [];
|
|
26169
26473
|
throw e;
|
|
26170
26474
|
});
|
|
26171
26475
|
if (entries.some((name) => !/^\.init-[a-f0-9-]+\.tmp$/.test(name))) throw new InitError("EXISTING_RECORDS", t2("init.orphanStore"));
|
|
26172
26476
|
};
|
|
26173
|
-
const config2 = { schemaVersion: 1, baseline: first.state.head.commit ? { kind: "commit", objectFormat: first.state.repository.objectFormat, commit: first.state.head.commit } : { kind: "empty" } };
|
|
26174
|
-
const text2 = JSON.stringify(config2, null, 2) + "\n";
|
|
26175
26477
|
const recheck = async () => {
|
|
26176
26478
|
await checkRecords();
|
|
26177
26479
|
await repo.checkIgnore(root);
|
|
@@ -26189,34 +26491,59 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
26189
26491
|
}
|
|
26190
26492
|
throw error62;
|
|
26191
26493
|
}
|
|
26192
|
-
|
|
26193
|
-
|
|
26194
|
-
|
|
26195
|
-
|
|
26196
|
-
|
|
26197
|
-
|
|
26494
|
+
return finish("created");
|
|
26495
|
+
}
|
|
26496
|
+
function legacySchemaVersion(text2) {
|
|
26497
|
+
let value;
|
|
26498
|
+
try {
|
|
26499
|
+
value = JSON.parse(text2);
|
|
26500
|
+
} catch {
|
|
26501
|
+
return void 0;
|
|
26502
|
+
}
|
|
26503
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
26504
|
+
const version2 = value.schemaVersion;
|
|
26505
|
+
return Number.isInteger(version2) && version2 < SCHEMA_VERSION ? version2 : void 0;
|
|
26506
|
+
}
|
|
26507
|
+
async function writeWikiPolicy(root, readDoc) {
|
|
26508
|
+
if (await fileInfo(join9(root, ...WIKI_DIR.split("/")))) return;
|
|
26509
|
+
const template = await readDoc("wiki.default").catch((e) => {
|
|
26510
|
+
if (e.code === "ENOENT") return void 0;
|
|
26511
|
+
throw e;
|
|
26512
|
+
});
|
|
26513
|
+
if (template === void 0) return;
|
|
26514
|
+
const doc = { id: generatePreviewId("W"), path: WIKI_ENTRY_PATH, title: t2("init.wikiReadmeTitle"), body: template.trim() };
|
|
26515
|
+
const text2 = renderDocument(doc);
|
|
26516
|
+
parseDocument(doc.path, text2);
|
|
26517
|
+
await mkdir4(join9(root, ...WIKI_DIR.split("/")), { recursive: true });
|
|
26518
|
+
await writeFile2(join9(root, ...WIKI_ENTRY_PATH.split("/")), text2, { flag: "wx" });
|
|
26198
26519
|
}
|
|
26199
26520
|
|
|
26200
26521
|
// src/commands/init.ts
|
|
26201
26522
|
async function runInit(options, version2) {
|
|
26523
|
+
const cancel = new AbortController();
|
|
26524
|
+
const update = updateCheckDisabled(process.env) ? disabledUpdate : resolveUpdate(version2, fetchLatestVersion, cancel.signal);
|
|
26202
26525
|
try {
|
|
26203
26526
|
const dto = await initializeSpecProject(
|
|
26204
26527
|
process.cwd(),
|
|
26205
26528
|
options.dryRun,
|
|
26206
26529
|
process.env,
|
|
26207
26530
|
void 0,
|
|
26208
|
-
{ version: version2, agent: options.agent, remove: options.removeAgents, skip: options.skipAgents }
|
|
26531
|
+
{ version: version2, agent: options.agent, remove: options.removeAgents, skip: options.skipAgents },
|
|
26532
|
+
void 0,
|
|
26533
|
+
update
|
|
26209
26534
|
);
|
|
26210
26535
|
if (!dto.ok) throw new Error("unreachable");
|
|
26211
26536
|
const docs = dto.agentDocs.mode === "skip" ? t2("init.text.skipped") : dto.agentDocs.paths.join(", ") + (dto.agentDocs.mode === "remove" ? " (" + t2("init.text.blockRemoved") + ")" : dto.outcome === "planned" ? " (" + t2("init.text.blockPlanned") + ")" : " (" + t2("init.text.blockUpdated") + ")");
|
|
26537
|
+
const newer = dto.install ? t2("init.text.updateAvailable", { version: dto.update.latestVersion, command: dto.install.npmGlobal }) + "\n" : "";
|
|
26212
26538
|
process.stdout.write(options.format === "text" ? `${dto.outcome}: ${dto.rootPath}/.gitifact/config.json
|
|
26213
|
-
${t2("init.text.storage")}: schemaVersion
|
|
26539
|
+
${t2("init.text.storage")}: schemaVersion ${dto.schemaVersion}
|
|
26214
26540
|
${t2("init.text.agentDocs")}: ${docs}
|
|
26215
|
-
` : JSON.stringify(dto) + "\n");
|
|
26541
|
+
${newer}` : JSON.stringify(dto) + "\n");
|
|
26216
26542
|
} catch (error62) {
|
|
26543
|
+
cancel.abort();
|
|
26217
26544
|
const known = error62 instanceof InitError || error62 instanceof RepositoryReadError;
|
|
26218
26545
|
const failure2 = { code: known ? error62.code : "INIT_FAILED", message: known ? error62.message : t2("init.failed") };
|
|
26219
|
-
process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(
|
|
26546
|
+
process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(projectInitV5.parse({ contract: "project-init", version: 5, ok: false, error: failure2 })) + "\n");
|
|
26220
26547
|
process.exitCode = 1;
|
|
26221
26548
|
}
|
|
26222
26549
|
}
|
|
@@ -26262,19 +26589,20 @@ async function refreshBlocks(cwd, version2, env, controls) {
|
|
|
26262
26589
|
first = await repo.inspect();
|
|
26263
26590
|
config2 = await readConfigFile(first.state.repository.rootPath);
|
|
26264
26591
|
} catch (error62) {
|
|
26265
|
-
if (error62 instanceof RepositoryReadError && error62.code === "NOT_A_REPOSITORY" || error62 instanceof InitError && error62.code === "MIGRATION_REQUIRED") return { state: "not-initialized", paths: [] };
|
|
26592
|
+
if (error62 instanceof RepositoryReadError && error62.code === "NOT_A_REPOSITORY" || error62 instanceof InitError && error62.code === "MIGRATION_REQUIRED") return { state: "not-initialized", paths: [], missing: [] };
|
|
26266
26593
|
throw error62;
|
|
26267
26594
|
}
|
|
26268
|
-
if (config2 === void 0) return { state: "not-initialized", paths: [] };
|
|
26595
|
+
if (config2 === void 0) return { state: "not-initialized", paths: [], missing: [] };
|
|
26269
26596
|
const root = first.state.repository.rootPath;
|
|
26270
26597
|
const plan = await planAgentDocs(root, { version: version2, onlyExisting: true, ...controls });
|
|
26271
|
-
|
|
26272
|
-
if (plan.
|
|
26598
|
+
const { missing: missing2 } = plan;
|
|
26599
|
+
if (plan.paths.length === 0) return { state: "no-block", paths: [], missing: missing2, root };
|
|
26600
|
+
if (plan.writes.length === 0) return { state: "current", paths: plan.paths, missing: missing2, root, candidates: plan.paths };
|
|
26273
26601
|
const unchanged = async () => {
|
|
26274
26602
|
if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !== config2) throw new InitError("INPUT_CHANGED", t2("init.inputChanged"));
|
|
26275
26603
|
};
|
|
26276
26604
|
const applied = await applyAgentDocs(root, plan, unchanged);
|
|
26277
|
-
return { state: "refreshed", paths: applied, root, candidates: plan.paths };
|
|
26605
|
+
return { state: "refreshed", paths: applied, missing: missing2, root, candidates: plan.paths };
|
|
26278
26606
|
}
|
|
26279
26607
|
function outsideBlock(text2) {
|
|
26280
26608
|
const normalized2 = text2.replace(/\r\n/g, "\n");
|
|
@@ -26320,14 +26648,14 @@ async function updateCommand(cwd, version2, env = process.env, controls = {}) {
|
|
|
26320
26648
|
refreshBlocks(cwd, version2, env, controls)
|
|
26321
26649
|
]);
|
|
26322
26650
|
const commit = controls.commit ? await commitBlocks(agentDocs, version2, env) : commitState("not-requested");
|
|
26323
|
-
return
|
|
26651
|
+
return updateV3.parse({
|
|
26324
26652
|
contract: "update",
|
|
26325
|
-
version:
|
|
26653
|
+
version: 3,
|
|
26326
26654
|
ok: true,
|
|
26327
26655
|
cliVersion: version2,
|
|
26328
26656
|
update,
|
|
26329
26657
|
install: update.status === "available" ? { npmGlobal: npmGlobalInstall(update.latestVersion) } : null,
|
|
26330
|
-
agentDocs: { state: agentDocs.state, paths: agentDocs.paths },
|
|
26658
|
+
agentDocs: { state: agentDocs.state, paths: agentDocs.paths, missing: agentDocs.missing },
|
|
26331
26659
|
commit
|
|
26332
26660
|
});
|
|
26333
26661
|
}
|
|
@@ -26343,6 +26671,7 @@ async function runUpdate(options, version2) {
|
|
|
26343
26671
|
lines.push(dto.update.status === "available" ? t2("update.text.available", { version: dto.update.latestVersion }) : dto.update.status === "up-to-date" ? t2("update.text.upToDate") : dto.update.status === "disabled" ? t2("update.text.disabled") : t2("update.text.unavailable"));
|
|
26344
26672
|
if (dto.install) lines.push(t2("update.text.install", { command: dto.install.npmGlobal }), t2("update.text.afterInstall"));
|
|
26345
26673
|
lines.push(dto.agentDocs.state === "refreshed" ? t2("update.text.blockRefreshed", { paths: dto.agentDocs.paths.join(", ") }) : dto.agentDocs.state === "current" ? t2("update.text.blockCurrent", { paths: dto.agentDocs.paths.join(", ") }) : dto.agentDocs.state === "no-block" ? t2("update.text.noBlock") : t2("update.text.notInitialized"));
|
|
26674
|
+
if (dto.agentDocs.missing.length) lines.push(t2("update.text.missing", { paths: dto.agentDocs.missing.join(", ") }));
|
|
26346
26675
|
const { commit } = dto;
|
|
26347
26676
|
if (commit.state === "committed") lines.push(t2("update.text.committed", { commit: commit.commit.slice(0, 12), paths: commit.paths.join(", ") }));
|
|
26348
26677
|
else if (commit.state === "nothing") lines.push(t2("update.text.commitNothing"));
|
|
@@ -26355,18 +26684,18 @@ async function runUpdate(options, version2) {
|
|
|
26355
26684
|
} catch (error62) {
|
|
26356
26685
|
const known = error62 instanceof InitError || error62 instanceof RepositoryReadError;
|
|
26357
26686
|
const failure2 = { code: known ? error62.code : "UPDATE_FAILED", message: known ? error62.message : t2("update.failed") };
|
|
26358
|
-
process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(
|
|
26687
|
+
process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(updateV3.parse({ contract: "update", version: 3, ok: false, error: failure2 })) + "\n");
|
|
26359
26688
|
process.exitCode = 1;
|
|
26360
26689
|
}
|
|
26361
26690
|
}
|
|
26362
26691
|
|
|
26363
26692
|
// src/commands/spec-preview.ts
|
|
26364
|
-
import { join as
|
|
26693
|
+
import { join as join15 } from "node:path";
|
|
26365
26694
|
import { readFile as readFile12, stat as stat2 } from "node:fs/promises";
|
|
26366
26695
|
|
|
26367
26696
|
// src/adapters/filesystem/spec-preview-prepare.ts
|
|
26368
|
-
import { lstat as
|
|
26369
|
-
import { join as
|
|
26697
|
+
import { lstat as lstat6 } from "node:fs/promises";
|
|
26698
|
+
import { join as join10 } from "node:path";
|
|
26370
26699
|
import { createHash as createHash4 } from "node:crypto";
|
|
26371
26700
|
var fail5 = (message) => {
|
|
26372
26701
|
throw new SpecPreviewError(message);
|
|
@@ -26379,7 +26708,7 @@ async function readPreviewContext(cwd) {
|
|
|
26379
26708
|
const { gitDir } = await reader.location();
|
|
26380
26709
|
const checkOperation = async () => {
|
|
26381
26710
|
for (const name of ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply"]) {
|
|
26382
|
-
const exists = await
|
|
26711
|
+
const exists = await lstat6(join10(gitDir, name)).catch((e) => {
|
|
26383
26712
|
if (e.code === "ENOENT") return void 0;
|
|
26384
26713
|
throw e;
|
|
26385
26714
|
});
|
|
@@ -26433,9 +26762,9 @@ async function verifyPreparedPreview(cwd, input2, staged) {
|
|
|
26433
26762
|
if (staged) {
|
|
26434
26763
|
const index = await c.reader.index();
|
|
26435
26764
|
if (index.size !== current.files.size) fail5(t2("prepare.stagingScope"));
|
|
26436
|
-
for (const [path3,
|
|
26765
|
+
for (const [path3, source2] of current.files) {
|
|
26437
26766
|
const oid2 = index.get(path3);
|
|
26438
|
-
const bytes = Buffer.from(
|
|
26767
|
+
const bytes = Buffer.from(source2);
|
|
26439
26768
|
const hash3 = createHash4(oid2?.length === 64 ? "sha256" : "sha1").update(`blob ${bytes.length}\0`).update(bytes).digest("hex");
|
|
26440
26769
|
if (oid2 !== hash3) fail5(t2("prepare.stagingContent", { path: path3 }));
|
|
26441
26770
|
}
|
|
@@ -26448,26 +26777,26 @@ async function verifyPreparedPreview(cwd, input2, staged) {
|
|
|
26448
26777
|
|
|
26449
26778
|
// src/adapters/filesystem/agent-inputs.ts
|
|
26450
26779
|
import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
|
|
26451
|
-
import { lstat as
|
|
26780
|
+
import { lstat as lstat7, mkdir as mkdir5, readdir as readdir5, realpath as realpath3, unlink as unlink4, writeFile as writeFile3 } from "node:fs/promises";
|
|
26452
26781
|
import { tmpdir } from "node:os";
|
|
26453
|
-
import { basename, dirname as
|
|
26782
|
+
import { basename, dirname as dirname4, join as join11 } from "node:path";
|
|
26454
26783
|
var maxAge = 7 * 24 * 60 * 60 * 1e3;
|
|
26455
|
-
var
|
|
26784
|
+
var info4 = (path3) => lstat7(path3).catch((e) => {
|
|
26456
26785
|
if (e.code === "ENOENT") return void 0;
|
|
26457
26786
|
throw e;
|
|
26458
26787
|
});
|
|
26459
26788
|
var comparable = (path3) => process.platform === "win32" ? path3.toLowerCase() : path3;
|
|
26460
26789
|
async function candidates(root, controls) {
|
|
26461
26790
|
const key = createHash5("sha256").update(comparable(await realpath3(root))).digest("hex").slice(0, 16);
|
|
26462
|
-
return { system:
|
|
26791
|
+
return { system: join11((controls.tmpdir ?? tmpdir)(), "gitifact", key), project: join11(root, ".gitifact", "tmp") };
|
|
26463
26792
|
}
|
|
26464
26793
|
async function writable(directory) {
|
|
26465
26794
|
try {
|
|
26466
|
-
await
|
|
26467
|
-
const stat3 = await
|
|
26795
|
+
await mkdir5(directory, { recursive: true });
|
|
26796
|
+
const stat3 = await info4(directory);
|
|
26468
26797
|
if (!stat3?.isDirectory() || stat3.isSymbolicLink()) return false;
|
|
26469
|
-
const probe =
|
|
26470
|
-
await
|
|
26798
|
+
const probe = join11(directory, ".probe-" + randomBytes2(8).toString("hex"));
|
|
26799
|
+
await writeFile3(probe, "", { flag: "wx" });
|
|
26471
26800
|
await unlink4(probe);
|
|
26472
26801
|
return true;
|
|
26473
26802
|
} catch {
|
|
@@ -26480,28 +26809,28 @@ async function prepareAgentInputs(root, controls = {}) {
|
|
|
26480
26809
|
if (await writable(system)) directory = system;
|
|
26481
26810
|
else if (await writable(project)) {
|
|
26482
26811
|
directory = project;
|
|
26483
|
-
await
|
|
26812
|
+
await writeFile3(join11(project, ".gitignore"), "*\n", { flag: "wx" }).catch((e) => {
|
|
26484
26813
|
if (e.code !== "EEXIST") throw e;
|
|
26485
26814
|
});
|
|
26486
26815
|
}
|
|
26487
26816
|
if (!directory) return void 0;
|
|
26488
26817
|
const now = (controls.now ?? Date.now)();
|
|
26489
|
-
for (const name of await
|
|
26818
|
+
for (const name of await readdir5(directory).catch(() => [])) {
|
|
26490
26819
|
if (name === ".gitignore") continue;
|
|
26491
|
-
const stat3 = await
|
|
26492
|
-
if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await unlink4(
|
|
26820
|
+
const stat3 = await info4(join11(directory, name)).catch(() => void 0);
|
|
26821
|
+
if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await unlink4(join11(directory, name)).catch(() => void 0);
|
|
26493
26822
|
}
|
|
26494
|
-
return { save:
|
|
26823
|
+
return { save: join11(directory, "save.json"), commit: join11(directory, "commit.json") };
|
|
26495
26824
|
}
|
|
26496
26825
|
async function discardAgentInput(root, file2, controls = {}) {
|
|
26497
26826
|
try {
|
|
26498
|
-
const stat3 = await
|
|
26827
|
+
const stat3 = await info4(file2);
|
|
26499
26828
|
if (!stat3?.isFile() || stat3.isSymbolicLink()) return false;
|
|
26500
|
-
const parent = comparable(await realpath3(
|
|
26829
|
+
const parent = comparable(await realpath3(dirname4(file2)));
|
|
26501
26830
|
const { system, project } = await candidates(root, controls);
|
|
26502
26831
|
let managed = false;
|
|
26503
26832
|
for (const directory of [system, project]) {
|
|
26504
|
-
if (await
|
|
26833
|
+
if (await info4(directory) && comparable(await realpath3(directory)) === parent) managed = true;
|
|
26505
26834
|
}
|
|
26506
26835
|
if (!managed || basename(file2) === ".gitignore") return false;
|
|
26507
26836
|
await unlink4(file2);
|
|
@@ -26513,13 +26842,13 @@ async function discardAgentInput(root, file2, controls = {}) {
|
|
|
26513
26842
|
|
|
26514
26843
|
// src/commands/spec-preview-commit.ts
|
|
26515
26844
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
26516
|
-
import { readFile as readFile10, mkdir as
|
|
26517
|
-
import { join as
|
|
26845
|
+
import { readFile as readFile10, mkdir as mkdir6, open as open4, rename as rename4, unlink as unlink5, rmdir as rmdir2, writeFile as writeFile4 } from "node:fs/promises";
|
|
26846
|
+
import { join as join13 } from "node:path";
|
|
26518
26847
|
|
|
26519
26848
|
// src/commands/spec-commit-files.ts
|
|
26520
26849
|
import { createHash as createHash6 } from "node:crypto";
|
|
26521
|
-
import { lstat as
|
|
26522
|
-
import { join as
|
|
26850
|
+
import { lstat as lstat8, readFile as readFile9 } from "node:fs/promises";
|
|
26851
|
+
import { join as join12 } from "node:path";
|
|
26523
26852
|
var fail6 = (message) => {
|
|
26524
26853
|
throw new SpecPreviewError(message);
|
|
26525
26854
|
};
|
|
@@ -26528,7 +26857,7 @@ var optional2 = (path3) => readFile9(path3).catch((e) => {
|
|
|
26528
26857
|
if (e.code === "ENOENT") return null;
|
|
26529
26858
|
throw e;
|
|
26530
26859
|
});
|
|
26531
|
-
var
|
|
26860
|
+
var info5 = (path3) => lstat8(path3).catch((e) => {
|
|
26532
26861
|
if (e.code === "ENOENT") return void 0;
|
|
26533
26862
|
throw e;
|
|
26534
26863
|
});
|
|
@@ -26540,11 +26869,11 @@ async function fingerprint(root, path3) {
|
|
|
26540
26869
|
validPath2(path3);
|
|
26541
26870
|
const parts = path3.split("/");
|
|
26542
26871
|
for (let i = 1; i <= parts.length; i++) {
|
|
26543
|
-
const target =
|
|
26544
|
-
const stat3 = await
|
|
26872
|
+
const target = join12(root, ...parts.slice(0, i));
|
|
26873
|
+
const stat3 = await info5(target);
|
|
26545
26874
|
if (!stat3) return null;
|
|
26546
26875
|
if (stat3.isSymbolicLink() || (i < parts.length ? !stat3.isDirectory() : !stat3.isFile())) fail6(t2("commitFiles.linkOrDirectory"));
|
|
26547
|
-
if (i < parts.length && await
|
|
26876
|
+
if (i < parts.length && await info5(join12(target, ".git"))) fail6(t2("commitFiles.nestedRepository"));
|
|
26548
26877
|
if (i === parts.length) {
|
|
26549
26878
|
if (stat3.nlink !== 1 || stat3.size > 16 * 1024 * 1024) fail6(t2("commitFiles.hardLinkOrSize"));
|
|
26550
26879
|
return hash2(Buffer.concat([Buffer.from(String(stat3.mode & 73) + "\0"), await readFile9(target)]));
|
|
@@ -26575,9 +26904,9 @@ function policyPaths(files) {
|
|
|
26575
26904
|
return [...all].sort();
|
|
26576
26905
|
}
|
|
26577
26906
|
async function checkLegacySelection(root, selected) {
|
|
26578
|
-
const legacyJson = /^\.(?:gitifact|tryce)\/(?:spec\/[^/]+\/tryce\.json|notes\/N-[a-f0-9-]+\.json|mode-[a-f0-9-]+\.json|config\.(?:init-1|prototype-1)\.[a-f0-9]+\.json)
|
|
26907
|
+
const legacyJson = /^\.(?:gitifact|tryce)\/(?:spec\/[^/]+\/tryce\.json|notes\/N-[a-f0-9-]+\.json|mode-[a-f0-9-]+\.json|config\.(?:init-1|prototype-1)\.[a-f0-9]+\.json)$|^\.gitifact\/(?:product|guides|overrides)\/.+$/;
|
|
26579
26908
|
for (const p of selected.filter((p2) => p2.startsWith(".gitifact/") && !record2(p2) && p2 !== ".gitifact/config.json")) {
|
|
26580
|
-
if (
|
|
26909
|
+
if (isAssetPath(p)) continue;
|
|
26581
26910
|
if (!legacyJson.test(p) || await fingerprint(root, p) !== null) fail6(t2("commitFiles.legacyDeleteOnly", { path: p }));
|
|
26582
26911
|
}
|
|
26583
26912
|
for (const p of selected.filter((p2) => p2.startsWith(".tryce/"))) {
|
|
@@ -26610,8 +26939,8 @@ async function previewCommit(cwd, action, input2) {
|
|
|
26610
26939
|
});
|
|
26611
26940
|
const indexPath = (await git(["rev-parse", "--path-format=absolute", "--git-path", "index"])).toString("utf8").trim();
|
|
26612
26941
|
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();
|
|
26613
|
-
const busy =
|
|
26614
|
-
if (await
|
|
26942
|
+
const busy = join13(gitDir, "gitifact-spec-commit.lock");
|
|
26943
|
+
if (await info5(busy)) fail6(t2("commit.busy", { path: busy }));
|
|
26615
26944
|
if ((await staged()).length) fail6(t2("commit.existingStagingPlan"));
|
|
26616
26945
|
const request = object3(input2);
|
|
26617
26946
|
let plan;
|
|
@@ -26668,33 +26997,33 @@ async function previewCommit(cwd, action, input2) {
|
|
|
26668
26997
|
if (!("plan" in refreshed) || refreshed.plan.digest !== plan.digest) fail6(t2("commit.planStale"));
|
|
26669
26998
|
const before = await reader.baseline();
|
|
26670
26999
|
const original = await optional2(indexPath);
|
|
26671
|
-
await
|
|
26672
|
-
const draftLock =
|
|
27000
|
+
await mkdir6(busy);
|
|
27001
|
+
const draftLock = join13(gitDir, "gitifact-spec-preview.lock");
|
|
26673
27002
|
let draftOwned = false;
|
|
26674
27003
|
let indexLock;
|
|
26675
27004
|
let uncertain = false;
|
|
26676
27005
|
let commitStarted = false;
|
|
26677
|
-
const temporary =
|
|
27006
|
+
const temporary = join13(gitDir, "gitifact-commit-index-" + randomUUID6());
|
|
26678
27007
|
const checkFiles = async () => {
|
|
26679
27008
|
for (const f of [...plan.files, ...plan.context]) if (f.hash !== await fingerprint(root, f.path)) fail6(t2("commit.fileChangedPreparing", { path: f.path }));
|
|
26680
27009
|
if ((await readLockedPreviewState(root)).stamp !== object3(plan.verification).stamp) fail6(t2("commit.specsChangedPreparing"));
|
|
26681
|
-
for (const name of ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply"]) if (await
|
|
27010
|
+
for (const name of ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply"]) if (await info5(join13(gitDir, name))) fail6(t2("commit.integrationStarted"));
|
|
26682
27011
|
};
|
|
26683
27012
|
try {
|
|
26684
27013
|
await verifyPreparedPreview(cwd, plan.verification, false);
|
|
26685
|
-
await
|
|
27014
|
+
await mkdir6(draftLock);
|
|
26686
27015
|
draftOwned = true;
|
|
26687
27016
|
indexLock = await open4(indexPath + ".lock", "wx", 384);
|
|
26688
27017
|
if (plan.index !== hash2(await optional2(indexPath) ?? Buffer.alloc(0)) || (await staged()).length) fail6(t2("commit.indexChangedLocked"));
|
|
26689
|
-
await
|
|
26690
|
-
if (original) await
|
|
27018
|
+
await writeFile4(join13(busy, "recovery.json"), JSON.stringify({ before, originalIndex: original?.toString("base64") ?? null, temporary, plan }));
|
|
27019
|
+
if (original) await writeFile4(temporary, original, { flag: "wx" });
|
|
26691
27020
|
else await git(["read-tree", "--empty"], temporary);
|
|
26692
27021
|
await git(["add", "--", ...selected], temporary);
|
|
26693
27022
|
const actual = await staged(temporary);
|
|
26694
27023
|
if (!actual.length || actual.some((p) => !selected.includes(p))) fail6(t2("commit.unexpectedFiles"));
|
|
26695
27024
|
for (const file2 of plan.files.filter((f) => record2(f.path) && f.hash !== null)) {
|
|
26696
27025
|
const stagedBytes = await git(["show", ":" + file2.path], temporary);
|
|
26697
|
-
if (!stagedBytes.equals(await readFile10(
|
|
27026
|
+
if (!stagedBytes.equals(await readFile10(join13(root, file2.path)))) fail6(t2("commit.filterChanged"));
|
|
26698
27027
|
}
|
|
26699
27028
|
await checkFiles();
|
|
26700
27029
|
if (JSON.stringify(before) !== JSON.stringify(await reader.baseline())) fail6(t2("commit.headChanged"));
|
|
@@ -26712,7 +27041,7 @@ async function previewCommit(cwd, action, input2) {
|
|
|
26712
27041
|
await indexLock.writeFile(await readFile10(temporary));
|
|
26713
27042
|
await indexLock.sync();
|
|
26714
27043
|
await indexLock.close();
|
|
26715
|
-
await
|
|
27044
|
+
await rename4(indexPath + ".lock", indexPath);
|
|
26716
27045
|
indexLock = void 0;
|
|
26717
27046
|
return { outcome: "committed", commit: after.head, paths: actual, requirements: plan.requirements, pushed: false };
|
|
26718
27047
|
} catch (error62) {
|
|
@@ -26733,7 +27062,7 @@ async function previewCommit(cwd, action, input2) {
|
|
|
26733
27062
|
});
|
|
26734
27063
|
await unlink5(temporary).catch(() => {
|
|
26735
27064
|
});
|
|
26736
|
-
await unlink5(
|
|
27065
|
+
await unlink5(join13(busy, "recovery.json")).catch(() => {
|
|
26737
27066
|
});
|
|
26738
27067
|
await rmdir2(busy);
|
|
26739
27068
|
if (draftOwned) await rmdir2(draftLock);
|
|
@@ -26743,8 +27072,8 @@ async function previewCommit(cwd, action, input2) {
|
|
|
26743
27072
|
|
|
26744
27073
|
// src/commands/spec-commit.ts
|
|
26745
27074
|
import { createHash as createHash8, randomUUID as randomUUID7 } from "node:crypto";
|
|
26746
|
-
import { mkdir as
|
|
26747
|
-
import { join as
|
|
27075
|
+
import { mkdir as mkdir7, open as open5, readFile as readFile11, rename as rename5, rmdir as rmdir3, unlink as unlink6, writeFile as writeFile5 } from "node:fs/promises";
|
|
27076
|
+
import { join as join14 } from "node:path";
|
|
26748
27077
|
var fields = ["reasons", "paths", "message", "authorization", "requirements", "policyFiles", "expected"];
|
|
26749
27078
|
var integration = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply"];
|
|
26750
27079
|
var same2 = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
@@ -26783,9 +27112,9 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26783
27112
|
...stdin ? { input: stdin } : {}
|
|
26784
27113
|
});
|
|
26785
27114
|
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();
|
|
26786
|
-
const busy =
|
|
27115
|
+
const busy = join14(gitDir, "gitifact-spec-commit.lock");
|
|
26787
27116
|
await failOnLegacyLock(gitDir);
|
|
26788
|
-
if (await
|
|
27117
|
+
if (await info5(busy)) fail6(t2("commit.busy", { path: busy }));
|
|
26789
27118
|
if ((await staged()).length) fail6(t2("commit.existingStaging"));
|
|
26790
27119
|
const indexHash = hash2(await optional2(indexPath) ?? Buffer.alloc(0));
|
|
26791
27120
|
const current = c.working;
|
|
@@ -26835,22 +27164,22 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26835
27164
|
const trailers = [
|
|
26836
27165
|
...requirements.map((id) => "Gitifact-Req: " + id),
|
|
26837
27166
|
...prepared.changes.filter((c2) => c2.kind === "design").map((c2) => "Gitifact-Design: " + c2.id),
|
|
26838
|
-
...prepared.changes.filter((c2) => c2.kind === "
|
|
27167
|
+
...prepared.changes.filter((c2) => c2.kind === "wiki").map((c2) => "Gitifact-Doc: " + c2.id)
|
|
26839
27168
|
];
|
|
26840
|
-
const temporary =
|
|
27169
|
+
const temporary = join14(gitDir, "gitifact-commit-index-" + randomUUID7());
|
|
26841
27170
|
let owned = false;
|
|
26842
27171
|
let uncertain = false;
|
|
26843
27172
|
let indexLock;
|
|
26844
27173
|
let committed;
|
|
26845
27174
|
try {
|
|
26846
27175
|
try {
|
|
26847
|
-
await
|
|
27176
|
+
await mkdir7(busy);
|
|
26848
27177
|
owned = true;
|
|
26849
27178
|
} catch (e) {
|
|
26850
27179
|
if (e.code === "EEXIST") fail6(t2("commit.busy", { path: busy }));
|
|
26851
27180
|
throw e;
|
|
26852
27181
|
}
|
|
26853
|
-
await previewTransaction(cwd, current.stamp, async () => ({ writes, data: {}, recheck: c.recheck }),
|
|
27182
|
+
await previewTransaction(cwd, current.stamp, async () => ({ writes, data: {}, recheck: c.recheck }), rename5, async (published) => {
|
|
26854
27183
|
let commitStarted = false;
|
|
26855
27184
|
try {
|
|
26856
27185
|
const lock = indexLock = await open5(indexPath + ".lock", "wx", 384);
|
|
@@ -26858,8 +27187,8 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26858
27187
|
if (hash2(original ?? Buffer.alloc(0)) !== indexHash || (await staged()).length) fail6(t2("commit.indexChanged"));
|
|
26859
27188
|
const locked = await hashes([.../* @__PURE__ */ new Set([...selected, ...contextPaths])]);
|
|
26860
27189
|
for (const [path3, value] of started) if (locked.get(path3) !== value) fail6(t2("commit.fileChangedRunning", { path: path3 }));
|
|
26861
|
-
await
|
|
26862
|
-
if (original) await
|
|
27190
|
+
await writeFile5(join14(busy, "recovery.json"), JSON.stringify({ before: c.base, originalIndex: original?.toString("base64") ?? null, temporary, paths: selected, message, requirements }));
|
|
27191
|
+
if (original) await writeFile5(temporary, original, { flag: "wx" });
|
|
26863
27192
|
else await git(["read-tree", "--empty"], temporary);
|
|
26864
27193
|
await git(["add", "--", ...stageable], temporary);
|
|
26865
27194
|
const actual = await staged(temporary);
|
|
@@ -26874,7 +27203,7 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26874
27203
|
const size = Number(header[2]);
|
|
26875
27204
|
if (end < 0 || header[1] !== "blob" || !Number.isSafeInteger(size) || output2[end + 1 + size] !== 10) fail6(t2("commit.stagedUnreadable", { path: path3 }));
|
|
26876
27205
|
const staged2 = output2.subarray(end + 1, end + 1 + size);
|
|
26877
|
-
const file2 = await readFile11(
|
|
27206
|
+
const file2 = await readFile11(join14(root, path3));
|
|
26878
27207
|
if (!staged2.equals(file2) && !staged2.equals(Buffer.from(file2.toString("latin1").replace(/\r\n/g, "\n"), "latin1"))) fail6(t2("commit.filterChanged"));
|
|
26879
27208
|
offset = end + size + 2;
|
|
26880
27209
|
}
|
|
@@ -26882,7 +27211,7 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26882
27211
|
const again = await hashes([...locked.keys()]);
|
|
26883
27212
|
for (const [path3, value] of locked) if (again.get(path3) !== value) fail6(t2("commit.fileChangedPreparing", { path: path3 }));
|
|
26884
27213
|
if ((await readLockedPreviewState(root)).stamp !== published.stamp) fail6(t2("commit.specsChangedPreparing"));
|
|
26885
|
-
for (const name of integration) if (await
|
|
27214
|
+
for (const name of integration) if (await info5(join14(gitDir, name))) fail6(t2("commit.integrationStarted"));
|
|
26886
27215
|
if (!same2(c.base, await c.reader.baseline())) fail6(t2("commit.headChanged"));
|
|
26887
27216
|
const tree = (await git(["write-tree"], temporary)).toString("utf8").trim();
|
|
26888
27217
|
commitStarted = true;
|
|
@@ -26900,7 +27229,7 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26900
27229
|
await lock.writeFile(await readFile11(temporary));
|
|
26901
27230
|
await lock.sync();
|
|
26902
27231
|
await lock.close();
|
|
26903
|
-
await
|
|
27232
|
+
await rename5(indexPath + ".lock", indexPath);
|
|
26904
27233
|
indexLock = void 0;
|
|
26905
27234
|
committed = { commit: after.head, paths: actual };
|
|
26906
27235
|
} catch (error62) {
|
|
@@ -26924,7 +27253,7 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26924
27253
|
await unlink6(temporary).catch(() => {
|
|
26925
27254
|
});
|
|
26926
27255
|
if (owned) {
|
|
26927
|
-
await unlink6(
|
|
27256
|
+
await unlink6(join14(busy, "recovery.json")).catch(() => {
|
|
26928
27257
|
});
|
|
26929
27258
|
await rmdir3(busy);
|
|
26930
27259
|
}
|
|
@@ -26941,7 +27270,7 @@ async function execute(action, options, controls) {
|
|
|
26941
27270
|
try {
|
|
26942
27271
|
const guard = specPreviewReader(process.cwd());
|
|
26943
27272
|
const { root, gitDir, objectFormat } = await guard.location();
|
|
26944
|
-
for (const marker2 of operations) if (await fileInfo(
|
|
27273
|
+
for (const marker2 of operations) if (await fileInfo(join15(gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
|
|
26945
27274
|
if (await guard.hasUnmerged()) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
26946
27275
|
const raw = await readConfigFile(root);
|
|
26947
27276
|
if (raw === void 0) throw new SpecPreviewError(t2("preview.notInitialized"));
|
|
@@ -26973,7 +27302,7 @@ async function execute(action, options, controls) {
|
|
|
26973
27302
|
if (action === "read") {
|
|
26974
27303
|
const commit = await reader.resolve(options.ref ?? "HEAD");
|
|
26975
27304
|
const bundle = await reader.readBundle(commit);
|
|
26976
|
-
process.stdout.write(JSON.stringify({ ...envelope, ok: true, commit, specs: bundle.specs,
|
|
27305
|
+
process.stdout.write(JSON.stringify({ ...envelope, ok: true, commit, specs: bundle.specs, wiki: bundle.wiki }) + "\n");
|
|
26977
27306
|
} else {
|
|
26978
27307
|
const [from, to] = await Promise.all([reader.resolve(options.from), reader.resolve(options.to)]);
|
|
26979
27308
|
const [before, after] = await Promise.all([reader.readBundle(from), reader.readBundle(to)]);
|
|
@@ -26986,8 +27315,8 @@ async function execute(action, options, controls) {
|
|
|
26986
27315
|
}
|
|
26987
27316
|
}
|
|
26988
27317
|
async function readInputFile(path3) {
|
|
26989
|
-
const
|
|
26990
|
-
if (!
|
|
27318
|
+
const source2 = await stat2(path3);
|
|
27319
|
+
if (!source2.isFile() || source2.size > 1024 * 1024) throw new SpecPreviewError(t2("preview.inputFile"));
|
|
26991
27320
|
return readFile12(path3);
|
|
26992
27321
|
}
|
|
26993
27322
|
async function readStdin(stream) {
|
|
@@ -27002,29 +27331,29 @@ async function readStdin(stream) {
|
|
|
27002
27331
|
}
|
|
27003
27332
|
function narrowWorking(working, options, inputs) {
|
|
27004
27333
|
if (options.stamp) return { stamp: working.stamp, inputs };
|
|
27005
|
-
let { specs,
|
|
27334
|
+
let { specs, wiki, warnings } = working;
|
|
27006
27335
|
if (options.feature !== void 0) {
|
|
27007
27336
|
specs = specs.filter((s) => s.path === ".gitifact/spec/" + options.feature + "/requirements.md");
|
|
27008
27337
|
if (!specs.length) throw new SpecPreviewError(t2("preview.unknownFeature", { feature: options.feature }));
|
|
27009
|
-
|
|
27010
|
-
warnings = warnings.filter((w) => w.specId === specs[0].id);
|
|
27338
|
+
wiki = { documents: [], history: [] };
|
|
27339
|
+
warnings = warnings.filter((w) => w.code === "MISSING_DESIGN_REFERENCE" && w.specId === specs[0].id);
|
|
27011
27340
|
}
|
|
27012
|
-
if (!options.ids) return { stamp: working.stamp, inputs, specs,
|
|
27341
|
+
if (!options.ids) return { stamp: working.stamp, inputs, specs, wiki, warnings };
|
|
27013
27342
|
return {
|
|
27014
27343
|
stamp: working.stamp,
|
|
27015
27344
|
inputs,
|
|
27016
27345
|
warnings,
|
|
27017
27346
|
specs: specs.map((s) => ({ id: s.id, path: s.path, title: s.title, design: s.design?.title ?? null, requirements: s.requirements.map((r) => ({ id: r.id, title: r.title })) })),
|
|
27018
|
-
|
|
27347
|
+
wiki: { documents: wiki.documents.map((d) => ({ id: d.id, path: d.path, title: d.title })) }
|
|
27019
27348
|
};
|
|
27020
27349
|
}
|
|
27021
27350
|
|
|
27022
27351
|
// src/commands/migrate.ts
|
|
27023
|
-
import { lstat as
|
|
27024
|
-
import { join as
|
|
27352
|
+
import { lstat as lstat10, readdir as readdir6, readFile as readFile13, rename as rename6, writeFile as writeFile6, unlink as unlink7 } from "node:fs/promises";
|
|
27353
|
+
import { join as join16 } from "node:path";
|
|
27025
27354
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
27026
27355
|
var operations2 = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"];
|
|
27027
|
-
var
|
|
27356
|
+
var info6 = (path3) => lstat10(path3).catch((e) => {
|
|
27028
27357
|
if (e.code === "ENOENT") return void 0;
|
|
27029
27358
|
throw e;
|
|
27030
27359
|
});
|
|
@@ -27035,40 +27364,40 @@ var fail7 = (code, message) => {
|
|
|
27035
27364
|
async function migrateStore(cwd, dryRun = false) {
|
|
27036
27365
|
const reader = specPreviewReader(cwd);
|
|
27037
27366
|
const { root, gitDir } = await reader.location();
|
|
27038
|
-
for (const marker3 of operations2) if (await
|
|
27367
|
+
for (const marker3 of operations2) if (await info6(join16(gitDir, marker3))) fail7("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker3 }));
|
|
27039
27368
|
if (await reader.hasUnmerged()) fail7("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
27040
|
-
for (const name of [...LEGACY_LOCKS, "gitifact-spec-preview.lock", "gitifact-spec-commit.lock"]) if (await
|
|
27041
|
-
const from =
|
|
27042
|
-
const to =
|
|
27043
|
-
const fromInfo = await
|
|
27044
|
-
if (await
|
|
27369
|
+
for (const name of [...LEGACY_LOCKS, "gitifact-spec-preview.lock", "gitifact-spec-commit.lock"]) if (await info6(join16(gitDir, name))) fail7("LOCKED", t2("migrate.locked", { path: join16(gitDir, name) }));
|
|
27370
|
+
const from = join16(root, ".tryce");
|
|
27371
|
+
const to = join16(root, ".gitifact");
|
|
27372
|
+
const fromInfo = await info6(from);
|
|
27373
|
+
if (await info6(to)) fail7("ALREADY_MIGRATED", t2("migrate.alreadyMigrated"));
|
|
27045
27374
|
if (!fromInfo) return fail7("NOTHING_TO_MIGRATE", t2("migrate.nothingToMigrate"));
|
|
27046
27375
|
if (!fromInfo.isDirectory() || fromInfo.isSymbolicLink()) fail7("PATH_CONFLICT", t2("migrate.notDirectory"));
|
|
27047
|
-
const configInfo = await
|
|
27376
|
+
const configInfo = await info6(join16(from, "config.json"));
|
|
27048
27377
|
if (!configInfo?.isFile() || configInfo.isSymbolicLink()) fail7("NOT_INITIALIZED", t2("migrate.noConfig"));
|
|
27049
|
-
const config2 = parseManagedConfig(decode4(await readFile13(
|
|
27378
|
+
const config2 = parseManagedConfig(decode4(await readFile13(join16(from, "config.json"))));
|
|
27050
27379
|
if (!("schemaVersion" in config2)) fail7("UNSUPPORTED_FORMAT", t2("migrate.legacyJson"));
|
|
27051
|
-
const top = (await
|
|
27380
|
+
const top = (await readdir6(from)).sort();
|
|
27052
27381
|
if (top.some((name) => name !== "config.json" && name !== "spec")) fail7("UNSUPPORTED_FORMAT", t2("migrate.extraEntries"));
|
|
27053
27382
|
const files = /* @__PURE__ */ new Map();
|
|
27054
27383
|
const rewrites = [];
|
|
27055
27384
|
const legacyRecords = [];
|
|
27056
27385
|
const marker2 = /^<!-- tryce-(spec|req|design|ref): /;
|
|
27057
27386
|
if (top.includes("spec")) {
|
|
27058
|
-
for (const feature2 of (await
|
|
27059
|
-
const dir =
|
|
27060
|
-
const stat3 = await
|
|
27387
|
+
for (const feature2 of (await readdir6(join16(from, "spec"))).sort()) {
|
|
27388
|
+
const dir = join16(from, "spec", feature2);
|
|
27389
|
+
const stat3 = await info6(dir);
|
|
27061
27390
|
if (!stat3?.isDirectory() || stat3.isSymbolicLink()) fail7("UNSUPPORTED_FORMAT", t2("migrate.notSpecFolder", { feature: feature2 }));
|
|
27062
|
-
for (const name of (await
|
|
27391
|
+
for (const name of (await readdir6(dir)).sort()) {
|
|
27063
27392
|
if (!["requirements.md", "design.md", "history.jsonl", "tryce.json"].includes(name)) fail7("UNSUPPORTED_FORMAT", t2("migrate.unsupportedFile", { feature: feature2, name }));
|
|
27064
|
-
const fileStat = await
|
|
27393
|
+
const fileStat = await info6(join16(dir, name));
|
|
27065
27394
|
if (!fileStat?.isFile() || fileStat.isSymbolicLink() || fileStat.nlink !== 1 || fileStat.size > 1024 * 1024) fail7("UNSUPPORTED_FORMAT", t2("migrate.fileTooLarge", { name }));
|
|
27066
27395
|
const newPath = `.gitifact/spec/${feature2}/${name}`;
|
|
27067
27396
|
if (name === "tryce.json") {
|
|
27068
27397
|
legacyRecords.push(newPath);
|
|
27069
27398
|
continue;
|
|
27070
27399
|
}
|
|
27071
|
-
const text2 = decode4(await readFile13(
|
|
27400
|
+
const text2 = decode4(await readFile13(join16(dir, name)));
|
|
27072
27401
|
if (name === "history.jsonl") {
|
|
27073
27402
|
files.set(newPath, text2);
|
|
27074
27403
|
continue;
|
|
@@ -27111,13 +27440,13 @@ async function migrateStore(cwd, dryRun = false) {
|
|
|
27111
27440
|
]
|
|
27112
27441
|
});
|
|
27113
27442
|
if (dryRun) return result("planned");
|
|
27114
|
-
await
|
|
27443
|
+
await rename6(from, to);
|
|
27115
27444
|
for (const r of rewrites) {
|
|
27116
|
-
const full =
|
|
27117
|
-
const temp =
|
|
27118
|
-
await
|
|
27445
|
+
const full = join16(root, r.path);
|
|
27446
|
+
const temp = join16(root, ".gitifact", "spec", ".gitifact-migrate-" + randomBytes3(8).toString("hex"));
|
|
27447
|
+
await writeFile6(temp, r.content, { flag: "wx" });
|
|
27119
27448
|
try {
|
|
27120
|
-
await
|
|
27449
|
+
await rename6(temp, full);
|
|
27121
27450
|
} finally {
|
|
27122
27451
|
await unlink7(temp).catch(() => {
|
|
27123
27452
|
});
|
|
@@ -27137,16 +27466,16 @@ async function runMigrate(options) {
|
|
|
27137
27466
|
}
|
|
27138
27467
|
|
|
27139
27468
|
// src/main.ts
|
|
27140
|
-
var program2 = new Command().name("gitifact").description(t2("help.program")).version("0.
|
|
27469
|
+
var program2 = new Command().name("gitifact").description(t2("help.program")).version("0.5.1").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
|
|
27141
27470
|
program2.command("status").description(t2("help.status")).allowExcessArguments(false).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).action(async (options) => {
|
|
27142
27471
|
await runStatus(options.format);
|
|
27143
27472
|
});
|
|
27144
|
-
program2.command("browser").description(t2("help.browser")).allowExcessArguments(false).option("--port <port>", t2("help.browserPort"), parsePort, 0).option("--dev", t2("help.browserDev")).option("--no-update-check", t2("help.browserNoUpdateCheck")).action((options) => runBrowser(options, "0.
|
|
27145
|
-
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.
|
|
27473
|
+
program2.command("browser").description(t2("help.browser")).allowExcessArguments(false).option("--port <port>", t2("help.browserPort"), parsePort, 0).option("--dev", t2("help.browserDev")).option("--no-update-check", t2("help.browserNoUpdateCheck")).action((options) => runBrowser(options, "0.5.1"));
|
|
27474
|
+
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.5.1"));
|
|
27146
27475
|
program2.command("docs").description(t2("help.docs")).argument("[topic]", t2("help.docsTopic")).allowExcessArguments(false).action(async (topic) => {
|
|
27147
27476
|
await runDocs(topic);
|
|
27148
27477
|
});
|
|
27149
|
-
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")).action((options) => runUpdate(options, "0.
|
|
27478
|
+
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")).action((options) => runUpdate(options, "0.5.1"));
|
|
27150
27479
|
program2.command("migrate").description(t2("help.migrate")).allowExcessArguments(false).option("--dry-run", t2("help.migrateDryRun")).action(runMigrate);
|
|
27151
27480
|
var spec = program2.command("spec").description(t2("help.spec"));
|
|
27152
27481
|
var replaced = t2("help.deprecated");
|