gitifact 0.4.3 → 0.4.4
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 +4 -2
- package/dist/browser/assets/{Markdown-GTI3nRCA.js → Markdown-Drk5rJF_.js} +1 -1
- package/dist/browser/assets/{MetadataListItem-c6t7SM9J.js → MetadataListItem-o7NlLubs.js} +1 -1
- package/dist/browser/assets/{Table-BJ_vfcZE.js → Table-DmxmodQG.js} +1 -1
- package/dist/browser/assets/{about-5hmfP7Oq.js → about-BFpb4QWu.js} +1 -1
- package/dist/browser/assets/{changelog-jpn_8GXM.js → changelog-rtqqVzIW.js} +1 -1
- package/dist/browser/assets/{contributors._email-DUoflTLI.js → contributors._email-CsIeETiA.js} +1 -1
- package/dist/browser/assets/{contributors.index-DfuU03rG.js → contributors.index-BVn-RzjO.js} +1 -1
- package/dist/browser/assets/{document-DeNuHzWM.js → document-BGXuvcs6.js} +1 -1
- package/dist/browser/assets/{features._featureId-mcSjZyhs.js → features._featureId-Bw_4HsoD.js} +1 -1
- package/dist/browser/assets/{requirements-QHPB6OmC.js → features.index-Cf-ct7tk.js} +1 -1
- package/dist/browser/assets/{git-Cj31Qjk8.js → git-E_25yRrM.js} +1 -1
- package/dist/browser/assets/{guides._documentId-BuOa1m0p.js → guides._documentId-CJIzrRob.js} +1 -1
- package/dist/browser/assets/{guides.index-BNK_skIs.js → guides.index-Bh0g2XhD.js} +1 -1
- package/dist/browser/assets/{index-CdO3EM0n.js → index-Bmk5GeZg.js} +3 -3
- package/dist/browser/assets/{page-header-CBWpQAOT.js → page-header-Cmn2UulE.js} +2 -2
- package/dist/browser/assets/{product-DZ2_YsMy.js → product-lcmaeBnU.js} +1 -1
- package/dist/browser/assets/{product.document-C5VnB7EK.js → product.document-BH-jLWyC.js} +1 -1
- package/dist/browser/assets/{product.index-DybzHICn.js → product.index-3Hnsh67g.js} +1 -1
- package/dist/browser/assets/{request-state-CtVGSFDZ.js → request-state-hT3U3nPp.js} +1 -1
- package/dist/browser/assets/{features.index-PaoXpYJM.js → requirements-C5TAa2tx.js} +1 -1
- package/dist/browser/assets/{routes-BWWTOV27.js → routes-D_2lOqSD.js} +1 -1
- package/dist/browser/assets/{settings-DN1DDBYj.js → settings-Dzak94qo.js} +1 -1
- package/dist/browser/index.html +8 -8
- package/dist/i18n/ko/block.md +23 -11
- package/dist/i18n/ko/changelog.md +9 -0
- package/dist/i18n/ko/docs/workflow.md +2 -2
- package/dist/main.js +116 -18
- package/package.json +3 -3
package/dist/i18n/ko/block.md
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
## Gitifact Guide
|
|
2
|
+
|
|
1
3
|
gitifact v{version} · {language} · 저장 규약 schemaVersion 1
|
|
4
|
+
|
|
2
5
|
CLI: 모든 명령은 `gitifact <cmd>`로 실행한다. 프로젝트 지침이 다른 실행 방법을 지정하면 그것을 따른다.
|
|
3
6
|
|
|
4
|
-
시작할
|
|
7
|
+
### 시작할 때
|
|
8
|
+
|
|
9
|
+
- `gitifact` 명령이 없으면 이 프로젝트에 참여하는 데 필요한 CLI가 설치되지 않은 것이다. 사용자에게 알리고 동의를 받아 `npm install -g gitifact@{version}`으로 설치한 뒤 진행한다. 설치하지 못하면 명세 저장·커밋을 추측으로 대신하지 않는다.
|
|
5
10
|
- `gitifact spec working`으로 제품 설명·지침·기능 명세를 읽고 git status와 기존 staging을 확인한다.
|
|
6
11
|
- 이 블록은 요약이다. 상세 형식은 `gitifact docs <topic>`으로 읽고 기억으로 채우지 않는다.
|
|
7
12
|
|
|
8
|
-
무엇을 요구사항으로 남기는가
|
|
13
|
+
### 무엇을 요구사항으로 남기는가
|
|
14
|
+
|
|
15
|
+
제품 동작과 유지할 제약만 남긴다.
|
|
16
|
+
|
|
9
17
|
| 요청 | 처리 |
|
|
10
18
|
| --- | --- |
|
|
11
19
|
| 게시물을 삭제할 수 있게 해주세요 | 요구사항으로 정리한다 |
|
|
@@ -13,7 +21,8 @@ CLI: 모든 명령은 `gitifact <cmd>`로 실행한다. 프로젝트 지침이
|
|
|
13
21
|
| 지금 푸시해주세요 | 작업 지시다. 등록하지 않는다 |
|
|
14
22
|
| 외부 서비스 없이 동작해야 합니다 | 제품 제약으로 명세에 반영한다 |
|
|
15
23
|
|
|
16
|
-
|
|
24
|
+
### 규칙
|
|
25
|
+
|
|
17
26
|
- 명세를 저장하기 전에 `gitifact docs spec`을 읽는다. ID는 CLI가 발급한 값만 쓴다.
|
|
18
27
|
- 새 기능은 requirements.md와 design.md를 함께 정리한다(`gitifact docs design`). 요구사항만 요청받으면 따른다.
|
|
19
28
|
- 커밋 요청을 받으면 `gitifact docs commit`을 읽고 명세·이유·코드·테스트를 함께 커밋한다.
|
|
@@ -23,11 +32,14 @@ CLI: 모든 명령은 `gitifact <cmd>`로 실행한다. 프로젝트 지침이
|
|
|
23
32
|
- save·commit 입력 JSON은 `spec working`이 알려 준 inputs 경로에 쓴다. 성공하면 CLI가 지운다. 조회 결과와 docs 출력은 파일로 저장하지 않고 필요할 때 다시 실행한다.
|
|
24
33
|
- 사용자가 요구사항·프로젝트 현황·변경 이력을 보여 달라고 하면 `gitifact browser`를 백그라운드로 실행하고 출력된 URL을 알려 준다. 채팅 요약으로 대신하지 않는다.
|
|
25
34
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
### 명령
|
|
36
|
+
|
|
37
|
+
- `docs <topic>`: {topics}
|
|
38
|
+
- `spec working`: 현재 명세·제품·지침 전체, stamp, 입력 파일 경로 (`--stamp`, `--feature <이름>`, `--ids`)
|
|
39
|
+
- `spec save --file <json|->`: 요구사항·설계·문서 저장
|
|
40
|
+
- `spec commit --file <json|->`: 변경 이유 기록과 커밋을 한 번에
|
|
41
|
+
- `browser`: 읽기 전용 브라우저 서버 실행, URL 출력 후 계속 실행
|
|
42
|
+
- `update [--commit]`: 새 버전 확인과 설치 안내, 이 블록을 현재 버전으로 갱신. `--commit`은 블록만 바뀐 파일을 고정 메시지로 커밋한다
|
|
43
|
+
- `init`: 처음 도입할 때 설정과 이 블록을 만든다
|
|
44
|
+
|
|
45
|
+
---
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## 0.4.4 - 2026-09-17
|
|
2
|
+
### Added
|
|
3
|
+
- `update --commit`이 블록 안만 바뀐 지침 파일을 `chore(gitifact): refresh GITIFACT block to v<버전>` 메시지로 그 파일만 커밋합니다. 다른 staging은 그대로 남고 훅·서명도 평소대로 실행됩니다. 블록 밖에도 수정이 있거나, 추적하지 않는 파일이거나, Git이 커밋을 거부하면 커밋하지 않고 이유를 알립니다.
|
|
4
|
+
- GITIFACT 블록이 `gitifact` 명령이 없는 환경을 안내합니다. 에이전트는 사용자에게 알리고 동의를 받아 블록에 적힌 버전으로 설치합니다.
|
|
5
|
+
### Changed
|
|
6
|
+
- GITIFACT 블록을 `## Gitifact Guide` 제목, 절 제목, 목록으로 정리하고 끝에 구분선을 넣었습니다. Markdown으로 볼 때 명령 목록이 한 줄로 합쳐지지 않습니다. `update` 또는 `init`을 다시 실행하면 갱신됩니다.
|
|
7
|
+
- 브라우저의 업데이트 안내 문장이 설치 후 `gitifact update --commit`을 실행하도록 안내합니다.
|
|
8
|
+
- `update` 출력 계약이 version 2가 되었습니다. 커밋 결과를 담는 `commit` 필드가 추가됐습니다.
|
|
9
|
+
|
|
1
10
|
## 0.4.3 - 2026-09-17
|
|
2
11
|
### Fixed
|
|
3
12
|
- 브라우저의 새 버전 안내 창에서 제목과 닫기 버튼이 창 가장자리에 붙어 잘려 보이던 문제를 고쳤습니다.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## 시작과 형식 확인
|
|
6
6
|
|
|
7
|
-
현재 경로·브랜치·Git 상태와 기존 staging을 확인하고 적용되는 AGENTS.md·CLAUDE.md를 원문으로 읽는다. 지침에서 지정한 CLI를 사용한다. 아래 `gitifact`는 그 실행 방법을 뜻한다. CLI가 없다면 설치·전역 설정 변경을 임의로 하지
|
|
7
|
+
현재 경로·브랜치·Git 상태와 기존 staging을 확인하고 적용되는 AGENTS.md·CLAUDE.md를 원문으로 읽는다. 지침에서 지정한 CLI를 사용한다. 아래 `gitifact`는 그 실행 방법을 뜻한다. CLI가 없다면 이 프로젝트에 참여하는 데 필요한 도구가 빠진 것이다. 설치·전역 설정 변경을 임의로 하지 않고, 사용자에게 알린 뒤 동의를 받아 블록 첫머리에 적힌 버전으로 설치한다(`npm install -g gitifact@<버전>`). 설치 전에는 가능한 조사부터 진행하고 명세 저장·커밋을 추측으로 대신하지 않는다.
|
|
8
8
|
|
|
9
9
|
설정과 실제 파일, CLI 도움말을 함께 확인해 다음 중 하나의 흐름을 선택한다. 명령이 존재한다는 사실만으로 프로젝트 사용이나 전환이 허용되지는 않는다.
|
|
10
10
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- **기존 형식:** workflow-1·prototype-1·init-1 설정은 현재 CLI가 조회·기록하지 않는다. 기존 기록을 삭제하거나 새 형식으로 가장하지 않고, 기존 기록을 읽으려면 0.4.0 이하 CLI가 필요하다고 알린다.
|
|
13
13
|
- **미도입:** 도입이 허용됐으면 Git 상태와 지침을 확인하고 `init --dry-run`, `init`으로 연결한다. Git 저장소가 없으면 Git 생성 권한을 확인한다. 기존 변경과 staging을 보존한다.
|
|
14
14
|
|
|
15
|
-
init은 `.gitifact/config.json`과 도입 기준선을 만들고, AGENTS.md 등 에이전트 지침 파일에 `<!-- GITIFACT:START -->`와 `<!-- GITIFACT:END -->` 사이의 블록을 쓴다. 마커 바깥의 내용은 건드리지 않는다. 요구사항·커밋은 만들지 않는다. 블록은 규칙의 요약이며, 상세 형식은 `gitifact docs <topic>`으로 읽는다. CLI를 업데이트한 뒤 `update`(또는 `init`)를 실행하면 블록이 갱신된다. `update`는 새 버전 여부와 설치 방법도 알려 주며 설치를 직접 실행하지는 않는다.
|
|
15
|
+
init은 `.gitifact/config.json`과 도입 기준선을 만들고, AGENTS.md 등 에이전트 지침 파일에 `<!-- GITIFACT:START -->`와 `<!-- GITIFACT:END -->` 사이의 블록을 쓴다. 마커 바깥의 내용은 건드리지 않는다. 요구사항·커밋은 만들지 않는다. 블록은 규칙의 요약이며, 상세 형식은 `gitifact docs <topic>`으로 읽는다. CLI를 업데이트한 뒤 `update`(또는 `init`)를 실행하면 블록이 갱신된다. `update`는 새 버전 여부와 설치 방법도 알려 주며 설치를 직접 실행하지는 않는다. 사용자가 업데이트를 요청하면 `update --commit`을 쓴다. 블록 안만 바뀐 지침 파일을 `chore(gitifact): refresh GITIFACT block to v<버전>` 메시지로 그 파일만 커밋하고, 다른 staging은 그대로 둔다. 블록 밖에도 수정이 있거나 추적하지 않는 파일이거나 Git이 커밋을 거부하면 커밋하지 않고 `commit.reason`으로 알린다. 이때 에이전트가 메시지를 바꿔 대신 커밋하지 않고 사용자에게 알린다.
|
|
16
16
|
|
|
17
17
|
구형 기록의 자동 마이그레이션은 아직 없다. 명시적인 전환 작업은 필요한 요구사항을 검증한 뒤 합의한 보존·제거 범위로 처리한다. init을 재실행하거나 설정을 임의 변경해 전환을 우회하지 않는다.
|
|
18
18
|
|
package/dist/main.js
CHANGED
|
@@ -24563,11 +24563,20 @@ var browserSessionV2 = external_exports.strictObject({
|
|
|
24563
24563
|
update: updateStateV1
|
|
24564
24564
|
});
|
|
24565
24565
|
|
|
24566
|
-
// ../../packages/contracts/dist/versions/update-
|
|
24567
|
-
var
|
|
24566
|
+
// ../../packages/contracts/dist/versions/update-v2.js
|
|
24567
|
+
var commitReason = external_exports.enum(["untracked", "other-changes", "commit-failed"]);
|
|
24568
|
+
var agentDocsCommit = external_exports.strictObject({
|
|
24569
|
+
state: external_exports.enum(["not-requested", "nothing", "committed", "skipped"]),
|
|
24570
|
+
commit: external_exports.string().nullable(),
|
|
24571
|
+
paths: external_exports.array(external_exports.string()),
|
|
24572
|
+
message: external_exports.string().nullable(),
|
|
24573
|
+
reason: commitReason.nullable(),
|
|
24574
|
+
detail: external_exports.string().nullable()
|
|
24575
|
+
}).refine((value) => value.state === "committed" === (value.commit !== null) && value.state === "skipped" === (value.reason !== null), "commit is set exactly when committed, reason exactly when skipped.");
|
|
24576
|
+
var updateV2 = external_exports.discriminatedUnion("ok", [
|
|
24568
24577
|
external_exports.strictObject({
|
|
24569
24578
|
contract: external_exports.literal("update"),
|
|
24570
|
-
version: external_exports.literal(
|
|
24579
|
+
version: external_exports.literal(2),
|
|
24571
24580
|
ok: external_exports.literal(true),
|
|
24572
24581
|
cliVersion: external_exports.string().min(1),
|
|
24573
24582
|
update: updateStateV1,
|
|
@@ -24575,9 +24584,10 @@ var updateV1 = external_exports.discriminatedUnion("ok", [
|
|
|
24575
24584
|
install: external_exports.strictObject({ npmGlobal: external_exports.string().min(1) }).nullable(),
|
|
24576
24585
|
// refreshed: blocks whose version line differed were rewritten. current: every block already matched.
|
|
24577
24586
|
// not-initialized: no .gitifact config here, so only the version check ran. no-block: no file carries a block.
|
|
24578
|
-
agentDocs: external_exports.strictObject({ state: external_exports.enum(["refreshed", "current", "not-initialized", "no-block"]), paths: external_exports.array(external_exports.string()) })
|
|
24587
|
+
agentDocs: external_exports.strictObject({ state: external_exports.enum(["refreshed", "current", "not-initialized", "no-block"]), paths: external_exports.array(external_exports.string()) }),
|
|
24588
|
+
commit: agentDocsCommit
|
|
24579
24589
|
}),
|
|
24580
|
-
external_exports.strictObject({ contract: external_exports.literal("update"), version: external_exports.literal(
|
|
24590
|
+
external_exports.strictObject({ contract: external_exports.literal("update"), version: external_exports.literal(2), ok: external_exports.literal(false), error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }) })
|
|
24581
24591
|
]);
|
|
24582
24592
|
|
|
24583
24593
|
// src/shared/i18n/ko/messages.json
|
|
@@ -24814,6 +24824,7 @@ var messages_default2 = {
|
|
|
24814
24824
|
"docs.summary.product": "PRODUCT.md\uC640 guides \uBB38\uC11C\uC758 \uC5ED\uD560\uACFC \uC800\uC7A5 \uBA85\uB839",
|
|
24815
24825
|
"docs.summary.commit": "spec commit \uC785\uB825, \uC774\uC720 \uAE30\uB85D, \uC2E4\uD328 \uD6C4 \uBCF5\uAD6C",
|
|
24816
24826
|
"help.browserNoUpdateCheck": "\uC0C8 \uBC84\uC804 \uD655\uC778\uC744 \uC704\uD55C npm \uB808\uC9C0\uC2A4\uD2B8\uB9AC \uC870\uD68C\uB97C \uB054 (GITIFACT_NO_UPDATE_CHECK\uC640 \uAC19\uC74C)",
|
|
24827
|
+
"help.updateCommit": "\uAC31\uC2E0\uD55C GITIFACT \uBE14\uB85D\uC774 HEAD\uC640 \uBE14\uB85D \uC548\uC5D0\uC11C\uB9CC \uB2E4\uB974\uBA74 \uADF8 \uD30C\uC77C\uB9CC \uACE0\uC815 \uBA54\uC2DC\uC9C0\uB85C \uCEE4\uBC0B",
|
|
24817
24828
|
"help.update": "\uC0C8 \uBC84\uC804 \uD655\uC778\uACFC \uC124\uCE58 \uBC29\uBC95 \uC548\uB0B4, \uD504\uB85C\uC81D\uD2B8 \uC9C0\uCE68 \uD30C\uC77C\uC758 GITIFACT \uBE14\uB85D\uC744 \uD604\uC7AC \uBC84\uC804\uC73C\uB85C \uAC31\uC2E0",
|
|
24818
24829
|
"server.badLanguage": "\uC5B8\uC5B4 \uCF54\uB4DC\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
24819
24830
|
"server.changelogNotFound": "\uD328\uCE58\uB178\uD2B8\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
@@ -24824,11 +24835,17 @@ var messages_default2 = {
|
|
|
24824
24835
|
"update.text.disabled": "\uC0C8 \uBC84\uC804 \uD655\uC778\uC774 \uAEBC\uC838 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
24825
24836
|
"update.text.unavailable": "\uC0C8 \uBC84\uC804\uC744 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uB124\uD2B8\uC6CC\uD06C \uC5F0\uACB0\uC744 \uD655\uC778\uD558\uC138\uC694.",
|
|
24826
24837
|
"update.text.install": "npm \uC804\uC5ED \uC124\uCE58\uC6A9: {command}",
|
|
24827
|
-
"update.text.afterInstall": "\uC124\uCE58\uD55C \uB4A4 gitifact update\
|
|
24838
|
+
"update.text.afterInstall": "\uC124\uCE58\uD55C \uB4A4 gitifact update --commit\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 \uC9C0\uCE68 \uBE14\uB85D\uC774 \uC0C8 \uBC84\uC804\uC73C\uB85C \uAC31\uC2E0\uB418\uACE0 \uCEE4\uBC0B\uB429\uB2C8\uB2E4.",
|
|
24828
24839
|
"update.text.blockRefreshed": "\uC9C0\uCE68 \uBE14\uB85D \uAC31\uC2E0: {paths}",
|
|
24829
24840
|
"update.text.blockCurrent": "\uC9C0\uCE68 \uBE14\uB85D\uC740 \uC774\uBBF8 \uD604\uC7AC \uBC84\uC804\uC785\uB2C8\uB2E4: {paths}",
|
|
24830
24841
|
"update.text.noBlock": "GITIFACT \uBE14\uB85D\uC774 \uC788\uB294 \uC9C0\uCE68 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. gitifact init\uC73C\uB85C \uC124\uCE58\uD558\uC138\uC694.",
|
|
24831
24842
|
"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.",
|
|
24843
|
+
"update.text.committed": "\uC9C0\uCE68 \uBE14\uB85D \uCEE4\uBC0B: {commit} ({paths})",
|
|
24844
|
+
"update.text.commitNothing": "\uCEE4\uBC0B\uD560 \uC9C0\uCE68 \uBE14\uB85D \uBCC0\uACBD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
24845
|
+
"update.text.commitSkipped.untracked": "\uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. Git\uC774 \uCD94\uC801\uD558\uC9C0 \uC54A\uB294 \uC9C0\uCE68 \uD30C\uC77C\uC785\uB2C8\uB2E4: {paths}",
|
|
24846
|
+
"update.text.commitSkipped.other-changes": "\uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uBE14\uB85D \uBC16\uC5D0\uB3C4 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC740 \uC218\uC815\uC774 \uC788\uC2B5\uB2C8\uB2E4: {paths}",
|
|
24847
|
+
"update.text.commitSkipped.commit-failed": "\uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. git commit\uC774 \uAC70\uBD80\uB410\uACE0 \uAC31\uC2E0\uD55C \uD30C\uC77C\uC740 \uADF8\uB300\uB85C \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4: {paths}",
|
|
24848
|
+
"update.commitResultUnknown": "\uCEE4\uBC0B \uACB0\uACFC\uB97C \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. HEAD {commit}\uC758 \uBD80\uBAA8\uC640 \uBCC0\uACBD \uD30C\uC77C\uC744 \uC9C1\uC811 \uD655\uC778\uD558\uC138\uC694.",
|
|
24832
24849
|
"update.failed": "\uC5C5\uB370\uC774\uD2B8 \uD655\uC778\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4."
|
|
24833
24850
|
};
|
|
24834
24851
|
|
|
@@ -26204,7 +26221,39 @@ ${t2("init.text.agentDocs")}: ${docs}
|
|
|
26204
26221
|
}
|
|
26205
26222
|
}
|
|
26206
26223
|
|
|
26224
|
+
// src/adapters/git/agent-docs-commit.ts
|
|
26225
|
+
function agentDocsGit(root, inherited = process.env) {
|
|
26226
|
+
const env = { ...inherited, GIT_OPTIONAL_LOCKS: "0", GIT_NO_LAZY_FETCH: "1", GIT_TERMINAL_PROMPT: "0", GIT_LITERAL_PATHSPECS: "1", LC_ALL: "C" };
|
|
26227
|
+
const runner = createGitRunner();
|
|
26228
|
+
const git = (args, acceptedExitCodes = [0]) => runner(
|
|
26229
|
+
["-c", "core.fsmonitor=false", ...args],
|
|
26230
|
+
{ cwd: root, env, timeoutMs: 12e4, maxBytes: 16 * 1024 * 1024, acceptedExitCodes }
|
|
26231
|
+
);
|
|
26232
|
+
return {
|
|
26233
|
+
async head() {
|
|
26234
|
+
return (await git(["rev-parse", "-q", "--verify", "HEAD^{commit}"], [0, 1])).toString("utf8").trim() || null;
|
|
26235
|
+
},
|
|
26236
|
+
// The committed text of a path, or null when HEAD does not track it.
|
|
26237
|
+
async committed(head, path3) {
|
|
26238
|
+
if ((await git(["ls-tree", "-z", head, "--", path3])).length === 0) return null;
|
|
26239
|
+
return (await git(["cat-file", "blob", head + ":" + path3])).toString("utf8");
|
|
26240
|
+
},
|
|
26241
|
+
// `--only` commits the working content of these paths and leaves every other staged entry staged. Hooks and signing stay on.
|
|
26242
|
+
async commitOnly(paths2, message) {
|
|
26243
|
+
await git(["commit", "--only", "-m", message, "--", ...paths2]);
|
|
26244
|
+
},
|
|
26245
|
+
async changedPaths(commit) {
|
|
26246
|
+
return (await git(["diff-tree", "--no-commit-id", "--name-only", "--no-renames", "-r", "-z", "--root", commit])).toString("utf8").split("\0").filter(Boolean).sort();
|
|
26247
|
+
},
|
|
26248
|
+
async parents(commit) {
|
|
26249
|
+
return (await git(["rev-list", "--parents", "-n", "1", commit])).toString("utf8").trim().split(" ").slice(1);
|
|
26250
|
+
}
|
|
26251
|
+
};
|
|
26252
|
+
}
|
|
26253
|
+
|
|
26207
26254
|
// src/commands/update.ts
|
|
26255
|
+
var blockCommitMessage = (version2) => "chore(gitifact): refresh GITIFACT block to v" + version2;
|
|
26256
|
+
var commitState = (state, extra = {}) => ({ state, commit: null, paths: [], message: null, reason: null, detail: null, ...extra });
|
|
26208
26257
|
async function refreshBlocks(cwd, version2, env, controls) {
|
|
26209
26258
|
const repo = initRepository(cwd, env);
|
|
26210
26259
|
let first;
|
|
@@ -26219,31 +26268,72 @@ async function refreshBlocks(cwd, version2, env, controls) {
|
|
|
26219
26268
|
if (config2 === void 0) return { state: "not-initialized", paths: [] };
|
|
26220
26269
|
const root = first.state.repository.rootPath;
|
|
26221
26270
|
const plan = await planAgentDocs(root, { version: version2, onlyExisting: true, ...controls });
|
|
26222
|
-
if (plan.paths.length === 0) return { state: "no-block", paths: [] };
|
|
26223
|
-
if (plan.writes.length === 0) return { state: "current", paths: plan.paths };
|
|
26271
|
+
if (plan.paths.length === 0) return { state: "no-block", paths: [], root };
|
|
26272
|
+
if (plan.writes.length === 0) return { state: "current", paths: plan.paths, root, candidates: plan.paths };
|
|
26224
26273
|
const unchanged = async () => {
|
|
26225
26274
|
if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !== config2) throw new InitError("INPUT_CHANGED", t2("init.inputChanged"));
|
|
26226
26275
|
};
|
|
26227
|
-
|
|
26276
|
+
const applied = await applyAgentDocs(root, plan, unchanged);
|
|
26277
|
+
return { state: "refreshed", paths: applied, root, candidates: plan.paths };
|
|
26278
|
+
}
|
|
26279
|
+
function outsideBlock(text2) {
|
|
26280
|
+
const normalized2 = text2.replace(/\r\n/g, "\n");
|
|
26281
|
+
try {
|
|
26282
|
+
const found = findBlock(normalized2);
|
|
26283
|
+
return found ? normalized2.slice(0, found.start) + AGENT_START + normalized2.slice(found.end) : null;
|
|
26284
|
+
} catch {
|
|
26285
|
+
return null;
|
|
26286
|
+
}
|
|
26287
|
+
}
|
|
26288
|
+
async function commitBlocks(docs, version2, env) {
|
|
26289
|
+
if (!docs.root || !docs.candidates?.length) return commitState("nothing");
|
|
26290
|
+
const git = agentDocsGit(docs.root, env);
|
|
26291
|
+
const head = await git.head();
|
|
26292
|
+
const changed = [];
|
|
26293
|
+
for (const path3 of docs.candidates) {
|
|
26294
|
+
const working = await managedRead(docs.root, path3);
|
|
26295
|
+
const committed = head ? await git.committed(head, path3) : null;
|
|
26296
|
+
if (working === null) continue;
|
|
26297
|
+
if (committed === null) return commitState("skipped", { paths: [path3], reason: "untracked" });
|
|
26298
|
+
if (committed.replace(/\r\n/g, "\n") === working.replace(/\r\n/g, "\n")) continue;
|
|
26299
|
+
const before = outsideBlock(committed);
|
|
26300
|
+
if (before === null || before !== outsideBlock(working)) return commitState("skipped", { paths: [path3], reason: "other-changes" });
|
|
26301
|
+
changed.push(path3);
|
|
26302
|
+
}
|
|
26303
|
+
if (changed.length === 0) return commitState("nothing");
|
|
26304
|
+
const message = blockCommitMessage(version2);
|
|
26305
|
+
let failure2;
|
|
26306
|
+
try {
|
|
26307
|
+
await git.commitOnly(changed, message);
|
|
26308
|
+
} catch (error62) {
|
|
26309
|
+
failure2 = error62;
|
|
26310
|
+
}
|
|
26311
|
+
const after = await git.head();
|
|
26312
|
+
if (!after || after === head) return commitState("skipped", { paths: changed, reason: "commit-failed", detail: failure2 instanceof Error ? failure2.message : null });
|
|
26313
|
+
const parents = await git.parents(after);
|
|
26314
|
+
if (parents.length !== 1 || parents[0] !== head) throw new InitError("COMMIT_RESULT_UNKNOWN", t2("update.commitResultUnknown", { commit: after }));
|
|
26315
|
+
return commitState("committed", { commit: after, paths: await git.changedPaths(after), message });
|
|
26228
26316
|
}
|
|
26229
26317
|
async function updateCommand(cwd, version2, env = process.env, controls = {}) {
|
|
26230
26318
|
const [update, agentDocs] = await Promise.all([
|
|
26231
26319
|
updateCheckDisabled(env) ? disabledUpdate : resolveUpdate(version2, controls.fetchLatest ?? fetchLatestVersion, void 0, controls.timeoutMs),
|
|
26232
26320
|
refreshBlocks(cwd, version2, env, controls)
|
|
26233
26321
|
]);
|
|
26234
|
-
|
|
26322
|
+
const commit = controls.commit ? await commitBlocks(agentDocs, version2, env) : commitState("not-requested");
|
|
26323
|
+
return updateV2.parse({
|
|
26235
26324
|
contract: "update",
|
|
26236
|
-
version:
|
|
26325
|
+
version: 2,
|
|
26237
26326
|
ok: true,
|
|
26238
26327
|
cliVersion: version2,
|
|
26239
26328
|
update,
|
|
26240
26329
|
install: update.status === "available" ? { npmGlobal: npmGlobalInstall(update.latestVersion) } : null,
|
|
26241
|
-
agentDocs
|
|
26330
|
+
agentDocs: { state: agentDocs.state, paths: agentDocs.paths },
|
|
26331
|
+
commit
|
|
26242
26332
|
});
|
|
26243
26333
|
}
|
|
26244
26334
|
async function runUpdate(options, version2) {
|
|
26245
26335
|
try {
|
|
26246
|
-
const dto = await updateCommand(process.cwd(), version2);
|
|
26336
|
+
const dto = await updateCommand(process.cwd(), version2, process.env, { commit: !!options.commit });
|
|
26247
26337
|
if (!dto.ok) throw new Error("unreachable");
|
|
26248
26338
|
if (options.format !== "text") {
|
|
26249
26339
|
process.stdout.write(JSON.stringify(dto) + "\n");
|
|
@@ -26253,11 +26343,19 @@ async function runUpdate(options, version2) {
|
|
|
26253
26343
|
lines.push(dto.update.status === "available" ? t2("update.text.available", { version: dto.update.latestVersion }) : dto.update.status === "up-to-date" ? t2("update.text.upToDate") : dto.update.status === "disabled" ? t2("update.text.disabled") : t2("update.text.unavailable"));
|
|
26254
26344
|
if (dto.install) lines.push(t2("update.text.install", { command: dto.install.npmGlobal }), t2("update.text.afterInstall"));
|
|
26255
26345
|
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"));
|
|
26346
|
+
const { commit } = dto;
|
|
26347
|
+
if (commit.state === "committed") lines.push(t2("update.text.committed", { commit: commit.commit.slice(0, 12), paths: commit.paths.join(", ") }));
|
|
26348
|
+
else if (commit.state === "nothing") lines.push(t2("update.text.commitNothing"));
|
|
26349
|
+
else if (commit.state === "skipped") {
|
|
26350
|
+
const paths2 = commit.paths.join(", ");
|
|
26351
|
+
const text2 = commit.reason === "untracked" ? t2("update.text.commitSkipped.untracked", { paths: paths2 }) : commit.reason === "other-changes" ? t2("update.text.commitSkipped.other-changes", { paths: paths2 }) : t2("update.text.commitSkipped.commit-failed", { paths: paths2 });
|
|
26352
|
+
lines.push(text2 + (commit.detail ? " (" + commit.detail + ")" : ""));
|
|
26353
|
+
}
|
|
26256
26354
|
process.stdout.write(lines.join("\n") + "\n");
|
|
26257
26355
|
} catch (error62) {
|
|
26258
26356
|
const known = error62 instanceof InitError || error62 instanceof RepositoryReadError;
|
|
26259
26357
|
const failure2 = { code: known ? error62.code : "UPDATE_FAILED", message: known ? error62.message : t2("update.failed") };
|
|
26260
|
-
process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(
|
|
26358
|
+
process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(updateV2.parse({ contract: "update", version: 2, ok: false, error: failure2 })) + "\n");
|
|
26261
26359
|
process.exitCode = 1;
|
|
26262
26360
|
}
|
|
26263
26361
|
}
|
|
@@ -27039,16 +27137,16 @@ async function runMigrate(options) {
|
|
|
27039
27137
|
}
|
|
27040
27138
|
|
|
27041
27139
|
// src/main.ts
|
|
27042
|
-
var program2 = new Command().name("gitifact").description(t2("help.program")).version("0.4.
|
|
27140
|
+
var program2 = new Command().name("gitifact").description(t2("help.program")).version("0.4.4").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
|
|
27043
27141
|
program2.command("status").description(t2("help.status")).allowExcessArguments(false).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).action(async (options) => {
|
|
27044
27142
|
await runStatus(options.format);
|
|
27045
27143
|
});
|
|
27046
|
-
program2.command("browser").description(t2("help.browser")).allowExcessArguments(false).option("--port <port>", t2("help.browserPort"), parsePort, 0).option("--dev", t2("help.browserDev")).option("--no-update-check", t2("help.browserNoUpdateCheck")).action((options) => runBrowser(options, "0.4.
|
|
27047
|
-
program2.command("init").description(t2("help.init")).allowExcessArguments(false).option("--dry-run", t2("help.initDryRun")).addOption(new Option("--agent <agent>", t2("help.initAgent")).choices([...agentPresetNames])).addOption(new Option("--remove-agents", t2("help.initRemoveAgents")).conflicts("skipAgents")).option("--skip-agents", t2("help.initSkipAgents")).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).action((options) => runInit(options, "0.4.
|
|
27144
|
+
program2.command("browser").description(t2("help.browser")).allowExcessArguments(false).option("--port <port>", t2("help.browserPort"), parsePort, 0).option("--dev", t2("help.browserDev")).option("--no-update-check", t2("help.browserNoUpdateCheck")).action((options) => runBrowser(options, "0.4.4"));
|
|
27145
|
+
program2.command("init").description(t2("help.init")).allowExcessArguments(false).option("--dry-run", t2("help.initDryRun")).addOption(new Option("--agent <agent>", t2("help.initAgent")).choices([...agentPresetNames])).addOption(new Option("--remove-agents", t2("help.initRemoveAgents")).conflicts("skipAgents")).option("--skip-agents", t2("help.initSkipAgents")).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).action((options) => runInit(options, "0.4.4"));
|
|
27048
27146
|
program2.command("docs").description(t2("help.docs")).argument("[topic]", t2("help.docsTopic")).allowExcessArguments(false).action(async (topic) => {
|
|
27049
27147
|
await runDocs(topic);
|
|
27050
27148
|
});
|
|
27051
|
-
program2.command("update").description(t2("help.update")).allowExcessArguments(false).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).action((options) => runUpdate(options, "0.4.
|
|
27149
|
+
program2.command("update").description(t2("help.update")).allowExcessArguments(false).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).option("--commit", t2("help.updateCommit")).action((options) => runUpdate(options, "0.4.4"));
|
|
27052
27150
|
program2.command("migrate").description(t2("help.migrate")).allowExcessArguments(false).option("--dry-run", t2("help.migrateDryRun")).action(runMigrate);
|
|
27053
27151
|
var spec = program2.command("spec").description(t2("help.spec"));
|
|
27054
27152
|
var replaced = t2("help.deprecated");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitifact",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Git-backed requirements and change history for coding agents.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"commander": "15.0.0",
|
|
38
38
|
"esbuild": "0.28.2",
|
|
39
|
-
"@gitifact/
|
|
40
|
-
"@gitifact/
|
|
39
|
+
"@gitifact/core": "0.0.0",
|
|
40
|
+
"@gitifact/contracts": "0.0.0"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "node scripts/build.mjs",
|