gitifact 0.4.4 → 0.5.0
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 +7 -5
- package/dist/browser/assets/{Markdown-Drk5rJF_.js → Markdown-C8C4nHPG.js} +3 -3
- package/dist/browser/assets/{MetadataListItem-o7NlLubs.js → MetadataListItem-CcPRe-aG.js} +1 -1
- package/dist/browser/assets/Table-B_rvjexw.js +3 -0
- package/dist/browser/assets/about-D6bBZN8p.js +3 -0
- package/dist/browser/assets/{routes-D_2lOqSD.js → activity-B7x648pR.js} +1 -1
- package/dist/browser/assets/changelog-DaxXahXm.js +2 -0
- package/dist/browser/assets/{contributors._email-CsIeETiA.js → contributors._email-BZevZqRL.js} +1 -1
- package/dist/browser/assets/{contributors.index-BVn-RzjO.js → contributors.index-BgA9LkC0.js} +1 -1
- package/dist/browser/assets/document-CFtIdIPN.js +9 -0
- package/dist/browser/assets/document-QMuypGH5.css +1 -0
- package/dist/browser/assets/features._featureId-BBYORTqC.js +1 -0
- package/dist/browser/assets/{requirements-C5TAa2tx.js → features.index-DuwIen6d.js} +1 -1
- package/dist/browser/assets/git-DVoIz_Mx.js +1 -0
- package/dist/browser/assets/index-BP3PrawI.js +48 -0
- package/dist/browser/assets/page-header-Zwbd52Q5.js +124 -0
- package/dist/browser/assets/product-DNlxFjU7.css +1 -0
- package/dist/browser/assets/product-euizlTTB.js +8 -0
- package/dist/browser/assets/product.index-B4D5SxOO.js +1 -0
- package/dist/browser/assets/request-state-Dz-oeAkg.js +1 -0
- package/dist/browser/assets/{features.index-Cf-ct7tk.js → requirements-wk9Joy7M.js} +1 -1
- package/dist/browser/assets/{settings-Dzak94qo.js → settings-DwA6H4Ax.js} +1 -1
- package/dist/browser/assets/wiki-DkAnuzON.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 +21 -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 +710 -437
- 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,19 @@ 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 CLI\uAC00 \uC77D\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC815\uC2DD \uBC84\uC804 \uC804 \uADDC\uC57D\uC740 \uC804\uD658\uD558\uC9C0 \uC54A\uC73C\uBA70 \uC0C8\uB85C init \uD569\uB2C8\uB2E4."
|
|
3510
3514
|
};
|
|
3511
3515
|
|
|
3512
3516
|
// ../../packages/core/dist/shared/i18n/index.js
|
|
@@ -3578,6 +3582,7 @@ function parseProjectConfig(text2) {
|
|
|
3578
3582
|
}
|
|
3579
3583
|
|
|
3580
3584
|
// ../../packages/core/dist/formats/spec-project.js
|
|
3585
|
+
var SCHEMA_VERSION = 2;
|
|
3581
3586
|
function parseManagedConfig(text2) {
|
|
3582
3587
|
let value;
|
|
3583
3588
|
try {
|
|
@@ -3588,41 +3593,50 @@ function parseManagedConfig(text2) {
|
|
|
3588
3593
|
if (!value || typeof value !== "object" || Array.isArray(value) || !("schemaVersion" in value))
|
|
3589
3594
|
return parseProjectConfig(text2);
|
|
3590
3595
|
const config2 = value;
|
|
3591
|
-
if (Object.keys(config2).sort().join(",") !== "baseline,schemaVersion"
|
|
3596
|
+
if (Object.keys(config2).sort().join(",") !== "baseline,schemaVersion")
|
|
3592
3597
|
throw new InitError("INVALID_CONFIG", t("config.schemaFields"));
|
|
3598
|
+
if (config2.schemaVersion !== SCHEMA_VERSION)
|
|
3599
|
+
throw new InitError("UNSUPPORTED_SCHEMA", t("config.unsupportedSchema", { version: String(config2.schemaVersion) }));
|
|
3593
3600
|
const checked = parseProjectConfig(JSON.stringify({ kind: "tryce-project", format: "workflow-1", mode: "auto", baseline: config2.baseline }));
|
|
3594
|
-
return { schemaVersion:
|
|
3601
|
+
return { schemaVersion: SCHEMA_VERSION, baseline: checked.baseline };
|
|
3595
3602
|
}
|
|
3596
3603
|
|
|
3597
3604
|
// ../../packages/core/dist/formats/spec-preview.js
|
|
3598
3605
|
var SpecPreviewError = class extends Error {
|
|
3599
3606
|
code = "INVALID_SPEC_PREVIEW";
|
|
3600
3607
|
};
|
|
3601
|
-
var
|
|
3602
|
-
var
|
|
3603
|
-
var
|
|
3604
|
-
var
|
|
3605
|
-
var
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
}
|
|
3608
|
+
var WIKI_DIR = ".gitifact/wiki";
|
|
3609
|
+
var WIKI_HISTORY_PATH = WIKI_DIR + "/history.jsonl";
|
|
3610
|
+
var WIKI_ENTRY = "README.md";
|
|
3611
|
+
var WIKI_ENTRY_PATH = WIKI_DIR + "/" + WIKI_ENTRY;
|
|
3612
|
+
var ASSETS_DIR = ".gitifact/assets";
|
|
3613
|
+
var RECOMMENDED_ASSET_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "webp", "svg", "pdf"];
|
|
3614
|
+
var ASSET_SIZE_LIMIT = 1024 * 1024;
|
|
3615
|
+
var ASSETS_TOTAL_LIMIT = 50 * 1024 * 1024;
|
|
3610
3616
|
var token = "[a-z2-7]{10}";
|
|
3611
3617
|
var STORE_DIR = ".gitifact";
|
|
3612
3618
|
var LEGACY_STORE_DIR = ".tryce";
|
|
3613
3619
|
var STORE_DIRS = [STORE_DIR, LEGACY_STORE_DIR];
|
|
3614
3620
|
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";
|
|
3621
|
+
var recordPathPattern = /^(?:\.(?:gitifact|tryce)\/spec\/[^/]+\/(?:requirements\.md|design\.md|history\.jsonl)|\.gitifact\/wiki\/(?:(?:[^/]+\/)*[^/]+\.md|history\.jsonl))$/;
|
|
3622
|
+
var documentPathPattern = /^\.gitifact\/wiki\/((?:[^/]+\/)*[^/]+\.md)$/;
|
|
3623
|
+
var documentHistoryPattern = /^\.gitifact\/wiki\/history\.jsonl$/;
|
|
3619
3624
|
var nameToken = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
3625
|
+
var segmentOk = (part) => !!part && part !== "." && part !== ".." && !/[\\:\0]/.test(part);
|
|
3626
|
+
function isAssetPath(path3) {
|
|
3627
|
+
if (!path3.startsWith(ASSETS_DIR + "/"))
|
|
3628
|
+
return false;
|
|
3629
|
+
const parts = path3.slice(ASSETS_DIR.length + 1).split("/");
|
|
3630
|
+
return parts.every(segmentOk) && parts.length <= 8 && path3.length <= 300;
|
|
3631
|
+
}
|
|
3632
|
+
var assetExtension = (path3) => (/\.([A-Za-z0-9]+)$/.exec(path3)?.[1] ?? "").toLowerCase();
|
|
3633
|
+
var rootUpperName = /^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)*\.md$/;
|
|
3620
3634
|
function validateDocumentRelativePath(relative) {
|
|
3621
3635
|
const parts = relative.split("/");
|
|
3622
3636
|
if (relative.length > 200 || parts.length > 8)
|
|
3623
3637
|
fail(t("document.pathTooLong", { path: relative }));
|
|
3624
3638
|
const file2 = parts.pop();
|
|
3625
|
-
if (!file2.endsWith(".md") || !nameToken.test(file2.slice(0, -3)))
|
|
3639
|
+
if (!(parts.length === 0 && rootUpperName.test(file2)) && (!file2.endsWith(".md") || !nameToken.test(file2.slice(0, -3))))
|
|
3626
3640
|
fail(t("document.fileName", { path: relative }));
|
|
3627
3641
|
for (const part of parts)
|
|
3628
3642
|
if (!nameToken.test(part) || part.length > 80)
|
|
@@ -3634,19 +3648,115 @@ var fail = (message) => {
|
|
|
3634
3648
|
throw new SpecPreviewError(message);
|
|
3635
3649
|
};
|
|
3636
3650
|
var normalized = (value) => value.replace(/\r\n/g, "\n").trim();
|
|
3651
|
+
var scalar = (value) => {
|
|
3652
|
+
const quoted = /^"((?:[^"\\]|\\["\\])*)"$/.exec(value);
|
|
3653
|
+
if (quoted)
|
|
3654
|
+
return quoted[1].replace(/\\(["\\])/g, "$1");
|
|
3655
|
+
if (!value || /^["'#&*!|>%@`[{-]/.test(value) || /:\s|\s#/.test(value) || value !== value.trim())
|
|
3656
|
+
fail(t("frontmatter.scalar", { value }));
|
|
3657
|
+
return value;
|
|
3658
|
+
};
|
|
3659
|
+
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, "\\$&") + '"';
|
|
3660
|
+
function parseFrontmatter(lines, kind, legacyIds) {
|
|
3661
|
+
const legacy = new RegExp(`^<!-- ${marker}-(?:${kind === "wiki" ? "product|guide" : kind}): (${legacyIds}-${token}) -->$`).exec(lines[0] ?? "");
|
|
3662
|
+
if (legacy) {
|
|
3663
|
+
lines.shift();
|
|
3664
|
+
return { id: legacy[1] };
|
|
3665
|
+
}
|
|
3666
|
+
if (lines[0] !== "---")
|
|
3667
|
+
fail(t("frontmatter.required"));
|
|
3668
|
+
lines.shift();
|
|
3669
|
+
const end = lines.indexOf("---");
|
|
3670
|
+
if (end === -1)
|
|
3671
|
+
fail(t("frontmatter.unclosed"));
|
|
3672
|
+
const block = lines.splice(0, end);
|
|
3673
|
+
lines.shift();
|
|
3674
|
+
const fields2 = /* @__PURE__ */ new Map();
|
|
3675
|
+
for (let i = 0; i < block.length; i++) {
|
|
3676
|
+
const line = block[i];
|
|
3677
|
+
if (!line.trim())
|
|
3678
|
+
continue;
|
|
3679
|
+
const entry2 = /^([a-z]+):(?: (.*))?$/.exec(line);
|
|
3680
|
+
if (!entry2 || fields2.has(entry2[1]))
|
|
3681
|
+
fail(t("frontmatter.line", { line }));
|
|
3682
|
+
const [, key, value] = entry2;
|
|
3683
|
+
if (key !== "sources") {
|
|
3684
|
+
if (value === void 0)
|
|
3685
|
+
fail(t("frontmatter.line", { line }));
|
|
3686
|
+
fields2.set(key, scalar(value));
|
|
3687
|
+
continue;
|
|
3688
|
+
}
|
|
3689
|
+
if (value !== void 0)
|
|
3690
|
+
fail(t("frontmatter.line", { line }));
|
|
3691
|
+
const sources2 = [];
|
|
3692
|
+
while (i + 1 < block.length && /^ /.test(block[i + 1])) {
|
|
3693
|
+
const item = block[++i];
|
|
3694
|
+
const head = /^ - ([a-z]+): (.*)$/.exec(item);
|
|
3695
|
+
if (!head)
|
|
3696
|
+
fail(t("frontmatter.line", { line: item }));
|
|
3697
|
+
const map2 = { [head[1]]: scalar(head[2]) };
|
|
3698
|
+
while (i + 1 < block.length && /^ [a-z]+: /.test(block[i + 1])) {
|
|
3699
|
+
const more = /^ ([a-z]+): (.*)$/.exec(block[++i]);
|
|
3700
|
+
if (more[1] in map2)
|
|
3701
|
+
fail(t("frontmatter.line", { line: block[i] }));
|
|
3702
|
+
map2[more[1]] = scalar(more[2]);
|
|
3703
|
+
}
|
|
3704
|
+
sources2.push(validateSource(map2));
|
|
3705
|
+
}
|
|
3706
|
+
fields2.set("sources", sources2);
|
|
3707
|
+
}
|
|
3708
|
+
const id = fields2.get("id");
|
|
3709
|
+
if (typeof id !== "string" || !new RegExp(`^(?:${legacyIds})-${token}$`).test(id))
|
|
3710
|
+
fail(t("frontmatter.id", { kind }));
|
|
3711
|
+
for (const key of fields2.keys())
|
|
3712
|
+
if (key !== "id" && !(key === "sources" && kind === "design"))
|
|
3713
|
+
fail(t("frontmatter.unknownKey", { key }));
|
|
3714
|
+
const sources = fields2.get("sources");
|
|
3715
|
+
return { id, ...Array.isArray(sources) ? { sources } : {} };
|
|
3716
|
+
}
|
|
3717
|
+
function validateSource(value) {
|
|
3718
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
3719
|
+
fail(t("source.invalid"));
|
|
3720
|
+
const map2 = value;
|
|
3721
|
+
const keys2 = Object.keys(map2).sort();
|
|
3722
|
+
if (keys2.some((k) => !["title", "path", "url", "note"].includes(k)) || typeof map2.title !== "string" || !map2.title.trim() || "path" in map2 === "url" in map2)
|
|
3723
|
+
fail(t("source.invalid"));
|
|
3724
|
+
for (const key of keys2) {
|
|
3725
|
+
const v = map2[key];
|
|
3726
|
+
if (typeof v !== "string" || !v.trim() || /[\r\n\0]/.test(v) || v.length > 500)
|
|
3727
|
+
fail(t("source.invalid"));
|
|
3728
|
+
}
|
|
3729
|
+
if (typeof map2.url === "string" && !/^https?:\/\/\S+$/.test(map2.url))
|
|
3730
|
+
fail(t("source.url", { url: map2.url }));
|
|
3731
|
+
if (typeof map2.path === "string" && (/^[a-z]+:/i.test(map2.path) || map2.path.startsWith("/") || !map2.path.endsWith(".md")))
|
|
3732
|
+
fail(t("source.path", { path: map2.path }));
|
|
3733
|
+
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() } : {} };
|
|
3734
|
+
}
|
|
3735
|
+
function renderFrontmatter(front) {
|
|
3736
|
+
const lines = ["---", "id: " + front.id];
|
|
3737
|
+
if (front.sources?.length) {
|
|
3738
|
+
lines.push("sources:");
|
|
3739
|
+
for (const s of front.sources) {
|
|
3740
|
+
const entries = ["title", "path", "url", "note"].filter((k) => s[k] !== void 0).map((k) => k + ": " + quote(s[k]));
|
|
3741
|
+
lines.push(" - " + entries[0], ...entries.slice(1).map((e) => " " + e));
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
lines.push("---");
|
|
3745
|
+
return lines.join("\n") + "\n";
|
|
3746
|
+
}
|
|
3637
3747
|
function renderDesignPreview(id, design) {
|
|
3638
|
-
return
|
|
3639
|
-
|
|
3748
|
+
return renderFrontmatter({ id, ...design.sources?.length ? { sources: design.sources } : {} }) + `
|
|
3640
3749
|
# ${design.title}
|
|
3641
3750
|
|
|
3642
3751
|
${design.body}
|
|
3643
3752
|
`;
|
|
3644
3753
|
}
|
|
3645
|
-
function parseDesignPreview(
|
|
3646
|
-
if (
|
|
3754
|
+
function parseDesignPreview(source2, specId) {
|
|
3755
|
+
if (source2.includes("\0") || /\r(?!\n)/.test(source2))
|
|
3647
3756
|
fail(t("design.invalidCharacters"));
|
|
3648
|
-
const lines = normalized(
|
|
3649
|
-
|
|
3757
|
+
const lines = normalized(source2).split("\n");
|
|
3758
|
+
const front = parseFrontmatter(lines, "design", "S");
|
|
3759
|
+
if (front.id !== specId)
|
|
3650
3760
|
fail(t("design.idMismatch"));
|
|
3651
3761
|
while (lines[0] === "")
|
|
3652
3762
|
lines.shift();
|
|
@@ -3684,32 +3794,24 @@ function parseDesignPreview(source, specId) {
|
|
|
3684
3794
|
}).join("\n");
|
|
3685
3795
|
if (fence)
|
|
3686
3796
|
fail(t("design.unclosedFence"));
|
|
3687
|
-
return { title, body, requirements: [...references] };
|
|
3797
|
+
return { title, body, requirements: [...references], sources: front.sources ?? [] };
|
|
3688
3798
|
}
|
|
3689
3799
|
function renderDocument(doc) {
|
|
3690
|
-
return
|
|
3691
|
-
|
|
3800
|
+
return renderFrontmatter({ id: doc.id }) + `
|
|
3692
3801
|
# ${doc.title}
|
|
3693
3802
|
|
|
3694
3803
|
${doc.body}
|
|
3695
3804
|
`;
|
|
3696
3805
|
}
|
|
3697
|
-
function parseDocument(path3,
|
|
3806
|
+
function parseDocument(path3, source2) {
|
|
3698
3807
|
const match = documentPathPattern.exec(path3);
|
|
3699
3808
|
if (!match)
|
|
3700
3809
|
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))
|
|
3810
|
+
validateDocumentRelativePath(match[1]);
|
|
3811
|
+
if (source2.includes("\0") || /\r(?!\n)/.test(source2))
|
|
3708
3812
|
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 }));
|
|
3813
|
+
const lines = normalized(source2).split("\n");
|
|
3814
|
+
const { id } = parseFrontmatter(lines, "wiki", "W");
|
|
3713
3815
|
while (lines[0] === "")
|
|
3714
3816
|
lines.shift();
|
|
3715
3817
|
const heading = lines.shift();
|
|
@@ -3738,58 +3840,49 @@ function parseDocument(path3, source) {
|
|
|
3738
3840
|
}
|
|
3739
3841
|
if (fence)
|
|
3740
3842
|
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) {
|
|
3843
|
+
return { id, path: path3, title, body };
|
|
3844
|
+
}
|
|
3845
|
+
function parseWikiFiles(files) {
|
|
3846
|
+
const wiki = { documents: [], history: parsePreviewHistory(files.get(WIKI_HISTORY_PATH) ?? "", "documents") };
|
|
3847
|
+
for (const [path3, source2] of files)
|
|
3848
|
+
if (documentPathPattern.test(path3))
|
|
3849
|
+
wiki.documents.push(parseDocument(path3, source2));
|
|
3850
|
+
wiki.documents.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
3851
|
+
validateWiki(wiki);
|
|
3852
|
+
return wiki;
|
|
3853
|
+
}
|
|
3854
|
+
function validateWiki(wiki) {
|
|
3757
3855
|
const ids = /* @__PURE__ */ new Set();
|
|
3758
3856
|
const paths2 = /* @__PURE__ */ new Set();
|
|
3759
3857
|
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
|
-
}
|
|
3858
|
+
for (const d of wiki.documents) {
|
|
3859
|
+
if (ids.has(d.id))
|
|
3860
|
+
fail(t("document.duplicateId", { id: d.id }));
|
|
3861
|
+
ids.add(d.id);
|
|
3862
|
+
const lower = d.path.toLowerCase();
|
|
3863
|
+
if (paths2.has(lower))
|
|
3864
|
+
fail(t("document.caseOnlyPath", { path: d.path }));
|
|
3865
|
+
paths2.add(lower);
|
|
3866
|
+
}
|
|
3867
|
+
for (const h of wiki.history) {
|
|
3868
|
+
if (historyIds.has(h.id))
|
|
3869
|
+
fail(t("history.duplicateId", { id: h.id }));
|
|
3870
|
+
historyIds.add(h.id);
|
|
3779
3871
|
}
|
|
3780
3872
|
}
|
|
3781
|
-
var
|
|
3782
|
-
var
|
|
3873
|
+
var emptyWiki = () => ({ documents: [], history: [] });
|
|
3874
|
+
var emptyBundle = () => ({ specs: [], wiki: emptyWiki() });
|
|
3875
|
+
var asBundle = (value) => Array.isArray(value) ? { specs: value, wiki: emptyWiki() } : value;
|
|
3783
3876
|
function parsePreviewBundle(files) {
|
|
3784
|
-
const bundle = { specs: parsePreviewFiles(files),
|
|
3877
|
+
const bundle = { specs: parsePreviewFiles(files), wiki: parseWikiFiles(files) };
|
|
3785
3878
|
validateBundle(bundle);
|
|
3786
3879
|
return bundle;
|
|
3787
3880
|
}
|
|
3788
3881
|
function validateBundle(bundle) {
|
|
3789
3882
|
validatePreviewSnapshot(bundle.specs);
|
|
3790
|
-
|
|
3883
|
+
validateWiki(bundle.wiki);
|
|
3791
3884
|
const ids = /* @__PURE__ */ new Set();
|
|
3792
|
-
for (const h of [...bundle.specs.flatMap((s) => s.history), ...bundle.
|
|
3885
|
+
for (const h of [...bundle.specs.flatMap((s) => s.history), ...bundle.wiki.history]) {
|
|
3793
3886
|
if (ids.has(h.id))
|
|
3794
3887
|
fail(t("history.duplicateId", { id: h.id }));
|
|
3795
3888
|
ids.add(h.id);
|
|
@@ -3799,28 +3892,92 @@ function designReferenceWarnings(specs) {
|
|
|
3799
3892
|
const known = new Set(specs.flatMap((s) => s.requirements.map((r) => r.id)));
|
|
3800
3893
|
return specs.flatMap((s) => (s.design?.requirements ?? []).filter((id) => !known.has(id)).map((id) => ({ specId: s.id, requirement: id, code: "MISSING_DESIGN_REFERENCE" })));
|
|
3801
3894
|
}
|
|
3895
|
+
function resolveLink(from, link3) {
|
|
3896
|
+
const bare = link3.replace(/[?#].*$/, "");
|
|
3897
|
+
if (!bare || /^[a-z][a-z0-9+.-]*:/i.test(bare) || bare.startsWith("/") || bare.startsWith("//"))
|
|
3898
|
+
return null;
|
|
3899
|
+
const parts = from.split("/").slice(0, -1);
|
|
3900
|
+
for (const part of decodeSafe(bare).split("/")) {
|
|
3901
|
+
if (part === "" || part === ".")
|
|
3902
|
+
continue;
|
|
3903
|
+
if (part === "..") {
|
|
3904
|
+
if (!parts.length)
|
|
3905
|
+
return null;
|
|
3906
|
+
parts.pop();
|
|
3907
|
+
continue;
|
|
3908
|
+
}
|
|
3909
|
+
if (/[\\:\0]/.test(part))
|
|
3910
|
+
return null;
|
|
3911
|
+
parts.push(part);
|
|
3912
|
+
}
|
|
3913
|
+
return parts.join("/");
|
|
3914
|
+
}
|
|
3915
|
+
var decodeSafe = (value) => {
|
|
3916
|
+
try {
|
|
3917
|
+
return decodeURIComponent(value);
|
|
3918
|
+
} catch {
|
|
3919
|
+
return value;
|
|
3920
|
+
}
|
|
3921
|
+
};
|
|
3922
|
+
function extractLinks(body) {
|
|
3923
|
+
const links = [];
|
|
3924
|
+
let fence;
|
|
3925
|
+
for (const line of body.split("\n")) {
|
|
3926
|
+
if (fence) {
|
|
3927
|
+
if (new RegExp(`^ {0,3}${fence.char}{${fence.size},}\\s*$`).test(line))
|
|
3928
|
+
fence = void 0;
|
|
3929
|
+
continue;
|
|
3930
|
+
}
|
|
3931
|
+
const open6 = /^ {0,3}(`{3,}|~{3,})/.exec(line);
|
|
3932
|
+
if (open6) {
|
|
3933
|
+
fence = { char: open6[1][0], size: open6[1].length };
|
|
3934
|
+
continue;
|
|
3935
|
+
}
|
|
3936
|
+
for (const m of line.replace(/`[^`]*`/g, "").matchAll(/!?\[[^\]]*\]\((?:<([^>]+)>|([^)\s]+))(?:\s+"[^"]*")?\)/g))
|
|
3937
|
+
links.push(m[1] ?? m[2]);
|
|
3938
|
+
}
|
|
3939
|
+
return links;
|
|
3940
|
+
}
|
|
3941
|
+
function documentLinks(bundle) {
|
|
3942
|
+
const out = [];
|
|
3943
|
+
const collect = (from, links) => {
|
|
3944
|
+
for (const link3 of links) {
|
|
3945
|
+
const target = resolveLink(from, link3);
|
|
3946
|
+
if (target !== null)
|
|
3947
|
+
out.push({ from, link: link3, target });
|
|
3948
|
+
}
|
|
3949
|
+
};
|
|
3950
|
+
for (const s of bundle.specs) {
|
|
3951
|
+
collect(s.path, extractLinks(s.description + "\n" + s.requirements.map((r) => r.body).join("\n")));
|
|
3952
|
+
if (s.design) {
|
|
3953
|
+
const path3 = s.path.replace(/requirements\.md$/, "design.md");
|
|
3954
|
+
collect(path3, [...extractLinks(s.design.body), ...s.design.sources.flatMap((x) => x.path ? [x.path] : [])]);
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
for (const d of bundle.wiki.documents)
|
|
3958
|
+
collect(d.path, extractLinks(d.body));
|
|
3959
|
+
return out;
|
|
3960
|
+
}
|
|
3802
3961
|
function parsePreviewFiles(files) {
|
|
3803
3962
|
const specs = [];
|
|
3804
|
-
for (const [path3,
|
|
3963
|
+
for (const [path3, source2] of files) {
|
|
3805
3964
|
if (documentPathPattern.test(path3) || documentHistoryPattern.test(path3))
|
|
3806
3965
|
continue;
|
|
3807
3966
|
if (path3.endsWith("/requirements.md")) {
|
|
3808
|
-
specs.push(parseSpecPreview(path3,
|
|
3967
|
+
specs.push(parseSpecPreview(path3, source2, files.get(path3.replace(/requirements\.md$/, "history.jsonl")) ?? "", files.get(path3.replace(/requirements\.md$/, "design.md"))));
|
|
3809
3968
|
} else if (!files.has(path3.replace(/(?:history\.jsonl|design\.md)$/, "requirements.md")))
|
|
3810
3969
|
fail(t("spec.orphanFile", { path: path3 }));
|
|
3811
3970
|
}
|
|
3812
3971
|
validatePreviewSnapshot(specs);
|
|
3813
3972
|
return specs;
|
|
3814
3973
|
}
|
|
3815
|
-
function parseSpecPreview(path3,
|
|
3974
|
+
function parseSpecPreview(path3, source2, history = "", designSource) {
|
|
3816
3975
|
if (!/^\.(?:gitifact|tryce)\/spec\/[^/]+\/requirements\.md$/.test(path3))
|
|
3817
3976
|
fail(t("spec.unsupportedPath", { path: path3 }));
|
|
3818
|
-
if (
|
|
3977
|
+
if (source2.includes("\0") || source2.includes("\r") && /\r(?!\n)/.test(source2))
|
|
3819
3978
|
fail(t("spec.invalidCharacters", { path: path3 }));
|
|
3820
|
-
const lines = normalized(
|
|
3821
|
-
const
|
|
3822
|
-
if (!specMatch)
|
|
3823
|
-
fail(t("spec.idRequired", { path: path3 }));
|
|
3979
|
+
const lines = normalized(source2).split("\n");
|
|
3980
|
+
const specId = parseFrontmatter(lines, "spec", "S").id;
|
|
3824
3981
|
let title = "";
|
|
3825
3982
|
let description = "";
|
|
3826
3983
|
let current;
|
|
@@ -3833,7 +3990,7 @@ function parseSpecPreview(path3, source, history = "", designSource) {
|
|
|
3833
3990
|
else
|
|
3834
3991
|
description += line + "\n";
|
|
3835
3992
|
};
|
|
3836
|
-
for (let i =
|
|
3993
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3837
3994
|
const line = lines[i];
|
|
3838
3995
|
if (waitingId) {
|
|
3839
3996
|
const match = new RegExp(`^<!-- ${marker}-req: (R-${token}) -->$`).exec(line);
|
|
@@ -3890,13 +4047,13 @@ function parseSpecPreview(path3, source, history = "", designSource) {
|
|
|
3890
4047
|
fail(t("spec.emptyOrDuplicateRequirement", { id: r.id }));
|
|
3891
4048
|
ids.add(r.id);
|
|
3892
4049
|
}
|
|
3893
|
-
return { id:
|
|
4050
|
+
return { id: specId, path: path3, title, description: normalized(description), requirements, history: parsePreviewHistory(history), ...designSource === void 0 ? {} : { design: parseDesignPreview(designSource, specId) } };
|
|
3894
4051
|
}
|
|
3895
|
-
function parsePreviewHistory(
|
|
4052
|
+
function parsePreviewHistory(source2, scope = "spec") {
|
|
3896
4053
|
const records = [];
|
|
3897
4054
|
const ids = /* @__PURE__ */ new Set();
|
|
3898
4055
|
const shapes = scope === "spec" ? ["id,reason,requirements", "designs,id,reason,requirements"] : ["documents,id,reason,requirements"];
|
|
3899
|
-
for (const line of
|
|
4056
|
+
for (const line of source2.replace(/\r\n/g, "\n").split("\n")) {
|
|
3900
4057
|
if (!line.trim())
|
|
3901
4058
|
continue;
|
|
3902
4059
|
let value;
|
|
@@ -3908,7 +4065,7 @@ function parsePreviewHistory(source, scope = "spec") {
|
|
|
3908
4065
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
3909
4066
|
fail(t("history.invalidRecord"));
|
|
3910
4067
|
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" || !/^
|
|
4068
|
+
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
4069
|
fail(t("history.invalidFields"));
|
|
3913
4070
|
ids.add(r.id);
|
|
3914
4071
|
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 +4092,7 @@ function validatePreviewSnapshot(specs) {
|
|
|
3935
4092
|
}
|
|
3936
4093
|
}
|
|
3937
4094
|
}
|
|
4095
|
+
var sameEntry = (a, b) => a.title === b.title && a.body === b.body && JSON.stringify(a.sources ?? []) === JSON.stringify(b.sources ?? []);
|
|
3938
4096
|
function compareSpecPreviews(before, after) {
|
|
3939
4097
|
validatePreviewSnapshot(before);
|
|
3940
4098
|
validatePreviewSnapshot(after);
|
|
@@ -3944,7 +4102,7 @@ function compareSpecPreviews(before, after) {
|
|
|
3944
4102
|
for (const [specs, map2] of [[before, prev], [after, next]])
|
|
3945
4103
|
for (const s of specs)
|
|
3946
4104
|
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") });
|
|
4105
|
+
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
4106
|
const previousHistory = new Map(before.flatMap((s) => s.history.map((h) => [h.id, h])));
|
|
3949
4107
|
const reasons = after.flatMap((s) => s.history).filter((h) => {
|
|
3950
4108
|
const old = previousHistory.get(h.id);
|
|
@@ -3963,7 +4121,7 @@ function compareSpecPreviews(before, after) {
|
|
|
3963
4121
|
else {
|
|
3964
4122
|
if (from.specId !== to.specId || id.startsWith("S-") && storeRelative(from.path) !== storeRelative(to.path))
|
|
3965
4123
|
types.push("moved");
|
|
3966
|
-
if (from
|
|
4124
|
+
if (!sameEntry(from, to))
|
|
3967
4125
|
types.push("modified");
|
|
3968
4126
|
}
|
|
3969
4127
|
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 +4143,14 @@ function compareSpecPreviews(before, after) {
|
|
|
3985
4143
|
});
|
|
3986
4144
|
return { specChanges, changes };
|
|
3987
4145
|
}
|
|
3988
|
-
function
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
const index = (
|
|
4146
|
+
function compareWikis(before, after) {
|
|
4147
|
+
validateWiki(before);
|
|
4148
|
+
validateWiki(after);
|
|
4149
|
+
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
4150
|
const prev = index(before);
|
|
3993
4151
|
const next = index(after);
|
|
3994
|
-
const previousHistory = new Map(before.
|
|
3995
|
-
const reasons = after.
|
|
4152
|
+
const previousHistory = new Map(before.history.map((h) => [h.id, h]));
|
|
4153
|
+
const reasons = after.history.filter((h) => {
|
|
3996
4154
|
const old = previousHistory.get(h.id);
|
|
3997
4155
|
if (old && JSON.stringify(old) !== JSON.stringify(h))
|
|
3998
4156
|
fail(t("history.changed", { id: h.id }));
|
|
@@ -4012,8 +4170,7 @@ function compareDocumentSets(before, after) {
|
|
|
4012
4170
|
if (from.title !== to.title || from.body !== to.body)
|
|
4013
4171
|
types.push("modified");
|
|
4014
4172
|
}
|
|
4015
|
-
|
|
4016
|
-
return types.length ? [{ id, types, before: from, after: to, kind, reasons: reasons.filter((h) => (h.documents ?? []).includes(id)) }] : [];
|
|
4173
|
+
return types.length ? [{ id, types, before: from, after: to, kind: "wiki", reasons: reasons.filter((h) => (h.documents ?? []).includes(id)) }] : [];
|
|
4017
4174
|
});
|
|
4018
4175
|
const changed = new Set(changes.map((c) => c.id));
|
|
4019
4176
|
for (const h of reasons)
|
|
@@ -4027,7 +4184,7 @@ function comparePreviewBundles(before, after) {
|
|
|
4027
4184
|
validateBundle(a);
|
|
4028
4185
|
validateBundle(b);
|
|
4029
4186
|
const specs = compareSpecPreviews(a.specs, b.specs);
|
|
4030
|
-
return { specChanges: specs.specChanges, changes: [...specs.changes, ...
|
|
4187
|
+
return { specChanges: specs.specChanges, changes: [...specs.changes, ...compareWikis(a.wiki, b.wiki)] };
|
|
4031
4188
|
}
|
|
4032
4189
|
|
|
4033
4190
|
// ../../packages/core/dist/formats/changelog.js
|
|
@@ -4106,8 +4263,7 @@ function previewPath(feature2) {
|
|
|
4106
4263
|
return `.gitifact/spec/${feature2}/requirements.md`;
|
|
4107
4264
|
}
|
|
4108
4265
|
function renderSpecPreview(spec2) {
|
|
4109
|
-
return
|
|
4110
|
-
|
|
4266
|
+
return renderFrontmatter({ id: spec2.id }) + `
|
|
4111
4267
|
# ${spec2.title}
|
|
4112
4268
|
|
|
4113
4269
|
` + (spec2.description ? spec2.description + "\n\n" : "") + spec2.requirements.map((r) => `## ${r.title}
|
|
@@ -4119,11 +4275,11 @@ ${r.body}
|
|
|
4119
4275
|
function editSpecPreview(original, input2, generate) {
|
|
4120
4276
|
if (!Array.isArray(input2) || !input2.length || input2.length > 100)
|
|
4121
4277
|
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)]), ...
|
|
4278
|
+
const source2 = asBundle(original);
|
|
4279
|
+
const specs = source2.specs.map((s) => ({ ...s, requirements: s.requirements.map((r) => ({ ...r })), history: s.history.map((h) => ({ ...h, requirements: [...h.requirements] })) }));
|
|
4280
|
+
const wiki = { documents: source2.wiki.documents.map((d) => ({ ...d })), history: source2.wiki.history.map((h) => ({ ...h })) };
|
|
4281
|
+
validateBundle({ specs, wiki });
|
|
4282
|
+
const used = /* @__PURE__ */ new Set([...specs.flatMap((s) => [s.id, ...s.requirements.map((r) => r.id)]), ...wiki.documents.map((x) => x.id)]);
|
|
4127
4283
|
const allocate = (prefix) => {
|
|
4128
4284
|
for (let attempt = 0; attempt < 100; attempt++) {
|
|
4129
4285
|
const id = generate(prefix);
|
|
@@ -4147,14 +4303,12 @@ function editSpecPreview(original, input2, generate) {
|
|
|
4147
4303
|
update: ["type", "id", "title", "body"],
|
|
4148
4304
|
move: ["type", "id", "feature"],
|
|
4149
4305
|
"rename-spec": ["type", "id", "title"],
|
|
4150
|
-
"set-design": ["type", "feature", "title", "body"],
|
|
4306
|
+
"set-design": ["type", "feature", "title", "body", "sources"],
|
|
4151
4307
|
"delete-design": ["type", "feature"],
|
|
4152
4308
|
"create-doc": ["type", "path", "title", "body"],
|
|
4153
4309
|
"update-doc": ["type", "id", "title", "body"],
|
|
4154
4310
|
"move-doc": ["type", "id", "path"],
|
|
4155
|
-
"delete-doc": ["type", "id"]
|
|
4156
|
-
"set-product": ["type", "title", "body"],
|
|
4157
|
-
"delete-product": ["type"]
|
|
4311
|
+
"delete-doc": ["type", "id"]
|
|
4158
4312
|
};
|
|
4159
4313
|
const type = String(op.type);
|
|
4160
4314
|
const fields2 = schemas[type];
|
|
@@ -4174,41 +4328,24 @@ function editSpecPreview(original, input2, generate) {
|
|
|
4174
4328
|
const path3 = previewPath(str("feature"));
|
|
4175
4329
|
return specs.find((s) => s.path === path3) ?? fail2(t("edit.specNotFound", { path: path3 }));
|
|
4176
4330
|
};
|
|
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
4331
|
if (type.endsWith("-doc")) {
|
|
4193
|
-
const
|
|
4194
|
-
const documentPath = (kind, self) => {
|
|
4332
|
+
const documentPath = (self) => {
|
|
4195
4333
|
const relative = str("path");
|
|
4196
4334
|
validateDocumentRelativePath(relative);
|
|
4197
|
-
const path3 = `${
|
|
4335
|
+
const path3 = `${WIKI_DIR}/${relative}`;
|
|
4198
4336
|
if (self && self.path === path3)
|
|
4199
4337
|
fail2(t("edit.samePath"));
|
|
4200
|
-
if (
|
|
4338
|
+
if (wiki.documents.some((d) => d !== self && d.path.toLowerCase() === path3.toLowerCase()))
|
|
4201
4339
|
fail2(t("edit.documentPathExists", { path: path3 }));
|
|
4202
4340
|
return path3;
|
|
4203
4341
|
};
|
|
4204
4342
|
const find = () => {
|
|
4205
4343
|
const id = str("id");
|
|
4206
|
-
return
|
|
4344
|
+
return wiki.documents.find((d) => d.id === id) ?? fail2(t("edit.documentIdNotFound", { id }));
|
|
4207
4345
|
};
|
|
4208
4346
|
if (type === "create-doc") {
|
|
4209
|
-
const
|
|
4210
|
-
|
|
4211
|
-
documents.find((s) => s.kind === kind).documents.push(doc);
|
|
4347
|
+
const doc = { id: allocate("W"), path: documentPath(), title: str("title"), body: str("body") };
|
|
4348
|
+
wiki.documents.push(doc);
|
|
4212
4349
|
results.push({ type, id: doc.id });
|
|
4213
4350
|
} else if (type === "update-doc") {
|
|
4214
4351
|
const doc = find();
|
|
@@ -4217,12 +4354,11 @@ function editSpecPreview(original, input2, generate) {
|
|
|
4217
4354
|
results.push({ type, id: doc.id });
|
|
4218
4355
|
} else if (type === "move-doc") {
|
|
4219
4356
|
const doc = find();
|
|
4220
|
-
doc.path = documentPath(doc
|
|
4357
|
+
doc.path = documentPath(doc);
|
|
4221
4358
|
results.push({ type, id: doc.id });
|
|
4222
4359
|
} else {
|
|
4223
4360
|
const doc = find();
|
|
4224
|
-
|
|
4225
|
-
set2.documents = set2.documents.filter((d) => d.id !== doc.id);
|
|
4361
|
+
wiki.documents = wiki.documents.filter((d) => d.id !== doc.id);
|
|
4226
4362
|
results.push({ type, id: doc.id });
|
|
4227
4363
|
}
|
|
4228
4364
|
continue;
|
|
@@ -4245,8 +4381,16 @@ function editSpecPreview(original, input2, generate) {
|
|
|
4245
4381
|
if (!spec2.design)
|
|
4246
4382
|
fail2(t("edit.noDesignToDelete"));
|
|
4247
4383
|
delete spec2.design;
|
|
4248
|
-
} else
|
|
4249
|
-
|
|
4384
|
+
} else {
|
|
4385
|
+
let sources = [];
|
|
4386
|
+
const list = op.sources;
|
|
4387
|
+
if (list !== void 0) {
|
|
4388
|
+
if (!Array.isArray(list) || list.length > 50)
|
|
4389
|
+
return fail2(t("edit.invalidSources"));
|
|
4390
|
+
sources = list.map(validateSource);
|
|
4391
|
+
}
|
|
4392
|
+
spec2.design = parseDesignPreview(renderDesignPreview(spec2.id, { title: str("title"), body: str("body"), sources }), spec2.id);
|
|
4393
|
+
}
|
|
4250
4394
|
results.push({ type, id: spec2.id });
|
|
4251
4395
|
} else if (type === "rename-spec") {
|
|
4252
4396
|
const spec2 = specs.find((s) => s.id === str("id")) ?? fail2(t("edit.specIdNotFound"));
|
|
@@ -4254,13 +4398,13 @@ function editSpecPreview(original, input2, generate) {
|
|
|
4254
4398
|
results.push({ type, id: spec2.id });
|
|
4255
4399
|
} else {
|
|
4256
4400
|
const id = str("id");
|
|
4257
|
-
const
|
|
4258
|
-
const req =
|
|
4401
|
+
const source3 = specs.find((s) => s.requirements.some((r) => r.id === id)) ?? fail2(t("edit.requirementIdNotFound"));
|
|
4402
|
+
const req = source3.requirements.find((r) => r.id === id);
|
|
4259
4403
|
if (type === "move") {
|
|
4260
4404
|
const destination = target();
|
|
4261
|
-
if (
|
|
4405
|
+
if (source3 === destination)
|
|
4262
4406
|
fail2(t("edit.sameSpec"));
|
|
4263
|
-
|
|
4407
|
+
source3.requirements = source3.requirements.filter((r) => r.id !== id);
|
|
4264
4408
|
destination.requirements.push(req);
|
|
4265
4409
|
} else {
|
|
4266
4410
|
req.title = str("title");
|
|
@@ -4274,14 +4418,12 @@ function editSpecPreview(original, input2, generate) {
|
|
|
4274
4418
|
if (JSON.stringify({ ...parsed, history: spec2.history }) !== JSON.stringify(spec2))
|
|
4275
4419
|
fail2(t("edit.bodyChangesSpecStructure"));
|
|
4276
4420
|
}
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
}
|
|
4283
|
-
validateBundle({ specs, documents });
|
|
4284
|
-
return { specs, documents, results };
|
|
4421
|
+
wiki.documents.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
4422
|
+
for (const doc of wiki.documents)
|
|
4423
|
+
if (JSON.stringify(parseDocument(doc.path, renderDocument(doc))) !== JSON.stringify(doc))
|
|
4424
|
+
fail2(t("edit.bodyChangesDocumentStructure", { path: doc.path }));
|
|
4425
|
+
validateBundle({ specs, wiki });
|
|
4426
|
+
return { specs, wiki, results };
|
|
4285
4427
|
}
|
|
4286
4428
|
|
|
4287
4429
|
// ../../packages/core/dist/use-cases/prepare-spec-preview.js
|
|
@@ -4295,7 +4437,7 @@ function pendingPreviewReasons(before, current) {
|
|
|
4295
4437
|
const b = asBundle(current);
|
|
4296
4438
|
return [
|
|
4297
4439
|
...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.
|
|
4440
|
+
...b.wiki.history.slice(a.wiki.history.length).map((h) => ({ kind: "wiki", path: WIKI_HISTORY_PATH, ...h }))
|
|
4299
4441
|
];
|
|
4300
4442
|
}
|
|
4301
4443
|
function finalSpecPreviewChanges(beforeValue, currentValue) {
|
|
@@ -4310,14 +4452,11 @@ function finalSpecPreviewChanges(beforeValue, currentValue) {
|
|
|
4310
4452
|
if (next && !equal(next.history.slice(0, previous.history.length), previous.history))
|
|
4311
4453
|
fail3(t("reason.committedImmutable", { id: previous.id }));
|
|
4312
4454
|
}
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
if (!equal(next.history.slice(0, previous.history.length), previous.history))
|
|
4316
|
-
fail3(t("reason.committedDocumentImmutable", { kind: previous.kind }));
|
|
4317
|
-
}
|
|
4455
|
+
if (!equal(current.wiki.history.slice(0, before.wiki.history.length), before.wiki.history))
|
|
4456
|
+
fail3(t("reason.committedDocumentImmutable"));
|
|
4318
4457
|
const clean = {
|
|
4319
4458
|
specs: current.specs.map((s) => ({ ...s, history: before.specs.find((p) => p.id === s.id)?.history ?? [] })),
|
|
4320
|
-
documents: current.documents
|
|
4459
|
+
wiki: { documents: current.wiki.documents, history: before.wiki.history }
|
|
4321
4460
|
};
|
|
4322
4461
|
return comparePreviewBundles(before, clean);
|
|
4323
4462
|
}
|
|
@@ -4332,8 +4471,8 @@ function prepareSpecPreview(beforeValue, currentValue, baseFiles, currentFiles,
|
|
|
4332
4471
|
const writes = /* @__PURE__ */ new Map();
|
|
4333
4472
|
const pending = /* @__PURE__ */ new Map();
|
|
4334
4473
|
const covered = /* @__PURE__ */ new Set();
|
|
4335
|
-
const used = new Set([...before.flatMap((s) => s.history), ...current.flatMap((s) => s.history), ...beforeBundle.
|
|
4336
|
-
const
|
|
4474
|
+
const used = new Set([...before.flatMap((s) => s.history), ...current.flatMap((s) => s.history), ...beforeBundle.wiki.history, ...currentBundle.wiki.history].map((h) => h.id));
|
|
4475
|
+
const wikiPending = [];
|
|
4337
4476
|
const allocate = () => {
|
|
4338
4477
|
for (let attempt = 0; attempt < 100; attempt++) {
|
|
4339
4478
|
const id = generate();
|
|
@@ -4359,21 +4498,16 @@ function prepareSpecPreview(beforeValue, currentValue, baseFiles, currentFiles,
|
|
|
4359
4498
|
if (raw.requirements.length)
|
|
4360
4499
|
fail3(t("reason.documentWithRequirements"));
|
|
4361
4500
|
const ids = raw.documents;
|
|
4362
|
-
let kind;
|
|
4363
4501
|
for (const value2 of ids) {
|
|
4364
|
-
if (typeof value2 !== "string" || !/^
|
|
4502
|
+
if (typeof value2 !== "string" || !/^W-[a-z2-7]{10}$/.test(value2) || covered.has(value2))
|
|
4365
4503
|
return fail3(t("reason.documentTargetInvalid"));
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
fail3(t("reason.singleDocumentKind"));
|
|
4369
|
-
kind = change2.kind;
|
|
4504
|
+
if (!delta.changes.some((c) => c.id === value2))
|
|
4505
|
+
fail3(t("reason.documentUnchanged", { id: value2 }));
|
|
4370
4506
|
covered.add(value2);
|
|
4371
4507
|
}
|
|
4372
4508
|
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 }]);
|
|
4509
|
+
const reusable = currentBundle.wiki.history.slice(beforeBundle.wiki.history.length).find((h) => h.reason === reason && equal([...h.documents ?? []].sort(), documents));
|
|
4510
|
+
wikiPending.push({ id: reusable?.id ?? allocate(), requirements: [], documents, reason });
|
|
4377
4511
|
continue;
|
|
4378
4512
|
}
|
|
4379
4513
|
for (const value2 of [...raw.requirements, ...raw.designs ?? []]) {
|
|
@@ -4401,34 +4535,32 @@ function prepareSpecPreview(beforeValue, currentValue, baseFiles, currentFiles,
|
|
|
4401
4535
|
pending.set(specId, [...pending.get(specId) ?? [], record3]);
|
|
4402
4536
|
}
|
|
4403
4537
|
}
|
|
4538
|
+
const historyText = (base, records) => {
|
|
4539
|
+
let text2 = base ?? "";
|
|
4540
|
+
if (records.length)
|
|
4541
|
+
text2 += (text2 && !text2.endsWith("\n") ? "\n" : "") + records.map((h) => JSON.stringify(h) + "\n").join("");
|
|
4542
|
+
return base !== void 0 || records.length ? text2 : null;
|
|
4543
|
+
};
|
|
4404
4544
|
for (const spec2 of current) {
|
|
4405
4545
|
const old = before.find((s) => s.id === spec2.id);
|
|
4406
4546
|
const base = old ? baseFiles.get(historyPath(old)) : void 0;
|
|
4407
4547
|
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
4548
|
const path3 = historyPath(spec2);
|
|
4412
|
-
const next = base
|
|
4549
|
+
const next = historyText(base, records);
|
|
4413
4550
|
if ((currentFiles.get(path3) ?? null) !== next)
|
|
4414
4551
|
writes.set(path3, next);
|
|
4415
4552
|
}
|
|
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);
|
|
4553
|
+
{
|
|
4554
|
+
const records = wikiPending.sort((a, b) => a.documents[0] < b.documents[0] ? -1 : 1);
|
|
4555
|
+
const next = historyText(baseFiles.get(WIKI_HISTORY_PATH), records);
|
|
4556
|
+
if ((currentFiles.get(WIKI_HISTORY_PATH) ?? null) !== next)
|
|
4557
|
+
writes.set(WIKI_HISTORY_PATH, next);
|
|
4426
4558
|
}
|
|
4427
4559
|
return {
|
|
4428
4560
|
writes,
|
|
4429
4561
|
changes: delta.changes,
|
|
4430
4562
|
specChanges: delta.specChanges,
|
|
4431
|
-
reasons: [...[...pending].flatMap(([specId, records]) => records.map((record3) => ({ specId, ...record3 }))), ...
|
|
4563
|
+
reasons: [...[...pending].flatMap(([specId, records]) => records.map((record3) => ({ specId, ...record3 }))), ...wikiPending.map((record3) => ({ kind: "wiki", ...record3 }))],
|
|
4432
4564
|
withoutReason: delta.changes.filter((c) => !covered.has(c.id)).map((c) => c.id)
|
|
4433
4565
|
};
|
|
4434
4566
|
}
|
|
@@ -4629,7 +4761,7 @@ function createRepositoryReader(cwd, options = {}) {
|
|
|
4629
4761
|
return output2;
|
|
4630
4762
|
};
|
|
4631
4763
|
const discover = async () => {
|
|
4632
|
-
const
|
|
4764
|
+
const scalar2 = async (flag) => decodeGitLine(await invoke(["rev-parse", "--path-format=absolute", flag]));
|
|
4633
4765
|
const flags = ["--is-bare-repository", "--is-inside-git-dir", "--is-inside-work-tree"];
|
|
4634
4766
|
const paths2 = ["--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-object-format"];
|
|
4635
4767
|
let combined;
|
|
@@ -4646,7 +4778,7 @@ function createRepositoryReader(cwd, options = {}) {
|
|
|
4646
4778
|
}
|
|
4647
4779
|
if (bare === "true" || internal === "true" || inside !== "true") throw new RepositoryReadError("UNSUPPORTED_REPOSITORY");
|
|
4648
4780
|
let values = combined.slice(3);
|
|
4649
|
-
if (values.length !== paths2.length) values = await Promise.all(paths2.map(
|
|
4781
|
+
if (values.length !== paths2.length) values = await Promise.all(paths2.map(scalar2));
|
|
4650
4782
|
const [rootPath, gitDir, commonDir, objectFormat] = values;
|
|
4651
4783
|
if (objectFormat !== "sha1" && objectFormat !== "sha256") throw new RepositoryReadError("GIT_UNSUPPORTED");
|
|
4652
4784
|
return { rootPath, gitDir, commonDir, objectFormat };
|
|
@@ -5381,10 +5513,10 @@ function cached(getter) {
|
|
|
5381
5513
|
function nullish(input2) {
|
|
5382
5514
|
return input2 === null || input2 === void 0;
|
|
5383
5515
|
}
|
|
5384
|
-
function cleanRegex(
|
|
5385
|
-
const start =
|
|
5386
|
-
const end =
|
|
5387
|
-
return
|
|
5516
|
+
function cleanRegex(source2) {
|
|
5517
|
+
const start = source2.startsWith("^") ? 1 : 0;
|
|
5518
|
+
const end = source2.endsWith("$") ? source2.length - 1 : source2.length;
|
|
5519
|
+
return source2.slice(start, end);
|
|
5388
5520
|
}
|
|
5389
5521
|
function floatSafeRemainder(val, step) {
|
|
5390
5522
|
const ratio = val / step;
|
|
@@ -5452,28 +5584,28 @@ function putProp(target, key, value) {
|
|
|
5452
5584
|
else
|
|
5453
5585
|
target[key] = value;
|
|
5454
5586
|
}
|
|
5455
|
-
function mirrorShape(target,
|
|
5456
|
-
const raw = sourceShape(
|
|
5587
|
+
function mirrorShape(target, source2, keys2, wrap) {
|
|
5588
|
+
const raw = sourceShape(source2);
|
|
5457
5589
|
for (const key of keys2) {
|
|
5458
5590
|
const desc = Object.getOwnPropertyDescriptor(raw, key);
|
|
5459
5591
|
if (!desc.enumerable)
|
|
5460
5592
|
continue;
|
|
5461
5593
|
if (desc.get) {
|
|
5462
5594
|
deferProp(target, key, () => {
|
|
5463
|
-
const value =
|
|
5595
|
+
const value = source2._zod.def.shape[key];
|
|
5464
5596
|
return wrap ? wrap(value, key) : value;
|
|
5465
5597
|
});
|
|
5466
5598
|
} else
|
|
5467
5599
|
putProp(target, key, wrap ? wrap(desc.value, key) : desc.value);
|
|
5468
5600
|
}
|
|
5469
5601
|
}
|
|
5470
|
-
function mirrorProps(target,
|
|
5471
|
-
for (const key of Reflect.ownKeys(
|
|
5472
|
-
const desc = Object.getOwnPropertyDescriptor(
|
|
5602
|
+
function mirrorProps(target, source2) {
|
|
5603
|
+
for (const key of Reflect.ownKeys(source2)) {
|
|
5604
|
+
const desc = Object.getOwnPropertyDescriptor(source2, key);
|
|
5473
5605
|
if (!desc.enumerable)
|
|
5474
5606
|
continue;
|
|
5475
5607
|
if (desc.get)
|
|
5476
|
-
deferProp(target, key, () =>
|
|
5608
|
+
deferProp(target, key, () => source2[key]);
|
|
5477
5609
|
else
|
|
5478
5610
|
putProp(target, key, desc.value);
|
|
5479
5611
|
}
|
|
@@ -6686,8 +6818,8 @@ var creditCard = /^\d(?:[ -]?\d){11,18}$/;
|
|
|
6686
6818
|
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
6819
|
var iban = /^[A-Z]{2}(?!00|01|99)\d{2}[A-Z0-9]{11,30}$/;
|
|
6688
6820
|
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(`^${
|
|
6821
|
+
function anchor(source2) {
|
|
6822
|
+
return new RegExp(`^${source2}$`);
|
|
6691
6823
|
}
|
|
6692
6824
|
var date = /* @__PURE__ */ anchor(dateSource);
|
|
6693
6825
|
function timeSource(args) {
|
|
@@ -9510,11 +9642,11 @@ var $ZodTemplateLiteral = /* @__PURE__ */ $constructor("$ZodTemplateLiteral", (i
|
|
|
9510
9642
|
const regexParts = [];
|
|
9511
9643
|
for (const part of def.parts) {
|
|
9512
9644
|
if (typeof part === "object" && part !== null) {
|
|
9513
|
-
const
|
|
9514
|
-
if (!
|
|
9645
|
+
const source2 = partPattern(part);
|
|
9646
|
+
if (!source2) {
|
|
9515
9647
|
throw new Error(`Invalid template literal part, no pattern found: ${[...part._zod.traits].shift()}`);
|
|
9516
9648
|
}
|
|
9517
|
-
regexParts.push(cleanRegex(
|
|
9649
|
+
regexParts.push(cleanRegex(source2));
|
|
9518
9650
|
} else if (part === null || primitiveTypes.has(typeof part)) {
|
|
9519
9651
|
regexParts.push(escapeRegex(`${part}`));
|
|
9520
9652
|
} else {
|
|
@@ -17640,9 +17772,9 @@ function withParser(schema, parser) {
|
|
|
17640
17772
|
installCompiledUserMethods(clone2, schema, parser);
|
|
17641
17773
|
return clone2;
|
|
17642
17774
|
}
|
|
17643
|
-
function installCompiledUserMethods(target,
|
|
17775
|
+
function installCompiledUserMethods(target, source2, parser) {
|
|
17644
17776
|
const targetAny = target;
|
|
17645
|
-
const sourceAny =
|
|
17777
|
+
const sourceAny = source2;
|
|
17646
17778
|
if (typeof sourceAny.safeParse === "function") {
|
|
17647
17779
|
const originalSafeParse = sourceAny.safeParse;
|
|
17648
17780
|
targetAny.safeParse = (data, params) => {
|
|
@@ -20237,10 +20369,10 @@ function _stringFormat(Class2, format, fnOrRegex, _params = {}) {
|
|
|
20237
20369
|
|
|
20238
20370
|
// ../../node_modules/.pnpm/zod@4.6.4/node_modules/zod/v4/core/to-json-schema.js
|
|
20239
20371
|
function assignProps(target, ...sources) {
|
|
20240
|
-
for (const
|
|
20241
|
-
for (const key of Reflect.ownKeys(
|
|
20242
|
-
if (Object.prototype.propertyIsEnumerable.call(
|
|
20243
|
-
assignProp(target, key,
|
|
20372
|
+
for (const source2 of sources) {
|
|
20373
|
+
for (const key of Reflect.ownKeys(source2)) {
|
|
20374
|
+
if (Object.prototype.propertyIsEnumerable.call(source2, key)) {
|
|
20375
|
+
assignProp(target, key, source2[key]);
|
|
20244
20376
|
}
|
|
20245
20377
|
}
|
|
20246
20378
|
}
|
|
@@ -24463,7 +24595,7 @@ var projectInitV4 = external_exports.discriminatedUnion("ok", [
|
|
|
24463
24595
|
outcome: external_exports.enum(["planned", "created", "already-initialized"]),
|
|
24464
24596
|
rootPath: external_exports.string(),
|
|
24465
24597
|
configPath: external_exports.literal(".gitifact/config.json"),
|
|
24466
|
-
schemaVersion: external_exports.literal(
|
|
24598
|
+
schemaVersion: external_exports.literal(2),
|
|
24467
24599
|
baseline: external_exports.discriminatedUnion("kind", [
|
|
24468
24600
|
external_exports.strictObject({ kind: external_exports.literal("empty") }),
|
|
24469
24601
|
external_exports.strictObject({ kind: external_exports.literal("commit"), objectFormat: external_exports.enum(["sha1", "sha256"]), commit: external_exports.string() })
|
|
@@ -24473,25 +24605,26 @@ var projectInitV4 = external_exports.discriminatedUnion("ok", [
|
|
|
24473
24605
|
external_exports.strictObject({ contract: external_exports.literal("project-init"), version: external_exports.literal(4), ok: external_exports.literal(false), error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }) })
|
|
24474
24606
|
]);
|
|
24475
24607
|
|
|
24476
|
-
// ../../packages/contracts/dist/versions/browser-specs-
|
|
24608
|
+
// ../../packages/contracts/dist/versions/browser-specs-v2.js
|
|
24477
24609
|
var requirement = external_exports.strictObject({ id: external_exports.string(), title: external_exports.string(), body: external_exports.string() });
|
|
24478
24610
|
var contributor = external_exports.strictObject({ email: external_exports.string(), name: external_exports.string(), commits: external_exports.number().int().nonnegative(), latest: external_exports.string() });
|
|
24611
|
+
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
24612
|
var feature = external_exports.strictObject({
|
|
24480
24613
|
id: external_exports.string(),
|
|
24481
24614
|
path: external_exports.string(),
|
|
24482
24615
|
title: external_exports.string(),
|
|
24483
24616
|
description: external_exports.string(),
|
|
24484
24617
|
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(),
|
|
24618
|
+
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
24619
|
// Authors of commits touching the feature folder (current or legacy store) and the latest such commit; empty until first committed.
|
|
24487
24620
|
contributors: external_exports.array(contributor),
|
|
24488
24621
|
updatedAt: external_exports.string().nullable()
|
|
24489
24622
|
});
|
|
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
|
|
24623
|
+
var snapshot = requirement.extend({ specId: external_exports.string(), path: external_exports.string(), sources: external_exports.array(source).optional() }).nullable();
|
|
24624
|
+
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() });
|
|
24625
|
+
var browserSpecsV2 = external_exports.strictObject({
|
|
24493
24626
|
contract: external_exports.literal("browser-specs"),
|
|
24494
|
-
version: external_exports.literal(
|
|
24627
|
+
version: external_exports.literal(2),
|
|
24495
24628
|
sessionId: external_exports.string(),
|
|
24496
24629
|
head: external_exports.string().nullable(),
|
|
24497
24630
|
observedAt: external_exports.string(),
|
|
@@ -24507,7 +24640,7 @@ var browserSpecsV1 = external_exports.strictObject({
|
|
|
24507
24640
|
committer: external_exports.string(),
|
|
24508
24641
|
message: external_exports.string(),
|
|
24509
24642
|
id: external_exports.string(),
|
|
24510
|
-
kind: external_exports.enum(["requirement", "design", "
|
|
24643
|
+
kind: external_exports.enum(["requirement", "design", "wiki"]).optional(),
|
|
24511
24644
|
types: external_exports.array(external_exports.enum(["created", "modified", "moved", "deleted"])),
|
|
24512
24645
|
before: snapshot,
|
|
24513
24646
|
after: snapshot,
|
|
@@ -24563,7 +24696,7 @@ var browserSessionV2 = external_exports.strictObject({
|
|
|
24563
24696
|
update: updateStateV1
|
|
24564
24697
|
});
|
|
24565
24698
|
|
|
24566
|
-
// ../../packages/contracts/dist/versions/update-
|
|
24699
|
+
// ../../packages/contracts/dist/versions/update-v3.js
|
|
24567
24700
|
var commitReason = external_exports.enum(["untracked", "other-changes", "commit-failed"]);
|
|
24568
24701
|
var agentDocsCommit = external_exports.strictObject({
|
|
24569
24702
|
state: external_exports.enum(["not-requested", "nothing", "committed", "skipped"]),
|
|
@@ -24573,10 +24706,10 @@ var agentDocsCommit = external_exports.strictObject({
|
|
|
24573
24706
|
reason: commitReason.nullable(),
|
|
24574
24707
|
detail: external_exports.string().nullable()
|
|
24575
24708
|
}).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
|
|
24709
|
+
var updateV3 = external_exports.discriminatedUnion("ok", [
|
|
24577
24710
|
external_exports.strictObject({
|
|
24578
24711
|
contract: external_exports.literal("update"),
|
|
24579
|
-
version: external_exports.literal(
|
|
24712
|
+
version: external_exports.literal(3),
|
|
24580
24713
|
ok: external_exports.literal(true),
|
|
24581
24714
|
cliVersion: external_exports.string().min(1),
|
|
24582
24715
|
update: updateStateV1,
|
|
@@ -24584,10 +24717,11 @@ var updateV2 = external_exports.discriminatedUnion("ok", [
|
|
|
24584
24717
|
install: external_exports.strictObject({ npmGlobal: external_exports.string().min(1) }).nullable(),
|
|
24585
24718
|
// refreshed: blocks whose version line differed were rewritten. current: every block already matched.
|
|
24586
24719
|
// not-initialized: no .gitifact config here, so only the version check ran. no-block: no file carries a block.
|
|
24587
|
-
|
|
24720
|
+
// missing: files `gitifact init` would add and update does not create, e.g. CLAUDE.md when only AGENTS.md has the block.
|
|
24721
|
+
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
24722
|
commit: agentDocsCommit
|
|
24589
24723
|
}),
|
|
24590
|
-
external_exports.strictObject({ contract: external_exports.literal("update"), version: external_exports.literal(
|
|
24724
|
+
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
24725
|
]);
|
|
24592
24726
|
|
|
24593
24727
|
// src/shared/i18n/ko/messages.json
|
|
@@ -24605,7 +24739,7 @@ var messages_default2 = {
|
|
|
24605
24739
|
"help.initRemoveAgents": "GITIFACT \uBE14\uB85D\uC744 \uC81C\uAC70",
|
|
24606
24740
|
"help.initSkipAgents": "\uC9C0\uCE68 \uD30C\uC77C\uC744 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uC74C",
|
|
24607
24741
|
"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,
|
|
24742
|
+
"help.docsTopic": "workflow, spec, design, wiki, commit \uC911 \uD558\uB098",
|
|
24609
24743
|
"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
24744
|
"help.migrateDryRun": "\uD30C\uC77C\uC744 \uBC14\uAFB8\uC9C0 \uC54A\uACE0 \uC804\uD658 \uACC4\uD68D \uD655\uC778",
|
|
24611
24745
|
"help.spec": "Markdown \uBA85\uC138 \uC791\uC131\xB7\uC870\uD68C\xB7Git \uAE30\uB85D",
|
|
@@ -24622,11 +24756,11 @@ var messages_default2 = {
|
|
|
24622
24756
|
"help.specDiffFrom": "\uC774\uC804 \uCEE4\uBC0B",
|
|
24623
24757
|
"help.specDiffTo": "\uC774\uD6C4 \uCEE4\uBC0B",
|
|
24624
24758
|
"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\
|
|
24759
|
+
"help.specWorking": "\uD604\uC7AC \uBA85\uC138\xB7\uC704\uD0A4 \uC804\uCCB4\uC640 stamp, \uC785\uB825 \uD30C\uC77C \uACBD\uB85C \uC870\uD68C",
|
|
24626
24760
|
"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 (\
|
|
24761
|
+
"help.specWorkingFeature": "\uD55C \uAE30\uB2A5 \uD3F4\uB354\uC758 \uBA85\uC138\uB9CC \uCD9C\uB825 (\uC704\uD0A4 \uC81C\uC678)",
|
|
24628
24762
|
"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\
|
|
24763
|
+
"help.specSave": "\uC694\uAD6C\uC0AC\uD56D\xB7\uC124\uACC4\xB7\uC704\uD0A4 \uC800\uC7A5",
|
|
24630
24764
|
"help.specRead": "\uCEE4\uBC0B \uC2DC\uC810\uC758 \uBA85\uC138 \uC870\uD68C",
|
|
24631
24765
|
"help.specDiff": "\uB450 \uCEE4\uBC0B \uC0AC\uC774\uC758 \uBA85\uC138 \uBCC0\uACBD \uC870\uD68C",
|
|
24632
24766
|
"git.operationInProgress": "Git \uC791\uC5C5\uC774 \uC9C4\uD589 \uC911\uC785\uB2C8\uB2E4: {marker}",
|
|
@@ -24722,7 +24856,6 @@ var messages_default2 = {
|
|
|
24722
24856
|
"server.crossSite": "\uC678\uBD80 \uD398\uC774\uC9C0\uC758 \uC694\uCCAD\uC744 \uD5C8\uC6A9\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
24723
24857
|
"server.noQuery": "\uC870\uD68C \uC778\uC790\uB97C \uBC1B\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
24724
24858
|
"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
24859
|
"server.apiNotFound": "API\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
24727
24860
|
"server.sessionChanged": "\uC11C\uBC84 \uC138\uC158\uC774 \uBCC0\uACBD\uB410\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC5F0\uACB0\uD558\uC138\uC694.",
|
|
24728
24861
|
"server.badRange": "\uC870\uD68C \uBC94\uC704\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
@@ -24820,8 +24953,7 @@ var messages_default2 = {
|
|
|
24820
24953
|
"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
24954
|
"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
24955
|
"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",
|
|
24956
|
+
"docs.summary.design": "design.md \uBAA9\uCC28, \uCC38\uC870 \uC8FC\uC11D\uACFC \uCC38\uACE0 \uBB38\uC11C, \uAC1C\uC815 \uBC29\uC2DD",
|
|
24825
24957
|
"docs.summary.commit": "spec commit \uC785\uB825, \uC774\uC720 \uAE30\uB85D, \uC2E4\uD328 \uD6C4 \uBCF5\uAD6C",
|
|
24826
24958
|
"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
24959
|
"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 +24972,19 @@ var messages_default2 = {
|
|
|
24840
24972
|
"update.text.blockCurrent": "\uC9C0\uCE68 \uBE14\uB85D\uC740 \uC774\uBBF8 \uD604\uC7AC \uBC84\uC804\uC785\uB2C8\uB2E4: {paths}",
|
|
24841
24973
|
"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
24974
|
"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.",
|
|
24975
|
+
"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
24976
|
"update.text.committed": "\uC9C0\uCE68 \uBE14\uB85D \uCEE4\uBC0B: {commit} ({paths})",
|
|
24844
24977
|
"update.text.commitNothing": "\uCEE4\uBC0B\uD560 \uC9C0\uCE68 \uBE14\uB85D \uBCC0\uACBD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
24845
24978
|
"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
24979
|
"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
24980
|
"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
24981
|
"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."
|
|
24982
|
+
"update.failed": "\uC5C5\uB370\uC774\uD2B8 \uD655\uC778\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.",
|
|
24983
|
+
"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)",
|
|
24984
|
+
"server.assetNotFound": "\uC5D0\uC14B\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
24985
|
+
"docs.wikiPolicyProject": "\uC6B4\uC601 \uBC29\uCE68 (.gitifact/wiki/README.md)",
|
|
24986
|
+
"docs.wikiPolicyDefault": "\uC6B4\uC601 \uBC29\uCE68 (\uAE30\uBCF8\uAC12. .gitifact/wiki/README.md\uAC00 \uC5C6\uC5B4 \uB0B4\uC7A5 \uBC29\uCE68\uC744 \uC2E3\uB294\uB2E4)",
|
|
24987
|
+
"init.wikiReadmeTitle": "\uC704\uD0A4 \uC6B4\uC601 \uBC29\uCE68"
|
|
24850
24988
|
};
|
|
24851
24989
|
|
|
24852
24990
|
// src/shared/i18n/index.ts
|
|
@@ -24922,8 +25060,8 @@ async function runStatus(format) {
|
|
|
24922
25060
|
import { createServer } from "node:http";
|
|
24923
25061
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
24924
25062
|
import { fileURLToPath } from "node:url";
|
|
24925
|
-
import { lstat as
|
|
24926
|
-
import { join as
|
|
25063
|
+
import { lstat as lstat4, readFile as readFile4 } from "node:fs/promises";
|
|
25064
|
+
import { join as join5 } from "node:path";
|
|
24927
25065
|
|
|
24928
25066
|
// src/server/assets.ts
|
|
24929
25067
|
import { readdir, readFile } from "node:fs/promises";
|
|
@@ -24956,6 +25094,7 @@ function contentType(path3) {
|
|
|
24956
25094
|
if (/\.jpe?g$/.test(path3)) return "image/jpeg";
|
|
24957
25095
|
if (path3.endsWith(".gif")) return "image/gif";
|
|
24958
25096
|
if (path3.endsWith(".webp")) return "image/webp";
|
|
25097
|
+
if (path3.endsWith(".pdf")) return "application/pdf";
|
|
24959
25098
|
return "application/octet-stream";
|
|
24960
25099
|
}
|
|
24961
25100
|
|
|
@@ -25062,7 +25201,7 @@ function commandScoped(key, load) {
|
|
|
25062
25201
|
}
|
|
25063
25202
|
|
|
25064
25203
|
// src/adapters/git/spec-preview-reader.ts
|
|
25065
|
-
var RECORD_PATHSPECS = [...STORE_DIRS.map((d) => d + "/spec/"),
|
|
25204
|
+
var RECORD_PATHSPECS = [...STORE_DIRS.map((d) => d + "/spec/"), WIKI_DIR + "/"];
|
|
25066
25205
|
var LegacyBaselineError = class extends SpecPreviewError {
|
|
25067
25206
|
};
|
|
25068
25207
|
function specPreviewReader(cwd) {
|
|
@@ -25096,7 +25235,7 @@ function specPreviewReader(cwd) {
|
|
|
25096
25235
|
const [, mode, oid2, stage, path3] = match;
|
|
25097
25236
|
if (!["100644", "100755"].includes(mode) || stage !== "0") throw new SpecPreviewError(t2("reader.stagingConflictOrLink"));
|
|
25098
25237
|
if (!recordPathPattern.test(path3)) {
|
|
25099
|
-
if (
|
|
25238
|
+
if (path3.startsWith(WIKI_DIR + "/")) continue;
|
|
25100
25239
|
throw new SpecPreviewError(t2("reader.stagingUnsupported"));
|
|
25101
25240
|
}
|
|
25102
25241
|
files.set(path3, oid2);
|
|
@@ -25192,38 +25331,85 @@ function specPreviewReader(cwd) {
|
|
|
25192
25331
|
}
|
|
25193
25332
|
|
|
25194
25333
|
// src/adapters/filesystem/spec-preview-store.ts
|
|
25195
|
-
import { lstat as
|
|
25196
|
-
import { join as
|
|
25334
|
+
import { lstat as lstat3, readdir as readdir3, readFile as readFile3, mkdir as mkdir2, writeFile, rename, unlink as unlink2, rmdir } from "node:fs/promises";
|
|
25335
|
+
import { join as join4, dirname } from "node:path";
|
|
25197
25336
|
import { createHash as createHash2, randomBytes } from "node:crypto";
|
|
25198
25337
|
|
|
25199
|
-
// src/adapters/filesystem/
|
|
25200
|
-
import { lstat,
|
|
25338
|
+
// src/adapters/filesystem/working-warnings.ts
|
|
25339
|
+
import { lstat, readdir as readdir2 } from "node:fs/promises";
|
|
25201
25340
|
import { join as join2 } from "node:path";
|
|
25341
|
+
var info = async (path3) => lstat(path3).catch((e) => {
|
|
25342
|
+
if (e.code === "ENOENT") return void 0;
|
|
25343
|
+
throw e;
|
|
25344
|
+
});
|
|
25345
|
+
var posix = (path3) => path3.split("\\").join("/");
|
|
25346
|
+
async function listAssets(root) {
|
|
25347
|
+
const out = [];
|
|
25348
|
+
async function visit2(relative, depth) {
|
|
25349
|
+
const stat3 = await info(join2(root, relative));
|
|
25350
|
+
if (!stat3 || stat3.isSymbolicLink()) return;
|
|
25351
|
+
if (stat3.isDirectory()) {
|
|
25352
|
+
if (depth > 8 || out.length > 4e3) return;
|
|
25353
|
+
for (const name of (await readdir2(join2(root, relative))).sort()) await visit2(relative + "/" + name, depth + 1);
|
|
25354
|
+
} else if (stat3.isFile()) out.push({ path: posix(relative), bytes: stat3.size });
|
|
25355
|
+
}
|
|
25356
|
+
await visit2(ASSETS_DIR, 0);
|
|
25357
|
+
return out;
|
|
25358
|
+
}
|
|
25359
|
+
async function workingWarnings(root, bundle) {
|
|
25360
|
+
const warnings = designReferenceWarnings(bundle.specs);
|
|
25361
|
+
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)]);
|
|
25362
|
+
const assets = await listAssets(root);
|
|
25363
|
+
const assetPaths = new Set(assets.map((a) => a.path));
|
|
25364
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
25365
|
+
for (const { from, link: link3, target } of documentLinks(bundle)) {
|
|
25366
|
+
if (records.has(target)) continue;
|
|
25367
|
+
if (assetPaths.has(target)) {
|
|
25368
|
+
referenced.add(target);
|
|
25369
|
+
continue;
|
|
25370
|
+
}
|
|
25371
|
+
const stat3 = target.startsWith(".gitifact/") ? void 0 : await info(join2(root, ...target.split("/")));
|
|
25372
|
+
if (!stat3?.isFile()) warnings.push({ code: "MISSING_LINK_TARGET", path: from, target, link: link3 });
|
|
25373
|
+
}
|
|
25374
|
+
let total = 0;
|
|
25375
|
+
for (const asset of assets) {
|
|
25376
|
+
total += asset.bytes;
|
|
25377
|
+
if (asset.bytes > ASSET_SIZE_LIMIT) warnings.push({ code: "ASSET_SIZE", path: asset.path, bytes: asset.bytes });
|
|
25378
|
+
if (!RECOMMENDED_ASSET_EXTENSIONS.includes(assetExtension(asset.path))) warnings.push({ code: "ASSET_EXTENSION", path: asset.path });
|
|
25379
|
+
if (!referenced.has(asset.path)) warnings.push({ code: "UNREFERENCED_ASSET", path: asset.path });
|
|
25380
|
+
}
|
|
25381
|
+
if (total > ASSETS_TOTAL_LIMIT) warnings.push({ code: "ASSETS_TOTAL_SIZE", bytes: total });
|
|
25382
|
+
return warnings;
|
|
25383
|
+
}
|
|
25384
|
+
|
|
25385
|
+
// src/adapters/filesystem/config-file.ts
|
|
25386
|
+
import { lstat as lstat2, readFile as readFile2, realpath, mkdir, open as open2, link, unlink } from "node:fs/promises";
|
|
25387
|
+
import { join as join3 } from "node:path";
|
|
25202
25388
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
25203
25389
|
var missing = (error62) => error62.code === "ENOENT";
|
|
25204
|
-
var fileInfo = (path3) =>
|
|
25390
|
+
var fileInfo = (path3) => lstat2(path3).catch((error62) => {
|
|
25205
25391
|
if (missing(error62)) return void 0;
|
|
25206
25392
|
throw error62;
|
|
25207
25393
|
});
|
|
25208
25394
|
async function configDirectory(root) {
|
|
25209
|
-
const path3 =
|
|
25210
|
-
const
|
|
25211
|
-
if (
|
|
25212
|
-
return
|
|
25395
|
+
const path3 = join3(root, ".gitifact");
|
|
25396
|
+
const info7 = await fileInfo(path3);
|
|
25397
|
+
if (info7 && (!info7.isDirectory() || info7.isSymbolicLink())) throw new InitError("PATH_CONFLICT", t2("config.storeNotDirectory"));
|
|
25398
|
+
return info7;
|
|
25213
25399
|
}
|
|
25214
25400
|
async function readConfigFile(root) {
|
|
25215
25401
|
await configDirectory(root);
|
|
25216
|
-
const path3 =
|
|
25217
|
-
const
|
|
25218
|
-
if (!
|
|
25219
|
-
if (await fileInfo(
|
|
25402
|
+
const path3 = join3(root, ".gitifact", "config.json");
|
|
25403
|
+
const info7 = await fileInfo(path3);
|
|
25404
|
+
if (!info7) {
|
|
25405
|
+
if (await fileInfo(join3(root, ".tryce", "config.json"))) throw new InitError("MIGRATION_REQUIRED", t2("config.migrateFirst"));
|
|
25220
25406
|
return void 0;
|
|
25221
25407
|
}
|
|
25222
|
-
if (!
|
|
25408
|
+
if (!info7.isFile() || info7.isSymbolicLink() || info7.size > 65536) throw new InitError("PATH_CONFLICT", t2("config.fileShape"));
|
|
25223
25409
|
const bytes = await readFile2(path3);
|
|
25224
25410
|
const after = await fileInfo(path3);
|
|
25225
25411
|
await configDirectory(root);
|
|
25226
|
-
if (!after || after.ino !==
|
|
25412
|
+
if (!after || after.ino !== info7.ino || after.dev !== info7.dev || after.mtimeMs !== info7.mtimeMs || bytes.length > 65536) {
|
|
25227
25413
|
throw new InitError("INPUT_CHANGED", t2("config.changedWhileReading"));
|
|
25228
25414
|
}
|
|
25229
25415
|
try {
|
|
@@ -25233,7 +25419,7 @@ async function readConfigFile(root) {
|
|
|
25233
25419
|
}
|
|
25234
25420
|
}
|
|
25235
25421
|
async function publishConfig(root, text2, recheck, beforePublish) {
|
|
25236
|
-
const directory =
|
|
25422
|
+
const directory = join3(root, ".gitifact");
|
|
25237
25423
|
await configDirectory(root);
|
|
25238
25424
|
await mkdir(directory).catch((error62) => {
|
|
25239
25425
|
if (error62.code !== "EEXIST") throw error62;
|
|
@@ -25246,7 +25432,7 @@ async function publishConfig(root, text2, recheck, beforePublish) {
|
|
|
25246
25432
|
throw new InitError("INPUT_CHANGED", t2("config.directoryChanged"));
|
|
25247
25433
|
}
|
|
25248
25434
|
};
|
|
25249
|
-
const temporary =
|
|
25435
|
+
const temporary = join3(directory, ".init-" + randomUUID3() + ".tmp");
|
|
25250
25436
|
const handle = await open2(temporary, "wx", 384);
|
|
25251
25437
|
const tempOwner = await handle.stat();
|
|
25252
25438
|
try {
|
|
@@ -25263,7 +25449,7 @@ async function publishConfig(root, text2, recheck, beforePublish) {
|
|
|
25263
25449
|
const bytes = await readFile2(temporary, "utf8");
|
|
25264
25450
|
if (bytes !== text2) throw new InitError("INPUT_CHANGED", t2("config.temporaryChanged"));
|
|
25265
25451
|
parseManagedConfig(bytes);
|
|
25266
|
-
await link(temporary,
|
|
25452
|
+
await link(temporary, join3(directory, "config.json"));
|
|
25267
25453
|
} finally {
|
|
25268
25454
|
await unchanged();
|
|
25269
25455
|
const current = await fileInfo(temporary);
|
|
@@ -25275,14 +25461,14 @@ async function publishConfig(root, text2, recheck, beforePublish) {
|
|
|
25275
25461
|
var fail4 = (message) => {
|
|
25276
25462
|
throw new SpecPreviewError(message);
|
|
25277
25463
|
};
|
|
25278
|
-
var
|
|
25464
|
+
var info2 = async (path3) => lstat3(path3).catch((e) => {
|
|
25279
25465
|
if (e.code === "ENOENT") return void 0;
|
|
25280
25466
|
throw e;
|
|
25281
25467
|
});
|
|
25282
25468
|
var digest = (value) => createHash2("sha256").update(value).digest("hex");
|
|
25283
25469
|
var LEGACY_LOCKS = ["tryce-spec-preview.lock", "tryce-spec-commit.lock"];
|
|
25284
25470
|
async function failOnLegacyLock(gitDir) {
|
|
25285
|
-
for (const name of LEGACY_LOCKS) if (await
|
|
25471
|
+
for (const name of LEGACY_LOCKS) if (await info2(join4(gitDir, name))) fail4(t2("store.legacyLock", { path: join4(gitDir, name) }));
|
|
25286
25472
|
}
|
|
25287
25473
|
var decode3 = (bytes) => new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
25288
25474
|
var generatePreviewId = (prefix) => prefix + "-" + [...randomBytes(10)].map((n) => "abcdefghijklmnopqrstuvwxyz234567"[n & 31]).join("");
|
|
@@ -25292,48 +25478,48 @@ async function snapshot2(root) {
|
|
|
25292
25478
|
const files = /* @__PURE__ */ new Map();
|
|
25293
25479
|
let count = 0;
|
|
25294
25480
|
let bytes = 0;
|
|
25295
|
-
const parent = await
|
|
25481
|
+
const parent = await info2(join4(root, ".gitifact"));
|
|
25296
25482
|
if (parent && (!parent.isDirectory() || parent.isSymbolicLink())) fail4(t2("store.storeNotDirectory"));
|
|
25297
|
-
if (!parent && await
|
|
25483
|
+
if (!parent && await info2(join4(root, ".tryce"))) fail4(t2("store.migrateFirst"));
|
|
25298
25484
|
const config2 = await readConfigFile(root);
|
|
25299
|
-
if (config2 !== void 0 && !("schemaVersion" in parseManagedConfig(config2)) || await
|
|
25485
|
+
if (config2 !== void 0 && !("schemaVersion" in parseManagedConfig(config2)) || await info2(join4(root, "specs"))) fail4(t2("store.legacyProject"));
|
|
25300
25486
|
async function visit2(path3, depth, documents) {
|
|
25301
|
-
const stat3 = await
|
|
25487
|
+
const stat3 = await info2(join4(root, path3));
|
|
25302
25488
|
if (!stat3) return;
|
|
25303
25489
|
if (stat3.isSymbolicLink() || !stat3.isDirectory() && !stat3.isFile()) fail4(t2("store.linkOrSpecial", { path: path3 }));
|
|
25304
25490
|
if (++count > 4e3) fail4(t2("store.tooManyFiles"));
|
|
25305
25491
|
if (stat3.isDirectory()) {
|
|
25306
25492
|
if (depth > (documents ? 8 : 1)) fail4(t2("store.tooDeep", { path: path3 }));
|
|
25307
|
-
for (const name of (await
|
|
25493
|
+
for (const name of (await readdir3(join4(root, path3))).sort()) await visit2(path3 + "/" + name, depth + 1, documents);
|
|
25308
25494
|
} else {
|
|
25309
25495
|
if (path3.endsWith("/tryce.json")) fail4(t2("store.legacyJson"));
|
|
25310
25496
|
if (documents ? !(path3.endsWith(".md") || depth === 1 && path3.endsWith("/history.jsonl")) : !/\/(requirements\.md|design\.md|history\.jsonl)$/.test(path3)) return;
|
|
25311
25497
|
if (!recordPathPattern.test(path3) || path3.startsWith(".tryce/")) fail4(t2("store.unsupportedPath", { path: path3 }));
|
|
25312
25498
|
if (stat3.nlink !== 1 || stat3.size > 1024 * 1024) fail4(t2("store.hardLinkOrSize"));
|
|
25313
|
-
const raw = await readFile3(
|
|
25499
|
+
const raw = await readFile3(join4(root, path3));
|
|
25314
25500
|
bytes += raw.length;
|
|
25315
25501
|
if (raw.length > 1024 * 1024 || bytes > 16 * 1024 * 1024) fail4(t2("store.sizeLimit"));
|
|
25316
25502
|
files.set(path3, decode3(raw));
|
|
25317
25503
|
}
|
|
25318
25504
|
}
|
|
25319
25505
|
await visit2(".gitifact/spec", 0, false);
|
|
25320
|
-
|
|
25506
|
+
await visit2(WIKI_DIR, 0, true);
|
|
25321
25507
|
const bundle = parsePreviewBundle(files);
|
|
25322
|
-
return { files, specs: bundle.specs,
|
|
25508
|
+
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
25509
|
}
|
|
25324
25510
|
async function readWorkingPreviewState(cwd) {
|
|
25325
25511
|
const { root, gitDir } = await specPreviewReader(cwd).location();
|
|
25326
25512
|
await failOnLegacyLock(gitDir);
|
|
25327
|
-
if (await
|
|
25513
|
+
if (await info2(join4(gitDir, "gitifact-spec-preview.lock"))) fail4(t2("store.locked"));
|
|
25328
25514
|
const first = await snapshot2(root);
|
|
25329
25515
|
const second = await snapshot2(root);
|
|
25330
25516
|
if (first.stamp !== second.stamp) fail4(t2("store.changedWhileReading"));
|
|
25331
|
-
if (await
|
|
25517
|
+
if (await info2(join4(gitDir, "gitifact-spec-preview.lock"))) fail4(t2("store.lockedWhileReading"));
|
|
25332
25518
|
return first;
|
|
25333
25519
|
}
|
|
25334
25520
|
async function readWorkingPreview(cwd) {
|
|
25335
|
-
const { stamp, specs,
|
|
25336
|
-
return { stamp, specs,
|
|
25521
|
+
const { root, stamp, specs, wiki, bundle } = await readWorkingPreviewState(cwd);
|
|
25522
|
+
return { stamp, specs, wiki, warnings: await workingWarnings(root, bundle) };
|
|
25337
25523
|
}
|
|
25338
25524
|
async function saveWorkingPreview(cwd, input2, publish = rename) {
|
|
25339
25525
|
if (!input2 || typeof input2 !== "object" || Array.isArray(input2)) fail4(t2("store.invalidInput"));
|
|
@@ -25347,8 +25533,8 @@ async function saveWorkingPreview(cwd, input2, publish = rename) {
|
|
|
25347
25533
|
if (!old || renderSpecPreview(old) !== renderSpecPreview(spec2)) writes.set(spec2.path, renderSpecPreview(spec2));
|
|
25348
25534
|
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
25535
|
}
|
|
25350
|
-
const previousDocs = before.
|
|
25351
|
-
const nextDocs = result.
|
|
25536
|
+
const previousDocs = before.wiki.documents;
|
|
25537
|
+
const nextDocs = result.wiki.documents;
|
|
25352
25538
|
for (const doc of nextDocs) {
|
|
25353
25539
|
const old = previousDocs.find((d) => d.id === doc.id);
|
|
25354
25540
|
if (old && old.path !== doc.path) writes.set(old.path, null);
|
|
@@ -25362,7 +25548,7 @@ var readLockedPreviewState = (root) => snapshot2(root);
|
|
|
25362
25548
|
async function previewTransaction(cwd, expected2, build, publish = rename, afterPublish) {
|
|
25363
25549
|
const { root, gitDir } = await specPreviewReader(cwd).location();
|
|
25364
25550
|
await failOnLegacyLock(gitDir);
|
|
25365
|
-
const lock =
|
|
25551
|
+
const lock = join4(gitDir, "gitifact-spec-preview.lock");
|
|
25366
25552
|
try {
|
|
25367
25553
|
await mkdir2(lock);
|
|
25368
25554
|
} catch (e) {
|
|
@@ -25375,7 +25561,7 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25375
25561
|
const published = [];
|
|
25376
25562
|
let keepRecovery = false;
|
|
25377
25563
|
async function directory(path3) {
|
|
25378
|
-
const stat3 = await
|
|
25564
|
+
const stat3 = await info2(path3);
|
|
25379
25565
|
if (stat3) {
|
|
25380
25566
|
if (!stat3.isDirectory() || stat3.isSymbolicLink()) fail4(t2("store.notDirectory", { path: path3 }));
|
|
25381
25567
|
return;
|
|
@@ -25399,14 +25585,14 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25399
25585
|
else finalFiles.set(c.path, c.after);
|
|
25400
25586
|
}
|
|
25401
25587
|
if (finalFiles.size > 2e3 || [...finalFiles.values()].reduce((n, s) => n + Buffer.byteLength(s), 0) > 16 * 1024 * 1024) fail4(t2("store.totalLimit"));
|
|
25402
|
-
await writeFile(
|
|
25588
|
+
await writeFile(join4(lock, "recovery.json"), JSON.stringify({ root, changed }), { flag: "wx" });
|
|
25403
25589
|
for (const c of changed) {
|
|
25404
25590
|
if (c.after === null) {
|
|
25405
25591
|
temporary.push("");
|
|
25406
25592
|
continue;
|
|
25407
25593
|
}
|
|
25408
|
-
await directory(dirname(
|
|
25409
|
-
const temp =
|
|
25594
|
+
await directory(dirname(join4(root, c.path)));
|
|
25595
|
+
const temp = join4(dirname(join4(root, c.path)), ".gitifact-save-" + randomBytes(12).toString("hex"));
|
|
25410
25596
|
await writeFile(temp, c.after, { flag: "wx" });
|
|
25411
25597
|
temporary.push(temp);
|
|
25412
25598
|
}
|
|
@@ -25414,8 +25600,8 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25414
25600
|
await result.recheck?.();
|
|
25415
25601
|
for (let i = 0; i < changed.length; i++) {
|
|
25416
25602
|
const c = changed[i];
|
|
25417
|
-
const path3 =
|
|
25418
|
-
const stat3 = await
|
|
25603
|
+
const path3 = join4(root, c.path);
|
|
25604
|
+
const stat3 = await info2(path3);
|
|
25419
25605
|
if (stat3?.isSymbolicLink() || stat3 && !stat3.isFile() || (stat3 ? decode3(await readFile3(path3)) : null) !== c.before) fail4(t2("store.changedBeforeWrite"));
|
|
25420
25606
|
if (c.after === null) await unlink2(path3);
|
|
25421
25607
|
else await publish(temporary[i], path3);
|
|
@@ -25425,7 +25611,7 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25425
25611
|
if (after.stamp !== digest(JSON.stringify([...finalFiles].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) + (before.config ?? ""))) fail4(t2("store.changedWhileWriting"));
|
|
25426
25612
|
await result.recheck?.();
|
|
25427
25613
|
await afterPublish?.(after);
|
|
25428
|
-
return { ...result.data, stamp: after.stamp, paths: changed.map((c) => c.path), specs: after.specs,
|
|
25614
|
+
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
25615
|
} catch (error62) {
|
|
25430
25616
|
if (error62 instanceof PreservedPreviewError) {
|
|
25431
25617
|
keepRecovery = true;
|
|
@@ -25433,15 +25619,15 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25433
25619
|
}
|
|
25434
25620
|
for (const c of [...published].reverse()) {
|
|
25435
25621
|
try {
|
|
25436
|
-
const path3 =
|
|
25437
|
-
const stat3 = await
|
|
25622
|
+
const path3 = join4(root, c.path);
|
|
25623
|
+
const stat3 = await info2(path3);
|
|
25438
25624
|
if (c.after === null ? !!stat3 : !stat3?.isFile() || stat3.isSymbolicLink() || decode3(await readFile3(path3)) !== c.after) {
|
|
25439
25625
|
keepRecovery = true;
|
|
25440
25626
|
continue;
|
|
25441
25627
|
}
|
|
25442
25628
|
if (c.before === null) await unlink2(path3);
|
|
25443
25629
|
else {
|
|
25444
|
-
const temp =
|
|
25630
|
+
const temp = join4(dirname(path3), ".gitifact-restore-" + randomBytes(12).toString("hex"));
|
|
25445
25631
|
temporary.push(temp);
|
|
25446
25632
|
await writeFile(temp, c.before, { flag: "wx" });
|
|
25447
25633
|
await rename(temp, path3);
|
|
@@ -25459,7 +25645,7 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25459
25645
|
for (const path3 of [...createdDirs].reverse()) await rmdir(path3).catch(() => {
|
|
25460
25646
|
});
|
|
25461
25647
|
if (!keepRecovery) {
|
|
25462
|
-
await unlink2(
|
|
25648
|
+
await unlink2(join4(lock, "recovery.json")).catch((e) => {
|
|
25463
25649
|
if (e.code !== "ENOENT") throw e;
|
|
25464
25650
|
});
|
|
25465
25651
|
await rmdir(lock);
|
|
@@ -25501,8 +25687,8 @@ function createSpecBrowserReader(root, sessionId, inherited = process.env) {
|
|
|
25501
25687
|
if (cursor > 0 && expectedHead !== base.head) throw new SpecPreviewError(t2("specReader.historyChanged"));
|
|
25502
25688
|
const current = await readWorkingPreviewState(root);
|
|
25503
25689
|
const bare = current.specs.map(({ history: _history, ...s }) => ({ ...s, contributors: [], updatedAt: null }));
|
|
25504
|
-
const bareDocuments = current.
|
|
25505
|
-
if (!base.head) return
|
|
25690
|
+
const bareDocuments = current.wiki.documents.map((d) => ({ ...d, updatedAt: null }));
|
|
25691
|
+
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
25692
|
const head = base.head;
|
|
25507
25693
|
const features = await Promise.all(bare.map(async (feature2) => {
|
|
25508
25694
|
const folder = feature2.path.replace(/^\.(?:gitifact|tryce)\/(spec\/[^/]+)\/requirements\.md$/, "$1");
|
|
@@ -25518,7 +25704,7 @@ function createSpecBrowserReader(root, sessionId, inherited = process.env) {
|
|
|
25518
25704
|
return { ...feature2, contributors: [...people.values()].sort((a, b) => b.commits - a.commits), updatedAt: lines[0]?.split("\0")[2] ?? null };
|
|
25519
25705
|
}));
|
|
25520
25706
|
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 =
|
|
25707
|
+
const documentDirs = [WIKI_DIR];
|
|
25522
25708
|
const [rows, dirty] = await Promise.all([
|
|
25523
25709
|
git([
|
|
25524
25710
|
"log",
|
|
@@ -25552,7 +25738,7 @@ function createSpecBrowserReader(root, sessionId, inherited = process.env) {
|
|
|
25552
25738
|
const [commit, parents, author, email3, date5, committer, message] = row.split("\0");
|
|
25553
25739
|
if (!commit || author === void 0 || email3 === void 0 || !date5 || committer === void 0 || message === void 0) throw new SpecPreviewError(t2("specReader.historyUnreadable"));
|
|
25554
25740
|
const after = await snapshot3(commit);
|
|
25555
|
-
let before =
|
|
25741
|
+
let before = emptyBundle();
|
|
25556
25742
|
const parent = parents?.split(" ")[0];
|
|
25557
25743
|
if (parent) {
|
|
25558
25744
|
try {
|
|
@@ -25579,9 +25765,9 @@ function createSpecBrowserReader(root, sessionId, inherited = process.env) {
|
|
|
25579
25765
|
}));
|
|
25580
25766
|
}));
|
|
25581
25767
|
if (await readHead() !== head || (await readWorkingPreviewState(root)).stamp !== current.stamp) throw new SpecPreviewError(t2("specReader.projectChanged"));
|
|
25582
|
-
return
|
|
25768
|
+
return browserSpecsV2.parse({
|
|
25583
25769
|
contract: "browser-specs",
|
|
25584
|
-
version:
|
|
25770
|
+
version: 2,
|
|
25585
25771
|
sessionId,
|
|
25586
25772
|
head,
|
|
25587
25773
|
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -25675,18 +25861,27 @@ async function startBrowserServer(options) {
|
|
|
25675
25861
|
if (api) {
|
|
25676
25862
|
if (request.headers["sec-fetch-site"] === "cross-site" && !allowedOrigin) return fail8(response, 403, "FORBIDDEN", t2("server.crossSite"));
|
|
25677
25863
|
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
|
|
25864
|
+
if (path3.startsWith("/api/v1/assets/")) {
|
|
25865
|
+
const relative = path3.slice("/api/v1/assets/".length);
|
|
25866
|
+
const assetPath2 = ASSETS_DIR + "/" + relative;
|
|
25680
25867
|
if (request.method !== "GET") {
|
|
25681
25868
|
response.setHeader("Allow", "GET");
|
|
25682
25869
|
return fail8(response, 405, "METHOD_NOT_ALLOWED", t2("server.methodNotAllowed"));
|
|
25683
25870
|
}
|
|
25684
|
-
if (!
|
|
25685
|
-
const file2 =
|
|
25686
|
-
const stat3 = await
|
|
25687
|
-
if (!stat3?.isFile() || stat3.isSymbolicLink() || stat3.size >
|
|
25871
|
+
if (!isAssetPath(assetPath2)) return fail8(response, 404, "NOT_FOUND", t2("server.assetNotFound"));
|
|
25872
|
+
const file2 = join5(initial.repository.rootPath, ...assetPath2.split("/"));
|
|
25873
|
+
const stat3 = await lstat4(file2).catch(() => void 0);
|
|
25874
|
+
if (!stat3?.isFile() || stat3.isSymbolicLink() || stat3.size > 20 * 1024 * 1024) return fail8(response, 404, "NOT_FOUND", t2("server.assetNotFound"));
|
|
25688
25875
|
const bytes = await readFile4(file2);
|
|
25689
|
-
|
|
25876
|
+
const extension = assetExtension(relative);
|
|
25877
|
+
const inline = ["png", "jpg", "jpeg", "gif", "webp", "svg"].includes(extension);
|
|
25878
|
+
response.writeHead(200, {
|
|
25879
|
+
"Content-Type": contentType("." + extension),
|
|
25880
|
+
"Cache-Control": "no-cache",
|
|
25881
|
+
"Content-Length": bytes.length,
|
|
25882
|
+
"Content-Security-Policy": "default-src 'none'; sandbox",
|
|
25883
|
+
"Content-Disposition": inline ? "inline" : 'attachment; filename="' + encodeURIComponent(relative.split("/").pop()) + '"'
|
|
25884
|
+
});
|
|
25690
25885
|
response.end(bytes);
|
|
25691
25886
|
return;
|
|
25692
25887
|
}
|
|
@@ -25850,13 +26045,13 @@ async function runBrowser(options, version2) {
|
|
|
25850
26045
|
}
|
|
25851
26046
|
|
|
25852
26047
|
// src/commands/spec-init.ts
|
|
25853
|
-
import { readdir as
|
|
25854
|
-
import { join as
|
|
26048
|
+
import { mkdir as mkdir4, readdir as readdir4, writeFile as writeFile2 } from "node:fs/promises";
|
|
26049
|
+
import { join as join9 } from "node:path";
|
|
25855
26050
|
|
|
25856
26051
|
// src/adapters/git/init-repository.ts
|
|
25857
26052
|
import { createHash as createHash3 } from "node:crypto";
|
|
25858
26053
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
25859
|
-
import { join as
|
|
26054
|
+
import { join as join6 } from "node:path";
|
|
25860
26055
|
function initRepository(cwd, inherited = process.env) {
|
|
25861
26056
|
const env = { ...inherited, GIT_OPTIONAL_LOCKS: "0", GIT_NO_LAZY_FETCH: "1", GIT_TERMINAL_PROMPT: "0", GIT_LITERAL_PATHSPECS: "1", LC_ALL: "C" };
|
|
25862
26057
|
const runner = createGitRunner();
|
|
@@ -25876,7 +26071,7 @@ function initRepository(cwd, inherited = process.env) {
|
|
|
25876
26071
|
const gitDir = locations.length === 2 ? locations[0] : decodeGitLine(await git(["rev-parse", "--absolute-git-dir"], root));
|
|
25877
26072
|
const indexPath = locations.length === 2 ? locations[1] : decodeGitLine(await git(["rev-parse", "--path-format=absolute", "--git-path", "index"], root));
|
|
25878
26073
|
for (const marker2 of ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"]) {
|
|
25879
|
-
if (await fileInfo(
|
|
26074
|
+
if (await fileInfo(join6(gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
|
|
25880
26075
|
}
|
|
25881
26076
|
if (state.changes.some((change2) => change2.kind === "unmerged")) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
25882
26077
|
const index = await readFile5(indexPath).catch((error62) => {
|
|
@@ -25914,22 +26109,22 @@ function initRepository(cwd, inherited = process.env) {
|
|
|
25914
26109
|
|
|
25915
26110
|
// src/adapters/filesystem/managed-file.ts
|
|
25916
26111
|
import { open as open3, readFile as readFile6, mkdir as mkdir3, realpath as realpath2, rename as rename2, link as link2, unlink as unlink3 } from "node:fs/promises";
|
|
25917
|
-
import { join as
|
|
26112
|
+
import { join as join7, dirname as dirname2 } from "node:path";
|
|
25918
26113
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
25919
26114
|
var DEFAULT_LIMIT = 1024 * 1024;
|
|
25920
26115
|
async function managedPath(root, path3, create = false) {
|
|
25921
26116
|
let full = root;
|
|
25922
26117
|
const parts = path3.split("/");
|
|
25923
26118
|
for (let i = 0; i < parts.length; i++) {
|
|
25924
|
-
full =
|
|
25925
|
-
let
|
|
25926
|
-
if (!
|
|
26119
|
+
full = join7(full, parts[i]);
|
|
26120
|
+
let info7 = await fileInfo(full);
|
|
26121
|
+
if (!info7 && create && i < parts.length - 1) {
|
|
25927
26122
|
await mkdir3(full).catch((error62) => {
|
|
25928
26123
|
if (error62.code !== "EEXIST") throw error62;
|
|
25929
26124
|
});
|
|
25930
|
-
|
|
26125
|
+
info7 = await fileInfo(full);
|
|
25931
26126
|
}
|
|
25932
|
-
if (
|
|
26127
|
+
if (info7 && (info7.isSymbolicLink() || (i < parts.length - 1 ? !info7.isDirectory() : !info7.isFile()))) {
|
|
25933
26128
|
throw new InitError("PATH_CONFLICT", t2("managedFile.notRegularPath", { path: path3 }));
|
|
25934
26129
|
}
|
|
25935
26130
|
}
|
|
@@ -25969,7 +26164,7 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
25969
26164
|
if (previous !== null) await unlink3(full);
|
|
25970
26165
|
return;
|
|
25971
26166
|
}
|
|
25972
|
-
const temp =
|
|
26167
|
+
const temp = join7(directory, ".gitifact-managed-" + randomUUID5() + ".tmp");
|
|
25973
26168
|
const handle = await open3(temp, "wx", 384);
|
|
25974
26169
|
const tempOwner = await handle.stat();
|
|
25975
26170
|
try {
|
|
@@ -25995,14 +26190,53 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
25995
26190
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
25996
26191
|
|
|
25997
26192
|
// src/commands/docs.ts
|
|
25998
|
-
import { readFile as readFile7 } from "node:fs/promises";
|
|
25999
|
-
|
|
26193
|
+
import { lstat as lstat5, readFile as readFile7 } from "node:fs/promises";
|
|
26194
|
+
import { dirname as dirname3, join as join8 } from "node:path";
|
|
26195
|
+
var docTopics = ["workflow", "spec", "design", "wiki", "commit"];
|
|
26000
26196
|
var isDocTopic = (value) => docTopics.includes(value);
|
|
26001
|
-
var
|
|
26197
|
+
var readBundledDoc = (name, lang) => readFile7(new URL("./i18n/" + lang + "/docs/" + name + ".md", import.meta.url), "utf8");
|
|
26198
|
+
var info3 = (path3) => lstat5(path3).catch((e) => {
|
|
26199
|
+
if (e.code === "ENOENT") return void 0;
|
|
26200
|
+
throw e;
|
|
26201
|
+
});
|
|
26002
26202
|
function listDocTopics(lang = defaultLanguage2) {
|
|
26003
26203
|
const width = Math.max(...docTopics.map((topic) => topic.length));
|
|
26004
26204
|
return docTopics.map((topic) => topic.padEnd(width) + " " + t2(`docs.summary.${topic}`, {}, lang)).join("\n") + "\n";
|
|
26005
26205
|
}
|
|
26206
|
+
async function findProjectRoot(cwd) {
|
|
26207
|
+
for (let dir = cwd; ; dir = dirname3(dir)) {
|
|
26208
|
+
if ((await info3(join8(dir, ".gitifact", "config.json")))?.isFile()) return dir;
|
|
26209
|
+
if (dirname3(dir) === dir) return void 0;
|
|
26210
|
+
}
|
|
26211
|
+
}
|
|
26212
|
+
async function readWikiPolicy(root) {
|
|
26213
|
+
if (!root) return void 0;
|
|
26214
|
+
const path3 = join8(root, ...WIKI_ENTRY_PATH.split("/"));
|
|
26215
|
+
const stat3 = await info3(path3);
|
|
26216
|
+
if (!stat3?.isFile() || stat3.size > 1024 * 1024) return void 0;
|
|
26217
|
+
const lines = (await readFile7(path3, "utf8")).replace(/\r\n/g, "\n").split("\n");
|
|
26218
|
+
if (lines[0] === "---") {
|
|
26219
|
+
const end = lines.indexOf("---", 1);
|
|
26220
|
+
if (end > 0) lines.splice(0, end + 1);
|
|
26221
|
+
}
|
|
26222
|
+
while (lines.length && !lines[0].trim()) lines.shift();
|
|
26223
|
+
if (lines[0]?.startsWith("# ")) lines.shift();
|
|
26224
|
+
let fence = false;
|
|
26225
|
+
const body = lines.map((line) => {
|
|
26226
|
+
if (/^ {0,3}(`{3,}|~{3,})/.test(line)) fence = !fence;
|
|
26227
|
+
return !fence && /^#{1,5} /.test(line) ? "#" + line : line;
|
|
26228
|
+
}).join("\n").trim();
|
|
26229
|
+
return body || void 0;
|
|
26230
|
+
}
|
|
26231
|
+
async function renderDoc(topic, controls = {}, lang = defaultLanguage2) {
|
|
26232
|
+
const read = controls.readDoc ?? readBundledDoc;
|
|
26233
|
+
const format = (await read(topic, lang)).trimEnd();
|
|
26234
|
+
if (topic !== "wiki") return format + "\n";
|
|
26235
|
+
const own2 = await readWikiPolicy(await findProjectRoot(controls.cwd ?? process.cwd()));
|
|
26236
|
+
const policy = own2 ?? (await read("wiki.default", lang)).trim().replace(/^(#{1,5}) /gm, "#$1 ");
|
|
26237
|
+
const heading = own2 ? t2("docs.wikiPolicyProject", {}, lang) : t2("docs.wikiPolicyDefault", {}, lang);
|
|
26238
|
+
return format + "\n\n## " + heading + "\n\n" + policy + "\n";
|
|
26239
|
+
}
|
|
26006
26240
|
async function runDocs(topic, controls = {}) {
|
|
26007
26241
|
if (topic === void 0) {
|
|
26008
26242
|
process.stdout.write(listDocTopics());
|
|
@@ -26014,7 +26248,7 @@ async function runDocs(topic, controls = {}) {
|
|
|
26014
26248
|
process.exitCode = 1;
|
|
26015
26249
|
return;
|
|
26016
26250
|
}
|
|
26017
|
-
process.stdout.write(await (
|
|
26251
|
+
process.stdout.write(await renderDoc(topic, controls));
|
|
26018
26252
|
}
|
|
26019
26253
|
|
|
26020
26254
|
// src/commands/agent-block.ts
|
|
@@ -26055,6 +26289,16 @@ function isWrapperFile(text2) {
|
|
|
26055
26289
|
const lines = own2.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
26056
26290
|
return lines.length > 0 && lines.every((line) => /^@(?:\.\/)?[^\s]+$/.test(line) && candidatePaths.includes(line.replace(/^@(?:\.\/)?/, "")));
|
|
26057
26291
|
}
|
|
26292
|
+
var CLAUDE_WRAPPER_PATH = "CLAUDE.md";
|
|
26293
|
+
function claudeWrapperFor(agents) {
|
|
26294
|
+
return "@AGENTS.md" + eolOf(agents);
|
|
26295
|
+
}
|
|
26296
|
+
function isGeneratedClaudeWrapper(text2) {
|
|
26297
|
+
return text2.trim() === "@AGENTS.md";
|
|
26298
|
+
}
|
|
26299
|
+
function lacksClaudeFile(existing) {
|
|
26300
|
+
return !existing.has("CLAUDE.md") && !existing.has(".claude/CLAUDE.md");
|
|
26301
|
+
}
|
|
26058
26302
|
function resolveAgentPaths(preset, existing) {
|
|
26059
26303
|
if (preset && preset !== "all") {
|
|
26060
26304
|
const order = agentPresets[preset];
|
|
@@ -26088,7 +26332,7 @@ function removeBlock(text2, boilerplate) {
|
|
|
26088
26332
|
}
|
|
26089
26333
|
|
|
26090
26334
|
// src/commands/agent-docs.ts
|
|
26091
|
-
var skippedAgentDocs = { mode: "skip", paths: [], writes: [] };
|
|
26335
|
+
var skippedAgentDocs = { mode: "skip", paths: [], writes: [], missing: [] };
|
|
26092
26336
|
async function planAgentDocs(root, options) {
|
|
26093
26337
|
if (options.skip) return skippedAgentDocs;
|
|
26094
26338
|
const existing = /* @__PURE__ */ new Map();
|
|
@@ -26106,7 +26350,12 @@ async function planAgentDocs(root, options) {
|
|
|
26106
26350
|
paths2.push(path3);
|
|
26107
26351
|
writes.push({ path: path3, previous: text2, next: removeBlock(text2, boilerplateFor(path3)) });
|
|
26108
26352
|
}
|
|
26109
|
-
|
|
26353
|
+
const wrapper = existing.get(CLAUDE_WRAPPER_PATH);
|
|
26354
|
+
if (writes.some((write) => write.path === "AGENTS.md" && write.next === null) && wrapper !== void 0 && isGeneratedClaudeWrapper(wrapper)) {
|
|
26355
|
+
paths2.push(CLAUDE_WRAPPER_PATH);
|
|
26356
|
+
writes.push({ path: CLAUDE_WRAPPER_PATH, previous: wrapper, next: null });
|
|
26357
|
+
}
|
|
26358
|
+
return { mode: "remove", paths: paths2, writes, missing: [] };
|
|
26110
26359
|
}
|
|
26111
26360
|
const block = await renderAgentBlock(options.version, options);
|
|
26112
26361
|
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 +26369,19 @@ async function planAgentDocs(root, options) {
|
|
|
26120
26369
|
paths2.push(create);
|
|
26121
26370
|
writes.push({ path: create, previous: null, next: injectBlock(null, block, boilerplateFor(create)) });
|
|
26122
26371
|
}
|
|
26372
|
+
const missing2 = [];
|
|
26373
|
+
if (paths2.includes("AGENTS.md") && lacksClaudeFile(existing)) {
|
|
26374
|
+
if (options.onlyExisting) missing2.push(CLAUDE_WRAPPER_PATH);
|
|
26375
|
+
else {
|
|
26376
|
+
paths2.push(CLAUDE_WRAPPER_PATH);
|
|
26377
|
+
writes.push({ path: CLAUDE_WRAPPER_PATH, previous: null, next: claudeWrapperFor(existing.get("AGENTS.md") ?? null) });
|
|
26378
|
+
}
|
|
26379
|
+
}
|
|
26123
26380
|
for (const [path3, text2] of existing) {
|
|
26124
26381
|
if (paths2.includes(path3) || !isWrapperFile(text2) || !findBlock(text2)) continue;
|
|
26125
26382
|
writes.push({ path: path3, previous: text2, next: removeBlock(text2, boilerplateFor(path3)) });
|
|
26126
26383
|
}
|
|
26127
|
-
return { mode: "install", paths: paths2, writes };
|
|
26384
|
+
return { mode: "install", paths: paths2, writes, missing: missing2 };
|
|
26128
26385
|
}
|
|
26129
26386
|
async function applyAgentDocs(root, plan, recheck) {
|
|
26130
26387
|
for (const write of plan.writes) await managedWrite(root, write.path, write.previous, write.next, recheck);
|
|
@@ -26132,7 +26389,7 @@ async function applyAgentDocs(root, plan, recheck) {
|
|
|
26132
26389
|
}
|
|
26133
26390
|
|
|
26134
26391
|
// src/commands/spec-init.ts
|
|
26135
|
-
async function initializeSpecProject(cwd, dryRun = false, env = process.env, beforePublish, agentDocs) {
|
|
26392
|
+
async function initializeSpecProject(cwd, dryRun = false, env = process.env, beforePublish, agentDocs, readDoc = (name) => readBundledDoc(name, defaultLanguage2)) {
|
|
26136
26393
|
const repo = initRepository(cwd, env);
|
|
26137
26394
|
const first = await repo.inspect();
|
|
26138
26395
|
const root = first.state.repository.rootPath;
|
|
@@ -26163,14 +26420,14 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
26163
26420
|
if (old !== void 0) return existing(old);
|
|
26164
26421
|
if (first.trackedConfig) throw new InitError("CONFIG_DELETED", t2("init.configDeleted"));
|
|
26165
26422
|
const checkRecords = async () => {
|
|
26166
|
-
if (await fileInfo(
|
|
26167
|
-
const entries = await
|
|
26423
|
+
if (await fileInfo(join9(root, "specs"))) throw new InitError("EXISTING_RECORDS", t2("init.existingSpecs"));
|
|
26424
|
+
const entries = await readdir4(join9(root, ".gitifact")).catch((e) => {
|
|
26168
26425
|
if (e.code === "ENOENT") return [];
|
|
26169
26426
|
throw e;
|
|
26170
26427
|
});
|
|
26171
26428
|
if (entries.some((name) => !/^\.init-[a-f0-9-]+\.tmp$/.test(name))) throw new InitError("EXISTING_RECORDS", t2("init.orphanStore"));
|
|
26172
26429
|
};
|
|
26173
|
-
const config2 = { schemaVersion:
|
|
26430
|
+
const config2 = { schemaVersion: SCHEMA_VERSION, baseline: first.state.head.commit ? { kind: "commit", objectFormat: first.state.repository.objectFormat, commit: first.state.head.commit } : { kind: "empty" } };
|
|
26174
26431
|
const text2 = JSON.stringify(config2, null, 2) + "\n";
|
|
26175
26432
|
const recheck = async () => {
|
|
26176
26433
|
await checkRecords();
|
|
@@ -26194,8 +26451,22 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
26194
26451
|
};
|
|
26195
26452
|
await published();
|
|
26196
26453
|
await applyAgentDocs(root, docsPlan, published);
|
|
26454
|
+
await writeWikiPolicy(root, readDoc);
|
|
26197
26455
|
return result(config2, "created");
|
|
26198
26456
|
}
|
|
26457
|
+
async function writeWikiPolicy(root, readDoc) {
|
|
26458
|
+
if (await fileInfo(join9(root, ...WIKI_DIR.split("/")))) return;
|
|
26459
|
+
const template = await readDoc("wiki.default").catch((e) => {
|
|
26460
|
+
if (e.code === "ENOENT") return void 0;
|
|
26461
|
+
throw e;
|
|
26462
|
+
});
|
|
26463
|
+
if (template === void 0) return;
|
|
26464
|
+
const doc = { id: generatePreviewId("W"), path: WIKI_ENTRY_PATH, title: t2("init.wikiReadmeTitle"), body: template.trim() };
|
|
26465
|
+
const text2 = renderDocument(doc);
|
|
26466
|
+
parseDocument(doc.path, text2);
|
|
26467
|
+
await mkdir4(join9(root, ...WIKI_DIR.split("/")), { recursive: true });
|
|
26468
|
+
await writeFile2(join9(root, ...WIKI_ENTRY_PATH.split("/")), text2, { flag: "wx" });
|
|
26469
|
+
}
|
|
26199
26470
|
|
|
26200
26471
|
// src/commands/init.ts
|
|
26201
26472
|
async function runInit(options, version2) {
|
|
@@ -26210,7 +26481,7 @@ async function runInit(options, version2) {
|
|
|
26210
26481
|
if (!dto.ok) throw new Error("unreachable");
|
|
26211
26482
|
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") + ")");
|
|
26212
26483
|
process.stdout.write(options.format === "text" ? `${dto.outcome}: ${dto.rootPath}/.gitifact/config.json
|
|
26213
|
-
${t2("init.text.storage")}: schemaVersion
|
|
26484
|
+
${t2("init.text.storage")}: schemaVersion ${dto.schemaVersion}
|
|
26214
26485
|
${t2("init.text.agentDocs")}: ${docs}
|
|
26215
26486
|
` : JSON.stringify(dto) + "\n");
|
|
26216
26487
|
} catch (error62) {
|
|
@@ -26262,19 +26533,20 @@ async function refreshBlocks(cwd, version2, env, controls) {
|
|
|
26262
26533
|
first = await repo.inspect();
|
|
26263
26534
|
config2 = await readConfigFile(first.state.repository.rootPath);
|
|
26264
26535
|
} catch (error62) {
|
|
26265
|
-
if (error62 instanceof RepositoryReadError && error62.code === "NOT_A_REPOSITORY" || error62 instanceof InitError && error62.code === "MIGRATION_REQUIRED") return { state: "not-initialized", paths: [] };
|
|
26536
|
+
if (error62 instanceof RepositoryReadError && error62.code === "NOT_A_REPOSITORY" || error62 instanceof InitError && error62.code === "MIGRATION_REQUIRED") return { state: "not-initialized", paths: [], missing: [] };
|
|
26266
26537
|
throw error62;
|
|
26267
26538
|
}
|
|
26268
|
-
if (config2 === void 0) return { state: "not-initialized", paths: [] };
|
|
26539
|
+
if (config2 === void 0) return { state: "not-initialized", paths: [], missing: [] };
|
|
26269
26540
|
const root = first.state.repository.rootPath;
|
|
26270
26541
|
const plan = await planAgentDocs(root, { version: version2, onlyExisting: true, ...controls });
|
|
26271
|
-
|
|
26272
|
-
if (plan.
|
|
26542
|
+
const { missing: missing2 } = plan;
|
|
26543
|
+
if (plan.paths.length === 0) return { state: "no-block", paths: [], missing: missing2, root };
|
|
26544
|
+
if (plan.writes.length === 0) return { state: "current", paths: plan.paths, missing: missing2, root, candidates: plan.paths };
|
|
26273
26545
|
const unchanged = async () => {
|
|
26274
26546
|
if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !== config2) throw new InitError("INPUT_CHANGED", t2("init.inputChanged"));
|
|
26275
26547
|
};
|
|
26276
26548
|
const applied = await applyAgentDocs(root, plan, unchanged);
|
|
26277
|
-
return { state: "refreshed", paths: applied, root, candidates: plan.paths };
|
|
26549
|
+
return { state: "refreshed", paths: applied, missing: missing2, root, candidates: plan.paths };
|
|
26278
26550
|
}
|
|
26279
26551
|
function outsideBlock(text2) {
|
|
26280
26552
|
const normalized2 = text2.replace(/\r\n/g, "\n");
|
|
@@ -26320,14 +26592,14 @@ async function updateCommand(cwd, version2, env = process.env, controls = {}) {
|
|
|
26320
26592
|
refreshBlocks(cwd, version2, env, controls)
|
|
26321
26593
|
]);
|
|
26322
26594
|
const commit = controls.commit ? await commitBlocks(agentDocs, version2, env) : commitState("not-requested");
|
|
26323
|
-
return
|
|
26595
|
+
return updateV3.parse({
|
|
26324
26596
|
contract: "update",
|
|
26325
|
-
version:
|
|
26597
|
+
version: 3,
|
|
26326
26598
|
ok: true,
|
|
26327
26599
|
cliVersion: version2,
|
|
26328
26600
|
update,
|
|
26329
26601
|
install: update.status === "available" ? { npmGlobal: npmGlobalInstall(update.latestVersion) } : null,
|
|
26330
|
-
agentDocs: { state: agentDocs.state, paths: agentDocs.paths },
|
|
26602
|
+
agentDocs: { state: agentDocs.state, paths: agentDocs.paths, missing: agentDocs.missing },
|
|
26331
26603
|
commit
|
|
26332
26604
|
});
|
|
26333
26605
|
}
|
|
@@ -26343,6 +26615,7 @@ async function runUpdate(options, version2) {
|
|
|
26343
26615
|
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
26616
|
if (dto.install) lines.push(t2("update.text.install", { command: dto.install.npmGlobal }), t2("update.text.afterInstall"));
|
|
26345
26617
|
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"));
|
|
26618
|
+
if (dto.agentDocs.missing.length) lines.push(t2("update.text.missing", { paths: dto.agentDocs.missing.join(", ") }));
|
|
26346
26619
|
const { commit } = dto;
|
|
26347
26620
|
if (commit.state === "committed") lines.push(t2("update.text.committed", { commit: commit.commit.slice(0, 12), paths: commit.paths.join(", ") }));
|
|
26348
26621
|
else if (commit.state === "nothing") lines.push(t2("update.text.commitNothing"));
|
|
@@ -26355,18 +26628,18 @@ async function runUpdate(options, version2) {
|
|
|
26355
26628
|
} catch (error62) {
|
|
26356
26629
|
const known = error62 instanceof InitError || error62 instanceof RepositoryReadError;
|
|
26357
26630
|
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(
|
|
26631
|
+
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
26632
|
process.exitCode = 1;
|
|
26360
26633
|
}
|
|
26361
26634
|
}
|
|
26362
26635
|
|
|
26363
26636
|
// src/commands/spec-preview.ts
|
|
26364
|
-
import { join as
|
|
26637
|
+
import { join as join15 } from "node:path";
|
|
26365
26638
|
import { readFile as readFile12, stat as stat2 } from "node:fs/promises";
|
|
26366
26639
|
|
|
26367
26640
|
// src/adapters/filesystem/spec-preview-prepare.ts
|
|
26368
|
-
import { lstat as
|
|
26369
|
-
import { join as
|
|
26641
|
+
import { lstat as lstat6 } from "node:fs/promises";
|
|
26642
|
+
import { join as join10 } from "node:path";
|
|
26370
26643
|
import { createHash as createHash4 } from "node:crypto";
|
|
26371
26644
|
var fail5 = (message) => {
|
|
26372
26645
|
throw new SpecPreviewError(message);
|
|
@@ -26379,7 +26652,7 @@ async function readPreviewContext(cwd) {
|
|
|
26379
26652
|
const { gitDir } = await reader.location();
|
|
26380
26653
|
const checkOperation = async () => {
|
|
26381
26654
|
for (const name of ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply"]) {
|
|
26382
|
-
const exists = await
|
|
26655
|
+
const exists = await lstat6(join10(gitDir, name)).catch((e) => {
|
|
26383
26656
|
if (e.code === "ENOENT") return void 0;
|
|
26384
26657
|
throw e;
|
|
26385
26658
|
});
|
|
@@ -26433,9 +26706,9 @@ async function verifyPreparedPreview(cwd, input2, staged) {
|
|
|
26433
26706
|
if (staged) {
|
|
26434
26707
|
const index = await c.reader.index();
|
|
26435
26708
|
if (index.size !== current.files.size) fail5(t2("prepare.stagingScope"));
|
|
26436
|
-
for (const [path3,
|
|
26709
|
+
for (const [path3, source2] of current.files) {
|
|
26437
26710
|
const oid2 = index.get(path3);
|
|
26438
|
-
const bytes = Buffer.from(
|
|
26711
|
+
const bytes = Buffer.from(source2);
|
|
26439
26712
|
const hash3 = createHash4(oid2?.length === 64 ? "sha256" : "sha1").update(`blob ${bytes.length}\0`).update(bytes).digest("hex");
|
|
26440
26713
|
if (oid2 !== hash3) fail5(t2("prepare.stagingContent", { path: path3 }));
|
|
26441
26714
|
}
|
|
@@ -26448,26 +26721,26 @@ async function verifyPreparedPreview(cwd, input2, staged) {
|
|
|
26448
26721
|
|
|
26449
26722
|
// src/adapters/filesystem/agent-inputs.ts
|
|
26450
26723
|
import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
|
|
26451
|
-
import { lstat as
|
|
26724
|
+
import { lstat as lstat7, mkdir as mkdir5, readdir as readdir5, realpath as realpath3, unlink as unlink4, writeFile as writeFile3 } from "node:fs/promises";
|
|
26452
26725
|
import { tmpdir } from "node:os";
|
|
26453
|
-
import { basename, dirname as
|
|
26726
|
+
import { basename, dirname as dirname4, join as join11 } from "node:path";
|
|
26454
26727
|
var maxAge = 7 * 24 * 60 * 60 * 1e3;
|
|
26455
|
-
var
|
|
26728
|
+
var info4 = (path3) => lstat7(path3).catch((e) => {
|
|
26456
26729
|
if (e.code === "ENOENT") return void 0;
|
|
26457
26730
|
throw e;
|
|
26458
26731
|
});
|
|
26459
26732
|
var comparable = (path3) => process.platform === "win32" ? path3.toLowerCase() : path3;
|
|
26460
26733
|
async function candidates(root, controls) {
|
|
26461
26734
|
const key = createHash5("sha256").update(comparable(await realpath3(root))).digest("hex").slice(0, 16);
|
|
26462
|
-
return { system:
|
|
26735
|
+
return { system: join11((controls.tmpdir ?? tmpdir)(), "gitifact", key), project: join11(root, ".gitifact", "tmp") };
|
|
26463
26736
|
}
|
|
26464
26737
|
async function writable(directory) {
|
|
26465
26738
|
try {
|
|
26466
|
-
await
|
|
26467
|
-
const stat3 = await
|
|
26739
|
+
await mkdir5(directory, { recursive: true });
|
|
26740
|
+
const stat3 = await info4(directory);
|
|
26468
26741
|
if (!stat3?.isDirectory() || stat3.isSymbolicLink()) return false;
|
|
26469
|
-
const probe =
|
|
26470
|
-
await
|
|
26742
|
+
const probe = join11(directory, ".probe-" + randomBytes2(8).toString("hex"));
|
|
26743
|
+
await writeFile3(probe, "", { flag: "wx" });
|
|
26471
26744
|
await unlink4(probe);
|
|
26472
26745
|
return true;
|
|
26473
26746
|
} catch {
|
|
@@ -26480,28 +26753,28 @@ async function prepareAgentInputs(root, controls = {}) {
|
|
|
26480
26753
|
if (await writable(system)) directory = system;
|
|
26481
26754
|
else if (await writable(project)) {
|
|
26482
26755
|
directory = project;
|
|
26483
|
-
await
|
|
26756
|
+
await writeFile3(join11(project, ".gitignore"), "*\n", { flag: "wx" }).catch((e) => {
|
|
26484
26757
|
if (e.code !== "EEXIST") throw e;
|
|
26485
26758
|
});
|
|
26486
26759
|
}
|
|
26487
26760
|
if (!directory) return void 0;
|
|
26488
26761
|
const now = (controls.now ?? Date.now)();
|
|
26489
|
-
for (const name of await
|
|
26762
|
+
for (const name of await readdir5(directory).catch(() => [])) {
|
|
26490
26763
|
if (name === ".gitignore") continue;
|
|
26491
|
-
const stat3 = await
|
|
26492
|
-
if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await unlink4(
|
|
26764
|
+
const stat3 = await info4(join11(directory, name)).catch(() => void 0);
|
|
26765
|
+
if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await unlink4(join11(directory, name)).catch(() => void 0);
|
|
26493
26766
|
}
|
|
26494
|
-
return { save:
|
|
26767
|
+
return { save: join11(directory, "save.json"), commit: join11(directory, "commit.json") };
|
|
26495
26768
|
}
|
|
26496
26769
|
async function discardAgentInput(root, file2, controls = {}) {
|
|
26497
26770
|
try {
|
|
26498
|
-
const stat3 = await
|
|
26771
|
+
const stat3 = await info4(file2);
|
|
26499
26772
|
if (!stat3?.isFile() || stat3.isSymbolicLink()) return false;
|
|
26500
|
-
const parent = comparable(await realpath3(
|
|
26773
|
+
const parent = comparable(await realpath3(dirname4(file2)));
|
|
26501
26774
|
const { system, project } = await candidates(root, controls);
|
|
26502
26775
|
let managed = false;
|
|
26503
26776
|
for (const directory of [system, project]) {
|
|
26504
|
-
if (await
|
|
26777
|
+
if (await info4(directory) && comparable(await realpath3(directory)) === parent) managed = true;
|
|
26505
26778
|
}
|
|
26506
26779
|
if (!managed || basename(file2) === ".gitignore") return false;
|
|
26507
26780
|
await unlink4(file2);
|
|
@@ -26513,13 +26786,13 @@ async function discardAgentInput(root, file2, controls = {}) {
|
|
|
26513
26786
|
|
|
26514
26787
|
// src/commands/spec-preview-commit.ts
|
|
26515
26788
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
26516
|
-
import { readFile as readFile10, mkdir as
|
|
26517
|
-
import { join as
|
|
26789
|
+
import { readFile as readFile10, mkdir as mkdir6, open as open4, rename as rename3, unlink as unlink5, rmdir as rmdir2, writeFile as writeFile4 } from "node:fs/promises";
|
|
26790
|
+
import { join as join13 } from "node:path";
|
|
26518
26791
|
|
|
26519
26792
|
// src/commands/spec-commit-files.ts
|
|
26520
26793
|
import { createHash as createHash6 } from "node:crypto";
|
|
26521
|
-
import { lstat as
|
|
26522
|
-
import { join as
|
|
26794
|
+
import { lstat as lstat8, readFile as readFile9 } from "node:fs/promises";
|
|
26795
|
+
import { join as join12 } from "node:path";
|
|
26523
26796
|
var fail6 = (message) => {
|
|
26524
26797
|
throw new SpecPreviewError(message);
|
|
26525
26798
|
};
|
|
@@ -26528,7 +26801,7 @@ var optional2 = (path3) => readFile9(path3).catch((e) => {
|
|
|
26528
26801
|
if (e.code === "ENOENT") return null;
|
|
26529
26802
|
throw e;
|
|
26530
26803
|
});
|
|
26531
|
-
var
|
|
26804
|
+
var info5 = (path3) => lstat8(path3).catch((e) => {
|
|
26532
26805
|
if (e.code === "ENOENT") return void 0;
|
|
26533
26806
|
throw e;
|
|
26534
26807
|
});
|
|
@@ -26540,11 +26813,11 @@ async function fingerprint(root, path3) {
|
|
|
26540
26813
|
validPath2(path3);
|
|
26541
26814
|
const parts = path3.split("/");
|
|
26542
26815
|
for (let i = 1; i <= parts.length; i++) {
|
|
26543
|
-
const target =
|
|
26544
|
-
const stat3 = await
|
|
26816
|
+
const target = join12(root, ...parts.slice(0, i));
|
|
26817
|
+
const stat3 = await info5(target);
|
|
26545
26818
|
if (!stat3) return null;
|
|
26546
26819
|
if (stat3.isSymbolicLink() || (i < parts.length ? !stat3.isDirectory() : !stat3.isFile())) fail6(t2("commitFiles.linkOrDirectory"));
|
|
26547
|
-
if (i < parts.length && await
|
|
26820
|
+
if (i < parts.length && await info5(join12(target, ".git"))) fail6(t2("commitFiles.nestedRepository"));
|
|
26548
26821
|
if (i === parts.length) {
|
|
26549
26822
|
if (stat3.nlink !== 1 || stat3.size > 16 * 1024 * 1024) fail6(t2("commitFiles.hardLinkOrSize"));
|
|
26550
26823
|
return hash2(Buffer.concat([Buffer.from(String(stat3.mode & 73) + "\0"), await readFile9(target)]));
|
|
@@ -26575,9 +26848,9 @@ function policyPaths(files) {
|
|
|
26575
26848
|
return [...all].sort();
|
|
26576
26849
|
}
|
|
26577
26850
|
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)
|
|
26851
|
+
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
26852
|
for (const p of selected.filter((p2) => p2.startsWith(".gitifact/") && !record2(p2) && p2 !== ".gitifact/config.json")) {
|
|
26580
|
-
if (
|
|
26853
|
+
if (isAssetPath(p)) continue;
|
|
26581
26854
|
if (!legacyJson.test(p) || await fingerprint(root, p) !== null) fail6(t2("commitFiles.legacyDeleteOnly", { path: p }));
|
|
26582
26855
|
}
|
|
26583
26856
|
for (const p of selected.filter((p2) => p2.startsWith(".tryce/"))) {
|
|
@@ -26610,8 +26883,8 @@ async function previewCommit(cwd, action, input2) {
|
|
|
26610
26883
|
});
|
|
26611
26884
|
const indexPath = (await git(["rev-parse", "--path-format=absolute", "--git-path", "index"])).toString("utf8").trim();
|
|
26612
26885
|
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
|
|
26886
|
+
const busy = join13(gitDir, "gitifact-spec-commit.lock");
|
|
26887
|
+
if (await info5(busy)) fail6(t2("commit.busy", { path: busy }));
|
|
26615
26888
|
if ((await staged()).length) fail6(t2("commit.existingStagingPlan"));
|
|
26616
26889
|
const request = object3(input2);
|
|
26617
26890
|
let plan;
|
|
@@ -26668,33 +26941,33 @@ async function previewCommit(cwd, action, input2) {
|
|
|
26668
26941
|
if (!("plan" in refreshed) || refreshed.plan.digest !== plan.digest) fail6(t2("commit.planStale"));
|
|
26669
26942
|
const before = await reader.baseline();
|
|
26670
26943
|
const original = await optional2(indexPath);
|
|
26671
|
-
await
|
|
26672
|
-
const draftLock =
|
|
26944
|
+
await mkdir6(busy);
|
|
26945
|
+
const draftLock = join13(gitDir, "gitifact-spec-preview.lock");
|
|
26673
26946
|
let draftOwned = false;
|
|
26674
26947
|
let indexLock;
|
|
26675
26948
|
let uncertain = false;
|
|
26676
26949
|
let commitStarted = false;
|
|
26677
|
-
const temporary =
|
|
26950
|
+
const temporary = join13(gitDir, "gitifact-commit-index-" + randomUUID6());
|
|
26678
26951
|
const checkFiles = async () => {
|
|
26679
26952
|
for (const f of [...plan.files, ...plan.context]) if (f.hash !== await fingerprint(root, f.path)) fail6(t2("commit.fileChangedPreparing", { path: f.path }));
|
|
26680
26953
|
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
|
|
26954
|
+
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
26955
|
};
|
|
26683
26956
|
try {
|
|
26684
26957
|
await verifyPreparedPreview(cwd, plan.verification, false);
|
|
26685
|
-
await
|
|
26958
|
+
await mkdir6(draftLock);
|
|
26686
26959
|
draftOwned = true;
|
|
26687
26960
|
indexLock = await open4(indexPath + ".lock", "wx", 384);
|
|
26688
26961
|
if (plan.index !== hash2(await optional2(indexPath) ?? Buffer.alloc(0)) || (await staged()).length) fail6(t2("commit.indexChangedLocked"));
|
|
26689
|
-
await
|
|
26690
|
-
if (original) await
|
|
26962
|
+
await writeFile4(join13(busy, "recovery.json"), JSON.stringify({ before, originalIndex: original?.toString("base64") ?? null, temporary, plan }));
|
|
26963
|
+
if (original) await writeFile4(temporary, original, { flag: "wx" });
|
|
26691
26964
|
else await git(["read-tree", "--empty"], temporary);
|
|
26692
26965
|
await git(["add", "--", ...selected], temporary);
|
|
26693
26966
|
const actual = await staged(temporary);
|
|
26694
26967
|
if (!actual.length || actual.some((p) => !selected.includes(p))) fail6(t2("commit.unexpectedFiles"));
|
|
26695
26968
|
for (const file2 of plan.files.filter((f) => record2(f.path) && f.hash !== null)) {
|
|
26696
26969
|
const stagedBytes = await git(["show", ":" + file2.path], temporary);
|
|
26697
|
-
if (!stagedBytes.equals(await readFile10(
|
|
26970
|
+
if (!stagedBytes.equals(await readFile10(join13(root, file2.path)))) fail6(t2("commit.filterChanged"));
|
|
26698
26971
|
}
|
|
26699
26972
|
await checkFiles();
|
|
26700
26973
|
if (JSON.stringify(before) !== JSON.stringify(await reader.baseline())) fail6(t2("commit.headChanged"));
|
|
@@ -26733,7 +27006,7 @@ async function previewCommit(cwd, action, input2) {
|
|
|
26733
27006
|
});
|
|
26734
27007
|
await unlink5(temporary).catch(() => {
|
|
26735
27008
|
});
|
|
26736
|
-
await unlink5(
|
|
27009
|
+
await unlink5(join13(busy, "recovery.json")).catch(() => {
|
|
26737
27010
|
});
|
|
26738
27011
|
await rmdir2(busy);
|
|
26739
27012
|
if (draftOwned) await rmdir2(draftLock);
|
|
@@ -26743,8 +27016,8 @@ async function previewCommit(cwd, action, input2) {
|
|
|
26743
27016
|
|
|
26744
27017
|
// src/commands/spec-commit.ts
|
|
26745
27018
|
import { createHash as createHash8, randomUUID as randomUUID7 } from "node:crypto";
|
|
26746
|
-
import { mkdir as
|
|
26747
|
-
import { join as
|
|
27019
|
+
import { mkdir as mkdir7, open as open5, readFile as readFile11, rename as rename4, rmdir as rmdir3, unlink as unlink6, writeFile as writeFile5 } from "node:fs/promises";
|
|
27020
|
+
import { join as join14 } from "node:path";
|
|
26748
27021
|
var fields = ["reasons", "paths", "message", "authorization", "requirements", "policyFiles", "expected"];
|
|
26749
27022
|
var integration = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply"];
|
|
26750
27023
|
var same2 = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
@@ -26783,9 +27056,9 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26783
27056
|
...stdin ? { input: stdin } : {}
|
|
26784
27057
|
});
|
|
26785
27058
|
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 =
|
|
27059
|
+
const busy = join14(gitDir, "gitifact-spec-commit.lock");
|
|
26787
27060
|
await failOnLegacyLock(gitDir);
|
|
26788
|
-
if (await
|
|
27061
|
+
if (await info5(busy)) fail6(t2("commit.busy", { path: busy }));
|
|
26789
27062
|
if ((await staged()).length) fail6(t2("commit.existingStaging"));
|
|
26790
27063
|
const indexHash = hash2(await optional2(indexPath) ?? Buffer.alloc(0));
|
|
26791
27064
|
const current = c.working;
|
|
@@ -26835,16 +27108,16 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26835
27108
|
const trailers = [
|
|
26836
27109
|
...requirements.map((id) => "Gitifact-Req: " + id),
|
|
26837
27110
|
...prepared.changes.filter((c2) => c2.kind === "design").map((c2) => "Gitifact-Design: " + c2.id),
|
|
26838
|
-
...prepared.changes.filter((c2) => c2.kind === "
|
|
27111
|
+
...prepared.changes.filter((c2) => c2.kind === "wiki").map((c2) => "Gitifact-Doc: " + c2.id)
|
|
26839
27112
|
];
|
|
26840
|
-
const temporary =
|
|
27113
|
+
const temporary = join14(gitDir, "gitifact-commit-index-" + randomUUID7());
|
|
26841
27114
|
let owned = false;
|
|
26842
27115
|
let uncertain = false;
|
|
26843
27116
|
let indexLock;
|
|
26844
27117
|
let committed;
|
|
26845
27118
|
try {
|
|
26846
27119
|
try {
|
|
26847
|
-
await
|
|
27120
|
+
await mkdir7(busy);
|
|
26848
27121
|
owned = true;
|
|
26849
27122
|
} catch (e) {
|
|
26850
27123
|
if (e.code === "EEXIST") fail6(t2("commit.busy", { path: busy }));
|
|
@@ -26858,8 +27131,8 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26858
27131
|
if (hash2(original ?? Buffer.alloc(0)) !== indexHash || (await staged()).length) fail6(t2("commit.indexChanged"));
|
|
26859
27132
|
const locked = await hashes([.../* @__PURE__ */ new Set([...selected, ...contextPaths])]);
|
|
26860
27133
|
for (const [path3, value] of started) if (locked.get(path3) !== value) fail6(t2("commit.fileChangedRunning", { path: path3 }));
|
|
26861
|
-
await
|
|
26862
|
-
if (original) await
|
|
27134
|
+
await writeFile5(join14(busy, "recovery.json"), JSON.stringify({ before: c.base, originalIndex: original?.toString("base64") ?? null, temporary, paths: selected, message, requirements }));
|
|
27135
|
+
if (original) await writeFile5(temporary, original, { flag: "wx" });
|
|
26863
27136
|
else await git(["read-tree", "--empty"], temporary);
|
|
26864
27137
|
await git(["add", "--", ...stageable], temporary);
|
|
26865
27138
|
const actual = await staged(temporary);
|
|
@@ -26874,7 +27147,7 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26874
27147
|
const size = Number(header[2]);
|
|
26875
27148
|
if (end < 0 || header[1] !== "blob" || !Number.isSafeInteger(size) || output2[end + 1 + size] !== 10) fail6(t2("commit.stagedUnreadable", { path: path3 }));
|
|
26876
27149
|
const staged2 = output2.subarray(end + 1, end + 1 + size);
|
|
26877
|
-
const file2 = await readFile11(
|
|
27150
|
+
const file2 = await readFile11(join14(root, path3));
|
|
26878
27151
|
if (!staged2.equals(file2) && !staged2.equals(Buffer.from(file2.toString("latin1").replace(/\r\n/g, "\n"), "latin1"))) fail6(t2("commit.filterChanged"));
|
|
26879
27152
|
offset = end + size + 2;
|
|
26880
27153
|
}
|
|
@@ -26882,7 +27155,7 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26882
27155
|
const again = await hashes([...locked.keys()]);
|
|
26883
27156
|
for (const [path3, value] of locked) if (again.get(path3) !== value) fail6(t2("commit.fileChangedPreparing", { path: path3 }));
|
|
26884
27157
|
if ((await readLockedPreviewState(root)).stamp !== published.stamp) fail6(t2("commit.specsChangedPreparing"));
|
|
26885
|
-
for (const name of integration) if (await
|
|
27158
|
+
for (const name of integration) if (await info5(join14(gitDir, name))) fail6(t2("commit.integrationStarted"));
|
|
26886
27159
|
if (!same2(c.base, await c.reader.baseline())) fail6(t2("commit.headChanged"));
|
|
26887
27160
|
const tree = (await git(["write-tree"], temporary)).toString("utf8").trim();
|
|
26888
27161
|
commitStarted = true;
|
|
@@ -26924,7 +27197,7 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26924
27197
|
await unlink6(temporary).catch(() => {
|
|
26925
27198
|
});
|
|
26926
27199
|
if (owned) {
|
|
26927
|
-
await unlink6(
|
|
27200
|
+
await unlink6(join14(busy, "recovery.json")).catch(() => {
|
|
26928
27201
|
});
|
|
26929
27202
|
await rmdir3(busy);
|
|
26930
27203
|
}
|
|
@@ -26941,7 +27214,7 @@ async function execute(action, options, controls) {
|
|
|
26941
27214
|
try {
|
|
26942
27215
|
const guard = specPreviewReader(process.cwd());
|
|
26943
27216
|
const { root, gitDir, objectFormat } = await guard.location();
|
|
26944
|
-
for (const marker2 of operations) if (await fileInfo(
|
|
27217
|
+
for (const marker2 of operations) if (await fileInfo(join15(gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
|
|
26945
27218
|
if (await guard.hasUnmerged()) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
26946
27219
|
const raw = await readConfigFile(root);
|
|
26947
27220
|
if (raw === void 0) throw new SpecPreviewError(t2("preview.notInitialized"));
|
|
@@ -26973,7 +27246,7 @@ async function execute(action, options, controls) {
|
|
|
26973
27246
|
if (action === "read") {
|
|
26974
27247
|
const commit = await reader.resolve(options.ref ?? "HEAD");
|
|
26975
27248
|
const bundle = await reader.readBundle(commit);
|
|
26976
|
-
process.stdout.write(JSON.stringify({ ...envelope, ok: true, commit, specs: bundle.specs,
|
|
27249
|
+
process.stdout.write(JSON.stringify({ ...envelope, ok: true, commit, specs: bundle.specs, wiki: bundle.wiki }) + "\n");
|
|
26977
27250
|
} else {
|
|
26978
27251
|
const [from, to] = await Promise.all([reader.resolve(options.from), reader.resolve(options.to)]);
|
|
26979
27252
|
const [before, after] = await Promise.all([reader.readBundle(from), reader.readBundle(to)]);
|
|
@@ -26986,8 +27259,8 @@ async function execute(action, options, controls) {
|
|
|
26986
27259
|
}
|
|
26987
27260
|
}
|
|
26988
27261
|
async function readInputFile(path3) {
|
|
26989
|
-
const
|
|
26990
|
-
if (!
|
|
27262
|
+
const source2 = await stat2(path3);
|
|
27263
|
+
if (!source2.isFile() || source2.size > 1024 * 1024) throw new SpecPreviewError(t2("preview.inputFile"));
|
|
26991
27264
|
return readFile12(path3);
|
|
26992
27265
|
}
|
|
26993
27266
|
async function readStdin(stream) {
|
|
@@ -27002,29 +27275,29 @@ async function readStdin(stream) {
|
|
|
27002
27275
|
}
|
|
27003
27276
|
function narrowWorking(working, options, inputs) {
|
|
27004
27277
|
if (options.stamp) return { stamp: working.stamp, inputs };
|
|
27005
|
-
let { specs,
|
|
27278
|
+
let { specs, wiki, warnings } = working;
|
|
27006
27279
|
if (options.feature !== void 0) {
|
|
27007
27280
|
specs = specs.filter((s) => s.path === ".gitifact/spec/" + options.feature + "/requirements.md");
|
|
27008
27281
|
if (!specs.length) throw new SpecPreviewError(t2("preview.unknownFeature", { feature: options.feature }));
|
|
27009
|
-
|
|
27010
|
-
warnings = warnings.filter((w) => w.specId === specs[0].id);
|
|
27282
|
+
wiki = { documents: [], history: [] };
|
|
27283
|
+
warnings = warnings.filter((w) => w.code === "MISSING_DESIGN_REFERENCE" && w.specId === specs[0].id);
|
|
27011
27284
|
}
|
|
27012
|
-
if (!options.ids) return { stamp: working.stamp, inputs, specs,
|
|
27285
|
+
if (!options.ids) return { stamp: working.stamp, inputs, specs, wiki, warnings };
|
|
27013
27286
|
return {
|
|
27014
27287
|
stamp: working.stamp,
|
|
27015
27288
|
inputs,
|
|
27016
27289
|
warnings,
|
|
27017
27290
|
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
|
-
|
|
27291
|
+
wiki: { documents: wiki.documents.map((d) => ({ id: d.id, path: d.path, title: d.title })) }
|
|
27019
27292
|
};
|
|
27020
27293
|
}
|
|
27021
27294
|
|
|
27022
27295
|
// src/commands/migrate.ts
|
|
27023
|
-
import { lstat as
|
|
27024
|
-
import { join as
|
|
27296
|
+
import { lstat as lstat10, readdir as readdir6, readFile as readFile13, rename as rename5, writeFile as writeFile6, unlink as unlink7 } from "node:fs/promises";
|
|
27297
|
+
import { join as join16 } from "node:path";
|
|
27025
27298
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
27026
27299
|
var operations2 = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"];
|
|
27027
|
-
var
|
|
27300
|
+
var info6 = (path3) => lstat10(path3).catch((e) => {
|
|
27028
27301
|
if (e.code === "ENOENT") return void 0;
|
|
27029
27302
|
throw e;
|
|
27030
27303
|
});
|
|
@@ -27035,40 +27308,40 @@ var fail7 = (code, message) => {
|
|
|
27035
27308
|
async function migrateStore(cwd, dryRun = false) {
|
|
27036
27309
|
const reader = specPreviewReader(cwd);
|
|
27037
27310
|
const { root, gitDir } = await reader.location();
|
|
27038
|
-
for (const marker3 of operations2) if (await
|
|
27311
|
+
for (const marker3 of operations2) if (await info6(join16(gitDir, marker3))) fail7("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker3 }));
|
|
27039
27312
|
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
|
|
27313
|
+
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) }));
|
|
27314
|
+
const from = join16(root, ".tryce");
|
|
27315
|
+
const to = join16(root, ".gitifact");
|
|
27316
|
+
const fromInfo = await info6(from);
|
|
27317
|
+
if (await info6(to)) fail7("ALREADY_MIGRATED", t2("migrate.alreadyMigrated"));
|
|
27045
27318
|
if (!fromInfo) return fail7("NOTHING_TO_MIGRATE", t2("migrate.nothingToMigrate"));
|
|
27046
27319
|
if (!fromInfo.isDirectory() || fromInfo.isSymbolicLink()) fail7("PATH_CONFLICT", t2("migrate.notDirectory"));
|
|
27047
|
-
const configInfo = await
|
|
27320
|
+
const configInfo = await info6(join16(from, "config.json"));
|
|
27048
27321
|
if (!configInfo?.isFile() || configInfo.isSymbolicLink()) fail7("NOT_INITIALIZED", t2("migrate.noConfig"));
|
|
27049
|
-
const config2 = parseManagedConfig(decode4(await readFile13(
|
|
27322
|
+
const config2 = parseManagedConfig(decode4(await readFile13(join16(from, "config.json"))));
|
|
27050
27323
|
if (!("schemaVersion" in config2)) fail7("UNSUPPORTED_FORMAT", t2("migrate.legacyJson"));
|
|
27051
|
-
const top = (await
|
|
27324
|
+
const top = (await readdir6(from)).sort();
|
|
27052
27325
|
if (top.some((name) => name !== "config.json" && name !== "spec")) fail7("UNSUPPORTED_FORMAT", t2("migrate.extraEntries"));
|
|
27053
27326
|
const files = /* @__PURE__ */ new Map();
|
|
27054
27327
|
const rewrites = [];
|
|
27055
27328
|
const legacyRecords = [];
|
|
27056
27329
|
const marker2 = /^<!-- tryce-(spec|req|design|ref): /;
|
|
27057
27330
|
if (top.includes("spec")) {
|
|
27058
|
-
for (const feature2 of (await
|
|
27059
|
-
const dir =
|
|
27060
|
-
const stat3 = await
|
|
27331
|
+
for (const feature2 of (await readdir6(join16(from, "spec"))).sort()) {
|
|
27332
|
+
const dir = join16(from, "spec", feature2);
|
|
27333
|
+
const stat3 = await info6(dir);
|
|
27061
27334
|
if (!stat3?.isDirectory() || stat3.isSymbolicLink()) fail7("UNSUPPORTED_FORMAT", t2("migrate.notSpecFolder", { feature: feature2 }));
|
|
27062
|
-
for (const name of (await
|
|
27335
|
+
for (const name of (await readdir6(dir)).sort()) {
|
|
27063
27336
|
if (!["requirements.md", "design.md", "history.jsonl", "tryce.json"].includes(name)) fail7("UNSUPPORTED_FORMAT", t2("migrate.unsupportedFile", { feature: feature2, name }));
|
|
27064
|
-
const fileStat = await
|
|
27337
|
+
const fileStat = await info6(join16(dir, name));
|
|
27065
27338
|
if (!fileStat?.isFile() || fileStat.isSymbolicLink() || fileStat.nlink !== 1 || fileStat.size > 1024 * 1024) fail7("UNSUPPORTED_FORMAT", t2("migrate.fileTooLarge", { name }));
|
|
27066
27339
|
const newPath = `.gitifact/spec/${feature2}/${name}`;
|
|
27067
27340
|
if (name === "tryce.json") {
|
|
27068
27341
|
legacyRecords.push(newPath);
|
|
27069
27342
|
continue;
|
|
27070
27343
|
}
|
|
27071
|
-
const text2 = decode4(await readFile13(
|
|
27344
|
+
const text2 = decode4(await readFile13(join16(dir, name)));
|
|
27072
27345
|
if (name === "history.jsonl") {
|
|
27073
27346
|
files.set(newPath, text2);
|
|
27074
27347
|
continue;
|
|
@@ -27113,9 +27386,9 @@ async function migrateStore(cwd, dryRun = false) {
|
|
|
27113
27386
|
if (dryRun) return result("planned");
|
|
27114
27387
|
await rename5(from, to);
|
|
27115
27388
|
for (const r of rewrites) {
|
|
27116
|
-
const full =
|
|
27117
|
-
const temp =
|
|
27118
|
-
await
|
|
27389
|
+
const full = join16(root, r.path);
|
|
27390
|
+
const temp = join16(root, ".gitifact", "spec", ".gitifact-migrate-" + randomBytes3(8).toString("hex"));
|
|
27391
|
+
await writeFile6(temp, r.content, { flag: "wx" });
|
|
27119
27392
|
try {
|
|
27120
27393
|
await rename5(temp, full);
|
|
27121
27394
|
} finally {
|
|
@@ -27137,16 +27410,16 @@ async function runMigrate(options) {
|
|
|
27137
27410
|
}
|
|
27138
27411
|
|
|
27139
27412
|
// src/main.ts
|
|
27140
|
-
var program2 = new Command().name("gitifact").description(t2("help.program")).version("0.
|
|
27413
|
+
var program2 = new Command().name("gitifact").description(t2("help.program")).version("0.5.0").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
|
|
27141
27414
|
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
27415
|
await runStatus(options.format);
|
|
27143
27416
|
});
|
|
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.
|
|
27417
|
+
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.0"));
|
|
27418
|
+
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.0"));
|
|
27146
27419
|
program2.command("docs").description(t2("help.docs")).argument("[topic]", t2("help.docsTopic")).allowExcessArguments(false).action(async (topic) => {
|
|
27147
27420
|
await runDocs(topic);
|
|
27148
27421
|
});
|
|
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.
|
|
27422
|
+
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.0"));
|
|
27150
27423
|
program2.command("migrate").description(t2("help.migrate")).allowExcessArguments(false).option("--dry-run", t2("help.migrateDryRun")).action(runMigrate);
|
|
27151
27424
|
var spec = program2.command("spec").description(t2("help.spec"));
|
|
27152
27425
|
var replaced = t2("help.deprecated");
|