gitifact 0.6.2 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -49
- package/dist/browser/assets/{Grid-D6Cs5W3k.js → Grid-DlI9bhVm.js} +1 -1
- package/dist/browser/assets/{Markdown-B-gnkr0l.js → Markdown-68DgpF6D.js} +5 -5
- package/dist/browser/assets/MetadataListItem-Bvdm7SCW.js +1 -0
- package/dist/browser/assets/{Table-OoWAxVgg.js → Table-GZptlOQa.js} +2 -2
- package/dist/browser/assets/about-LBmmoJtj.js +3 -0
- package/dist/browser/assets/activity-DRgs2s8a.jpg +0 -0
- package/dist/browser/assets/{activity-gA1jJdpp.js → activity-paVxugse.js} +1 -1
- package/dist/browser/assets/changelog-DMndHW2p.js +2 -0
- package/dist/browser/assets/{contributors._email-DHQfeuSG.js → contributors._email-Ch0pZAte.js} +1 -1
- package/dist/browser/assets/{contributors.index-Bd2NXXUX.js → contributors.index-BwSL0x8o.js} +1 -1
- package/dist/browser/assets/document-Ceyg9sKE.js +11 -0
- package/dist/browser/assets/feature-requirements-Ci6Hez1P.jpg +0 -0
- package/dist/browser/assets/{features._featureId-D69yHwjx.js → features._featureId-DtlIpe3Y.js} +1 -1
- package/dist/browser/assets/{features.index-Ct7YPi11.js → features.index-CsxYbWGn.js} +1 -1
- package/dist/browser/assets/getting-started-7e77o6gE.css +1 -0
- package/dist/browser/assets/getting-started-LVh16CZ8.js +1 -0
- package/dist/browser/assets/git-H0K9dpC3.js +1 -0
- package/dist/browser/assets/index-DClmARNh.js +48 -0
- package/dist/browser/assets/page-header-sbYRXZP4.js +531 -0
- package/dist/browser/assets/product-BIGIVBUa.js +10 -0
- package/dist/browser/assets/product-DNaVAIwO.css +1 -0
- package/dist/browser/assets/{product.index-CMOZsrSy.js → product.index-vzVtG_rC.js} +1 -1
- package/dist/browser/assets/project-wiki-BBWDVTfk.jpg +0 -0
- package/dist/browser/assets/request-state-J0QLm_8G.js +1 -0
- package/dist/browser/assets/{requirements-D5-_AEko.js → requirements-POF-07kZ.js} +1 -1
- package/dist/browser/assets/settings-eas7Zq56.js +1 -0
- package/dist/browser/assets/{wiki-Dh9TPhWY.js → wiki-fpAHbhnh.js} +1 -1
- package/dist/browser/index.html +10 -10
- package/dist/i18n/en/block.md +48 -0
- package/dist/i18n/en/changelog.md +185 -0
- package/dist/i18n/en/docs/commit.md +45 -0
- package/dist/i18n/en/docs/design.md +52 -0
- package/dist/i18n/en/docs/spec.md +62 -0
- package/dist/i18n/en/docs/wiki.default.md +27 -0
- package/dist/i18n/en/docs/wiki.md +43 -0
- package/dist/i18n/en/docs/workflow.md +72 -0
- package/dist/i18n/en/docs/writing.md +54 -0
- package/dist/i18n/ko/block.md +5 -5
- package/dist/i18n/ko/changelog.md +20 -0
- package/dist/i18n/ko/docs/workflow.md +8 -2
- package/dist/i18n/ko/docs/writing.md +1 -1
- package/dist/main.js +770 -185
- package/package.json +4 -4
- package/dist/browser/assets/MetadataListItem-CUyEFaPI.js +0 -1
- package/dist/browser/assets/about-Jsk-9EI-.js +0 -3
- package/dist/browser/assets/changelog-CiWFK5pK.js +0 -2
- package/dist/browser/assets/document-C8DLwdPF.js +0 -11
- package/dist/browser/assets/git-CStwM7mH.js +0 -1
- package/dist/browser/assets/index-jPjBDJh1.js +0 -48
- package/dist/browser/assets/page-header-Bn6J9gVJ.js +0 -147
- package/dist/browser/assets/product-BSEt07YP.css +0 -1
- package/dist/browser/assets/product-Beikpygp.js +0 -10
- package/dist/browser/assets/request-state-DkerHkm2.js +0 -1
- package/dist/browser/assets/settings-rKkznd6Q.js +0 -1
package/dist/main.js
CHANGED
|
@@ -3514,10 +3514,130 @@ var messages_default = {
|
|
|
3514
3514
|
"config.newerSchema": "\uC774 \uD504\uB85C\uC81D\uD2B8\uB294 \uB354 \uC0C8\uB85C\uC6B4 \uC800\uC7A5 \uADDC\uC57D(schemaVersion {version})\uC744 \uC501\uB2C8\uB2E4. npm install -g gitifact@latest\uB85C CLI\uB97C \uC62C\uB9B0 \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694."
|
|
3515
3515
|
};
|
|
3516
3516
|
|
|
3517
|
+
// ../../packages/core/dist/shared/i18n/en/messages.json
|
|
3518
|
+
var messages_default2 = {
|
|
3519
|
+
"config.jsonUnreadable": "Could not read configuration JSON. Preserve the original file.",
|
|
3520
|
+
"config.unsupportedFormat": "Unsupported project format. Use a compatible CLI.",
|
|
3521
|
+
"config.invalidFields": "Invalid project configuration fields or values.",
|
|
3522
|
+
"config.jsonUnreadableShort": "Could not read configuration JSON.",
|
|
3523
|
+
"config.schemaFields": "Check the schemaVersion and baseline fields.",
|
|
3524
|
+
"document.pathTooLong": "Wiki page path exceeds the depth or length limit: {path}",
|
|
3525
|
+
"document.fileName": "Wiki filenames must use lowercase letters, digits, hyphens and .md; uppercase root names such as README.md and ARCHITECTURE.md are allowed: {path}",
|
|
3526
|
+
"document.folderName": "Wiki folder names must use lowercase letters, digits and hyphens: {path}",
|
|
3527
|
+
"design.invalidCharacters": "Invalid characters in the design.",
|
|
3528
|
+
"design.idMismatch": "The design ID must match the feature's S-ID.",
|
|
3529
|
+
"design.titleRequired": "A design title is required.",
|
|
3530
|
+
"design.bodyRequired": "Design content is required.",
|
|
3531
|
+
"design.singleTopHeading": "A design must have exactly one top-level heading.",
|
|
3532
|
+
"design.referenceFormat": "Check the design reference comment format.",
|
|
3533
|
+
"design.unclosedFence": "Unclosed code fence in the design.",
|
|
3534
|
+
"document.unsupportedPath": "Unsupported wiki path: {path}",
|
|
3535
|
+
"document.invalidCharacters": "Invalid document characters: {path}",
|
|
3536
|
+
"document.titleRequired": "A document title is required: {path}",
|
|
3537
|
+
"document.bodyRequired": "Document content is required: {path}",
|
|
3538
|
+
"document.singleTopHeading": "A document must have exactly one top-level heading: {path}",
|
|
3539
|
+
"document.noMarkerInBody": "Gitifact comments are not allowed in document content: {path}",
|
|
3540
|
+
"document.unclosedFence": "Unclosed document code fence: {path}",
|
|
3541
|
+
"document.duplicateId": "Duplicate wiki page ID in the repository: {id}",
|
|
3542
|
+
"document.caseOnlyPath": "Document paths differ only in case: {path}",
|
|
3543
|
+
"history.duplicateId": "Duplicate history record ID in the repository: {id}",
|
|
3544
|
+
"spec.orphanFile": "Design or reason file without a requirements document: {path}",
|
|
3545
|
+
"spec.unsupportedPath": "Unsupported specification path: {path}",
|
|
3546
|
+
"spec.invalidCharacters": "Invalid specification characters: {path}",
|
|
3547
|
+
"spec.requirementIdRequired": "A valid ID is required directly below the requirement heading: {path}",
|
|
3548
|
+
"spec.invalidFence": "Invalid code fence.",
|
|
3549
|
+
"spec.titleOnce": "The document title must appear once, at the beginning: {path}",
|
|
3550
|
+
"spec.emptyTitle": "Empty document title.",
|
|
3551
|
+
"spec.titleMissing": "Missing document title: {path}",
|
|
3552
|
+
"spec.emptyRequirementTitle": "Empty requirement title.",
|
|
3553
|
+
"spec.commentPosition": "Unsupported HTML comment position: {path}",
|
|
3554
|
+
"spec.atxOnly": "Only ATX headings starting in column one are supported: {path}",
|
|
3555
|
+
"spec.incomplete": "Incomplete specification: {path}",
|
|
3556
|
+
"spec.emptyOrDuplicateRequirement": "Empty content or duplicate requirement ID: {id}",
|
|
3557
|
+
"history.invalidJsonl": "Could not parse history JSONL.",
|
|
3558
|
+
"history.invalidRecord": "Invalid history record.",
|
|
3559
|
+
"history.invalidFields": "Invalid history record fields, IDs or targets.",
|
|
3560
|
+
"spec.duplicateId": "Duplicate specification ID: {id}",
|
|
3561
|
+
"spec.duplicateRequirementId": "Duplicate requirement ID in the repository: {id}",
|
|
3562
|
+
"history.changed": "An existing history record was modified: {id}",
|
|
3563
|
+
"history.unlinkedRequirement": "A new history record is not linked to this requirement change: {id}",
|
|
3564
|
+
"history.unlinkedDocument": "A new history record is not linked to this document change: {id}",
|
|
3565
|
+
"edit.invalidFeatureName": "Invalid feature folder name.",
|
|
3566
|
+
"edit.operationCount": "operations must contain 1 to 100 entries.",
|
|
3567
|
+
"edit.idGenerated": "Invalid generated ID.",
|
|
3568
|
+
"edit.idUnavailable": "Could not allocate a unique ID.",
|
|
3569
|
+
"edit.invalidOperation": "Invalid edit input.",
|
|
3570
|
+
"edit.unsupportedField": "Unsupported edit field.",
|
|
3571
|
+
"edit.emptyField": "Empty edit field: {field}",
|
|
3572
|
+
"edit.invalidCharacters": "Invalid characters in edit field: {field}",
|
|
3573
|
+
"edit.specNotFound": "Target specification not found: {path}",
|
|
3574
|
+
"edit.samePath": "Already at the same path.",
|
|
3575
|
+
"edit.documentPathExists": "Document path already exists: {path}",
|
|
3576
|
+
"edit.featureExists": "Feature already exists.",
|
|
3577
|
+
"edit.noDesignToDelete": "No design to delete.",
|
|
3578
|
+
"edit.specIdNotFound": "Specification ID not found.",
|
|
3579
|
+
"edit.requirementIdNotFound": "Requirement ID not found.",
|
|
3580
|
+
"edit.sameSpec": "Already belongs to this specification.",
|
|
3581
|
+
"edit.bodyChangesSpecStructure": "The content changes the specification structure.",
|
|
3582
|
+
"edit.bodyChangesDocumentStructure": "The content changes the document structure: {path}",
|
|
3583
|
+
"reason.keepEmptySpec": "Keep an empty specification to preserve its existing reasons: {id}",
|
|
3584
|
+
"reason.committedImmutable": "Committed reasons cannot be edited, deleted or reassigned: {id}",
|
|
3585
|
+
"reason.committedDocumentImmutable": "Committed wiki reasons cannot be edited, deleted or reassigned.",
|
|
3586
|
+
"reason.count": "reasons must be an array with at most 100 entries.",
|
|
3587
|
+
"reason.idGenerated": "Invalid generated reason ID.",
|
|
3588
|
+
"reason.idUnavailable": "Could not allocate a unique reason ID.",
|
|
3589
|
+
"reason.invalidInput": "Invalid reason input.",
|
|
3590
|
+
"reason.targetsRequired": "Check the reason and target requirements.",
|
|
3591
|
+
"reason.documentWithRequirements": "Document reasons cannot also target requirements.",
|
|
3592
|
+
"reason.documentTargetInvalid": "Invalid or duplicate document reason targets.",
|
|
3593
|
+
"reason.documentUnchanged": "Cannot attach a reason to a document with no final change: {id}",
|
|
3594
|
+
"reason.invalidTarget": "Invalid reason target.",
|
|
3595
|
+
"reason.targetKind": "Check the reason target type.",
|
|
3596
|
+
"reason.targetInvalid": "Invalid or duplicate reason targets.",
|
|
3597
|
+
"reason.requirementUnchanged": "Cannot attach a reason to a requirement with no final change: {id}",
|
|
3598
|
+
"reason.keepDeletedSpec": "Keep the original specification to record the deletion reason.",
|
|
3599
|
+
"readError.GIT_NOT_FOUND": "Git executable not found.",
|
|
3600
|
+
"readError.GIT_CONTEXT_OVERRIDE": "Environment variables override the Git context of the current checkout.",
|
|
3601
|
+
"readError.NOT_A_REPOSITORY": "The current directory is not inside a Git working tree.",
|
|
3602
|
+
"readError.UNSUPPORTED_REPOSITORY": "Cannot read a bare repository or Git administrative directory.",
|
|
3603
|
+
"readError.GIT_UNSUPPORTED": "Required Git options or object format are not supported.",
|
|
3604
|
+
"readError.GIT_FAILED": "Git read failed. Check repository access and Git status.",
|
|
3605
|
+
"readError.INVALID_GIT_OUTPUT": "Git status output does not match the supported format.",
|
|
3606
|
+
"readError.UNSUPPORTED_PATH_ENCODING": "A Git path cannot be represented as UTF-8 without loss.",
|
|
3607
|
+
"readError.REPOSITORY_CHANGED": "Repository state changed while reading. Try again shortly.",
|
|
3608
|
+
"readError.READ_LIMIT_EXCEEDED": "Git read exceeded the time or output limit.",
|
|
3609
|
+
"changelog.atLine": "Release notes, line {line}: {reason}",
|
|
3610
|
+
"changelog.emptyVersion": "Version {version} has no entries.",
|
|
3611
|
+
"changelog.invalidDate": "Invalid date: {text}",
|
|
3612
|
+
"changelog.order": "Versions must be ordered newest first: {version}",
|
|
3613
|
+
"changelog.sectionBeforeVersion": "A section heading appears before a version heading.",
|
|
3614
|
+
"changelog.duplicateSection": "Duplicate {section} section in a version.",
|
|
3615
|
+
"changelog.itemOutsideSection": "Entries must appear under ### Added, Changed, Removed or Fixed.",
|
|
3616
|
+
"changelog.unknownLine": 'Unrecognized line. Use "## X.Y.Z - YYYY-MM-DD" for versions, "### Added|Changed|Removed|Fixed" for sections and "- " for entries: {text}',
|
|
3617
|
+
"changelog.noVersions": "No versions found.",
|
|
3618
|
+
"frontmatter.required": "Frontmatter starting with --- is required on the first line.",
|
|
3619
|
+
"frontmatter.unclosed": "Missing closing --- for frontmatter.",
|
|
3620
|
+
"frontmatter.line": "Unsupported frontmatter line: {line}",
|
|
3621
|
+
"frontmatter.scalar": "Frontmatter values must be plain unquoted strings or double-quoted strings: {value}",
|
|
3622
|
+
"frontmatter.id": "The frontmatter id is not a valid {kind} ID.",
|
|
3623
|
+
"frontmatter.unknownKey": "Unsupported frontmatter key: {key}",
|
|
3624
|
+
"source.invalid": "A source needs a title, either path or url, and an optional note.",
|
|
3625
|
+
"source.url": "Source url must use http(s): {url}",
|
|
3626
|
+
"source.path": "Source path must be relative and end in .md: {path}",
|
|
3627
|
+
"edit.documentIdNotFound": "Wiki page ID not found: {id}",
|
|
3628
|
+
"edit.invalidSources": "sources must be an array with at most 50 entries.",
|
|
3629
|
+
"config.unsupportedSchema": "This project uses an older storage convention (schemaVersion {version}) that this CLI does not read. Pre-release conventions are not migrated. If .gitifact contains only config.json, gitifact init can replace it with the current convention.",
|
|
3630
|
+
"config.newerSchema": "This project uses a newer storage convention (schemaVersion {version}). Run npm install -g gitifact@latest and try again."
|
|
3631
|
+
};
|
|
3632
|
+
|
|
3517
3633
|
// ../../packages/core/dist/shared/i18n/index.js
|
|
3518
3634
|
var defaultLanguage = "ko";
|
|
3519
|
-
var
|
|
3520
|
-
function
|
|
3635
|
+
var resolveLanguage = () => defaultLanguage;
|
|
3636
|
+
function configureCoreLanguage(resolver) {
|
|
3637
|
+
resolveLanguage = resolver;
|
|
3638
|
+
}
|
|
3639
|
+
var catalogs = { ko: messages_default, en: messages_default2 };
|
|
3640
|
+
function t(key, values = {}, lang = resolveLanguage()) {
|
|
3521
3641
|
return catalogs[lang][key].replace(/\{(\w+)\}/g, (whole, name) => name in values ? String(values[name]) : whole);
|
|
3522
3642
|
}
|
|
3523
3643
|
|
|
@@ -3984,13 +4104,13 @@ function parseSpecPreview(path3, source2, history = "", designSource) {
|
|
|
3984
4104
|
const specId = parseFrontmatter(lines, "spec", "S").id;
|
|
3985
4105
|
let title = "";
|
|
3986
4106
|
let description = "";
|
|
3987
|
-
let
|
|
4107
|
+
let current2;
|
|
3988
4108
|
const requirements = [];
|
|
3989
4109
|
let fence;
|
|
3990
4110
|
let waitingId = false;
|
|
3991
4111
|
const append = (line) => {
|
|
3992
|
-
if (
|
|
3993
|
-
|
|
4112
|
+
if (current2)
|
|
4113
|
+
current2.body += line + "\n";
|
|
3994
4114
|
else
|
|
3995
4115
|
description += line + "\n";
|
|
3996
4116
|
};
|
|
@@ -3998,9 +4118,9 @@ function parseSpecPreview(path3, source2, history = "", designSource) {
|
|
|
3998
4118
|
const line = lines[i];
|
|
3999
4119
|
if (waitingId) {
|
|
4000
4120
|
const match = new RegExp(`^<!-- ${marker}-req: (R-${token}) -->$`).exec(line);
|
|
4001
|
-
if (!match || !
|
|
4121
|
+
if (!match || !current2)
|
|
4002
4122
|
fail(t("spec.requirementIdRequired", { path: path3 }));
|
|
4003
|
-
|
|
4123
|
+
current2.id = match[1];
|
|
4004
4124
|
waitingId = false;
|
|
4005
4125
|
continue;
|
|
4006
4126
|
}
|
|
@@ -4019,7 +4139,7 @@ function parseSpecPreview(path3, source2, history = "", designSource) {
|
|
|
4019
4139
|
continue;
|
|
4020
4140
|
}
|
|
4021
4141
|
if (/^# /.test(line)) {
|
|
4022
|
-
if (title ||
|
|
4142
|
+
if (title || current2 || description.trim())
|
|
4023
4143
|
fail(t("spec.titleOnce", { path: path3 }));
|
|
4024
4144
|
title = line.slice(2).trim();
|
|
4025
4145
|
if (!title)
|
|
@@ -4029,10 +4149,10 @@ function parseSpecPreview(path3, source2, history = "", designSource) {
|
|
|
4029
4149
|
if (/^## /.test(line)) {
|
|
4030
4150
|
if (!title)
|
|
4031
4151
|
fail(t("spec.titleMissing", { path: path3 }));
|
|
4032
|
-
|
|
4033
|
-
if (!
|
|
4152
|
+
current2 = { id: "", title: line.slice(3).trim(), body: "" };
|
|
4153
|
+
if (!current2.title)
|
|
4034
4154
|
fail(t("spec.emptyRequirementTitle"));
|
|
4035
|
-
requirements.push(
|
|
4155
|
+
requirements.push(current2);
|
|
4036
4156
|
waitingId = true;
|
|
4037
4157
|
continue;
|
|
4038
4158
|
}
|
|
@@ -4436,9 +4556,9 @@ var fail3 = (message) => {
|
|
|
4436
4556
|
};
|
|
4437
4557
|
var equal = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
4438
4558
|
var historyPath = (s) => s.path.replace(/requirements\.md$/, "history.jsonl");
|
|
4439
|
-
function pendingPreviewReasons(before,
|
|
4559
|
+
function pendingPreviewReasons(before, current2) {
|
|
4440
4560
|
const a = asBundle(before);
|
|
4441
|
-
const b = asBundle(
|
|
4561
|
+
const b = asBundle(current2);
|
|
4442
4562
|
return [
|
|
4443
4563
|
...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 }))),
|
|
4444
4564
|
...b.wiki.history.slice(a.wiki.history.length).map((h) => ({ kind: "wiki", path: WIKI_HISTORY_PATH, ...h }))
|
|
@@ -4446,21 +4566,21 @@ function pendingPreviewReasons(before, current) {
|
|
|
4446
4566
|
}
|
|
4447
4567
|
function finalSpecPreviewChanges(beforeValue, currentValue) {
|
|
4448
4568
|
const before = asBundle(beforeValue);
|
|
4449
|
-
const
|
|
4569
|
+
const current2 = asBundle(currentValue);
|
|
4450
4570
|
validateBundle(before);
|
|
4451
|
-
validateBundle(
|
|
4571
|
+
validateBundle(current2);
|
|
4452
4572
|
for (const previous of before.specs) {
|
|
4453
|
-
const next =
|
|
4573
|
+
const next = current2.specs.find((s) => s.id === previous.id);
|
|
4454
4574
|
if (previous.history.length && !next)
|
|
4455
4575
|
fail3(t("reason.keepEmptySpec", { id: previous.id }));
|
|
4456
4576
|
if (next && !equal(next.history.slice(0, previous.history.length), previous.history))
|
|
4457
4577
|
fail3(t("reason.committedImmutable", { id: previous.id }));
|
|
4458
4578
|
}
|
|
4459
|
-
if (!equal(
|
|
4579
|
+
if (!equal(current2.wiki.history.slice(0, before.wiki.history.length), before.wiki.history))
|
|
4460
4580
|
fail3(t("reason.committedDocumentImmutable"));
|
|
4461
4581
|
const clean = {
|
|
4462
|
-
specs:
|
|
4463
|
-
wiki: { documents:
|
|
4582
|
+
specs: current2.specs.map((s) => ({ ...s, history: before.specs.find((p) => p.id === s.id)?.history ?? [] })),
|
|
4583
|
+
wiki: { documents: current2.wiki.documents, history: before.wiki.history }
|
|
4464
4584
|
};
|
|
4465
4585
|
return comparePreviewBundles(before, clean);
|
|
4466
4586
|
}
|
|
@@ -4468,14 +4588,14 @@ function prepareSpecPreview(beforeValue, currentValue, baseFiles, currentFiles,
|
|
|
4468
4588
|
const beforeBundle = asBundle(beforeValue);
|
|
4469
4589
|
const currentBundle = asBundle(currentValue);
|
|
4470
4590
|
const before = beforeBundle.specs;
|
|
4471
|
-
const
|
|
4591
|
+
const current2 = currentBundle.specs;
|
|
4472
4592
|
const delta = finalSpecPreviewChanges(beforeBundle, currentBundle);
|
|
4473
4593
|
if (!Array.isArray(input2) || input2.length > 100)
|
|
4474
4594
|
fail3(t("reason.count"));
|
|
4475
4595
|
const writes = /* @__PURE__ */ new Map();
|
|
4476
4596
|
const pending = /* @__PURE__ */ new Map();
|
|
4477
4597
|
const covered = /* @__PURE__ */ new Set();
|
|
4478
|
-
const used = new Set([...before.flatMap((s) => s.history), ...
|
|
4598
|
+
const used = new Set([...before.flatMap((s) => s.history), ...current2.flatMap((s) => s.history), ...beforeBundle.wiki.history, ...currentBundle.wiki.history].map((h) => h.id));
|
|
4479
4599
|
const wikiPending = [];
|
|
4480
4600
|
const allocate = () => {
|
|
4481
4601
|
for (let attempt = 0; attempt < 100; attempt++) {
|
|
@@ -4524,7 +4644,7 @@ function prepareSpecPreview(beforeValue, currentValue, baseFiles, currentFiles,
|
|
|
4524
4644
|
fail3(t("reason.targetInvalid"));
|
|
4525
4645
|
const change2 = delta.changes.find((c) => c.id === id) ?? fail3(t("reason.requirementUnchanged", { id }));
|
|
4526
4646
|
const specId = (change2.after ?? change2.before).specId;
|
|
4527
|
-
if (!
|
|
4647
|
+
if (!current2.some((s) => s.id === specId))
|
|
4528
4648
|
fail3(t("reason.keepDeletedSpec"));
|
|
4529
4649
|
covered.add(id);
|
|
4530
4650
|
groups.set(specId, [...groups.get(specId) ?? [], id]);
|
|
@@ -4532,7 +4652,7 @@ function prepareSpecPreview(beforeValue, currentValue, baseFiles, currentFiles,
|
|
|
4532
4652
|
for (const [specId, ids] of groups) {
|
|
4533
4653
|
const requirements = ids.filter((id) => id.startsWith("R-")).sort();
|
|
4534
4654
|
const designs = ids.filter((id) => id.startsWith("S-")).sort();
|
|
4535
|
-
const spec2 =
|
|
4655
|
+
const spec2 = current2.find((s) => s.id === specId);
|
|
4536
4656
|
const oldCount = before.find((s) => s.id === specId)?.history.length ?? 0;
|
|
4537
4657
|
const reusable = spec2.history.slice(oldCount).find((h) => h.reason === reason && equal([...h.requirements].sort(), requirements) && equal([...h.designs ?? []].sort(), designs));
|
|
4538
4658
|
const record3 = { id: reusable?.id ?? allocate(), requirements, ...designs.length ? { designs } : {}, reason };
|
|
@@ -4545,7 +4665,7 @@ function prepareSpecPreview(beforeValue, currentValue, baseFiles, currentFiles,
|
|
|
4545
4665
|
text2 += (text2 && !text2.endsWith("\n") ? "\n" : "") + records.map((h) => JSON.stringify(h) + "\n").join("");
|
|
4546
4666
|
return base !== void 0 || records.length ? text2 : null;
|
|
4547
4667
|
};
|
|
4548
|
-
for (const spec2 of
|
|
4668
|
+
for (const spec2 of current2) {
|
|
4549
4669
|
const old = before.find((s) => s.id === spec2.id);
|
|
4550
4670
|
const base = old ? baseFiles.get(historyPath(old)) : void 0;
|
|
4551
4671
|
const records = (pending.get(spec2.id) ?? []).sort((a, b) => (a.requirements[0] ?? a.designs[0]) < (b.requirements[0] ?? b.designs[0]) ? -1 : 1);
|
|
@@ -24797,8 +24917,74 @@ var updateV3 = external_exports.discriminatedUnion("ok", [
|
|
|
24797
24917
|
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() }) })
|
|
24798
24918
|
]);
|
|
24799
24919
|
|
|
24920
|
+
// ../../packages/contracts/dist/versions/project-init-v6.js
|
|
24921
|
+
var projectInitV6 = external_exports.discriminatedUnion("ok", [
|
|
24922
|
+
external_exports.strictObject({
|
|
24923
|
+
contract: external_exports.literal("project-init"),
|
|
24924
|
+
version: external_exports.literal(6),
|
|
24925
|
+
ok: external_exports.literal(true),
|
|
24926
|
+
outcome: external_exports.enum(["planned", "created", "replaced", "already-initialized"]),
|
|
24927
|
+
rootPath: external_exports.string(),
|
|
24928
|
+
configPath: external_exports.literal(".gitifact/config.json"),
|
|
24929
|
+
schemaVersion: external_exports.literal(2),
|
|
24930
|
+
baseline: external_exports.discriminatedUnion("kind", [
|
|
24931
|
+
external_exports.strictObject({ kind: external_exports.literal("empty") }),
|
|
24932
|
+
external_exports.strictObject({ kind: external_exports.literal("commit"), objectFormat: external_exports.enum(["sha1", "sha256"]), commit: external_exports.string() })
|
|
24933
|
+
]),
|
|
24934
|
+
agentDocs: external_exports.strictObject({ mode: external_exports.enum(["install", "remove", "skip"]), paths: external_exports.array(external_exports.string()) }),
|
|
24935
|
+
update: updateStateV1,
|
|
24936
|
+
// Present only when a newer release exists. npx refreshes the project blocks without a global install; npmGlobal remains an alternative.
|
|
24937
|
+
install: external_exports.strictObject({ npx: external_exports.string().min(1), npmGlobal: external_exports.string().min(1) }).nullable()
|
|
24938
|
+
}),
|
|
24939
|
+
external_exports.strictObject({ contract: external_exports.literal("project-init"), version: external_exports.literal(6), ok: external_exports.literal(false), error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }) })
|
|
24940
|
+
]);
|
|
24941
|
+
|
|
24942
|
+
// ../../packages/contracts/dist/versions/update-v4.js
|
|
24943
|
+
var commitReason2 = external_exports.enum(["untracked", "other-changes", "commit-failed"]);
|
|
24944
|
+
var agentDocsCommit2 = external_exports.strictObject({
|
|
24945
|
+
state: external_exports.enum(["not-requested", "nothing", "committed", "skipped"]),
|
|
24946
|
+
commit: external_exports.string().nullable(),
|
|
24947
|
+
paths: external_exports.array(external_exports.string()),
|
|
24948
|
+
message: external_exports.string().nullable(),
|
|
24949
|
+
reason: commitReason2.nullable(),
|
|
24950
|
+
detail: external_exports.string().nullable()
|
|
24951
|
+
}).refine((value) => value.state === "committed" === (value.commit !== null) && value.state === "skipped" === (value.reason !== null), "commit is set exactly when committed, reason exactly when skipped.");
|
|
24952
|
+
var updateV4 = external_exports.discriminatedUnion("ok", [
|
|
24953
|
+
external_exports.strictObject({
|
|
24954
|
+
contract: external_exports.literal("update"),
|
|
24955
|
+
version: external_exports.literal(4),
|
|
24956
|
+
ok: external_exports.literal(true),
|
|
24957
|
+
cliVersion: external_exports.string().min(1),
|
|
24958
|
+
update: updateStateV1,
|
|
24959
|
+
// Present only when a newer release exists. npx refreshes the project blocks without a global install; npmGlobal remains an alternative.
|
|
24960
|
+
install: external_exports.strictObject({ npx: external_exports.string().min(1), npmGlobal: external_exports.string().min(1) }).nullable(),
|
|
24961
|
+
// refreshed: blocks whose version line differed were rewritten. current: every block already matched.
|
|
24962
|
+
// not-initialized: no .gitifact config here, so only the version check ran. no-block: no file carries a block.
|
|
24963
|
+
// missing: files `gitifact init` would add and update does not create, e.g. CLAUDE.md when only AGENTS.md has the block.
|
|
24964
|
+
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()) }),
|
|
24965
|
+
commit: agentDocsCommit2
|
|
24966
|
+
}),
|
|
24967
|
+
external_exports.strictObject({ contract: external_exports.literal("update"), version: external_exports.literal(4), ok: external_exports.literal(false), error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }) })
|
|
24968
|
+
]);
|
|
24969
|
+
|
|
24970
|
+
// ../../packages/contracts/dist/versions/browser-session-v3.js
|
|
24971
|
+
var browserSessionV3 = browserSessionV2.omit({ update: true }).extend({ version: external_exports.literal(3) });
|
|
24972
|
+
|
|
24973
|
+
// ../../packages/contracts/dist/versions/update-check-v1.js
|
|
24974
|
+
var updateCheckV1 = external_exports.strictObject({
|
|
24975
|
+
contract: external_exports.literal("update-check"),
|
|
24976
|
+
version: external_exports.literal(1),
|
|
24977
|
+
ok: external_exports.literal(true),
|
|
24978
|
+
cliVersion: external_exports.string().min(1),
|
|
24979
|
+
update: updateStateV1.refine((value) => value.status !== "checking", "The check must have finished."),
|
|
24980
|
+
command: external_exports.string().min(1).nullable()
|
|
24981
|
+
}).refine((value) => value.update.status === "available" === (value.command !== null), "An update command is present exactly when a newer release is available.");
|
|
24982
|
+
|
|
24983
|
+
// src/shared/i18n/index.ts
|
|
24984
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
24985
|
+
|
|
24800
24986
|
// src/shared/i18n/ko/messages.json
|
|
24801
|
-
var
|
|
24987
|
+
var messages_default3 = {
|
|
24802
24988
|
"help.program": "\uD504\uB85C\uC81D\uD2B8\uC758 \uC694\uAD6C\uC0AC\uD56D\uACFC \uACB0\uC815 \uC774\uB825\uC744 Git\uC5D0 \uB0A8\uAE30\uB294 \uB3C4\uAD6C",
|
|
24803
24989
|
"help.notYet": "\uC804\uCCB4 \uC774\uB825 \uAC80\uC0AC\uC640 GUI \uC694\uAD6C\uC0AC\uD56D \uD3B8\uC9D1\uC740 \uC544\uC9C1 \uC81C\uACF5\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
24804
24990
|
"help.status": "\uD604\uC7AC checkout\uC758 Git \uC0C1\uD0DC \uC870\uD68C (gitifact \uAC80\uC0AC \uBBF8\uC2E4\uD589)",
|
|
@@ -24972,7 +25158,7 @@ var messages_default2 = {
|
|
|
24972
25158
|
"init.text.blockUpdated": "\uBE14\uB85D \uAC31\uC2E0",
|
|
24973
25159
|
"init.text.storage": "\uC800\uC7A5 \uADDC\uC57D",
|
|
24974
25160
|
"init.text.agentDocs": "\uC5D0\uC774\uC804\uD2B8 \uC9C0\uCE68",
|
|
24975
|
-
"init.text.updateAvailable": "\uC0C8 \uBC84\uC804 {version}\uC774 \uC788\uC2B5\uB2C8\uB2E4. {command}\uB85C \
|
|
25161
|
+
"init.text.updateAvailable": "\uC0C8 \uBC84\uC804 {version}\uC774 \uC788\uC2B5\uB2C8\uB2E4. {command}\uB85C \uC0C8 \uBC84\uC804\uC744 \uC2E4\uD589\uD558\uACE0 \uD504\uB85C\uC81D\uD2B8 \uC9C0\uCE68\uC744 \uAC31\uC2E0\uD558\uC138\uC694.",
|
|
24976
25162
|
"init.failed": "\uCD08\uAE30\uD654\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uD30C\uC77C \uC811\uADFC \uAD8C\uD55C\uACFC \uC0DD\uC131\uB41C \uC124\uC815\xB7\uC784\uC2DC \uD30C\uC77C\uC744 \uD655\uC778\uD558\uC138\uC694.",
|
|
24977
25163
|
"config.storeNotDirectory": ".gitifact\uB294 \uB9C1\uD06C\uAC00 \uC544\uB2CC \uC77C\uBC18 \uB514\uB809\uD130\uB9AC\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
|
|
24978
25164
|
"config.migrateFirst": ".tryce \uC800\uC7A5\uC18C\uC785\uB2C8\uB2E4. gitifact migrate\uB85C .gitifact\uC5D0 \uC804\uD658\uD558\uC138\uC694.",
|
|
@@ -25009,7 +25195,6 @@ var messages_default2 = {
|
|
|
25009
25195
|
"docs.summary.design": "design.md \uBAA9\uCC28, \uCC38\uC870 \uC8FC\uC11D\uACFC \uCC38\uACE0 \uBB38\uC11C, \uAC1C\uC815 \uBC29\uC2DD",
|
|
25010
25196
|
"docs.summary.writing": "\uC704\uD0A4\xB7\uC694\uAD6C\uC0AC\uD56D\xB7\uC124\uACC4\uC5D0 \uACF5\uD1B5\uC73C\uB85C \uC801\uC6A9\uD558\uB294 \uBB38\uCCB4, \uC778\uC6A9\uACFC Alert, AI \uC2AC\uB86D \uC81C\uAC70",
|
|
25011
25197
|
"docs.summary.commit": "spec commit \uC785\uB825, \uC774\uC720 \uAE30\uB85D, \uC2E4\uD328 \uD6C4 \uBCF5\uAD6C",
|
|
25012
|
-
"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)",
|
|
25013
25198
|
"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",
|
|
25014
25199
|
"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",
|
|
25015
25200
|
"server.changelogNotFound": "\uD328\uCE58\uB178\uD2B8\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
@@ -25019,13 +25204,13 @@ var messages_default2 = {
|
|
|
25019
25204
|
"update.text.upToDate": "\uCD5C\uC2E0 \uBC84\uC804\uC744 \uC0AC\uC6A9 \uC911\uC785\uB2C8\uB2E4.",
|
|
25020
25205
|
"update.text.disabled": "\uC0C8 \uBC84\uC804 \uD655\uC778\uC774 \uAEBC\uC838 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
25021
25206
|
"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.",
|
|
25022
|
-
"update.text.install": "
|
|
25023
|
-
"update.text.afterInstall": "\uC124\uCE58\
|
|
25207
|
+
"update.text.install": "\uC5C5\uB370\uC774\uD2B8 \uBA85\uB839: {command}",
|
|
25208
|
+
"update.text.afterInstall": "\uC704 \uBA85\uB839\uC740 \uC804\uC5ED \uC124\uCE58 \uC5C6\uC774 \uC0C8 \uBC84\uC804\uC73C\uB85C \uC9C0\uCE68\uC744 \uAC31\uC2E0\uD569\uB2C8\uB2E4. \uD504\uB85C\uC81D\uD2B8 \uC758\uC874\uC131\uC73C\uB85C \uAD00\uB9AC\uD55C\uB2E4\uBA74 \uD574\uB2F9 \uBC84\uC804\uC744 \uBA3C\uC800 \uAC31\uC2E0\uD558\uC138\uC694. \uBE0C\uB77C\uC6B0\uC800\uB3C4 \uC0C8 \uBC84\uC804\uC73C\uB85C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
|
|
25024
25209
|
"update.text.blockRefreshed": "\uC9C0\uCE68 \uBE14\uB85D \uAC31\uC2E0: {paths}",
|
|
25025
25210
|
"update.text.blockCurrent": "\uC9C0\uCE68 \uBE14\uB85D\uC740 \uC774\uBBF8 \uD604\uC7AC \uBC84\uC804\uC785\uB2C8\uB2E4: {paths}",
|
|
25026
|
-
"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.",
|
|
25211
|
+
"update.text.noBlock": "GITIFACT \uBE14\uB85D\uC774 \uC788\uB294 \uC9C0\uCE68 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. npx --yes gitifact@{version} init\uC73C\uB85C \uC124\uCE58\uD558\uC138\uC694.",
|
|
25027
25212
|
"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.",
|
|
25028
|
-
"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.",
|
|
25213
|
+
"update.text.missing": "{paths} \uD30C\uC77C\uC774 \uC5C6\uC5B4 Claude Code\uAC00 AGENTS.md\uC758 \uC9C0\uCE68\uC744 \uC77D\uC9C0 \uBABB\uD569\uB2C8\uB2E4. npx --yes gitifact@{version} init\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 AGENTS.md\uB97C \uAC00\uC838\uC624\uB294 \uD30C\uC77C\uC744 \uB9CC\uB4ED\uB2C8\uB2E4.",
|
|
25029
25214
|
"update.text.committed": "\uC9C0\uCE68 \uBE14\uB85D \uCEE4\uBC0B: {commit} ({paths})",
|
|
25030
25215
|
"update.text.commitNothing": "\uCEE4\uBC0B\uD560 \uC9C0\uCE68 \uBE14\uB85D \uBCC0\uACBD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25031
25216
|
"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}",
|
|
@@ -25037,13 +25222,278 @@ var messages_default2 = {
|
|
|
25037
25222
|
"server.assetNotFound": "\uC5D0\uC14B\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
25038
25223
|
"docs.wikiPolicyProject": "\uC6B4\uC601 \uBC29\uCE68 (.gitifact/wiki/README.md)",
|
|
25039
25224
|
"docs.wikiPolicyDefault": "\uC6B4\uC601 \uBC29\uCE68 (\uAE30\uBCF8\uAC12. .gitifact/wiki/README.md\uAC00 \uC5C6\uC5B4 \uB0B4\uC7A5 \uBC29\uCE68\uC744 \uC2E3\uB294\uB2E4)",
|
|
25040
|
-
"init.wikiReadmeTitle": "\uC704\uD0A4 \uC6B4\uC601 \uBC29\uCE68"
|
|
25225
|
+
"init.wikiReadmeTitle": "\uC704\uD0A4 \uC6B4\uC601 \uBC29\uCE68",
|
|
25226
|
+
"help.lang": "\uCD9C\uB825 \uC5B8\uC5B4 (ko \uB610\uB294 en)",
|
|
25227
|
+
"help.updateCheck": "\uD30C\uC77C\uC744 \uBC14\uAFB8\uC9C0 \uC54A\uACE0 \uC0C8 \uBC84\uC804\uB9CC \uD655\uC778"
|
|
25228
|
+
};
|
|
25229
|
+
|
|
25230
|
+
// src/shared/i18n/en/messages.json
|
|
25231
|
+
var messages_default4 = {
|
|
25232
|
+
"help.program": "Track project requirements and decisions in Git",
|
|
25233
|
+
"help.notYet": "Full history validation and GUI requirement editing are not available yet.",
|
|
25234
|
+
"help.status": "Read Git status for the current checkout (Gitifact checks are not run)",
|
|
25235
|
+
"help.format": "Output format",
|
|
25236
|
+
"help.browser": "View the current checkout in a local browser",
|
|
25237
|
+
"help.browserPort": "Listening port (0 selects an available port)",
|
|
25238
|
+
"help.browserDev": "Allow the Vite development server origin",
|
|
25239
|
+
"help.init": "Create project configuration and baseline, and install or refresh GITIFACT instruction blocks",
|
|
25240
|
+
"help.initDryRun": "Preview initialization without creating files",
|
|
25241
|
+
"help.initAgent": "Target agent (default: all existing instruction files, or AGENTS.md if none exist)",
|
|
25242
|
+
"help.initRemoveAgents": "Remove GITIFACT blocks",
|
|
25243
|
+
"help.initSkipAgents": "Leave agent instruction files unchanged",
|
|
25244
|
+
"help.docs": "Print agent guidance (omit the topic to list topics)",
|
|
25245
|
+
"help.docsTopic": "One of workflow, spec, design, wiki, writing, commit",
|
|
25246
|
+
"help.migrate": "Move .tryce to .gitifact, preserving IDs, reasons and history",
|
|
25247
|
+
"help.migrateDryRun": "Preview migration without changing files",
|
|
25248
|
+
"help.spec": "Write, read and record Markdown specifications in Git",
|
|
25249
|
+
"help.specCommit": "Record change reasons and commit related files together",
|
|
25250
|
+
"help.specCommitFile": "JSON file containing reasons, paths, message and authorization (- reads stdin)",
|
|
25251
|
+
"help.specCommitDryRun": "Preview without writing files or creating a commit",
|
|
25252
|
+
"help.specSaveFile": "UTF-8 JSON file containing expected and operations (- reads stdin)",
|
|
25253
|
+
"help.specReadRef": "Commit to read",
|
|
25254
|
+
"help.specDiffFrom": "Earlier commit",
|
|
25255
|
+
"help.specDiffTo": "Later commit",
|
|
25256
|
+
"help.specChanges": "Compare the final working specifications with HEAD and read uncommitted reasons",
|
|
25257
|
+
"help.specWorking": "Read current specifications, wiki, stamp and input file paths",
|
|
25258
|
+
"help.specWorkingStamp": "Print only the stamp and input file paths",
|
|
25259
|
+
"help.specWorkingFeature": "Read one feature folder (excluding the wiki)",
|
|
25260
|
+
"help.specWorkingIds": "Print only IDs, titles and paths, without content",
|
|
25261
|
+
"help.specSave": "Save requirements, designs and wiki pages",
|
|
25262
|
+
"help.specRead": "Read specifications at a commit",
|
|
25263
|
+
"help.specDiff": "Compare specifications between two commits",
|
|
25264
|
+
"git.operationInProgress": "A Git operation is in progress: {marker}",
|
|
25265
|
+
"git.resolveConflicts": "Resolve Git conflicts first.",
|
|
25266
|
+
"migrate.locked": "A lock or recovery data exists: {path}",
|
|
25267
|
+
"migrate.alreadyMigrated": ".gitifact already exists. Compare any remaining .tryce contents before removing them manually.",
|
|
25268
|
+
"migrate.nothingToMigrate": "No .tryce store found.",
|
|
25269
|
+
"migrate.notDirectory": ".tryce must be a regular directory, not a link.",
|
|
25270
|
+
"migrate.noConfig": ".tryce/config.json is missing.",
|
|
25271
|
+
"migrate.legacyJson": "Legacy JSON records are not migrated. Preserve them and check the separate transition procedure.",
|
|
25272
|
+
"migrate.extraEntries": ".tryce contains entries other than config.json and spec. Review them before retrying.",
|
|
25273
|
+
"migrate.notSpecFolder": "Not a specification folder: {feature}",
|
|
25274
|
+
"migrate.unsupportedFile": "Unsupported specification file: .tryce/spec/{feature}/{name}",
|
|
25275
|
+
"migrate.fileTooLarge": "Not a regular file, or larger than 1 MiB: {name}",
|
|
25276
|
+
"migrate.nextCommit": "Review the changes, then use gitifact spec commit to commit the .tryce deletions and .gitifact additions together.",
|
|
25277
|
+
"migrate.nextLegacyJson": "Legacy JSON specifications (tryce.json) are not read. Move their contents to Markdown specifications, then include their deletion in spec commit.",
|
|
25278
|
+
"migrate.nextSkills": "If .agents/skills/tryce-workflow or its .claude copy remains, remove those old integrations and rerun gitifact init to refresh GITIFACT blocks.",
|
|
25279
|
+
"migrate.nextRename": "Update .tryce and tryce-workflow references in .gitignore, AGENTS.md and CLAUDE.md.",
|
|
25280
|
+
"migrate.failed": "Migration failed. Check the state of .tryce and .gitifact.",
|
|
25281
|
+
"preview.notInitialized": "Run gitifact init first.",
|
|
25282
|
+
"preview.legacyProject": "This existing project requires a separate transition.",
|
|
25283
|
+
"preview.inputFile": "Input must be a JSON file no larger than 1 MiB.",
|
|
25284
|
+
"preview.inputSize": "Input exceeds the size limit.",
|
|
25285
|
+
"preview.inputJson": "The input file must be UTF-8 JSON.",
|
|
25286
|
+
"preview.unknownFeature": "No specification in feature folder {feature}.",
|
|
25287
|
+
"preview.failed": "Specification operation failed.",
|
|
25288
|
+
"initRepo.configIgnored": "Configuration is excluded by an ignore rule: {rule}",
|
|
25289
|
+
"initRepo.baselineMismatch": "The baseline does not match the current repository.",
|
|
25290
|
+
"initRepo.baselineUnverified": "Could not verify the baseline commit and its ancestry. Preserve the baseline and inspect the history.",
|
|
25291
|
+
"commit.unknownField": "Unknown commit input field.",
|
|
25292
|
+
"commit.authorizationRequired": "A user request or explicit project policy is required to authorize a commit.",
|
|
25293
|
+
"commit.trailerInMessage": "Specify Gitifact references through requirements, not message trailers.",
|
|
25294
|
+
"commit.invalidReferences": "Invalid requirement references.",
|
|
25295
|
+
"commit.busy": "A previous commit operation or recovery data exists: {path}",
|
|
25296
|
+
"commit.selectPending": "Select all changed specifications and reasons together. Unselected specifications are not added automatically.",
|
|
25297
|
+
"commit.unknownRequirement": "Reference only existing requirement IDs.",
|
|
25298
|
+
"commit.unexpectedFiles": "An empty commit or files outside the selection were detected.",
|
|
25299
|
+
"commit.filterChanged": "A Git filter changed the specification or reason contents.",
|
|
25300
|
+
"commit.specsChangedPreparing": "The specification set changed while preparing the commit.",
|
|
25301
|
+
"commit.integrationStarted": "A Git integration operation has started.",
|
|
25302
|
+
"commit.headChanged": "HEAD changed.",
|
|
25303
|
+
"commit.resultAltered": "The commit ran, but a hook or concurrent operation changed the result.",
|
|
25304
|
+
"commit.indexChangedAfter": "The original index changed after the commit.",
|
|
25305
|
+
"commit.hookChangedStaging": "A hook changed staging after the commit.",
|
|
25306
|
+
"commit.expectedType": "expected must be the string returned by changes.",
|
|
25307
|
+
"commit.existingStaging": "Existing staging has been preserved. Review the selection before retrying.",
|
|
25308
|
+
"commit.staleInput": "Stale input. Run changes again.",
|
|
25309
|
+
"commit.newlinePath": "Specification paths containing newlines are not supported.",
|
|
25310
|
+
"commit.storedShape": "Could not verify the Git representation of the specification files.",
|
|
25311
|
+
"commit.indexChanged": "The index changed during execution.",
|
|
25312
|
+
"commit.fileChangedRunning": "A file or policy changed during execution: {path}",
|
|
25313
|
+
"commit.stagedUnreadable": "Could not read staged specifications or reasons: {path}",
|
|
25314
|
+
"commit.fileChangedPreparing": "A file or policy changed while preparing the commit: {path}",
|
|
25315
|
+
"commit.resultUnknown": "Could not verify the commit result.",
|
|
25316
|
+
"commit.uncertain": "The Git result needs inspection. Do not retry; inspect HEAD and recovery data: {path}",
|
|
25317
|
+
"store.legacyLock": "A previous Tryce lock or recovery data exists: {path}",
|
|
25318
|
+
"store.storeNotDirectory": ".gitifact must be a regular directory.",
|
|
25319
|
+
"store.migrateFirst": "This is a .tryce store. Run gitifact migrate before using it.",
|
|
25320
|
+
"store.legacyProject": "This project format requires a separate transition.",
|
|
25321
|
+
"store.linkOrSpecial": "Links and special files are not supported: {path}",
|
|
25322
|
+
"store.tooManyFiles": "The specification file count limit was exceeded.",
|
|
25323
|
+
"store.tooDeep": "The folder depth limit was exceeded: {path}",
|
|
25324
|
+
"store.legacyJson": "Legacy JSON records are not migrated.",
|
|
25325
|
+
"store.unsupportedPath": "Unsupported specification path: {path}",
|
|
25326
|
+
"store.hardLinkOrSize": "Check for hard links or files larger than 1 MiB.",
|
|
25327
|
+
"store.sizeLimit": "The specification size limit was exceeded.",
|
|
25328
|
+
"store.locked": "A specification save lock or recovery data exists.",
|
|
25329
|
+
"store.changedWhileReading": "Specifications changed while being read. Read them again.",
|
|
25330
|
+
"store.lockedWhileReading": "A save lock appeared while reading.",
|
|
25331
|
+
"store.invalidInput": "Invalid save input.",
|
|
25332
|
+
"store.expectedOperations": "expected and operations are required.",
|
|
25333
|
+
"store.notDirectory": "Not a regular directory: {path}",
|
|
25334
|
+
"store.staleExpected": "Specifications changed since the last read. Run working again.",
|
|
25335
|
+
"store.unsupportedSavePath": "Unsupported save path.",
|
|
25336
|
+
"store.fileSizeLimit": "A specification exceeds the 1 MiB limit.",
|
|
25337
|
+
"store.totalLimit": "The total specification size limit was exceeded.",
|
|
25338
|
+
"store.changedPreparing": "Specifications changed while preparing the save.",
|
|
25339
|
+
"store.changedBeforeWrite": "A file changed immediately before writing.",
|
|
25340
|
+
"store.changedWhileWriting": "Specifications or configuration changed while writing.",
|
|
25341
|
+
"store.recoveryIncomplete": "Automatic recovery could not finish. Other edits were preserved. Recovery data: {path}",
|
|
25342
|
+
"server.closing": "The server is shutting down.",
|
|
25343
|
+
"server.forbiddenHost": "Host is not allowed.",
|
|
25344
|
+
"server.forbiddenOrigin": "Origin is not allowed.",
|
|
25345
|
+
"server.badPath": "Invalid path.",
|
|
25346
|
+
"server.noBody": "Request bodies are not accepted.",
|
|
25347
|
+
"server.crossSite": "Requests from external pages are not allowed.",
|
|
25348
|
+
"server.noQuery": "Query parameters are not accepted.",
|
|
25349
|
+
"server.methodNotAllowed": "Method is not allowed.",
|
|
25350
|
+
"server.apiNotFound": "API endpoint not found.",
|
|
25351
|
+
"server.sessionChanged": "The server session changed. Reconnect to continue.",
|
|
25352
|
+
"server.badRange": "Check the requested range.",
|
|
25353
|
+
"server.changeNotFound": "This commit does not contain that change.",
|
|
25354
|
+
"server.specsUnreadable": "Could not read specifications.",
|
|
25355
|
+
"server.refreshOrigin": "Refresh requires a same-origin Origin header.",
|
|
25356
|
+
"server.fileNotFound": "File not found.",
|
|
25357
|
+
"server.internal": "Could not process the request.",
|
|
25358
|
+
"server.portInUse": "The requested port is in use. Choose another port.",
|
|
25359
|
+
"server.addressUnknown": "Could not determine the server address.",
|
|
25360
|
+
"reader.gitContextOverride": "Reading is disabled when the Git context is overridden.",
|
|
25361
|
+
"reader.notUtf8": "Content is not UTF-8.",
|
|
25362
|
+
"reader.invalidIndexEntry": "Invalid index entry.",
|
|
25363
|
+
"reader.stagingConflictOrLink": "Specification staging contains a conflict or link.",
|
|
25364
|
+
"reader.stagingUnsupported": "Specification staging contains an unsupported file.",
|
|
25365
|
+
"reader.headUnknown": "Could not determine HEAD.",
|
|
25366
|
+
"reader.damagedHead": "A damaged HEAD is not treated as an empty repository.",
|
|
25367
|
+
"reader.objectFormat": "Unsupported Git object format.",
|
|
25368
|
+
"reader.commitUnknown": "Could not resolve the commit.",
|
|
25369
|
+
"reader.invalidTree": "Invalid Git tree.",
|
|
25370
|
+
"reader.linkOrSubmodule": "The specification area contains a link or submodule: {path}",
|
|
25371
|
+
"reader.legacyJson": "Use an older compatible CLI to read legacy JSON records. This command does not migrate them.",
|
|
25372
|
+
"reader.unsupportedEntry": "Unsupported path or file type: {path}",
|
|
25373
|
+
"reader.mixedStores": "A historical commit contains both .gitifact and .tryce specifications. Check the transition commit.",
|
|
25374
|
+
"reader.mixedFormats": "A historical commit mixes old and new specification formats.",
|
|
25375
|
+
"reader.fileLimit": "The specification read file limit was exceeded.",
|
|
25376
|
+
"reader.blobShape": "Check the blob format and the 1 MiB size limit.",
|
|
25377
|
+
"reader.totalLimit": "The total specification read limit of 16 MiB was exceeded.",
|
|
25378
|
+
"reader.blobBoundary": "Unexpected Git blob response boundary.",
|
|
25379
|
+
"prepare.integrationInProgress": "Specifications cannot be committed during a Git integration operation.",
|
|
25380
|
+
"prepare.headChanged": "HEAD or the branch changed. Run changes again.",
|
|
25381
|
+
"commitFiles.relativePath": "Select repository-relative paths to specifications or regular files only.",
|
|
25382
|
+
"commitFiles.linkOrDirectory": "Links, directories and special files cannot be selected.",
|
|
25383
|
+
"commitFiles.nestedRepository": "Nested repositories cannot be selected.",
|
|
25384
|
+
"commitFiles.hardLinkOrSize": "Check for hard links or oversized files.",
|
|
25385
|
+
"commitFiles.invalidInput": "Invalid commit input.",
|
|
25386
|
+
"commitFiles.emptyOrTooLong": "An input string is empty or exceeds the limit.",
|
|
25387
|
+
"commitFiles.invalidPaths": "Invalid file list size or format.",
|
|
25388
|
+
"commitFiles.legacyDeleteOnly": "Legacy records in .gitifact can only be selected for deletion: {path}",
|
|
25389
|
+
"commitFiles.tryceDeleteOnly": "Old .tryce records can only be selected for deletion during migration: {path}",
|
|
25390
|
+
"init.migrationRequired": "This format requires a separate transition. Preserve the configuration and records.",
|
|
25391
|
+
"init.inputChanged": "Input changed while planning initialization.",
|
|
25392
|
+
"init.configDeleted": "A deleted tracked configuration is not treated as a new project.",
|
|
25393
|
+
"init.existingSpecs": "Check the existing specs path.",
|
|
25394
|
+
"init.orphanStore": ".gitifact data without configuration is not adopted automatically.",
|
|
25395
|
+
"init.legacyRecords": "This project contains records written with an older storage convention (schemaVersion {version}), so its configuration was not changed. This CLI does not migrate that convention. Preserve the records elsewhere and leave only config.json in .gitifact before running init again.",
|
|
25396
|
+
"init.headOrIndexChanged": "HEAD or the index changed.",
|
|
25397
|
+
"init.configAppeared": "Another process created the configuration.",
|
|
25398
|
+
"init.changedAfterWrite": "Input changed after creating the configuration. Inspect the current files.",
|
|
25399
|
+
"init.text.skipped": "Skipped",
|
|
25400
|
+
"init.text.blockRemoved": "Block removed",
|
|
25401
|
+
"init.text.blockPlanned": "Block installation planned",
|
|
25402
|
+
"init.text.blockUpdated": "Block refreshed",
|
|
25403
|
+
"init.text.storage": "Storage convention",
|
|
25404
|
+
"init.text.agentDocs": "Agent instructions",
|
|
25405
|
+
"init.text.updateAvailable": "Version {version} is available. Run {command} to use it and refresh the project instructions.",
|
|
25406
|
+
"init.failed": "Initialization failed. Check file permissions and the generated configuration and temporary files.",
|
|
25407
|
+
"config.storeNotDirectory": ".gitifact must be a regular directory, not a link.",
|
|
25408
|
+
"config.migrateFirst": "This is a .tryce store. Run gitifact migrate to move it to .gitifact.",
|
|
25409
|
+
"config.fileShape": "config.json must be a regular file no larger than 64 KiB.",
|
|
25410
|
+
"config.changedWhileReading": "Configuration changed while reading. Retry the command.",
|
|
25411
|
+
"config.notUtf8Json": "Configuration must be UTF-8 JSON.",
|
|
25412
|
+
"config.directoryChanged": "The configuration directory changed. Inspect temporary files.",
|
|
25413
|
+
"config.temporaryChanged": "The temporary configuration changed.",
|
|
25414
|
+
"managedFile.notRegularPath": "Not a regular path: {path}",
|
|
25415
|
+
"managedFile.tooLarge": "Exceeds {kib} KiB: {path}",
|
|
25416
|
+
"managedFile.changedWhileReading": "File changed while reading: {path}",
|
|
25417
|
+
"managedFile.notUtf8": "File must be UTF-8: {path}",
|
|
25418
|
+
"managedFile.targetChanged": "Target path changed: {path}",
|
|
25419
|
+
"managedFile.removeTargetChanged": "Removal target changed: {path}",
|
|
25420
|
+
"managedFile.publishTargetChanged": "Publication target changed: {path}",
|
|
25421
|
+
"agentBlock.endWithoutStart": "An end marker has no matching start marker. Repair the block before retrying.",
|
|
25422
|
+
"agentBlock.startWithoutEnd": "A GITIFACT block has no end marker. Repair the block before retrying.",
|
|
25423
|
+
"agentBlock.duplicateStart": "Multiple GITIFACT start markers found. Remove duplicate blocks before retrying.",
|
|
25424
|
+
"specReader.headUnreadable": "Could not read HEAD.",
|
|
25425
|
+
"specReader.schemaRequired": "A project using the supported schema is required.",
|
|
25426
|
+
"specReader.authorUnreadable": "Could not read Git authors.",
|
|
25427
|
+
"specReader.historyUnreadable": "Could not read Git history.",
|
|
25428
|
+
"specReader.projectChanged": "The project changed while reading. Refresh to continue.",
|
|
25429
|
+
"docs.unknownTopic": "Unknown topic: {topic}. Available topics: {topics}",
|
|
25430
|
+
"status.text.repository": "Repository: {path}",
|
|
25431
|
+
"status.text.changes": "Changed Git paths: {count}; Gitifact checks not run",
|
|
25432
|
+
"status.text.observed": "Observed: {at} (best effort; not a snapshot of file contents)",
|
|
25433
|
+
"browser.invalidPort": "Port must be an integer between 0 and 65535.",
|
|
25434
|
+
"browser.failed": "Could not start the browser server.",
|
|
25435
|
+
"browser.assetSymlink": "Browser assets must not be symlinks.",
|
|
25436
|
+
"browser.assetsUnreadable": "Could not read the browser build. Rebuild with pnpm build.",
|
|
25437
|
+
"docs.summary.workflow": "Startup checks, identifying requirement requests, and completion reporting",
|
|
25438
|
+
"docs.summary.spec": "User stories, acceptance criteria, IDs and spec save input",
|
|
25439
|
+
"docs.summary.design": "Design structure, requirement references, sources and revisions",
|
|
25440
|
+
"docs.summary.writing": "Writing style, quotations, alerts and removing filler from documentation",
|
|
25441
|
+
"docs.summary.commit": "spec commit input, change reasons and recovery after failure",
|
|
25442
|
+
"help.updateCommit": "Commit only instruction files changed solely inside the GITIFACT block, using a fixed message",
|
|
25443
|
+
"help.update": "Check for updates, show installation instructions and refresh existing GITIFACT blocks",
|
|
25444
|
+
"server.changelogNotFound": "Release notes not found.",
|
|
25445
|
+
"server.changelogUnreadable": "Could not read release notes.",
|
|
25446
|
+
"update.text.current": "Current version: {version}",
|
|
25447
|
+
"update.text.available": "New version: {version}",
|
|
25448
|
+
"update.text.upToDate": "You are using the latest version.",
|
|
25449
|
+
"update.text.disabled": "Update checks are disabled.",
|
|
25450
|
+
"update.text.unavailable": "Could not check for updates. Check your network connection.",
|
|
25451
|
+
"update.text.install": "Update command: {command}",
|
|
25452
|
+
"update.text.afterInstall": "This command refreshes the instructions with the new version without a global installation. If you manage Gitifact as a project dependency, update that dependency first. Restart the browser with the new version.",
|
|
25453
|
+
"update.text.blockRefreshed": "Instruction blocks refreshed: {paths}",
|
|
25454
|
+
"update.text.blockCurrent": "Instruction blocks are already current: {paths}",
|
|
25455
|
+
"update.text.noBlock": "No instruction files contain a GITIFACT block. Run npx --yes gitifact@{version} init to install one.",
|
|
25456
|
+
"update.text.notInitialized": "Only the version was checked because this project is not initialized.",
|
|
25457
|
+
"update.text.missing": "{paths} is missing, so Claude Code cannot load the AGENTS.md instructions. Rerun npx --yes gitifact@{version} init to create the import file.",
|
|
25458
|
+
"update.text.committed": "Instruction blocks committed: {commit} ({paths})",
|
|
25459
|
+
"update.text.commitNothing": "No instruction block changes to commit.",
|
|
25460
|
+
"update.text.commitSkipped.untracked": "Not committed. Instruction files are untracked: {paths}",
|
|
25461
|
+
"update.text.commitSkipped.other-changes": "Not committed. There are uncommitted edits outside the blocks: {paths}",
|
|
25462
|
+
"update.text.commitSkipped.commit-failed": "Not committed. git commit was rejected; refreshed files remain in place: {paths}",
|
|
25463
|
+
"update.commitResultUnknown": "Could not verify the commit result. Inspect the parents and changed files of HEAD {commit}.",
|
|
25464
|
+
"update.failed": "Update check failed.",
|
|
25465
|
+
"docs.summary.wiki": "Wiki format, save commands, assets and this project's wiki policy (wiki/README.md)",
|
|
25466
|
+
"server.assetNotFound": "Asset not found.",
|
|
25467
|
+
"docs.wikiPolicyProject": "Wiki policy (.gitifact/wiki/README.md)",
|
|
25468
|
+
"docs.wikiPolicyDefault": "Default wiki policy (.gitifact/wiki/README.md is missing)",
|
|
25469
|
+
"init.wikiReadmeTitle": "Wiki policy",
|
|
25470
|
+
"help.lang": "Output language (ko or en)",
|
|
25471
|
+
"help.updateCheck": "Check for a newer version without changing files"
|
|
25041
25472
|
};
|
|
25042
25473
|
|
|
25043
25474
|
// src/shared/i18n/index.ts
|
|
25044
|
-
var defaultLanguage2 = "
|
|
25045
|
-
var catalogs2 = { ko:
|
|
25046
|
-
|
|
25475
|
+
var defaultLanguage2 = "en";
|
|
25476
|
+
var catalogs2 = { ko: messages_default3, en: messages_default4 };
|
|
25477
|
+
var requestLanguage = new AsyncLocalStorage();
|
|
25478
|
+
var current = environmentLanguage();
|
|
25479
|
+
var explicit;
|
|
25480
|
+
function resolveLanguage2(value) {
|
|
25481
|
+
return /^ko(?:[-_.@]|$)/i.test(value ?? "") ? "ko" : "en";
|
|
25482
|
+
}
|
|
25483
|
+
function environmentLanguage(env = process.env) {
|
|
25484
|
+
return resolveLanguage2(env.GITIFACT_LANG || env.LC_ALL || env.LC_MESSAGES || env.LANG || Intl.DateTimeFormat().resolvedOptions().locale);
|
|
25485
|
+
}
|
|
25486
|
+
function configureLanguage(language, selected) {
|
|
25487
|
+
current = language;
|
|
25488
|
+
explicit = selected;
|
|
25489
|
+
}
|
|
25490
|
+
var getLanguage = () => requestLanguage.getStore() ?? current;
|
|
25491
|
+
var explicitLanguage = () => explicit;
|
|
25492
|
+
function withLanguage(language, action) {
|
|
25493
|
+
return requestLanguage.run(language, action);
|
|
25494
|
+
}
|
|
25495
|
+
configureCoreLanguage(getLanguage);
|
|
25496
|
+
function t2(key, values = {}, lang = getLanguage()) {
|
|
25047
25497
|
return catalogs2[lang][key].replace(/\{(\w+)\}/g, (whole, name) => name in values ? String(values[name]) : whole);
|
|
25048
25498
|
}
|
|
25049
25499
|
|
|
@@ -25117,63 +25567,13 @@ import { readFile as readFile5 } from "node:fs/promises";
|
|
|
25117
25567
|
|
|
25118
25568
|
// src/server/status-session.ts
|
|
25119
25569
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
25120
|
-
|
|
25121
|
-
// src/shared/update-check.ts
|
|
25122
|
-
var updateCheckTimeoutMs = 3e3;
|
|
25123
|
-
var release2 = /^(\d+)\.(\d+)\.(\d+)$/;
|
|
25124
|
-
var running = /^(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z.-]+)?$/;
|
|
25125
|
-
function isNewerRelease(latest, current) {
|
|
25126
|
-
const next = release2.exec(latest);
|
|
25127
|
-
const now = running.exec(current);
|
|
25128
|
-
if (!next || !now) return false;
|
|
25129
|
-
for (let index = 1; index <= 3; index++) {
|
|
25130
|
-
const difference = Number(next[index]) - Number(now[index]);
|
|
25131
|
-
if (difference !== 0) return difference > 0;
|
|
25132
|
-
}
|
|
25133
|
-
return now[4] !== void 0;
|
|
25134
|
-
}
|
|
25135
|
-
var updateCheckDisabled = (env, flagged = false) => flagged || env.GITIFACT_NO_UPDATE_CHECK !== void 0 && env.GITIFACT_NO_UPDATE_CHECK !== "" && env.GITIFACT_NO_UPDATE_CHECK !== "0";
|
|
25136
|
-
var checkingUpdate = { status: "checking", latestVersion: null };
|
|
25137
|
-
var disabledUpdate = { status: "disabled", latestVersion: null };
|
|
25138
|
-
async function resolveUpdate(current, fetchLatest, signal, timeoutMs = updateCheckTimeoutMs) {
|
|
25139
|
-
const controller = new AbortController();
|
|
25140
|
-
const abort = () => controller.abort();
|
|
25141
|
-
const timer = setTimeout(abort, timeoutMs);
|
|
25142
|
-
signal?.addEventListener("abort", abort, { once: true });
|
|
25143
|
-
if (signal?.aborted) controller.abort();
|
|
25144
|
-
try {
|
|
25145
|
-
const latest = await Promise.race([
|
|
25146
|
-
fetchLatest(controller.signal),
|
|
25147
|
-
new Promise((_resolve, reject) => {
|
|
25148
|
-
if (controller.signal.aborted) reject(new Error("aborted"));
|
|
25149
|
-
else controller.signal.addEventListener("abort", () => reject(new Error("aborted")), { once: true });
|
|
25150
|
-
})
|
|
25151
|
-
]);
|
|
25152
|
-
if (typeof latest !== "string" || !release2.test(latest)) return { status: "unavailable", latestVersion: null };
|
|
25153
|
-
return { status: isNewerRelease(latest, current) ? "available" : "up-to-date", latestVersion: latest };
|
|
25154
|
-
} catch {
|
|
25155
|
-
return { status: "unavailable", latestVersion: null };
|
|
25156
|
-
} finally {
|
|
25157
|
-
clearTimeout(timer);
|
|
25158
|
-
signal?.removeEventListener("abort", abort);
|
|
25159
|
-
}
|
|
25160
|
-
}
|
|
25161
|
-
var npmGlobalInstall = (version2) => "npm install -g gitifact@" + version2;
|
|
25162
|
-
|
|
25163
|
-
// src/server/status-session.ts
|
|
25164
25570
|
function createStatusSession(initial, read, cliVersion = "0.0.0") {
|
|
25165
25571
|
const identity2 = { sessionId: randomUUID2(), repository: { key: initial.repository.key, worktreeKey: initial.repository.worktreeKey } };
|
|
25166
|
-
|
|
25572
|
+
const session = browserSessionV3.parse({ contract: "browser-session", version: 3, ...identity2, cliVersion });
|
|
25167
25573
|
let latest = initial;
|
|
25168
25574
|
let pending;
|
|
25169
25575
|
return {
|
|
25170
|
-
|
|
25171
|
-
get session() {
|
|
25172
|
-
return browserSessionV2.parse({ contract: "browser-session", version: 2, ...identity2, cliVersion, update });
|
|
25173
|
-
},
|
|
25174
|
-
setUpdate(next) {
|
|
25175
|
-
update = next;
|
|
25176
|
-
},
|
|
25576
|
+
session,
|
|
25177
25577
|
get latest() {
|
|
25178
25578
|
return latest;
|
|
25179
25579
|
},
|
|
@@ -25292,7 +25692,7 @@ async function dispatch(routes, sessionId, url2, path3, request, response, allow
|
|
|
25292
25692
|
if (reply && !response.destroyed) json2(response, reply.status, reply.body);
|
|
25293
25693
|
} catch (error62) {
|
|
25294
25694
|
if (error62 instanceof HttpError) return fail4(response, error62.status, error62.code, error62.message);
|
|
25295
|
-
fail4(response, 503, "INTERNAL_ERROR", error62 instanceof Error && error62.message ? error62.message : found.unreadable ?? t2("server.internal"));
|
|
25695
|
+
fail4(response, 503, "INTERNAL_ERROR", error62 instanceof Error && error62.message ? error62.message : found.unreadable?.() ?? t2("server.internal"));
|
|
25296
25696
|
}
|
|
25297
25697
|
}
|
|
25298
25698
|
|
|
@@ -25301,12 +25701,12 @@ import { readdir, readFile } from "node:fs/promises";
|
|
|
25301
25701
|
import { join } from "node:path";
|
|
25302
25702
|
async function loadBrowserAssets(directory) {
|
|
25303
25703
|
const assets = /* @__PURE__ */ new Map();
|
|
25304
|
-
async function walk(
|
|
25305
|
-
for (const entry2 of await readdir(
|
|
25704
|
+
async function walk(current2, prefix) {
|
|
25705
|
+
for (const entry2 of await readdir(current2, { withFileTypes: true })) {
|
|
25306
25706
|
if (entry2.isSymbolicLink()) throw new Error(t2("browser.assetSymlink"));
|
|
25307
25707
|
const name = prefix + entry2.name;
|
|
25308
|
-
if (entry2.isDirectory()) await walk(join(
|
|
25309
|
-
else if (entry2.isFile()) assets.set("/" + name, await readFile(join(
|
|
25708
|
+
if (entry2.isDirectory()) await walk(join(current2, entry2.name), name + "/");
|
|
25709
|
+
else if (entry2.isFile()) assets.set("/" + name, await readFile(join(current2, entry2.name)));
|
|
25310
25710
|
}
|
|
25311
25711
|
}
|
|
25312
25712
|
try {
|
|
@@ -25348,17 +25748,18 @@ function serveStatic(request, response, path3, assets) {
|
|
|
25348
25748
|
|
|
25349
25749
|
// src/server/routes/project-routes.ts
|
|
25350
25750
|
function projectRoutes(store, readChangelog) {
|
|
25751
|
+
const localized = (value) => value.ok ? value : failureDto(new RepositoryReadError(value.error.code));
|
|
25351
25752
|
return [
|
|
25352
25753
|
// Re-reading the session never contacts the registry; it returns the state the single startup check left.
|
|
25353
25754
|
route({ method: "GET", path: "/api/v1/session", session: false, handle: () => ok(store.session) }),
|
|
25354
|
-
route({ method: "GET", path: "/api/v1/status", session: true, handle: () => ({ status: store.latest.ok ? 200 : 503, body: store.latest }) }),
|
|
25755
|
+
route({ method: "GET", path: "/api/v1/status", session: true, handle: () => ({ status: store.latest.ok ? 200 : 503, body: localized(store.latest) }) }),
|
|
25355
25756
|
route({ method: "POST", path: "/api/v1/status/refresh", session: true, handle: async ({ allowedOrigin }) => {
|
|
25356
25757
|
if (!allowedOrigin) throw new HttpError(403, "FORBIDDEN", t2("server.refreshOrigin"));
|
|
25357
25758
|
const value = await store.refresh();
|
|
25358
|
-
return { status: value.ok ? 200 : 503, body: value };
|
|
25759
|
+
return { status: value.ok ? 200 : 503, body: localized(value) };
|
|
25359
25760
|
} }),
|
|
25360
|
-
route({ method: "GET", path: "/api/v1/changelog", session: true, query: changelogQueryV1, unreadable: t2("server.changelogUnreadable"), handle: async ({ query }) => {
|
|
25361
|
-
const language = query.lang ??
|
|
25761
|
+
route({ method: "GET", path: "/api/v1/changelog", session: true, query: changelogQueryV1, unreadable: () => t2("server.changelogUnreadable"), handle: async ({ query }) => {
|
|
25762
|
+
const language = query.lang ?? getLanguage();
|
|
25362
25763
|
const own2 = await readChangelog(language);
|
|
25363
25764
|
const text2 = own2 ?? (language === defaultLanguage2 ? null : await readChangelog(defaultLanguage2));
|
|
25364
25765
|
if (text2 === null) throw new HttpError(404, "NOT_FOUND", t2("server.changelogNotFound"));
|
|
@@ -25368,8 +25769,8 @@ function projectRoutes(store, readChangelog) {
|
|
|
25368
25769
|
}
|
|
25369
25770
|
|
|
25370
25771
|
// src/adapters/git/command-scope.ts
|
|
25371
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
25372
|
-
var storage = new
|
|
25772
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
25773
|
+
var storage = new AsyncLocalStorage2();
|
|
25373
25774
|
var withCommandScope = (run) => storage.run(/* @__PURE__ */ new Map(), run);
|
|
25374
25775
|
function commandScoped(key, load) {
|
|
25375
25776
|
const cache = storage.getStore();
|
|
@@ -25615,8 +26016,8 @@ async function publishConfig(root, text2, recheck, beforePublish, replace = fals
|
|
|
25615
26016
|
const owner = await configDirectory(root);
|
|
25616
26017
|
const canonical = await realpath(directory);
|
|
25617
26018
|
const unchanged = async () => {
|
|
25618
|
-
const
|
|
25619
|
-
if (!owner || !
|
|
26019
|
+
const current2 = await configDirectory(root);
|
|
26020
|
+
if (!owner || !current2 || current2.ino !== owner.ino || current2.dev !== owner.dev || await realpath(directory) !== canonical) {
|
|
25620
26021
|
throw new InitError("INPUT_CHANGED", t2("config.directoryChanged"));
|
|
25621
26022
|
}
|
|
25622
26023
|
};
|
|
@@ -25641,8 +26042,8 @@ async function publishConfig(root, text2, recheck, beforePublish, replace = fals
|
|
|
25641
26042
|
else await link(temporary, join3(directory, "config.json"));
|
|
25642
26043
|
} finally {
|
|
25643
26044
|
await unchanged();
|
|
25644
|
-
const
|
|
25645
|
-
if (
|
|
26045
|
+
const current2 = await fileInfo(temporary);
|
|
26046
|
+
if (current2?.ino === tempOwner.ino && current2.dev === tempOwner.dev) await unlink(temporary);
|
|
25646
26047
|
}
|
|
25647
26048
|
}
|
|
25648
26049
|
|
|
@@ -25897,7 +26298,7 @@ function createCheckoutReader(root, sessionId, inherited = process.env) {
|
|
|
25897
26298
|
const raw = await readConfigFile(root);
|
|
25898
26299
|
if (!raw || !("schemaVersion" in parseManagedConfig(raw))) throw new SpecPreviewError(t2("specReader.schemaRequired"));
|
|
25899
26300
|
const head = await readHead();
|
|
25900
|
-
const [
|
|
26301
|
+
const [current2, dirty, authors, everyone] = await settled([
|
|
25901
26302
|
readWorkingPreviewState(root),
|
|
25902
26303
|
head ? git(["status", "--porcelain=v1", "--", ".gitifact/spec", WIKI_DIR]) : Promise.resolve(""),
|
|
25903
26304
|
head ? storeAuthors(head) : Promise.resolve(void 0),
|
|
@@ -25911,11 +26312,11 @@ function createCheckoutReader(root, sessionId, inherited = process.env) {
|
|
|
25911
26312
|
if (!name || !email3 || !latest) throw new SpecPreviewError(t2("specReader.authorUnreadable"));
|
|
25912
26313
|
tally(people, name, email3, latest);
|
|
25913
26314
|
}
|
|
25914
|
-
const features =
|
|
26315
|
+
const features = current2.specs.map(({ history: _history, ...feature2 }) => {
|
|
25915
26316
|
const entry2 = authors?.folders.get(feature2.path.replace(/^\.(?:gitifact|tryce)\/(spec\/[^/]+)\/requirements\.md$/, "$1"));
|
|
25916
26317
|
return { ...feature2, contributors: entry2 ? [...entry2.people.values()].sort((a, b) => b.commits - a.commits) : [], updatedAt: entry2?.latest ?? null };
|
|
25917
26318
|
});
|
|
25918
|
-
const documents =
|
|
26319
|
+
const documents = current2.wiki.documents.map((doc) => ({ ...doc, updatedAt: authors?.pages.get(doc.path) ?? null }));
|
|
25919
26320
|
if (await readHead() !== head) throw new SpecPreviewError(t2("specReader.projectChanged"));
|
|
25920
26321
|
const checkout = browserSpecsV4.parse({
|
|
25921
26322
|
contract: "browser-specs",
|
|
@@ -25934,18 +26335,69 @@ function createCheckoutReader(root, sessionId, inherited = process.env) {
|
|
|
25934
26335
|
...f.requirements.map((r) => ({ kind: "requirement", ref: r.id, title: r.title, where: f.title, body: r.body, featureId: f.id })),
|
|
25935
26336
|
...f.design ? [{ kind: "design", ref: f.id + ":design", title: f.design.title, where: f.title, body: f.design.body, featureId: f.id }] : []
|
|
25936
26337
|
]).concat(documents.map((d) => ({ kind: "document", ref: d.id, title: d.title, where: d.path.replace(/^\.gitifact\/wiki\//, ""), body: d.body, documentId: d.id })));
|
|
25937
|
-
return { checkout, stamp:
|
|
26338
|
+
return { checkout, stamp: current2.stamp, search };
|
|
25938
26339
|
}
|
|
25939
26340
|
return () => {
|
|
25940
|
-
|
|
26341
|
+
const language = getLanguage();
|
|
26342
|
+
let value = pending.get(language);
|
|
25941
26343
|
if (!value) {
|
|
25942
|
-
value = read().finally(() => pending.delete(
|
|
25943
|
-
pending.set(
|
|
26344
|
+
value = read().finally(() => pending.delete(language));
|
|
26345
|
+
pending.set(language, value);
|
|
25944
26346
|
}
|
|
25945
26347
|
return value;
|
|
25946
26348
|
};
|
|
25947
26349
|
}
|
|
25948
26350
|
|
|
26351
|
+
// src/server/history/merge-records.ts
|
|
26352
|
+
function remergeRecords(patch) {
|
|
26353
|
+
const touched = /* @__PURE__ */ new Set();
|
|
26354
|
+
for (const file2 of patch.split(/^diff --git /m).slice(1)) {
|
|
26355
|
+
const lines = file2.split("\n");
|
|
26356
|
+
const oldPath = lines.find((l) => l.startsWith("--- "))?.slice(4);
|
|
26357
|
+
const newPath = lines.find((l) => l.startsWith("+++ "))?.slice(4);
|
|
26358
|
+
if (!oldPath && !newPath) continue;
|
|
26359
|
+
const path3 = (newPath === "/dev/null" ? oldPath : newPath)?.replace(/^[ab]\//, "");
|
|
26360
|
+
if (!path3 || !recordPathPattern.test(path3) || !path3.endsWith(".md")) continue;
|
|
26361
|
+
const start = lines.findIndex((l) => l.startsWith("@@ "));
|
|
26362
|
+
if (start < 0 || !/^@@ -[01](?:,\d+)? \+[01](?:,\d+)? @@/.test(lines[start])) throw new SpecPreviewError(t2("specReader.historyUnreadable"));
|
|
26363
|
+
for (const side of ["-", "+"]) {
|
|
26364
|
+
const content = [];
|
|
26365
|
+
for (const line of lines.slice(start + 1)) {
|
|
26366
|
+
if (line.startsWith("@@ ")) throw new SpecPreviewError(t2("specReader.historyUnreadable"));
|
|
26367
|
+
if (line.startsWith(" ") || line.startsWith(side)) content.push({ text: line.slice(1).replace(/\r$/, ""), changed: line.startsWith(side) });
|
|
26368
|
+
}
|
|
26369
|
+
if (!path3.endsWith("/requirements.md")) {
|
|
26370
|
+
const source2 = content.map((l) => l.text).join("\n");
|
|
26371
|
+
const id = /^(?:id: "?|<!-- (?:gitifact|tryce)-(?:design|product|guide): )([SWPG]-[a-z2-7]{10})(?:"?$| -->$)/m.exec(source2)?.[1];
|
|
26372
|
+
if (id && content.some((l) => l.changed)) touched.add(id);
|
|
26373
|
+
continue;
|
|
26374
|
+
}
|
|
26375
|
+
let section = [];
|
|
26376
|
+
let fence;
|
|
26377
|
+
const finish = () => {
|
|
26378
|
+
if (!section[0]?.text.startsWith("## ")) return;
|
|
26379
|
+
const id = section.map((l) => /^<!-- (?:gitifact|tryce)-req: (R-[a-z2-7]{10}) -->$/.exec(l.text)?.[1]).find(Boolean);
|
|
26380
|
+
if (id && section.some((l) => l.changed)) touched.add(id);
|
|
26381
|
+
};
|
|
26382
|
+
for (const line of content) {
|
|
26383
|
+
if (!fence && /^## /.test(line.text)) {
|
|
26384
|
+
finish();
|
|
26385
|
+
section = [];
|
|
26386
|
+
}
|
|
26387
|
+
section.push(line);
|
|
26388
|
+
if (fence) {
|
|
26389
|
+
if (new RegExp(`^ {0,3}${fence.char}{${fence.size},}\\s*$`).test(line.text)) fence = void 0;
|
|
26390
|
+
} else {
|
|
26391
|
+
const match = /^ {0,3}(`{3,}|~{3,})/.exec(line.text);
|
|
26392
|
+
if (match) fence = { char: match[1][0], size: match[1].length };
|
|
26393
|
+
}
|
|
26394
|
+
}
|
|
26395
|
+
finish();
|
|
26396
|
+
}
|
|
26397
|
+
}
|
|
26398
|
+
return touched;
|
|
26399
|
+
}
|
|
26400
|
+
|
|
25949
26401
|
// src/server/history/commit-changes.ts
|
|
25950
26402
|
var HISTORY_PATHSPECS = [
|
|
25951
26403
|
...STORE_DIRS.flatMap((d) => [`:(glob)${d}/spec/*/requirements.md`, `:(glob)${d}/spec/*/design.md`, `:(glob)${d}/spec/*/history.jsonl`]),
|
|
@@ -25973,7 +26425,7 @@ function createCommitChanges(root, snapshot3) {
|
|
|
25973
26425
|
if (!oldBlob || !newBlob || path3 === void 0) throw new SpecPreviewError(t2("specReader.historyUnreadable"));
|
|
25974
26426
|
if (recordPathPattern.test(path3) || isLegacy(path3)) entries.push({ oldBlob, newBlob, path: path3 });
|
|
25975
26427
|
}
|
|
25976
|
-
return { commit, parent: parents.split(" ")[0] || void 0, author, email: email3, date: date5, committer, message, entries };
|
|
26428
|
+
return { commit, parent: parents.split(" ")[0] || void 0, parents: parents.split(" ").filter(Boolean), author, email: email3, date: date5, committer, message, entries };
|
|
25977
26429
|
});
|
|
25978
26430
|
}
|
|
25979
26431
|
function wanted(entries) {
|
|
@@ -26044,9 +26496,56 @@ function createCommitChanges(root, snapshot3) {
|
|
|
26044
26496
|
}
|
|
26045
26497
|
return { commit: c.commit, events: eventsOf(c, comparePreviewBundles(before, after).changes), boundary };
|
|
26046
26498
|
}
|
|
26499
|
+
async function merge2(c) {
|
|
26500
|
+
let touched;
|
|
26501
|
+
if (c.parents.length === 2) {
|
|
26502
|
+
const patch = reader.decode(await reader.run([
|
|
26503
|
+
"-c",
|
|
26504
|
+
"merge.conflictStyle=merge",
|
|
26505
|
+
"show",
|
|
26506
|
+
"--remerge-diff",
|
|
26507
|
+
"--format=",
|
|
26508
|
+
"--no-color",
|
|
26509
|
+
"--no-ext-diff",
|
|
26510
|
+
"--no-textconv",
|
|
26511
|
+
"--no-renames",
|
|
26512
|
+
"--src-prefix=a/",
|
|
26513
|
+
"--dst-prefix=b/",
|
|
26514
|
+
"--unified=1048576",
|
|
26515
|
+
c.commit,
|
|
26516
|
+
"--",
|
|
26517
|
+
...HISTORY_PATHSPECS
|
|
26518
|
+
]));
|
|
26519
|
+
touched = remergeRecords(patch);
|
|
26520
|
+
if (!touched.size) return { commit: c.commit, events: [], boundary: false };
|
|
26521
|
+
}
|
|
26522
|
+
const after = await snapshot3(c.commit);
|
|
26523
|
+
let boundary = false;
|
|
26524
|
+
const parents = [];
|
|
26525
|
+
for (const parent of c.parents) {
|
|
26526
|
+
try {
|
|
26527
|
+
parents.push(await snapshot3(parent));
|
|
26528
|
+
} catch (error62) {
|
|
26529
|
+
if (error62 instanceof LegacyBaselineError) {
|
|
26530
|
+
parents.push(emptyBundle());
|
|
26531
|
+
boundary = true;
|
|
26532
|
+
} else throw error62;
|
|
26533
|
+
}
|
|
26534
|
+
}
|
|
26535
|
+
const withoutReasons = (b) => ({ specs: b.specs.map((s) => ({ ...s, history: [] })), wiki: { ...b.wiki, history: [] } });
|
|
26536
|
+
const differences = parents.map((p) => comparePreviewBundles(withoutReasons(p), withoutReasons(after)).changes);
|
|
26537
|
+
const reasons = (b) => [...b.specs.flatMap((s) => s.history), ...b.wiki.history];
|
|
26538
|
+
const inherited = new Set(parents.flatMap((p) => reasons(p).map((r) => r.id)));
|
|
26539
|
+
const added = reasons(after).filter((r) => !inherited.has(r.id));
|
|
26540
|
+
const selected = differences[0].filter((change2) => touched ? touched.has(change2.id) : differences.every((d) => d.some((v) => v.id === change2.id)));
|
|
26541
|
+
return { commit: c.commit, events: eventsOf(c, selected.map((change2) => ({
|
|
26542
|
+
...change2,
|
|
26543
|
+
reasons: added.filter((r) => [...r.requirements, ...r.designs ?? [], ...r.documents ?? []].includes(change2.id))
|
|
26544
|
+
}))), boundary };
|
|
26545
|
+
}
|
|
26047
26546
|
async function batch(commits) {
|
|
26048
26547
|
const names = /* @__PURE__ */ new Map();
|
|
26049
|
-
for (const c of commits.filter((c2) => !c2.entries.some((e) => isLegacy(e.path) && !isZero(e.newBlob)))) {
|
|
26548
|
+
for (const c of commits.filter((c2) => c2.parents.length < 2 && !c2.entries.some((e) => isLegacy(e.path) && !isZero(e.newBlob)))) {
|
|
26050
26549
|
const files = wanted(c.entries);
|
|
26051
26550
|
const boundary = c.entries.some((e) => isLegacy(e.path) && !isZero(e.oldBlob));
|
|
26052
26551
|
names.set(c, { after: files.map((f) => c.commit + ":" + f), before: c.parent && !boundary ? files.map((f) => c.parent + ":" + f) : [], boundary });
|
|
@@ -26059,6 +26558,10 @@ function createCommitChanges(root, snapshot3) {
|
|
|
26059
26558
|
}
|
|
26060
26559
|
const result = [];
|
|
26061
26560
|
for (const c of commits) {
|
|
26561
|
+
if (c.parents.length > 1) {
|
|
26562
|
+
result.push(await merge2(c));
|
|
26563
|
+
continue;
|
|
26564
|
+
}
|
|
26062
26565
|
let value;
|
|
26063
26566
|
const want = names.get(c);
|
|
26064
26567
|
if (want && blobs) {
|
|
@@ -26076,12 +26579,12 @@ function createCommitChanges(root, snapshot3) {
|
|
|
26076
26579
|
return result;
|
|
26077
26580
|
}
|
|
26078
26581
|
return {
|
|
26079
|
-
/**
|
|
26582
|
+
/** All reachable record commits, children before parents even when author clocks differ. */
|
|
26080
26583
|
async lineage(head) {
|
|
26081
|
-
const text2 = reader.decode(await reader.run(["rev-list", "--
|
|
26584
|
+
const text2 = reader.decode(await reader.run(["rev-list", "--full-history", "--date-order", head, "--", ...HISTORY_PATHSPECS]));
|
|
26082
26585
|
return text2.split("\n").filter(Boolean);
|
|
26083
26586
|
},
|
|
26084
|
-
/**
|
|
26587
|
+
/** Changes in the given order; ordinary commits are batched, merges also read their resolution diff. */
|
|
26085
26588
|
async of(commits) {
|
|
26086
26589
|
const out = [];
|
|
26087
26590
|
for (let i = 0; i < commits.length; i += BATCH) {
|
|
@@ -26104,7 +26607,7 @@ function createCommitChanges(root, snapshot3) {
|
|
|
26104
26607
|
import { DatabaseSync } from "node:sqlite";
|
|
26105
26608
|
import { mkdir as mkdir3, rm } from "node:fs/promises";
|
|
26106
26609
|
import { join as join5 } from "node:path";
|
|
26107
|
-
var INDEX_FORMAT =
|
|
26610
|
+
var INDEX_FORMAT = 2;
|
|
26108
26611
|
var SCHEMA = `
|
|
26109
26612
|
-- Commits already read, and whether the commit's parent was legacy JSON the history does not read past.
|
|
26110
26613
|
CREATE TABLE commits (oid TEXT PRIMARY KEY, boundary INTEGER NOT NULL);
|
|
@@ -26114,7 +26617,7 @@ var SCHEMA = `
|
|
|
26114
26617
|
id TEXT NOT NULL, kind TEXT NOT NULL, types TEXT NOT NULL, email TEXT NOT NULL, date TEXT NOT NULL,
|
|
26115
26618
|
before_spec TEXT, after_spec TEXT, needle TEXT NOT NULL, row TEXT NOT NULL, detail TEXT NOT NULL);
|
|
26116
26619
|
CREATE INDEX changes_by_commit ON changes (oid, ord);
|
|
26117
|
-
--
|
|
26620
|
+
-- All reachable record commits of one HEAD, children before parents (pos 0 is newest).
|
|
26118
26621
|
CREATE TABLE lineage (head TEXT NOT NULL, pos INTEGER NOT NULL, oid TEXT NOT NULL, PRIMARY KEY (head, pos));
|
|
26119
26622
|
CREATE INDEX lineage_by_commit ON lineage (head, oid);
|
|
26120
26623
|
CREATE TABLE heads (head TEXT PRIMARY KEY, seen INTEGER NOT NULL);
|
|
@@ -26295,12 +26798,13 @@ function createHistoryIndex(root, snapshot3) {
|
|
|
26295
26798
|
}
|
|
26296
26799
|
function ensure(head) {
|
|
26297
26800
|
if (built.has(head)) return Promise.resolve();
|
|
26298
|
-
|
|
26801
|
+
const key = head + ":" + getLanguage();
|
|
26802
|
+
let pending = building.get(key);
|
|
26299
26803
|
if (!pending) {
|
|
26300
26804
|
pending = build(head).then(() => {
|
|
26301
26805
|
built.add(head);
|
|
26302
|
-
}).finally(() => building.delete(
|
|
26303
|
-
building.set(
|
|
26806
|
+
}).finally(() => building.delete(key));
|
|
26807
|
+
building.set(key, pending);
|
|
26304
26808
|
}
|
|
26305
26809
|
return pending;
|
|
26306
26810
|
}
|
|
@@ -26381,8 +26885,8 @@ function createHistoryIndex(root, snapshot3) {
|
|
|
26381
26885
|
/** Replaces the search rows of one worktree's checkout when it changed since they were written. */
|
|
26382
26886
|
async syncCheckout(scope, stamp, documents) {
|
|
26383
26887
|
await database.with((db) => {
|
|
26384
|
-
const
|
|
26385
|
-
if (
|
|
26888
|
+
const current2 = db.prepare("SELECT stamp FROM checkouts WHERE scope = ?").get(scope);
|
|
26889
|
+
if (current2?.stamp === stamp) return;
|
|
26386
26890
|
transaction(db, () => {
|
|
26387
26891
|
db.prepare("DELETE FROM search WHERE scope = ?").run(scope);
|
|
26388
26892
|
const row = db.prepare("INSERT INTO search (scope, kind, ref, oid, payload, title, place, body) VALUES (?,?,?,?,?,?,?,?)");
|
|
@@ -26448,13 +26952,14 @@ function recordRoutes(root, sessionId, env) {
|
|
|
26448
26952
|
const reader = specPreviewReader(root);
|
|
26449
26953
|
const snapshots = /* @__PURE__ */ new Map();
|
|
26450
26954
|
const snapshot3 = (oid3) => {
|
|
26451
|
-
|
|
26955
|
+
const key = oid3 + ":" + getLanguage();
|
|
26956
|
+
let value = snapshots.get(key);
|
|
26452
26957
|
if (!value) {
|
|
26453
26958
|
value = reader.readBundle(oid3).catch((e) => {
|
|
26454
|
-
snapshots.delete(
|
|
26959
|
+
snapshots.delete(key);
|
|
26455
26960
|
throw e;
|
|
26456
26961
|
});
|
|
26457
|
-
snapshots.set(
|
|
26962
|
+
snapshots.set(key, value);
|
|
26458
26963
|
}
|
|
26459
26964
|
if (snapshots.size > 128) snapshots.delete(snapshots.keys().next().value);
|
|
26460
26965
|
return value;
|
|
@@ -26462,7 +26967,7 @@ function recordRoutes(root, sessionId, env) {
|
|
|
26462
26967
|
const readCheckout = createCheckoutReader(root, sessionId, env);
|
|
26463
26968
|
const history = createHistoryIndex(root, snapshot3);
|
|
26464
26969
|
const scope = "checkout:" + root;
|
|
26465
|
-
const unreadable = t2("server.specsUnreadable");
|
|
26970
|
+
const unreadable = () => t2("server.specsUnreadable");
|
|
26466
26971
|
let synced;
|
|
26467
26972
|
const checkout = async () => {
|
|
26468
26973
|
const read = await readCheckout();
|
|
@@ -26552,18 +27057,19 @@ async function startBrowserServer(options) {
|
|
|
26552
27057
|
...recordRoutes(root, sessionId, options.env),
|
|
26553
27058
|
...assetRoutes(root)
|
|
26554
27059
|
];
|
|
26555
|
-
let updatePending;
|
|
26556
27060
|
let closing = false;
|
|
26557
27061
|
let origin = "";
|
|
26558
27062
|
const server = createServer({ requestTimeout: 5e3, headersTimeout: 5e3, maxHeaderSize: 8192 }, (request, response) => {
|
|
26559
|
-
|
|
27063
|
+
const header = request.headers["accept-language"];
|
|
27064
|
+
const language = typeof header === "string" ? resolveLanguage2(header.split(",")[0]?.split(";")[0]?.trim()) : getLanguage();
|
|
27065
|
+
void withLanguage(language, async () => {
|
|
26560
27066
|
if (closing) return fail4(response, 503, "SERVER_CLOSING", t2("server.closing"));
|
|
26561
27067
|
const admitted = admit(request, response, origin, !!options.dev);
|
|
26562
27068
|
if (!admitted) return;
|
|
26563
27069
|
if (admitted.api) return dispatch(routes, sessionId, admitted.url, admitted.path, request, response, admitted.allowedOrigin);
|
|
26564
27070
|
return serveStatic(request, response, admitted.path, assets);
|
|
26565
|
-
})
|
|
26566
|
-
if (!response.headersSent && !response.destroyed) fail4(response, 500, "INTERNAL_ERROR", t2("server.internal"));
|
|
27071
|
+
}).catch(() => {
|
|
27072
|
+
if (!response.headersSent && !response.destroyed) fail4(response, 500, "INTERNAL_ERROR", t2("server.internal", {}, language));
|
|
26567
27073
|
else response.destroy();
|
|
26568
27074
|
});
|
|
26569
27075
|
});
|
|
@@ -26587,12 +27093,6 @@ async function startBrowserServer(options) {
|
|
|
26587
27093
|
const address = server.address();
|
|
26588
27094
|
if (!address || typeof address === "string") throw new Error(t2("server.addressUnknown"));
|
|
26589
27095
|
origin = "http://127.0.0.1:" + address.port;
|
|
26590
|
-
if (options.fetchLatest) {
|
|
26591
|
-
store.setUpdate(checkingUpdate);
|
|
26592
|
-
updatePending = resolveUpdate(store.session.cliVersion, options.fetchLatest, controller.signal, options.updateCheckTimeoutMs).then((state) => {
|
|
26593
|
-
store.setUpdate(state);
|
|
26594
|
-
});
|
|
26595
|
-
}
|
|
26596
27096
|
let resolveClosed;
|
|
26597
27097
|
const closed = new Promise((resolve) => {
|
|
26598
27098
|
resolveClosed = resolve;
|
|
@@ -26608,7 +27108,6 @@ async function startBrowserServer(options) {
|
|
|
26608
27108
|
server.closeAllConnections();
|
|
26609
27109
|
});
|
|
26610
27110
|
await store.pending;
|
|
26611
|
-
await updatePending;
|
|
26612
27111
|
options.signal?.removeEventListener("abort", onAbort);
|
|
26613
27112
|
options.signal?.removeEventListener("abort", closeOnAbort);
|
|
26614
27113
|
resolveClosed();
|
|
@@ -26625,17 +27124,6 @@ async function startBrowserServer(options) {
|
|
|
26625
27124
|
}, close, closed };
|
|
26626
27125
|
}
|
|
26627
27126
|
|
|
26628
|
-
// src/adapters/registry/latest-version.ts
|
|
26629
|
-
var manifestUrl = "https://registry.npmjs.org/gitifact";
|
|
26630
|
-
var fetchLatestVersion = async (signal) => {
|
|
26631
|
-
const response = await fetch(manifestUrl, { signal, redirect: "error", headers: { Accept: "application/vnd.npm.install-v1+json" } });
|
|
26632
|
-
if (!response.ok) throw new Error("registry status " + response.status);
|
|
26633
|
-
const manifest = await response.json();
|
|
26634
|
-
const latest = manifest["dist-tags"]?.latest;
|
|
26635
|
-
if (typeof latest !== "string") throw new Error("registry answer has no latest tag");
|
|
26636
|
-
return latest;
|
|
26637
|
-
};
|
|
26638
|
-
|
|
26639
27127
|
// src/commands/browser.ts
|
|
26640
27128
|
function parsePort(value) {
|
|
26641
27129
|
if (!/^\d+$/.test(value) || Number(value) > 65535) throw new InvalidArgumentError(t2("browser.invalidPort"));
|
|
@@ -26652,9 +27140,7 @@ async function runBrowser(options, version2) {
|
|
|
26652
27140
|
port: options.port,
|
|
26653
27141
|
dev: options.dev ?? false,
|
|
26654
27142
|
signal: controller.signal,
|
|
26655
|
-
cliVersion: version2
|
|
26656
|
-
// --no-update-check or GITIFACT_NO_UPDATE_CHECK keeps the server from contacting the npm registry.
|
|
26657
|
-
...updateCheckDisabled(process.env, options.updateCheck === false) ? {} : { fetchLatest: fetchLatestVersion }
|
|
27143
|
+
cliVersion: version2
|
|
26658
27144
|
});
|
|
26659
27145
|
if (!controller.signal.aborted) process.stdout.write(server.url + "/\n");
|
|
26660
27146
|
await server.closed;
|
|
@@ -26824,7 +27310,7 @@ var info3 = (path3) => lstat5(path3).catch((e) => {
|
|
|
26824
27310
|
if (e.code === "ENOENT") return void 0;
|
|
26825
27311
|
throw e;
|
|
26826
27312
|
});
|
|
26827
|
-
function listDocTopics(lang =
|
|
27313
|
+
function listDocTopics(lang = getLanguage()) {
|
|
26828
27314
|
const width = Math.max(...docTopics.map((topic) => topic.length));
|
|
26829
27315
|
return docTopics.map((topic) => topic.padEnd(width) + " " + t2(`docs.summary.${topic}`, {}, lang)).join("\n") + "\n";
|
|
26830
27316
|
}
|
|
@@ -26853,7 +27339,7 @@ async function readWikiPolicy(root) {
|
|
|
26853
27339
|
}).join("\n").trim();
|
|
26854
27340
|
return body || void 0;
|
|
26855
27341
|
}
|
|
26856
|
-
async function renderDoc(topic, controls = {}, lang =
|
|
27342
|
+
async function renderDoc(topic, controls = {}, lang = getLanguage()) {
|
|
26857
27343
|
const read = controls.readDoc ?? readBundledDoc;
|
|
26858
27344
|
const format = (await read(topic, lang)).trimEnd();
|
|
26859
27345
|
if (topic !== "wiki") return format + "\n";
|
|
@@ -26892,11 +27378,16 @@ function boilerplateFor(path3) {
|
|
|
26892
27378
|
return "# " + name + "\n\nProject-specific guidance for AI coding agents.\n";
|
|
26893
27379
|
}
|
|
26894
27380
|
var readBundledBlock = (lang) => readFile9(new URL("./i18n/" + lang + "/block.md", import.meta.url), "utf8");
|
|
26895
|
-
async function renderAgentBlock(version2, controls = {}, lang =
|
|
27381
|
+
async function renderAgentBlock(version2, controls = {}, lang = getLanguage()) {
|
|
26896
27382
|
const body = await (controls.readBlock ?? readBundledBlock)(lang);
|
|
26897
27383
|
const filled = body.trimEnd().replace(/\{(version|language|topics)\}/g, (_whole, name) => name === "version" ? version2 : name === "language" ? lang : docTopics.join(", "));
|
|
26898
27384
|
return AGENT_START + "\n" + filled + "\n" + AGENT_END;
|
|
26899
27385
|
}
|
|
27386
|
+
function parseAgentBlock(text2) {
|
|
27387
|
+
const match = /^gitifact v(\S+)(?: · ([a-z][a-z-]*))? · .*schemaVersion (\d+)$/m.exec(text2);
|
|
27388
|
+
if (!match) return null;
|
|
27389
|
+
return { version: match[1], language: match[2] ?? "ko", schemaVersion: Number(match[3]) };
|
|
27390
|
+
}
|
|
26900
27391
|
function findBlock(text2) {
|
|
26901
27392
|
const start = text2.indexOf(AGENT_START);
|
|
26902
27393
|
if (start === -1) {
|
|
@@ -26982,17 +27473,23 @@ async function planAgentDocs(root, options) {
|
|
|
26982
27473
|
}
|
|
26983
27474
|
return { mode: "remove", paths: paths2, writes, missing: [] };
|
|
26984
27475
|
}
|
|
26985
|
-
const
|
|
27476
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
27477
|
+
const blockFor = async (previous) => {
|
|
27478
|
+
const stored = previous ? parseAgentBlock(previous)?.language : void 0;
|
|
27479
|
+
const language = explicitLanguage() ?? (stored === "ko" || stored === "en" ? stored : getLanguage());
|
|
27480
|
+
if (!blocks.has(language)) blocks.set(language, await renderAgentBlock(options.version, options, language));
|
|
27481
|
+
return blocks.get(language);
|
|
27482
|
+
};
|
|
26986
27483
|
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);
|
|
26987
27484
|
for (const path3 of inject) {
|
|
26988
27485
|
const previous = existing.get(path3);
|
|
26989
|
-
const next = injectBlock(previous,
|
|
27486
|
+
const next = injectBlock(previous, await blockFor(previous), boilerplateFor(path3));
|
|
26990
27487
|
paths2.push(path3);
|
|
26991
27488
|
if (next !== previous) writes.push({ path: path3, previous, next });
|
|
26992
27489
|
}
|
|
26993
27490
|
if (create) {
|
|
26994
27491
|
paths2.push(create);
|
|
26995
|
-
writes.push({ path: create, previous: null, next: injectBlock(null,
|
|
27492
|
+
writes.push({ path: create, previous: null, next: injectBlock(null, await blockFor(), boilerplateFor(create)) });
|
|
26996
27493
|
}
|
|
26997
27494
|
const missing2 = [];
|
|
26998
27495
|
if (paths2.includes("AGENTS.md") && lacksClaudeFile(existing)) {
|
|
@@ -27013,17 +27510,59 @@ async function applyAgentDocs(root, plan, recheck) {
|
|
|
27013
27510
|
return plan.writes.map((write) => write.path);
|
|
27014
27511
|
}
|
|
27015
27512
|
|
|
27513
|
+
// src/shared/update-check.ts
|
|
27514
|
+
var updateCheckTimeoutMs = 3e3;
|
|
27515
|
+
var release2 = /^(\d+)\.(\d+)\.(\d+)$/;
|
|
27516
|
+
var running = /^(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z.-]+)?$/;
|
|
27517
|
+
function isNewerRelease(latest, current2) {
|
|
27518
|
+
const next = release2.exec(latest);
|
|
27519
|
+
const now = running.exec(current2);
|
|
27520
|
+
if (!next || !now) return false;
|
|
27521
|
+
for (let index = 1; index <= 3; index++) {
|
|
27522
|
+
const difference = Number(next[index]) - Number(now[index]);
|
|
27523
|
+
if (difference !== 0) return difference > 0;
|
|
27524
|
+
}
|
|
27525
|
+
return now[4] !== void 0;
|
|
27526
|
+
}
|
|
27527
|
+
var updateCheckDisabled = (env, flagged = false) => flagged || env.GITIFACT_NO_UPDATE_CHECK !== void 0 && env.GITIFACT_NO_UPDATE_CHECK !== "" && env.GITIFACT_NO_UPDATE_CHECK !== "0";
|
|
27528
|
+
var disabledUpdate = { status: "disabled", latestVersion: null };
|
|
27529
|
+
async function resolveUpdate(current2, fetchLatest, signal, timeoutMs = updateCheckTimeoutMs) {
|
|
27530
|
+
const controller = new AbortController();
|
|
27531
|
+
const abort = () => controller.abort();
|
|
27532
|
+
const timer = setTimeout(abort, timeoutMs);
|
|
27533
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
27534
|
+
if (signal?.aborted) controller.abort();
|
|
27535
|
+
try {
|
|
27536
|
+
const latest = await Promise.race([
|
|
27537
|
+
fetchLatest(controller.signal),
|
|
27538
|
+
new Promise((_resolve, reject) => {
|
|
27539
|
+
if (controller.signal.aborted) reject(new Error("aborted"));
|
|
27540
|
+
else controller.signal.addEventListener("abort", () => reject(new Error("aborted")), { once: true });
|
|
27541
|
+
})
|
|
27542
|
+
]);
|
|
27543
|
+
if (typeof latest !== "string" || !release2.test(latest)) return { status: "unavailable", latestVersion: null };
|
|
27544
|
+
return { status: isNewerRelease(latest, current2) ? "available" : "up-to-date", latestVersion: latest };
|
|
27545
|
+
} catch {
|
|
27546
|
+
return { status: "unavailable", latestVersion: null };
|
|
27547
|
+
} finally {
|
|
27548
|
+
clearTimeout(timer);
|
|
27549
|
+
signal?.removeEventListener("abort", abort);
|
|
27550
|
+
}
|
|
27551
|
+
}
|
|
27552
|
+
var npmGlobalInstall = (version2) => "npm install -g gitifact@" + version2;
|
|
27553
|
+
var npxUpdate = (version2) => "npx --yes gitifact@" + version2 + " update";
|
|
27554
|
+
|
|
27016
27555
|
// src/commands/spec-init.ts
|
|
27017
|
-
async function initializeSpecProject(cwd, dryRun = false, env = process.env, beforePublish, agentDocs, readDoc = (name) => readBundledDoc(name,
|
|
27556
|
+
async function initializeSpecProject(cwd, dryRun = false, env = process.env, beforePublish, agentDocs, readDoc = (name) => readBundledDoc(name, getLanguage()), update = disabledUpdate) {
|
|
27018
27557
|
const repo = initRepository(cwd, env);
|
|
27019
27558
|
const first = await repo.inspect();
|
|
27020
27559
|
const root = first.state.repository.rootPath;
|
|
27021
27560
|
const docsPlan = agentDocs ? await planAgentDocs(root, agentDocs) : skippedAgentDocs;
|
|
27022
27561
|
const result = async (config3, outcome) => {
|
|
27023
27562
|
const checked = await update;
|
|
27024
|
-
return
|
|
27563
|
+
return projectInitV6.parse({
|
|
27025
27564
|
contract: "project-init",
|
|
27026
|
-
version:
|
|
27565
|
+
version: 6,
|
|
27027
27566
|
ok: true,
|
|
27028
27567
|
outcome,
|
|
27029
27568
|
rootPath: root,
|
|
@@ -27032,7 +27571,7 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
|
|
|
27032
27571
|
baseline: config3.baseline,
|
|
27033
27572
|
agentDocs: { mode: docsPlan.mode, paths: docsPlan.paths },
|
|
27034
27573
|
update: checked,
|
|
27035
|
-
install: checked.status === "available" ? { npmGlobal: npmGlobalInstall(checked.latestVersion) } : null
|
|
27574
|
+
install: checked.status === "available" ? { npx: npxUpdate(checked.latestVersion), npmGlobal: npmGlobalInstall(checked.latestVersion) } : null
|
|
27036
27575
|
});
|
|
27037
27576
|
};
|
|
27038
27577
|
const existing = async (text3) => {
|
|
@@ -27133,6 +27672,17 @@ async function writeWikiPolicy(root, readDoc) {
|
|
|
27133
27672
|
await writeFile2(join10(root, ...WIKI_ENTRY_PATH.split("/")), text2, { flag: "wx" });
|
|
27134
27673
|
}
|
|
27135
27674
|
|
|
27675
|
+
// src/adapters/registry/latest-version.ts
|
|
27676
|
+
var manifestUrl = "https://registry.npmjs.org/gitifact";
|
|
27677
|
+
var fetchLatestVersion = async (signal) => {
|
|
27678
|
+
const response = await fetch(manifestUrl, { signal, redirect: "error", headers: { Accept: "application/vnd.npm.install-v1+json" } });
|
|
27679
|
+
if (!response.ok) throw new Error("registry status " + response.status);
|
|
27680
|
+
const manifest = await response.json();
|
|
27681
|
+
const latest = manifest["dist-tags"]?.latest;
|
|
27682
|
+
if (typeof latest !== "string") throw new Error("registry answer has no latest tag");
|
|
27683
|
+
return latest;
|
|
27684
|
+
};
|
|
27685
|
+
|
|
27136
27686
|
// src/commands/init.ts
|
|
27137
27687
|
async function runInit(options, version2) {
|
|
27138
27688
|
const cancel = new AbortController();
|
|
@@ -27149,7 +27699,7 @@ async function runInit(options, version2) {
|
|
|
27149
27699
|
);
|
|
27150
27700
|
if (!dto.ok) throw new Error("unreachable");
|
|
27151
27701
|
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") + ")");
|
|
27152
|
-
const newer = dto.install ? t2("init.text.updateAvailable", { version: dto.update.latestVersion, command: dto.install.
|
|
27702
|
+
const newer = dto.install ? t2("init.text.updateAvailable", { version: dto.update.latestVersion, command: dto.install.npx }) + "\n" : "";
|
|
27153
27703
|
process.stdout.write(options.format === "text" ? `${dto.outcome}: ${dto.rootPath}/.gitifact/config.json
|
|
27154
27704
|
${t2("init.text.storage")}: schemaVersion ${dto.schemaVersion}
|
|
27155
27705
|
${t2("init.text.agentDocs")}: ${docs}
|
|
@@ -27158,7 +27708,7 @@ ${newer}` : JSON.stringify(dto) + "\n");
|
|
|
27158
27708
|
cancel.abort();
|
|
27159
27709
|
const known = error62 instanceof InitError || error62 instanceof RepositoryReadError;
|
|
27160
27710
|
const failure2 = { code: known ? error62.code : "INIT_FAILED", message: known ? error62.message : t2("init.failed") };
|
|
27161
|
-
process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(
|
|
27711
|
+
process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(projectInitV6.parse({ contract: "project-init", version: 6, ok: false, error: failure2 })) + "\n");
|
|
27162
27712
|
process.exitCode = 1;
|
|
27163
27713
|
}
|
|
27164
27714
|
}
|
|
@@ -27263,13 +27813,13 @@ async function updateCommand(cwd, version2, env = process.env, controls = {}) {
|
|
|
27263
27813
|
refreshBlocks(cwd, version2, env, controls)
|
|
27264
27814
|
]);
|
|
27265
27815
|
const commit = controls.commit ? await commitBlocks(agentDocs, version2, env) : commitState("not-requested");
|
|
27266
|
-
return
|
|
27816
|
+
return updateV4.parse({
|
|
27267
27817
|
contract: "update",
|
|
27268
|
-
version:
|
|
27818
|
+
version: 4,
|
|
27269
27819
|
ok: true,
|
|
27270
27820
|
cliVersion: version2,
|
|
27271
27821
|
update,
|
|
27272
|
-
install: update.status === "available" ? { npmGlobal: npmGlobalInstall(update.latestVersion) } : null,
|
|
27822
|
+
install: update.status === "available" ? { npx: npxUpdate(update.latestVersion), npmGlobal: npmGlobalInstall(update.latestVersion) } : null,
|
|
27273
27823
|
agentDocs: { state: agentDocs.state, paths: agentDocs.paths, missing: agentDocs.missing },
|
|
27274
27824
|
commit
|
|
27275
27825
|
});
|
|
@@ -27284,9 +27834,9 @@ async function runUpdate(options, version2) {
|
|
|
27284
27834
|
}
|
|
27285
27835
|
const lines = [t2("update.text.current", { version: dto.cliVersion })];
|
|
27286
27836
|
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"));
|
|
27287
|
-
if (dto.install) lines.push(t2("update.text.install", { command: dto.install.
|
|
27288
|
-
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"));
|
|
27289
|
-
if (dto.agentDocs.missing.length) lines.push(t2("update.text.missing", { paths: dto.agentDocs.missing.join(", ") }));
|
|
27837
|
+
if (dto.install) lines.push(t2("update.text.install", { command: dto.install.npx }), t2("update.text.afterInstall"));
|
|
27838
|
+
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", { version: dto.cliVersion }) : t2("update.text.notInitialized"));
|
|
27839
|
+
if (dto.agentDocs.missing.length) lines.push(t2("update.text.missing", { paths: dto.agentDocs.missing.join(", "), version: dto.cliVersion }));
|
|
27290
27840
|
const { commit } = dto;
|
|
27291
27841
|
if (commit.state === "committed") lines.push(t2("update.text.committed", { commit: commit.commit.slice(0, 12), paths: commit.paths.join(", ") }));
|
|
27292
27842
|
else if (commit.state === "nothing") lines.push(t2("update.text.commitNothing"));
|
|
@@ -27299,11 +27849,38 @@ async function runUpdate(options, version2) {
|
|
|
27299
27849
|
} catch (error62) {
|
|
27300
27850
|
const known = error62 instanceof InitError || error62 instanceof RepositoryReadError;
|
|
27301
27851
|
const failure2 = { code: known ? error62.code : "UPDATE_FAILED", message: known ? error62.message : t2("update.failed") };
|
|
27302
|
-
process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(
|
|
27852
|
+
process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(updateV4.parse({ contract: "update", version: 4, ok: false, error: failure2 })) + "\n");
|
|
27303
27853
|
process.exitCode = 1;
|
|
27304
27854
|
}
|
|
27305
27855
|
}
|
|
27306
27856
|
|
|
27857
|
+
// src/commands/update-check.ts
|
|
27858
|
+
async function checkUpdateCommand(version2, env = process.env, controls = {}) {
|
|
27859
|
+
const update = updateCheckDisabled(env) ? disabledUpdate : await resolveUpdate(version2, controls.fetchLatest ?? fetchLatestVersion, void 0, controls.timeoutMs);
|
|
27860
|
+
return updateCheckV1.parse({
|
|
27861
|
+
contract: "update-check",
|
|
27862
|
+
version: 1,
|
|
27863
|
+
ok: true,
|
|
27864
|
+
cliVersion: version2,
|
|
27865
|
+
update,
|
|
27866
|
+
command: update.status === "available" ? npxUpdate(update.latestVersion) : null
|
|
27867
|
+
});
|
|
27868
|
+
}
|
|
27869
|
+
async function runUpdateCheck(format, version2) {
|
|
27870
|
+
const dto = await checkUpdateCommand(version2);
|
|
27871
|
+
if (format === "json") {
|
|
27872
|
+
process.stdout.write(JSON.stringify(dto) + "\n");
|
|
27873
|
+
return;
|
|
27874
|
+
}
|
|
27875
|
+
const state = dto.update;
|
|
27876
|
+
const lines = [
|
|
27877
|
+
t2("update.text.current", { version: dto.cliVersion }),
|
|
27878
|
+
state.status === "available" ? t2("update.text.available", { version: state.latestVersion }) : state.status === "up-to-date" ? t2("update.text.upToDate") : state.status === "disabled" ? t2("update.text.disabled") : t2("update.text.unavailable")
|
|
27879
|
+
];
|
|
27880
|
+
if (dto.command) lines.push(t2("update.text.install", { command: dto.command }));
|
|
27881
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
27882
|
+
}
|
|
27883
|
+
|
|
27307
27884
|
// src/commands/spec-preview.ts
|
|
27308
27885
|
import { join as join15 } from "node:path";
|
|
27309
27886
|
import { readFile as readFile12, stat as stat2 } from "node:fs/promises";
|
|
@@ -27345,11 +27922,11 @@ async function readPreviewContext(cwd) {
|
|
|
27345
27922
|
var context = readPreviewContext;
|
|
27346
27923
|
async function readFinalPreviewChanges(cwd) {
|
|
27347
27924
|
const c = await context(cwd);
|
|
27348
|
-
const
|
|
27349
|
-
const delta = finalSpecPreviewChanges(c.bundle,
|
|
27925
|
+
const current2 = await readWorkingPreviewState(cwd);
|
|
27926
|
+
const delta = finalSpecPreviewChanges(c.bundle, current2.bundle);
|
|
27350
27927
|
await c.recheck();
|
|
27351
|
-
const pendingReasons = pendingPreviewReasons(c.bundle,
|
|
27352
|
-
return { base: c.base, expected: expected(c.base,
|
|
27928
|
+
const pendingReasons = pendingPreviewReasons(c.bundle, current2.bundle);
|
|
27929
|
+
return { base: c.base, expected: expected(c.base, current2.stamp), stamp: current2.stamp, pendingReasons, ...delta };
|
|
27353
27930
|
}
|
|
27354
27931
|
|
|
27355
27932
|
// src/adapters/filesystem/agent-inputs.ts
|
|
@@ -27535,15 +28112,15 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
27535
28112
|
if (await info5(busy)) fail7(t2("commit.busy", { path: busy }));
|
|
27536
28113
|
if ((await staged()).length) fail7(t2("commit.existingStaging"));
|
|
27537
28114
|
const indexHash = hash2(await optional2(indexPath) ?? Buffer.alloc(0));
|
|
27538
|
-
const
|
|
27539
|
-
if (request.expected !== void 0 && request.expected !== previewExpected(c.base,
|
|
27540
|
-
const reasons = request.reasons ?? pendingPreviewReasons(c.bundle,
|
|
27541
|
-
const prepared = prepareSpecPreview(c.bundle,
|
|
28115
|
+
const current2 = c.working;
|
|
28116
|
+
if (request.expected !== void 0 && request.expected !== previewExpected(c.base, current2.stamp)) fail7(t2("commit.staleInput"));
|
|
28117
|
+
const reasons = request.reasons ?? pendingPreviewReasons(c.bundle, current2.bundle).map((h) => ({ requirements: h.requirements, ...h.designs ? { designs: h.designs } : {}, ...h.documents ? { documents: h.documents } : {}, reason: h.reason }));
|
|
28118
|
+
const prepared = prepareSpecPreview(c.bundle, current2.bundle, c.files, current2.files, reasons, () => generatePreviewId("H"));
|
|
27542
28119
|
const blobId = (text2) => {
|
|
27543
28120
|
const bytes = Buffer.from(text2);
|
|
27544
28121
|
return createHash7(objectFormat).update(`blob ${bytes.length}\0`).update(bytes).digest("hex");
|
|
27545
28122
|
};
|
|
27546
|
-
const present = [...
|
|
28123
|
+
const present = [...current2.files.keys()];
|
|
27547
28124
|
if (present.some((p) => p.includes("\n"))) fail7(t2("commit.newlinePath"));
|
|
27548
28125
|
const stored = present.length ? (await git(["hash-object", "--stdin-paths"], void 0, Buffer.from(present.join("\n") + "\n"))).toString("utf8").split("\n") : [""];
|
|
27549
28126
|
if (stored.length !== present.length + 1) fail7(t2("commit.storedShape"));
|
|
@@ -27552,14 +28129,14 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
27552
28129
|
const finalId = (p) => writes.has(p) ? writes.get(p) === null ? void 0 : blobId(writes.get(p)) : workingIds.get(p);
|
|
27553
28130
|
const pending = [.../* @__PURE__ */ new Set([...c.files.keys(), ...present, ...writes.keys()])].filter((p) => (c.files.has(p) ? blobId(c.files.get(p)) : void 0) !== finalId(p));
|
|
27554
28131
|
if (pending.some((p) => !selected.includes(p))) fail7(t2("commit.selectPending"));
|
|
27555
|
-
const finalFiles = new Map(
|
|
28132
|
+
const finalFiles = new Map(current2.files);
|
|
27556
28133
|
for (const [path3, next] of writes) {
|
|
27557
28134
|
if (next === null) finalFiles.delete(path3);
|
|
27558
28135
|
else finalFiles.set(path3, next);
|
|
27559
28136
|
}
|
|
27560
28137
|
await checkLegacySelection(root, selected);
|
|
27561
28138
|
const stageable = selected.filter((p) => !record2(p) || finalFiles.has(p) || c.files.has(p));
|
|
27562
|
-
const known = /* @__PURE__ */ new Set([...
|
|
28139
|
+
const known = /* @__PURE__ */ new Set([...current2.specs.flatMap((s) => s.requirements.map((r) => r.id)), ...prepared.changes.filter((x) => x.kind === "requirement").map((x) => x.id)]);
|
|
27563
28140
|
if (references.some((r) => !known.has(r))) fail7(t2("commit.unknownRequirement"));
|
|
27564
28141
|
const requirements = [.../* @__PURE__ */ new Set([...references, ...prepared.changes.filter((x) => x.kind === "requirement").map((x) => x.id)])].sort();
|
|
27565
28142
|
const contextPaths = [.../* @__PURE__ */ new Set([...policyPaths(selected), ...extra])].sort();
|
|
@@ -27597,7 +28174,7 @@ async function specCommit(cwd, input2, dryRun) {
|
|
|
27597
28174
|
if (e.code === "EEXIST") fail7(t2("commit.busy", { path: busy }));
|
|
27598
28175
|
throw e;
|
|
27599
28176
|
}
|
|
27600
|
-
await previewTransaction(cwd,
|
|
28177
|
+
await previewTransaction(cwd, current2.stamp, async () => ({ writes, data: {}, recheck: c.recheck }), rename4, async (published) => {
|
|
27601
28178
|
let commitStarted = false;
|
|
27602
28179
|
try {
|
|
27603
28180
|
const lock = indexLock = await open4(indexPath + ".lock", "wx", 384);
|
|
@@ -27884,16 +28461,24 @@ async function runMigrate(options) {
|
|
|
27884
28461
|
}
|
|
27885
28462
|
|
|
27886
28463
|
// src/main.ts
|
|
27887
|
-
var
|
|
28464
|
+
var languageArgs = process.argv.slice(2).slice(0, process.argv.slice(2).indexOf("--") < 0 ? void 0 : process.argv.slice(2).indexOf("--"));
|
|
28465
|
+
var selectedLanguage;
|
|
28466
|
+
for (let i = 0; i < languageArgs.length; i++) {
|
|
28467
|
+
const arg = languageArgs[i];
|
|
28468
|
+
const value = arg === "--lang" ? languageArgs[++i] : arg.startsWith("--lang=") ? arg.slice(7) : void 0;
|
|
28469
|
+
if (value === "ko" || value === "en") selectedLanguage = value;
|
|
28470
|
+
}
|
|
28471
|
+
configureLanguage(selectedLanguage ?? environmentLanguage(), selectedLanguage);
|
|
28472
|
+
var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.7.1").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
|
|
27888
28473
|
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) => {
|
|
27889
28474
|
await runStatus(options.format);
|
|
27890
28475
|
});
|
|
27891
|
-
program2.command("browser").description(t2("help.browser")).allowExcessArguments(false).option("--port <port>", t2("help.browserPort"), parsePort, 0).option("--dev", t2("help.browserDev")).
|
|
27892
|
-
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.
|
|
28476
|
+
program2.command("browser").description(t2("help.browser")).allowExcessArguments(false).option("--port <port>", t2("help.browserPort"), parsePort, 0).option("--dev", t2("help.browserDev")).action((options) => runBrowser(options, "0.7.1"));
|
|
28477
|
+
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.7.1"));
|
|
27893
28478
|
program2.command("docs").description(t2("help.docs")).argument("[topic]", t2("help.docsTopic")).allowExcessArguments(false).action(async (topic) => {
|
|
27894
28479
|
await runDocs(topic);
|
|
27895
28480
|
});
|
|
27896
|
-
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.
|
|
28481
|
+
program2.command("update").description(t2("help.update")).allowExcessArguments(false).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).option("--commit", t2("help.updateCommit")).addOption(new Option("--check", t2("help.updateCheck")).conflicts("commit")).action((options) => options.check ? runUpdateCheck(options.format, "0.7.1") : runUpdate(options, "0.7.1"));
|
|
27897
28482
|
program2.command("migrate").description(t2("help.migrate")).allowExcessArguments(false).option("--dry-run", t2("help.migrateDryRun")).action(runMigrate);
|
|
27898
28483
|
var spec = program2.command("spec").description(t2("help.spec"));
|
|
27899
28484
|
spec.command("commit").description(t2("help.specCommit")).allowExcessArguments(false).requiredOption("--file <path>", t2("help.specCommitFile")).option("--dry-run", t2("help.specCommitDryRun")).action((o) => runSpecPreview("commit", o));
|