gitifact 0.4.3 → 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 +10 -6
- package/dist/browser/assets/{Markdown-GTI3nRCA.js → Markdown-C8C4nHPG.js} +3 -3
- package/dist/browser/assets/{MetadataListItem-c6t7SM9J.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-BWWTOV27.js → activity-B7x648pR.js} +1 -1
- package/dist/browser/assets/changelog-DaxXahXm.js +2 -0
- package/dist/browser/assets/{contributors._email-DUoflTLI.js → contributors._email-BZevZqRL.js} +1 -1
- package/dist/browser/assets/{contributors.index-DfuU03rG.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-QHPB6OmC.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-PaoXpYJM.js → requirements-wk9Joy7M.js} +1 -1
- package/dist/browser/assets/{settings-DN1DDBYj.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 +27 -13
- package/dist/i18n/ko/changelog.md +30 -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 +21 -11
- package/dist/main.js +810 -439
- package/package.json +1 -1
- package/dist/browser/assets/Table-BJ_vfcZE.js +0 -3
- package/dist/browser/assets/about-5hmfP7Oq.js +0 -3
- package/dist/browser/assets/changelog-jpn_8GXM.js +0 -2
- package/dist/browser/assets/document-D41OCO3e.css +0 -1
- package/dist/browser/assets/document-DeNuHzWM.js +0 -1
- package/dist/browser/assets/features._featureId-mcSjZyhs.js +0 -1
- package/dist/browser/assets/git-Cj31Qjk8.js +0 -1
- package/dist/browser/assets/guides._documentId-BuOa1m0p.js +0 -1
- package/dist/browser/assets/guides.index-BNK_skIs.js +0 -1
- package/dist/browser/assets/index-CdO3EM0n.js +0 -56
- package/dist/browser/assets/page-header-CBWpQAOT.js +0 -126
- package/dist/browser/assets/product-BrvAWtBo.css +0 -1
- package/dist/browser/assets/product-DZ2_YsMy.js +0 -8
- package/dist/browser/assets/product.document-C5VnB7EK.js +0 -1
- package/dist/browser/assets/product.index-DybzHICn.js +0 -1
- package/dist/browser/assets/request-state-CtVGSFDZ.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,11 +24696,20 @@ var browserSessionV2 = external_exports.strictObject({
|
|
|
24563
24696
|
update: updateStateV1
|
|
24564
24697
|
});
|
|
24565
24698
|
|
|
24566
|
-
// ../../packages/contracts/dist/versions/update-
|
|
24567
|
-
var
|
|
24699
|
+
// ../../packages/contracts/dist/versions/update-v3.js
|
|
24700
|
+
var commitReason = external_exports.enum(["untracked", "other-changes", "commit-failed"]);
|
|
24701
|
+
var agentDocsCommit = external_exports.strictObject({
|
|
24702
|
+
state: external_exports.enum(["not-requested", "nothing", "committed", "skipped"]),
|
|
24703
|
+
commit: external_exports.string().nullable(),
|
|
24704
|
+
paths: external_exports.array(external_exports.string()),
|
|
24705
|
+
message: external_exports.string().nullable(),
|
|
24706
|
+
reason: commitReason.nullable(),
|
|
24707
|
+
detail: external_exports.string().nullable()
|
|
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.");
|
|
24709
|
+
var updateV3 = external_exports.discriminatedUnion("ok", [
|
|
24568
24710
|
external_exports.strictObject({
|
|
24569
24711
|
contract: external_exports.literal("update"),
|
|
24570
|
-
version: external_exports.literal(
|
|
24712
|
+
version: external_exports.literal(3),
|
|
24571
24713
|
ok: external_exports.literal(true),
|
|
24572
24714
|
cliVersion: external_exports.string().min(1),
|
|
24573
24715
|
update: updateStateV1,
|
|
@@ -24575,9 +24717,11 @@ var updateV1 = external_exports.discriminatedUnion("ok", [
|
|
|
24575
24717
|
install: external_exports.strictObject({ npmGlobal: external_exports.string().min(1) }).nullable(),
|
|
24576
24718
|
// refreshed: blocks whose version line differed were rewritten. current: every block already matched.
|
|
24577
24719
|
// not-initialized: no .gitifact config here, so only the version check ran. no-block: no file carries a block.
|
|
24578
|
-
|
|
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()) }),
|
|
24722
|
+
commit: agentDocsCommit
|
|
24579
24723
|
}),
|
|
24580
|
-
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() }) })
|
|
24581
24725
|
]);
|
|
24582
24726
|
|
|
24583
24727
|
// src/shared/i18n/ko/messages.json
|
|
@@ -24595,7 +24739,7 @@ var messages_default2 = {
|
|
|
24595
24739
|
"help.initRemoveAgents": "GITIFACT \uBE14\uB85D\uC744 \uC81C\uAC70",
|
|
24596
24740
|
"help.initSkipAgents": "\uC9C0\uCE68 \uD30C\uC77C\uC744 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uC74C",
|
|
24597
24741
|
"help.docs": "\uC5D0\uC774\uC804\uD2B8\uC6A9 \uC791\uC5C5 \uC9C0\uCE68 \uCD9C\uB825 (\uC8FC\uC81C \uC5C6\uC774 \uC2E4\uD589\uD558\uBA74 \uBAA9\uB85D)",
|
|
24598
|
-
"help.docsTopic": "workflow, spec, design,
|
|
24742
|
+
"help.docsTopic": "workflow, spec, design, wiki, commit \uC911 \uD558\uB098",
|
|
24599
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)",
|
|
24600
24744
|
"help.migrateDryRun": "\uD30C\uC77C\uC744 \uBC14\uAFB8\uC9C0 \uC54A\uACE0 \uC804\uD658 \uACC4\uD68D \uD655\uC778",
|
|
24601
24745
|
"help.spec": "Markdown \uBA85\uC138 \uC791\uC131\xB7\uC870\uD68C\xB7Git \uAE30\uB85D",
|
|
@@ -24612,11 +24756,11 @@ var messages_default2 = {
|
|
|
24612
24756
|
"help.specDiffFrom": "\uC774\uC804 \uCEE4\uBC0B",
|
|
24613
24757
|
"help.specDiffTo": "\uC774\uD6C4 \uCEE4\uBC0B",
|
|
24614
24758
|
"help.specChanges": "HEAD \uB300\uBE44 \uCD5C\uC885 \uBA85\uC138 \uCC28\uC774\uC640 \uBBF8\uCEE4\uBC0B \uC774\uC720 \uC870\uD68C",
|
|
24615
|
-
"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",
|
|
24616
24760
|
"help.specWorkingStamp": "stamp\uC640 \uC785\uB825 \uD30C\uC77C \uACBD\uB85C\uB9CC \uCD9C\uB825",
|
|
24617
|
-
"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)",
|
|
24618
24762
|
"help.specWorkingIds": "\uBCF8\uBB38 \uC5C6\uC774 ID\xB7\uC81C\uBAA9\xB7\uACBD\uB85C\uB9CC \uCD9C\uB825",
|
|
24619
|
-
"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",
|
|
24620
24764
|
"help.specRead": "\uCEE4\uBC0B \uC2DC\uC810\uC758 \uBA85\uC138 \uC870\uD68C",
|
|
24621
24765
|
"help.specDiff": "\uB450 \uCEE4\uBC0B \uC0AC\uC774\uC758 \uBA85\uC138 \uBCC0\uACBD \uC870\uD68C",
|
|
24622
24766
|
"git.operationInProgress": "Git \uC791\uC5C5\uC774 \uC9C4\uD589 \uC911\uC785\uB2C8\uB2E4: {marker}",
|
|
@@ -24712,7 +24856,6 @@ var messages_default2 = {
|
|
|
24712
24856
|
"server.crossSite": "\uC678\uBD80 \uD398\uC774\uC9C0\uC758 \uC694\uCCAD\uC744 \uD5C8\uC6A9\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
24713
24857
|
"server.noQuery": "\uC870\uD68C \uC778\uC790\uB97C \uBC1B\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
24714
24858
|
"server.methodNotAllowed": "\uD5C8\uC6A9\uD558\uC9C0 \uC54A\uB294 \uBA54\uC11C\uB4DC\uC785\uB2C8\uB2E4.",
|
|
24715
|
-
"server.productImageNotFound": "\uC81C\uD488 \uC774\uBBF8\uC9C0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
24716
24859
|
"server.apiNotFound": "API\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
24717
24860
|
"server.sessionChanged": "\uC11C\uBC84 \uC138\uC158\uC774 \uBCC0\uACBD\uB410\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC5F0\uACB0\uD558\uC138\uC694.",
|
|
24718
24861
|
"server.badRange": "\uC870\uD68C \uBC94\uC704\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
@@ -24810,10 +24953,10 @@ var messages_default2 = {
|
|
|
24810
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.",
|
|
24811
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",
|
|
24812
24955
|
"docs.summary.spec": "\uC0AC\uC6A9\uC790 \uC2A4\uD1A0\uB9AC\xB7\uC218\uC6A9 \uC870\uAC74 \uD615\uC2DD, ID \uADDC\uCE59, spec save \uC785\uB825",
|
|
24813
|
-
"docs.summary.design": "design.md \uBAA9\uCC28, \uCC38\uC870 \uC8FC\uC11D, \uAC1C\uC815 \uBC29\uC2DD",
|
|
24814
|
-
"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",
|
|
24815
24957
|
"docs.summary.commit": "spec commit \uC785\uB825, \uC774\uC720 \uAE30\uB85D, \uC2E4\uD328 \uD6C4 \uBCF5\uAD6C",
|
|
24816
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)",
|
|
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",
|
|
24817
24960
|
"help.update": "\uC0C8 \uBC84\uC804 \uD655\uC778\uACFC \uC124\uCE58 \uBC29\uBC95 \uC548\uB0B4, \uD504\uB85C\uC81D\uD2B8 \uC9C0\uCE68 \uD30C\uC77C\uC758 GITIFACT \uBE14\uB85D\uC744 \uD604\uC7AC \uBC84\uC804\uC73C\uB85C \uAC31\uC2E0",
|
|
24818
24961
|
"server.badLanguage": "\uC5B8\uC5B4 \uCF54\uB4DC\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
24819
24962
|
"server.changelogNotFound": "\uD328\uCE58\uB178\uD2B8\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
@@ -24824,12 +24967,24 @@ var messages_default2 = {
|
|
|
24824
24967
|
"update.text.disabled": "\uC0C8 \uBC84\uC804 \uD655\uC778\uC774 \uAEBC\uC838 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
24825
24968
|
"update.text.unavailable": "\uC0C8 \uBC84\uC804\uC744 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uB124\uD2B8\uC6CC\uD06C \uC5F0\uACB0\uC744 \uD655\uC778\uD558\uC138\uC694.",
|
|
24826
24969
|
"update.text.install": "npm \uC804\uC5ED \uC124\uCE58\uC6A9: {command}",
|
|
24827
|
-
"update.text.afterInstall": "\uC124\uCE58\uD55C \uB4A4 gitifact update\
|
|
24970
|
+
"update.text.afterInstall": "\uC124\uCE58\uD55C \uB4A4 gitifact update --commit\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 \uC9C0\uCE68 \uBE14\uB85D\uC774 \uC0C8 \uBC84\uC804\uC73C\uB85C \uAC31\uC2E0\uB418\uACE0 \uCEE4\uBC0B\uB429\uB2C8\uB2E4.",
|
|
24828
24971
|
"update.text.blockRefreshed": "\uC9C0\uCE68 \uBE14\uB85D \uAC31\uC2E0: {paths}",
|
|
24829
24972
|
"update.text.blockCurrent": "\uC9C0\uCE68 \uBE14\uB85D\uC740 \uC774\uBBF8 \uD604\uC7AC \uBC84\uC804\uC785\uB2C8\uB2E4: {paths}",
|
|
24830
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.",
|
|
24831
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.",
|
|
24832
|
-
"update.
|
|
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.",
|
|
24976
|
+
"update.text.committed": "\uC9C0\uCE68 \uBE14\uB85D \uCEE4\uBC0B: {commit} ({paths})",
|
|
24977
|
+
"update.text.commitNothing": "\uCEE4\uBC0B\uD560 \uC9C0\uCE68 \uBE14\uB85D \uBCC0\uACBD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
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}",
|
|
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}",
|
|
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}",
|
|
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.",
|
|
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"
|
|
24833
24988
|
};
|
|
24834
24989
|
|
|
24835
24990
|
// src/shared/i18n/index.ts
|
|
@@ -24905,8 +25060,8 @@ async function runStatus(format) {
|
|
|
24905
25060
|
import { createServer } from "node:http";
|
|
24906
25061
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
24907
25062
|
import { fileURLToPath } from "node:url";
|
|
24908
|
-
import { lstat as
|
|
24909
|
-
import { join as
|
|
25063
|
+
import { lstat as lstat4, readFile as readFile4 } from "node:fs/promises";
|
|
25064
|
+
import { join as join5 } from "node:path";
|
|
24910
25065
|
|
|
24911
25066
|
// src/server/assets.ts
|
|
24912
25067
|
import { readdir, readFile } from "node:fs/promises";
|
|
@@ -24939,6 +25094,7 @@ function contentType(path3) {
|
|
|
24939
25094
|
if (/\.jpe?g$/.test(path3)) return "image/jpeg";
|
|
24940
25095
|
if (path3.endsWith(".gif")) return "image/gif";
|
|
24941
25096
|
if (path3.endsWith(".webp")) return "image/webp";
|
|
25097
|
+
if (path3.endsWith(".pdf")) return "application/pdf";
|
|
24942
25098
|
return "application/octet-stream";
|
|
24943
25099
|
}
|
|
24944
25100
|
|
|
@@ -25045,7 +25201,7 @@ function commandScoped(key, load) {
|
|
|
25045
25201
|
}
|
|
25046
25202
|
|
|
25047
25203
|
// src/adapters/git/spec-preview-reader.ts
|
|
25048
|
-
var RECORD_PATHSPECS = [...STORE_DIRS.map((d) => d + "/spec/"),
|
|
25204
|
+
var RECORD_PATHSPECS = [...STORE_DIRS.map((d) => d + "/spec/"), WIKI_DIR + "/"];
|
|
25049
25205
|
var LegacyBaselineError = class extends SpecPreviewError {
|
|
25050
25206
|
};
|
|
25051
25207
|
function specPreviewReader(cwd) {
|
|
@@ -25079,7 +25235,7 @@ function specPreviewReader(cwd) {
|
|
|
25079
25235
|
const [, mode, oid2, stage, path3] = match;
|
|
25080
25236
|
if (!["100644", "100755"].includes(mode) || stage !== "0") throw new SpecPreviewError(t2("reader.stagingConflictOrLink"));
|
|
25081
25237
|
if (!recordPathPattern.test(path3)) {
|
|
25082
|
-
if (
|
|
25238
|
+
if (path3.startsWith(WIKI_DIR + "/")) continue;
|
|
25083
25239
|
throw new SpecPreviewError(t2("reader.stagingUnsupported"));
|
|
25084
25240
|
}
|
|
25085
25241
|
files.set(path3, oid2);
|
|
@@ -25175,38 +25331,85 @@ function specPreviewReader(cwd) {
|
|
|
25175
25331
|
}
|
|
25176
25332
|
|
|
25177
25333
|
// src/adapters/filesystem/spec-preview-store.ts
|
|
25178
|
-
import { lstat as
|
|
25179
|
-
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";
|
|
25180
25336
|
import { createHash as createHash2, randomBytes } from "node:crypto";
|
|
25181
25337
|
|
|
25182
|
-
// src/adapters/filesystem/
|
|
25183
|
-
import { lstat,
|
|
25338
|
+
// src/adapters/filesystem/working-warnings.ts
|
|
25339
|
+
import { lstat, readdir as readdir2 } from "node:fs/promises";
|
|
25184
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";
|
|
25185
25388
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
25186
25389
|
var missing = (error62) => error62.code === "ENOENT";
|
|
25187
|
-
var fileInfo = (path3) =>
|
|
25390
|
+
var fileInfo = (path3) => lstat2(path3).catch((error62) => {
|
|
25188
25391
|
if (missing(error62)) return void 0;
|
|
25189
25392
|
throw error62;
|
|
25190
25393
|
});
|
|
25191
25394
|
async function configDirectory(root) {
|
|
25192
|
-
const path3 =
|
|
25193
|
-
const
|
|
25194
|
-
if (
|
|
25195
|
-
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;
|
|
25196
25399
|
}
|
|
25197
25400
|
async function readConfigFile(root) {
|
|
25198
25401
|
await configDirectory(root);
|
|
25199
|
-
const path3 =
|
|
25200
|
-
const
|
|
25201
|
-
if (!
|
|
25202
|
-
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"));
|
|
25203
25406
|
return void 0;
|
|
25204
25407
|
}
|
|
25205
|
-
if (!
|
|
25408
|
+
if (!info7.isFile() || info7.isSymbolicLink() || info7.size > 65536) throw new InitError("PATH_CONFLICT", t2("config.fileShape"));
|
|
25206
25409
|
const bytes = await readFile2(path3);
|
|
25207
25410
|
const after = await fileInfo(path3);
|
|
25208
25411
|
await configDirectory(root);
|
|
25209
|
-
if (!after || after.ino !==
|
|
25412
|
+
if (!after || after.ino !== info7.ino || after.dev !== info7.dev || after.mtimeMs !== info7.mtimeMs || bytes.length > 65536) {
|
|
25210
25413
|
throw new InitError("INPUT_CHANGED", t2("config.changedWhileReading"));
|
|
25211
25414
|
}
|
|
25212
25415
|
try {
|
|
@@ -25216,7 +25419,7 @@ async function readConfigFile(root) {
|
|
|
25216
25419
|
}
|
|
25217
25420
|
}
|
|
25218
25421
|
async function publishConfig(root, text2, recheck, beforePublish) {
|
|
25219
|
-
const directory =
|
|
25422
|
+
const directory = join3(root, ".gitifact");
|
|
25220
25423
|
await configDirectory(root);
|
|
25221
25424
|
await mkdir(directory).catch((error62) => {
|
|
25222
25425
|
if (error62.code !== "EEXIST") throw error62;
|
|
@@ -25229,7 +25432,7 @@ async function publishConfig(root, text2, recheck, beforePublish) {
|
|
|
25229
25432
|
throw new InitError("INPUT_CHANGED", t2("config.directoryChanged"));
|
|
25230
25433
|
}
|
|
25231
25434
|
};
|
|
25232
|
-
const temporary =
|
|
25435
|
+
const temporary = join3(directory, ".init-" + randomUUID3() + ".tmp");
|
|
25233
25436
|
const handle = await open2(temporary, "wx", 384);
|
|
25234
25437
|
const tempOwner = await handle.stat();
|
|
25235
25438
|
try {
|
|
@@ -25246,7 +25449,7 @@ async function publishConfig(root, text2, recheck, beforePublish) {
|
|
|
25246
25449
|
const bytes = await readFile2(temporary, "utf8");
|
|
25247
25450
|
if (bytes !== text2) throw new InitError("INPUT_CHANGED", t2("config.temporaryChanged"));
|
|
25248
25451
|
parseManagedConfig(bytes);
|
|
25249
|
-
await link(temporary,
|
|
25452
|
+
await link(temporary, join3(directory, "config.json"));
|
|
25250
25453
|
} finally {
|
|
25251
25454
|
await unchanged();
|
|
25252
25455
|
const current = await fileInfo(temporary);
|
|
@@ -25258,14 +25461,14 @@ async function publishConfig(root, text2, recheck, beforePublish) {
|
|
|
25258
25461
|
var fail4 = (message) => {
|
|
25259
25462
|
throw new SpecPreviewError(message);
|
|
25260
25463
|
};
|
|
25261
|
-
var
|
|
25464
|
+
var info2 = async (path3) => lstat3(path3).catch((e) => {
|
|
25262
25465
|
if (e.code === "ENOENT") return void 0;
|
|
25263
25466
|
throw e;
|
|
25264
25467
|
});
|
|
25265
25468
|
var digest = (value) => createHash2("sha256").update(value).digest("hex");
|
|
25266
25469
|
var LEGACY_LOCKS = ["tryce-spec-preview.lock", "tryce-spec-commit.lock"];
|
|
25267
25470
|
async function failOnLegacyLock(gitDir) {
|
|
25268
|
-
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) }));
|
|
25269
25472
|
}
|
|
25270
25473
|
var decode3 = (bytes) => new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
25271
25474
|
var generatePreviewId = (prefix) => prefix + "-" + [...randomBytes(10)].map((n) => "abcdefghijklmnopqrstuvwxyz234567"[n & 31]).join("");
|
|
@@ -25275,48 +25478,48 @@ async function snapshot2(root) {
|
|
|
25275
25478
|
const files = /* @__PURE__ */ new Map();
|
|
25276
25479
|
let count = 0;
|
|
25277
25480
|
let bytes = 0;
|
|
25278
|
-
const parent = await
|
|
25481
|
+
const parent = await info2(join4(root, ".gitifact"));
|
|
25279
25482
|
if (parent && (!parent.isDirectory() || parent.isSymbolicLink())) fail4(t2("store.storeNotDirectory"));
|
|
25280
|
-
if (!parent && await
|
|
25483
|
+
if (!parent && await info2(join4(root, ".tryce"))) fail4(t2("store.migrateFirst"));
|
|
25281
25484
|
const config2 = await readConfigFile(root);
|
|
25282
|
-
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"));
|
|
25283
25486
|
async function visit2(path3, depth, documents) {
|
|
25284
|
-
const stat3 = await
|
|
25487
|
+
const stat3 = await info2(join4(root, path3));
|
|
25285
25488
|
if (!stat3) return;
|
|
25286
25489
|
if (stat3.isSymbolicLink() || !stat3.isDirectory() && !stat3.isFile()) fail4(t2("store.linkOrSpecial", { path: path3 }));
|
|
25287
25490
|
if (++count > 4e3) fail4(t2("store.tooManyFiles"));
|
|
25288
25491
|
if (stat3.isDirectory()) {
|
|
25289
25492
|
if (depth > (documents ? 8 : 1)) fail4(t2("store.tooDeep", { path: path3 }));
|
|
25290
|
-
for (const name of (await
|
|
25493
|
+
for (const name of (await readdir3(join4(root, path3))).sort()) await visit2(path3 + "/" + name, depth + 1, documents);
|
|
25291
25494
|
} else {
|
|
25292
25495
|
if (path3.endsWith("/tryce.json")) fail4(t2("store.legacyJson"));
|
|
25293
25496
|
if (documents ? !(path3.endsWith(".md") || depth === 1 && path3.endsWith("/history.jsonl")) : !/\/(requirements\.md|design\.md|history\.jsonl)$/.test(path3)) return;
|
|
25294
25497
|
if (!recordPathPattern.test(path3) || path3.startsWith(".tryce/")) fail4(t2("store.unsupportedPath", { path: path3 }));
|
|
25295
25498
|
if (stat3.nlink !== 1 || stat3.size > 1024 * 1024) fail4(t2("store.hardLinkOrSize"));
|
|
25296
|
-
const raw = await readFile3(
|
|
25499
|
+
const raw = await readFile3(join4(root, path3));
|
|
25297
25500
|
bytes += raw.length;
|
|
25298
25501
|
if (raw.length > 1024 * 1024 || bytes > 16 * 1024 * 1024) fail4(t2("store.sizeLimit"));
|
|
25299
25502
|
files.set(path3, decode3(raw));
|
|
25300
25503
|
}
|
|
25301
25504
|
}
|
|
25302
25505
|
await visit2(".gitifact/spec", 0, false);
|
|
25303
|
-
|
|
25506
|
+
await visit2(WIKI_DIR, 0, true);
|
|
25304
25507
|
const bundle = parsePreviewBundle(files);
|
|
25305
|
-
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 ?? "")) };
|
|
25306
25509
|
}
|
|
25307
25510
|
async function readWorkingPreviewState(cwd) {
|
|
25308
25511
|
const { root, gitDir } = await specPreviewReader(cwd).location();
|
|
25309
25512
|
await failOnLegacyLock(gitDir);
|
|
25310
|
-
if (await
|
|
25513
|
+
if (await info2(join4(gitDir, "gitifact-spec-preview.lock"))) fail4(t2("store.locked"));
|
|
25311
25514
|
const first = await snapshot2(root);
|
|
25312
25515
|
const second = await snapshot2(root);
|
|
25313
25516
|
if (first.stamp !== second.stamp) fail4(t2("store.changedWhileReading"));
|
|
25314
|
-
if (await
|
|
25517
|
+
if (await info2(join4(gitDir, "gitifact-spec-preview.lock"))) fail4(t2("store.lockedWhileReading"));
|
|
25315
25518
|
return first;
|
|
25316
25519
|
}
|
|
25317
25520
|
async function readWorkingPreview(cwd) {
|
|
25318
|
-
const { stamp, specs,
|
|
25319
|
-
return { stamp, specs,
|
|
25521
|
+
const { root, stamp, specs, wiki, bundle } = await readWorkingPreviewState(cwd);
|
|
25522
|
+
return { stamp, specs, wiki, warnings: await workingWarnings(root, bundle) };
|
|
25320
25523
|
}
|
|
25321
25524
|
async function saveWorkingPreview(cwd, input2, publish = rename) {
|
|
25322
25525
|
if (!input2 || typeof input2 !== "object" || Array.isArray(input2)) fail4(t2("store.invalidInput"));
|
|
@@ -25330,8 +25533,8 @@ async function saveWorkingPreview(cwd, input2, publish = rename) {
|
|
|
25330
25533
|
if (!old || renderSpecPreview(old) !== renderSpecPreview(spec2)) writes.set(spec2.path, renderSpecPreview(spec2));
|
|
25331
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);
|
|
25332
25535
|
}
|
|
25333
|
-
const previousDocs = before.
|
|
25334
|
-
const nextDocs = result.
|
|
25536
|
+
const previousDocs = before.wiki.documents;
|
|
25537
|
+
const nextDocs = result.wiki.documents;
|
|
25335
25538
|
for (const doc of nextDocs) {
|
|
25336
25539
|
const old = previousDocs.find((d) => d.id === doc.id);
|
|
25337
25540
|
if (old && old.path !== doc.path) writes.set(old.path, null);
|
|
@@ -25345,7 +25548,7 @@ var readLockedPreviewState = (root) => snapshot2(root);
|
|
|
25345
25548
|
async function previewTransaction(cwd, expected2, build, publish = rename, afterPublish) {
|
|
25346
25549
|
const { root, gitDir } = await specPreviewReader(cwd).location();
|
|
25347
25550
|
await failOnLegacyLock(gitDir);
|
|
25348
|
-
const lock =
|
|
25551
|
+
const lock = join4(gitDir, "gitifact-spec-preview.lock");
|
|
25349
25552
|
try {
|
|
25350
25553
|
await mkdir2(lock);
|
|
25351
25554
|
} catch (e) {
|
|
@@ -25358,7 +25561,7 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25358
25561
|
const published = [];
|
|
25359
25562
|
let keepRecovery = false;
|
|
25360
25563
|
async function directory(path3) {
|
|
25361
|
-
const stat3 = await
|
|
25564
|
+
const stat3 = await info2(path3);
|
|
25362
25565
|
if (stat3) {
|
|
25363
25566
|
if (!stat3.isDirectory() || stat3.isSymbolicLink()) fail4(t2("store.notDirectory", { path: path3 }));
|
|
25364
25567
|
return;
|
|
@@ -25382,14 +25585,14 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25382
25585
|
else finalFiles.set(c.path, c.after);
|
|
25383
25586
|
}
|
|
25384
25587
|
if (finalFiles.size > 2e3 || [...finalFiles.values()].reduce((n, s) => n + Buffer.byteLength(s), 0) > 16 * 1024 * 1024) fail4(t2("store.totalLimit"));
|
|
25385
|
-
await writeFile(
|
|
25588
|
+
await writeFile(join4(lock, "recovery.json"), JSON.stringify({ root, changed }), { flag: "wx" });
|
|
25386
25589
|
for (const c of changed) {
|
|
25387
25590
|
if (c.after === null) {
|
|
25388
25591
|
temporary.push("");
|
|
25389
25592
|
continue;
|
|
25390
25593
|
}
|
|
25391
|
-
await directory(dirname(
|
|
25392
|
-
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"));
|
|
25393
25596
|
await writeFile(temp, c.after, { flag: "wx" });
|
|
25394
25597
|
temporary.push(temp);
|
|
25395
25598
|
}
|
|
@@ -25397,8 +25600,8 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25397
25600
|
await result.recheck?.();
|
|
25398
25601
|
for (let i = 0; i < changed.length; i++) {
|
|
25399
25602
|
const c = changed[i];
|
|
25400
|
-
const path3 =
|
|
25401
|
-
const stat3 = await
|
|
25603
|
+
const path3 = join4(root, c.path);
|
|
25604
|
+
const stat3 = await info2(path3);
|
|
25402
25605
|
if (stat3?.isSymbolicLink() || stat3 && !stat3.isFile() || (stat3 ? decode3(await readFile3(path3)) : null) !== c.before) fail4(t2("store.changedBeforeWrite"));
|
|
25403
25606
|
if (c.after === null) await unlink2(path3);
|
|
25404
25607
|
else await publish(temporary[i], path3);
|
|
@@ -25408,7 +25611,7 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25408
25611
|
if (after.stamp !== digest(JSON.stringify([...finalFiles].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) + (before.config ?? ""))) fail4(t2("store.changedWhileWriting"));
|
|
25409
25612
|
await result.recheck?.();
|
|
25410
25613
|
await afterPublish?.(after);
|
|
25411
|
-
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) };
|
|
25412
25615
|
} catch (error62) {
|
|
25413
25616
|
if (error62 instanceof PreservedPreviewError) {
|
|
25414
25617
|
keepRecovery = true;
|
|
@@ -25416,15 +25619,15 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25416
25619
|
}
|
|
25417
25620
|
for (const c of [...published].reverse()) {
|
|
25418
25621
|
try {
|
|
25419
|
-
const path3 =
|
|
25420
|
-
const stat3 = await
|
|
25622
|
+
const path3 = join4(root, c.path);
|
|
25623
|
+
const stat3 = await info2(path3);
|
|
25421
25624
|
if (c.after === null ? !!stat3 : !stat3?.isFile() || stat3.isSymbolicLink() || decode3(await readFile3(path3)) !== c.after) {
|
|
25422
25625
|
keepRecovery = true;
|
|
25423
25626
|
continue;
|
|
25424
25627
|
}
|
|
25425
25628
|
if (c.before === null) await unlink2(path3);
|
|
25426
25629
|
else {
|
|
25427
|
-
const temp =
|
|
25630
|
+
const temp = join4(dirname(path3), ".gitifact-restore-" + randomBytes(12).toString("hex"));
|
|
25428
25631
|
temporary.push(temp);
|
|
25429
25632
|
await writeFile(temp, c.before, { flag: "wx" });
|
|
25430
25633
|
await rename(temp, path3);
|
|
@@ -25442,7 +25645,7 @@ async function previewTransaction(cwd, expected2, build, publish = rename, after
|
|
|
25442
25645
|
for (const path3 of [...createdDirs].reverse()) await rmdir(path3).catch(() => {
|
|
25443
25646
|
});
|
|
25444
25647
|
if (!keepRecovery) {
|
|
25445
|
-
await unlink2(
|
|
25648
|
+
await unlink2(join4(lock, "recovery.json")).catch((e) => {
|
|
25446
25649
|
if (e.code !== "ENOENT") throw e;
|
|
25447
25650
|
});
|
|
25448
25651
|
await rmdir(lock);
|
|
@@ -25484,8 +25687,8 @@ function createSpecBrowserReader(root, sessionId, inherited = process.env) {
|
|
|
25484
25687
|
if (cursor > 0 && expectedHead !== base.head) throw new SpecPreviewError(t2("specReader.historyChanged"));
|
|
25485
25688
|
const current = await readWorkingPreviewState(root);
|
|
25486
25689
|
const bare = current.specs.map(({ history: _history, ...s }) => ({ ...s, contributors: [], updatedAt: null }));
|
|
25487
|
-
const bareDocuments = current.
|
|
25488
|
-
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 });
|
|
25489
25692
|
const head = base.head;
|
|
25490
25693
|
const features = await Promise.all(bare.map(async (feature2) => {
|
|
25491
25694
|
const folder = feature2.path.replace(/^\.(?:gitifact|tryce)\/(spec\/[^/]+)\/requirements\.md$/, "$1");
|
|
@@ -25501,7 +25704,7 @@ function createSpecBrowserReader(root, sessionId, inherited = process.env) {
|
|
|
25501
25704
|
return { ...feature2, contributors: [...people.values()].sort((a, b) => b.commits - a.commits), updatedAt: lines[0]?.split("\0")[2] ?? null };
|
|
25502
25705
|
}));
|
|
25503
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 })));
|
|
25504
|
-
const documentDirs =
|
|
25707
|
+
const documentDirs = [WIKI_DIR];
|
|
25505
25708
|
const [rows, dirty] = await Promise.all([
|
|
25506
25709
|
git([
|
|
25507
25710
|
"log",
|
|
@@ -25535,7 +25738,7 @@ function createSpecBrowserReader(root, sessionId, inherited = process.env) {
|
|
|
25535
25738
|
const [commit, parents, author, email3, date5, committer, message] = row.split("\0");
|
|
25536
25739
|
if (!commit || author === void 0 || email3 === void 0 || !date5 || committer === void 0 || message === void 0) throw new SpecPreviewError(t2("specReader.historyUnreadable"));
|
|
25537
25740
|
const after = await snapshot3(commit);
|
|
25538
|
-
let before =
|
|
25741
|
+
let before = emptyBundle();
|
|
25539
25742
|
const parent = parents?.split(" ")[0];
|
|
25540
25743
|
if (parent) {
|
|
25541
25744
|
try {
|
|
@@ -25562,9 +25765,9 @@ function createSpecBrowserReader(root, sessionId, inherited = process.env) {
|
|
|
25562
25765
|
}));
|
|
25563
25766
|
}));
|
|
25564
25767
|
if (await readHead() !== head || (await readWorkingPreviewState(root)).stamp !== current.stamp) throw new SpecPreviewError(t2("specReader.projectChanged"));
|
|
25565
|
-
return
|
|
25768
|
+
return browserSpecsV2.parse({
|
|
25566
25769
|
contract: "browser-specs",
|
|
25567
|
-
version:
|
|
25770
|
+
version: 2,
|
|
25568
25771
|
sessionId,
|
|
25569
25772
|
head,
|
|
25570
25773
|
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -25658,18 +25861,27 @@ async function startBrowserServer(options) {
|
|
|
25658
25861
|
if (api) {
|
|
25659
25862
|
if (request.headers["sec-fetch-site"] === "cross-site" && !allowedOrigin) return fail8(response, 403, "FORBIDDEN", t2("server.crossSite"));
|
|
25660
25863
|
if (url2.search && path3 !== "/api/v1/specs" && path3 !== "/api/v1/changelog") return fail8(response, 400, "BAD_REQUEST", t2("server.noQuery"));
|
|
25661
|
-
if (path3.startsWith("/api/v1/
|
|
25662
|
-
const
|
|
25864
|
+
if (path3.startsWith("/api/v1/assets/")) {
|
|
25865
|
+
const relative = path3.slice("/api/v1/assets/".length);
|
|
25866
|
+
const assetPath2 = ASSETS_DIR + "/" + relative;
|
|
25663
25867
|
if (request.method !== "GET") {
|
|
25664
25868
|
response.setHeader("Allow", "GET");
|
|
25665
25869
|
return fail8(response, 405, "METHOD_NOT_ALLOWED", t2("server.methodNotAllowed"));
|
|
25666
25870
|
}
|
|
25667
|
-
if (!
|
|
25668
|
-
const file2 =
|
|
25669
|
-
const stat3 = await
|
|
25670
|
-
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"));
|
|
25671
25875
|
const bytes = await readFile4(file2);
|
|
25672
|
-
|
|
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
|
+
});
|
|
25673
25885
|
response.end(bytes);
|
|
25674
25886
|
return;
|
|
25675
25887
|
}
|
|
@@ -25833,13 +26045,13 @@ async function runBrowser(options, version2) {
|
|
|
25833
26045
|
}
|
|
25834
26046
|
|
|
25835
26047
|
// src/commands/spec-init.ts
|
|
25836
|
-
import { readdir as
|
|
25837
|
-
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";
|
|
25838
26050
|
|
|
25839
26051
|
// src/adapters/git/init-repository.ts
|
|
25840
26052
|
import { createHash as createHash3 } from "node:crypto";
|
|
25841
26053
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
25842
|
-
import { join as
|
|
26054
|
+
import { join as join6 } from "node:path";
|
|
25843
26055
|
function initRepository(cwd, inherited = process.env) {
|
|
25844
26056
|
const env = { ...inherited, GIT_OPTIONAL_LOCKS: "0", GIT_NO_LAZY_FETCH: "1", GIT_TERMINAL_PROMPT: "0", GIT_LITERAL_PATHSPECS: "1", LC_ALL: "C" };
|
|
25845
26057
|
const runner = createGitRunner();
|
|
@@ -25859,7 +26071,7 @@ function initRepository(cwd, inherited = process.env) {
|
|
|
25859
26071
|
const gitDir = locations.length === 2 ? locations[0] : decodeGitLine(await git(["rev-parse", "--absolute-git-dir"], root));
|
|
25860
26072
|
const indexPath = locations.length === 2 ? locations[1] : decodeGitLine(await git(["rev-parse", "--path-format=absolute", "--git-path", "index"], root));
|
|
25861
26073
|
for (const marker2 of ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"]) {
|
|
25862
|
-
if (await fileInfo(
|
|
26074
|
+
if (await fileInfo(join6(gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
|
|
25863
26075
|
}
|
|
25864
26076
|
if (state.changes.some((change2) => change2.kind === "unmerged")) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
25865
26077
|
const index = await readFile5(indexPath).catch((error62) => {
|
|
@@ -25897,22 +26109,22 @@ function initRepository(cwd, inherited = process.env) {
|
|
|
25897
26109
|
|
|
25898
26110
|
// src/adapters/filesystem/managed-file.ts
|
|
25899
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";
|
|
25900
|
-
import { join as
|
|
26112
|
+
import { join as join7, dirname as dirname2 } from "node:path";
|
|
25901
26113
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
25902
26114
|
var DEFAULT_LIMIT = 1024 * 1024;
|
|
25903
26115
|
async function managedPath(root, path3, create = false) {
|
|
25904
26116
|
let full = root;
|
|
25905
26117
|
const parts = path3.split("/");
|
|
25906
26118
|
for (let i = 0; i < parts.length; i++) {
|
|
25907
|
-
full =
|
|
25908
|
-
let
|
|
25909
|
-
if (!
|
|
26119
|
+
full = join7(full, parts[i]);
|
|
26120
|
+
let info7 = await fileInfo(full);
|
|
26121
|
+
if (!info7 && create && i < parts.length - 1) {
|
|
25910
26122
|
await mkdir3(full).catch((error62) => {
|
|
25911
26123
|
if (error62.code !== "EEXIST") throw error62;
|
|
25912
26124
|
});
|
|
25913
|
-
|
|
26125
|
+
info7 = await fileInfo(full);
|
|
25914
26126
|
}
|
|
25915
|
-
if (
|
|
26127
|
+
if (info7 && (info7.isSymbolicLink() || (i < parts.length - 1 ? !info7.isDirectory() : !info7.isFile()))) {
|
|
25916
26128
|
throw new InitError("PATH_CONFLICT", t2("managedFile.notRegularPath", { path: path3 }));
|
|
25917
26129
|
}
|
|
25918
26130
|
}
|
|
@@ -25952,7 +26164,7 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
25952
26164
|
if (previous !== null) await unlink3(full);
|
|
25953
26165
|
return;
|
|
25954
26166
|
}
|
|
25955
|
-
const temp =
|
|
26167
|
+
const temp = join7(directory, ".gitifact-managed-" + randomUUID5() + ".tmp");
|
|
25956
26168
|
const handle = await open3(temp, "wx", 384);
|
|
25957
26169
|
const tempOwner = await handle.stat();
|
|
25958
26170
|
try {
|
|
@@ -25978,14 +26190,53 @@ async function managedWrite(root, path3, previous, next, recheck) {
|
|
|
25978
26190
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
25979
26191
|
|
|
25980
26192
|
// src/commands/docs.ts
|
|
25981
|
-
import { readFile as readFile7 } from "node:fs/promises";
|
|
25982
|
-
|
|
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"];
|
|
25983
26196
|
var isDocTopic = (value) => docTopics.includes(value);
|
|
25984
|
-
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
|
+
});
|
|
25985
26202
|
function listDocTopics(lang = defaultLanguage2) {
|
|
25986
26203
|
const width = Math.max(...docTopics.map((topic) => topic.length));
|
|
25987
26204
|
return docTopics.map((topic) => topic.padEnd(width) + " " + t2(`docs.summary.${topic}`, {}, lang)).join("\n") + "\n";
|
|
25988
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
|
+
}
|
|
25989
26240
|
async function runDocs(topic, controls = {}) {
|
|
25990
26241
|
if (topic === void 0) {
|
|
25991
26242
|
process.stdout.write(listDocTopics());
|
|
@@ -25997,7 +26248,7 @@ async function runDocs(topic, controls = {}) {
|
|
|
25997
26248
|
process.exitCode = 1;
|
|
25998
26249
|
return;
|
|
25999
26250
|
}
|
|
26000
|
-
process.stdout.write(await (
|
|
26251
|
+
process.stdout.write(await renderDoc(topic, controls));
|
|
26001
26252
|
}
|
|
26002
26253
|
|
|
26003
26254
|
// src/commands/agent-block.ts
|
|
@@ -26038,6 +26289,16 @@ function isWrapperFile(text2) {
|
|
|
26038
26289
|
const lines = own2.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
26039
26290
|
return lines.length > 0 && lines.every((line) => /^@(?:\.\/)?[^\s]+$/.test(line) && candidatePaths.includes(line.replace(/^@(?:\.\/)?/, "")));
|
|
26040
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
|
+
}
|
|
26041
26302
|
function resolveAgentPaths(preset, existing) {
|
|
26042
26303
|
if (preset && preset !== "all") {
|
|
26043
26304
|
const order = agentPresets[preset];
|
|
@@ -26071,7 +26332,7 @@ function removeBlock(text2, boilerplate) {
|
|
|
26071
26332
|
}
|
|
26072
26333
|
|
|
26073
26334
|
// src/commands/agent-docs.ts
|
|
26074
|
-
var skippedAgentDocs = { mode: "skip", paths: [], writes: [] };
|
|
26335
|
+
var skippedAgentDocs = { mode: "skip", paths: [], writes: [], missing: [] };
|
|
26075
26336
|
async function planAgentDocs(root, options) {
|
|
26076
26337
|
if (options.skip) return skippedAgentDocs;
|
|
26077
26338
|
const existing = /* @__PURE__ */ new Map();
|
|
@@ -26089,7 +26350,12 @@ async function planAgentDocs(root, options) {
|
|
|
26089
26350
|
paths2.push(path3);
|
|
26090
26351
|
writes.push({ path: path3, previous: text2, next: removeBlock(text2, boilerplateFor(path3)) });
|
|
26091
26352
|
}
|
|
26092
|
-
|
|
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: [] };
|
|
26093
26359
|
}
|
|
26094
26360
|
const block = await renderAgentBlock(options.version, options);
|
|
26095
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);
|
|
@@ -26103,11 +26369,19 @@ async function planAgentDocs(root, options) {
|
|
|
26103
26369
|
paths2.push(create);
|
|
26104
26370
|
writes.push({ path: create, previous: null, next: injectBlock(null, block, boilerplateFor(create)) });
|
|
26105
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
|
+
}
|
|
26106
26380
|
for (const [path3, text2] of existing) {
|
|
26107
26381
|
if (paths2.includes(path3) || !isWrapperFile(text2) || !findBlock(text2)) continue;
|
|
26108
26382
|
writes.push({ path: path3, previous: text2, next: removeBlock(text2, boilerplateFor(path3)) });
|
|
26109
26383
|
}
|
|
26110
|
-
return { mode: "install", paths: paths2, writes };
|
|
26384
|
+
return { mode: "install", paths: paths2, writes, missing: missing2 };
|
|
26111
26385
|
}
|
|
26112
26386
|
async function applyAgentDocs(root, plan, recheck) {
|
|
26113
26387
|
for (const write of plan.writes) await managedWrite(root, write.path, write.previous, write.next, recheck);
|
|
@@ -26115,7 +26389,7 @@ async function applyAgentDocs(root, plan, recheck) {
|
|
|
26115
26389
|
}
|
|
26116
26390
|
|
|
26117
26391
|
// src/commands/spec-init.ts
|
|
26118
|
-
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)) {
|
|
26119
26393
|
const repo = initRepository(cwd, env);
|
|
26120
26394
|
const first = await repo.inspect();
|
|
26121
26395
|
const root = first.state.repository.rootPath;
|
|
@@ -26146,14 +26420,14 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
26146
26420
|
if (old !== void 0) return existing(old);
|
|
26147
26421
|
if (first.trackedConfig) throw new InitError("CONFIG_DELETED", t2("init.configDeleted"));
|
|
26148
26422
|
const checkRecords = async () => {
|
|
26149
|
-
if (await fileInfo(
|
|
26150
|
-
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) => {
|
|
26151
26425
|
if (e.code === "ENOENT") return [];
|
|
26152
26426
|
throw e;
|
|
26153
26427
|
});
|
|
26154
26428
|
if (entries.some((name) => !/^\.init-[a-f0-9-]+\.tmp$/.test(name))) throw new InitError("EXISTING_RECORDS", t2("init.orphanStore"));
|
|
26155
26429
|
};
|
|
26156
|
-
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" } };
|
|
26157
26431
|
const text2 = JSON.stringify(config2, null, 2) + "\n";
|
|
26158
26432
|
const recheck = async () => {
|
|
26159
26433
|
await checkRecords();
|
|
@@ -26177,8 +26451,22 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
26177
26451
|
};
|
|
26178
26452
|
await published();
|
|
26179
26453
|
await applyAgentDocs(root, docsPlan, published);
|
|
26454
|
+
await writeWikiPolicy(root, readDoc);
|
|
26180
26455
|
return result(config2, "created");
|
|
26181
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
|
+
}
|
|
26182
26470
|
|
|
26183
26471
|
// src/commands/init.ts
|
|
26184
26472
|
async function runInit(options, version2) {
|
|
@@ -26193,7 +26481,7 @@ async function runInit(options, version2) {
|
|
|
26193
26481
|
if (!dto.ok) throw new Error("unreachable");
|
|
26194
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") + ")");
|
|
26195
26483
|
process.stdout.write(options.format === "text" ? `${dto.outcome}: ${dto.rootPath}/.gitifact/config.json
|
|
26196
|
-
${t2("init.text.storage")}: schemaVersion
|
|
26484
|
+
${t2("init.text.storage")}: schemaVersion ${dto.schemaVersion}
|
|
26197
26485
|
${t2("init.text.agentDocs")}: ${docs}
|
|
26198
26486
|
` : JSON.stringify(dto) + "\n");
|
|
26199
26487
|
} catch (error62) {
|
|
@@ -26204,7 +26492,39 @@ ${t2("init.text.agentDocs")}: ${docs}
|
|
|
26204
26492
|
}
|
|
26205
26493
|
}
|
|
26206
26494
|
|
|
26495
|
+
// src/adapters/git/agent-docs-commit.ts
|
|
26496
|
+
function agentDocsGit(root, inherited = process.env) {
|
|
26497
|
+
const env = { ...inherited, GIT_OPTIONAL_LOCKS: "0", GIT_NO_LAZY_FETCH: "1", GIT_TERMINAL_PROMPT: "0", GIT_LITERAL_PATHSPECS: "1", LC_ALL: "C" };
|
|
26498
|
+
const runner = createGitRunner();
|
|
26499
|
+
const git = (args, acceptedExitCodes = [0]) => runner(
|
|
26500
|
+
["-c", "core.fsmonitor=false", ...args],
|
|
26501
|
+
{ cwd: root, env, timeoutMs: 12e4, maxBytes: 16 * 1024 * 1024, acceptedExitCodes }
|
|
26502
|
+
);
|
|
26503
|
+
return {
|
|
26504
|
+
async head() {
|
|
26505
|
+
return (await git(["rev-parse", "-q", "--verify", "HEAD^{commit}"], [0, 1])).toString("utf8").trim() || null;
|
|
26506
|
+
},
|
|
26507
|
+
// The committed text of a path, or null when HEAD does not track it.
|
|
26508
|
+
async committed(head, path3) {
|
|
26509
|
+
if ((await git(["ls-tree", "-z", head, "--", path3])).length === 0) return null;
|
|
26510
|
+
return (await git(["cat-file", "blob", head + ":" + path3])).toString("utf8");
|
|
26511
|
+
},
|
|
26512
|
+
// `--only` commits the working content of these paths and leaves every other staged entry staged. Hooks and signing stay on.
|
|
26513
|
+
async commitOnly(paths2, message) {
|
|
26514
|
+
await git(["commit", "--only", "-m", message, "--", ...paths2]);
|
|
26515
|
+
},
|
|
26516
|
+
async changedPaths(commit) {
|
|
26517
|
+
return (await git(["diff-tree", "--no-commit-id", "--name-only", "--no-renames", "-r", "-z", "--root", commit])).toString("utf8").split("\0").filter(Boolean).sort();
|
|
26518
|
+
},
|
|
26519
|
+
async parents(commit) {
|
|
26520
|
+
return (await git(["rev-list", "--parents", "-n", "1", commit])).toString("utf8").trim().split(" ").slice(1);
|
|
26521
|
+
}
|
|
26522
|
+
};
|
|
26523
|
+
}
|
|
26524
|
+
|
|
26207
26525
|
// src/commands/update.ts
|
|
26526
|
+
var blockCommitMessage = (version2) => "chore(gitifact): refresh GITIFACT block to v" + version2;
|
|
26527
|
+
var commitState = (state, extra = {}) => ({ state, commit: null, paths: [], message: null, reason: null, detail: null, ...extra });
|
|
26208
26528
|
async function refreshBlocks(cwd, version2, env, controls) {
|
|
26209
26529
|
const repo = initRepository(cwd, env);
|
|
26210
26530
|
let first;
|
|
@@ -26213,37 +26533,79 @@ async function refreshBlocks(cwd, version2, env, controls) {
|
|
|
26213
26533
|
first = await repo.inspect();
|
|
26214
26534
|
config2 = await readConfigFile(first.state.repository.rootPath);
|
|
26215
26535
|
} catch (error62) {
|
|
26216
|
-
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: [] };
|
|
26217
26537
|
throw error62;
|
|
26218
26538
|
}
|
|
26219
|
-
if (config2 === void 0) return { state: "not-initialized", paths: [] };
|
|
26539
|
+
if (config2 === void 0) return { state: "not-initialized", paths: [], missing: [] };
|
|
26220
26540
|
const root = first.state.repository.rootPath;
|
|
26221
26541
|
const plan = await planAgentDocs(root, { version: version2, onlyExisting: true, ...controls });
|
|
26222
|
-
|
|
26223
|
-
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 };
|
|
26224
26545
|
const unchanged = async () => {
|
|
26225
26546
|
if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !== config2) throw new InitError("INPUT_CHANGED", t2("init.inputChanged"));
|
|
26226
26547
|
};
|
|
26227
|
-
|
|
26548
|
+
const applied = await applyAgentDocs(root, plan, unchanged);
|
|
26549
|
+
return { state: "refreshed", paths: applied, missing: missing2, root, candidates: plan.paths };
|
|
26550
|
+
}
|
|
26551
|
+
function outsideBlock(text2) {
|
|
26552
|
+
const normalized2 = text2.replace(/\r\n/g, "\n");
|
|
26553
|
+
try {
|
|
26554
|
+
const found = findBlock(normalized2);
|
|
26555
|
+
return found ? normalized2.slice(0, found.start) + AGENT_START + normalized2.slice(found.end) : null;
|
|
26556
|
+
} catch {
|
|
26557
|
+
return null;
|
|
26558
|
+
}
|
|
26559
|
+
}
|
|
26560
|
+
async function commitBlocks(docs, version2, env) {
|
|
26561
|
+
if (!docs.root || !docs.candidates?.length) return commitState("nothing");
|
|
26562
|
+
const git = agentDocsGit(docs.root, env);
|
|
26563
|
+
const head = await git.head();
|
|
26564
|
+
const changed = [];
|
|
26565
|
+
for (const path3 of docs.candidates) {
|
|
26566
|
+
const working = await managedRead(docs.root, path3);
|
|
26567
|
+
const committed = head ? await git.committed(head, path3) : null;
|
|
26568
|
+
if (working === null) continue;
|
|
26569
|
+
if (committed === null) return commitState("skipped", { paths: [path3], reason: "untracked" });
|
|
26570
|
+
if (committed.replace(/\r\n/g, "\n") === working.replace(/\r\n/g, "\n")) continue;
|
|
26571
|
+
const before = outsideBlock(committed);
|
|
26572
|
+
if (before === null || before !== outsideBlock(working)) return commitState("skipped", { paths: [path3], reason: "other-changes" });
|
|
26573
|
+
changed.push(path3);
|
|
26574
|
+
}
|
|
26575
|
+
if (changed.length === 0) return commitState("nothing");
|
|
26576
|
+
const message = blockCommitMessage(version2);
|
|
26577
|
+
let failure2;
|
|
26578
|
+
try {
|
|
26579
|
+
await git.commitOnly(changed, message);
|
|
26580
|
+
} catch (error62) {
|
|
26581
|
+
failure2 = error62;
|
|
26582
|
+
}
|
|
26583
|
+
const after = await git.head();
|
|
26584
|
+
if (!after || after === head) return commitState("skipped", { paths: changed, reason: "commit-failed", detail: failure2 instanceof Error ? failure2.message : null });
|
|
26585
|
+
const parents = await git.parents(after);
|
|
26586
|
+
if (parents.length !== 1 || parents[0] !== head) throw new InitError("COMMIT_RESULT_UNKNOWN", t2("update.commitResultUnknown", { commit: after }));
|
|
26587
|
+
return commitState("committed", { commit: after, paths: await git.changedPaths(after), message });
|
|
26228
26588
|
}
|
|
26229
26589
|
async function updateCommand(cwd, version2, env = process.env, controls = {}) {
|
|
26230
26590
|
const [update, agentDocs] = await Promise.all([
|
|
26231
26591
|
updateCheckDisabled(env) ? disabledUpdate : resolveUpdate(version2, controls.fetchLatest ?? fetchLatestVersion, void 0, controls.timeoutMs),
|
|
26232
26592
|
refreshBlocks(cwd, version2, env, controls)
|
|
26233
26593
|
]);
|
|
26234
|
-
|
|
26594
|
+
const commit = controls.commit ? await commitBlocks(agentDocs, version2, env) : commitState("not-requested");
|
|
26595
|
+
return updateV3.parse({
|
|
26235
26596
|
contract: "update",
|
|
26236
|
-
version:
|
|
26597
|
+
version: 3,
|
|
26237
26598
|
ok: true,
|
|
26238
26599
|
cliVersion: version2,
|
|
26239
26600
|
update,
|
|
26240
26601
|
install: update.status === "available" ? { npmGlobal: npmGlobalInstall(update.latestVersion) } : null,
|
|
26241
|
-
agentDocs
|
|
26602
|
+
agentDocs: { state: agentDocs.state, paths: agentDocs.paths, missing: agentDocs.missing },
|
|
26603
|
+
commit
|
|
26242
26604
|
});
|
|
26243
26605
|
}
|
|
26244
26606
|
async function runUpdate(options, version2) {
|
|
26245
26607
|
try {
|
|
26246
|
-
const dto = await updateCommand(process.cwd(), version2);
|
|
26608
|
+
const dto = await updateCommand(process.cwd(), version2, process.env, { commit: !!options.commit });
|
|
26247
26609
|
if (!dto.ok) throw new Error("unreachable");
|
|
26248
26610
|
if (options.format !== "text") {
|
|
26249
26611
|
process.stdout.write(JSON.stringify(dto) + "\n");
|
|
@@ -26253,22 +26615,31 @@ async function runUpdate(options, version2) {
|
|
|
26253
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"));
|
|
26254
26616
|
if (dto.install) lines.push(t2("update.text.install", { command: dto.install.npmGlobal }), t2("update.text.afterInstall"));
|
|
26255
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(", ") }));
|
|
26619
|
+
const { commit } = dto;
|
|
26620
|
+
if (commit.state === "committed") lines.push(t2("update.text.committed", { commit: commit.commit.slice(0, 12), paths: commit.paths.join(", ") }));
|
|
26621
|
+
else if (commit.state === "nothing") lines.push(t2("update.text.commitNothing"));
|
|
26622
|
+
else if (commit.state === "skipped") {
|
|
26623
|
+
const paths2 = commit.paths.join(", ");
|
|
26624
|
+
const text2 = commit.reason === "untracked" ? t2("update.text.commitSkipped.untracked", { paths: paths2 }) : commit.reason === "other-changes" ? t2("update.text.commitSkipped.other-changes", { paths: paths2 }) : t2("update.text.commitSkipped.commit-failed", { paths: paths2 });
|
|
26625
|
+
lines.push(text2 + (commit.detail ? " (" + commit.detail + ")" : ""));
|
|
26626
|
+
}
|
|
26256
26627
|
process.stdout.write(lines.join("\n") + "\n");
|
|
26257
26628
|
} catch (error62) {
|
|
26258
26629
|
const known = error62 instanceof InitError || error62 instanceof RepositoryReadError;
|
|
26259
26630
|
const failure2 = { code: known ? error62.code : "UPDATE_FAILED", message: known ? error62.message : t2("update.failed") };
|
|
26260
|
-
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");
|
|
26261
26632
|
process.exitCode = 1;
|
|
26262
26633
|
}
|
|
26263
26634
|
}
|
|
26264
26635
|
|
|
26265
26636
|
// src/commands/spec-preview.ts
|
|
26266
|
-
import { join as
|
|
26637
|
+
import { join as join15 } from "node:path";
|
|
26267
26638
|
import { readFile as readFile12, stat as stat2 } from "node:fs/promises";
|
|
26268
26639
|
|
|
26269
26640
|
// src/adapters/filesystem/spec-preview-prepare.ts
|
|
26270
|
-
import { lstat as
|
|
26271
|
-
import { join as
|
|
26641
|
+
import { lstat as lstat6 } from "node:fs/promises";
|
|
26642
|
+
import { join as join10 } from "node:path";
|
|
26272
26643
|
import { createHash as createHash4 } from "node:crypto";
|
|
26273
26644
|
var fail5 = (message) => {
|
|
26274
26645
|
throw new SpecPreviewError(message);
|
|
@@ -26281,7 +26652,7 @@ async function readPreviewContext(cwd) {
|
|
|
26281
26652
|
const { gitDir } = await reader.location();
|
|
26282
26653
|
const checkOperation = async () => {
|
|
26283
26654
|
for (const name of ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply"]) {
|
|
26284
|
-
const exists = await
|
|
26655
|
+
const exists = await lstat6(join10(gitDir, name)).catch((e) => {
|
|
26285
26656
|
if (e.code === "ENOENT") return void 0;
|
|
26286
26657
|
throw e;
|
|
26287
26658
|
});
|
|
@@ -26335,9 +26706,9 @@ async function verifyPreparedPreview(cwd, input2, staged) {
|
|
|
26335
26706
|
if (staged) {
|
|
26336
26707
|
const index = await c.reader.index();
|
|
26337
26708
|
if (index.size !== current.files.size) fail5(t2("prepare.stagingScope"));
|
|
26338
|
-
for (const [path3,
|
|
26709
|
+
for (const [path3, source2] of current.files) {
|
|
26339
26710
|
const oid2 = index.get(path3);
|
|
26340
|
-
const bytes = Buffer.from(
|
|
26711
|
+
const bytes = Buffer.from(source2);
|
|
26341
26712
|
const hash3 = createHash4(oid2?.length === 64 ? "sha256" : "sha1").update(`blob ${bytes.length}\0`).update(bytes).digest("hex");
|
|
26342
26713
|
if (oid2 !== hash3) fail5(t2("prepare.stagingContent", { path: path3 }));
|
|
26343
26714
|
}
|
|
@@ -26350,26 +26721,26 @@ async function verifyPreparedPreview(cwd, input2, staged) {
|
|
|
26350
26721
|
|
|
26351
26722
|
// src/adapters/filesystem/agent-inputs.ts
|
|
26352
26723
|
import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
|
|
26353
|
-
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";
|
|
26354
26725
|
import { tmpdir } from "node:os";
|
|
26355
|
-
import { basename, dirname as
|
|
26726
|
+
import { basename, dirname as dirname4, join as join11 } from "node:path";
|
|
26356
26727
|
var maxAge = 7 * 24 * 60 * 60 * 1e3;
|
|
26357
|
-
var
|
|
26728
|
+
var info4 = (path3) => lstat7(path3).catch((e) => {
|
|
26358
26729
|
if (e.code === "ENOENT") return void 0;
|
|
26359
26730
|
throw e;
|
|
26360
26731
|
});
|
|
26361
26732
|
var comparable = (path3) => process.platform === "win32" ? path3.toLowerCase() : path3;
|
|
26362
26733
|
async function candidates(root, controls) {
|
|
26363
26734
|
const key = createHash5("sha256").update(comparable(await realpath3(root))).digest("hex").slice(0, 16);
|
|
26364
|
-
return { system:
|
|
26735
|
+
return { system: join11((controls.tmpdir ?? tmpdir)(), "gitifact", key), project: join11(root, ".gitifact", "tmp") };
|
|
26365
26736
|
}
|
|
26366
26737
|
async function writable(directory) {
|
|
26367
26738
|
try {
|
|
26368
|
-
await
|
|
26369
|
-
const stat3 = await
|
|
26739
|
+
await mkdir5(directory, { recursive: true });
|
|
26740
|
+
const stat3 = await info4(directory);
|
|
26370
26741
|
if (!stat3?.isDirectory() || stat3.isSymbolicLink()) return false;
|
|
26371
|
-
const probe =
|
|
26372
|
-
await
|
|
26742
|
+
const probe = join11(directory, ".probe-" + randomBytes2(8).toString("hex"));
|
|
26743
|
+
await writeFile3(probe, "", { flag: "wx" });
|
|
26373
26744
|
await unlink4(probe);
|
|
26374
26745
|
return true;
|
|
26375
26746
|
} catch {
|
|
@@ -26382,28 +26753,28 @@ async function prepareAgentInputs(root, controls = {}) {
|
|
|
26382
26753
|
if (await writable(system)) directory = system;
|
|
26383
26754
|
else if (await writable(project)) {
|
|
26384
26755
|
directory = project;
|
|
26385
|
-
await
|
|
26756
|
+
await writeFile3(join11(project, ".gitignore"), "*\n", { flag: "wx" }).catch((e) => {
|
|
26386
26757
|
if (e.code !== "EEXIST") throw e;
|
|
26387
26758
|
});
|
|
26388
26759
|
}
|
|
26389
26760
|
if (!directory) return void 0;
|
|
26390
26761
|
const now = (controls.now ?? Date.now)();
|
|
26391
|
-
for (const name of await
|
|
26762
|
+
for (const name of await readdir5(directory).catch(() => [])) {
|
|
26392
26763
|
if (name === ".gitignore") continue;
|
|
26393
|
-
const stat3 = await
|
|
26394
|
-
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);
|
|
26395
26766
|
}
|
|
26396
|
-
return { save:
|
|
26767
|
+
return { save: join11(directory, "save.json"), commit: join11(directory, "commit.json") };
|
|
26397
26768
|
}
|
|
26398
26769
|
async function discardAgentInput(root, file2, controls = {}) {
|
|
26399
26770
|
try {
|
|
26400
|
-
const stat3 = await
|
|
26771
|
+
const stat3 = await info4(file2);
|
|
26401
26772
|
if (!stat3?.isFile() || stat3.isSymbolicLink()) return false;
|
|
26402
|
-
const parent = comparable(await realpath3(
|
|
26773
|
+
const parent = comparable(await realpath3(dirname4(file2)));
|
|
26403
26774
|
const { system, project } = await candidates(root, controls);
|
|
26404
26775
|
let managed = false;
|
|
26405
26776
|
for (const directory of [system, project]) {
|
|
26406
|
-
if (await
|
|
26777
|
+
if (await info4(directory) && comparable(await realpath3(directory)) === parent) managed = true;
|
|
26407
26778
|
}
|
|
26408
26779
|
if (!managed || basename(file2) === ".gitignore") return false;
|
|
26409
26780
|
await unlink4(file2);
|
|
@@ -26415,13 +26786,13 @@ async function discardAgentInput(root, file2, controls = {}) {
|
|
|
26415
26786
|
|
|
26416
26787
|
// src/commands/spec-preview-commit.ts
|
|
26417
26788
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
26418
|
-
import { readFile as readFile10, mkdir as
|
|
26419
|
-
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";
|
|
26420
26791
|
|
|
26421
26792
|
// src/commands/spec-commit-files.ts
|
|
26422
26793
|
import { createHash as createHash6 } from "node:crypto";
|
|
26423
|
-
import { lstat as
|
|
26424
|
-
import { join as
|
|
26794
|
+
import { lstat as lstat8, readFile as readFile9 } from "node:fs/promises";
|
|
26795
|
+
import { join as join12 } from "node:path";
|
|
26425
26796
|
var fail6 = (message) => {
|
|
26426
26797
|
throw new SpecPreviewError(message);
|
|
26427
26798
|
};
|
|
@@ -26430,7 +26801,7 @@ var optional2 = (path3) => readFile9(path3).catch((e) => {
|
|
|
26430
26801
|
if (e.code === "ENOENT") return null;
|
|
26431
26802
|
throw e;
|
|
26432
26803
|
});
|
|
26433
|
-
var
|
|
26804
|
+
var info5 = (path3) => lstat8(path3).catch((e) => {
|
|
26434
26805
|
if (e.code === "ENOENT") return void 0;
|
|
26435
26806
|
throw e;
|
|
26436
26807
|
});
|
|
@@ -26442,11 +26813,11 @@ async function fingerprint(root, path3) {
|
|
|
26442
26813
|
validPath2(path3);
|
|
26443
26814
|
const parts = path3.split("/");
|
|
26444
26815
|
for (let i = 1; i <= parts.length; i++) {
|
|
26445
|
-
const target =
|
|
26446
|
-
const stat3 = await
|
|
26816
|
+
const target = join12(root, ...parts.slice(0, i));
|
|
26817
|
+
const stat3 = await info5(target);
|
|
26447
26818
|
if (!stat3) return null;
|
|
26448
26819
|
if (stat3.isSymbolicLink() || (i < parts.length ? !stat3.isDirectory() : !stat3.isFile())) fail6(t2("commitFiles.linkOrDirectory"));
|
|
26449
|
-
if (i < parts.length && await
|
|
26820
|
+
if (i < parts.length && await info5(join12(target, ".git"))) fail6(t2("commitFiles.nestedRepository"));
|
|
26450
26821
|
if (i === parts.length) {
|
|
26451
26822
|
if (stat3.nlink !== 1 || stat3.size > 16 * 1024 * 1024) fail6(t2("commitFiles.hardLinkOrSize"));
|
|
26452
26823
|
return hash2(Buffer.concat([Buffer.from(String(stat3.mode & 73) + "\0"), await readFile9(target)]));
|
|
@@ -26477,9 +26848,9 @@ function policyPaths(files) {
|
|
|
26477
26848
|
return [...all].sort();
|
|
26478
26849
|
}
|
|
26479
26850
|
async function checkLegacySelection(root, selected) {
|
|
26480
|
-
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)\/.+$/;
|
|
26481
26852
|
for (const p of selected.filter((p2) => p2.startsWith(".gitifact/") && !record2(p2) && p2 !== ".gitifact/config.json")) {
|
|
26482
|
-
if (
|
|
26853
|
+
if (isAssetPath(p)) continue;
|
|
26483
26854
|
if (!legacyJson.test(p) || await fingerprint(root, p) !== null) fail6(t2("commitFiles.legacyDeleteOnly", { path: p }));
|
|
26484
26855
|
}
|
|
26485
26856
|
for (const p of selected.filter((p2) => p2.startsWith(".tryce/"))) {
|
|
@@ -26512,8 +26883,8 @@ async function previewCommit(cwd, action, input2) {
|
|
|
26512
26883
|
});
|
|
26513
26884
|
const indexPath = (await git(["rev-parse", "--path-format=absolute", "--git-path", "index"])).toString("utf8").trim();
|
|
26514
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();
|
|
26515
|
-
const busy =
|
|
26516
|
-
if (await
|
|
26886
|
+
const busy = join13(gitDir, "gitifact-spec-commit.lock");
|
|
26887
|
+
if (await info5(busy)) fail6(t2("commit.busy", { path: busy }));
|
|
26517
26888
|
if ((await staged()).length) fail6(t2("commit.existingStagingPlan"));
|
|
26518
26889
|
const request = object3(input2);
|
|
26519
26890
|
let plan;
|
|
@@ -26570,33 +26941,33 @@ async function previewCommit(cwd, action, input2) {
|
|
|
26570
26941
|
if (!("plan" in refreshed) || refreshed.plan.digest !== plan.digest) fail6(t2("commit.planStale"));
|
|
26571
26942
|
const before = await reader.baseline();
|
|
26572
26943
|
const original = await optional2(indexPath);
|
|
26573
|
-
await
|
|
26574
|
-
const draftLock =
|
|
26944
|
+
await mkdir6(busy);
|
|
26945
|
+
const draftLock = join13(gitDir, "gitifact-spec-preview.lock");
|
|
26575
26946
|
let draftOwned = false;
|
|
26576
26947
|
let indexLock;
|
|
26577
26948
|
let uncertain = false;
|
|
26578
26949
|
let commitStarted = false;
|
|
26579
|
-
const temporary =
|
|
26950
|
+
const temporary = join13(gitDir, "gitifact-commit-index-" + randomUUID6());
|
|
26580
26951
|
const checkFiles = async () => {
|
|
26581
26952
|
for (const f of [...plan.files, ...plan.context]) if (f.hash !== await fingerprint(root, f.path)) fail6(t2("commit.fileChangedPreparing", { path: f.path }));
|
|
26582
26953
|
if ((await readLockedPreviewState(root)).stamp !== object3(plan.verification).stamp) fail6(t2("commit.specsChangedPreparing"));
|
|
26583
|
-
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"));
|
|
26584
26955
|
};
|
|
26585
26956
|
try {
|
|
26586
26957
|
await verifyPreparedPreview(cwd, plan.verification, false);
|
|
26587
|
-
await
|
|
26958
|
+
await mkdir6(draftLock);
|
|
26588
26959
|
draftOwned = true;
|
|
26589
26960
|
indexLock = await open4(indexPath + ".lock", "wx", 384);
|
|
26590
26961
|
if (plan.index !== hash2(await optional2(indexPath) ?? Buffer.alloc(0)) || (await staged()).length) fail6(t2("commit.indexChangedLocked"));
|
|
26591
|
-
await
|
|
26592
|
-
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" });
|
|
26593
26964
|
else await git(["read-tree", "--empty"], temporary);
|
|
26594
26965
|
await git(["add", "--", ...selected], temporary);
|
|
26595
26966
|
const actual = await staged(temporary);
|
|
26596
26967
|
if (!actual.length || actual.some((p) => !selected.includes(p))) fail6(t2("commit.unexpectedFiles"));
|
|
26597
26968
|
for (const file2 of plan.files.filter((f) => record2(f.path) && f.hash !== null)) {
|
|
26598
26969
|
const stagedBytes = await git(["show", ":" + file2.path], temporary);
|
|
26599
|
-
if (!stagedBytes.equals(await readFile10(
|
|
26970
|
+
if (!stagedBytes.equals(await readFile10(join13(root, file2.path)))) fail6(t2("commit.filterChanged"));
|
|
26600
26971
|
}
|
|
26601
26972
|
await checkFiles();
|
|
26602
26973
|
if (JSON.stringify(before) !== JSON.stringify(await reader.baseline())) fail6(t2("commit.headChanged"));
|
|
@@ -26635,7 +27006,7 @@ async function previewCommit(cwd, action, input2) {
|
|
|
26635
27006
|
});
|
|
26636
27007
|
await unlink5(temporary).catch(() => {
|
|
26637
27008
|
});
|
|
26638
|
-
await unlink5(
|
|
27009
|
+
await unlink5(join13(busy, "recovery.json")).catch(() => {
|
|
26639
27010
|
});
|
|
26640
27011
|
await rmdir2(busy);
|
|
26641
27012
|
if (draftOwned) await rmdir2(draftLock);
|
|
@@ -26645,8 +27016,8 @@ async function previewCommit(cwd, action, input2) {
|
|
|
26645
27016
|
|
|
26646
27017
|
// src/commands/spec-commit.ts
|
|
26647
27018
|
import { createHash as createHash8, randomUUID as randomUUID7 } from "node:crypto";
|
|
26648
|
-
import { mkdir as
|
|
26649
|
-
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";
|
|
26650
27021
|
var fields = ["reasons", "paths", "message", "authorization", "requirements", "policyFiles", "expected"];
|
|
26651
27022
|
var integration = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply"];
|
|
26652
27023
|
var same2 = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
@@ -26685,9 +27056,9 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26685
27056
|
...stdin ? { input: stdin } : {}
|
|
26686
27057
|
});
|
|
26687
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();
|
|
26688
|
-
const busy =
|
|
27059
|
+
const busy = join14(gitDir, "gitifact-spec-commit.lock");
|
|
26689
27060
|
await failOnLegacyLock(gitDir);
|
|
26690
|
-
if (await
|
|
27061
|
+
if (await info5(busy)) fail6(t2("commit.busy", { path: busy }));
|
|
26691
27062
|
if ((await staged()).length) fail6(t2("commit.existingStaging"));
|
|
26692
27063
|
const indexHash = hash2(await optional2(indexPath) ?? Buffer.alloc(0));
|
|
26693
27064
|
const current = c.working;
|
|
@@ -26737,16 +27108,16 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26737
27108
|
const trailers = [
|
|
26738
27109
|
...requirements.map((id) => "Gitifact-Req: " + id),
|
|
26739
27110
|
...prepared.changes.filter((c2) => c2.kind === "design").map((c2) => "Gitifact-Design: " + c2.id),
|
|
26740
|
-
...prepared.changes.filter((c2) => c2.kind === "
|
|
27111
|
+
...prepared.changes.filter((c2) => c2.kind === "wiki").map((c2) => "Gitifact-Doc: " + c2.id)
|
|
26741
27112
|
];
|
|
26742
|
-
const temporary =
|
|
27113
|
+
const temporary = join14(gitDir, "gitifact-commit-index-" + randomUUID7());
|
|
26743
27114
|
let owned = false;
|
|
26744
27115
|
let uncertain = false;
|
|
26745
27116
|
let indexLock;
|
|
26746
27117
|
let committed;
|
|
26747
27118
|
try {
|
|
26748
27119
|
try {
|
|
26749
|
-
await
|
|
27120
|
+
await mkdir7(busy);
|
|
26750
27121
|
owned = true;
|
|
26751
27122
|
} catch (e) {
|
|
26752
27123
|
if (e.code === "EEXIST") fail6(t2("commit.busy", { path: busy }));
|
|
@@ -26760,8 +27131,8 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26760
27131
|
if (hash2(original ?? Buffer.alloc(0)) !== indexHash || (await staged()).length) fail6(t2("commit.indexChanged"));
|
|
26761
27132
|
const locked = await hashes([.../* @__PURE__ */ new Set([...selected, ...contextPaths])]);
|
|
26762
27133
|
for (const [path3, value] of started) if (locked.get(path3) !== value) fail6(t2("commit.fileChangedRunning", { path: path3 }));
|
|
26763
|
-
await
|
|
26764
|
-
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" });
|
|
26765
27136
|
else await git(["read-tree", "--empty"], temporary);
|
|
26766
27137
|
await git(["add", "--", ...stageable], temporary);
|
|
26767
27138
|
const actual = await staged(temporary);
|
|
@@ -26776,7 +27147,7 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26776
27147
|
const size = Number(header[2]);
|
|
26777
27148
|
if (end < 0 || header[1] !== "blob" || !Number.isSafeInteger(size) || output2[end + 1 + size] !== 10) fail6(t2("commit.stagedUnreadable", { path: path3 }));
|
|
26778
27149
|
const staged2 = output2.subarray(end + 1, end + 1 + size);
|
|
26779
|
-
const file2 = await readFile11(
|
|
27150
|
+
const file2 = await readFile11(join14(root, path3));
|
|
26780
27151
|
if (!staged2.equals(file2) && !staged2.equals(Buffer.from(file2.toString("latin1").replace(/\r\n/g, "\n"), "latin1"))) fail6(t2("commit.filterChanged"));
|
|
26781
27152
|
offset = end + size + 2;
|
|
26782
27153
|
}
|
|
@@ -26784,7 +27155,7 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26784
27155
|
const again = await hashes([...locked.keys()]);
|
|
26785
27156
|
for (const [path3, value] of locked) if (again.get(path3) !== value) fail6(t2("commit.fileChangedPreparing", { path: path3 }));
|
|
26786
27157
|
if ((await readLockedPreviewState(root)).stamp !== published.stamp) fail6(t2("commit.specsChangedPreparing"));
|
|
26787
|
-
for (const name of integration) if (await
|
|
27158
|
+
for (const name of integration) if (await info5(join14(gitDir, name))) fail6(t2("commit.integrationStarted"));
|
|
26788
27159
|
if (!same2(c.base, await c.reader.baseline())) fail6(t2("commit.headChanged"));
|
|
26789
27160
|
const tree = (await git(["write-tree"], temporary)).toString("utf8").trim();
|
|
26790
27161
|
commitStarted = true;
|
|
@@ -26826,7 +27197,7 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
26826
27197
|
await unlink6(temporary).catch(() => {
|
|
26827
27198
|
});
|
|
26828
27199
|
if (owned) {
|
|
26829
|
-
await unlink6(
|
|
27200
|
+
await unlink6(join14(busy, "recovery.json")).catch(() => {
|
|
26830
27201
|
});
|
|
26831
27202
|
await rmdir3(busy);
|
|
26832
27203
|
}
|
|
@@ -26843,7 +27214,7 @@ async function execute(action, options, controls) {
|
|
|
26843
27214
|
try {
|
|
26844
27215
|
const guard = specPreviewReader(process.cwd());
|
|
26845
27216
|
const { root, gitDir, objectFormat } = await guard.location();
|
|
26846
|
-
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 }));
|
|
26847
27218
|
if (await guard.hasUnmerged()) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
26848
27219
|
const raw = await readConfigFile(root);
|
|
26849
27220
|
if (raw === void 0) throw new SpecPreviewError(t2("preview.notInitialized"));
|
|
@@ -26875,7 +27246,7 @@ async function execute(action, options, controls) {
|
|
|
26875
27246
|
if (action === "read") {
|
|
26876
27247
|
const commit = await reader.resolve(options.ref ?? "HEAD");
|
|
26877
27248
|
const bundle = await reader.readBundle(commit);
|
|
26878
|
-
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");
|
|
26879
27250
|
} else {
|
|
26880
27251
|
const [from, to] = await Promise.all([reader.resolve(options.from), reader.resolve(options.to)]);
|
|
26881
27252
|
const [before, after] = await Promise.all([reader.readBundle(from), reader.readBundle(to)]);
|
|
@@ -26888,8 +27259,8 @@ async function execute(action, options, controls) {
|
|
|
26888
27259
|
}
|
|
26889
27260
|
}
|
|
26890
27261
|
async function readInputFile(path3) {
|
|
26891
|
-
const
|
|
26892
|
-
if (!
|
|
27262
|
+
const source2 = await stat2(path3);
|
|
27263
|
+
if (!source2.isFile() || source2.size > 1024 * 1024) throw new SpecPreviewError(t2("preview.inputFile"));
|
|
26893
27264
|
return readFile12(path3);
|
|
26894
27265
|
}
|
|
26895
27266
|
async function readStdin(stream) {
|
|
@@ -26904,29 +27275,29 @@ async function readStdin(stream) {
|
|
|
26904
27275
|
}
|
|
26905
27276
|
function narrowWorking(working, options, inputs) {
|
|
26906
27277
|
if (options.stamp) return { stamp: working.stamp, inputs };
|
|
26907
|
-
let { specs,
|
|
27278
|
+
let { specs, wiki, warnings } = working;
|
|
26908
27279
|
if (options.feature !== void 0) {
|
|
26909
27280
|
specs = specs.filter((s) => s.path === ".gitifact/spec/" + options.feature + "/requirements.md");
|
|
26910
27281
|
if (!specs.length) throw new SpecPreviewError(t2("preview.unknownFeature", { feature: options.feature }));
|
|
26911
|
-
|
|
26912
|
-
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);
|
|
26913
27284
|
}
|
|
26914
|
-
if (!options.ids) return { stamp: working.stamp, inputs, specs,
|
|
27285
|
+
if (!options.ids) return { stamp: working.stamp, inputs, specs, wiki, warnings };
|
|
26915
27286
|
return {
|
|
26916
27287
|
stamp: working.stamp,
|
|
26917
27288
|
inputs,
|
|
26918
27289
|
warnings,
|
|
26919
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 })) })),
|
|
26920
|
-
|
|
27291
|
+
wiki: { documents: wiki.documents.map((d) => ({ id: d.id, path: d.path, title: d.title })) }
|
|
26921
27292
|
};
|
|
26922
27293
|
}
|
|
26923
27294
|
|
|
26924
27295
|
// src/commands/migrate.ts
|
|
26925
|
-
import { lstat as
|
|
26926
|
-
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";
|
|
26927
27298
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
26928
27299
|
var operations2 = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"];
|
|
26929
|
-
var
|
|
27300
|
+
var info6 = (path3) => lstat10(path3).catch((e) => {
|
|
26930
27301
|
if (e.code === "ENOENT") return void 0;
|
|
26931
27302
|
throw e;
|
|
26932
27303
|
});
|
|
@@ -26937,40 +27308,40 @@ var fail7 = (code, message) => {
|
|
|
26937
27308
|
async function migrateStore(cwd, dryRun = false) {
|
|
26938
27309
|
const reader = specPreviewReader(cwd);
|
|
26939
27310
|
const { root, gitDir } = await reader.location();
|
|
26940
|
-
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 }));
|
|
26941
27312
|
if (await reader.hasUnmerged()) fail7("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
|
|
26942
|
-
for (const name of [...LEGACY_LOCKS, "gitifact-spec-preview.lock", "gitifact-spec-commit.lock"]) if (await
|
|
26943
|
-
const from =
|
|
26944
|
-
const to =
|
|
26945
|
-
const fromInfo = await
|
|
26946
|
-
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"));
|
|
26947
27318
|
if (!fromInfo) return fail7("NOTHING_TO_MIGRATE", t2("migrate.nothingToMigrate"));
|
|
26948
27319
|
if (!fromInfo.isDirectory() || fromInfo.isSymbolicLink()) fail7("PATH_CONFLICT", t2("migrate.notDirectory"));
|
|
26949
|
-
const configInfo = await
|
|
27320
|
+
const configInfo = await info6(join16(from, "config.json"));
|
|
26950
27321
|
if (!configInfo?.isFile() || configInfo.isSymbolicLink()) fail7("NOT_INITIALIZED", t2("migrate.noConfig"));
|
|
26951
|
-
const config2 = parseManagedConfig(decode4(await readFile13(
|
|
27322
|
+
const config2 = parseManagedConfig(decode4(await readFile13(join16(from, "config.json"))));
|
|
26952
27323
|
if (!("schemaVersion" in config2)) fail7("UNSUPPORTED_FORMAT", t2("migrate.legacyJson"));
|
|
26953
|
-
const top = (await
|
|
27324
|
+
const top = (await readdir6(from)).sort();
|
|
26954
27325
|
if (top.some((name) => name !== "config.json" && name !== "spec")) fail7("UNSUPPORTED_FORMAT", t2("migrate.extraEntries"));
|
|
26955
27326
|
const files = /* @__PURE__ */ new Map();
|
|
26956
27327
|
const rewrites = [];
|
|
26957
27328
|
const legacyRecords = [];
|
|
26958
27329
|
const marker2 = /^<!-- tryce-(spec|req|design|ref): /;
|
|
26959
27330
|
if (top.includes("spec")) {
|
|
26960
|
-
for (const feature2 of (await
|
|
26961
|
-
const dir =
|
|
26962
|
-
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);
|
|
26963
27334
|
if (!stat3?.isDirectory() || stat3.isSymbolicLink()) fail7("UNSUPPORTED_FORMAT", t2("migrate.notSpecFolder", { feature: feature2 }));
|
|
26964
|
-
for (const name of (await
|
|
27335
|
+
for (const name of (await readdir6(dir)).sort()) {
|
|
26965
27336
|
if (!["requirements.md", "design.md", "history.jsonl", "tryce.json"].includes(name)) fail7("UNSUPPORTED_FORMAT", t2("migrate.unsupportedFile", { feature: feature2, name }));
|
|
26966
|
-
const fileStat = await
|
|
27337
|
+
const fileStat = await info6(join16(dir, name));
|
|
26967
27338
|
if (!fileStat?.isFile() || fileStat.isSymbolicLink() || fileStat.nlink !== 1 || fileStat.size > 1024 * 1024) fail7("UNSUPPORTED_FORMAT", t2("migrate.fileTooLarge", { name }));
|
|
26968
27339
|
const newPath = `.gitifact/spec/${feature2}/${name}`;
|
|
26969
27340
|
if (name === "tryce.json") {
|
|
26970
27341
|
legacyRecords.push(newPath);
|
|
26971
27342
|
continue;
|
|
26972
27343
|
}
|
|
26973
|
-
const text2 = decode4(await readFile13(
|
|
27344
|
+
const text2 = decode4(await readFile13(join16(dir, name)));
|
|
26974
27345
|
if (name === "history.jsonl") {
|
|
26975
27346
|
files.set(newPath, text2);
|
|
26976
27347
|
continue;
|
|
@@ -27015,9 +27386,9 @@ async function migrateStore(cwd, dryRun = false) {
|
|
|
27015
27386
|
if (dryRun) return result("planned");
|
|
27016
27387
|
await rename5(from, to);
|
|
27017
27388
|
for (const r of rewrites) {
|
|
27018
|
-
const full =
|
|
27019
|
-
const temp =
|
|
27020
|
-
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" });
|
|
27021
27392
|
try {
|
|
27022
27393
|
await rename5(temp, full);
|
|
27023
27394
|
} finally {
|
|
@@ -27039,16 +27410,16 @@ async function runMigrate(options) {
|
|
|
27039
27410
|
}
|
|
27040
27411
|
|
|
27041
27412
|
// src/main.ts
|
|
27042
|
-
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());
|
|
27043
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) => {
|
|
27044
27415
|
await runStatus(options.format);
|
|
27045
27416
|
});
|
|
27046
|
-
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.
|
|
27047
|
-
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"));
|
|
27048
27419
|
program2.command("docs").description(t2("help.docs")).argument("[topic]", t2("help.docsTopic")).allowExcessArguments(false).action(async (topic) => {
|
|
27049
27420
|
await runDocs(topic);
|
|
27050
27421
|
});
|
|
27051
|
-
program2.command("update").description(t2("help.update")).allowExcessArguments(false).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).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"));
|
|
27052
27423
|
program2.command("migrate").description(t2("help.migrate")).allowExcessArguments(false).option("--dry-run", t2("help.migrateDryRun")).action(runMigrate);
|
|
27053
27424
|
var spec = program2.command("spec").description(t2("help.spec"));
|
|
27054
27425
|
var replaced = t2("help.deprecated");
|