@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.
- package/CLAUDE.md +4 -3
- package/README.ko.md +20 -0
- package/README.md +28 -0
- package/dist/package.json +6 -4
- package/dist/plugin/.codex-plugin/plugin.json +40 -0
- package/dist/plugin/review-agents/writing-reviewer/AGENT.md +4 -2
- package/dist/plugin/role-agents/_shared/references/ai-tell-quick-rules.md +2 -1
- package/dist/plugin/role-agents/humanize-monolith/AGENT.md +5 -2
- package/dist/plugin/scripts/acquisition.mjs +96 -0
- package/dist/plugin/scripts/gestalt-cli.mjs +144 -0
- package/dist/plugin/scripts/npm-environment.mjs +64 -0
- package/dist/plugin/scripts/package-owner.mjs +20 -0
- package/dist/plugin/scripts/resolve-cli.mjs +22 -0
- package/dist/plugin/skills/_shared/agent-delegation.md +10 -1
- package/dist/plugin/skills/_shared/agent-model.md +2 -0
- package/dist/plugin/skills/_shared/cli-launcher.md +47 -36
- package/dist/plugin/skills/_shared/codex-delegation-examples.md +99 -0
- package/dist/plugin/skills/_shared/host-runtime.md +66 -0
- package/dist/plugin/skills/_shared/question-capabilities.md +30 -0
- package/dist/plugin/skills/agent/SKILL.md +2 -0
- package/dist/plugin/skills/architecture/SKILL.md +2 -0
- package/dist/plugin/skills/blast-radius/SKILL.md +2 -0
- package/dist/plugin/skills/brief/SKILL.md +2 -0
- package/dist/plugin/skills/build-graph/SKILL.md +2 -0
- package/dist/plugin/skills/diff-radius/SKILL.md +2 -0
- package/dist/plugin/skills/dispatch/SKILL.md +2 -0
- package/dist/plugin/skills/doubt/SKILL.md +4 -2
- package/dist/plugin/skills/execute/SKILL.md +3 -1
- package/dist/plugin/skills/explain/SKILL.md +2 -0
- package/dist/plugin/skills/handoff/SKILL.md +2 -0
- package/dist/plugin/skills/interview/SKILL.md +8 -0
- package/dist/plugin/skills/jira-create/SKILL.md +2 -0
- package/dist/plugin/skills/lens/SKILL.md +2 -0
- package/dist/plugin/skills/local-pr/SKILL.md +4 -2
- package/dist/plugin/skills/market-scan/SKILL.md +2 -0
- package/dist/plugin/skills/pr/SKILL.md +6 -14
- package/dist/plugin/skills/presentation/SKILL.md +2 -0
- package/dist/plugin/skills/research/SKILL.md +2 -0
- package/dist/plugin/skills/reverse-plan/SKILL.md +2 -0
- package/dist/plugin/skills/review/SKILL.md +19 -18
- package/dist/plugin/skills/review-loop/SKILL.md +13 -10
- package/dist/plugin/skills/review-reply/SKILL.md +7 -15
- package/dist/plugin/skills/screen-ref/SKILL.md +2 -0
- package/dist/plugin/skills/setup/SKILL.md +14 -9
- package/dist/plugin/skills/share/SKILL.md +4 -2
- package/dist/plugin/skills/ship/SKILL.md +3 -1
- package/dist/plugin/skills/slack-send/SKILL.md +2 -0
- package/dist/plugin/skills/solve/SKILL.md +2 -0
- package/dist/plugin/skills/spec/SKILL.md +3 -1
- package/dist/plugin/skills/ux-research/SKILL.md +2 -0
- package/dist/plugin/skills/video-digest/SKILL.md +2 -0
- package/dist/schemas/gestalt.schema.json +25 -4
- package/dist/src/cli/commands/init.d.ts +2 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +6 -2
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/setup.d.ts +4 -1
- package/dist/src/cli/commands/setup.d.ts.map +1 -1
- package/dist/src/cli/commands/setup.js +2 -1
- package/dist/src/cli/commands/setup.js.map +1 -1
- package/dist/src/cli/index.d.ts.map +1 -1
- package/dist/src/cli/index.js +15 -3
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/core/config.d.ts +169 -150
- package/dist/src/core/config.d.ts.map +1 -1
- package/dist/src/core/config.js +41 -11
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/model-diagnostics.d.ts +9 -0
- package/dist/src/core/model-diagnostics.d.ts.map +1 -0
- package/dist/src/core/model-diagnostics.js +47 -0
- package/dist/src/core/model-diagnostics.js.map +1 -0
- package/dist/src/core/types.d.ts +3 -3
- package/dist/src/execute/prompts.d.ts.map +1 -1
- package/dist/src/execute/prompts.js +4 -1
- package/dist/src/execute/prompts.js.map +1 -1
- package/dist/src/mcp/client-host.d.ts +12 -0
- package/dist/src/mcp/client-host.d.ts.map +1 -0
- package/dist/src/mcp/client-host.js +41 -0
- package/dist/src/mcp/client-host.js.map +1 -0
- package/dist/src/mcp/server.d.ts.map +1 -1
- package/dist/src/mcp/server.js +20 -6
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/mcp/tools/status.d.ts +22 -7
- package/dist/src/mcp/tools/status.d.ts.map +1 -1
- package/dist/src/mcp/tools/status.js +8 -3
- package/dist/src/mcp/tools/status.js.map +1 -1
- package/package.json +6 -4
- package/plugin/.codex-plugin/plugin.json +2 -2
- package/plugin/.mcp.json +1 -1
- package/plugin/mcp.json +1 -1
- package/plugin/review-agents/writing-reviewer/AGENT.md +4 -2
- package/plugin/role-agents/_shared/references/ai-tell-quick-rules.md +2 -1
- package/plugin/role-agents/humanize-monolith/AGENT.md +5 -2
- package/plugin/scripts/acquisition.mjs +96 -0
- package/plugin/scripts/gestalt-cli.mjs +144 -0
- package/plugin/scripts/npm-environment.mjs +64 -0
- package/plugin/scripts/package-owner.mjs +20 -0
- package/plugin/scripts/resolve-cli.mjs +22 -0
- package/plugin/skills/_shared/agent-delegation.md +10 -1
- package/plugin/skills/_shared/agent-model.md +2 -0
- package/plugin/skills/_shared/cli-launcher.md +47 -36
- package/plugin/skills/_shared/codex-delegation-examples.md +99 -0
- package/plugin/skills/_shared/host-runtime.md +66 -0
- package/plugin/skills/_shared/question-capabilities.md +30 -0
- package/plugin/skills/agent/SKILL.md +2 -0
- package/plugin/skills/architecture/SKILL.md +2 -0
- package/plugin/skills/blast-radius/SKILL.md +2 -0
- package/plugin/skills/brief/SKILL.md +2 -0
- package/plugin/skills/build-graph/SKILL.md +2 -0
- package/plugin/skills/diff-radius/SKILL.md +2 -0
- package/plugin/skills/dispatch/SKILL.md +2 -0
- package/plugin/skills/doubt/SKILL.md +4 -2
- package/plugin/skills/execute/SKILL.md +3 -1
- package/plugin/skills/explain/SKILL.md +2 -0
- package/plugin/skills/handoff/SKILL.md +2 -0
- package/plugin/skills/interview/SKILL.md +8 -0
- package/plugin/skills/jira-create/SKILL.md +2 -0
- package/plugin/skills/lens/SKILL.md +2 -0
- package/plugin/skills/local-pr/SKILL.md +4 -2
- package/plugin/skills/market-scan/SKILL.md +2 -0
- package/plugin/skills/pr/SKILL.md +6 -14
- package/plugin/skills/presentation/SKILL.md +2 -0
- package/plugin/skills/research/SKILL.md +2 -0
- package/plugin/skills/reverse-plan/SKILL.md +2 -0
- package/plugin/skills/review/SKILL.md +19 -18
- package/plugin/skills/review-loop/SKILL.md +13 -10
- package/plugin/skills/review-reply/SKILL.md +7 -15
- package/plugin/skills/screen-ref/SKILL.md +2 -0
- package/plugin/skills/setup/SKILL.md +14 -9
- package/plugin/skills/share/SKILL.md +4 -2
- package/plugin/skills/ship/SKILL.md +3 -1
- package/plugin/skills/slack-send/SKILL.md +2 -0
- package/plugin/skills/solve/SKILL.md +2 -0
- package/plugin/skills/spec/SKILL.md +3 -1
- package/plugin/skills/ux-research/SKILL.md +2 -0
- package/plugin/skills/video-digest/SKILL.md +2 -0
- package/schemas/gestalt.schema.json +25 -4
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { diagnoseModelAliases } from '../../core/model-diagnostics.js';
|
|
1
2
|
import { ExecuteSessionRepository } from '../../execute/repository.js';
|
|
2
3
|
import { getVersion, getCachedUpdateResult, getSessionVersion } from '../../core/version.js';
|
|
3
4
|
import { resolveStatusSessionId } from '../session-selector.js';
|
|
@@ -27,8 +28,12 @@ function clamp(value, max) {
|
|
|
27
28
|
* handleStatusPassthrough를 탄다. 두 곳이 각자 이 객체를 만들면 한쪽에만 필드를
|
|
28
29
|
* 넣어도 테스트는 통과하고 실제 호출에는 안 나온다. 그래서 한 곳에서 만든다.
|
|
29
30
|
*/
|
|
30
|
-
export function buildStatusConfigInfo(config) {
|
|
31
|
+
export function buildStatusConfigInfo(config, clientDetection) {
|
|
31
32
|
return {
|
|
33
|
+
client: config?.client ?? null,
|
|
34
|
+
hostClient: config?.hostClient ?? null,
|
|
35
|
+
clientDetection: clientDetection ?? null,
|
|
36
|
+
modelWarnings: config ? diagnoseModelAliases(config) : [],
|
|
32
37
|
reasoningModel: config?.reasoningModel ?? null,
|
|
33
38
|
reasoningModelFallback: config?.reasoningModelFallback ?? null,
|
|
34
39
|
// 등록 에이전트가 없는 인라인 서브에이전트(예: review-reply의 스레드 분류)도
|
|
@@ -53,7 +58,7 @@ export function buildStatusConfigInfo(config) {
|
|
|
53
58
|
ruleSourceWarningCount: (config?.ruleSourceWarnings ?? []).length,
|
|
54
59
|
};
|
|
55
60
|
}
|
|
56
|
-
export function handleStatus(engine, rawInput, eventStore, config) {
|
|
61
|
+
export function handleStatus(engine, rawInput, eventStore, config, clientDetection) {
|
|
57
62
|
const updateResult = getCachedUpdateResult();
|
|
58
63
|
// current 는 이 세션이 실제로 로드한 플러그인 버전이다. 서버 자기 버전과 어긋날 수
|
|
59
64
|
// 있어서 server 를 따로 싣는다 — 둘이 다르면 GESTALT_MCP_BIN 지정이나 핀을 못 읽은 경우처럼
|
|
@@ -66,7 +71,7 @@ export function handleStatus(engine, rawInput, eventStore, config) {
|
|
|
66
71
|
latest: updateResult?.latestVersion ?? null,
|
|
67
72
|
updateAvailable: updateResult?.updateAvailable ?? false,
|
|
68
73
|
};
|
|
69
|
-
const statusConfigInfo = buildStatusConfigInfo(config);
|
|
74
|
+
const statusConfigInfo = buildStatusConfigInfo(config, clientDetection);
|
|
70
75
|
const sessionType = rawInput.sessionType ?? 'all';
|
|
71
76
|
const resolvedSessionId = rawInput.sessionId
|
|
72
77
|
? resolveStatusSessionId(rawInput.sessionId, sessionType, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../../src/mcp/tools/status.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../../src/mcp/tools/status.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tienne/gestalt",
|
|
3
|
-
"version": "0.
|
|
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",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gestalt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.122.0",
|
|
4
4
|
"description": "Gestalt psychology-driven AI development harness. Transforms scattered requirements into structured, validated specifications through interactive interviews.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tienne"
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"interface": {
|
|
23
23
|
"displayName": "Gestalt",
|
|
24
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
|
|
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
26
|
"developerName": "tienne",
|
|
27
27
|
"category": "Developer Tools",
|
|
28
28
|
"capabilities": [
|
package/plugin/.mcp.json
CHANGED
package/plugin/mcp.json
CHANGED
|
@@ -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`은 한 에이전트의
|