@mindfoldhq/trellis 0.4.0-beta.8 → 0.4.0-rc.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/README.md +10 -5
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.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 +165 -13
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +14 -2
- package/dist/commands/update.js.map +1 -1
- package/dist/configurators/codex.d.ts.map +1 -1
- package/dist/configurators/codex.js +2 -1
- package/dist/configurators/codex.js.map +1 -1
- package/dist/configurators/copilot.d.ts +9 -0
- package/dist/configurators/copilot.d.ts.map +1 -0
- package/dist/configurators/copilot.js +34 -0
- package/dist/configurators/copilot.js.map +1 -0
- package/dist/configurators/index.d.ts.map +1 -1
- package/dist/configurators/index.js +32 -1
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/windsurf.d.ts +8 -0
- package/dist/configurators/windsurf.d.ts.map +1 -0
- package/dist/configurators/windsurf.js +18 -0
- package/dist/configurators/windsurf.js.map +1 -0
- package/dist/migrations/manifests/0.4.0-beta.10.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.9.json +9 -0
- package/dist/migrations/manifests/0.4.0-rc.0.json +9 -0
- package/dist/templates/claude/hooks/inject-subagent-context.py +8 -1
- package/dist/templates/claude/hooks/ralph-loop.py +18 -10
- package/dist/templates/claude/hooks/session-start.py +60 -19
- package/dist/templates/claude/hooks/statusline.py +218 -0
- package/dist/templates/claude/settings.json +4 -0
- package/dist/templates/codex/hooks/session-start.py +60 -21
- package/dist/templates/codex/hooks.json +1 -1
- package/dist/templates/copilot/hooks/session-start.py +243 -0
- package/dist/templates/copilot/hooks.json +11 -0
- package/dist/templates/copilot/index.d.ts +23 -0
- package/dist/templates/copilot/index.d.ts.map +1 -0
- package/dist/templates/copilot/index.js +54 -0
- package/dist/templates/copilot/index.js.map +1 -0
- package/dist/templates/copilot/prompts/before-dev.prompt.md +33 -0
- package/dist/templates/copilot/prompts/brainstorm.prompt.md +491 -0
- package/dist/templates/copilot/prompts/break-loop.prompt.md +129 -0
- package/dist/templates/copilot/prompts/check-cross-layer.prompt.md +157 -0
- package/dist/templates/copilot/prompts/check.prompt.md +29 -0
- package/dist/templates/copilot/prompts/create-command.prompt.md +116 -0
- package/dist/templates/copilot/prompts/finish-work.prompt.md +157 -0
- package/dist/templates/copilot/prompts/integrate-skill.prompt.md +223 -0
- package/dist/templates/copilot/prompts/onboard.prompt.md +362 -0
- package/dist/templates/copilot/prompts/parallel.prompt.md +196 -0
- package/dist/templates/copilot/prompts/record-session.prompt.md +66 -0
- package/dist/templates/copilot/prompts/start.prompt.md +397 -0
- package/dist/templates/copilot/prompts/update-spec.prompt.md +358 -0
- package/dist/templates/extract.d.ts +18 -0
- package/dist/templates/extract.d.ts.map +1 -1
- package/dist/templates/extract.js +32 -0
- package/dist/templates/extract.js.map +1 -1
- package/dist/templates/iflow/hooks/inject-subagent-context.py +8 -1
- package/dist/templates/iflow/hooks/ralph-loop.py +8 -1
- package/dist/templates/iflow/hooks/session-start.py +60 -19
- package/dist/templates/markdown/spec/backend/directory-structure.md +1 -1
- package/dist/templates/opencode/agents/dispatch.md +20 -19
- package/dist/templates/opencode/lib/trellis-context.js +35 -239
- package/dist/templates/opencode/plugins/inject-subagent-context.js +71 -121
- package/dist/templates/opencode/plugins/session-start.js +150 -146
- package/dist/templates/trellis/scripts/add_session.py +6 -1
- package/dist/templates/trellis/scripts/common/__init__.py +2 -0
- package/dist/templates/trellis/scripts/common/cli_adapter.py +87 -9
- package/dist/templates/trellis/scripts/common/paths.py +57 -6
- package/dist/templates/trellis/scripts/common/task_store.py +6 -4
- package/dist/templates/trellis/scripts/common/task_utils.py +14 -8
- package/dist/templates/trellis/scripts/multi_agent/start.py +9 -5
- package/dist/templates/trellis/scripts/task.py +1 -1
- package/dist/templates/trellis/workflow.md +17 -4
- package/dist/templates/windsurf/index.d.ts +21 -0
- package/dist/templates/windsurf/index.d.ts.map +1 -0
- package/dist/templates/windsurf/index.js +44 -0
- package/dist/templates/windsurf/index.js.map +1 -0
- package/dist/templates/windsurf/workflows/trellis-before-dev.md +31 -0
- package/dist/templates/windsurf/workflows/trellis-brainstorm.md +491 -0
- package/dist/templates/windsurf/workflows/trellis-break-loop.md +111 -0
- package/dist/templates/windsurf/workflows/trellis-check-cross-layer.md +157 -0
- package/dist/templates/windsurf/workflows/trellis-check.md +27 -0
- package/dist/templates/windsurf/workflows/trellis-create-command.md +154 -0
- package/dist/templates/windsurf/workflows/trellis-finish-work.md +147 -0
- package/dist/templates/windsurf/workflows/trellis-integrate-skill.md +220 -0
- package/dist/templates/windsurf/workflows/trellis-onboard.md +362 -0
- package/dist/templates/windsurf/workflows/trellis-record-session.md +66 -0
- package/dist/templates/windsurf/workflows/trellis-start.md +373 -0
- package/dist/templates/windsurf/workflows/trellis-update-spec.md +358 -0
- package/dist/types/ai-tools.d.ts +5 -3
- package/dist/types/ai-tools.d.ts.map +1 -1
- package/dist/types/ai-tools.js +21 -1
- package/dist/types/ai-tools.js.map +1 -1
- package/dist/utils/template-fetcher.d.ts +17 -4
- package/dist/utils/template-fetcher.d.ts.map +1 -1
- package/dist/utils/template-fetcher.js +94 -12
- package/dist/utils/template-fetcher.js.map +1 -1
- package/package.json +1 -1
|
@@ -71,6 +71,10 @@ Execute each step in `phase` order.
|
|
|
71
71
|
|
|
72
72
|
> Hook will auto-inject all specs, requirements, and technical design to subagent context.
|
|
73
73
|
> Dispatch only needs to issue simple call commands.
|
|
74
|
+
>
|
|
75
|
+
> **OpenCode dispatch rule**: Call subagents synchronously (`run_in_background: false`).
|
|
76
|
+
> Do NOT use `TaskOutput` or background polling as the completion signal for child phases.
|
|
77
|
+
> The background wrapper can finish before the real subagent session is actually done.
|
|
74
78
|
|
|
75
79
|
### action: "implement"
|
|
76
80
|
|
|
@@ -79,7 +83,7 @@ Task(
|
|
|
79
83
|
subagent_type: "implement",
|
|
80
84
|
prompt: "Implement the feature described in prd.md in the task directory",
|
|
81
85
|
model: "opus",
|
|
82
|
-
run_in_background:
|
|
86
|
+
run_in_background: false
|
|
83
87
|
)
|
|
84
88
|
```
|
|
85
89
|
|
|
@@ -98,7 +102,7 @@ Task(
|
|
|
98
102
|
subagent_type: "check",
|
|
99
103
|
prompt: "Check code changes, fix issues yourself",
|
|
100
104
|
model: "opus",
|
|
101
|
-
run_in_background:
|
|
105
|
+
run_in_background: false
|
|
102
106
|
)
|
|
103
107
|
```
|
|
104
108
|
|
|
@@ -116,7 +120,7 @@ Task(
|
|
|
116
120
|
subagent_type: "debug",
|
|
117
121
|
prompt: "Fix the issues described in the task context",
|
|
118
122
|
model: "opus",
|
|
119
|
-
run_in_background:
|
|
123
|
+
run_in_background: false
|
|
120
124
|
)
|
|
121
125
|
```
|
|
122
126
|
|
|
@@ -132,7 +136,7 @@ Task(
|
|
|
132
136
|
subagent_type: "check",
|
|
133
137
|
prompt: "[finish] Execute final completion check before PR",
|
|
134
138
|
model: "opus",
|
|
135
|
-
run_in_background:
|
|
139
|
+
run_in_background: false
|
|
136
140
|
)
|
|
137
141
|
```
|
|
138
142
|
|
|
@@ -168,27 +172,23 @@ This will:
|
|
|
168
172
|
### Basic Pattern
|
|
169
173
|
|
|
170
174
|
```
|
|
171
|
-
|
|
175
|
+
result = Task(
|
|
172
176
|
subagent_type: "implement", // or "check", "debug"
|
|
173
177
|
prompt: "Simple task description",
|
|
174
178
|
model: "opus",
|
|
175
|
-
run_in_background:
|
|
179
|
+
run_in_background: false
|
|
176
180
|
)
|
|
177
181
|
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
result = TaskOutput(task_id, block=true, timeout=300000)
|
|
181
|
-
if result.status == "completed":
|
|
182
|
-
break
|
|
182
|
+
// Wait for the Task call to return before starting the next phase.
|
|
183
|
+
// Do NOT call TaskOutput or use background polling inside OpenCode dispatch.
|
|
183
184
|
```
|
|
184
185
|
|
|
185
|
-
###
|
|
186
|
+
### Execution Rule
|
|
186
187
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
| debug | 20 min | 4 times |
|
|
188
|
+
- Run one phase at a time
|
|
189
|
+
- Start the next phase only after the current `Task(...)` call returns
|
|
190
|
+
- If a phase returns a clear timeout or failure, handle that result explicitly
|
|
191
|
+
- Do **not** simulate completion by polling a background task wrapper
|
|
192
192
|
|
|
193
193
|
---
|
|
194
194
|
|
|
@@ -196,7 +196,7 @@ for i in 1..N:
|
|
|
196
196
|
|
|
197
197
|
### Timeout
|
|
198
198
|
|
|
199
|
-
If a subagent times out, notify the user and ask for guidance:
|
|
199
|
+
If a synchronous subagent call times out, notify the user and ask for guidance:
|
|
200
200
|
|
|
201
201
|
```
|
|
202
202
|
"Subagent {phase} timed out after {time}. Options:
|
|
@@ -207,10 +207,11 @@ If a subagent times out, notify the user and ask for guidance:
|
|
|
207
207
|
|
|
208
208
|
### Subagent Failure
|
|
209
209
|
|
|
210
|
-
If a subagent reports failure, read the output and decide:
|
|
210
|
+
If a synchronous subagent call reports failure, read the output and decide:
|
|
211
211
|
|
|
212
212
|
- If recoverable: call debug agent to fix
|
|
213
213
|
- If not recoverable: notify user and ask for guidance
|
|
214
|
+
- Do not switch back to `TaskOutput` polling for the same phase
|
|
214
215
|
|
|
215
216
|
---
|
|
216
217
|
|
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Trellis Context Manager
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* import { TrellisContext } from "./trellis-context.js"
|
|
9
|
-
* const ctx = new TrellisContext(directory)
|
|
10
|
-
* if (ctx.shouldSkipHook("session-start")) return
|
|
4
|
+
* Utility class for OpenCode plugins providing file reading,
|
|
5
|
+
* JSONL parsing, and context building capabilities.
|
|
11
6
|
*/
|
|
12
7
|
|
|
13
8
|
import { existsSync, readFileSync, appendFileSync, readdirSync } from "fs"
|
|
14
|
-
import { join } from "path"
|
|
15
|
-
import {
|
|
9
|
+
import { isAbsolute, join } from "path"
|
|
10
|
+
import { platform } from "os"
|
|
16
11
|
import { execSync } from "child_process"
|
|
17
12
|
|
|
18
|
-
// Python command: Windows uses 'python', macOS/Linux use 'python3'
|
|
19
13
|
const PYTHON_CMD = platform() === "win32" ? "python" : "python3"
|
|
20
|
-
|
|
21
14
|
// Debug logging
|
|
22
15
|
const DEBUG_LOG = "/tmp/trellis-plugin-debug.log"
|
|
23
16
|
|
|
@@ -33,151 +26,17 @@ function debugLog(prefix, ...args) {
|
|
|
33
26
|
|
|
34
27
|
/**
|
|
35
28
|
* Trellis Context Manager
|
|
36
|
-
*
|
|
37
|
-
* Centralized logic for:
|
|
38
|
-
* - Detecting oh-my-opencode installation
|
|
39
|
-
* - Checking .claude/hooks/ presence
|
|
40
|
-
* - Determining which plugin should handle each hook
|
|
41
29
|
*/
|
|
42
30
|
export class TrellisContext {
|
|
43
31
|
constructor(directory) {
|
|
44
32
|
this.directory = directory
|
|
45
|
-
this._omoInstalled = null
|
|
46
|
-
this._omoHooksEnabled = null
|
|
47
|
-
this._claudeHooksPresent = {}
|
|
48
|
-
|
|
49
33
|
debugLog("context", "TrellisContext initialized", { directory })
|
|
50
34
|
}
|
|
51
35
|
|
|
52
|
-
// ============================================================
|
|
53
|
-
// oh-my-opencode Detection
|
|
54
|
-
// ============================================================
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Check if oh-my-opencode is installed
|
|
58
|
-
*
|
|
59
|
-
* Detection order:
|
|
60
|
-
* 1. Check if oh-my-opencode.json exists (most reliable)
|
|
61
|
-
* 2. Fallback: check opencode.json plugin list
|
|
62
|
-
*/
|
|
63
|
-
isOmoInstalled() {
|
|
64
|
-
if (this._omoInstalled !== null) {
|
|
65
|
-
return this._omoInstalled
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const configDir = join(homedir(), ".config", "opencode")
|
|
70
|
-
|
|
71
|
-
// Method 1: Check oh-my-opencode.json existence (omo-specific config)
|
|
72
|
-
const omoConfigPath = join(configDir, "oh-my-opencode.json")
|
|
73
|
-
if (existsSync(omoConfigPath)) {
|
|
74
|
-
this._omoInstalled = true
|
|
75
|
-
debugLog("context", "omo installed: oh-my-opencode.json exists")
|
|
76
|
-
return true
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Method 2: Fallback to plugin list check
|
|
80
|
-
const configPath = join(configDir, "opencode.json")
|
|
81
|
-
if (!existsSync(configPath)) {
|
|
82
|
-
this._omoInstalled = false
|
|
83
|
-
debugLog("context", "omo not installed: no config files")
|
|
84
|
-
return false
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const content = readFileSync(configPath, "utf-8")
|
|
88
|
-
const config = JSON.parse(content)
|
|
89
|
-
const plugins = config.plugin || []
|
|
90
|
-
|
|
91
|
-
this._omoInstalled = plugins.some(p =>
|
|
92
|
-
typeof p === "string" && p.toLowerCase().includes("oh-my-opencode")
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
debugLog("context", "omo installed (plugin list):", this._omoInstalled)
|
|
96
|
-
return this._omoInstalled
|
|
97
|
-
} catch (e) {
|
|
98
|
-
debugLog("context", "omo detection error:", e.message)
|
|
99
|
-
this._omoInstalled = false
|
|
100
|
-
return false
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Check if omo's claude_code.hooks is enabled
|
|
106
|
-
* Reads oh-my-opencode.json or defaults to true
|
|
107
|
-
*/
|
|
108
|
-
isOmoHooksEnabled() {
|
|
109
|
-
if (this._omoHooksEnabled !== null) {
|
|
110
|
-
return this._omoHooksEnabled
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (!this.isOmoInstalled()) {
|
|
114
|
-
this._omoHooksEnabled = false
|
|
115
|
-
return false
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
try {
|
|
119
|
-
// Check global config
|
|
120
|
-
const globalConfig = join(homedir(), ".config", "opencode", "oh-my-opencode.json")
|
|
121
|
-
if (existsSync(globalConfig)) {
|
|
122
|
-
const content = readFileSync(globalConfig, "utf-8")
|
|
123
|
-
const config = JSON.parse(content)
|
|
124
|
-
if (config.claude_code?.hooks === false) {
|
|
125
|
-
this._omoHooksEnabled = false
|
|
126
|
-
debugLog("context", "omo hooks disabled in global config")
|
|
127
|
-
return false
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Check project config
|
|
132
|
-
const projectConfig = join(this.directory, "oh-my-opencode.json")
|
|
133
|
-
if (existsSync(projectConfig)) {
|
|
134
|
-
const content = readFileSync(projectConfig, "utf-8")
|
|
135
|
-
const config = JSON.parse(content)
|
|
136
|
-
if (config.claude_code?.hooks === false) {
|
|
137
|
-
this._omoHooksEnabled = false
|
|
138
|
-
debugLog("context", "omo hooks disabled in project config")
|
|
139
|
-
return false
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Default: enabled
|
|
144
|
-
this._omoHooksEnabled = true
|
|
145
|
-
debugLog("context", "omo hooks enabled (default)")
|
|
146
|
-
return true
|
|
147
|
-
} catch (e) {
|
|
148
|
-
debugLog("context", "omo hooks detection error:", e.message)
|
|
149
|
-
this._omoHooksEnabled = true // Default to enabled
|
|
150
|
-
return true
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// ============================================================
|
|
155
|
-
// .claude/hooks/ Detection
|
|
156
|
-
// ============================================================
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Check if a specific .claude/hooks/ file exists
|
|
160
|
-
*/
|
|
161
|
-
hasClaudeHook(hookName) {
|
|
162
|
-
if (hookName in this._claudeHooksPresent) {
|
|
163
|
-
return this._claudeHooksPresent[hookName]
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const hookPath = join(this.directory, ".claude", "hooks", `${hookName}.py`)
|
|
167
|
-
const exists = existsSync(hookPath)
|
|
168
|
-
|
|
169
|
-
this._claudeHooksPresent[hookName] = exists
|
|
170
|
-
debugLog("context", `claude hook ${hookName}:`, exists)
|
|
171
|
-
return exists
|
|
172
|
-
}
|
|
173
|
-
|
|
174
36
|
// ============================================================
|
|
175
37
|
// Trellis Project Detection
|
|
176
38
|
// ============================================================
|
|
177
39
|
|
|
178
|
-
/**
|
|
179
|
-
* Check if this is a Trellis-managed project
|
|
180
|
-
*/
|
|
181
40
|
isTrellisProject() {
|
|
182
41
|
return existsSync(join(this.directory, ".trellis"))
|
|
183
42
|
}
|
|
@@ -191,60 +50,56 @@ export class TrellisContext {
|
|
|
191
50
|
if (!existsSync(currentTaskPath)) {
|
|
192
51
|
return null
|
|
193
52
|
}
|
|
194
|
-
|
|
53
|
+
const taskRef = readFileSync(currentTaskPath, "utf-8").trim()
|
|
54
|
+
const normalized = this.normalizeTaskRef(taskRef)
|
|
55
|
+
return normalized || null
|
|
195
56
|
} catch {
|
|
196
57
|
return null
|
|
197
58
|
}
|
|
198
59
|
}
|
|
199
60
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
61
|
+
normalizeTaskRef(taskRef) {
|
|
62
|
+
if (!taskRef) {
|
|
63
|
+
return ""
|
|
64
|
+
}
|
|
203
65
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
* (because omo will handle it via .claude/hooks/)
|
|
207
|
-
*
|
|
208
|
-
* @param {string} hookName - Hook name without extension (e.g., "session-start")
|
|
209
|
-
* @returns {boolean} - true if we should skip, false if we should handle
|
|
210
|
-
*/
|
|
211
|
-
shouldSkipHook(hookName) {
|
|
212
|
-
// Not a Trellis project? Skip.
|
|
213
|
-
if (!this.isTrellisProject()) {
|
|
214
|
-
debugLog("context", `shouldSkipHook(${hookName}): skip - not Trellis project`)
|
|
215
|
-
return true
|
|
66
|
+
if (isAbsolute(taskRef)) {
|
|
67
|
+
return taskRef.trim()
|
|
216
68
|
}
|
|
217
69
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return false
|
|
70
|
+
let normalized = taskRef.trim().replace(/\\/g, "/")
|
|
71
|
+
while (normalized.startsWith("./")) {
|
|
72
|
+
normalized = normalized.slice(2)
|
|
222
73
|
}
|
|
223
74
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
debugLog("context", `shouldSkipHook(${hookName}): handle - omo hooks disabled`)
|
|
227
|
-
return false
|
|
75
|
+
if (normalized.startsWith("tasks/")) {
|
|
76
|
+
return `.trellis/${normalized}`
|
|
228
77
|
}
|
|
229
78
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
79
|
+
return normalized
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
resolveTaskDir(taskRef) {
|
|
83
|
+
const normalized = this.normalizeTaskRef(taskRef)
|
|
84
|
+
if (!normalized) {
|
|
85
|
+
return null
|
|
234
86
|
}
|
|
235
87
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
88
|
+
if (isAbsolute(normalized)) {
|
|
89
|
+
return normalized
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (normalized.startsWith(".trellis/")) {
|
|
93
|
+
return join(this.directory, normalized)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return join(this.directory, ".trellis", "tasks", normalized)
|
|
239
97
|
}
|
|
240
98
|
|
|
241
99
|
// ============================================================
|
|
242
100
|
// File Reading Utilities
|
|
243
101
|
// ============================================================
|
|
244
102
|
|
|
245
|
-
/**
|
|
246
|
-
* Read a file, return null on error
|
|
247
|
-
*/
|
|
248
103
|
readFile(filePath) {
|
|
249
104
|
try {
|
|
250
105
|
if (existsSync(filePath)) {
|
|
@@ -256,16 +111,10 @@ export class TrellisContext {
|
|
|
256
111
|
return null
|
|
257
112
|
}
|
|
258
113
|
|
|
259
|
-
/**
|
|
260
|
-
* Read a file relative to project directory
|
|
261
|
-
*/
|
|
262
114
|
readProjectFile(relativePath) {
|
|
263
115
|
return this.readFile(join(this.directory, relativePath))
|
|
264
116
|
}
|
|
265
117
|
|
|
266
|
-
/**
|
|
267
|
-
* Run a Python script and return output
|
|
268
|
-
*/
|
|
269
118
|
runScript(scriptPath, cwd = null) {
|
|
270
119
|
try {
|
|
271
120
|
const result = execSync(`${PYTHON_CMD} "${scriptPath}"`, {
|
|
@@ -284,12 +133,6 @@ export class TrellisContext {
|
|
|
284
133
|
// JSONL Reading
|
|
285
134
|
// ============================================================
|
|
286
135
|
|
|
287
|
-
/**
|
|
288
|
-
* Read all .md files in a directory
|
|
289
|
-
* @param {string} dirPath - Directory path relative to project root
|
|
290
|
-
* @param {number} maxFiles - Max files to read (prevent huge directories)
|
|
291
|
-
* @returns {Array<{path: string, content: string}>}
|
|
292
|
-
*/
|
|
293
136
|
readDirectoryMdFiles(dirPath, maxFiles = 20) {
|
|
294
137
|
const results = []
|
|
295
138
|
const fullPath = join(this.directory, dirPath)
|
|
@@ -339,11 +182,9 @@ export class TrellisContext {
|
|
|
339
182
|
if (!file) continue
|
|
340
183
|
|
|
341
184
|
if (entryType === "directory") {
|
|
342
|
-
// Read all .md files in directory
|
|
343
185
|
const dirEntries = this.readDirectoryMdFiles(file)
|
|
344
186
|
results.push(...dirEntries)
|
|
345
187
|
} else {
|
|
346
|
-
// Read single file
|
|
347
188
|
const fullPath = join(this.directory, file)
|
|
348
189
|
const fileContent = this.readFile(fullPath)
|
|
349
190
|
if (fileContent) {
|
|
@@ -357,74 +198,29 @@ export class TrellisContext {
|
|
|
357
198
|
return results
|
|
358
199
|
}
|
|
359
200
|
|
|
360
|
-
/**
|
|
361
|
-
* Build context string from file entries
|
|
362
|
-
*/
|
|
363
201
|
buildContextFromEntries(entries) {
|
|
364
202
|
return entries.map(e => `=== ${e.path} ===\n${e.content}`).join("\n\n")
|
|
365
203
|
}
|
|
366
204
|
}
|
|
367
205
|
|
|
368
206
|
// ============================================================
|
|
369
|
-
// Context Collector (for
|
|
207
|
+
// Context Collector (for session deduplication)
|
|
370
208
|
// ============================================================
|
|
371
209
|
|
|
372
|
-
/**
|
|
373
|
-
* Simple context collector for cross-hook communication
|
|
374
|
-
* Similar to oh-my-opencode's contextCollector
|
|
375
|
-
*/
|
|
376
210
|
class ContextCollector {
|
|
377
211
|
constructor() {
|
|
378
|
-
this.pending = new Map()
|
|
379
212
|
this.processed = new Set()
|
|
380
213
|
}
|
|
381
214
|
|
|
382
|
-
/**
|
|
383
|
-
* Store context for a session
|
|
384
|
-
*/
|
|
385
|
-
store(sessionID, content) {
|
|
386
|
-
this.pending.set(sessionID, {
|
|
387
|
-
content,
|
|
388
|
-
timestamp: Date.now()
|
|
389
|
-
})
|
|
390
|
-
debugLog("collector", "stored context for session:", sessionID, "length:", content.length)
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Check if session has pending context
|
|
395
|
-
*/
|
|
396
|
-
hasPending(sessionID) {
|
|
397
|
-
return this.pending.has(sessionID)
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* Get and consume pending context
|
|
402
|
-
*/
|
|
403
|
-
consume(sessionID) {
|
|
404
|
-
const pending = this.pending.get(sessionID)
|
|
405
|
-
this.pending.delete(sessionID)
|
|
406
|
-
return pending
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* Mark session as processed (for first-message-only injection)
|
|
411
|
-
*/
|
|
412
215
|
markProcessed(sessionID) {
|
|
413
216
|
this.processed.add(sessionID)
|
|
414
217
|
}
|
|
415
218
|
|
|
416
|
-
/**
|
|
417
|
-
* Check if session was already processed
|
|
418
|
-
*/
|
|
419
219
|
isProcessed(sessionID) {
|
|
420
220
|
return this.processed.has(sessionID)
|
|
421
221
|
}
|
|
422
222
|
|
|
423
|
-
/**
|
|
424
|
-
* Clear session state
|
|
425
|
-
*/
|
|
426
223
|
clear(sessionID) {
|
|
427
|
-
this.pending.delete(sessionID)
|
|
428
224
|
this.processed.delete(sessionID)
|
|
429
225
|
}
|
|
430
226
|
}
|