@mindfoldhq/trellis 0.6.6 → 0.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +37 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/channel/adapters/codex.d.ts +7 -1
- package/dist/commands/channel/adapters/codex.d.ts.map +1 -1
- package/dist/commands/channel/adapters/codex.js +19 -2
- package/dist/commands/channel/adapters/codex.js.map +1 -1
- package/dist/commands/channel/adapters/index.d.ts +3 -1
- package/dist/commands/channel/adapters/index.d.ts.map +1 -1
- package/dist/commands/channel/adapters/index.js +1 -1
- package/dist/commands/channel/adapters/index.js.map +1 -1
- package/dist/commands/channel/guard.d.ts.map +1 -1
- package/dist/commands/channel/guard.js +7 -2
- package/dist/commands/channel/guard.js.map +1 -1
- package/dist/commands/channel/index.d.ts.map +1 -1
- package/dist/commands/channel/index.js +4 -0
- package/dist/commands/channel/index.js.map +1 -1
- package/dist/commands/channel/spawn.d.ts +3 -0
- package/dist/commands/channel/spawn.d.ts.map +1 -1
- package/dist/commands/channel/spawn.js +1 -0
- package/dist/commands/channel/spawn.js.map +1 -1
- package/dist/commands/channel/store/paths.d.ts +14 -0
- package/dist/commands/channel/store/paths.d.ts.map +1 -1
- package/dist/commands/channel/store/paths.js +26 -0
- package/dist/commands/channel/store/paths.js.map +1 -1
- package/dist/commands/channel/supervisor.d.ts +3 -0
- package/dist/commands/channel/supervisor.d.ts.map +1 -1
- package/dist/commands/channel/supervisor.js +1 -0
- package/dist/commands/channel/supervisor.js.map +1 -1
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mem.d.ts.map +1 -1
- package/dist/commands/mem.js +30 -9
- package/dist/commands/mem.js.map +1 -1
- package/dist/commands/uninstall.d.ts +9 -0
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +67 -3
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.d.ts +40 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +126 -34
- package/dist/commands/update.js.map +1 -1
- package/dist/configurators/codex.d.ts.map +1 -1
- package/dist/configurators/codex.js +11 -12
- package/dist/configurators/codex.js.map +1 -1
- package/dist/configurators/grok.d.ts +22 -0
- package/dist/configurators/grok.d.ts.map +1 -0
- package/dist/configurators/grok.js +58 -0
- package/dist/configurators/grok.js.map +1 -0
- package/dist/configurators/index.d.ts.map +1 -1
- package/dist/configurators/index.js +12 -2
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/kimi.d.ts +29 -0
- package/dist/configurators/kimi.d.ts.map +1 -0
- package/dist/configurators/kimi.js +81 -0
- package/dist/configurators/kimi.js.map +1 -0
- package/dist/configurators/pi.js +9 -9
- package/dist/configurators/pi.js.map +1 -1
- package/dist/configurators/shared.d.ts.map +1 -1
- package/dist/configurators/shared.js +11 -4
- package/dist/configurators/shared.js.map +1 -1
- package/dist/configurators/zcode.d.ts +9 -4
- package/dist/configurators/zcode.d.ts.map +1 -1
- package/dist/configurators/zcode.js +41 -12
- package/dist/configurators/zcode.js.map +1 -1
- package/dist/migrations/manifests/0.5.0-beta.0.json +1 -1
- package/dist/migrations/manifests/0.6.7.json +9 -0
- package/dist/migrations/manifests/0.6.8.json +17 -0
- package/dist/templates/codex/agents/trellis-check.toml +5 -12
- package/dist/templates/codex/agents/trellis-implement.toml +5 -12
- package/dist/templates/codex/agents/trellis-research.toml +13 -15
- package/dist/templates/codex/config.toml +11 -0
- package/dist/templates/codex/hooks/session-start.py +9 -3
- package/dist/templates/codex/hooks.json +12 -0
- package/dist/templates/codex/skills/brainstorm/SKILL.md +40 -13
- package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +2 -2
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/agents.md +1 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/hooks-and-settings.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/overview.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md +8 -4
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +2 -1
- package/dist/templates/common/bundled-skills/trellis-session-insight/SKILL.md +1 -1
- package/dist/templates/common/skills/brainstorm.md +40 -13
- package/dist/templates/copilot/prompts/brainstorm.prompt.md +40 -13
- package/dist/templates/grok/agents/trellis-check.md +105 -0
- package/dist/templates/grok/agents/trellis-implement.md +116 -0
- package/dist/templates/grok/agents/trellis-research.md +146 -0
- package/dist/templates/grok/index.d.ts +16 -0
- package/dist/templates/grok/index.d.ts.map +1 -0
- package/dist/templates/grok/index.js +19 -0
- package/dist/templates/grok/index.js.map +1 -0
- package/dist/templates/kimi/agents/trellis-check.md +103 -0
- package/dist/templates/kimi/agents/trellis-implement.md +114 -0
- package/dist/templates/kimi/agents/trellis-research.md +140 -0
- package/dist/templates/kimi/index.d.ts +19 -0
- package/dist/templates/kimi/index.d.ts.map +1 -0
- package/dist/templates/kimi/index.js +22 -0
- package/dist/templates/kimi/index.js.map +1 -0
- package/dist/templates/omp/extensions/trellis/index.ts.txt +19 -5
- package/dist/templates/opencode/lib/session-utils.js +8 -2
- package/dist/templates/pi/extensions/trellis/index.ts.txt +8 -2
- package/dist/templates/pi/settings.json +0 -3
- package/dist/templates/shared-hooks/index.d.ts +11 -5
- package/dist/templates/shared-hooks/index.d.ts.map +1 -1
- package/dist/templates/shared-hooks/index.js +16 -5
- package/dist/templates/shared-hooks/index.js.map +1 -1
- package/dist/templates/shared-hooks/inject-subagent-context.py +151 -17
- package/dist/templates/shared-hooks/inject-workflow-state.py +50 -28
- package/dist/templates/shared-hooks/session-start.py +26 -6
- package/dist/templates/trellis/config.yaml +9 -7
- package/dist/templates/trellis/scripts/common/active_task.py +40 -13
- package/dist/templates/trellis/scripts/common/cli_adapter.py +69 -5
- package/dist/templates/trellis/scripts/common/config.py +16 -8
- package/dist/templates/trellis/scripts/common/git.py +36 -0
- package/dist/templates/trellis/scripts/common/io.py +28 -4
- package/dist/templates/trellis/scripts/common/task_context.py +19 -1
- package/dist/templates/trellis/scripts/common/task_store.py +98 -21
- package/dist/templates/trellis/scripts/common/task_utils.py +24 -0
- package/dist/templates/trellis/scripts/common/workflow_phase.py +19 -12
- package/dist/templates/trellis/scripts/task.py +85 -8
- package/dist/templates/trellis/workflow.md +20 -19
- package/dist/templates/zcode/agents/trellis-check.md +7 -0
- package/dist/templates/zcode/agents/trellis-implement.md +13 -4
- package/dist/templates/zcode/config.json +42 -0
- package/dist/templates/zcode/index.d.ts +10 -1
- package/dist/templates/zcode/index.d.ts.map +1 -1
- package/dist/templates/zcode/index.js +12 -2
- package/dist/templates/zcode/index.js.map +1 -1
- package/dist/types/ai-tools.d.ts +4 -4
- package/dist/types/ai-tools.d.ts.map +1 -1
- package/dist/types/ai-tools.js +76 -2
- package/dist/types/ai-tools.js.map +1 -1
- package/dist/utils/atomic-write.d.ts +12 -0
- package/dist/utils/atomic-write.d.ts.map +1 -0
- package/dist/utils/atomic-write.js +30 -0
- package/dist/utils/atomic-write.js.map +1 -0
- package/dist/utils/file-writer.d.ts.map +1 -1
- package/dist/utils/file-writer.js +6 -5
- package/dist/utils/file-writer.js.map +1 -1
- package/dist/utils/registry-config.d.ts.map +1 -1
- package/dist/utils/registry-config.js +3 -2
- package/dist/utils/registry-config.js.map +1 -1
- package/dist/utils/template-fetcher.d.ts.map +1 -1
- package/dist/utils/template-fetcher.js +35 -6
- package/dist/utils/template-fetcher.js.map +1 -1
- package/dist/utils/template-hash.d.ts.map +1 -1
- package/dist/utils/template-hash.js +2 -1
- package/dist/utils/template-hash.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: trellis-research
|
|
3
|
+
description: |
|
|
4
|
+
Code and tech search expert for Trellis. Finds files, patterns, and tech
|
|
5
|
+
solutions, and PERSISTS every finding to the current task's research/
|
|
6
|
+
directory. No code modifications outside that directory. On Kimi Code the
|
|
7
|
+
main session dispatches the built-in explore sub-agent with these
|
|
8
|
+
instructions.
|
|
9
|
+
---
|
|
10
|
+
# Research Agent
|
|
11
|
+
|
|
12
|
+
You are the Research Agent in the Trellis workflow.
|
|
13
|
+
|
|
14
|
+
## Core Principle
|
|
15
|
+
|
|
16
|
+
**You do one thing: find, explain, and PERSIST information.**
|
|
17
|
+
|
|
18
|
+
Conversations get compacted; files don't. Every research output MUST end up as a file under `{TASK_DIR}/research/`. Returning findings only through the chat reply is a failure - the caller cannot read them next session.
|
|
19
|
+
|
|
20
|
+
## Dispatch note (main session)
|
|
21
|
+
|
|
22
|
+
Kimi Code has no project-level custom sub-agent definitions — only the built-in `coder` / `explore` / `plan` sub-agents. The main session dispatches the built-in `explore` sub-agent via the Agent tool with a prompt that starts with `Active task: <path from task.py current>` and includes this skill's instructions (`.kimi-code/skills/trellis-research/SKILL.md`).
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Core Responsibilities
|
|
27
|
+
|
|
28
|
+
1. **Internal Search** - locate files/components, understand code logic, discover patterns
|
|
29
|
+
2. **External Search** - library docs, API references, best practices
|
|
30
|
+
3. **Persist** - write each research topic to `{TASK_DIR}/research/<topic>.md`
|
|
31
|
+
4. **Report** - return file paths + one-line summaries to the main agent, not full content
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Workflow
|
|
36
|
+
|
|
37
|
+
### Step 1: Resolve Current Task
|
|
38
|
+
|
|
39
|
+
Run `python3 ./.trellis/scripts/task.py current --source` to get the active task path. Prefer an `Active task: <path>` line in the dispatch prompt when present. If no active task is set, ask the user where to write output; do NOT guess.
|
|
40
|
+
|
|
41
|
+
Ensure `{TASK_DIR}/research/` exists before writing findings.
|
|
42
|
+
|
|
43
|
+
### Step 2: Understand Search Request
|
|
44
|
+
|
|
45
|
+
Classify the request as internal, external, or mixed. Determine scope and expected output shape.
|
|
46
|
+
|
|
47
|
+
### Step 3: Execute Search
|
|
48
|
+
|
|
49
|
+
Run independent searches in parallel where possible. Read relevant source files, specs, and documentation before forming conclusions.
|
|
50
|
+
|
|
51
|
+
### Step 4: Persist Each Topic
|
|
52
|
+
|
|
53
|
+
For each distinct research topic, write a markdown file at `{TASK_DIR}/research/<topic-slug>.md` using the File Format below.
|
|
54
|
+
|
|
55
|
+
### Step 5: Report to Main Agent
|
|
56
|
+
|
|
57
|
+
Reply with ONLY:
|
|
58
|
+
|
|
59
|
+
- List of files written, paths relative to repo root
|
|
60
|
+
- One-line summary per file
|
|
61
|
+
- Any critical caveats that the main agent needs to know right now
|
|
62
|
+
|
|
63
|
+
Do NOT paste full research content into the reply. The files are the contract.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Scope Limits
|
|
68
|
+
|
|
69
|
+
### Write Allowed
|
|
70
|
+
|
|
71
|
+
- `{TASK_DIR}/research/*.md` - your own output
|
|
72
|
+
- Creating `{TASK_DIR}/research/` if it doesn't exist
|
|
73
|
+
|
|
74
|
+
### Write Forbidden
|
|
75
|
+
|
|
76
|
+
- Code files (`src/`, `lib/`, etc.)
|
|
77
|
+
- Spec files (`.trellis/spec/`) - main agent should use the update-spec skill instead
|
|
78
|
+
- `.trellis/scripts/`, `.trellis/workflow.md`, platform config (`.kimi-code/`, `.claude/`, `.cursor/`, etc.)
|
|
79
|
+
- Other task directories
|
|
80
|
+
- Any git operation
|
|
81
|
+
|
|
82
|
+
If the user asks you to edit code, decline and suggest spawning `trellis-implement` instead.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## File Format
|
|
87
|
+
|
|
88
|
+
Each `{TASK_DIR}/research/<topic>.md` should follow:
|
|
89
|
+
|
|
90
|
+
```markdown
|
|
91
|
+
# Research: <topic>
|
|
92
|
+
|
|
93
|
+
- Query: <original query>
|
|
94
|
+
- Scope: internal / external / mixed
|
|
95
|
+
- Date: YYYY-MM-DD
|
|
96
|
+
|
|
97
|
+
## Findings
|
|
98
|
+
|
|
99
|
+
### Files Found
|
|
100
|
+
|
|
101
|
+
| File Path | Description |
|
|
102
|
+
|---|---|
|
|
103
|
+
| `src/services/xxx.ts` | Main implementation |
|
|
104
|
+
| `src/types/xxx.ts` | Type definitions |
|
|
105
|
+
|
|
106
|
+
### Code Patterns
|
|
107
|
+
|
|
108
|
+
<describe patterns, cite file:line>
|
|
109
|
+
|
|
110
|
+
### External References
|
|
111
|
+
|
|
112
|
+
- [Library X docs](url) - <why relevant, version constraints>
|
|
113
|
+
|
|
114
|
+
### Related Specs
|
|
115
|
+
|
|
116
|
+
- `.trellis/spec/xxx.md` - <description>
|
|
117
|
+
|
|
118
|
+
## Caveats / Not Found
|
|
119
|
+
|
|
120
|
+
<anything incomplete or uncertain>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Guidelines
|
|
126
|
+
|
|
127
|
+
### Do
|
|
128
|
+
|
|
129
|
+
- Provide specific file paths and line numbers
|
|
130
|
+
- Quote actual code snippets only when they are relevant
|
|
131
|
+
- Persist every topic to its own file
|
|
132
|
+
- Return file paths in your reply, not the full content
|
|
133
|
+
- Mark "not found" explicitly when searches come up empty
|
|
134
|
+
|
|
135
|
+
### Don't
|
|
136
|
+
|
|
137
|
+
- Don't write code or modify files outside `{TASK_DIR}/research/`
|
|
138
|
+
- Don't guess uncertain info
|
|
139
|
+
- Don't paste full research text into the reply
|
|
140
|
+
- Don't propose implementation changes unless the main agent explicitly asked for research options
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kimi Code template module.
|
|
3
|
+
*
|
|
4
|
+
* Kimi Code is a class-2 pull-based platform:
|
|
5
|
+
* - Workflow/bundled skills go to the shared `.agents/skills/` root via the
|
|
6
|
+
* neutral resolver (byte-identical to Codex/Gemini/Pi writes).
|
|
7
|
+
* - User-invocable entry points (`trellis-start` / `trellis-continue` /
|
|
8
|
+
* `trellis-finish-work`, invoked as `/skill:trellis-<name>`) and the Trellis
|
|
9
|
+
* agent prompts live under `.kimi-code/skills/<name>/SKILL.md`.
|
|
10
|
+
*
|
|
11
|
+
* Kimi has no project-level hooks/settings file Trellis may write and no
|
|
12
|
+
* custom sub-agent definitions (built-in coder/explore/plan only), so the
|
|
13
|
+
* Trellis agent prompts ship as skills; trellis-implement / trellis-check get
|
|
14
|
+
* the pull-based prelude, trellis-research stays standalone.
|
|
15
|
+
*/
|
|
16
|
+
import { type AgentTemplate } from "../template-utils.js";
|
|
17
|
+
/** Trellis agent prompts (trellis-implement, trellis-check, trellis-research), installed as Kimi skills. */
|
|
18
|
+
export declare function getAllAgents(): AgentTemplate[];
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/kimi/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAIhF,4GAA4G;AAC5G,wBAAgB,YAAY,IAAI,aAAa,EAAE,CAE9C"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kimi Code template module.
|
|
3
|
+
*
|
|
4
|
+
* Kimi Code is a class-2 pull-based platform:
|
|
5
|
+
* - Workflow/bundled skills go to the shared `.agents/skills/` root via the
|
|
6
|
+
* neutral resolver (byte-identical to Codex/Gemini/Pi writes).
|
|
7
|
+
* - User-invocable entry points (`trellis-start` / `trellis-continue` /
|
|
8
|
+
* `trellis-finish-work`, invoked as `/skill:trellis-<name>`) and the Trellis
|
|
9
|
+
* agent prompts live under `.kimi-code/skills/<name>/SKILL.md`.
|
|
10
|
+
*
|
|
11
|
+
* Kimi has no project-level hooks/settings file Trellis may write and no
|
|
12
|
+
* custom sub-agent definitions (built-in coder/explore/plan only), so the
|
|
13
|
+
* Trellis agent prompts ship as skills; trellis-implement / trellis-check get
|
|
14
|
+
* the pull-based prelude, trellis-research stays standalone.
|
|
15
|
+
*/
|
|
16
|
+
import { createTemplateReader } from "../template-utils.js";
|
|
17
|
+
const { listMdAgents } = createTemplateReader(import.meta.url);
|
|
18
|
+
/** Trellis agent prompts (trellis-implement, trellis-check, trellis-research), installed as Kimi skills. */
|
|
19
|
+
export function getAllAgents() {
|
|
20
|
+
return listMdAgents();
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/kimi/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,oBAAoB,EAAsB,MAAM,sBAAsB,CAAC;AAEhF,MAAM,EAAE,YAAY,EAAE,GAAG,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE/D,4GAA4G;AAC5G,MAAM,UAAU,YAAY;IAC1B,OAAO,YAAY,EAAE,CAAC;AACxB,CAAC"}
|
|
@@ -40,7 +40,7 @@ function buildContextKey(platformName: string, kind: string, value: string): str
|
|
|
40
40
|
return safeValue ? `${platformName}_${safeValue}` : `${platformName}_${hashValue(value)}`;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
function deriveContextKey(ctx?: { sessionManager?: { getSessionId?: () => string; getSessionFile?: () => string } }): string | null {
|
|
43
|
+
function deriveContextKey(ctx?: { sessionManager?: { getSessionId?: () => string | undefined; getSessionFile?: () => string | undefined } }): string | null {
|
|
44
44
|
const sessionId = ctx?.sessionManager?.getSessionId?.();
|
|
45
45
|
if (sessionId) {
|
|
46
46
|
return buildContextKey("omp", "session", sessionId);
|
|
@@ -336,7 +336,7 @@ export default function(pi: ExtensionAPI): void {
|
|
|
336
336
|
let lastCompactionTs = 0;
|
|
337
337
|
let lastInjectionTs = 0;
|
|
338
338
|
|
|
339
|
-
const rememberContextKey = (ctx?: { sessionManager?: { getSessionId?: () => string; getSessionFile?: () => string } }): string | null => {
|
|
339
|
+
const rememberContextKey = (ctx?: { sessionManager?: { getSessionId?: () => string | undefined; getSessionFile?: () => string | undefined } }): string | null => {
|
|
340
340
|
const key = deriveContextKey(ctx);
|
|
341
341
|
if (!key) return null;
|
|
342
342
|
return key;
|
|
@@ -439,24 +439,38 @@ export default function(pi: ExtensionAPI): void {
|
|
|
439
439
|
messages: [
|
|
440
440
|
...event.messages,
|
|
441
441
|
{
|
|
442
|
-
role: "custom",
|
|
442
|
+
role: "custom" as const,
|
|
443
443
|
customType: "trellis-workflow-state",
|
|
444
444
|
content: cached.workflowMsg,
|
|
445
|
+
display: false,
|
|
445
446
|
timestamp: Date.now(),
|
|
446
447
|
},
|
|
447
448
|
],
|
|
448
449
|
};
|
|
449
450
|
});
|
|
450
451
|
|
|
452
|
+
// OMP passes Bash event.input through to the tool execution parameters, so
|
|
453
|
+
// inject the session key through the shell-agnostic env field. An explicit
|
|
454
|
+
// per-call value wins over the derived key.
|
|
455
|
+
pi.on("tool_call", (event, ctx) => {
|
|
456
|
+
if (event.toolName !== "bash") return;
|
|
457
|
+
const contextKey = rememberContextKey(ctx);
|
|
458
|
+
if (!contextKey) return;
|
|
459
|
+
const input = event.input as { env?: Record<string, string> };
|
|
460
|
+
input.env = {
|
|
461
|
+
TRELLIS_CONTEXT_ID: contextKey,
|
|
462
|
+
...input.env,
|
|
463
|
+
};
|
|
464
|
+
});
|
|
465
|
+
|
|
451
466
|
pi.on("input", async (_event, ctx) => {
|
|
452
467
|
if (!projectRoot) {
|
|
453
468
|
projectRoot = findProjectRoot(ctx.cwd);
|
|
454
469
|
}
|
|
455
470
|
// Resolve projectRoot on first input if session_start missed it
|
|
456
|
-
if (!projectRoot) return
|
|
471
|
+
if (!projectRoot) return;
|
|
457
472
|
const contextKey = rememberContextKey(ctx);
|
|
458
473
|
// Pre-warm the cache so before_agent_start and context can use it
|
|
459
474
|
turnCache.get(projectRoot, contextKey);
|
|
460
|
-
return { action: "continue" };
|
|
461
475
|
});
|
|
462
476
|
}
|
|
@@ -8,8 +8,14 @@ import { debugLog } from "./trellis-context.js"
|
|
|
8
8
|
const PYTHON_CMD = platform() === "win32" ? "python" : "python3"
|
|
9
9
|
|
|
10
10
|
const FIRST_REPLY_NOTICE = `<first-reply-notice>
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
On the first visible assistant reply in this session, briefly acknowledge that Trellis SessionStart context loaded.
|
|
12
|
+
Choose the acknowledgment language in this order:
|
|
13
|
+
1. Use the language of the user's current request (the user message that triggered this reply).
|
|
14
|
+
2. If that request has no clear natural language, use an explicitly established project communication language.
|
|
15
|
+
3. If neither provides a language, output the language-neutral fallback exactly: \`Trellis SessionStart ✓\`.
|
|
16
|
+
Continue directly with the user's request after the acknowledgment.
|
|
17
|
+
The acknowledgment must not alter the language used for the remainder of the response.
|
|
18
|
+
This notice is one-shot: do not repeat it after the first visible assistant reply in this session.
|
|
13
19
|
</first-reply-notice>`
|
|
14
20
|
|
|
15
21
|
function hasCuratedJsonlEntry(jsonlPath) {
|
|
@@ -84,8 +84,14 @@ const ABORT_KILL_GRACE_MS = 1500;
|
|
|
84
84
|
const SESSION_OVERVIEW_TIMEOUT_MS = 1500;
|
|
85
85
|
const THROTTLE_MS = 500;
|
|
86
86
|
const FIRST_REPLY_NOTICE = `<first-reply-notice>
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
On the first visible assistant reply in this session, briefly acknowledge that Trellis SessionStart context loaded.
|
|
88
|
+
Choose the acknowledgment language in this order:
|
|
89
|
+
1. Use the language of the user's current request (the user message that triggered this reply).
|
|
90
|
+
2. If that request has no clear natural language, use an explicitly established project communication language.
|
|
91
|
+
3. If neither provides a language, output the language-neutral fallback exactly: \`Trellis SessionStart ✓\`.
|
|
92
|
+
Continue directly with the user's request after the acknowledgment.
|
|
93
|
+
The acknowledgment must not alter the language used for the remainder of the response.
|
|
94
|
+
This notice is one-shot: do not repeat it after the first visible assistant reply in this session.
|
|
89
95
|
</first-reply-notice>`;
|
|
90
96
|
|
|
91
97
|
// ── State types ───────────────────────────────────────────────────────
|
|
@@ -12,7 +12,7 @@ export interface HookScript {
|
|
|
12
12
|
content: string;
|
|
13
13
|
}
|
|
14
14
|
export type SharedHookName = "session-start.py" | "inject-shell-session-context.py" | "inject-workflow-state.py" | "inject-subagent-context.py";
|
|
15
|
-
export type SharedHookPlatform = "claude" | "cursor" | "codex" | "gemini" | "qoder" | "copilot" | "codebuddy" | "droid" | "kiro" | "trae";
|
|
15
|
+
export type SharedHookPlatform = "claude" | "cursor" | "codex" | "gemini" | "qoder" | "copilot" | "codebuddy" | "droid" | "kiro" | "trae" | "zcode";
|
|
16
16
|
/**
|
|
17
17
|
* Which shared hooks each platform actually invokes. Single source of truth
|
|
18
18
|
* for shared-hook distribution — both `writeSharedHooks` (runtime install)
|
|
@@ -25,10 +25,11 @@ export type SharedHookPlatform = "claude" | "cursor" | "codex" | "gemini" | "qod
|
|
|
25
25
|
* - `inject-workflow-state.py` — every platform with a UserPromptSubmit
|
|
26
26
|
* (or equivalent) event. Kiro + codex self-included; platforms without
|
|
27
27
|
* per-turn main-session hooks are excluded.
|
|
28
|
-
* - `inject-subagent-context.py` —
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
28
|
+
* - `inject-subagent-context.py` — platforms with native sub-agent context
|
|
29
|
+
* delivery. Most use a PreToolUse prompt mutation; Codex uses its
|
|
30
|
+
* SubagentStart `additionalContext` event. Class-2 (pull-based) platforms
|
|
31
|
+
* such as copilot, gemini, qoder, and trae still load context from a
|
|
32
|
+
* self-loading agent profile.
|
|
32
33
|
* - Kiro supports per-turn + spawn hooks on both surfaces (per the official
|
|
33
34
|
* docs https://kiro.dev/docs/cli/hooks/): the CLI custom agent declares
|
|
34
35
|
* `hooks.userPromptSubmit` + `hooks.agentSpawn`, and the IDE declares a
|
|
@@ -42,6 +43,11 @@ export type SharedHookPlatform = "claude" | "cursor" | "codex" | "gemini" | "qod
|
|
|
42
43
|
* or opt in to the Trellis one via `trellis init --with-statusline`
|
|
43
44
|
* (installed from `templates/claude/hooks/`, not from this table — no
|
|
44
45
|
* other platform has a statusLine event).
|
|
46
|
+
* - ZCode (3.x) ships a workspace hook config at `.zcode/config.json` covering
|
|
47
|
+
* SessionStart + UserPromptSubmit in the main session, plus PreToolUse for
|
|
48
|
+
* `Agent|Task`. Live probing confirmed `hookSpecificOutput.updatedInput`
|
|
49
|
+
* reaches the sub-agent prompt, so ZCode is class-1 and ships
|
|
50
|
+
* `inject-subagent-context.py`.
|
|
45
51
|
*/
|
|
46
52
|
export declare const SHARED_HOOKS_BY_PLATFORM: Record<SharedHookPlatform, readonly SharedHookName[]>;
|
|
47
53
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/shared-hooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,MAAM,WAAW,UAAU;IACzB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,iCAAiC,GACjC,0BAA0B,GAC1B,4BAA4B,CAAC;AAEjC,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,OAAO,GACP,SAAS,GACT,WAAW,GACX,OAAO,GACP,MAAM,GACN,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/shared-hooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,MAAM,WAAW,UAAU;IACzB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,iCAAiC,GACjC,0BAA0B,GAC1B,4BAA4B,CAAC;AAEjC,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,OAAO,GACP,SAAS,GACT,WAAW,GACX,OAAO,GACP,MAAM,GACN,MAAM,GACN,OAAO,CAAC;AAEZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAC3C,kBAAkB,EAClB,SAAS,cAAc,EAAE,CAqC1B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,UAAU,EAAE,CAWnD;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,kBAAkB,GAC3B,UAAU,EAAE,CAGd"}
|
|
@@ -25,10 +25,11 @@ function readTemplate(relativePath) {
|
|
|
25
25
|
* - `inject-workflow-state.py` — every platform with a UserPromptSubmit
|
|
26
26
|
* (or equivalent) event. Kiro + codex self-included; platforms without
|
|
27
27
|
* per-turn main-session hooks are excluded.
|
|
28
|
-
* - `inject-subagent-context.py` —
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
28
|
+
* - `inject-subagent-context.py` — platforms with native sub-agent context
|
|
29
|
+
* delivery. Most use a PreToolUse prompt mutation; Codex uses its
|
|
30
|
+
* SubagentStart `additionalContext` event. Class-2 (pull-based) platforms
|
|
31
|
+
* such as copilot, gemini, qoder, and trae still load context from a
|
|
32
|
+
* self-loading agent profile.
|
|
32
33
|
* - Kiro supports per-turn + spawn hooks on both surfaces (per the official
|
|
33
34
|
* docs https://kiro.dev/docs/cli/hooks/): the CLI custom agent declares
|
|
34
35
|
* `hooks.userPromptSubmit` + `hooks.agentSpawn`, and the IDE declares a
|
|
@@ -42,6 +43,11 @@ function readTemplate(relativePath) {
|
|
|
42
43
|
* or opt in to the Trellis one via `trellis init --with-statusline`
|
|
43
44
|
* (installed from `templates/claude/hooks/`, not from this table — no
|
|
44
45
|
* other platform has a statusLine event).
|
|
46
|
+
* - ZCode (3.x) ships a workspace hook config at `.zcode/config.json` covering
|
|
47
|
+
* SessionStart + UserPromptSubmit in the main session, plus PreToolUse for
|
|
48
|
+
* `Agent|Task`. Live probing confirmed `hookSpecificOutput.updatedInput`
|
|
49
|
+
* reaches the sub-agent prompt, so ZCode is class-1 and ships
|
|
50
|
+
* `inject-subagent-context.py`.
|
|
45
51
|
*/
|
|
46
52
|
export const SHARED_HOOKS_BY_PLATFORM = {
|
|
47
53
|
claude: [
|
|
@@ -54,7 +60,7 @@ export const SHARED_HOOKS_BY_PLATFORM = {
|
|
|
54
60
|
"inject-shell-session-context.py",
|
|
55
61
|
"inject-subagent-context.py",
|
|
56
62
|
],
|
|
57
|
-
codex: ["inject-workflow-state.py"],
|
|
63
|
+
codex: ["inject-workflow-state.py", "inject-subagent-context.py"],
|
|
58
64
|
gemini: ["session-start.py", "inject-workflow-state.py"],
|
|
59
65
|
qoder: ["session-start.py", "inject-workflow-state.py"],
|
|
60
66
|
copilot: ["inject-workflow-state.py"],
|
|
@@ -74,6 +80,11 @@ export const SHARED_HOOKS_BY_PLATFORM = {
|
|
|
74
80
|
"inject-subagent-context.py",
|
|
75
81
|
],
|
|
76
82
|
trae: ["session-start.py", "inject-workflow-state.py"],
|
|
83
|
+
zcode: [
|
|
84
|
+
"session-start.py",
|
|
85
|
+
"inject-workflow-state.py",
|
|
86
|
+
"inject-subagent-context.py",
|
|
87
|
+
],
|
|
77
88
|
};
|
|
78
89
|
/**
|
|
79
90
|
* Get all shared hook scripts. Content is platform-independent and can be
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/shared-hooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,SAAS,YAAY,CAAC,YAAoB;IACxC,OAAO,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;AAC9D,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/shared-hooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,SAAS,YAAY,CAAC,YAAoB;IACxC,OAAO,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;AAC9D,CAAC;AA4BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAGjC;IACF,MAAM,EAAE;QACN,kBAAkB;QAClB,0BAA0B;QAC1B,4BAA4B;KAC7B;IACD,MAAM,EAAE;QACN,kBAAkB;QAClB,iCAAiC;QACjC,4BAA4B;KAC7B;IACD,KAAK,EAAE,CAAC,0BAA0B,EAAE,4BAA4B,CAAC;IACjE,MAAM,EAAE,CAAC,kBAAkB,EAAE,0BAA0B,CAAC;IACxD,KAAK,EAAE,CAAC,kBAAkB,EAAE,0BAA0B,CAAC;IACvD,OAAO,EAAE,CAAC,0BAA0B,CAAC;IACrC,SAAS,EAAE;QACT,kBAAkB;QAClB,0BAA0B;QAC1B,4BAA4B;KAC7B;IACD,KAAK,EAAE;QACL,kBAAkB;QAClB,0BAA0B;QAC1B,4BAA4B;KAC7B;IACD,IAAI,EAAE;QACJ,kBAAkB;QAClB,0BAA0B;QAC1B,4BAA4B;KAC7B;IACD,IAAI,EAAE,CAAC,kBAAkB,EAAE,0BAA0B,CAAC;IACtD,KAAK,EAAE;QACL,kBAAkB;QAClB,0BAA0B;QAC1B,4BAA4B;KAC7B;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC;SACjC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SAChC,IAAI,EAAE,CAAC;IAEV,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,+BAA+B,CAC7C,QAA4B;IAE5B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,wBAAwB,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpE,OAAO,oBAAoB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -80,9 +80,12 @@ def find_repo_root(start_path: str) -> str | None:
|
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
def _detect_platform(input_data: dict) -> str | None:
|
|
83
|
+
if _hook_event_name(input_data) == "SubagentStart":
|
|
84
|
+
return "codex"
|
|
83
85
|
if isinstance(input_data.get("cursor_version"), str):
|
|
84
86
|
return "cursor"
|
|
85
87
|
env_map = {
|
|
88
|
+
"ZCODE_PROJECT_DIR": "zcode",
|
|
86
89
|
"CLAUDE_PROJECT_DIR": "claude",
|
|
87
90
|
"CURSOR_PROJECT_DIR": "cursor",
|
|
88
91
|
"CODEBUDDY_PROJECT_DIR": "codebuddy",
|
|
@@ -110,10 +113,20 @@ def _detect_platform(input_data: dict) -> str | None:
|
|
|
110
113
|
return "droid"
|
|
111
114
|
if ".kiro" in script_parts:
|
|
112
115
|
return "kiro"
|
|
116
|
+
if ".zcode" in script_parts:
|
|
117
|
+
return "zcode"
|
|
113
118
|
return None
|
|
114
119
|
|
|
115
120
|
|
|
116
|
-
def get_current_task(
|
|
121
|
+
def get_current_task(
|
|
122
|
+
repo_root: str,
|
|
123
|
+
input_data: dict,
|
|
124
|
+
*,
|
|
125
|
+
platform: str | None = None,
|
|
126
|
+
allow_single_session_fallback: bool = True,
|
|
127
|
+
allow_environment_context: bool = True,
|
|
128
|
+
require_existing: bool = False,
|
|
129
|
+
) -> str | None:
|
|
117
130
|
"""Resolve current task directory through the unified active task resolver."""
|
|
118
131
|
scripts_dir = Path(repo_root) / DIR_WORKFLOW / "scripts"
|
|
119
132
|
if str(scripts_dir) not in sys.path:
|
|
@@ -126,8 +139,12 @@ def get_current_task(repo_root: str, input_data: dict) -> str | None:
|
|
|
126
139
|
active = resolve_active_task(
|
|
127
140
|
Path(repo_root),
|
|
128
141
|
input_data,
|
|
129
|
-
platform=_detect_platform(input_data),
|
|
142
|
+
platform=platform or _detect_platform(input_data),
|
|
143
|
+
allow_single_session_fallback=allow_single_session_fallback,
|
|
144
|
+
allow_environment_context=allow_environment_context,
|
|
130
145
|
)
|
|
146
|
+
if require_existing and active.stale:
|
|
147
|
+
return None
|
|
131
148
|
return active.task_path
|
|
132
149
|
|
|
133
150
|
|
|
@@ -574,6 +591,98 @@ def _string_value(value: Any) -> str:
|
|
|
574
591
|
return ""
|
|
575
592
|
|
|
576
593
|
|
|
594
|
+
def _hook_event_name(input_data: dict) -> str:
|
|
595
|
+
"""Return a hook event name from the documented snake/camel-case fields."""
|
|
596
|
+
return _string_value(
|
|
597
|
+
input_data.get("hook_event_name") or input_data.get("hookEventName")
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def _codex_subagent_type(input_data: dict) -> str:
|
|
602
|
+
"""Return a Trellis Codex agent type only for a native start event."""
|
|
603
|
+
if _hook_event_name(input_data) != "SubagentStart":
|
|
604
|
+
return ""
|
|
605
|
+
agent_type = _string_value(
|
|
606
|
+
input_data.get("agent_type") or input_data.get("agentType")
|
|
607
|
+
)
|
|
608
|
+
return agent_type if agent_type in AGENTS_ALL else ""
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
def build_codex_subagent_context(
|
|
612
|
+
subagent_type: str,
|
|
613
|
+
task_dir: str,
|
|
614
|
+
context: str,
|
|
615
|
+
) -> str:
|
|
616
|
+
"""Build developer context for a native, already-dispatched Codex role."""
|
|
617
|
+
role = subagent_type.removeprefix("trellis-")
|
|
618
|
+
return f"""<!-- trellis-hook-injected -->
|
|
619
|
+
# Trellis Native {role.title()} Subagent
|
|
620
|
+
|
|
621
|
+
You are the dispatched `{subagent_type}` role for this task. Perform that role
|
|
622
|
+
directly; do not follow main-session dispatch or wait instructions, and do not
|
|
623
|
+
spawn another Trellis subagent.
|
|
624
|
+
|
|
625
|
+
Active task: {task_dir}
|
|
626
|
+
|
|
627
|
+
## Curated Context
|
|
628
|
+
|
|
629
|
+
{context}"""
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def _handle_codex_subagent_start(input_data: dict) -> None:
|
|
633
|
+
"""Emit Codex developer context for a recognised native Trellis subagent.
|
|
634
|
+
|
|
635
|
+
The event supplies the parent session id. Disabling the generic
|
|
636
|
+
single-session fallback is essential here: native starts must never borrow
|
|
637
|
+
a task from another Codex window when that parent id is absent or stale.
|
|
638
|
+
"""
|
|
639
|
+
subagent_type = _codex_subagent_type(input_data)
|
|
640
|
+
parent_session_id = _string_value(input_data.get("session_id"))
|
|
641
|
+
if not subagent_type or not parent_session_id:
|
|
642
|
+
return
|
|
643
|
+
|
|
644
|
+
cwd = _string_value(input_data.get("cwd")) or os.getcwd()
|
|
645
|
+
repo_root = find_repo_root(cwd)
|
|
646
|
+
if not repo_root:
|
|
647
|
+
return
|
|
648
|
+
|
|
649
|
+
task_dir = get_current_task(
|
|
650
|
+
repo_root,
|
|
651
|
+
{"session_id": parent_session_id},
|
|
652
|
+
platform="codex",
|
|
653
|
+
allow_single_session_fallback=False,
|
|
654
|
+
allow_environment_context=False,
|
|
655
|
+
require_existing=True,
|
|
656
|
+
)
|
|
657
|
+
if not task_dir:
|
|
658
|
+
return
|
|
659
|
+
|
|
660
|
+
if subagent_type in AGENTS_REQUIRE_TASK:
|
|
661
|
+
task_dir_full = Path(repo_root) / task_dir
|
|
662
|
+
if not task_dir_full.is_dir():
|
|
663
|
+
return
|
|
664
|
+
|
|
665
|
+
if subagent_type == AGENT_IMPLEMENT:
|
|
666
|
+
context = get_implement_context(repo_root, task_dir)
|
|
667
|
+
elif subagent_type == AGENT_CHECK:
|
|
668
|
+
context = get_check_context(repo_root, task_dir)
|
|
669
|
+
else:
|
|
670
|
+
context = get_research_context(repo_root, task_dir)
|
|
671
|
+
|
|
672
|
+
if not context:
|
|
673
|
+
return
|
|
674
|
+
|
|
675
|
+
output = {
|
|
676
|
+
"hookSpecificOutput": {
|
|
677
|
+
"hookEventName": "SubagentStart",
|
|
678
|
+
"additionalContext": build_codex_subagent_context(
|
|
679
|
+
subagent_type, task_dir, context
|
|
680
|
+
),
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
print(json.dumps(output, ensure_ascii=False))
|
|
684
|
+
|
|
685
|
+
|
|
577
686
|
def _extract_subagent_name(value: Any) -> str:
|
|
578
687
|
"""Extract a sub-agent name from common platform encodings.
|
|
579
688
|
|
|
@@ -652,10 +761,15 @@ def _parse_hook_input(input_data: dict) -> tuple[str, str, dict]:
|
|
|
652
761
|
- Claude Code / Qoder / CodeBuddy / Droid: tool_name=Task|Agent, tool_input.subagent_type
|
|
653
762
|
- Cursor: tool_name=Task|Subagent, tool_input.subagent_type
|
|
654
763
|
- Copilot CLI: toolName=task (camelCase key, lowercase value)
|
|
764
|
+
- ZCode: toolName=Agent, toolInput/tool_input.subagent_type
|
|
655
765
|
- Gemini CLI: tool_name IS the agent name (BeforeTool matcher already filtered)
|
|
656
766
|
- Kiro: agentSpawn hook, agent_name field at top level
|
|
657
767
|
"""
|
|
658
768
|
tool_input = input_data.get("tool_input", {})
|
|
769
|
+
if not isinstance(tool_input, dict):
|
|
770
|
+
tool_input = input_data.get("toolInput", {})
|
|
771
|
+
if not isinstance(tool_input, dict):
|
|
772
|
+
tool_input = {}
|
|
659
773
|
|
|
660
774
|
# Standard format: Task/Agent tool with subagent_type
|
|
661
775
|
tool_name = input_data.get("tool_name", "") or input_data.get("toolName", "")
|
|
@@ -692,6 +806,17 @@ def main():
|
|
|
692
806
|
input_data = json.load(sys.stdin)
|
|
693
807
|
except json.JSONDecodeError:
|
|
694
808
|
sys.exit(0)
|
|
809
|
+
if not isinstance(input_data, dict):
|
|
810
|
+
sys.exit(0)
|
|
811
|
+
|
|
812
|
+
if _hook_event_name(input_data) == "SubagentStart":
|
|
813
|
+
try:
|
|
814
|
+
_handle_codex_subagent_start(input_data)
|
|
815
|
+
except Exception:
|
|
816
|
+
# A native context hook must never prevent Codex from spawning the
|
|
817
|
+
# requested child when its runtime state is unavailable or stale.
|
|
818
|
+
pass
|
|
819
|
+
sys.exit(0)
|
|
695
820
|
|
|
696
821
|
subagent_type, original_prompt, tool_input = _parse_hook_input(input_data)
|
|
697
822
|
cwd = input_data.get("cwd", os.getcwd())
|
|
@@ -745,23 +870,32 @@ def main():
|
|
|
745
870
|
if not context:
|
|
746
871
|
sys.exit(0)
|
|
747
872
|
|
|
748
|
-
# Return updated input
|
|
749
|
-
#
|
|
750
|
-
#
|
|
873
|
+
# Return updated input. Most platforms ignore unrecognized fields, so we
|
|
874
|
+
# include multiple formats. ZCode is stricter; live probing confirmed the
|
|
875
|
+
# nested Claude-compatible shape below reaches the sub-agent prompt.
|
|
751
876
|
updated = {**tool_input, "prompt": new_prompt}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
877
|
+
if _detect_platform(input_data) == "zcode":
|
|
878
|
+
output = {
|
|
879
|
+
"hookSpecificOutput": {
|
|
880
|
+
"hookEventName": "PreToolUse",
|
|
881
|
+
"permissionDecision": "allow",
|
|
882
|
+
"updatedInput": updated,
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
else:
|
|
886
|
+
output = {
|
|
887
|
+
# Claude Code / Qoder / CodeBuddy / Droid format
|
|
888
|
+
"hookSpecificOutput": {
|
|
889
|
+
"hookEventName": "PreToolUse",
|
|
890
|
+
"permissionDecision": "allow",
|
|
891
|
+
"updatedInput": updated,
|
|
892
|
+
},
|
|
893
|
+
# Cursor format
|
|
894
|
+
"permission": "allow",
|
|
895
|
+
"updated_input": updated,
|
|
896
|
+
# Gemini format
|
|
757
897
|
"updatedInput": updated,
|
|
758
|
-
}
|
|
759
|
-
# Cursor format
|
|
760
|
-
"permission": "allow",
|
|
761
|
-
"updated_input": updated,
|
|
762
|
-
# Gemini format
|
|
763
|
-
"updatedInput": updated,
|
|
764
|
-
}
|
|
898
|
+
}
|
|
765
899
|
|
|
766
900
|
print(json.dumps(output, ensure_ascii=False))
|
|
767
901
|
sys.exit(0)
|