@tienne/gestalt 0.120.2 → 0.122.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/CLAUDE.md +4 -3
  2. package/README.ko.md +20 -0
  3. package/README.md +28 -0
  4. package/dist/package.json +6 -4
  5. package/dist/plugin/.codex-plugin/plugin.json +40 -0
  6. package/dist/plugin/review-agents/writing-reviewer/AGENT.md +4 -2
  7. package/dist/plugin/role-agents/_shared/references/ai-tell-quick-rules.md +2 -1
  8. package/dist/plugin/role-agents/humanize-monolith/AGENT.md +5 -2
  9. package/dist/plugin/scripts/acquisition.mjs +96 -0
  10. package/dist/plugin/scripts/gestalt-cli.mjs +144 -0
  11. package/dist/plugin/scripts/npm-environment.mjs +64 -0
  12. package/dist/plugin/scripts/package-owner.mjs +20 -0
  13. package/dist/plugin/scripts/resolve-cli.mjs +22 -0
  14. package/dist/plugin/skills/_shared/agent-delegation.md +10 -1
  15. package/dist/plugin/skills/_shared/agent-model.md +2 -0
  16. package/dist/plugin/skills/_shared/cli-launcher.md +47 -36
  17. package/dist/plugin/skills/_shared/codex-delegation-examples.md +99 -0
  18. package/dist/plugin/skills/_shared/host-runtime.md +66 -0
  19. package/dist/plugin/skills/_shared/question-capabilities.md +30 -0
  20. package/dist/plugin/skills/agent/SKILL.md +2 -0
  21. package/dist/plugin/skills/architecture/SKILL.md +2 -0
  22. package/dist/plugin/skills/blast-radius/SKILL.md +2 -0
  23. package/dist/plugin/skills/brief/SKILL.md +2 -0
  24. package/dist/plugin/skills/build-graph/SKILL.md +2 -0
  25. package/dist/plugin/skills/diff-radius/SKILL.md +2 -0
  26. package/dist/plugin/skills/dispatch/SKILL.md +2 -0
  27. package/dist/plugin/skills/doubt/SKILL.md +4 -2
  28. package/dist/plugin/skills/execute/SKILL.md +3 -1
  29. package/dist/plugin/skills/explain/SKILL.md +2 -0
  30. package/dist/plugin/skills/handoff/SKILL.md +2 -0
  31. package/dist/plugin/skills/interview/SKILL.md +8 -0
  32. package/dist/plugin/skills/jira-create/SKILL.md +2 -0
  33. package/dist/plugin/skills/lens/SKILL.md +2 -0
  34. package/dist/plugin/skills/local-pr/SKILL.md +4 -2
  35. package/dist/plugin/skills/market-scan/SKILL.md +2 -0
  36. package/dist/plugin/skills/pr/SKILL.md +6 -14
  37. package/dist/plugin/skills/presentation/SKILL.md +2 -0
  38. package/dist/plugin/skills/research/SKILL.md +2 -0
  39. package/dist/plugin/skills/reverse-plan/SKILL.md +2 -0
  40. package/dist/plugin/skills/review/SKILL.md +19 -18
  41. package/dist/plugin/skills/review-loop/SKILL.md +13 -10
  42. package/dist/plugin/skills/review-reply/SKILL.md +7 -15
  43. package/dist/plugin/skills/screen-ref/SKILL.md +2 -0
  44. package/dist/plugin/skills/setup/SKILL.md +14 -9
  45. package/dist/plugin/skills/share/SKILL.md +4 -2
  46. package/dist/plugin/skills/ship/SKILL.md +3 -1
  47. package/dist/plugin/skills/slack-send/SKILL.md +2 -0
  48. package/dist/plugin/skills/solve/SKILL.md +2 -0
  49. package/dist/plugin/skills/spec/SKILL.md +3 -1
  50. package/dist/plugin/skills/ux-research/SKILL.md +2 -0
  51. package/dist/plugin/skills/video-digest/SKILL.md +2 -0
  52. package/dist/schemas/gestalt.schema.json +25 -4
  53. package/dist/src/cli/commands/init.d.ts +2 -0
  54. package/dist/src/cli/commands/init.d.ts.map +1 -1
  55. package/dist/src/cli/commands/init.js +6 -2
  56. package/dist/src/cli/commands/init.js.map +1 -1
  57. package/dist/src/cli/commands/setup.d.ts +4 -1
  58. package/dist/src/cli/commands/setup.d.ts.map +1 -1
  59. package/dist/src/cli/commands/setup.js +2 -1
  60. package/dist/src/cli/commands/setup.js.map +1 -1
  61. package/dist/src/cli/index.d.ts.map +1 -1
  62. package/dist/src/cli/index.js +15 -3
  63. package/dist/src/cli/index.js.map +1 -1
  64. package/dist/src/core/config.d.ts +169 -150
  65. package/dist/src/core/config.d.ts.map +1 -1
  66. package/dist/src/core/config.js +41 -11
  67. package/dist/src/core/config.js.map +1 -1
  68. package/dist/src/core/model-diagnostics.d.ts +9 -0
  69. package/dist/src/core/model-diagnostics.d.ts.map +1 -0
  70. package/dist/src/core/model-diagnostics.js +47 -0
  71. package/dist/src/core/model-diagnostics.js.map +1 -0
  72. package/dist/src/core/types.d.ts +3 -3
  73. package/dist/src/execute/prompts.d.ts.map +1 -1
  74. package/dist/src/execute/prompts.js +4 -1
  75. package/dist/src/execute/prompts.js.map +1 -1
  76. package/dist/src/mcp/client-host.d.ts +12 -0
  77. package/dist/src/mcp/client-host.d.ts.map +1 -0
  78. package/dist/src/mcp/client-host.js +41 -0
  79. package/dist/src/mcp/client-host.js.map +1 -0
  80. package/dist/src/mcp/server.d.ts.map +1 -1
  81. package/dist/src/mcp/server.js +20 -6
  82. package/dist/src/mcp/server.js.map +1 -1
  83. package/dist/src/mcp/tools/status.d.ts +22 -7
  84. package/dist/src/mcp/tools/status.d.ts.map +1 -1
  85. package/dist/src/mcp/tools/status.js +8 -3
  86. package/dist/src/mcp/tools/status.js.map +1 -1
  87. package/package.json +6 -4
  88. package/plugin/.codex-plugin/plugin.json +2 -2
  89. package/plugin/.mcp.json +1 -1
  90. package/plugin/mcp.json +1 -1
  91. package/plugin/review-agents/writing-reviewer/AGENT.md +4 -2
  92. package/plugin/role-agents/_shared/references/ai-tell-quick-rules.md +2 -1
  93. package/plugin/role-agents/humanize-monolith/AGENT.md +5 -2
  94. package/plugin/scripts/acquisition.mjs +96 -0
  95. package/plugin/scripts/gestalt-cli.mjs +144 -0
  96. package/plugin/scripts/npm-environment.mjs +64 -0
  97. package/plugin/scripts/package-owner.mjs +20 -0
  98. package/plugin/scripts/resolve-cli.mjs +22 -0
  99. package/plugin/skills/_shared/agent-delegation.md +10 -1
  100. package/plugin/skills/_shared/agent-model.md +2 -0
  101. package/plugin/skills/_shared/cli-launcher.md +47 -36
  102. package/plugin/skills/_shared/codex-delegation-examples.md +99 -0
  103. package/plugin/skills/_shared/host-runtime.md +66 -0
  104. package/plugin/skills/_shared/question-capabilities.md +30 -0
  105. package/plugin/skills/agent/SKILL.md +2 -0
  106. package/plugin/skills/architecture/SKILL.md +2 -0
  107. package/plugin/skills/blast-radius/SKILL.md +2 -0
  108. package/plugin/skills/brief/SKILL.md +2 -0
  109. package/plugin/skills/build-graph/SKILL.md +2 -0
  110. package/plugin/skills/diff-radius/SKILL.md +2 -0
  111. package/plugin/skills/dispatch/SKILL.md +2 -0
  112. package/plugin/skills/doubt/SKILL.md +4 -2
  113. package/plugin/skills/execute/SKILL.md +3 -1
  114. package/plugin/skills/explain/SKILL.md +2 -0
  115. package/plugin/skills/handoff/SKILL.md +2 -0
  116. package/plugin/skills/interview/SKILL.md +8 -0
  117. package/plugin/skills/jira-create/SKILL.md +2 -0
  118. package/plugin/skills/lens/SKILL.md +2 -0
  119. package/plugin/skills/local-pr/SKILL.md +4 -2
  120. package/plugin/skills/market-scan/SKILL.md +2 -0
  121. package/plugin/skills/pr/SKILL.md +6 -14
  122. package/plugin/skills/presentation/SKILL.md +2 -0
  123. package/plugin/skills/research/SKILL.md +2 -0
  124. package/plugin/skills/reverse-plan/SKILL.md +2 -0
  125. package/plugin/skills/review/SKILL.md +19 -18
  126. package/plugin/skills/review-loop/SKILL.md +13 -10
  127. package/plugin/skills/review-reply/SKILL.md +7 -15
  128. package/plugin/skills/screen-ref/SKILL.md +2 -0
  129. package/plugin/skills/setup/SKILL.md +14 -9
  130. package/plugin/skills/share/SKILL.md +4 -2
  131. package/plugin/skills/ship/SKILL.md +3 -1
  132. package/plugin/skills/slack-send/SKILL.md +2 -0
  133. package/plugin/skills/solve/SKILL.md +2 -0
  134. package/plugin/skills/spec/SKILL.md +3 -1
  135. package/plugin/skills/ux-research/SKILL.md +2 -0
  136. package/plugin/skills/video-digest/SKILL.md +2 -0
  137. package/schemas/gestalt.schema.json +25 -4
package/CLAUDE.md CHANGED
@@ -7,7 +7,7 @@
7
7
  ## 설계 전제
8
8
  - Execute Engine은 설계상 **항상 Passthrough 모드**다. Claude Code가 도구(Bash/Edit 등)로 실제 파일 수정과 코드 실행을 하므로 LLM 주체가 되고, API 키 유무와 무관하다. MCP 서버도 API 키가 없으면 Passthrough가 자동으로 켜진다.
9
9
  - Local PR은 에이전트끼리 레포 안에서 PR을 만들고 리뷰하고 머지하는 자리라 원격에 안 나간다. 워크트리 여럿이 `.gestalt/reviews.db` 하나를 공유한다.
10
- - 코드 그래프 훅 자동 주입과 WebFetch 디스크 캐시 훅은 둘 다 기본 꺼짐이다. peer 보는 중 표시 훅은 늘 돌지만 Node를 안 띄운다. 세션에 보는 사람이나 승인 대기가 있거나 팀원의 에이전트가 요약을 가져갔을 때만 데몬이 상태 파일을 써 두고 훅은 그 세션에만 붙인다.
10
+ - 현재 Gestalt 훅 통합은 Claude Code 전용이다. 코드 그래프 훅 자동 주입과 WebFetch 디스크 캐시 훅은 둘 다 기본 꺼짐이다. peer 보는 중 표시 훅은 늘 돌지만 Node를 안 띄운다. 세션에 보는 사람이나 승인 대기가 있거나 팀원의 에이전트가 요약을 가져갔을 때만 데몬이 상태 파일을 써 두고 훅은 그 세션에만 붙인다.
11
11
 
12
12
  ## Key Commands
13
13
  ```bash
@@ -30,6 +30,7 @@ MCP 도구 목록과 액션 스키마는 `src/mcp/`의 툴 정의가 기준이
30
30
  세션 공유 → [`docs/peer.md`](./docs/peer.md)
31
31
  아키텍처 그림 → [`docs/architecture-view.md`](./docs/architecture-view.md)
32
32
  WebFetch 캐시 훅 → [`docs/webfetch-cache.md`](./docs/webfetch-cache.md)
33
+ Codex 훅 지원 범위 → [`docs/codex-hooks.md`](./docs/codex-hooks.md)
33
34
  설계 제안 → [docs/proposals/](./docs/proposals/README.md)
34
35
 
35
36
  ## Role Agent 자동 라우팅
@@ -69,8 +70,8 @@ agents/ Claude 플러그인 에이전트 (gestalt:<이
69
70
  - Codex는 `path`가 가리킨 디렉토리를 통째로 복사한다. 레포 루트를 가리키면 `.git`과 `node_modules`까지 딸려가 1.6GB가 되므로 반드시 `plugin/`으로 좁힌다.
70
71
  - Codex는 심링크를 따라가지 않는다. 자산은 실물 파일로 `plugin/` 안에 있어야 한다.
71
72
  - `plugin/skills/review/SKILL.md`가 `../../role-agents/`를 참조한다. 스킬과 에이전트를 함께 옮겨야 이 상대 깊이가 유지된다.
72
- - 훅은 루트 `hooks/hooks.json` 하나만 두고 `plugin.json`에 `hooks` 필드를 넣지 않는다. 문서상 필드와 기본 파일은 merge된다. 같은 파일을 가리키면 실제로는 한 번만 걸리는 걸 확인했지만 그래도 한 곳만 둔다.
73
- - Codex와 Grok 훅은 지원하지 않는다. 훅 자동 주입은 Claude 전용이다.
73
+ - Claude Code 훅은 루트 `hooks/hooks.json` 하나만 두고 `plugin.json`에 `hooks` 필드를 넣지 않는다. Claude Code 문서상 필드와 기본 파일은 merge된다. 같은 파일을 가리키면 실제로는 한 번만 걸리는 걸 확인했지만 그래도 한 곳만 둔다.
74
+ - Gestalt의 코드 그래프 자동 주입, WebFetch 캐시, peer 보는 중 표시 훅은 현재 Claude Code 전용이며 Codex와 Grok에는 통합하지 않았다. Codex 자체는 수동 설치한 desktop 플러그인의 훅을 지원하지만 명시적 trust가 필요하고 훅 포함 플러그인은 public directory 대상이 아니다. 근거와 호스트별 경계는 [`docs/codex-hooks.md`](./docs/codex-hooks.md)에 있다(공식 문서 확인: 2026-11-05).
74
75
  - 루트 `agents/`는 리뷰어 전부와 스킬이 `subagent_type: "gestalt:<이름>"`으로 띄우는 에이전트만 뽑은 생성물이다. 에이전트마다 설명이 모든 세션의 Agent 도구 설명에 붙어서 대상을 좁히고 설명도 빌드 스크립트의 한 줄짜리로 바꿔 싣는다. 고칠 곳은 원본 AGENT.md이고 도구와 `omitClaudeMd`는 `scripts/build-claude-agents.ts`의 표에서 정한다. 타입을 못 찾는 런타임은 Explore와 `ges_agent`로 폴백한다(`plugin/skills/_shared/agent-delegation.md`).
75
76
  - `plugin.json`에 `agents` 필드를 넣지 않는다. 배열로 넣으면 기본 `agents/` 디렉토리를 대체한다. 디렉토리 문자열을 넣으면 플러그인 로드가 통째로 깨진다(2.1.302에서 확인). 원리 에이전트가 있는 `plugin/agents/`는 Claude가 직접 읽는 자리가 아니다. 빌드가 continuity-judge만 여기서 읽어 루트 `agents/`로 뽑는다.
76
77
  - 생성물 본문의 룰북 경로는 `${CLAUDE_PLUGIN_ROOT}/plugin/...`이다. Claude Code가 에이전트 본문의 이 변수를 설치 경로로 치환해서 서브에이전트가 남의 레포에서 돌아도 풀린다.
package/README.ko.md CHANGED
@@ -246,6 +246,26 @@ codex plugin add gestalt@gestalt
246
246
 
247
247
  스킬은 다음 Codex 세션부터 잡혀요. 슬래시 커맨드랑 Claude Code Task 패널은 Claude Code 전용이라, Codex에서는 하려는 일을 말로 설명하면 Codex가 해당 `SKILL.md`를 읽어 진행해요.
248
248
 
249
+ 설치 후 새 세션에서 “Gestalt 상태 확인해줘” 또는 “이 프로젝트를 Codex용 Gestalt로 초기화해줘”라고 요청하면 돼요.
250
+ MCP 초기화 때 Codex를 인식해서 실행 컨텍스트는 기본으로 `AGENTS.md`에 기록하고 에이전트 모델은 현재
251
+ Codex 세션을 상속해요. 명시한 `client`와 모델 설정은 유지하며 `client: "both"`면 두 호스트 파일에 계속 기록해요.
252
+ 프로젝트에 Codex 설정을 남기려면 초기화 스킬에 요청하면 돼요. 스킬이 아래의 버전 고정 런처로
253
+ `setup --client codex`를 실행해요. 코드 그래프와 Git 훅까지 설정하려면 `init --client codex`를
254
+ 실행하고 고정된 CLI가 아직 이 옵션을 지원하지 않으면 호환 절차를 적용해요.
255
+
256
+ 스킬은 [호스트 어댑터](./plugin/skills/_shared/host-runtime.md)로 Claude 도구 예시를 현재 Codex 도구에 맞춰 실행해요.
257
+ CLI 검사는 [버전 고정 런처](./plugin/skills/_shared/cli-launcher.md)를 거쳐서 전역 `gestalt` 설치가 필요 없어요.
258
+ 첫 CLI 실행은 플러그인과 같은 npm 버전을 내려받을 수 있으니 Node.js >=22와 npm이 PATH에 있어야 해요.
259
+
260
+ 연결이 실패하면 MCP 상태와 Node/npm부터 확인해요. 호스트나 모델이 예상과 다르면 `ges_status`의 `hostClient`,
261
+ `client`, `tierModels`를 먼저 확인해요. Claude Task 패널과 Gestalt의 현재 Claude Code 훅 통합은 Codex 실행을 막지 않아요.
262
+
263
+ Gestalt는 코드 그래프 자동 주입, WebFetch 캐시, peer 보는 중 표시 훅을 아직 Codex에 통합하지 않았어요.
264
+ Codex desktop 자체는 수동 설치한 플러그인의 훅을 지원하지만 명시적 trust가 필요하고
265
+ 훅이 포함된 플러그인은 public directory에 제출할 수 없어요.
266
+ [Codex 훅 지원 범위](./docs/codex-hooks.md)에 2026-11-05 확인한
267
+ [공식 근거](https://developers.openai.com/plugins/build/plugins)와 Orca를 포함한 호스트별 경계를 정리했어요.
268
+
249
269
  ---
250
270
 
251
271
  ### 옵션 5: OpenAI Codex CLI (MCP만)
package/README.md CHANGED
@@ -221,6 +221,34 @@ Skills load on the next Codex session. Slash commands and the Claude Code Task
221
221
  panel are still Claude Code only — in Codex you invoke a skill by describing the
222
222
  task, and Codex reads the matching `SKILL.md`.
223
223
 
224
+ Start a new session after installing, then ask “Check Gestalt status” or
225
+ “Set up Gestalt for this Codex project.” The server recognizes Codex during MCP
226
+ initialization: new projects record execution context in `AGENTS.md`, and agent
227
+ models inherit the active Codex session by default. Explicit `client` and model
228
+ settings still take precedence; `client: "both"` continues writing both host files.
229
+ For a persistent Codex-only project, ask the setup skill to save `client: "codex"`.
230
+ The skill runs `setup --client codex` through the versioned launcher below
231
+ (or `init --client codex` for the graph and Git hook), with compatibility handling
232
+ when the pinned CLI predates that option.
233
+
234
+ Skills use the bundled [host adapter](./plugin/skills/_shared/host-runtime.md) to
235
+ translate Claude tool examples into the available Codex tools. They run CLI
236
+ checks through the [versioned launcher](./plugin/skills/_shared/cli-launcher.md),
237
+ so no global `gestalt` install is needed. The first CLI call may download the
238
+ plugin's exact npm version and needs Node.js >=22 and npm on PATH.
239
+
240
+ If startup fails, check MCP status and Node/npm availability. If the recorded
241
+ host or model is unexpected, inspect `ges_status` fields `hostClient`, `client`,
242
+ and `tierModels` before changing project settings. Claude Task panels and Gestalt's
243
+ current Claude Code hook integrations are optional; they do not gate Codex execution.
244
+
245
+ Gestalt has not integrated its code graph injection, WebFetch cache, or peer
246
+ watching hooks with Codex. Codex desktop supports hooks in manually installed
247
+ plugins, subject to explicit trust; plugins containing hooks are ineligible for
248
+ the public directory. See [Codex hook support](./docs/codex-hooks.md) for the
249
+ [official source](https://developers.openai.com/plugins/build/plugins), verified
250
+ 2026-11-05, and the boundaries for other hosts, including Orca.
251
+
224
252
  ---
225
253
 
226
254
  ### Option 5: OpenAI Codex CLI (MCP only)
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tienne/gestalt",
3
- "version": "0.120.2",
3
+ "version": "0.122.0",
4
4
  "description": "TypeScript AI Development Harness - Gestalt psychology-driven requirement clarification",
5
5
  "repository": {
6
6
  "type": "git",
@@ -29,11 +29,12 @@
29
29
  "scripts": {
30
30
  "dev": "tsx bin/gestalt.ts",
31
31
  "build": "tsc -p tsconfig.build.json",
32
- "postbuild": "rm -rf dist/plugin && mkdir -p dist/plugin && cp -r plugin/agents plugin/role-agents plugin/review-agents plugin/personas plugin/research-sources plugin/skills dist/plugin/ && cp -r schemas dist/ && cp package.json dist/ && chmod +x dist/bin/gestalt.js dist/bin/gestalt-hook.js && pnpm run verify:plugin && pnpm run verify:rules && pnpm run verify:rule-overlap && pnpm run verify:output-style && pnpm run verify:routing && pnpm run verify:agents",
32
+ "postbuild": "rm -rf dist/plugin && mkdir -p dist/plugin && cp -r plugin/agents plugin/role-agents plugin/review-agents plugin/personas plugin/research-sources plugin/skills plugin/scripts plugin/.codex-plugin dist/plugin/ && cp -r schemas dist/ && cp package.json dist/ && chmod +x dist/bin/gestalt.js dist/bin/gestalt-hook.js && pnpm run verify:plugin && pnpm run verify:rules && pnpm run verify:rule-overlap && pnpm run verify:output-style && pnpm run verify:routing && pnpm run verify:agents",
33
33
  "verify:plugin": "tsx scripts/verify-plugin-assets.ts",
34
34
  "prepublishOnly": "pnpm build",
35
35
  "test": "vitest run",
36
36
  "test:watch": "vitest",
37
+ "test:plugin-install": "vitest run --config vitest.plugin-install.config.ts",
37
38
  "serve": "tsx bin/gestalt.ts serve",
38
39
  "bench": "tsx benchmarks/run.ts",
39
40
  "resolution-bench": "tsx benchmarks/run-resolution-benchmark.ts",
@@ -47,7 +48,7 @@
47
48
  "postversion": "pnpm run version:sync && pnpm run build:output-style",
48
49
  "verify:rules": "tsx scripts/verify-rule-refs.ts",
49
50
  "verify:rule-overlap": "tsx scripts/verify-rule-overlap.ts",
50
- "gate": "pnpm typecheck && pnpm verify:rules && pnpm verify:rule-overlap && pnpm verify:routing && pnpm verify:agents && pnpm verify:skill-confusion && pnpm lint && pnpm format:check && pnpm build && pnpm test",
51
+ "gate": "pnpm typecheck && pnpm verify:rules && pnpm verify:rule-overlap && pnpm verify:routing && pnpm verify:agents && pnpm verify:skill-confusion && pnpm lint && pnpm format:check && pnpm build && pnpm test && pnpm test:plugin-install",
51
52
  "humanize:baseline": "tsx scripts/humanize-baseline.ts",
52
53
  "build:output-style": "tsx scripts/build-output-style.ts",
53
54
  "verify:output-style": "tsx scripts/build-output-style.ts --dry-run",
@@ -55,7 +56,8 @@
55
56
  "verify:routing": "tsx scripts/build-skill-routing.ts --check",
56
57
  "build:agents": "tsx scripts/build-claude-agents.ts",
57
58
  "verify:agents": "tsx scripts/build-claude-agents.ts --check",
58
- "verify:skill-confusion": "tsx scripts/verify-skill-confusion.ts"
59
+ "verify:skill-confusion": "tsx scripts/verify-skill-confusion.ts",
60
+ "test:codex-e2e": "tsx scripts/codex-e2e.ts"
59
61
  },
60
62
  "dependencies": {
61
63
  "@anthropic-ai/sdk": "^0.39.0",
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "gestalt",
3
+ "version": "0.122.0",
4
+ "description": "Gestalt psychology-driven AI development harness. Transforms scattered requirements into structured, validated specifications through interactive interviews.",
5
+ "author": {
6
+ "name": "tienne"
7
+ },
8
+ "homepage": "https://github.com/tienne/gestalt",
9
+ "repository": "https://github.com/tienne/gestalt",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "gestalt",
13
+ "requirements",
14
+ "specification",
15
+ "interview",
16
+ "mcp",
17
+ "ai-development",
18
+ "code-review"
19
+ ],
20
+ "skills": "./skills/",
21
+ "mcpServers": "./mcp.json",
22
+ "interface": {
23
+ "displayName": "Gestalt",
24
+ "shortDescription": "Requirements-to-spec pipeline with review and PR workflows",
25
+ "longDescription": "Gestalt turns vague ideas into validated specifications through Gestalt-principle interviews, then drives execution, code review, and PR authoring. Bundles workflow skills, specialist agents, and MCP tools.",
26
+ "developerName": "tienne",
27
+ "category": "Developer Tools",
28
+ "capabilities": [
29
+ "Interactive",
30
+ "Read",
31
+ "Write"
32
+ ],
33
+ "websiteURL": "https://github.com/tienne/gestalt",
34
+ "defaultPrompt": [
35
+ "Review this PR with the Gestalt review pipeline.",
36
+ "Clarify these requirements into a structured spec.",
37
+ "Write a PR description for the current branch."
38
+ ]
39
+ }
40
+ }
@@ -53,17 +53,19 @@ git diff --word-diff=porcelain -- <파일>
53
53
 
54
54
  한 조각이 룰에 걸리는지 보려면 그 조각이 들어간 **문장 전체**를 읽어야 합니다. 조각만 떼어 보면 문장 구조로 걸리는 룰(A-18 좌향 수식, C-11 연결어미 뒤 쉼표, E-2 종결어미 반복)을 판정할 수 없습니다. 판정은 문장으로 하되 이슈로 올릴지는 그 룰에 걸린 부분이 바뀐 조각 안에 있는지로 정합니다.
55
55
 
56
+ 아래 런처 경로는 이 문서 디렉토리 기준입니다. 실행 전에 절대 경로로 풉니다. 작업 레포의 동명 파일은 사용하지 않습니다.
57
+
56
58
  **정적 탐지를 먼저 돌립니다.** 정규식으로 잡히는 룰은 사람이 눈으로 세는 것보다 정확합니다.
57
59
 
58
60
  ```bash
59
- gestalt humanize-check --before <파일> --after <파일> --register doc --json
61
+ node "../../scripts/gestalt-cli.mjs" humanize-check --before <파일> --after <파일> --register doc --json
60
62
  ```
61
63
 
62
64
  같은 파일을 `--before`와 `--after`에 둘 다 넘기면 윤문 전후 비교가 아니라 그 파일에 남아 있는 S1 패턴을 세어 줍니다. 잡히는 룰은 21개뿐이니 나머지는 읽고 판정합니다.
63
65
 
64
66
  `--register`는 대상 문서의 성격에 맞춰 고릅니다. 문서는 `doc`, 리뷰 코멘트나 대화체는 `chat`, 보고서는 `report`입니다. `chat`에서는 몇몇 S2가 S1으로 올라갑니다. 사람은 대화에서 개념을 명사 덩어리로 뭉치지 않기 때문입니다.
65
67
 
66
- 레포에 이 명령이 없으면 그 단계를 건너뛰고 직접 읽어 판정합니다. 없다고 리뷰를 멈추지 않습니다.
68
+ 런처를 못 찾거나 버전 해석이 실패하면 그 단계를 건너뛰고 직접 읽어 판정합니다. 건너뛴 사실을 보고합니다.
67
69
 
68
70
  ## 어휘 선택을 볼 때
69
71
 
@@ -214,9 +214,10 @@
214
214
  ## 코드 검사 (자가 채점보다 위)
215
215
 
216
216
  아래 등급은 모델이 스스로 매기는 참고값이다. 실제 판단은 코드가 한다.
217
+ 런처 경로는 이 문서 디렉토리 기준이다. 실행 전에 절대 경로로 풀고 작업 레포의 동명 파일을 사용하지 않는다.
217
218
 
218
219
  ```bash
219
- gestalt humanize-check --before before.md --after after.md --register chat
220
+ node "../../../scripts/gestalt-cli.mjs" humanize-check --before before.md --after after.md --register chat
220
221
  ```
221
222
 
222
223
  변경률, S1 잔존, 보호 토큰 생존, 구조 보존 4가지 측면에서 각각 측정하고 exit code로 답한다
@@ -74,11 +74,14 @@ S2
74
74
 
75
75
  ### 0단계 — 스캔 (룰을 좁힌다)
76
76
 
77
+ 아래 런처 경로는 이 문서 디렉토리 기준이다. 실행 전에 절대 경로로 풀고 작업 레포의 동명 파일을 사용하지 않는다.
78
+ 런처를 못 찾거나 버전 해석이 실패하면 스캔과 코드 검사를 건너뛰고 직접 읽어 판정한다. 건너뛴 사실을 보고한다.
79
+
77
80
  원문이 파일로 있으면 먼저 스캔을 돌린다. 말투는 산문이 `doc`, 리뷰 코멘트와 슬랙이 `chat`,
78
81
  보고서가 `report`다.
79
82
 
80
83
  ```bash
81
- gestalt humanize-scan --file <원문> --register <doc|chat|report>
84
+ node "../../scripts/gestalt-cli.mjs" humanize-scan --file <원문> --register <doc|chat|report>
82
85
  ```
83
86
 
84
87
  종료 코드로도 답한다. 걸린 게 있으면 0, 어투는 깨끗한데 맞춤법만 걸리면 11, 탐지기가 가리는 범위에서 0건이면 10이다. 11이면 어투는 그대로 두고 맞춤법 절만 고쳐서 낸다.
@@ -202,7 +205,7 @@ summary에 적는다. 댈 게 없으면 그건 안 고친 것이라 원문을
202
205
  위 3단계 자가검증은 스스로 매기는 참고값이다. 원문과 윤문본이 파일로 있으면 판단은 코드에 맡긴다.
203
206
 
204
207
  ```bash
205
- gestalt humanize-check --before <원문> --after <윤문본> --register chat --attempt 1
208
+ node "../../scripts/gestalt-cli.mjs" humanize-check --before <원문> --after <윤문본> --register chat --attempt 1
206
209
  ```
207
210
 
208
211
  변경률, S1 제거율, 보호 토큰 생존, 구조 보존, 신규 유입을 각각 측정하고 exit code로 답한다
@@ -0,0 +1,96 @@
1
+ import { spawn } from 'node:child_process';
2
+ import process from 'node:process';
3
+ import { Buffer } from 'node:buffer';
4
+ import { setTimeout, clearTimeout } from 'node:timers';
5
+
6
+ // One cancellation scope spans npm resolution, version verification and cwd cleanup.
7
+ // A child's own signal/timeout is a failed attempt, never a user's cancellation.
8
+ export function createAcquisition({ cwd, env }) {
9
+ let current;
10
+ let signal;
11
+ const cancel = (received) => {
12
+ signal = received;
13
+ current?.cancel(received);
14
+ };
15
+ const terminate = () => cancel('SIGTERM');
16
+ const hangup = () => cancel('SIGHUP');
17
+ const interrupt = () => cancel('SIGINT');
18
+ process.on('SIGTERM', terminate);
19
+ process.on('SIGHUP', hangup);
20
+ process.on('SIGINT', interrupt);
21
+
22
+ return {
23
+ get signal() {
24
+ return signal;
25
+ },
26
+ run(command, args, timeoutMs) {
27
+ return new Promise((resolveResult) => {
28
+ const child = spawn(command, args, { cwd, env, stdio: ['ignore', 'pipe', 'pipe'] });
29
+ let stdout = '';
30
+ let bytes = 0;
31
+ let failed = false;
32
+ let interruptTimer;
33
+ let killTimer;
34
+ let drainTimer;
35
+ const stop = (received) => {
36
+ clearTimeout(interruptTimer);
37
+ child.kill(received);
38
+ // Give npm a chance to forward cancellation first. KILL targets only this
39
+ // direct child; descendants can survive if npm cannot relay the signal.
40
+ killTimer ??= setTimeout(() => child.kill('SIGKILL'), 5000);
41
+ };
42
+ const active = {
43
+ cancel(received) {
44
+ if (received === 'SIGINT') {
45
+ // Do not duplicate group INT. Any surviving child gets TERM after grace.
46
+ interruptTimer ??= setTimeout(() => stop('SIGTERM'), 2000);
47
+ } else stop(received);
48
+ },
49
+ };
50
+ current = active;
51
+ const deadline = setTimeout(() => {
52
+ failed = true;
53
+ stop('SIGTERM');
54
+ }, timeoutMs);
55
+ child.stdout.setEncoding('utf8');
56
+ child.stdout.on('data', (chunk) => {
57
+ bytes += Buffer.byteLength(chunk);
58
+ if (bytes > 64 * 1024) {
59
+ failed = true;
60
+ stop('SIGTERM');
61
+ } else stdout += chunk;
62
+ });
63
+ child.stderr.resume();
64
+ child.once('error', () => {
65
+ failed = true;
66
+ });
67
+ child.once('exit', () => {
68
+ clearTimeout(deadline);
69
+ clearTimeout(interruptTimer);
70
+ clearTimeout(killTimer);
71
+ if (current === active) current = undefined;
72
+ // Descendants can inherit both pipes after npm exits. Drain briefly, then
73
+ // release both handles so they cannot keep resolution open indefinitely.
74
+ drainTimer = setTimeout(() => {
75
+ child.stdout.destroy();
76
+ child.stderr.destroy();
77
+ }, 1000);
78
+ });
79
+ child.once('close', (status) => {
80
+ clearTimeout(deadline);
81
+ clearTimeout(interruptTimer);
82
+ clearTimeout(killTimer);
83
+ clearTimeout(drainTimer);
84
+ if (current === active) current = undefined;
85
+ resolveResult({ status: failed ? null : status, stdout });
86
+ });
87
+ if (signal) active.cancel(signal);
88
+ });
89
+ },
90
+ dispose() {
91
+ process.off('SIGTERM', terminate);
92
+ process.off('SIGHUP', hangup);
93
+ process.off('SIGINT', interrupt);
94
+ },
95
+ };
96
+ }
@@ -0,0 +1,144 @@
1
+ import process from 'node:process';
2
+ import { spawn } from 'node:child_process';
3
+ import {
4
+ existsSync,
5
+ mkdtempSync,
6
+ readFileSync,
7
+ realpathSync,
8
+ rmSync,
9
+ writeFileSync,
10
+ } from 'node:fs';
11
+ import { tmpdir } from 'node:os';
12
+ import { dirname, join, resolve } from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+ import { ownsPackage } from './package-owner.mjs';
15
+ import { createAcquisition } from './acquisition.mjs';
16
+ import { npmEnvironment } from './npm-environment.mjs';
17
+
18
+ const scripts = dirname(fileURLToPath(import.meta.url));
19
+ const pluginRoot = resolve(scripts, '..');
20
+ const args = process.argv.slice(2);
21
+ const fail = (message) => {
22
+ process.stderr.write(`gestalt CLI: ${message}\n`);
23
+ process.exit(1);
24
+ };
25
+
26
+ let version;
27
+ try {
28
+ version = JSON.parse(readFileSync(join(pluginRoot, '.codex-plugin/plugin.json'), 'utf8')).version;
29
+ if (
30
+ typeof version !== 'string' ||
31
+ !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(version)
32
+ ) {
33
+ throw new Error('invalid version');
34
+ }
35
+ } catch {
36
+ fail('cannot read the bundled plugin version; reinstall the plugin.');
37
+ }
38
+ if (Number(process.versions.node.split('.')[0]) < 22) fail('Node.js >=22 is required.');
39
+
40
+ // A development checkout is identified by the wrapper's own location, never cwd.
41
+ // Without tsx, use a matching compiled entry if available, then the exact npm pin.
42
+ const checkout = resolve(pluginRoot, '..');
43
+ let localArgs;
44
+ let sourceTsconfig;
45
+ try {
46
+ const pkg = JSON.parse(readFileSync(join(checkout, 'package.json'), 'utf8'));
47
+ const entry = join(checkout, 'bin/gestalt.ts');
48
+ if (pkg.name === '@tienne/gestalt' && pkg.version === version && existsSync(entry)) {
49
+ const tsx = join(checkout, 'node_modules/tsx/dist/loader.mjs');
50
+ // Keep the target project's tsconfig paths out of the source loader.
51
+ if (existsSync(tsx)) {
52
+ localArgs = ['--import', tsx, entry];
53
+ sourceTsconfig = join(checkout, 'tsconfig.json');
54
+ }
55
+ }
56
+ if (!localArgs && pkg.name === '@tienne/gestalt' && pkg.version === version) {
57
+ const compiled = [join(checkout, 'bin/gestalt.js'), join(checkout, 'dist/bin/gestalt.js')].find(
58
+ existsSync,
59
+ );
60
+ if (compiled) localArgs = [compiled];
61
+ }
62
+ } catch {
63
+ // A copied plugin is deliberately independent of its containing project.
64
+ }
65
+
66
+ let bin;
67
+ if (!localArgs) {
68
+ const neutral = mkdtempSync(join(tmpdir(), 'gestalt-cli-'));
69
+ const acquisition = createAcquisition({ cwd: neutral, env: npmEnvironment(process.env) });
70
+ try {
71
+ writeFileSync(join(neutral, 'package.json'), '{"private":true}\n');
72
+ for (const offline of [true, false]) {
73
+ if (!offline)
74
+ process.stderr.write(`gestalt CLI: resolving @tienne/gestalt@${version} from npm.\n`);
75
+ const result = await acquisition.run(
76
+ 'npm',
77
+ [
78
+ 'exec',
79
+ '--yes',
80
+ `--prefix=${neutral}`,
81
+ '--no-workspaces',
82
+ ...(offline ? ['--offline'] : []),
83
+ `--package=@tienne/gestalt@${version}`,
84
+ '--',
85
+ process.execPath,
86
+ join(scripts, 'resolve-cli.mjs'),
87
+ version,
88
+ ],
89
+ 180_000,
90
+ );
91
+ if (acquisition.signal) break;
92
+ if (result.status !== 0) continue;
93
+ try {
94
+ const candidate = JSON.parse(result.stdout.trim());
95
+ if (typeof candidate !== 'string') continue;
96
+ bin = realpathSync(candidate);
97
+ if (!ownsPackage(bin, version)) {
98
+ bin = undefined;
99
+ continue;
100
+ }
101
+ // Do not trust the resolver process or a stale global executable.
102
+ const check = await acquisition.run(process.execPath, [bin, '--version'], 10_000);
103
+ if (acquisition.signal) break;
104
+ if (check.status === 0 && check.stdout.trim() === version) break;
105
+ bin = undefined;
106
+ } catch {
107
+ bin = undefined;
108
+ }
109
+ }
110
+ } finally {
111
+ try {
112
+ rmSync(neutral, { recursive: true, force: true });
113
+ } finally {
114
+ acquisition.dispose();
115
+ }
116
+ }
117
+ if (acquisition.signal) process.kill(process.pid, acquisition.signal);
118
+ if (!bin)
119
+ fail(
120
+ `could not resolve @tienne/gestalt@${version}; check npm, network access, and its native dependencies. No other version was executed.`,
121
+ );
122
+ }
123
+
124
+ const child = spawn(process.execPath, [...(localArgs ?? [bin]), ...args], {
125
+ cwd: process.cwd(),
126
+ stdio: 'inherit',
127
+ ...(sourceTsconfig ? { env: { ...process.env, TSX_TSCONFIG_PATH: sourceTsconfig } } : {}),
128
+ });
129
+ const terminate = () => child.kill('SIGTERM');
130
+ const hangup = () => child.kill('SIGHUP');
131
+ // Group Ctrl-C reaches the child: do not forward it twice. Programmatic callers
132
+ // must use SIGTERM to cancel only this pid. Signal tests cover both delivery modes.
133
+ const interrupt = () => {};
134
+ process.on('SIGTERM', terminate);
135
+ process.on('SIGHUP', hangup);
136
+ process.on('SIGINT', interrupt);
137
+ child.once('error', (error) => fail(error.message));
138
+ child.once('exit', (code, signal) => {
139
+ process.off('SIGTERM', terminate);
140
+ process.off('SIGHUP', hangup);
141
+ process.off('SIGINT', interrupt);
142
+ if (signal) process.kill(process.pid, signal);
143
+ else process.exit(code ?? 1);
144
+ });
@@ -0,0 +1,64 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import process from 'node:process';
4
+ import { dirname, join } from 'node:path';
5
+
6
+ // Registry settings may deliberately use a provider token. Preserve referenced
7
+ // names before filtering known credentials; native-build and network settings pass through.
8
+ export function npmEnvironment(source) {
9
+ const env = { ...source };
10
+ const find = (name) => Object.keys(env).find((key) => key.toLowerCase() === name);
11
+ const get = (name) => {
12
+ const key = find(name);
13
+ return key === undefined ? undefined : env[key];
14
+ };
15
+ const referenced = new Set();
16
+ // Match npm's optional references and escaped dollar signs in configuration.
17
+ const envExpr = /(?<!\\)(\\*)\$\{([^${}?]+)(\?)?\}/g;
18
+ const collect = (text) => {
19
+ for (const match of text.matchAll(envExpr)) {
20
+ if (match[1].length % 2 === 0) referenced.add(match[2].toLowerCase());
21
+ }
22
+ };
23
+ const expand = (path) =>
24
+ path.replace(envExpr, (whole, escapes, key, optional) => {
25
+ if (escapes.length % 2) return whole.slice((escapes.length + 1) / 2);
26
+ const value = env[key] ?? (optional ? '' : '${' + key + '}');
27
+ return escapes.slice(escapes.length / 2) + value;
28
+ });
29
+ const home = get('home') ?? get('userprofile') ?? homedir();
30
+ const files = [
31
+ get('npm_config_userconfig') ?? join(home, '.npmrc'),
32
+ get('npm_config_globalconfig') ??
33
+ join(get('npm_config_prefix') ?? dirname(dirname(process.execPath)), 'etc/npmrc'),
34
+ ];
35
+ for (const file of files) {
36
+ try {
37
+ const text = readFileSync(expand(file).replace(/^~(?=[/\\])/, home), 'utf8');
38
+ collect(
39
+ text
40
+ .split('\n')
41
+ .filter((line) => !/^\s*[#;]/.test(line))
42
+ .join('\n'),
43
+ );
44
+ } catch {
45
+ // Missing/unreadable npm settings are handled by npm itself.
46
+ }
47
+ }
48
+ for (const [key, value] of Object.entries(env)) {
49
+ if (/^npm_config_/i.test(key)) collect(value);
50
+ }
51
+ for (const key of Object.keys(env)) {
52
+ if (/^npm_|^node_auth_token$/i.test(key) || referenced.has(key.toLowerCase())) continue;
53
+ if (
54
+ /^(ANTHROPIC_|OPENAI_|CLAUDE_CODE_OAUTH_|GEMINI_|CODEX_|XAI_|GROK_|GOOGLE_API_KEY$|AWS_ACCESS_KEY_ID$|AWS_SECRET_ACCESS_KEY$|AWS_SESSION_TOKEN$|GH_TOKEN$|GITHUB_TOKEN$)/i.test(
55
+ key,
56
+ ) ||
57
+ /(_API_KEY|_SECRET|_SECRET_KEY|_CLIENT_SECRET|_PASSWORD|_ACCESS_TOKEN|_AUTH_TOKEN|_BOT_TOKEN)$|^HF_TOKEN$/i.test(
58
+ key,
59
+ )
60
+ )
61
+ delete env[key];
62
+ }
63
+ return env;
64
+ }
@@ -0,0 +1,20 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+
4
+ // The nearest manifest owns the bin; an unrelated parent must not vouch for it.
5
+ export function ownsPackage(bin, version) {
6
+ try {
7
+ let directory = dirname(bin);
8
+ while (directory !== dirname(directory)) {
9
+ const manifest = join(directory, 'package.json');
10
+ if (existsSync(manifest)) {
11
+ const pkg = JSON.parse(readFileSync(manifest, 'utf8'));
12
+ return pkg.name === '@tienne/gestalt' && pkg.version === version;
13
+ }
14
+ directory = dirname(directory);
15
+ }
16
+ } catch {
17
+ return false;
18
+ }
19
+ return false;
20
+ }
@@ -0,0 +1,22 @@
1
+ import process from 'node:process';
2
+ import { realpathSync } from 'node:fs';
3
+ import { delimiter, join } from 'node:path';
4
+ import { ownsPackage } from './package-owner.mjs';
5
+
6
+ // npm exec prepends its package bin directory. Verify the owning package instead
7
+ // of accepting a same-named executable inherited from the user's PATH.
8
+ const expected = process.argv[2];
9
+ for (const entry of (process.env.PATH ?? '').split(delimiter)) {
10
+ if (!entry) continue;
11
+ const candidate = join(entry, 'gestalt');
12
+ try {
13
+ const bin = realpathSync(candidate);
14
+ if (ownsPackage(bin, expected)) {
15
+ process.stdout.write(JSON.stringify(bin));
16
+ process.exit(0);
17
+ }
18
+ } catch {
19
+ // Broken PATH entries and unrelated packages do not resolve this pin.
20
+ }
21
+ }
22
+ process.exitCode = 1;
@@ -1,5 +1,7 @@
1
1
  # 에이전트를 서브에이전트로 위임하기 (공유 규칙)
2
2
 
3
+ > **Codex 등 다른 호스트에서는** [`host-runtime.md`](./host-runtime.md)를 먼저 적용한다. `inherit`는 모델 인자 생략이며 Claude 별칭 폴백을 강제하지 않는다.
4
+
3
5
  > **현재 적용: `review`, `pr`, `brief`, `presentation`, `review-reply`, `slack-send`, `jira-create`, `ship`, `explain`, `research`, `ux-research`, `reverse-plan`.** 에이전트를 불러 쓰는 스킬은 전부 위임한다. 새 스킬이 `ges_agent get`을 메인에서 하면 그건 예외가 아니라 빠뜨린 것이다.
4
6
 
5
7
  ## 왜 필요한가
@@ -38,6 +40,13 @@
38
40
 
39
41
  ## 어떻게 위임하나
40
42
 
43
+ Codex는 [`codex-delegation-examples.md`](./codex-delegation-examples.md)의 네이티브 호출 예시를 따른다.
44
+ 현재 제공된 스키마를 확인하고 `inherit`이면 모델 인자를 생략한다. 아래 `Agent`, `Explore`,
45
+ `subagent_type`과 도구 권한 설명은 Claude Code 기준이며 Codex 호출 인자로 옮기지 않는다.
46
+ 페르소나 조회, 룰북 절대 경로와 입력 가드 등 공통 위임 규칙은 그대로 적용한다.
47
+
48
+ ### Claude Code 호출
49
+
41
50
  **`subagent_type`에 `gestalt:<에이전트명>`을 넣는다.** 리뷰어 전부와 스킬이 이렇게 띄우는 에이전트는 플러그인 `agents/`에 Claude Code 에이전트로도 들어 있다. AGENT.md를 `pnpm build:agents`가 옮겨 뽑은 것이라 지침은 `ges_agent`가 주는 것과 같다. 빌드가 스킬 문서의 `subagent_type: "gestalt:<이름>"`을 모아 뽑을 대상을 정하므로 새 스킬이 이 줄을 쓰면 다음 빌드에 함께 생긴다. 이때 `scripts/build-claude-agents.ts`의 한 줄 설명 표에 항목을 넣어야 빌드가 통과한다.
42
51
 
43
52
  이렇게 띄우면 AGENT.md 본문이 서브에이전트의 시스템 프롬프트로 바로 실린다. 프롬프트에서 페르소나를 가져오게 할 필요가 없다.
@@ -118,7 +127,7 @@ Agent {
118
127
 
119
128
  ## 실패했을 때
120
129
 
121
- `gestalt:` 타입을 못 찾으면 위의 Explore 폴백으로 간다. 폴백한 서브에이전트가 `ges_agent`에 닿지 못하면 (MCP 미연결 등) 아래를 순서대로 본다.
130
+ `gestalt:` 타입을 못 찾으면 위의 Explore 폴백으로 간다. Codex의 도구 부재와 독립 판정 제한은 [`host-runtime.md`](./host-runtime.md)를 우선한다. 폴백한 서브에이전트가 `ges_agent`에 닿지 못하면 (MCP 미연결 등) 아래를 순서대로 본다.
122
131
 
123
132
  1. **페르소나 없이 임의로 수행하지 않는다.** 어투와 판정 기준이 에이전트에 있는데 그걸 못 읽었으면 결과가 규칙을 안 지킨다. 이건 어느 경우에도 금지다.
124
133
  2. **메인 세션이 대신 `ges_agent get`을 해서 직접 수행하는 폴백은 쓸 수 있다.** 페르소나는 확보되니 결과는 규칙을 지킨다. 다만 컨텍스트 절감이 사라지므로 그 사실을 완료 보고에 밝힌다.
@@ -1,5 +1,7 @@
1
1
  # 에이전트 tier로 모델 고르기 (공유 규칙)
2
2
 
3
+ > **Codex 등 다른 호스트에서는** [`host-runtime.md`](./host-runtime.md)를 먼저 적용한다. `inherit`는 모델 인자 생략이며 Claude 별칭 폴백을 강제하지 않는다.
4
+
3
5
  `ges_agent`의 `get`과 `list`는 둘 다 `tier`와 **해석된 `model`** 을 돌려준다. 에이전트 frontmatter의
4
6
  tier가 "이 역할이 어느 정도 모델을 필요로 하나"를 선언하고 서버가 그걸 호스트 Agent 도구가 받는
5
7
  별칭으로 옮겨준 값이다. 둘의 차이는 `systemPrompt`가 딸려오느냐뿐이다. `get`은 한 에이전트의