deuk-agent-flow 4.0.21 → 4.0.36

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 CHANGED
@@ -8,6 +8,38 @@
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ### 수정됨 (Fixed)
12
+
13
+ - **cli:** 티켓 탐색이 상위 워크스페이스를 상속하지 않고 현재 agent-rule 경계에서 멈추도록 수정했습니다.
14
+
15
+ ## [4.0.36] - 2026-05-09
16
+
17
+ ### 수정됨 (Fixed)
18
+
19
+ - **init:** 첫 실행 대화형 설정을 workspace 용도 선택 하나로 줄이고, 나머지는 프로젝트 디렉터리 성격으로 추론하며, Deuk AgentContext MCP 선택지를 숨김 처리하고, 선택 후 먹통으로 설정 완료가 실패하던 흐름을 수정했습니다.
20
+ - **rules:** 최초 티켓 생성/사용 후 클릭 가능한 `Ticket start` 줄이 계속 보이도록 강제해, 승인 요청만 남고 활성 티켓이 숨겨지는 응답을 막았습니다.
21
+
22
+ ## [4.0.35] - 2026-05-09
23
+
24
+ ### 수정됨 (Fixed)
25
+
26
+ - **release:** 공개 패키지의 publish 스크립트가 소스 전용 테스트는 건너뛰고 npm smoke 검증은 유지하도록 수정했습니다.
27
+ - **release:** 공개 커밋 제목이 `sync` 같은 전달 단계가 아니라 공개되는 feature/fix/docs/release 변경 자체를 설명하도록 명확히 했습니다.
28
+ - **release:** 공개 export 실행 시에도 같은 public commit message 가이드를 출력해 공개 기록이 제품 변경 중심으로 남도록 했습니다.
29
+
30
+ ## [4.0.34] - 2026-05-09
31
+
32
+ ### 변경됨 (Changed)
33
+
34
+ - **init:** 첫 실행 질문을 workspace 종류, 기술 표면, AI client pointer, 선택형 Deuk AgentContext MCP memory 기준으로 재구성해 코딩 전용이 아닌 기획/시스템/연구/혼합 workspace도 자연스럽게 설정할 수 있게 했습니다.
35
+ - **docs:** 사용자용 업데이트 안내를 `npm install -g deuk-agent-flow` 이후 `deuk-agent-flow init`만 실행하는 흐름으로 단순화하고, repo 루트와 workspace 루트 갱신 방식을 명확히 했습니다.
36
+
37
+ ### 수정됨 (Fixed)
38
+
39
+ - **templates:** 패키지 `templates/`를 runtime 단일 진실 공급원으로 삼고, init/merge 중 legacy `.deuk-agent/templates` 복사본을 제거하도록 정리했습니다.
40
+ - **ticket:** `ticket create` strict 검증 전에 Phase 1 heading level 실수를 canonical heading으로 정규화하도록 수정했습니다.
41
+ - **release:** 공개 export 대상을 runtime 파일로 좁히고 오래된 tarball, `bundle/`, `node_modules/`, 내부 script, test 같은 공개 트리 찌꺼기를 제거하도록 했습니다.
42
+
11
43
  ## [4.0.21] - 2026-05-08
12
44
 
13
45
  ### 수정됨 (Fixed)
@@ -20,7 +52,7 @@
20
52
  ### 수정됨 (Fixed)
21
53
 
22
54
  - **docs:** 영문/한글 README 상단에 통합 npm 다운로드 배지를 복구하고, 공개 표기는 `deuk-flow` 라벨로 유지했습니다.
23
- - **release:** `docs/badges/`를 공개 미러에도 동기화하도록 보강해 README 다운로드 배지가 OSS sync와 patch 재배포 뒤에도 유지되게 했습니다.
55
+ - **release:** `docs/badges/`를 공개 릴리스 트리에도 복사하도록 보강해 README 다운로드 배지가 public export와 patch 재배포 뒤에도 유지되게 했습니다.
24
56
 
25
57
  ## [4.0.12] - 2026-05-07
26
58
 
@@ -41,7 +73,7 @@
41
73
  ### 수정됨 (Fixed)
42
74
 
43
75
  - npm과 GitHub 첫 화면에서 영어/한국어 README를 바로 오갈 수 있도록 언어 전환 링크를 복구했습니다.
44
- - README 문서 표에는 공개 문서 링크만 남기고, 내부 리서치와 성장 전략 문서는 npm/OSS 공개 표면에서 제외했습니다.
76
+ - README 문서 표에는 공개 문서 링크만 남기고, 내부 리서치와 성장 전략 문서는 npm/Public 공개 표면에서 제외했습니다.
45
77
 
46
78
  ## [3.3.2] - 2026-05-06
47
79
 
@@ -163,7 +195,7 @@
163
195
  ### 수정됨 (Fixed)
164
196
 
165
197
  - **rules:** 전역 `npx` 캐시 이슈(과거 버전 실행 문제)를 우회하기 위해 로컬 최신 스크립트 호출을 강제/권장하도록 수정
166
- - **scripts:** OSS 미러 저장소 동기화 시 잘못 표기되던 URL 예시 로그 메시지 정정
198
+ - **scripts:** Public 미러 저장소 동기화 시 잘못 표기되던 URL 예시 로그 메시지 정정
167
199
 
168
200
  ### 변경됨 (Changed)
169
201
 
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,19 +12,47 @@ 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.36] - 2026-05-09
16
+
17
+ ### Fixed
18
+
19
+ - **init:** reduce first-run interactive setup to the workspace-purpose choice, infer the remaining defaults from the project directory, hide the Deuk AgentContext MCP choice, and avoid the post-choice stall that prevented setup completion.
20
+ - **rules:** require the clickable `Ticket start` line to stay visible after first ticket creation/use, preventing approval-only replies from hiding the active ticket.
21
+
22
+ ## [4.0.35] - 2026-05-09
23
+
24
+ ### Fixed
25
+
26
+ - **release:** make public package publish scripts skip source-only tests while still running the npm smoke check.
27
+ - **release:** require public commit subjects to describe the released feature, fix, docs, or release change instead of using `sync` as the main label.
28
+ - **release:** print the same public commit-message guidance from the export script so public history records product changes rather than transport mechanics.
29
+
30
+ ## [4.0.34] - 2026-05-09
31
+
32
+ ### Changed
33
+
34
+ - **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.
35
+ - **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.
36
+
37
+ ### Fixed
38
+
39
+ - **templates:** made package `templates/` the runtime SSoT and removed legacy `.deuk-agent/templates` copies during init/merge.
40
+ - **ticket:** normalized common Phase 1 heading-level mistakes during `ticket create` before strict template validation.
41
+ - **release:** narrowed public export to runtime files and cleaned stale release-tree artifacts such as old tarballs, `bundle/`, `node_modules/`, internal scripts, and tests.
42
+
13
43
  ## [4.0.21] - 2026-05-08
14
44
 
15
45
  ### Fixed
16
46
 
17
47
  - **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.
18
- - **release:** synced `docs/badges/npm-downloads.json` into the public mirror and removed internal-only package payload leakage from the npm release surface.
48
+ - **release:** copied `docs/badges/npm-downloads.json` into the public release tree and removed internal-only package payload leakage from the npm release surface.
19
49
 
20
50
  ## [4.0.20] - 2026-05-08
21
51
 
22
52
  ### Fixed
23
53
 
24
54
  - **docs:** restored the combined npm downloads badge to the top of the English and Korean README surfaces while keeping the public `deuk-flow` label.
25
- - **release:** synced `docs/badges/` into the public mirror so the README downloads badge survives OSS sync and patch republish flow.
55
+ - **release:** copied `docs/badges/` into the public release tree so the README downloads badge survives public export and patch republish flow.
26
56
 
27
57
  ## [4.0.12] - 2026-05-07
28
58
 
@@ -43,7 +73,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
43
73
  ### Fixed
44
74
 
45
75
  - Restored the README language switch links so npm and GitHub readers can move between English and Korean documentation from the first screen.
46
- - Published only public documentation links in the README tables while keeping internal research and growth notes out of the npm/OSS surface.
76
+ - Published only public documentation links in the README tables while keeping internal research and growth notes out of the npm/Public surface.
47
77
 
48
78
  ## [3.3.2] - 2026-05-06
49
79
 
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.0</h1>
5
+ <h1>Deuk Agent Flow v4.0.36</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 코딩 에이전트를 위한 레포 소유 워크플로우 계층입니다. Codex, Copilot, Cursor, Claude Code, Gemini, Windsurf, 그리고 다음에 쓰게 될 에이전트까지 같은 티켓 흐름으로 들어오게 만듭니다.
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, MCP도 포인터 구조로 지원하지만, 워크스페이스별 검증을 권장합니다. MCP 서버 등록은 `init`에 딸려 들어가지 않고 별도로 설정합니다.
69
+ > v4.0.36은 에이전트 기반 리포지토리에 배포해 사용할 수 있는 상태입니다. 대화형 `init`은 이제 workspace 용도만 묻고, 나머지 설정은 프로젝트 디렉터리 성격으로 추론하며, Deuk AgentContext MCP 선택지는 첫 설정에서 숨김 처리하고, 이전의 다중 질문/먹통 완료 실패를 피합니다. 티켓 생성/사용 뒤에도 승인 요청 전에 클릭 가능한 `Ticket start` 줄이 계속 보이도록 했습니다. 현재는 **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
 
@@ -138,9 +138,11 @@ npm install -g deuk-agent-flow
138
138
  deuk-agent-flow init
139
139
  ```
140
140
 
141
+ 대화형 init의 선택지는 workspace 용도 질문 하나로 끝납니다. 문서 언어, workflow mode, ticket 공유, agent pointer, MCP memory 기본값은 프로젝트 디렉터리 성격으로 추론하거나 private 기본값으로 처리합니다.
142
+
141
143
  이후 일상 작업은 명령을 직접 치기보다 에이전트에게 짧게 말합니다. 예: "진행", "다음", "원인 다시 파악".
142
144
 
143
- 여러 프로젝트를 `~/workspace` 아래에서 관리한다면 각 프로젝트 루트에서 `deuk-agent-flow init`을 실행합니다. workspace 루트는 공통 포인터 역할을 있지만, 실제 작업 티켓과 로컬 규칙은 보통 프로젝트의 `.deuk-agent/`와 `PROJECT_RULE.md`가 소유합니다.
145
+ 단일 저장소라면 해당 저장소 루트에서 `deuk-agent-flow init`을 실행합니다. 여러 DeukAgentFlow 프로젝트를 포함한 루트 워크스페이스라면 워크스페이스 루트에서 같은 명령을 실행합니다. `init`은 루트 포인터와 자체 `PROJECT_RULE.md` / `.deuk-agent/` 상태를 가진 하위 워크스페이스를 함께 갱신합니다.
144
146
 
145
147
  이렇게 사용하면 효과가 극대화됩니다. workspace 루트는 공통 진입점, 각 프로젝트 루트는 독립 티켓/규칙/검증 단위로 나누고, 중첩 서버나 앱은 필요할 때 별도 프로젝트로 초기화하세요.
146
148
 
@@ -156,13 +158,28 @@ npm install -g deuk-agent-flow
156
158
  deuk-agent-flow init
157
159
  ```
158
160
 
161
+ 기존 저장소에 새 버전을 반영할 때는 먼저 글로벌 패키지를 업데이트한 뒤 init을 다시 실행합니다.
162
+
163
+ ```bash
164
+ npm install -g deuk-agent-flow
165
+ deuk-agent-flow init
166
+ ```
167
+
168
+ `init`은 현재 설치된 패키지의 규칙을 다시 적용하고 legacy/runtime template copy를 제거합니다. 글로벌 npm 패키지 업데이트 자체를 자동으로 수행하지는 않습니다.
169
+
170
+ 단일 저장소라면 해당 저장소 루트에서 `deuk-agent-flow init`을 실행합니다.
171
+
172
+ 여러 DeukAgentFlow 프로젝트를 포함한 루트 워크스페이스라면 그 워크스페이스 루트에서 같은 명령을 실행합니다. `init`은 루트 포인터와 자체 `PROJECT_RULE.md` / `.deuk-agent/` 상태를 가진 하위 워크스페이스를 함께 갱신하므로, 일반 사용자도 새 패키지 버전을 설치한 뒤 자신의 개인 워크스페이스 루트에서 AI agent rule을 갱신할 수 있습니다.
173
+
174
+ 사용하는 AI client 선택은 한 번으로 고정되지 않습니다. 나중에 다른 client를 쓰게 되면 `deuk-agent-flow init`을 다시 실행하고 추가할 AI client를 선택하면 됩니다.
175
+
159
176
  ### 2. 로컬 소스 개발 (메인테이너/파워 유저)
160
- v3.0은 **Global CLI Proxy**를 도입했습니다. `DeukAgentFlow` 워크스페이스 내부에서 개발 중이라면, 글로벌 명령이 자동으로 로컬 소스로 실행을 위임합니다.
177
+ 글로벌 명령은 기본적으로 설치된 패키지를 실행합니다. 다른 프로젝트 디렉터리에서 로컬 checkout 소스를 실행해야 하는 개발자는 명시적으로 로컬 소스 라우팅을 켜야 합니다.
161
178
 
162
179
  ```bash
163
180
  cd ~/workspace/DeukAgentFlow
164
181
  sudo npm link
165
- deuk-agent-flow init # 자동으로 로컬 scripts/cli.mjs로 라우팅됨
182
+ DEUK_AGENT_FLOW_USE_LOCAL=1 deuk-agent-flow init # 로컬 scripts/cli.mjs로 라우팅됨
166
183
  ```
167
184
 
168
185
  Codex나 Copilot을 주로 사용한다면 이 구성이 일상 운영에 가장 적합합니다. 현재는 이 두 환경에서 Hub-Spoke와 티켓 기반 워크플로우가 가장 부드럽게 동작합니다.
@@ -207,7 +224,7 @@ npm run badge:downloads
207
224
 
208
225
  워크플로우는 **티켓 기반 실행 계약(Ticket-Driven Execution Contract)**에 의해 통제됩니다.
209
226
 
210
- 1. **스캐폴딩 (Scaffolding)**: `init` 명령어가 프로젝트의 성격을 파악하고 `.deuk-agent/templates/`와 `AGENTS.md` (혹은 `PROJECT_RULE.md` 포인터)를 자동 배치합니다.
227
+ 1. **스캐폴딩 (Scaffolding)**: `init` 명령어가 `AGENTS.md`와 `PROJECT_RULE.md` 같은 로컬 포인터를 배치합니다. 런타임 템플릿은 `.deuk-agent/templates/`가 아니라 패키지의 `templates/`를 단일 진실 공급원으로 사용합니다.
211
228
  2. **티켓팅 (Plan Phase)**: 사용자가 짧게 지시하면 에이전트가 맥락을 읽고 내부 작업 지시서를 생성합니다. 이때 에이전트는 Plan Mode로 동작하며 코드를 수정할 수 없고 계획 수립에만 집중합니다.
212
229
  3. **실행 (Execute Phase)**: 사용자의 승인을 받은 후, 에이전트는 **타겟 서브모듈**에 고정되어 실질적인 코드 작성을 수행합니다. MCP Soft Gate가 인가되지 않은 파일의 수정을 감시합니다.
213
230
  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.0</h1>
5
+ <h1>Deuk Agent Flow v4.0.36</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 coding agents: Codex, Copilot, Cursor, Claude Code, Gemini, Windsurf, or the next agent you adopt can enter the same ticketed flow.
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, and MCP remain supported through pointer-style integration, but they should be validated per workspace before rollout. MCP server registration is separate from `init`.
69
+ > v4.0.36 is deployment-ready for agent-driven repositories. Interactive `init` now asks only the workspace purpose, infers the remaining setup from the project directory, hides the Deuk AgentContext MCP choice during first setup, and completes without the earlier multi-prompt stall. Ticket creation/use also keeps the clickable `Ticket start` line visible before approval is requested. 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,34 @@ 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
+ Interactive init now ends its choices at the workspace purpose prompt. Document language, workflow mode, ticket sharing, agent pointers, and MCP memory defaults are inferred from the project directory or kept private by default.
143
+
144
+ After that, day-to-day work starts through plain agent requests, not memorized commands. Say things like "continue", "next", or "inspect the cause".
145
+
146
+ 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.
147
+
148
+ For practical rollout details, see [docs/usage-guide.ko.md](docs/usage-guide.ko.md).
149
+
129
150
  ## 🛠️ Installation & Setup
130
151
 
131
152
  ### 1. Global Installation (Standard User)
@@ -136,17 +157,30 @@ npm install -g deuk-agent-flow
136
157
  deuk-agent-flow init
137
158
  ```
138
159
 
139
- If you manage many repos under one workspace, run `deuk-agent-flow init` in each project root that owns its own rules and tickets. The workspace root can act as a shared pointer, but day-to-day work usually belongs to each project's `PROJECT_RULE.md` and `.deuk-agent/`.
160
+ To apply a newly installed version to an existing repo, update the global package first and then rerun init:
161
+
162
+ ```bash
163
+ npm install -g deuk-agent-flow
164
+ deuk-agent-flow init
165
+ ```
166
+
167
+ `init` reapplies the currently installed package rules and removes legacy runtime template copies. It does not update the global npm package by itself.
168
+
169
+ For a single repo, run `deuk-agent-flow init` from that repo root.
170
+
171
+ 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.
172
+
173
+ 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
174
 
141
175
  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
176
 
143
177
  ### 2. Local Source Development (Maintainer/Power User)
144
- v3.0 introduces a **Global CLI Proxy**. If you are developing inside the `DeukAgentFlow` workspace, the global command will automatically delegate execution to your local source.
178
+ 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
179
 
146
180
  ```bash
147
181
  cd ~/workspace/DeukAgentFlow
148
182
  sudo npm link
149
- deuk-agent-flow init # Routes to local scripts/cli.mjs automatically
183
+ DEUK_AGENT_FLOW_USE_LOCAL=1 deuk-agent-flow init # Routes to local scripts/cli.mjs
150
184
  ```
151
185
 
152
186
  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 +225,7 @@ The badge should display `deuk-flow` while still summing downloads from both `de
191
225
 
192
226
  The workflow is governed by a **Ticket-Driven Execution Contract**.
193
227
 
194
- 1. **Scaffolding**: `init` deploys `.deuk-agent/templates/` and `AGENTS.md` (or local pointers like `PROJECT_RULE.md`).
228
+ 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
229
  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
230
  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
231
  4. **Verification**: The agent performs a side-effect audit and convention (e.g., DC-DUP) check before closure.
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * Global Proxy for DeukAgentFlow CLI
4
- * Routes to the local workspace source when present so global installs and
5
- * temporary npx packages do not shadow active local development.
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 wsRoot = findWorkspaceRoot(process.cwd());
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")
@@ -1,34 +1,19 @@
1
1
  ---
2
- version: 65
3
- changelog: "v65: Collapse compact ticket-selection CLI output and running commentary surfaces to a single ticket-start line or silence."
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: before file changes, select or create one active ticket, relay the CLI-provided ticket-start line in chat, reopen and review the durable ticket body, wait for explicit user approval, complete Phase 1 in that ticket, run `deuk-agent-flow ticket guard --topic <id> --ticket-started --ticket-reviewed --approval approved` successfully, and call `set_workflow_context(project, ticket_id, phase)`.
12
- - User requirements are ticket-first: every user request that implies investigation, change, verification, or judgment must first be represented in a ticket containing cause, analysis, and design/approach before work continues.
13
- - Existing-ticket close actions are not new-ticket triggers: when the user asks to commit, report, archive, or close work that is already inside the approved active ticket scope, treat it as that ticket's Phase 4 close action unless the user also adds new scope.
14
- - User approval or correction creates another ticket update loop: record the approval/correction in the ticket, re-check scope and plan, then continue only through `deuk-agent-flow ticket guard --ticket-started --ticket-reviewed --approval approved`.
15
- - Approval is mandatory for every ticket. A ticket may not progress without explicit user approval, regardless of urgency or convenience.
16
- - `Ticket start` exposure is not approval. Phrases such as "진행", "go ahead", or "continue" count only after the agent has shown the exact ticket link, scope, and planned change summary for the active ticket; otherwise treat them as a request for scope clarification and stop.
17
- - Approval review must treat `rank/priority` as a hard gate. If `rank/priority` is missing, the ticket is incomplete and approval must be withheld.
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,15 @@ 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, or non-clickable links must be converted without changing the id.
58
- - Approval-pending contract: stop if approval is pending and state that explicit user approval is required before work; this blocks unapproved work, not concise answers to explicit user-requested output or direct questions about workflow state.
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, hidden, 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, approval-only text such as `승인 바랍니다`, or status text before or instead of it. This keeps the active ticket visible even when earlier tool results are folded, hidden, or compacted. This blocks unapproved work, not concise answers to explicit user-requested output or direct questions about workflow state.
42
+ - Hidden-ticket regression guard: if the latest visible chat line after ticket creation/use is only an approval request or a generic permission prompt, treat the ticket as not exposed. Immediately repeat the clickable `Ticket start` line and compact scope before waiting for approval.
59
43
  - 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
44
  - 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
45
  - 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.
46
+ - 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
47
  - 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
48
  - 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
49
  - 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 +59,28 @@ changelog: "v65: Collapse compact ticket-selection CLI output and running commen
74
59
  - Prefer targeted reads and the shortest valid path that still preserves boot, phase contracts, verification, and close.
75
60
 
76
61
  ## 2. Boot Sequence (run once)
77
-
78
62
  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
63
  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
64
  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
65
 
82
- ### First Ticket One-Shot Recipe
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.
66
+ ### First Ticket Recipe
67
+ 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
68
 
86
- 1. Prepare a filled Phase 1 markdown body before running `ticket create`, preferably via stdin (`--plan-body-file -`) or process substitution. It MUST contain the actual APC, Compact Plan, Problem Analysis, Source Observations, Cause Hypotheses, Improvement Direction, and Audit Evidence content. APC uses `[BOUNDARY]`, `[CONTRACT]`, and `[PATCH PLAN]` markers from the template; validation checks that those marker bodies are not empty, not the markdown heading level.
87
- 2. Run the canonical command:
69
+ 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.
70
+ 2. Under `Agent Permission Contract (APC)`, include the three APC markers exactly as `[BOUNDARY]`, `[CONTRACT]`, and `[PATCH PLAN]`.
71
+ 3. Run this exact stdin command:
88
72
 
89
73
  ```bash
90
74
  deuk-agent-flow ticket create \
91
75
  --topic <topic> \
92
76
  --summary "<concrete summary>" \
93
77
  --plan-body-file - \
94
- --non-interactive
78
+ --non-interactive <<'EOF'
79
+ <filled Phase 1 body with the required sections above>
80
+ EOF
95
81
  ```
96
82
 
97
- If `ticket create` fails, do not inspect unrelated repo files, run implementation commands, call `set_workflow_context`, or manually write `.deuk-agent/tickets/**/*.md`; fill only CLI-reported missing fields in the same internal body and rerun the same command. If a temporary file is unavoidable, keep it outside the workspace, delete it after `ticket create`, and never present it as a ticket artifact. Interactive fallback, template-only fallback, and auto-generated filler text do not satisfy this requirement.
83
+ 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
84
 
99
85
  ### First-Turn Invariant
100
86
 
@@ -114,7 +100,6 @@ Use the mentioned ticket directly. For investigation/regression/why questions, c
114
100
  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
101
 
116
102
  ## 3. Phase Contract
117
-
118
103
  At each phase, ask available tooling for one complete requirement bundle and follow it exactly. Minimum bundle:
119
104
 
120
105
  - Required ticket fields/tasks for the phase.
@@ -126,7 +111,6 @@ At each phase, ask available tooling for one complete requirement bundle and fol
126
111
  If the bundle is missing, contradictory, or unverifiable, stop and record the blocker in the ticket. Do not invent a shortcut.
127
112
 
128
113
  ## 4. Ticket Lifecycle
129
-
130
114
  - Phase 0: read local truth; use RAG only when it changes the plan.
131
115
  - Phase 1: create/update the main ticket with cause, analysis, design/approach, findings, hypotheses, scope, compact plan, and phase contract.
132
116
  - Phase 2: execute only the approved/ticketed plan.
@@ -135,19 +119,13 @@ If the bundle is missing, contradictory, or unverifiable, stop and record the bl
135
119
  - Keep chat compact once the ticket carries the durable record.
136
120
 
137
121
  ## 5. Hard Stops
138
-
139
122
  - 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
123
  - 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 the user or tool contract authorizes execution.
142
- - If repo inspection started before ticket selection or creation, stop, create/select the ticket immediately, record the drift in Phase 1, and only then continue.
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.
124
+ - 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.
125
+ - 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
126
 
146
127
  ## 6. Tool Delegation
147
-
148
- - Use `rg`/`rg --files` first for local search and `apply_patch` for manual edits.
149
- - Use MCP/RAG only when local evidence is insufficient or older decisions matter; keep searches narrow.
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/`.
128
+ - 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.
129
+ - Let CLI own lifecycle enforcement, claim checks, reports, and audits; use `deuk-agent-flow ticket hotfix` only for legitimate generated-file emergencies.
130
+ - Keep notes/reports under `.deuk-agent/docs/`.
153
131
  - `rules audit` must fail if the kernel loses boot, ticket-first, phase-contract, tool-delegation, hard-stop, or verification invariants.
@@ -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
- | `.deuk-agent/templates/` | 티켓 플랜 작성을 위한 표준화된 청사진 |
42
+ | `templates/` | 티켓, 규칙, 스킬 템플릿의 패키지 소유 단일 진실 공급원 |
43
43
  | `bin/deuk-agent-flow.js` | 글로벌 실행 프록시 |
44
44
 
45
45
  ## 5. 엄격한 Phase 기반 티켓 워크플로우 (TDW)
@@ -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
- | `.deuk-agent/templates/` | Standardized blueprint for tickets and plans |
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.21",
3
+ "version": "4.0.36",
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
- "test": "node --test scripts/tests/*.test.mjs",
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": {
@@ -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, TEMPLATE_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";
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(`[MIGRATE] Merging legacy templates into ${AGENT_ROOT_DIR}/${TEMPLATE_SUBDIR}`);
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 allowedDirs = new Set(["docs", "knowledge", "tickets", "templates", "skill-templates", "skills"]);
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 syncTemplates(cwd, bundleRoot, dryRun) {
1374
- const tplDestDir = join(cwd, AGENT_ROOT_DIR, TEMPLATE_SUBDIR);
1375
- const tplSourceDir = join(bundleRoot, "templates");
1376
- if (!existsSync(tplSourceDir)) return;
1377
-
1378
- if (!existsSync(tplDestDir) && !dryRun) {
1379
- mkdirSync(tplDestDir, { recursive: true });
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; never leave only an approval request such as \`승인 바랍니다\` visible after ticket creation/use. 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.sourceShims !== false) {
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 MCP / Phase 0 Status Check
1662
+ // 0.1 Deuk AgentContext MCP memory status check
1717
1663
  const mcpActive = await isMcpActive(opts.cwd);
1718
- console.log(`\n[POLICY] MCP Status: ${mcpActive ? "\x1b[32mACTIVE\x1b[0m" : "\x1b[33mINACTIVE\x1b[0m"}`);
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(`[POLICY] Phase 0 RAG validation is \x1b[32mENFORCED\x1b[0m for ticket creation.\n`);
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(`[POLICY] Running in offline/disconnected mode.\n`);
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. Templates Sync (.deuk-agent/templates/)
1781
- syncTemplates(subCwd, bundleRoot, opts.dryRun);
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
- syncTemplates(opts.cwd, bundleRoot, opts.dryRun);
1743
+ removeRuntimeTemplateCopies(opts.cwd, opts.dryRun);
1798
1744
  syncSkillTemplates(opts.cwd, bundleRoot, opts.dryRun);
1799
1745
  }
@@ -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, AGENT_TOOLS, resolveDocsLanguage, normalizeWorkflowMode, WORKFLOW_MODE_EXECUTE } from "./cli-utils.mjs";
5
5
 
6
6
  export async function ask(rl, question) {
7
7
  return new Promise((resolve) => rl.question(question, resolve));
@@ -39,23 +39,45 @@ export async function selectMany(rl, prompt, choices) {
39
39
  }
40
40
  }
41
41
 
42
+ function hasAny(cwd, names) {
43
+ return names.some((name) => existsSync(join(cwd, name)));
44
+ }
45
+
46
+ export function inferInitDefaults(cwd, opts = {}) {
47
+ const packageJsonPath = join(cwd, "package.json");
48
+ const packageJson = existsSync(packageJsonPath)
49
+ ? readFileSync(packageJsonPath, "utf8")
50
+ : "";
42
51
 
52
+ const stack = opts.stack
53
+ || (hasAny(cwd, ["pyproject.toml", "requirements.txt", "notebooks"]) ? "data" : null)
54
+ || (hasAny(cwd, ["Dockerfile", "docker-compose.yml", "docker-compose.yaml", "k8s", "terraform"]) ? "infra" : null)
55
+ || (hasAny(cwd, ["Cargo.toml", "go.mod", "pom.xml", "build.gradle"]) ? "backend" : null)
56
+ || (packageJson && /"(@vitejs\/|vite|next|react|vue|svelte|astro)"/i.test(packageJson) ? "web" : null)
57
+ || (packageJson ? "backend" : null)
58
+ || "none";
59
+
60
+ const tools = AGENT_TOOLS.map((tool) => tool.value);
61
+
62
+ return {
63
+ stack,
64
+ agentTools: opts.agentTools ?? tools,
65
+ docsLanguage: resolveDocsLanguage(opts.docsLanguage ?? "auto"),
66
+ workflowMode: normalizeWorkflowMode(opts.workflowMode ?? opts.workflow ?? opts.approval ?? WORKFLOW_MODE_EXECUTE),
67
+ shareTickets: opts.shareTickets ?? false,
68
+ contextMcp: opts.contextMcp ?? "skip",
69
+ remoteSync: opts.remoteSync ?? false,
70
+ pipelineUrl: opts.pipelineUrl || ""
71
+ };
72
+ }
43
73
 
44
74
  export async function runInteractive(opts) {
45
75
  const rl = createInterface({ input: process.stdin, output: process.stdout });
46
76
  try {
47
77
  console.log("\nDeukAgentFlow init — let's configure your workspace.\n");
48
78
 
49
- const stack = await selectOne(rl, "What is your primary tech stack?", STACKS);
50
- const tools = await selectMany(rl, "Which agent tools do you use?", AGENT_TOOLS);
51
- const docsLanguage = await selectOne(rl, "What document language should generated tickets/plans use?", DOC_LANGUAGE_CHOICES);
52
- const workflowMode = opts.workflowMode
53
- ? normalizeWorkflowMode(opts.workflowMode)
54
- : await selectOne(rl, "What workflow mode should be saved?", [
55
- { label: "Plan mode (prepare only)", value: WORKFLOW_MODE_PLAN },
56
- { label: "Execute mode (apply changes)", value: WORKFLOW_MODE_EXECUTE },
57
- ]);
58
- const shareTickets = await askYesNo("Do you want to share (git-track) tickets for this repository?", false);
79
+ const workspaceKind = await selectOne(rl, "What kind of workspace is this?", WORKSPACE_KINDS);
80
+ const defaults = inferInitDefaults(opts.cwd, opts);
59
81
 
60
82
  const targetAgents = join(opts.cwd, "AGENTS.md");
61
83
  let agentsDefault = "inject";
@@ -66,37 +88,32 @@ export async function runInteractive(opts) {
66
88
  const content = readFileSync(targetAgents, "utf8");
67
89
  const hasMarkers = content.includes("deuk-agent-rule:begin") || content.includes("## DeukAgentFlow");
68
90
  if (!hasMarkers) {
69
- const choice = await selectOne(rl, "AGENTS.md exists but has no markers. How to apply?", [
70
- { label: "Append managed block at the end (safe)", value: "inject" },
71
- { label: "Overwrite entire AGENTS.md", value: "overwrite" },
72
- { label: "Skip AGENTS.md", value: "skip" },
73
- ]);
74
- agentsDefault = choice;
91
+ agentsDefault = "inject";
92
+ console.log("\n AGENTS.md exists without managed markers will append a managed block.");
75
93
  }
76
94
  }
77
95
 
78
- const remoteSync = opts.remoteSync !== undefined ? opts.remoteSync : (await askYesNo("Enable AI Pipeline remote synchronization? (Advanced)", false));
79
- let pipelineUrl = opts.pipelineUrl || "";
80
- if (remoteSync && !pipelineUrl) {
81
- pipelineUrl = (await ask(rl, "Enter AI Pipeline Endpoint URL: ")).trim();
82
- }
83
-
84
96
  opts.agents = opts.agents ?? agentsDefault;
85
- opts.stack = stack;
86
- opts.agentTools = tools;
87
- opts.docsLanguage = resolveDocsLanguage(docsLanguage);
88
- opts.workflowMode = normalizeWorkflowMode(opts.workflowMode || workflowMode);
89
- opts.shareTickets = shareTickets;
90
- opts.remoteSync = remoteSync;
91
- opts.pipelineUrl = pipelineUrl;
97
+ opts.workspaceKind = workspaceKind;
98
+ opts.kind = workspaceKind;
99
+ opts.stack = defaults.stack;
100
+ opts.agentTools = defaults.agentTools;
101
+ opts.docsLanguage = defaults.docsLanguage;
102
+ opts.workflowMode = defaults.workflowMode;
103
+ opts.shareTickets = defaults.shareTickets;
104
+ opts.contextMcp = defaults.contextMcp;
105
+ opts.remoteSync = defaults.remoteSync;
106
+ opts.pipelineUrl = defaults.pipelineUrl;
92
107
 
93
- console.log("\n Stack : " + stack);
94
- console.log(" Tools : " + (tools.join(", ") || "none"));
108
+ console.log("\n Workspace Kind: " + workspaceKind);
109
+ console.log(" Technical Surface: " + opts.stack);
110
+ console.log(" AI Clients: " + (opts.agentTools.join(", ") || "all supported clients"));
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(" Remote Sync: " + (opts.remoteSync ? "Enabled" : "Disabled"));
99
- if (opts.remoteSync) console.log(" Pipeline URL: " + opts.pipelineUrl);
114
+ console.log(" Deuk AgentContext MCP: hidden during init");
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 one-shot flow: collect real observations first, pass a filled body with `--plan-body-file -`, then run `ticket create` once.",
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
- if (isCompactTicketOutput(opts)) {
459
- console.log("Approval pending: explicit user approval is required before work.");
460
- console.log(`Guard topic: ${ticketId}`);
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
- console.log(`${opts.dryRun ? "Ticket would be created" : "Ticket created"}: ${toFileUri(abs)}`);
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
- syncActiveTicketId(opts.cwd);
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: entry.path,
1816
+ file: finalPath,
1784
1817
  phase: 4,
1785
1818
  status: opts.status
1786
1819
  });
1787
- console.log(`ticket: ${opts.status} -> ${entry.topic} (${entry.path})`);
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;
@@ -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: "Unity / C#", value: "unity" },
90
- { label: "Unity + WebApp + C++ Server (Hybrid)", value: "unity-webapp-cpp" },
91
- { label: "Next.js + C#", value: "nextjs-dotnet" },
92
- { label: "Web (React / Vue / general)", value: "web" },
93
- { label: "Java / Spring Boot", value: "java" },
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: "Cursor (Rule System)", value: "cursor" },
108
+ { label: "OpenAI Codex", value: "codex" },
99
109
  { label: "GitHub Copilot", value: "copilot" },
100
- { label: "Codex / OpenAI", value: "codex" },
110
+ { label: "Claude Code", value: "claude" },
111
+ { label: "Cursor", value: "cursor" },
101
112
  { label: "Gemini / Antigravity", value: "gemini" },
102
- { label: "Claude / Dev", value: "claude" },
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' to update.\x1b[0m\n`);
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
@@ -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
- const LEGACY_ARCHIVED_TEMPLATE_NAMES = new Set([
49
- "module-rule-template.md",
50
- "ticket-list-template.md",
51
- "ticket-template-ko.md",
52
- "ticket-template.md"
53
- ]);
54
-
55
- function isLegacyArchivedTemplateArtifact(relPath, content) {
56
- const normalized = relPath.replace(/\\/g, "/");
57
- if (!normalized.includes(`${TICKET_DIR_NAME}/archive/`)) return false;
58
- if (!LEGACY_ARCHIVED_TEMPLATE_NAMES.has(normalized.split("/").pop())) return false;
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
  }
@@ -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)
@@ -78,7 +78,7 @@ try {
78
78
  "test -f .codex/AGENTS.md",
79
79
  "test -f PROJECT_RULE.md",
80
80
  "test -d .deuk-agent",
81
- "test -d .deuk-agent/templates",
81
+ "test ! -d .deuk-agent/templates",
82
82
  "test -d .deuk-agent/skill-templates",
83
83
  ].join("\n");
84
84