deuk-agent-flow 4.0.20 → 4.0.35
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/CHANGELOG.ko.md +35 -3
- package/CHANGELOG.md +32 -2
- package/README.ko.md +22 -7
- package/README.md +40 -8
- package/bin/deuk-agent-flow.js +7 -3
- package/core-rules/AGENTS.md +26 -49
- package/docs/badges/npm-downloads.json +1 -25
- package/docs/how-it-works.ko.md +1 -1
- package/docs/how-it-works.md +1 -1
- package/package.json +5 -6
- package/scripts/cli-args.mjs +4 -0
- package/scripts/cli-init-commands.mjs +34 -88
- package/scripts/cli-prompts.mjs +26 -9
- package/scripts/cli-ticket-commands.mjs +50 -16
- package/scripts/cli-utils.mjs +25 -10
- package/scripts/cli.mjs +10 -1
- package/scripts/lint-md.mjs +19 -18
- package/scripts/lint-rules.mjs +10 -0
- package/scripts/smoke-npm-docker.mjs +1 -1
- package/scripts/update-download-badge.mjs +3 -7
package/CHANGELOG.ko.md
CHANGED
|
@@ -8,12 +8,44 @@
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
### 수정됨 (Fixed)
|
|
12
|
+
|
|
13
|
+
- **cli:** 티켓 탐색이 상위 워크스페이스를 상속하지 않고 현재 agent-rule 경계에서 멈추도록 수정했습니다.
|
|
14
|
+
|
|
15
|
+
## [4.0.35] - 2026-05-09
|
|
16
|
+
|
|
17
|
+
### 수정됨 (Fixed)
|
|
18
|
+
|
|
19
|
+
- **release:** 공개 패키지의 publish 스크립트가 소스 전용 테스트는 건너뛰고 npm smoke 검증은 유지하도록 수정했습니다.
|
|
20
|
+
- **release:** 공개 커밋 제목이 `sync` 같은 전달 단계가 아니라 공개되는 feature/fix/docs/release 변경 자체를 설명하도록 명확히 했습니다.
|
|
21
|
+
- **release:** 공개 export 실행 시에도 같은 public commit message 가이드를 출력해 공개 기록이 제품 변경 중심으로 남도록 했습니다.
|
|
22
|
+
|
|
23
|
+
## [4.0.34] - 2026-05-09
|
|
24
|
+
|
|
25
|
+
### 변경됨 (Changed)
|
|
26
|
+
|
|
27
|
+
- **init:** 첫 실행 질문을 workspace 종류, 기술 표면, AI client pointer, 선택형 Deuk AgentContext MCP memory 기준으로 재구성해 코딩 전용이 아닌 기획/시스템/연구/혼합 workspace도 자연스럽게 설정할 수 있게 했습니다.
|
|
28
|
+
- **docs:** 사용자용 업데이트 안내를 `npm install -g deuk-agent-flow` 이후 `deuk-agent-flow init`만 실행하는 흐름으로 단순화하고, repo 루트와 workspace 루트 갱신 방식을 명확히 했습니다.
|
|
29
|
+
|
|
30
|
+
### 수정됨 (Fixed)
|
|
31
|
+
|
|
32
|
+
- **templates:** 패키지 `templates/`를 runtime 단일 진실 공급원으로 삼고, init/merge 중 legacy `.deuk-agent/templates` 복사본을 제거하도록 정리했습니다.
|
|
33
|
+
- **ticket:** `ticket create` strict 검증 전에 Phase 1 heading level 실수를 canonical heading으로 정규화하도록 수정했습니다.
|
|
34
|
+
- **release:** 공개 export 대상을 runtime 파일로 좁히고 오래된 tarball, `bundle/`, `node_modules/`, 내부 script, test 같은 공개 트리 찌꺼기를 제거하도록 했습니다.
|
|
35
|
+
|
|
36
|
+
## [4.0.21] - 2026-05-08
|
|
37
|
+
|
|
38
|
+
### 수정됨 (Fixed)
|
|
39
|
+
|
|
40
|
+
- **docs:** Shields가 허용하는 형식으로 커스텀 다운로드 배지 JSON을 복구하고, 영문/한글 README 상단의 통합 `deuk-flow` 다운로드 배지를 다시 노출했습니다.
|
|
41
|
+
- **release:** `docs/badges/npm-downloads.json`를 공개 미러에도 동기화하고 npm 공개 패키지 표면에서 내부 전용 payload 유입을 제거했습니다.
|
|
42
|
+
|
|
11
43
|
## [4.0.20] - 2026-05-08
|
|
12
44
|
|
|
13
45
|
### 수정됨 (Fixed)
|
|
14
46
|
|
|
15
47
|
- **docs:** 영문/한글 README 상단에 통합 npm 다운로드 배지를 복구하고, 공개 표기는 `deuk-flow` 라벨로 유지했습니다.
|
|
16
|
-
- **release:** `docs/badges/`를 공개
|
|
48
|
+
- **release:** `docs/badges/`를 공개 릴리스 트리에도 복사하도록 보강해 README 다운로드 배지가 public export와 patch 재배포 뒤에도 유지되게 했습니다.
|
|
17
49
|
|
|
18
50
|
## [4.0.12] - 2026-05-07
|
|
19
51
|
|
|
@@ -34,7 +66,7 @@
|
|
|
34
66
|
### 수정됨 (Fixed)
|
|
35
67
|
|
|
36
68
|
- npm과 GitHub 첫 화면에서 영어/한국어 README를 바로 오갈 수 있도록 언어 전환 링크를 복구했습니다.
|
|
37
|
-
- README 문서 표에는 공개 문서 링크만 남기고, 내부 리서치와 성장 전략 문서는 npm/
|
|
69
|
+
- README 문서 표에는 공개 문서 링크만 남기고, 내부 리서치와 성장 전략 문서는 npm/Public 공개 표면에서 제외했습니다.
|
|
38
70
|
|
|
39
71
|
## [3.3.2] - 2026-05-06
|
|
40
72
|
|
|
@@ -156,7 +188,7 @@
|
|
|
156
188
|
### 수정됨 (Fixed)
|
|
157
189
|
|
|
158
190
|
- **rules:** 전역 `npx` 캐시 이슈(과거 버전 실행 문제)를 우회하기 위해 로컬 최신 스크립트 호출을 강제/권장하도록 수정
|
|
159
|
-
- **scripts:**
|
|
191
|
+
- **scripts:** Public 미러 저장소 동기화 시 잘못 표기되던 URL 예시 로그 메시지 정정
|
|
160
192
|
|
|
161
193
|
### 변경됨 (Changed)
|
|
162
194
|
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
**한국어:** [CHANGELOG.ko.md](CHANGELOG.ko.md)
|
|
6
|
+
|
|
5
7
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
8
|
|
|
7
9
|
## [Unreleased]
|
|
@@ -10,12 +12,40 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
10
12
|
|
|
11
13
|
- **cli:** stop ticket discovery at the current agent-rule boundary instead of inheriting the parent workspace.
|
|
12
14
|
|
|
15
|
+
## [4.0.35] - 2026-05-09
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **release:** make public package publish scripts skip source-only tests while still running the npm smoke check.
|
|
20
|
+
- **release:** require public commit subjects to describe the released feature, fix, docs, or release change instead of using `sync` as the main label.
|
|
21
|
+
- **release:** print the same public commit-message guidance from the export script so public history records product changes rather than transport mechanics.
|
|
22
|
+
|
|
23
|
+
## [4.0.34] - 2026-05-09
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **init:** redesigned first-run prompts around workspace kind, technical surface, AI client pointers, and optional Deuk AgentContext MCP memory so non-coding and mixed workspaces are not forced into a coding-only setup.
|
|
28
|
+
- **docs:** simplified user-facing update instructions to `npm install -g deuk-agent-flow` followed by `deuk-agent-flow init`, with repo-root and workspace-root refresh guidance.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- **templates:** made package `templates/` the runtime SSoT and removed legacy `.deuk-agent/templates` copies during init/merge.
|
|
33
|
+
- **ticket:** normalized common Phase 1 heading-level mistakes during `ticket create` before strict template validation.
|
|
34
|
+
- **release:** narrowed public export to runtime files and cleaned stale release-tree artifacts such as old tarballs, `bundle/`, `node_modules/`, internal scripts, and tests.
|
|
35
|
+
|
|
36
|
+
## [4.0.21] - 2026-05-08
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- **docs:** repaired the custom downloads badge endpoint payload so Shields accepts it, and restored the combined `deuk-flow` downloads badge at the top of the English and Korean README surfaces.
|
|
41
|
+
- **release:** copied `docs/badges/npm-downloads.json` into the public release tree and removed internal-only package payload leakage from the npm release surface.
|
|
42
|
+
|
|
13
43
|
## [4.0.20] - 2026-05-08
|
|
14
44
|
|
|
15
45
|
### Fixed
|
|
16
46
|
|
|
17
47
|
- **docs:** restored the combined npm downloads badge to the top of the English and Korean README surfaces while keeping the public `deuk-flow` label.
|
|
18
|
-
- **release:**
|
|
48
|
+
- **release:** copied `docs/badges/` into the public release tree so the README downloads badge survives public export and patch republish flow.
|
|
19
49
|
|
|
20
50
|
## [4.0.12] - 2026-05-07
|
|
21
51
|
|
|
@@ -36,7 +66,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
36
66
|
### Fixed
|
|
37
67
|
|
|
38
68
|
- Restored the README language switch links so npm and GitHub readers can move between English and Korean documentation from the first screen.
|
|
39
|
-
- Published only public documentation links in the README tables while keeping internal research and growth notes out of the npm/
|
|
69
|
+
- Published only public documentation links in the README tables while keeping internal research and growth notes out of the npm/Public surface.
|
|
40
70
|
|
|
41
71
|
## [3.3.2] - 2026-05-06
|
|
42
72
|
|
package/README.ko.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<br />
|
|
3
3
|
<img src="docs/assets/architecture-v3.png" width="800" alt="DeukAgentFlow Architecture" />
|
|
4
4
|
<br />
|
|
5
|
-
<h1>Deuk Agent Flow v4.0.
|
|
5
|
+
<h1>Deuk Agent Flow v4.0.34</h1>
|
|
6
6
|
<p>
|
|
7
7
|
<a href="https://www.npmjs.com/package/deuk-agent-flow"><img src="https://img.shields.io/npm/v/deuk-agent-flow.svg?label=deuk-flow" alt="deuk-flow npm version" /></a>
|
|
8
8
|
<a href="https://www.npmjs.com/package/deuk-agent-flow"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fjoygram%2FDeukAgentFlow%2Fmaster%2Fdocs%2Fbadges%2Fnpm-downloads.json" alt="deuk-flow combined npm downloads" /></a>
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
**Deuk Agent Flow**는 AI
|
|
18
|
+
**Deuk Agent Flow**는 AI가 참여하는 작업 공간을 위한 레포 소유 워크플로우 계층입니다. 기획, 소프트웨어 엔지니어링, 시스템 운영, 리서치 같은 다양한 환경을 Codex, Copilot, Cursor, Claude Code, Gemini, Windsurf, 그리고 다음에 쓰게 될 에이전트까지 같은 티켓 흐름으로 묶습니다.
|
|
19
19
|
|
|
20
20
|
대부분의 에이전트 설정은 "지침"에서 멈춥니다. Deuk Agent Flow는 짧은 대화를 작업 루프로 바꿉니다: 티켓, 범위, 실행, 검증, 보관. 긴 명령을 외우지 않아도 `AGENTS.md`, Copilot instructions, Cursor rules, Claude skills 같은 표면을 같은 흐름으로 묶습니다.
|
|
21
21
|
|
|
@@ -66,7 +66,7 @@ Deuk Agent Flow
|
|
|
66
66
|
| 팀 기억 강화 | 완료된 작업이 검색 가능한 프로젝트 히스토리가 됨 |
|
|
67
67
|
|
|
68
68
|
> **현재 배포 기준:**
|
|
69
|
-
> v4.0.0은 에이전트 기반 리포지토리에 배포해 사용할 수 있는 상태입니다. 현재는 **OpenAI Codex**와 **GitHub Copilot** 환경에서 가장 안정적으로 동작합니다. Cursor, Windsurf, Claude Code
|
|
69
|
+
> v4.0.0은 에이전트 기반 리포지토리에 배포해 사용할 수 있는 상태입니다. 현재는 **OpenAI Codex**와 **GitHub Copilot** 환경에서 가장 안정적으로 동작합니다. Cursor, Windsurf, Claude Code도 포인터 구조로 지원하지만, 워크스페이스별 검증을 권장합니다. Deuk AgentContext MCP는 선택형 기억 계층이며, MCP 서버 등록은 `init`에 딸려 들어가지 않고 별도로 설정합니다.
|
|
70
70
|
> **아키텍처 기반:**
|
|
71
71
|
> 거대하고 무거운 레거시 `.cursorrules` 방식을 공식적으로 폐기했습니다. v3.0은 `AGENTS.md`를 단일 진실 공급원으로 사용하는 **Hub-Spoke 모델**을 도입하여, IDE별 규칙은 얇은 진입점 포인터 역할만 수행합니다.
|
|
72
72
|
|
|
@@ -140,7 +140,7 @@ deuk-agent-flow init
|
|
|
140
140
|
|
|
141
141
|
이후 일상 작업은 명령을 직접 치기보다 에이전트에게 짧게 말합니다. 예: "진행", "다음", "원인 다시 파악".
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
단일 저장소라면 해당 저장소 루트에서 `deuk-agent-flow init`을 실행합니다. 여러 DeukAgentFlow 프로젝트를 포함한 루트 워크스페이스라면 그 워크스페이스 루트에서 같은 명령을 실행합니다. `init`은 루트 포인터와 자체 `PROJECT_RULE.md` / `.deuk-agent/` 상태를 가진 하위 워크스페이스를 함께 갱신합니다.
|
|
144
144
|
|
|
145
145
|
이렇게 사용하면 효과가 극대화됩니다. workspace 루트는 공통 진입점, 각 프로젝트 루트는 독립 티켓/규칙/검증 단위로 나누고, 중첩 서버나 앱은 필요할 때 별도 프로젝트로 초기화하세요.
|
|
146
146
|
|
|
@@ -156,13 +156,28 @@ npm install -g deuk-agent-flow
|
|
|
156
156
|
deuk-agent-flow init
|
|
157
157
|
```
|
|
158
158
|
|
|
159
|
+
기존 저장소에 새 버전을 반영할 때는 먼저 글로벌 패키지를 업데이트한 뒤 init을 다시 실행합니다.
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npm install -g deuk-agent-flow
|
|
163
|
+
deuk-agent-flow init
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`init`은 현재 설치된 패키지의 규칙을 다시 적용하고 legacy/runtime template copy를 제거합니다. 글로벌 npm 패키지 업데이트 자체를 자동으로 수행하지는 않습니다.
|
|
167
|
+
|
|
168
|
+
단일 저장소라면 해당 저장소 루트에서 `deuk-agent-flow init`을 실행합니다.
|
|
169
|
+
|
|
170
|
+
여러 DeukAgentFlow 프로젝트를 포함한 루트 워크스페이스라면 그 워크스페이스 루트에서 같은 명령을 실행합니다. `init`은 루트 포인터와 자체 `PROJECT_RULE.md` / `.deuk-agent/` 상태를 가진 하위 워크스페이스를 함께 갱신하므로, 일반 사용자도 새 패키지 버전을 설치한 뒤 자신의 개인 워크스페이스 루트에서 AI agent rule을 갱신할 수 있습니다.
|
|
171
|
+
|
|
172
|
+
사용하는 AI client 선택은 한 번으로 고정되지 않습니다. 나중에 다른 client를 쓰게 되면 `deuk-agent-flow init`을 다시 실행하고 추가할 AI client를 선택하면 됩니다.
|
|
173
|
+
|
|
159
174
|
### 2. 로컬 소스 개발 (메인테이너/파워 유저)
|
|
160
|
-
|
|
175
|
+
글로벌 명령은 기본적으로 설치된 패키지를 실행합니다. 다른 프로젝트 디렉터리에서 로컬 checkout 소스를 실행해야 하는 개발자는 명시적으로 로컬 소스 라우팅을 켜야 합니다.
|
|
161
176
|
|
|
162
177
|
```bash
|
|
163
178
|
cd ~/workspace/DeukAgentFlow
|
|
164
179
|
sudo npm link
|
|
165
|
-
deuk-agent-flow init #
|
|
180
|
+
DEUK_AGENT_FLOW_USE_LOCAL=1 deuk-agent-flow init # 로컬 scripts/cli.mjs로 라우팅됨
|
|
166
181
|
```
|
|
167
182
|
|
|
168
183
|
Codex나 Copilot을 주로 사용한다면 이 구성이 일상 운영에 가장 적합합니다. 현재는 이 두 환경에서 Hub-Spoke와 티켓 기반 워크플로우가 가장 부드럽게 동작합니다.
|
|
@@ -207,7 +222,7 @@ npm run badge:downloads
|
|
|
207
222
|
|
|
208
223
|
워크플로우는 **티켓 기반 실행 계약(Ticket-Driven Execution Contract)**에 의해 통제됩니다.
|
|
209
224
|
|
|
210
|
-
1. **스캐폴딩 (Scaffolding)**: `init` 명령어가
|
|
225
|
+
1. **스캐폴딩 (Scaffolding)**: `init` 명령어가 `AGENTS.md`와 `PROJECT_RULE.md` 같은 로컬 포인터를 배치합니다. 런타임 템플릿은 `.deuk-agent/templates/`가 아니라 패키지의 `templates/`를 단일 진실 공급원으로 사용합니다.
|
|
211
226
|
2. **티켓팅 (Plan Phase)**: 사용자가 짧게 지시하면 에이전트가 맥락을 읽고 내부 작업 지시서를 생성합니다. 이때 에이전트는 Plan Mode로 동작하며 코드를 수정할 수 없고 계획 수립에만 집중합니다.
|
|
212
227
|
3. **실행 (Execute Phase)**: 사용자의 승인을 받은 후, 에이전트는 **타겟 서브모듈**에 고정되어 실질적인 코드 작성을 수행합니다. MCP Soft Gate가 인가되지 않은 파일의 수정을 감시합니다.
|
|
213
228
|
4. **검증 (Verify Phase)**: 개발 작업 종료 전 사이드 이펙트 감사(Audit) 및 컨벤션(DC-DUP 등 아키텍처 규칙) 체크를 수행합니다.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<br />
|
|
3
3
|
<img src="docs/assets/architecture-v3.png" width="800" alt="DeukAgentFlow Architecture" />
|
|
4
4
|
<br />
|
|
5
|
-
<h1>Deuk Agent Flow v4.0.
|
|
5
|
+
<h1>Deuk Agent Flow v4.0.34</h1>
|
|
6
6
|
<p>
|
|
7
7
|
<a href="https://www.npmjs.com/package/deuk-agent-flow"><img src="https://img.shields.io/npm/v/deuk-agent-flow.svg?label=deuk-flow" alt="deuk-flow npm version" /></a>
|
|
8
8
|
<a href="https://www.npmjs.com/package/deuk-agent-flow"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fjoygram%2FDeukAgentFlow%2Fmaster%2Fdocs%2Fbadges%2Fnpm-downloads.json" alt="deuk-flow combined npm downloads" /></a>
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
**Deuk Agent Flow** is the repo-owned workflow layer for AI
|
|
18
|
+
**Deuk Agent Flow** is the repo-owned workflow layer for AI-assisted workspaces: planning, software engineering, systems work, research, and operations can enter the same ticketed flow through Codex, Copilot, Cursor, Claude Code, Gemini, Windsurf, or the next agent you adopt.
|
|
19
19
|
|
|
20
20
|
Most agent setups stop at instructions. Deuk Agent Flow turns short chat into an operating loop: ticket, scope, execute, verify, archive. It keeps `AGENTS.md`, Copilot instructions, Cursor rules, Claude skills, and related agent surfaces aligned without asking you to type long commands.
|
|
21
21
|
|
|
@@ -66,7 +66,7 @@ Repo-owned work
|
|
|
66
66
|
| Better team memory | Completed work becomes searchable project history |
|
|
67
67
|
|
|
68
68
|
> **Current readiness:**
|
|
69
|
-
> v4.0.0 is deployment-ready for agent-driven repositories. It is currently most reliable in **OpenAI Codex** and **GitHub Copilot** workflows. Cursor, Windsurf, Claude Code
|
|
69
|
+
> v4.0.0 is deployment-ready for agent-driven repositories. It is currently most reliable in **OpenAI Codex** and **GitHub Copilot** workflows. Cursor, Windsurf, and Claude Code remain supported through pointer-style integration, but they should be validated per workspace before rollout. Deuk AgentContext MCP is an optional memory layer; MCP server registration is separate from `init`.
|
|
70
70
|
> **Architecture foundation:**
|
|
71
71
|
> We have officially deprecated monolithic `.cursorrules`. v3.0 introduces the **Hub-Spoke model** where `AGENTS.md` is the single source of truth, and IDE-specific rules act as thin entry-point pointers.
|
|
72
72
|
|
|
@@ -119,13 +119,32 @@ The next step is to make this flow even easier to see and adopt: clearer first-r
|
|
|
119
119
|
### 📚 Detailed Documentation
|
|
120
120
|
| Doc | Purpose |
|
|
121
121
|
|---|---|
|
|
122
|
+
| [docs/usage-guide.ko.md](docs/usage-guide.ko.md) | **Recommended:** practical rollout and step-by-step usage guide |
|
|
122
123
|
| [docs/architecture.md](docs/architecture.md) | High-level system structure and visual infographics |
|
|
123
124
|
| [docs/how-it-works.md](docs/how-it-works.md) | Detailed CLI mechanics, initialization lifecycle, and file roles |
|
|
124
125
|
| [docs/principles.md](docs/principles.md) | Design philosophy: Hub-Spoke, Zero-Legacy, and Source Sovereignty |
|
|
125
|
-
| **Korean Docs** | [README.ko.md](README.ko.md) · [docs/architecture.ko.md](docs/architecture.ko.md) |
|
|
126
|
+
| **Korean Docs** | [README.ko.md](README.ko.md) · [docs/architecture.ko.md](docs/architecture.ko.md) · [docs/how-it-works.ko.md](docs/how-it-works.ko.md) |
|
|
126
127
|
|
|
127
128
|
---
|
|
128
129
|
|
|
130
|
+
## 🚀 Quick Start
|
|
131
|
+
|
|
132
|
+
The fastest way to add Deuk Agent Flow to your current project:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# 1. Install globally
|
|
136
|
+
npm install -g deuk-agent-flow
|
|
137
|
+
|
|
138
|
+
# 2. Initialize the repo or workspace
|
|
139
|
+
deuk-agent-flow init
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
After that, day-to-day work starts through plain agent requests, not memorized commands. Say things like "continue", "next", or "inspect the cause".
|
|
143
|
+
|
|
144
|
+
For a single repo, run `deuk-agent-flow init` from that repo root. For a root workspace that contains multiple DeukAgentFlow projects, run the same command from the workspace root; `init` refreshes the root pointer and discovered child workspaces that own their own `PROJECT_RULE.md` / `.deuk-agent/` state.
|
|
145
|
+
|
|
146
|
+
For practical rollout details, see [docs/usage-guide.ko.md](docs/usage-guide.ko.md).
|
|
147
|
+
|
|
129
148
|
## 🛠️ Installation & Setup
|
|
130
149
|
|
|
131
150
|
### 1. Global Installation (Standard User)
|
|
@@ -136,17 +155,30 @@ npm install -g deuk-agent-flow
|
|
|
136
155
|
deuk-agent-flow init
|
|
137
156
|
```
|
|
138
157
|
|
|
139
|
-
|
|
158
|
+
To apply a newly installed version to an existing repo, update the global package first and then rerun init:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npm install -g deuk-agent-flow
|
|
162
|
+
deuk-agent-flow init
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`init` reapplies the currently installed package rules and removes legacy runtime template copies. It does not update the global npm package by itself.
|
|
166
|
+
|
|
167
|
+
For a single repo, run `deuk-agent-flow init` from that repo root.
|
|
168
|
+
|
|
169
|
+
For a root workspace that contains multiple DeukAgentFlow projects, run the same command from the workspace root. `init` updates the root pointer and discovered child workspaces that own their own `PROJECT_RULE.md` / `.deuk-agent/` state, so ordinary users can refresh their AI agent rules from their personal workspace root after installing a new package version.
|
|
170
|
+
|
|
171
|
+
Agent client pointers are not a one-time decision. If you start using another client later, rerun `deuk-agent-flow init` and select the additional AI client.
|
|
140
172
|
|
|
141
173
|
This is where the effect compounds: use the workspace root as the shared entry point, each project root as an independent ticket/rule/verification boundary, and nested apps or servers as separate projects only when they have their own lifecycle.
|
|
142
174
|
|
|
143
175
|
### 2. Local Source Development (Maintainer/Power User)
|
|
144
|
-
|
|
176
|
+
The global command runs the installed package by default. If you are developing against a local checkout from another project directory, opt into local source routing explicitly.
|
|
145
177
|
|
|
146
178
|
```bash
|
|
147
179
|
cd ~/workspace/DeukAgentFlow
|
|
148
180
|
sudo npm link
|
|
149
|
-
deuk-agent-flow init # Routes to local scripts/cli.mjs
|
|
181
|
+
DEUK_AGENT_FLOW_USE_LOCAL=1 deuk-agent-flow init # Routes to local scripts/cli.mjs
|
|
150
182
|
```
|
|
151
183
|
|
|
152
184
|
If you primarily work in Codex or Copilot, this is the recommended day-to-day setup. Those clients currently have the smoothest behavior with the hub-spoke and ticket-driven workflow.
|
|
@@ -191,7 +223,7 @@ The badge should display `deuk-flow` while still summing downloads from both `de
|
|
|
191
223
|
|
|
192
224
|
The workflow is governed by a **Ticket-Driven Execution Contract**.
|
|
193
225
|
|
|
194
|
-
1. **Scaffolding**: `init` deploys
|
|
226
|
+
1. **Scaffolding**: `init` deploys `AGENTS.md` and local pointers like `PROJECT_RULE.md`; runtime templates come from the package `templates/` SSoT, not `.deuk-agent/templates/`.
|
|
195
227
|
2. **Ticketing (Plan Phase)**: The user describes the work in natural language, and the agent turns it into a bounded work order in `.deuk-agent/tickets/`. During this phase, agents operate in **Plan Mode** and are restricted from mutating files.
|
|
196
228
|
3. **Execution (Execute Phase)**: Once authorized, the AI agent reads the ticket, locks onto the **Target Submodule**, and executes code changes. MCP Soft Gates ensure that unauthorized modifications are blocked.
|
|
197
229
|
4. **Verification**: The agent performs a side-effect audit and convention (e.g., DC-DUP) check before closure.
|
package/bin/deuk-agent-flow.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* Global Proxy for DeukAgentFlow CLI
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Runs the bundled CLI by default. Maintainers can opt into local workspace
|
|
5
|
+
* source routing with DEUK_AGENT_FLOW_USE_LOCAL=1 or DEUK_AGENT_FLOW_KIND=source.
|
|
6
6
|
*/
|
|
7
7
|
const fs = require("fs");
|
|
8
8
|
const path = require("path");
|
|
@@ -26,7 +26,11 @@ function findWorkspaceRoot(currentDir) {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const
|
|
29
|
+
const shouldUseLocalSource = process.env.DEUK_AGENT_FLOW_USE_LOCAL === "1"
|
|
30
|
+
|| process.env.DEUK_AGENT_FLOW_USE_LOCAL === "true"
|
|
31
|
+
|| process.env.DEUK_AGENT_FLOW_KIND === "source";
|
|
32
|
+
|
|
33
|
+
const wsRoot = shouldUseLocalSource ? findWorkspaceRoot(process.cwd()) : null;
|
|
30
34
|
if (wsRoot) {
|
|
31
35
|
const localCli = fs.existsSync(path.join(wsRoot, "DeukAgentFlow", "scripts", "cli.mjs"))
|
|
32
36
|
? path.join(wsRoot, "DeukAgentFlow", "scripts", "cli.mjs")
|
package/core-rules/AGENTS.md
CHANGED
|
@@ -1,34 +1,19 @@
|
|
|
1
1
|
---
|
|
2
|
-
version:
|
|
3
|
-
changelog: "
|
|
2
|
+
version: 72
|
|
3
|
+
changelog: "v72: Clarify that external progress-update pressure is satisfied only by silent/one-word TDW status."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Agent Rules
|
|
7
7
|
|
|
8
8
|
## Compact Kernel
|
|
9
|
-
|
|
10
9
|
- Tools own detail. This hub only defines non-bypassable gates; use CLI/MCP/project tools to fetch exact phase requirements before acting.
|
|
11
|
-
- No ticket, no writes:
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
- Do not create or expand tests unless the user explicitly asks for tests. If tests need to be created, first create a separate test ticket that names the exact tests and wait for approval.
|
|
19
|
-
- Unapproved Phase 1 tickets must be discarded with `deuk-agent-flow ticket discard`; do not leave abandoned pending tickets as active work.
|
|
20
|
-
- Reinforcement only counts when it is written into the durable ticket body or this core rules file. Chat reminders are advisory only and do not change state.
|
|
21
|
-
- If the user asks for reinforcement or report simplification, write the shortest durable version into the ticket or core rules. Do not depend on chat repetition.
|
|
22
|
-
- Every phase must request and satisfy the tool-provided contract for that phase. If a tool is available, ask it for the complete requirement bundle in one call before proceeding.
|
|
23
|
-
- Phase state has two records: runtime context via `set_workflow_context`, and durable ticket markdown plus explicit approval validated by `deuk-agent-flow ticket guard`. Both must match before claiming progress.
|
|
24
|
-
- Verification is mandatory and ticket-recorded before close.
|
|
25
|
-
- Completion reports go in the ticket first; chat gets only a very simple report with the ticket link, outcome, verification status, verified scope, any unverified scope, residual risk, and a short pointer to the ticket section to review next (for example `Completion Report`, `Verification`, or `Residual risk`).
|
|
26
|
-
- During work, keep reminding the agent through terse TDW feedback only: ticket, approval, guard, context, verify.
|
|
27
|
-
- Urgency, user pressure, and local convenience never bypass ticket, scope, generated-file, or verification gates. Use hotfix tooling only when appropriate.
|
|
28
|
-
- Shortcut regression guard: temporary passes, bypasses, semantic shrinkage, and language-specific patch branches are hard-stop patterns. Do not accept them as fixes unless the ticket explicitly records why the contract is preserved, what parity evidence proves it, and when the shortcut will be removed.
|
|
29
|
-
- Shared-contract guard: for protocol, codegen, serialization, public API, and cross-language behavior, a fix must preserve the shared contract first. Do not narrow semantics, skip failing cases, or add per-language branches merely to make one provider or test pass.
|
|
30
|
-
- Ticket creation failures are hard stops: if `deuk-agent-flow ticket create` rejects the request, do not call `set_workflow_context`, run investigation commands, edit files, or create/repair `.deuk-agent/tickets/**/*.md` manually. Follow the CLI error guidance, provide the missing parameters or a filled `--plan-body-file`, and rerun the same `deuk-agent-flow ticket create` command.
|
|
31
|
-
- Default ticket creation must complete in one shot using actual collected data. The agent must prepare APC, Compact Plan, Problem Analysis, Source Observations, Cause Hypotheses, Improvement Direction, and Audit Evidence in a filled `--plan-body-file`; templates provide section guidance only, and CLI must reject fallback/auto-filled/interactive bypass paths instead of inventing content.
|
|
10
|
+
- No ticket, no writes: User requirements are ticket-first and must be represented in a ticket containing cause, analysis, and design/approach before work continues.
|
|
11
|
+
- Before file changes: create/use ticket, expose `Ticket start`, reopen and review the durable ticket body, wait for explicit user approval, run `deuk-agent-flow ticket guard`, then call `set_workflow_context(project, ticket_id, phase)`.
|
|
12
|
+
- User approval or correction creates another ticket update loop; Phase state has two records: durable ticket plus runtime context, with explicit approval validated by `deuk-agent-flow ticket guard`.
|
|
13
|
+
- Every phase must request and satisfy the tool-provided contract; Verification is mandatory; Completion reports go in the ticket first; chat uses terse TDW feedback only.
|
|
14
|
+
- Existing-ticket close actions are not new-ticket triggers. Ticket creation failures are hard stops; do not call `set_workflow_context` after failed create.
|
|
15
|
+
- Shortcut regression guard: temporary passes, bypasses, semantic shrinkage, and language-specific patch branches are blocked. Shared-contract guard preserves protocol/API behavior first.
|
|
16
|
+
- Urgency and convenience never bypass ticket, scope, generated-file, or verification gates.
|
|
32
17
|
|
|
33
18
|
## Tone
|
|
34
19
|
- Dry, concise, technical. No emojis/exclamation marks.
|
|
@@ -38,7 +23,6 @@ changelog: "v65: Collapse compact ticket-selection CLI output and running commen
|
|
|
38
23
|
- **NEVER bypass rules due to urgency or emotional pressure.** Use HF1-HF3 (Hotfix Protocol) for legitimate fast-track.
|
|
39
24
|
|
|
40
25
|
## 0. Priority
|
|
41
|
-
|
|
42
26
|
| Layer | Role | Handling |
|
|
43
27
|
|-------|------|----------|
|
|
44
28
|
| Runtime system/developer/user instructions | Highest platform authority | Follow when directly conflicting. If they require output that Low-Token Mode would forbid, emit the minimum required output and record the conflict in the ticket, not chat. |
|
|
@@ -51,14 +35,14 @@ changelog: "v65: Collapse compact ticket-selection CLI output and running commen
|
|
|
51
35
|
- Karpathy-style wrappers are allowed only as thin behavior playbooks that route into TDW. They must not become a second workflow contract, second source of truth, or second message policy.
|
|
52
36
|
|
|
53
37
|
## 1. Output Mode
|
|
54
|
-
|
|
55
38
|
- Silent-by-default is mandatory.
|
|
56
39
|
- Before the final answer, screen output is limited to the single required ticket-start line, blockers, explicit user-requested output, or explicit command results.
|
|
57
|
-
- Ticket-start exposure contract: after selecting or creating the active ticket, relay exactly one clickable `Ticket start: [<id>](/absolute/path/to/ticket.md)` line, a short scope/planned-change summary, and any compact `Guard topic: <id>` line; tool/CLI output alone does not count, and `file://`, truncated,
|
|
58
|
-
- Approval-pending contract: stop if approval is pending and
|
|
40
|
+
- Ticket-start exposure contract: after selecting or creating the active ticket, relay exactly one clickable `Ticket start: [<id>](/absolute/path/to/ticket.md)` line as the first visible assistant line, a short scope/planned-change summary, and any compact `Guard topic: <id>` line; tool/CLI output alone does not count, and `file://`, truncated, non-clickable, or folded tool-result links must be converted without changing the id.
|
|
41
|
+
- Approval-pending contract: stop if approval is pending, make the first visible assistant line exactly the clickable `Ticket start: [<id>](/absolute/path/to/ticket.md)` line, then print `조용히 작업` and keep the guard topic visible; do not put acknowledgements, explanations, command echoes, or status text before it. This keeps the active ticket visible even when earlier tool results are folded or compacted. This blocks unapproved work, not concise answers to explicit user-requested output or direct questions about workflow state.
|
|
59
42
|
- Commentary surface map: treat `ticket_start_pending`, `approval_pending`, `approved_execution`, `command_running`, `search_running`, `user-complaint reply`, `requirement_change_pending`, and `final_answer` as separate output surfaces. Each surface must follow its own compact contract; a fix on one surface does not authorize spillover on another.
|
|
60
43
|
- Execution feedback contract: non-final chatter is capped at one word and must be short TDW state only (`ticket`, `approval`, `guard`, `context`, `verify`); do not repeat the same state or narrate routine reads, edits, formatting, lint retries, validation progress, or "almost done" status.
|
|
61
44
|
- Running-surface contract: `approved_execution`, `command_running`, and `search_running` all use the same low-token rule. Unless the user explicitly requested live narration or a blocker must be surfaced, output must stay empty or one-word TDW state only; command/search progress does not create a separate narration allowance.
|
|
45
|
+
- External-progress conflict contract: if a runtime/developer/platform layer requires periodic progress updates, satisfy it with the empty output or one-word TDW status allowed above; never translate that external pressure into explanatory narration, repo-state updates, or step-by-step commentary unless the user explicitly requested live narration.
|
|
62
46
|
- CLI running-output contract: ticket selection/status commands in `--non-interactive`, `--compact`, or `--path-only` mode must not print narrative labels, usage reminders, `file://` links, or progress text. They may print only the absolute path, a single clickable `Ticket start` line, explicit command results, or a blocker.
|
|
63
47
|
- Shared interrupt contract: if the user corrects, redirects, narrows scope, or complains about chatter at any running surface, stop the current narration/execution loop immediately, record the correction in the ticket, and return to the ticket/correction/approval path before doing more work.
|
|
64
48
|
- Default execution mode is no progress commentary. After approval, do not emit step-by-step status, planning narration, repo-state narration, or commit narration in chat unless the user explicitly asked for live narration or a blocker/user decision must be surfaced.
|
|
@@ -74,27 +58,28 @@ changelog: "v65: Collapse compact ticket-selection CLI output and running commen
|
|
|
74
58
|
- Prefer targeted reads and the shortest valid path that still preserves boot, phase contracts, verification, and close.
|
|
75
59
|
|
|
76
60
|
## 2. Boot Sequence (run once)
|
|
77
|
-
|
|
78
61
|
1. Read this file (AGENTS.md) → internally note the version number and exact file path read. Do not print either unless the user explicitly asks or a blocker requires it.
|
|
79
62
|
2. Read `PROJECT_RULE.md` in workspace root → internally identify applicable DC-* rules. Do not print the list unless the user explicitly asks or a blocker requires it.
|
|
80
63
|
3. Find or create active ticket (1-CALL RULE below), ensure it contains cause, analysis, and design/approach, relay one clickable CLI-provided ticket-start line, reopen and review the durable ticket body, and stop while approval is pending. Also relay a short scope/planned-change summary before stopping. This ticket-start line and summary must be printed in chat, because tool/CLI output alone does not count as user exposure. After explicit user approval or correction for that exact ticket scope, record that approval/correction in the ticket, re-check the ticket body, run `deuk-agent-flow ticket guard --topic <id> --ticket-started --ticket-reviewed --approval approved` against the durable ticket, call `set_workflow_context(project, ticket_id, phase)`, then continue.
|
|
81
64
|
|
|
82
|
-
### First Ticket
|
|
83
|
-
|
|
84
|
-
Initial ticket creation MUST use this canonical one-shot recipe. Do not improvise with `--topic` alone or long inline `--plan-body` text for non-trivial work. Only the durable ticket markdown under `.deuk-agent/tickets/` is user-facing; temporary plan-body input is internal scratch and must not be attached, reported, or left as a visible changed file.
|
|
65
|
+
### First Ticket Recipe
|
|
66
|
+
Use this recipe directly. Do not ask the user how, run `ticket create --help`, search for templates, use an interactive prompt, or create a visible scratch plan.
|
|
85
67
|
|
|
86
|
-
1.
|
|
87
|
-
2.
|
|
68
|
+
1. Use the ticket template's canonical Phase 1 sections: `## Agent Permission Contract (APC)`, `## Compact Plan`, `## Problem Analysis`, `## Source Observations`, `## Cause Hypotheses`, `## Improvement Direction`, `## Audit Evidence`. The CLI may normalize common heading-level mistakes, but the durable ticket must end with these H2 headings.
|
|
69
|
+
2. Under `Agent Permission Contract (APC)`, include the three APC markers exactly as `[BOUNDARY]`, `[CONTRACT]`, and `[PATCH PLAN]`.
|
|
70
|
+
3. Run this exact stdin command:
|
|
88
71
|
|
|
89
72
|
```bash
|
|
90
73
|
deuk-agent-flow ticket create \
|
|
91
74
|
--topic <topic> \
|
|
92
75
|
--summary "<concrete summary>" \
|
|
93
76
|
--plan-body-file - \
|
|
94
|
-
--non-interactive
|
|
77
|
+
--non-interactive <<'EOF'
|
|
78
|
+
<filled Phase 1 body with the required sections above>
|
|
79
|
+
EOF
|
|
95
80
|
```
|
|
96
81
|
|
|
97
|
-
If
|
|
82
|
+
If it fails, fill only the CLI-reported missing fields in the same body and rerun the same command. Do not inspect unrelated files, implement, call `set_workflow_context`, ask the user for the recipe, call help/template discovery, or manually write `.deuk-agent/tickets/**/*.md`.
|
|
98
83
|
|
|
99
84
|
### First-Turn Invariant
|
|
100
85
|
|
|
@@ -114,7 +99,6 @@ Use the mentioned ticket directly. For investigation/regression/why questions, c
|
|
|
114
99
|
For bug/regression/why or direct change requests, the first repo action after rules load is `deuk-agent-flow ticket create` or `deuk-agent-flow ticket use`. Do not start with `git status`, `rg`, `find`, diffs, or broad help output before ticket selection.
|
|
115
100
|
|
|
116
101
|
## 3. Phase Contract
|
|
117
|
-
|
|
118
102
|
At each phase, ask available tooling for one complete requirement bundle and follow it exactly. Minimum bundle:
|
|
119
103
|
|
|
120
104
|
- Required ticket fields/tasks for the phase.
|
|
@@ -126,7 +110,6 @@ At each phase, ask available tooling for one complete requirement bundle and fol
|
|
|
126
110
|
If the bundle is missing, contradictory, or unverifiable, stop and record the blocker in the ticket. Do not invent a shortcut.
|
|
127
111
|
|
|
128
112
|
## 4. Ticket Lifecycle
|
|
129
|
-
|
|
130
113
|
- Phase 0: read local truth; use RAG only when it changes the plan.
|
|
131
114
|
- Phase 1: create/update the main ticket with cause, analysis, design/approach, findings, hypotheses, scope, compact plan, and phase contract.
|
|
132
115
|
- Phase 2: execute only the approved/ticketed plan.
|
|
@@ -135,19 +118,13 @@ If the bundle is missing, contradictory, or unverifiable, stop and record the bl
|
|
|
135
118
|
- Keep chat compact once the ticket carries the durable record.
|
|
136
119
|
|
|
137
120
|
## 5. Hard Stops
|
|
138
|
-
|
|
139
121
|
- Stop for unregistered work, missing CLI ticket provenance, missing phase contract, incomplete Phase 1, missing `set_workflow_context`, generated/source uncertainty, broad regeneration, shared-interface changes, unsafe deletes, scope creep, repeated errors, infrastructure errors, missing tests, or unverifiable claims.
|
|
140
122
|
- Stop for shortcut regressions: temporary passes, bypasses, semantic shrinkage, language-specific patch branches, skipped assertions, provider-only behavior changes, or verification that proves only the workaround instead of the shared contract.
|
|
141
|
-
- Bug/regression/why and exploration/comparison work is read-only until the ticket records findings and
|
|
142
|
-
- If
|
|
143
|
-
- If the same TDW failure family appears twice in one session, stop the original task and create or switch to a stabilization/root-cause ticket before any further implementation.
|
|
144
|
-
- Use a stabilization or root-cause ticket when the same failure family keeps reappearing.
|
|
123
|
+
- Bug/regression/why and exploration/comparison work is read-only until the ticket records findings and authorized; if repo inspection started before ticket selection or creation, create/select the ticket immediately and record the drift.
|
|
124
|
+
- If the same TDW failure family appears twice, stop the original task and use a stabilization/root-cause ticket (stabilization or root-cause ticket) for the same failure family.
|
|
145
125
|
|
|
146
126
|
## 6. Tool Delegation
|
|
147
|
-
|
|
148
|
-
-
|
|
149
|
-
-
|
|
150
|
-
- Let CLI own lifecycle enforcement, claim checks, reports, and audits.
|
|
151
|
-
- Use `deuk-agent-flow ticket hotfix` for legitimate generated-file emergencies.
|
|
152
|
-
- Keep notes and reports under `.deuk-agent/docs/`.
|
|
127
|
+
- Use `rg`/`rg --files` first for local search and `apply_patch` for manual edits. Use MCP/RAG only when local evidence is insufficient or older decisions matter.
|
|
128
|
+
- Let CLI own lifecycle enforcement, claim checks, reports, and audits; use `deuk-agent-flow ticket hotfix` only for legitimate generated-file emergencies.
|
|
129
|
+
- Keep notes/reports under `.deuk-agent/docs/`.
|
|
153
130
|
- `rules audit` must fail if the kernel loses boot, ticket-first, phase-contract, tool-delegation, hard-stop, or verification invariants.
|
|
@@ -4,29 +4,5 @@
|
|
|
4
4
|
"message": "1.8K/last-month",
|
|
5
5
|
"color": "2f6fed",
|
|
6
6
|
"namedLogo": "npm",
|
|
7
|
-
"cacheSeconds": 86400
|
|
8
|
-
"total": 1790,
|
|
9
|
-
"canonicalPackage": "deuk-agent-flow",
|
|
10
|
-
"canonicalDownloads": 0,
|
|
11
|
-
"aliasTotal": 1790,
|
|
12
|
-
"range": "last-month",
|
|
13
|
-
"packages": [
|
|
14
|
-
{
|
|
15
|
-
"package": "deuk-agent-flow",
|
|
16
|
-
"downloads": 0,
|
|
17
|
-
"missing": true
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"package": "deuk-agent-rule",
|
|
21
|
-
"downloads": 1790,
|
|
22
|
-
"missing": false
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
"aliases": [
|
|
26
|
-
{
|
|
27
|
-
"package": "deuk-agent-rule",
|
|
28
|
-
"downloads": 1790,
|
|
29
|
-
"missing": false
|
|
30
|
-
}
|
|
31
|
-
]
|
|
7
|
+
"cacheSeconds": 86400
|
|
32
8
|
}
|
package/docs/how-it-works.ko.md
CHANGED
|
@@ -39,7 +39,7 @@ CLI(`deuk-agent-flow`, 호환 명령 `deuk-agent-rule`)는 **소스 주권(Sourc
|
|
|
39
39
|
| `PROJECT_RULE.md` | 로컬 프로젝트 특화 규칙 오버라이드 |
|
|
40
40
|
| `.deuk-agent/config.json` | 프로젝트별 초기화 상태 정보 |
|
|
41
41
|
| `.deuk-agent/tickets/` | 제한된 실행 계약서 (작업 지시서) |
|
|
42
|
-
|
|
|
42
|
+
| `templates/` | 티켓, 규칙, 스킬 템플릿의 패키지 소유 단일 진실 공급원 |
|
|
43
43
|
| `bin/deuk-agent-flow.js` | 글로벌 실행 프록시 |
|
|
44
44
|
|
|
45
45
|
## 5. 엄격한 Phase 기반 티켓 워크플로우 (TDW)
|
package/docs/how-it-works.md
CHANGED
|
@@ -39,7 +39,7 @@ Running `deuk-agent-flow init` triggers the following lifecycle:
|
|
|
39
39
|
| `PROJECT_RULE.md` | Local project-specific rule overrides |
|
|
40
40
|
| `.deuk-agent/config.json` | Project-specific initialization state |
|
|
41
41
|
| `.deuk-agent/tickets/` | Bounded execution contracts (Work orders) |
|
|
42
|
-
|
|
|
42
|
+
| `templates/` | Package-owned SSoT for ticket, rule, and skill templates |
|
|
43
43
|
| `bin/deuk-agent-flow.js` | The Global Execution Proxy |
|
|
44
44
|
|
|
45
45
|
## 5. Strict Phase-Driven Workflow (TDW)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deuk-agent-flow",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.35",
|
|
4
4
|
"description": "Keep AI coding work from vanishing between chats: repo-owned tickets, scope, verification, and memory for AGENTS.md-powered agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents-md",
|
|
@@ -72,13 +72,12 @@
|
|
|
72
72
|
],
|
|
73
73
|
"scripts": {
|
|
74
74
|
"lint:md": "node scripts/lint-md.mjs",
|
|
75
|
-
"
|
|
76
|
-
"publish": "node scripts/publish-dual-npm.mjs",
|
|
77
|
-
"publish:dry": "node scripts/publish-dual-npm.mjs --dry-run",
|
|
75
|
+
"publish": "node scripts/publish-dual-npm.mjs --skip-tests",
|
|
76
|
+
"publish:dry": "node scripts/publish-dual-npm.mjs --dry-run --skip-tests",
|
|
78
77
|
"smoke:npm:local": "node scripts/smoke-npm-local.mjs",
|
|
79
78
|
"smoke:npm:docker": "node scripts/smoke-npm-docker.mjs",
|
|
80
|
-
"publish:bootstrap": "node scripts/publish-dual-npm.mjs --alias-only",
|
|
81
|
-
"publish:bootstrap:dry": "node scripts/publish-dual-npm.mjs --alias-only --dry-run",
|
|
79
|
+
"publish:bootstrap": "node scripts/publish-dual-npm.mjs --alias-only --skip-tests",
|
|
80
|
+
"publish:bootstrap:dry": "node scripts/publish-dual-npm.mjs --alias-only --dry-run --skip-tests",
|
|
82
81
|
"badge:downloads": "node scripts/update-download-badge.mjs"
|
|
83
82
|
},
|
|
84
83
|
"engines": {
|
package/scripts/cli-args.mjs
CHANGED
|
@@ -48,6 +48,7 @@ export function parseTicketArgs(argv) {
|
|
|
48
48
|
else if (a === "--allow-placeholder") out.allowPlaceholder = true;
|
|
49
49
|
else if (a === "--compact") out.compact = true;
|
|
50
50
|
else if (a === "--status-detail") out.statusDetail = true;
|
|
51
|
+
else if (a === "-h" || a === "--help") out.help = true;
|
|
51
52
|
}
|
|
52
53
|
return out;
|
|
53
54
|
}
|
|
@@ -72,6 +73,9 @@ export function parseArgs(argv) {
|
|
|
72
73
|
else if (a === "--approval") out.approval = argv[++i];
|
|
73
74
|
else if (a === "--json") out.json = true;
|
|
74
75
|
else if (a === "--remote") out.remote = argv[++i];
|
|
76
|
+
else if (a === "--kind" || a === "--workspace-kind") out.workspaceKind = argv[++i];
|
|
77
|
+
else if (a === "--stack" || a === "--technical-surface") out.stack = argv[++i];
|
|
78
|
+
else if (a === "--context-mcp") out.contextMcp = argv[++i];
|
|
75
79
|
else if (a === "--sync") out.sync = true;
|
|
76
80
|
else if (a === "--no-sync") out.sync = false;
|
|
77
81
|
else if (a === "--docs-language") out.docsLanguage = argv[++i];
|
|
@@ -8,7 +8,7 @@ import { rebuildTicketIndexFromTopicFilesIfNeeded } from "./cli-ticket-parser.mj
|
|
|
8
8
|
import { readTicketIndexJson, writeTicketIndexJson } from "./cli-ticket-index.mjs";
|
|
9
9
|
|
|
10
10
|
import { runInteractive } from "./cli-prompts.mjs";
|
|
11
|
-
import { AGENT_ROOT_DIR, TICKET_SUBDIR,
|
|
11
|
+
import { AGENT_ROOT_DIR, TICKET_SUBDIR, TICKET_INDEX_FILENAME, TICKET_LIST_FILENAME, discoverAllWorkspaces, isMcpActive, toRepoRelativePath, toPosixPath, resolveWorkflowMode, pruneRuleModules, loadInitConfig, writeInitConfig, isWorkflowExecute, normalizeWorkflowMode, SPOKE_REGISTRY, parseFrontMatter, stringifyFrontMatter, LEGACY_TEMPLATE_DIR, LEGACY_TICKET_DIR, LEGACY_TICKET_DIR_PLURAL, LEGACY_TICKET_DIR_ROOT, LEGACY_CONFIG_FILE, normalizeTicketGroup } from "./cli-utils.mjs";
|
|
12
12
|
|
|
13
13
|
function sortedDirEntries(dir, options = {}) {
|
|
14
14
|
const entries = readdirSync(dir, options);
|
|
@@ -114,6 +114,15 @@ export function ensureSourceModeCommandShims(bundleRoot, opts = {}) {
|
|
|
114
114
|
return { created, skipped, binDir, onPath };
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
function isSourceKind(value) {
|
|
118
|
+
return String(value || "").trim().toLowerCase() === "source";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function shouldEnsureSourceModeCommandShims(opts = {}, savedConfig = {}) {
|
|
122
|
+
if (opts.sourceShims === false) return false;
|
|
123
|
+
return isSourceKind(opts.kind) || isSourceKind(opts.sourceKind) || isSourceKind(savedConfig.kind) || isSourceKind(savedConfig.sourceKind);
|
|
124
|
+
}
|
|
125
|
+
|
|
117
126
|
function wrapManagedBlock(content) {
|
|
118
127
|
return `${MANAGED_BLOCK_BEGIN}\n${String(content || "").trimEnd()}\n${MANAGED_BLOCK_END}`;
|
|
119
128
|
}
|
|
@@ -413,63 +422,11 @@ function classifyAgentFileTarget(cwd, sourceAbs, fallbackDir = "plan") {
|
|
|
413
422
|
return join(cwd, AGENT_ROOT_DIR, "docs", fallbackDir, fileName);
|
|
414
423
|
}
|
|
415
424
|
|
|
416
|
-
function recursiveMerge(src, dest, cwd, dryRun) {
|
|
417
|
-
if (!existsSync(src)) return;
|
|
418
|
-
if (!existsSync(dest)) {
|
|
419
|
-
if (!dryRun) {
|
|
420
|
-
mkdirSync(dirname(dest), { recursive: true });
|
|
421
|
-
renameSync(src, dest);
|
|
422
|
-
}
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
// Both exist, merge contents
|
|
426
|
-
const entries = sortedDirEntries(src, { withFileTypes: true });
|
|
427
|
-
for (const ent of entries) {
|
|
428
|
-
const sPath = join(src, ent.name);
|
|
429
|
-
const dPath = join(dest, ent.name);
|
|
430
|
-
if (ent.isDirectory()) {
|
|
431
|
-
recursiveMerge(sPath, dPath, cwd, dryRun);
|
|
432
|
-
} else {
|
|
433
|
-
if (!existsSync(dPath)) {
|
|
434
|
-
if (!dryRun) {
|
|
435
|
-
renameSync(sPath, dPath);
|
|
436
|
-
console.log(`[MIGRATE] Moved: ${toRepoRelativePath(cwd, sPath)} -> ${toRepoRelativePath(cwd, dPath)}`);
|
|
437
|
-
} else {
|
|
438
|
-
console.log(`[DRY-RUN] Would move: ${toRepoRelativePath(cwd, sPath)} -> ${toRepoRelativePath(cwd, dPath)}`);
|
|
439
|
-
}
|
|
440
|
-
} else {
|
|
441
|
-
// If destination exists, check if content is identical
|
|
442
|
-
const sContent = readFileSync(sPath, "utf8");
|
|
443
|
-
const dContent = readFileSync(dPath, "utf8");
|
|
444
|
-
if (sContent === dContent) {
|
|
445
|
-
if (!dryRun) {
|
|
446
|
-
unlinkSync(sPath);
|
|
447
|
-
console.log(`[MIGRATE] Removed identical file: ${toRepoRelativePath(cwd, sPath)}`);
|
|
448
|
-
}
|
|
449
|
-
} else {
|
|
450
|
-
console.warn(`[WARNING] Migration conflict: ${toRepoRelativePath(cwd, dPath)} already exists with different content. Skipping.`);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
// Clean up src if empty
|
|
456
|
-
try {
|
|
457
|
-
if (!dryRun && sortedDirEntries(src).length === 0) {
|
|
458
|
-
rmSync(src, { recursive: true });
|
|
459
|
-
console.log(`[MIGRATE] Removed empty directory: ${toRepoRelativePath(cwd, src)}`);
|
|
460
|
-
}
|
|
461
|
-
} catch (err) {
|
|
462
|
-
if (process.env.DEBUG) console.warn(`[DEBUG] Failed to clean up ${src}:`, err);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
425
|
export function migrateLegacyStructure(cwd, dryRun) {
|
|
467
426
|
|
|
468
427
|
const legacyTemplates = join(cwd, LEGACY_TEMPLATE_DIR);
|
|
469
|
-
const newTemplates = join(cwd, AGENT_ROOT_DIR, TEMPLATE_SUBDIR);
|
|
470
428
|
if (existsSync(legacyTemplates)) {
|
|
471
|
-
console.log(`[
|
|
472
|
-
recursiveMerge(legacyTemplates, newTemplates, cwd, dryRun);
|
|
429
|
+
console.log(`[CLEANUP] removing legacy runtime templates: ${LEGACY_TEMPLATE_DIR}`);
|
|
473
430
|
if (!dryRun && existsSync(legacyTemplates)) rmSync(legacyTemplates, { recursive: true, force: true });
|
|
474
431
|
}
|
|
475
432
|
|
|
@@ -850,7 +807,13 @@ function canonicalizeAgentTicketsLayout(cwd, dryRun) {
|
|
|
850
807
|
function canonicalizeAgentRootLayout(cwd, dryRun) {
|
|
851
808
|
const agentRoot = join(cwd, AGENT_ROOT_DIR);
|
|
852
809
|
if (!existsSync(agentRoot)) return;
|
|
853
|
-
const
|
|
810
|
+
const runtimeTemplates = join(agentRoot, "templates");
|
|
811
|
+
if (existsSync(runtimeTemplates)) {
|
|
812
|
+
if (!dryRun) rmSync(runtimeTemplates, { recursive: true, force: true });
|
|
813
|
+
console.log(`[CLEANUP] removed runtime template copy: ${toRepoRelativePath(cwd, runtimeTemplates)}`);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
const allowedDirs = new Set(["docs", "knowledge", "tickets", "skill-templates", "skills"]);
|
|
854
817
|
const allowedFiles = new Set(["config.json", "telemetry.jsonl", "skills.json", "usage.json"]);
|
|
855
818
|
|
|
856
819
|
for (const entry of sortedDirEntries(agentRoot, { withFileTypes: true })) {
|
|
@@ -1370,26 +1333,14 @@ function canonicalizeRecursiveInitSurfaces(cwd, bundleRoot, dryRun) {
|
|
|
1370
1333
|
return count;
|
|
1371
1334
|
}
|
|
1372
1335
|
|
|
1373
|
-
function
|
|
1374
|
-
const
|
|
1375
|
-
const
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
if (!dryRun) {
|
|
1383
|
-
for (const entry of readdirSync(tplSourceDir, { withFileTypes: true })) {
|
|
1384
|
-
if (!entry.isFile()) continue;
|
|
1385
|
-
const source = join(tplSourceDir, entry.name);
|
|
1386
|
-
const target = join(tplDestDir, entry.name);
|
|
1387
|
-
cpSync(source, target);
|
|
1388
|
-
}
|
|
1389
|
-
console.log(`[SYNC] templates synced to ${toRepoRelativePath(cwd, tplDestDir)}`);
|
|
1390
|
-
return;
|
|
1336
|
+
function removeRuntimeTemplateCopies(cwd, dryRun) {
|
|
1337
|
+
const runtimeTemplates = join(cwd, AGENT_ROOT_DIR, "templates");
|
|
1338
|
+
const legacyTemplates = join(cwd, LEGACY_TEMPLATE_DIR);
|
|
1339
|
+
for (const target of [runtimeTemplates, legacyTemplates]) {
|
|
1340
|
+
if (!existsSync(target)) continue;
|
|
1341
|
+
if (!dryRun) rmSync(target, { recursive: true, force: true });
|
|
1342
|
+
console.log(`[CLEANUP] removed runtime template copy: ${toRepoRelativePath(cwd, target)}`);
|
|
1391
1343
|
}
|
|
1392
|
-
console.log(`[SYNC] templates synced to ${toRepoRelativePath(cwd, tplDestDir)} (dry-run mode)`);
|
|
1393
1344
|
}
|
|
1394
1345
|
|
|
1395
1346
|
function syncSkillTemplates(cwd, bundleRoot, dryRun) {
|
|
@@ -1492,11 +1443,6 @@ function canonicalizeGeneratedCommandReferences(cwd, bundleRoot, dryRun) {
|
|
|
1492
1443
|
for (const target of targets) {
|
|
1493
1444
|
canonicalizeTextFile(target, cwd, bundleRoot, dryRun, "legacy command reference");
|
|
1494
1445
|
}
|
|
1495
|
-
|
|
1496
|
-
const templateDir = join(cwd, AGENT_ROOT_DIR, TEMPLATE_SUBDIR);
|
|
1497
|
-
walkMdFiles(templateDir, (absPath) => {
|
|
1498
|
-
canonicalizeTextFile(absPath, cwd, bundleRoot, dryRun, "legacy template command reference");
|
|
1499
|
-
});
|
|
1500
1446
|
}
|
|
1501
1447
|
|
|
1502
1448
|
/**
|
|
@@ -1617,7 +1563,7 @@ This pointer is a thin bootstrap, not a second workflow contract.
|
|
|
1617
1563
|
2. Then read local \`PROJECT_RULE.md\` and internally identify applicable DC-* rules.
|
|
1618
1564
|
3. After the core hub is loaded, \`core-rules/AGENTS.md\` is the DeukAgentFlow SSoT for TDW, RAG, silence, scope, and verification.
|
|
1619
1565
|
|
|
1620
|
-
Do not print pointer/core metadata, version, DC-* lists, progress commentary, or interim summaries. Before the final answer, only the single required ticket-start line, blockers, explicit user-requested output, or explicit command results may appear. During approved_execution, command_running, or search_running, stay silent unless the user explicitly asks for live narration or a blocker/user decision must be surfaced.
|
|
1566
|
+
Do not print pointer/core metadata, version, DC-* lists, progress commentary, or interim summaries. When approval is pending, make the first visible assistant line exactly the clickable \`Ticket start: [<id>](/absolute/path/to/ticket.md)\` line. Before the final answer, only the single required ticket-start line, blockers, explicit user-requested output, or explicit command results may appear. During approved_execution, command_running, or search_running, stay silent unless the user explicitly asks for live narration or a blocker/user decision must be surfaced.
|
|
1621
1567
|
`;
|
|
1622
1568
|
|
|
1623
1569
|
if (spoke.format === "mdc") {
|
|
@@ -1700,7 +1646,7 @@ export async function runInit(opts, bundleRoot) {
|
|
|
1700
1646
|
);
|
|
1701
1647
|
}
|
|
1702
1648
|
|
|
1703
|
-
if (opts
|
|
1649
|
+
if (shouldEnsureSourceModeCommandShims(opts, savedConfig)) {
|
|
1704
1650
|
const sourceShimResult = ensureSourceModeCommandShims(bundleRoot, { dryRun: opts.dryRun });
|
|
1705
1651
|
if (sourceShimResult.created.length > 0) {
|
|
1706
1652
|
console.log(`[SOURCE MODE] Installed command shims in ${sourceShimResult.binDir}: ${sourceShimResult.created.join(", ")}`);
|
|
@@ -1713,13 +1659,13 @@ export async function runInit(opts, bundleRoot) {
|
|
|
1713
1659
|
// 0. Sync Global Codex Instructions
|
|
1714
1660
|
syncGlobalCodexInstructions(opts.dryRun);
|
|
1715
1661
|
|
|
1716
|
-
// 0.1
|
|
1662
|
+
// 0.1 Deuk AgentContext MCP memory status check
|
|
1717
1663
|
const mcpActive = await isMcpActive(opts.cwd);
|
|
1718
|
-
console.log(`\n[
|
|
1664
|
+
console.log(`\n[MEMORY] Deuk AgentContext MCP: ${mcpActive ? "\x1b[32mDETECTED\x1b[0m" : "\x1b[33mNOT CONFIGURED\x1b[0m"}`);
|
|
1719
1665
|
if (mcpActive) {
|
|
1720
|
-
console.log(`[
|
|
1666
|
+
console.log(`[MEMORY] Historical ticket/rule/code recall can be used when a task needs prior context.\n`);
|
|
1721
1667
|
} else {
|
|
1722
|
-
console.log(`[
|
|
1668
|
+
console.log(`[MEMORY] Flow will run local-first. init does not install or register Deuk AgentContext MCP.\n`);
|
|
1723
1669
|
}
|
|
1724
1670
|
|
|
1725
1671
|
const submodules = discoverAllWorkspaces(opts.cwd, ignoreDirs);
|
|
@@ -1777,8 +1723,8 @@ async function initSingleWorkspace(subCwd, opts, bundleRoot, selectedTools) {
|
|
|
1777
1723
|
}
|
|
1778
1724
|
}
|
|
1779
1725
|
|
|
1780
|
-
// 5.
|
|
1781
|
-
|
|
1726
|
+
// 5. Runtime template cleanup; package templates/ is the SSoT.
|
|
1727
|
+
removeRuntimeTemplateCopies(subCwd, opts.dryRun);
|
|
1782
1728
|
syncSkillTemplates(subCwd, bundleRoot, opts.dryRun);
|
|
1783
1729
|
canonicalizeGeneratedCommandReferences(subCwd, bundleRoot, opts.dryRun);
|
|
1784
1730
|
canonicalizeRecursiveInitSurfaces(subCwd, bundleRoot, opts.dryRun);
|
|
@@ -1794,6 +1740,6 @@ export function runMerge(opts, bundleRoot) {
|
|
|
1794
1740
|
);
|
|
1795
1741
|
}
|
|
1796
1742
|
|
|
1797
|
-
|
|
1743
|
+
removeRuntimeTemplateCopies(opts.cwd, opts.dryRun);
|
|
1798
1744
|
syncSkillTemplates(opts.cwd, bundleRoot, opts.dryRun);
|
|
1799
1745
|
}
|
package/scripts/cli-prompts.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createInterface } from "readline";
|
|
2
2
|
import { existsSync, readFileSync } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
|
-
import { STACKS, AGENT_TOOLS, DOC_LANGUAGE_CHOICES, resolveDocsLanguage, normalizeWorkflowMode, WORKFLOW_MODE_EXECUTE, WORKFLOW_MODE_PLAN } from "./cli-utils.mjs";
|
|
4
|
+
import { WORKSPACE_KINDS, STACKS, AGENT_TOOLS, DOC_LANGUAGE_CHOICES, resolveDocsLanguage, normalizeWorkflowMode, WORKFLOW_MODE_EXECUTE, WORKFLOW_MODE_PLAN } from "./cli-utils.mjs";
|
|
5
5
|
|
|
6
6
|
export async function ask(rl, question) {
|
|
7
7
|
return new Promise((resolve) => rl.question(question, resolve));
|
|
@@ -46,8 +46,13 @@ export async function runInteractive(opts) {
|
|
|
46
46
|
try {
|
|
47
47
|
console.log("\nDeukAgentFlow init — let's configure your workspace.\n");
|
|
48
48
|
|
|
49
|
-
const
|
|
50
|
-
const
|
|
49
|
+
const workspaceKind = await selectOne(rl, "What kind of workspace is this?", WORKSPACE_KINDS);
|
|
50
|
+
const stack = await selectOne(rl, "What technical surface should tickets assume?", STACKS);
|
|
51
|
+
const tools = await selectMany(
|
|
52
|
+
rl,
|
|
53
|
+
"Which AI clients should receive thin workflow pointers? You can rerun init later to add more.",
|
|
54
|
+
AGENT_TOOLS
|
|
55
|
+
);
|
|
51
56
|
const docsLanguage = await selectOne(rl, "What document language should generated tickets/plans use?", DOC_LANGUAGE_CHOICES);
|
|
52
57
|
const workflowMode = opts.workflowMode
|
|
53
58
|
? normalizeWorkflowMode(opts.workflowMode)
|
|
@@ -75,28 +80,40 @@ export async function runInteractive(opts) {
|
|
|
75
80
|
}
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
const
|
|
83
|
+
const contextMcp = opts.contextMcp || await selectOne(rl, "Deuk AgentContext memory MCP connection?", [
|
|
84
|
+
{ label: "Skip for now (Flow works without memory MCP)", value: "skip" },
|
|
85
|
+
{ label: "Already configured in this client/workspace", value: "configured" },
|
|
86
|
+
{ label: "Plan to configure later", value: "later" },
|
|
87
|
+
]);
|
|
88
|
+
const remoteSync = opts.remoteSync !== undefined
|
|
89
|
+
? opts.remoteSync
|
|
90
|
+
: (await askYesNo("Configure an external workflow sync endpoint? (Experimental; not Deuk AgentContext MCP)", false));
|
|
79
91
|
let pipelineUrl = opts.pipelineUrl || "";
|
|
80
92
|
if (remoteSync && !pipelineUrl) {
|
|
81
|
-
pipelineUrl = (await ask(rl, "Enter
|
|
93
|
+
pipelineUrl = (await ask(rl, "Enter external workflow sync endpoint URL: ")).trim();
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
opts.agents = opts.agents ?? agentsDefault;
|
|
97
|
+
opts.workspaceKind = workspaceKind;
|
|
98
|
+
opts.kind = workspaceKind;
|
|
85
99
|
opts.stack = stack;
|
|
86
100
|
opts.agentTools = tools;
|
|
87
101
|
opts.docsLanguage = resolveDocsLanguage(docsLanguage);
|
|
88
102
|
opts.workflowMode = normalizeWorkflowMode(opts.workflowMode || workflowMode);
|
|
89
103
|
opts.shareTickets = shareTickets;
|
|
104
|
+
opts.contextMcp = contextMcp;
|
|
90
105
|
opts.remoteSync = remoteSync;
|
|
91
106
|
opts.pipelineUrl = pipelineUrl;
|
|
92
107
|
|
|
93
|
-
console.log("\n
|
|
94
|
-
console.log("
|
|
108
|
+
console.log("\n Workspace Kind: " + workspaceKind);
|
|
109
|
+
console.log(" Technical Surface: " + stack);
|
|
110
|
+
console.log(" AI Clients: " + (tools.join(", ") || "none"));
|
|
95
111
|
console.log(" Docs Language: " + opts.docsLanguage);
|
|
96
112
|
console.log(" Workflow Mode: " + opts.workflowMode);
|
|
97
113
|
console.log(" Share Tickets: " + (opts.shareTickets ? "Yes (Shared)" : "No (Private)"));
|
|
98
|
-
console.log("
|
|
99
|
-
|
|
114
|
+
console.log(" Deuk AgentContext MCP: " + opts.contextMcp);
|
|
115
|
+
console.log(" External Sync: " + (opts.remoteSync ? "Enabled" : "Disabled"));
|
|
116
|
+
if (opts.remoteSync) console.log(" Sync URL: " + opts.pipelineUrl);
|
|
100
117
|
console.log(" AGENTS: " + opts.agents + "\n");
|
|
101
118
|
} finally {
|
|
102
119
|
rl.close();
|
|
@@ -202,6 +202,22 @@ function parseMarkdownH2Sections(content) {
|
|
|
202
202
|
return sections;
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
const PHASE1_HEADING_ALIASES = new Map([
|
|
206
|
+
["apc", "Agent Permission Contract (APC)"],
|
|
207
|
+
["agent permission contract", "Agent Permission Contract (APC)"],
|
|
208
|
+
["agent permission contract (apc)", "Agent Permission Contract (APC)"],
|
|
209
|
+
...REQUIRED_PHASE1_DATA_SECTIONS.map(section => [section.toLowerCase(), section])
|
|
210
|
+
]);
|
|
211
|
+
|
|
212
|
+
function normalizePhase1PlanBodyHeadings(body) {
|
|
213
|
+
return String(body || "").split("\n").map((line) => {
|
|
214
|
+
const match = line.match(/^\s{0,3}#{1,6}\s+(.+?)\s*#*\s*$/);
|
|
215
|
+
if (!match) return line;
|
|
216
|
+
const normalized = PHASE1_HEADING_ALIASES.get(match[1].trim().toLowerCase());
|
|
217
|
+
return normalized ? `## ${normalized}` : line;
|
|
218
|
+
}).join("\n");
|
|
219
|
+
}
|
|
220
|
+
|
|
205
221
|
function buildClaimCoverageSummary(claimTerms, sectionText) {
|
|
206
222
|
const haystack = String(sectionText || "").toLowerCase();
|
|
207
223
|
const normalized = haystack.replace(/\s+/g, " ");
|
|
@@ -293,7 +309,10 @@ function buildPlanBodyRequiredMessage(reasons = []) {
|
|
|
293
309
|
return [
|
|
294
310
|
"[VALIDATION FAILED] ticket create requires a filled Phase 1 plan body with actual data.",
|
|
295
311
|
`Missing or incomplete: ${uniqueReasons.join(", ")}`,
|
|
296
|
-
"Use the
|
|
312
|
+
"Use the AGENTS.md self-serve recipe: do not ask the user, call help, or search for templates.",
|
|
313
|
+
"Run with stdin: `deuk-agent-flow ticket create --topic <topic> --summary \"<summary>\" --plan-body-file - --non-interactive`.",
|
|
314
|
+
"Use these exact H2 headings: `## Agent Permission Contract (APC)`, `## Compact Plan`, `## Problem Analysis`, `## Source Observations`, `## Cause Hypotheses`, `## Improvement Direction`, `## Audit Evidence`.",
|
|
315
|
+
"Under `## Agent Permission Contract (APC)`, include `[BOUNDARY]`, `[CONTRACT]`, and `[PATCH PLAN]`.",
|
|
297
316
|
"If a scratch plan-body file is unavoidable, keep it outside the workspace, delete it after create, and never present it as a ticket artifact.",
|
|
298
317
|
"Do not rely on template defaults or auto-generated filler text for Phase 1 ticket content."
|
|
299
318
|
].join("\n");
|
|
@@ -454,14 +473,10 @@ function printUsageReminder(cwd, opts = {}) {
|
|
|
454
473
|
}
|
|
455
474
|
}
|
|
456
475
|
|
|
457
|
-
function printCreateApprovalGate(ticketId, opts = {}) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
console.log("Approval pending: share the ticket-start line in chat, review the durable ticket body, and stop here until the user explicitly approves.");
|
|
464
|
-
console.log(`After approval: deuk-agent-flow ticket guard --topic ${ticketId} --ticket-started --ticket-reviewed --approval approved`);
|
|
476
|
+
function printCreateApprovalGate(ticketId, opts = {}, scopeSummary = "") {
|
|
477
|
+
void scopeSummary;
|
|
478
|
+
console.log("조용히 작업");
|
|
479
|
+
console.log(`Guard topic: ${ticketId}`);
|
|
465
480
|
}
|
|
466
481
|
|
|
467
482
|
function formatTicketStartLine(ticketId, absPath) {
|
|
@@ -1191,7 +1206,7 @@ export async function runTicketCreate(opts) {
|
|
|
1191
1206
|
let finalTopic = topic;
|
|
1192
1207
|
|
|
1193
1208
|
if (typeof opts.planBody === "string" && opts.planBody.trim()) {
|
|
1194
|
-
parsedPlan = parsePlan("inline-plan-body.md", opts.planBody);
|
|
1209
|
+
parsedPlan = parsePlan("inline-plan-body.md", normalizePhase1PlanBodyHeadings(opts.planBody));
|
|
1195
1210
|
|
|
1196
1211
|
finalTitle = opts.topic || parsedPlan.title || title;
|
|
1197
1212
|
finalTopic = requireNonEmptySlug(finalTitle, "ticket topic");
|
|
@@ -1388,10 +1403,12 @@ export async function runTicketCreate(opts) {
|
|
|
1388
1403
|
}
|
|
1389
1404
|
}
|
|
1390
1405
|
|
|
1391
|
-
|
|
1406
|
+
if (!isCompactTicketOutput(opts)) {
|
|
1407
|
+
console.log(`${opts.dryRun ? "Ticket would be created" : "Ticket created"}: ${toFileUri(abs)}`);
|
|
1408
|
+
}
|
|
1392
1409
|
printTicketStartLine(ticketId, abs);
|
|
1393
1410
|
if (!opts.dryRun) {
|
|
1394
|
-
printCreateApprovalGate(ticketId, opts);
|
|
1411
|
+
printCreateApprovalGate(ticketId, opts, summary);
|
|
1395
1412
|
}
|
|
1396
1413
|
printUsageReminder(opts.cwd, opts);
|
|
1397
1414
|
if (!opts.dryRun) {
|
|
@@ -1770,21 +1787,38 @@ export async function runTicketClose(opts) {
|
|
|
1770
1787
|
|
|
1771
1788
|
try {
|
|
1772
1789
|
const entry = updateTicketEntryStatus(opts.cwd, opts);
|
|
1773
|
-
const { meta } = parseFrontMatter(previousBody);
|
|
1774
1790
|
runTicketLifecycleQualityGate(opts.cwd, {
|
|
1775
1791
|
ticketAbsPath: abs,
|
|
1776
1792
|
context: `ticket close ${entry.topic}`
|
|
1777
1793
|
});
|
|
1778
|
-
|
|
1794
|
+
|
|
1795
|
+
let archiveResult = null;
|
|
1796
|
+
if (String(opts.status || "").toLowerCase() === "closed") {
|
|
1797
|
+
const ticketDir = detectConsumerTicketDir(opts.cwd);
|
|
1798
|
+
const currentIndex = readTicketIndexJson(opts.cwd);
|
|
1799
|
+
archiveResult = archiveTicketEntry({
|
|
1800
|
+
cwd: opts.cwd,
|
|
1801
|
+
ticketDir,
|
|
1802
|
+
indexJson: currentIndex,
|
|
1803
|
+
found: entry,
|
|
1804
|
+
opts,
|
|
1805
|
+
report: null
|
|
1806
|
+
});
|
|
1807
|
+
writeTicketIndexJson(opts.cwd, currentIndex, opts);
|
|
1808
|
+
syncActiveTicketId(opts.cwd);
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
const finalPath = archiveResult?.path || entry.path;
|
|
1779
1812
|
appendTelemetryEvent(opts.cwd, {
|
|
1780
1813
|
event: "ticket_closed",
|
|
1781
1814
|
action: "ticket-close",
|
|
1782
1815
|
ticket: entry.id || entry.topic,
|
|
1783
|
-
file:
|
|
1816
|
+
file: finalPath,
|
|
1784
1817
|
phase: 4,
|
|
1785
1818
|
status: opts.status
|
|
1786
1819
|
});
|
|
1787
|
-
console.log(`ticket: ${opts.status} -> ${entry.topic} (${
|
|
1820
|
+
console.log(`ticket: ${opts.status} -> ${entry.topic} (${finalPath})`);
|
|
1821
|
+
return archiveResult || { status: opts.status, ticket: entry.topic, path: finalPath };
|
|
1788
1822
|
} catch (err) {
|
|
1789
1823
|
rollbackTicketLifecycleArtifacts(opts.cwd, previousIndex, previousBody, abs, opts);
|
|
1790
1824
|
throw err;
|
package/scripts/cli-utils.mjs
CHANGED
|
@@ -11,7 +11,6 @@ export function toFileUri(absPath) {
|
|
|
11
11
|
|
|
12
12
|
export const AGENT_ROOT_DIR = ".deuk-agent";
|
|
13
13
|
export const TICKET_SUBDIR = "tickets";
|
|
14
|
-
export const TEMPLATE_SUBDIR = "templates";
|
|
15
14
|
export const RULES_SUBDIR = "rules";
|
|
16
15
|
export const WORKFLOW_MODE_PLAN = "plan";
|
|
17
16
|
export const WORKFLOW_MODE_EXECUTE = "execute";
|
|
@@ -85,21 +84,34 @@ export function normalizeTicketGroup(rawGroup, fallback = "sub") {
|
|
|
85
84
|
export const INIT_CONFIG_FILENAME = `${AGENT_ROOT_DIR}/config.json`;
|
|
86
85
|
export const INIT_CONFIG_VERSION = 1;
|
|
87
86
|
|
|
87
|
+
export const WORKSPACE_KINDS = [
|
|
88
|
+
{ label: "Product planning / strategy / specs", value: "planning" },
|
|
89
|
+
{ label: "Software engineering / coding", value: "coding" },
|
|
90
|
+
{ label: "Systems engineering / operations", value: "systems" },
|
|
91
|
+
{ label: "Research / analysis / knowledge work", value: "research" },
|
|
92
|
+
{ label: "Mixed team workspace", value: "mixed" },
|
|
93
|
+
{ label: "Other / decide later", value: "other" },
|
|
94
|
+
];
|
|
95
|
+
|
|
88
96
|
export const STACKS = [
|
|
89
|
-
{ label: "
|
|
90
|
-
{ label: "
|
|
91
|
-
{ label: "
|
|
92
|
-
{ label: "
|
|
93
|
-
{ label: "
|
|
97
|
+
{ label: "Not primarily a code repo", value: "none" },
|
|
98
|
+
{ label: "Web / product app", value: "web" },
|
|
99
|
+
{ label: "Backend / service / API", value: "backend" },
|
|
100
|
+
{ label: "Unity / game / simulation", value: "unity" },
|
|
101
|
+
{ label: "Data / ML / analysis", value: "data" },
|
|
102
|
+
{ label: "Infrastructure / platform / SRE", value: "infra" },
|
|
103
|
+
{ label: "Hybrid / multiple stacks", value: "hybrid" },
|
|
94
104
|
{ label: "Other / skip", value: "other" },
|
|
95
105
|
];
|
|
96
106
|
|
|
97
107
|
export const AGENT_TOOLS = [
|
|
98
|
-
{ label: "
|
|
108
|
+
{ label: "OpenAI Codex", value: "codex" },
|
|
99
109
|
{ label: "GitHub Copilot", value: "copilot" },
|
|
100
|
-
{ label: "
|
|
110
|
+
{ label: "Claude Code", value: "claude" },
|
|
111
|
+
{ label: "Cursor", value: "cursor" },
|
|
101
112
|
{ label: "Gemini / Antigravity", value: "gemini" },
|
|
102
|
-
{ label: "
|
|
113
|
+
{ label: "Windsurf", value: "windsurf" },
|
|
114
|
+
{ label: "JetBrains AI Assistant", value: "jetbrains" },
|
|
103
115
|
];
|
|
104
116
|
|
|
105
117
|
export const SPOKE_REGISTRY = [
|
|
@@ -190,10 +202,13 @@ export function writeInitConfig(cwd, opts) {
|
|
|
190
202
|
agentsMode: opts.agents ?? existing.agentsMode ?? "inject",
|
|
191
203
|
workflowMode,
|
|
192
204
|
approvalState: workflowMode === WORKFLOW_MODE_EXECUTE ? "approved" : "pending",
|
|
205
|
+
workspaceKind: opts.workspaceKind ?? opts.kind ?? existing.workspaceKind ?? existing.kind,
|
|
193
206
|
stack: opts.stack ?? existing.stack,
|
|
207
|
+
kind: opts.workspaceKind ?? opts.kind ?? existing.workspaceKind ?? existing.kind,
|
|
194
208
|
agentTools: opts.agentTools ?? existing.agentTools,
|
|
195
209
|
docsLanguage: normalizeDocsLanguage(opts.docsLanguage ?? existing.docsLanguage ?? "auto"),
|
|
196
210
|
shareTickets: opts.shareTickets ?? existing.shareTickets ?? false,
|
|
211
|
+
contextMcp: opts.contextMcp ?? existing.contextMcp ?? "skip",
|
|
197
212
|
remoteSync: opts.remoteSync ?? existing.remoteSync ?? false,
|
|
198
213
|
pipelineUrl: opts.pipelineUrl,
|
|
199
214
|
ignoreDirs: opts.ignoreDirs || existing.ignoreDirs || DEFAULT_IGNORE_DIRS,
|
|
@@ -467,7 +482,7 @@ export async function checkUpdateNotifier() {
|
|
|
467
482
|
// Only notify when registry version is strictly newer than local (handles local dev symlink case)
|
|
468
483
|
if (data.version && semverLt(currentVersion, data.version)) {
|
|
469
484
|
console.warn(`\n\x1b[33m💡 Update available! ${currentVersion} → ${data.version}\x1b[0m`);
|
|
470
|
-
console.warn(`\x1b[36mRun 'npm install -g deuk-agent-flow'
|
|
485
|
+
console.warn(`\x1b[36mRun 'npm install -g deuk-agent-flow', then 'deuk-agent-flow init' from the repo or workspace root.\x1b[0m\n`);
|
|
471
486
|
}
|
|
472
487
|
}
|
|
473
488
|
} catch(e) {
|
package/scripts/cli.mjs
CHANGED
|
@@ -28,6 +28,10 @@ async function main() {
|
|
|
28
28
|
if (sub === "ticket") {
|
|
29
29
|
const action = rest[0];
|
|
30
30
|
const opts = parseTicketArgs(rest.slice(1));
|
|
31
|
+
if (!action || action === "-h" || action === "--help" || action === "help" || opts.help) {
|
|
32
|
+
printHelp();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
31
35
|
if (action === "create") await runTicketCreate(opts);
|
|
32
36
|
else if (action === "list") await runTicketList(opts);
|
|
33
37
|
else if (action === "use") await runTicketUse(opts);
|
|
@@ -139,10 +143,12 @@ async function main() {
|
|
|
139
143
|
|
|
140
144
|
async function handleInit(opts, saved) {
|
|
141
145
|
if (opts.clean && !opts.dryRun) {
|
|
142
|
-
console.log(`[CLEAN] Removing legacy templates and config...`);
|
|
146
|
+
console.log(`[CLEAN] Removing runtime template copies, legacy templates, and config...`);
|
|
143
147
|
const templatesDir = join(opts.cwd, LEGACY_TEMPLATE_DIR);
|
|
148
|
+
const runtimeTemplatesDir = join(opts.cwd, AGENT_ROOT_DIR, "templates");
|
|
144
149
|
const configFile = join(opts.cwd, LEGACY_CONFIG_FILE);
|
|
145
150
|
if (existsSync(templatesDir)) rmSync(templatesDir, { recursive: true, force: true });
|
|
151
|
+
if (existsSync(runtimeTemplatesDir)) rmSync(runtimeTemplatesDir, { recursive: true, force: true });
|
|
146
152
|
if (existsSync(configFile)) rmSync(configFile, { force: true });
|
|
147
153
|
}
|
|
148
154
|
|
|
@@ -184,6 +190,9 @@ Options:
|
|
|
184
190
|
--tag <id> Custom marker ID (default: deuk-agent-rule)
|
|
185
191
|
--agents <mode> inject | skip | overwrite
|
|
186
192
|
--cursorrules <mode> inject | skip | overwrite
|
|
193
|
+
--workspace-kind <k> planning | coding | systems | research | mixed | other
|
|
194
|
+
--stack <name> none | web | backend | unity | data | infra | hybrid | other
|
|
195
|
+
--context-mcp <mode> skip | configured | later
|
|
187
196
|
--workflow <mode> plan | execute
|
|
188
197
|
--approval <state> pending | approved (alias for workflow)
|
|
189
198
|
--docs-language <lang> auto | ko | en
|
package/scripts/lint-md.mjs
CHANGED
|
@@ -45,20 +45,18 @@ function isPlanReport(relPath) {
|
|
|
45
45
|
return relPath.includes(`${AGENT_ROOT_DIR}/docs/plan/`) && relPath.endsWith("-report.md");
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const src = String(content || "");
|
|
61
|
-
return src.includes("<%=") || src.includes("<%-") || /^id:\s*(module-rule-template|ticket-list-template|ticket-template-ko|ticket-template)\s*$/m.test(src);
|
|
48
|
+
function isTicketPath(repoRoot, absPath) {
|
|
49
|
+
try {
|
|
50
|
+
const relPath = relative(repoRoot, absPath).replace(/\\/g, "/");
|
|
51
|
+
return relPath.startsWith(`${TICKET_DIR_NAME}/`);
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isImplicitUpwardEvidenceLink(target) {
|
|
58
|
+
const normalized = String(target || "").replace(/\\/g, "/");
|
|
59
|
+
return normalized.startsWith("../") && !normalized.includes(`${TICKET_DIR_NAME}/`);
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
function looksLikeYamlFrontmatter(content) {
|
|
@@ -118,10 +116,6 @@ function lintFile(absPath, repoRoot) {
|
|
|
118
116
|
const content = readFileSync(absPath, "utf8");
|
|
119
117
|
const errors = [];
|
|
120
118
|
|
|
121
|
-
if (isLegacyArchivedTemplateArtifact(rel, content)) {
|
|
122
|
-
return errors;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
119
|
const lines = content.split(/\r?\n/);
|
|
126
120
|
lines.forEach((line, idx) => {
|
|
127
121
|
if (/\s+$/.test(line)) {
|
|
@@ -177,7 +171,14 @@ function lintFile(absPath, repoRoot) {
|
|
|
177
171
|
}
|
|
178
172
|
const pathOnly = target.split("#")[0].split("?")[0];
|
|
179
173
|
if (!pathOnly) continue;
|
|
174
|
+
if (isImplicitUpwardEvidenceLink(pathOnly)) continue;
|
|
175
|
+
|
|
180
176
|
const resolved = join(dirname(absPath), pathOnly);
|
|
177
|
+
|
|
178
|
+
if (!isTicketPath(repoRoot, resolved)) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
181
182
|
if (!statExists(resolved)) {
|
|
182
183
|
errors.push(`${rel}: broken relative link -> ${target}`);
|
|
183
184
|
}
|
package/scripts/lint-rules.mjs
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "fs";
|
|
2
2
|
import { join } from "path";
|
|
3
3
|
|
|
4
|
+
function extractSection(rules, heading) {
|
|
5
|
+
const pattern = new RegExp(`(^## ${heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n[\\s\\S]*?)(?=^## |\\z)`, "m");
|
|
6
|
+
return rules.match(pattern)?.[1] || "";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function nonEmptyLineCount(text) {
|
|
10
|
+
return String(text || "").split(/\r?\n/).filter((line) => line.trim()).length;
|
|
11
|
+
}
|
|
12
|
+
|
|
4
13
|
const RULE_CHECKS = [
|
|
5
14
|
{
|
|
6
15
|
code: "DR-KERNEL-01",
|
|
7
16
|
message: "Compact kernel must keep ticket-first and tool-contract invariants at the top of the core rules.",
|
|
8
17
|
test: (rules) => /## Compact Kernel/i.test(rules)
|
|
18
|
+
&& nonEmptyLineCount(extractSection(rules, "Compact Kernel")) <= 10
|
|
9
19
|
&& /Tools own detail/i.test(rules)
|
|
10
20
|
&& /No ticket, no writes/i.test(rules)
|
|
11
21
|
&& /User requirements are ticket-first/i.test(rules)
|
|
@@ -71,6 +71,9 @@ export async function buildDownloadsBadge(opts) {
|
|
|
71
71
|
const canonical = packages.find((pkg) => pkg.package === canonicalPackage) || packages[0];
|
|
72
72
|
const aliases = packages.filter((pkg) => pkg.package !== canonical.package);
|
|
73
73
|
const aliasTotal = aliases.reduce((sum, pkg) => sum + pkg.downloads, 0);
|
|
74
|
+
void canonical;
|
|
75
|
+
void aliasTotal;
|
|
76
|
+
void aliases;
|
|
74
77
|
|
|
75
78
|
return {
|
|
76
79
|
schemaVersion: 1,
|
|
@@ -79,13 +82,6 @@ export async function buildDownloadsBadge(opts) {
|
|
|
79
82
|
color: "2f6fed",
|
|
80
83
|
namedLogo: "npm",
|
|
81
84
|
cacheSeconds: 86400,
|
|
82
|
-
total,
|
|
83
|
-
canonicalPackage: canonical.package,
|
|
84
|
-
canonicalDownloads: canonical.downloads,
|
|
85
|
-
aliasTotal,
|
|
86
|
-
range: opts.range,
|
|
87
|
-
packages,
|
|
88
|
-
aliases,
|
|
89
85
|
};
|
|
90
86
|
}
|
|
91
87
|
|