@mindfoldhq/trellis 0.5.12 → 0.5.14
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 +49 -49
- package/dist/migrations/manifests/0.5.13.json +9 -0
- package/dist/migrations/manifests/0.5.14.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.10.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.7.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.8.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.9.json +9 -0
- package/dist/templates/claude/settings.json +4 -4
- package/dist/templates/codebuddy/settings.json +4 -4
- package/dist/templates/codex/hooks.json +1 -1
- package/dist/templates/copilot/hooks/session-start.py +12 -11
- package/dist/templates/copilot/hooks.json +2 -2
- package/dist/templates/cursor/hooks.json +2 -2
- package/dist/templates/droid/settings.json +4 -4
- package/dist/templates/gemini/settings.json +2 -2
- package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +10 -4
- package/dist/templates/opencode/lib/trellis-context.js +73 -11
- package/dist/templates/opencode/plugins/inject-subagent-context.js +109 -23
- package/dist/templates/opencode/plugins/inject-workflow-state.js +8 -1
- package/dist/templates/opencode/plugins/session-start.js +9 -1
- package/dist/templates/qoder/settings.json +4 -4
- package/dist/templates/trellis/scripts/common/safe_commit.py +49 -19
- package/dist/templates/trellis/scripts/common/session_context.py +214 -137
- package/dist/templates/trellis/scripts/common/task_store.py +42 -12
- package/package.json +1 -1
|
@@ -14,24 +14,39 @@ import { TrellisContext, debugLog } from "../lib/trellis-context.js"
|
|
|
14
14
|
const AGENTS_ALL = ["implement", "check", "research"]
|
|
15
15
|
const AGENTS_REQUIRE_TASK = ["implement", "check"]
|
|
16
16
|
|
|
17
|
+
// Match `Active task: <path>` on the first non-empty line of the dispatch
|
|
18
|
+
// prompt. Mirrors the contract in workflow.md's [workflow-state:in_progress]
|
|
19
|
+
// breadcrumb so multi-window users can disambiguate which task is targeted.
|
|
20
|
+
const ACTIVE_TASK_HINT_RE = /^\s*Active task:\s*(\S+)\s*$/m
|
|
21
|
+
|
|
22
|
+
function extractActiveTaskHint(prompt) {
|
|
23
|
+
if (typeof prompt !== "string" || !prompt) return null
|
|
24
|
+
const match = prompt.match(ACTIVE_TASK_HINT_RE)
|
|
25
|
+
return match ? match[1].trim() : null
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
/**
|
|
18
|
-
* Get context for implement agent
|
|
29
|
+
* Get context for implement agent. `taskDir` may be relative
|
|
30
|
+
* (`.trellis/tasks/foo`) or absolute; both are resolved via
|
|
31
|
+
* `ctx.resolveTaskDir`.
|
|
19
32
|
*/
|
|
20
33
|
function getImplementContext(ctx, taskDir) {
|
|
21
34
|
const parts = []
|
|
35
|
+
const taskDirFull = ctx.resolveTaskDir(taskDir)
|
|
36
|
+
if (!taskDirFull) return ""
|
|
22
37
|
|
|
23
|
-
const jsonlPath = join(
|
|
38
|
+
const jsonlPath = join(taskDirFull, "implement.jsonl")
|
|
24
39
|
const entries = ctx.readJsonlWithFiles(jsonlPath)
|
|
25
40
|
if (entries.length > 0) {
|
|
26
41
|
parts.push(ctx.buildContextFromEntries(entries))
|
|
27
42
|
}
|
|
28
43
|
|
|
29
|
-
const prd = ctx.
|
|
44
|
+
const prd = ctx.readFile(join(taskDirFull, "prd.md"))
|
|
30
45
|
if (prd) {
|
|
31
46
|
parts.push(`=== ${taskDir}/prd.md (Requirements) ===\n${prd}`)
|
|
32
47
|
}
|
|
33
48
|
|
|
34
|
-
const info = ctx.
|
|
49
|
+
const info = ctx.readFile(join(taskDirFull, "info.md"))
|
|
35
50
|
if (info) {
|
|
36
51
|
parts.push(`=== ${taskDir}/info.md (Technical Design) ===\n${info}`)
|
|
37
52
|
}
|
|
@@ -40,18 +55,20 @@ function getImplementContext(ctx, taskDir) {
|
|
|
40
55
|
}
|
|
41
56
|
|
|
42
57
|
/**
|
|
43
|
-
* Get context for check agent
|
|
58
|
+
* Get context for check agent. `taskDir` may be relative or absolute.
|
|
44
59
|
*/
|
|
45
60
|
function getCheckContext(ctx, taskDir) {
|
|
46
61
|
const parts = []
|
|
62
|
+
const taskDirFull = ctx.resolveTaskDir(taskDir)
|
|
63
|
+
if (!taskDirFull) return ""
|
|
47
64
|
|
|
48
|
-
const jsonlPath = join(
|
|
65
|
+
const jsonlPath = join(taskDirFull, "check.jsonl")
|
|
49
66
|
const entries = ctx.readJsonlWithFiles(jsonlPath)
|
|
50
67
|
if (entries.length > 0) {
|
|
51
68
|
parts.push(ctx.buildContextFromEntries(entries))
|
|
52
69
|
}
|
|
53
70
|
|
|
54
|
-
const prd = ctx.
|
|
71
|
+
const prd = ctx.readFile(join(taskDirFull, "prd.md"))
|
|
55
72
|
if (prd) {
|
|
56
73
|
parts.push(`=== ${taskDir}/prd.md (Requirements) ===\n${prd}`)
|
|
57
74
|
}
|
|
@@ -128,7 +145,8 @@ function getResearchContext(ctx) {
|
|
|
128
145
|
*/
|
|
129
146
|
function buildPrompt(agentType, originalPrompt, context, isFinish = false) {
|
|
130
147
|
const templates = {
|
|
131
|
-
implement:
|
|
148
|
+
implement: `<!-- trellis-hook-injected -->
|
|
149
|
+
# Implement Agent Task
|
|
132
150
|
|
|
133
151
|
You are the Implement Agent in the Multi-Agent Pipeline.
|
|
134
152
|
|
|
@@ -157,7 +175,8 @@ ${originalPrompt}
|
|
|
157
175
|
- Follow all dev specs injected above
|
|
158
176
|
- Report list of modified/created files when done`,
|
|
159
177
|
|
|
160
|
-
check: isFinish ?
|
|
178
|
+
check: isFinish ? `<!-- trellis-hook-injected -->
|
|
179
|
+
# Finish Agent Task
|
|
161
180
|
|
|
162
181
|
You are performing the final check before creating a PR.
|
|
163
182
|
|
|
@@ -191,7 +210,8 @@ ${originalPrompt}
|
|
|
191
210
|
- Do NOT update specs for trivial changes (typos, formatting, obvious fixes)
|
|
192
211
|
- If critical CODE issues found, report them clearly (fix specs, not code)
|
|
193
212
|
- Verify all acceptance criteria in prd.md are met` :
|
|
194
|
-
|
|
213
|
+
`<!-- trellis-hook-injected -->
|
|
214
|
+
# Check Agent Task
|
|
195
215
|
|
|
196
216
|
You are the Check Agent in the Multi-Agent Pipeline.
|
|
197
217
|
|
|
@@ -219,7 +239,8 @@ ${originalPrompt}
|
|
|
219
239
|
- Fix issues yourself, don't just report
|
|
220
240
|
- Must execute complete checklist`,
|
|
221
241
|
|
|
222
|
-
research:
|
|
242
|
+
research: `<!-- trellis-hook-injected -->
|
|
243
|
+
# Research Agent Task
|
|
223
244
|
|
|
224
245
|
You are the Research Agent in the Multi-Agent Pipeline.
|
|
225
246
|
|
|
@@ -264,9 +285,29 @@ function powershellQuote(value) {
|
|
|
264
285
|
return `'${String(value).replace(/'/g, "''")}'`
|
|
265
286
|
}
|
|
266
287
|
|
|
267
|
-
function
|
|
268
|
-
|
|
269
|
-
|
|
288
|
+
function envValue(env, key) {
|
|
289
|
+
const value = env?.[key]
|
|
290
|
+
return typeof value === "string" && value.trim() ? value.trim() : null
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function shellBasename(value) {
|
|
294
|
+
return value.replace(/\\/g, "/").split("/").pop()?.toLowerCase() || ""
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function isWindowsPosixShell(env = process.env) {
|
|
298
|
+
if (envValue(env, "MSYSTEM")) return true
|
|
299
|
+
if (envValue(env, "MINGW_PREFIX")) return true
|
|
300
|
+
if (envValue(env, "OPENCODE_GIT_BASH_PATH")) return true
|
|
301
|
+
|
|
302
|
+
const ostype = envValue(env, "OSTYPE")?.toLowerCase() || ""
|
|
303
|
+
if (/(msys|mingw|cygwin)/.test(ostype)) return true
|
|
304
|
+
|
|
305
|
+
const shell = shellBasename(envValue(env, "SHELL") || "")
|
|
306
|
+
return /^(bash|sh|zsh)(\.exe)?$/.test(shell)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function buildTrellisContextPrefix(contextKey, hostPlatform = process.platform, env = process.env) {
|
|
310
|
+
if (hostPlatform === "win32" && !isWindowsPosixShell(env)) {
|
|
270
311
|
return `$env:TRELLIS_CONTEXT_ID = ${powershellQuote(contextKey)}; `
|
|
271
312
|
}
|
|
272
313
|
|
|
@@ -285,7 +326,7 @@ function commandStartsWithTrellisContext(command) {
|
|
|
285
326
|
return (
|
|
286
327
|
/^TRELLIS_CONTEXT_ID\s*=/.test(firstCommand) ||
|
|
287
328
|
/^export\s+TRELLIS_CONTEXT_ID\s*=/.test(firstCommand) ||
|
|
288
|
-
/^env\s+(?:[
|
|
329
|
+
/^env\s+(?:(?:-\S+|[A-Za-z_][A-Za-z0-9_]*=\S*)\s+)*TRELLIS_CONTEXT_ID\s*=/.test(firstCommand) ||
|
|
289
330
|
/^\$env:TRELLIS_CONTEXT_ID\s*=/i.test(firstCommand)
|
|
290
331
|
)
|
|
291
332
|
}
|
|
@@ -294,7 +335,7 @@ function commandStartsWithTrellisContext(command) {
|
|
|
294
335
|
* OpenCode TUI may not expose OPENCODE_RUN_ID to Bash. The plugin hook still
|
|
295
336
|
* receives session identity, so inject it into Bash commands before execution.
|
|
296
337
|
*/
|
|
297
|
-
function injectTrellisContextIntoBash(ctx, input, output, hostPlatform) {
|
|
338
|
+
function injectTrellisContextIntoBash(ctx, input, output, hostPlatform, env) {
|
|
298
339
|
const args = output?.args
|
|
299
340
|
const commandKey = getBashCommandKey(args)
|
|
300
341
|
if (!commandKey) return false
|
|
@@ -306,7 +347,7 @@ function injectTrellisContextIntoBash(ctx, input, output, hostPlatform) {
|
|
|
306
347
|
const contextKey = ctx.getContextKey(input)
|
|
307
348
|
if (!contextKey) return false
|
|
308
349
|
|
|
309
|
-
args[commandKey] = `${buildTrellisContextPrefix(contextKey, hostPlatform)}${command}`
|
|
350
|
+
args[commandKey] = `${buildTrellisContextPrefix(contextKey, hostPlatform, env)}${command}`
|
|
310
351
|
return true
|
|
311
352
|
}
|
|
312
353
|
|
|
@@ -315,7 +356,7 @@ function injectTrellisContextIntoBash(ctx, input, output, hostPlatform) {
|
|
|
315
356
|
// (packages/opencode/src/plugin/index.ts — `for ([_, fn] of Object.entries(mod)) await fn(input)`);
|
|
316
357
|
// the previous `{ id, server }` object shape failed with
|
|
317
358
|
// `TypeError: fn is not a function` in 1.2.x.
|
|
318
|
-
export default async ({ directory, platform: hostPlatform = process.platform }) => {
|
|
359
|
+
export default async ({ directory, platform: hostPlatform = process.platform, env = process.env }) => {
|
|
319
360
|
const ctx = new TrellisContext(directory)
|
|
320
361
|
debugLog("inject", "Plugin loaded, directory:", directory)
|
|
321
362
|
|
|
@@ -329,7 +370,7 @@ export default async ({ directory, platform: hostPlatform = process.platform })
|
|
|
329
370
|
|
|
330
371
|
const toolName = input?.tool?.toLowerCase()
|
|
331
372
|
if (toolName === "bash") {
|
|
332
|
-
if (injectTrellisContextIntoBash(ctx, input, output, hostPlatform)) {
|
|
373
|
+
if (injectTrellisContextIntoBash(ctx, input, output, hostPlatform, env)) {
|
|
333
374
|
debugLog("inject", "Injected TRELLIS_CONTEXT_ID into Bash command")
|
|
334
375
|
}
|
|
335
376
|
return
|
|
@@ -354,8 +395,53 @@ export default async ({ directory, platform: hostPlatform = process.platform })
|
|
|
354
395
|
return
|
|
355
396
|
}
|
|
356
397
|
|
|
357
|
-
// Resolve active task
|
|
358
|
-
|
|
398
|
+
// Resolve active task in this priority order (only later steps
|
|
399
|
+
// run when earlier ones miss):
|
|
400
|
+
// 1. Exact session runtime context lookup for input.sessionID
|
|
401
|
+
// 2. `Active task: <path>` hint in the dispatch prompt
|
|
402
|
+
// (explicit per-dispatch override — beats single-session
|
|
403
|
+
// inference so multi-window users can disambiguate)
|
|
404
|
+
// 3. Single-session fallback — only when exactly 1 session
|
|
405
|
+
// runtime file exists locally
|
|
406
|
+
let taskDir = null
|
|
407
|
+
let taskSource = null
|
|
408
|
+
|
|
409
|
+
const contextKey = ctx.getContextKey(input)
|
|
410
|
+
if (contextKey) {
|
|
411
|
+
const context = ctx.readContext(contextKey)
|
|
412
|
+
const exactRef = ctx.normalizeTaskRef(context?.current_task || "")
|
|
413
|
+
if (exactRef) {
|
|
414
|
+
taskDir = exactRef
|
|
415
|
+
taskSource = `session:${contextKey}`
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (!taskDir) {
|
|
420
|
+
const hintRef = extractActiveTaskHint(originalPrompt)
|
|
421
|
+
if (hintRef) {
|
|
422
|
+
const hintNormalized = ctx.normalizeTaskRef(hintRef)
|
|
423
|
+
if (hintNormalized) {
|
|
424
|
+
const hintDir = ctx.resolveTaskDir(hintNormalized)
|
|
425
|
+
if (hintDir && existsSync(hintDir)) {
|
|
426
|
+
taskDir = hintNormalized
|
|
427
|
+
taskSource = "prompt-hint"
|
|
428
|
+
debugLog("inject", "Resolved task from Active task: hint:", hintNormalized)
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (!taskDir) {
|
|
435
|
+
const fallback = ctx._resolveSingleSessionFallback()
|
|
436
|
+
if (fallback?.taskPath) {
|
|
437
|
+
const fallbackDir = ctx.resolveTaskDir(fallback.taskPath)
|
|
438
|
+
if (fallbackDir && existsSync(fallbackDir)) {
|
|
439
|
+
taskDir = fallback.taskPath
|
|
440
|
+
taskSource = fallback.source
|
|
441
|
+
debugLog("inject", "Resolved task via single-session fallback:", taskDir, "source:", taskSource)
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
359
445
|
|
|
360
446
|
// Agents requiring task directory
|
|
361
447
|
if (AGENTS_REQUIRE_TASK.includes(subagentType)) {
|
|
@@ -364,8 +450,8 @@ export default async ({ directory, platform: hostPlatform = process.platform })
|
|
|
364
450
|
debugLog("inject", "Skipping - no current task")
|
|
365
451
|
return
|
|
366
452
|
}
|
|
367
|
-
const taskDirFull =
|
|
368
|
-
if (!existsSync(taskDirFull)) {
|
|
453
|
+
const taskDirFull = ctx.resolveTaskDir(taskDir)
|
|
454
|
+
if (!taskDirFull || !existsSync(taskDirFull)) {
|
|
369
455
|
debugLog("inject", "Skipping - task directory not found")
|
|
370
456
|
return
|
|
371
457
|
}
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
import { existsSync, readFileSync } from "fs"
|
|
27
27
|
import { join } from "path"
|
|
28
|
-
import { TrellisContext, debugLog } from "../lib/trellis-context.js"
|
|
28
|
+
import { TrellisContext, debugLog, isTrellisSubagent } from "../lib/trellis-context.js"
|
|
29
29
|
|
|
30
30
|
// Supports STATUS values with letters, digits, underscores, hyphens
|
|
31
31
|
// (so "in-review" / "blocked-by-team" work alongside "in_progress").
|
|
@@ -111,6 +111,13 @@ export default async ({ directory }) => {
|
|
|
111
111
|
// so it persists in conversation history.
|
|
112
112
|
"chat.message": async (input, output) => {
|
|
113
113
|
try {
|
|
114
|
+
// Skip Trellis sub-agent turns — the per-turn breadcrumb is for the
|
|
115
|
+
// main session only; sub-agent context comes from the parent's
|
|
116
|
+
// tool.execute.before injection.
|
|
117
|
+
if (isTrellisSubagent(input)) {
|
|
118
|
+
debugLog("workflow-state", "Skipping trellis subagent turn:", input?.agent)
|
|
119
|
+
return
|
|
120
|
+
}
|
|
114
121
|
if (process.env.TRELLIS_HOOKS === "0" || process.env.TRELLIS_DISABLE_HOOKS === "1") {
|
|
115
122
|
return
|
|
116
123
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Uses OpenCode's chat.message hook directly so the context persists in history.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { TrellisContext, contextCollector, debugLog } from "../lib/trellis-context.js"
|
|
9
|
+
import { TrellisContext, contextCollector, debugLog, isTrellisSubagent } from "../lib/trellis-context.js"
|
|
10
10
|
import {
|
|
11
11
|
buildSessionContext,
|
|
12
12
|
hasPersistedInjectedContext,
|
|
@@ -43,6 +43,14 @@ export default async ({ directory, client }) => {
|
|
|
43
43
|
const agent = input.agent || "unknown"
|
|
44
44
|
debugLog("session", "chat.message called, sessionID:", sessionID, "agent:", agent)
|
|
45
45
|
|
|
46
|
+
// Skip Trellis sub-agent turns — sub-agent context is injected by
|
|
47
|
+
// `inject-subagent-context.js` on the parent's tool.execute.before;
|
|
48
|
+
// re-injecting the main-session SessionStart here would drown that.
|
|
49
|
+
if (isTrellisSubagent(input)) {
|
|
50
|
+
debugLog("session", "Skipping trellis subagent turn:", agent)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
46
54
|
if (process.env.TRELLIS_HOOKS === "0" || process.env.TRELLIS_DISABLE_HOOKS === "1") {
|
|
47
55
|
debugLog("session", "Skipping - TRELLIS_HOOKS disabled")
|
|
48
56
|
return
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
{
|
|
8
8
|
"type": "command",
|
|
9
9
|
"command": "{{PYTHON_CMD}} .qoder/hooks/session-start.py",
|
|
10
|
-
"timeout":
|
|
10
|
+
"timeout": 30
|
|
11
11
|
}
|
|
12
12
|
]
|
|
13
13
|
},
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
{
|
|
18
18
|
"type": "command",
|
|
19
19
|
"command": "{{PYTHON_CMD}} .qoder/hooks/session-start.py",
|
|
20
|
-
"timeout":
|
|
20
|
+
"timeout": 30
|
|
21
21
|
}
|
|
22
22
|
]
|
|
23
23
|
},
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
{
|
|
28
28
|
"type": "command",
|
|
29
29
|
"command": "{{PYTHON_CMD}} .qoder/hooks/session-start.py",
|
|
30
|
-
"timeout":
|
|
30
|
+
"timeout": 30
|
|
31
31
|
}
|
|
32
32
|
]
|
|
33
33
|
}
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
{
|
|
39
39
|
"type": "command",
|
|
40
40
|
"command": "{{PYTHON_CMD}} .qoder/hooks/inject-workflow-state.py",
|
|
41
|
-
"timeout":
|
|
41
|
+
"timeout": 15
|
|
42
42
|
}
|
|
43
43
|
]
|
|
44
44
|
}
|
|
@@ -111,31 +111,61 @@ def safe_trellis_paths_to_add(repo_root: Path) -> list[str]:
|
|
|
111
111
|
return paths
|
|
112
112
|
|
|
113
113
|
|
|
114
|
-
def safe_archive_paths_to_add(
|
|
114
|
+
def safe_archive_paths_to_add(
|
|
115
|
+
repo_root: Path,
|
|
116
|
+
task_name: str | None = None,
|
|
117
|
+
modified_children: list[str] | None = None,
|
|
118
|
+
) -> list[str]:
|
|
115
119
|
"""Return paths to stage after `task.py archive`.
|
|
116
120
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
Scoped to ONLY the paths the archive operation actually touched:
|
|
122
|
+
|
|
123
|
+
- the archive subtree (where the freshly-moved task lives)
|
|
124
|
+
- the source task directory (for source-side deletes; caller pairs
|
|
125
|
+
this with `git rm --cached` since `git add` won't stage deletes
|
|
126
|
+
for a path that no longer exists in the working tree)
|
|
127
|
+
- any child task directories whose `task.json` was edited to drop
|
|
128
|
+
the archived parent (parent-children relationship update)
|
|
129
|
+
|
|
130
|
+
This narrow scope avoids "scope creep" — dirty changes in OTHER
|
|
131
|
+
active task dirs (parallel-window edits) are NOT bundled into the
|
|
132
|
+
archive commit. Callers handle each kind of change in its own
|
|
133
|
+
commit boundary.
|
|
134
|
+
|
|
135
|
+
Backwards-compat: with no arguments, the function walks the whole
|
|
136
|
+
`.trellis/tasks/` subtree the old way (active tasks + archive). New
|
|
137
|
+
callers should always pass `task_name`.
|
|
122
138
|
"""
|
|
123
139
|
paths: list[str] = []
|
|
124
140
|
tasks_dir = repo_root / DIR_WORKFLOW / DIR_TASKS
|
|
125
|
-
if tasks_dir.is_dir():
|
|
126
|
-
|
|
127
|
-
|
|
141
|
+
if not tasks_dir.is_dir():
|
|
142
|
+
return paths
|
|
143
|
+
|
|
144
|
+
archive_dir = tasks_dir / DIR_ARCHIVE
|
|
145
|
+
|
|
146
|
+
if task_name is not None:
|
|
147
|
+
# Narrow scope — only paths that still exist on disk (so
|
|
148
|
+
# `git add` doesn't choke on the moved-away source). The caller
|
|
149
|
+
# handles the source-side deletes via `git rm --cached`
|
|
150
|
+
# explicitly.
|
|
128
151
|
if archive_dir.is_dir():
|
|
129
|
-
paths.append(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
152
|
+
paths.append(
|
|
153
|
+
f"{DIR_WORKFLOW}/{DIR_TASKS}/{DIR_ARCHIVE}"
|
|
154
|
+
)
|
|
155
|
+
for child_name in modified_children or []:
|
|
156
|
+
paths.append(f"{DIR_WORKFLOW}/{DIR_TASKS}/{child_name}")
|
|
157
|
+
return paths
|
|
158
|
+
|
|
159
|
+
# Legacy wide scope (no task_name): preserve old behavior so callers
|
|
160
|
+
# that have not been updated keep working.
|
|
161
|
+
if archive_dir.is_dir():
|
|
162
|
+
paths.append(f"{DIR_WORKFLOW}/{DIR_TASKS}/{DIR_ARCHIVE}")
|
|
163
|
+
for child in sorted(tasks_dir.iterdir()):
|
|
164
|
+
if not child.is_dir():
|
|
165
|
+
continue
|
|
166
|
+
if child.name == DIR_ARCHIVE:
|
|
167
|
+
continue
|
|
168
|
+
paths.append(f"{DIR_WORKFLOW}/{DIR_TASKS}/{child.name}")
|
|
139
169
|
return paths
|
|
140
170
|
|
|
141
171
|
|