@mindfoldhq/trellis 0.6.9 → 0.7.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/cli/index.js +2 -0
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/commands/update.d.ts.map +1 -1
  4. package/dist/commands/update.js +0 -8
  5. package/dist/commands/update.js.map +1 -1
  6. package/dist/commands/workflow.d.ts +6 -0
  7. package/dist/commands/workflow.d.ts.map +1 -1
  8. package/dist/commands/workflow.js +124 -0
  9. package/dist/commands/workflow.js.map +1 -1
  10. package/dist/configurators/workflow.d.ts +0 -14
  11. package/dist/configurators/workflow.d.ts.map +1 -1
  12. package/dist/configurators/workflow.js +1 -37
  13. package/dist/configurators/workflow.js.map +1 -1
  14. package/dist/migrations/manifests/0.6.10.json +9 -0
  15. package/dist/migrations/manifests/0.7.0-beta.0.json +9 -0
  16. package/dist/templates/claude/settings.json +22 -0
  17. package/dist/templates/codex/agents/trellis-check.toml +7 -5
  18. package/dist/templates/codex/agents/trellis-implement.toml +7 -5
  19. package/dist/templates/codex/agents/trellis-research.toml +13 -8
  20. package/dist/templates/codex/hooks/session-start.py +20 -1
  21. package/dist/templates/codex/hooks.json +25 -0
  22. package/dist/templates/copilot/hooks/session-start.py +20 -1
  23. package/dist/templates/opencode/plugins/inject-workflow-state.js +111 -10
  24. package/dist/templates/pi/extensions/trellis/index.ts.txt +4 -3
  25. package/dist/templates/shared-hooks/index.d.ts +5 -1
  26. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  27. package/dist/templates/shared-hooks/index.js +10 -1
  28. package/dist/templates/shared-hooks/index.js.map +1 -1
  29. package/dist/templates/shared-hooks/inject-spec-context.py +840 -0
  30. package/dist/templates/shared-hooks/inject-subagent-context.py +4 -3
  31. package/dist/templates/shared-hooks/inject-workflow-state.py +35 -9
  32. package/dist/templates/shared-hooks/session-start.py +22 -1
  33. package/dist/templates/trellis/config.yaml +47 -0
  34. package/dist/templates/trellis/index.d.ts +4 -3
  35. package/dist/templates/trellis/index.d.ts.map +1 -1
  36. package/dist/templates/trellis/index.js +7 -3
  37. package/dist/templates/trellis/index.js.map +1 -1
  38. package/dist/templates/trellis/scripts/add_session.py +0 -45
  39. package/dist/templates/trellis/scripts/common/active_task.py +5 -2
  40. package/dist/templates/trellis/scripts/common/config.py +18 -0
  41. package/dist/templates/trellis/scripts/common/git_context.py +34 -2
  42. package/dist/templates/trellis/scripts/common/paths.py +28 -0
  43. package/dist/templates/trellis/scripts/common/spec_inject.py +439 -0
  44. package/dist/templates/trellis/scripts/common/spec_match.py +395 -0
  45. package/dist/templates/trellis/scripts/common/task_context.py +10 -8
  46. package/dist/templates/trellis/scripts/common/task_store.py +30 -0
  47. package/dist/templates/trellis/scripts/common/workflow_phase.py +3 -2
  48. package/dist/templates/trellis/scripts/common/workflow_selection.py +177 -0
  49. package/dist/templates/trellis/scripts/task.py +82 -0
  50. package/package.json +2 -2
  51. package/dist/templates/trellis/gitattributes.txt +0 -9
@@ -207,11 +207,12 @@ def truncate_utf8(data: bytes, cap: int) -> bytes:
207
207
  seq_len = 4
208
208
  else:
209
209
  seq_len = 1
210
- # Drop the lead byte too if its full sequence didn't fit.
210
+ # Cut before the lead byte when its full sequence didn't fit;
211
+ # otherwise the trailing sequence is complete — keep it whole.
211
212
  if (i - 1) + seq_len > len(truncated):
212
- i -= 1
213
+ return truncated[: i - 1]
213
214
 
214
- return truncated[:i]
215
+ return truncated
215
216
 
216
217
 
217
218
  class _Budget:
@@ -10,9 +10,11 @@ The emitted ``hookEventName`` field is platform-aware: most hosts expect
10
10
  CodeBuddy / Droid / Codex / Copilot wiring), but Gemini CLI 0.40.x renamed
11
11
  its per-turn event to ``BeforeAgent`` and its schema validator rejects the
12
12
  legacy name. ``_detect_platform`` picks the right value at runtime.
13
- Breadcrumb text is pulled exclusively from workflow.md
14
- [workflow-state:STATUS] tag blocks — workflow.md is the single source of
15
- truth. There are no fallback dicts in this script: when workflow.md is
13
+ Breadcrumb text is pulled exclusively from the resolved workflow file's
14
+ [workflow-state:STATUS] tag blocks — the active task may select a
15
+ per-task variant (`.trellis/workflows/<id>.md` via task.json `workflow`),
16
+ otherwise personal, team, and global defaults are resolved in order.
17
+ There are no fallback dicts in this script: when the resolved workflow is
16
18
  missing or a tag is absent, the breadcrumb degrades to a generic
17
19
  "Refer to workflow.md for current step." line so users see (and fix)
18
20
  the broken state instead of the hook silently masking it.
@@ -183,16 +185,40 @@ _TAG_RE = re.compile(
183
185
  re.DOTALL,
184
186
  )
185
187
 
186
- def load_breadcrumbs(root: Path) -> dict[str, str]:
187
- """Parse workflow.md for [workflow-state:STATUS] blocks.
188
+ def _resolve_workflow_md(root: Path, input_data: dict) -> Path:
189
+ """Resolve the active task's workflow file, falling back to the global one.
188
190
 
189
- Returns {status: body_text}. workflow.md is the single source of
191
+ The per-task resolution rule lives in common.workflow_selection inside
192
+ .trellis/scripts. Older installed projects may not ship that module, and
193
+ hooks must never crash the session — ANY failure (import error, old
194
+ scripts tree, resolver bug) falls back to the global workflow.md.
195
+ """
196
+ try:
197
+ scripts_dir = root / ".trellis" / "scripts"
198
+ if str(scripts_dir) not in sys.path:
199
+ sys.path.insert(0, str(scripts_dir))
200
+ from common.workflow_selection import resolve_workflow_md # type: ignore[import-not-found]
201
+
202
+ return resolve_workflow_md(
203
+ root, input_data, platform=_detect_platform(input_data)
204
+ )
205
+ except Exception:
206
+ return root / ".trellis" / "workflow.md"
207
+
208
+
209
+ def load_breadcrumbs(root: Path, input_data: dict) -> dict[str, str]:
210
+ """Parse the resolved workflow file for [workflow-state:STATUS] blocks.
211
+
212
+ Returns {status: body_text}. The workflow file is the single source of
190
213
  truth — there are no fallback dicts in this script. Missing tags
191
- (or a missing/unreadable workflow.md) fall back to a generic line
214
+ (or a missing/unreadable workflow file) fall back to a generic line
192
215
  in build_breadcrumb so users see the broken state and fix
193
216
  workflow.md, rather than the hook silently masking the issue.
217
+ The active task's per-task workflow selection (task.json `workflow`
218
+ field) is honored via _resolve_workflow_md; without a selection this
219
+ reads the global .trellis/workflow.md exactly as before.
194
220
  """
195
- workflow = root / ".trellis" / "workflow.md"
221
+ workflow = _resolve_workflow_md(root, input_data)
196
222
  if not workflow.is_file():
197
223
  return {}
198
224
  try:
@@ -411,7 +437,7 @@ def main() -> int:
411
437
  if prompt_has_skip_keyword(data.get("prompt", ""), _resolve_skip_keyword(config)):
412
438
  return 0 # user opted out of the per-turn breadcrumb for this turn
413
439
 
414
- templates = load_breadcrumbs(root)
440
+ templates = load_breadcrumbs(root, data)
415
441
  platform = _detect_platform(data)
416
442
  task = get_active_task(root, data)
417
443
  if task is None:
@@ -715,6 +715,27 @@ def _strip_breadcrumb_tag_blocks(content: str) -> str:
715
715
  return re.sub(r"\n{3,}", "\n\n", stripped).strip()
716
716
 
717
717
 
718
+ def _resolve_workflow_md(root: Path, input_data: dict) -> Path:
719
+ """Resolve the active task's workflow file, falling back to the global one.
720
+
721
+ The per-task resolution rule lives in common.workflow_selection inside
722
+ .trellis/scripts. Older installed projects may not ship that module, and
723
+ hooks must never crash the session — ANY failure (import error, old
724
+ scripts tree, resolver bug) falls back to the global workflow.md.
725
+ """
726
+ try:
727
+ scripts_dir = root / ".trellis" / "scripts"
728
+ if str(scripts_dir) not in sys.path:
729
+ sys.path.insert(0, str(scripts_dir))
730
+ from common.workflow_selection import resolve_workflow_md # type: ignore[import-not-found]
731
+
732
+ return resolve_workflow_md(
733
+ root, input_data, platform=_detect_platform(input_data)
734
+ )
735
+ except Exception:
736
+ return root / ".trellis" / "workflow.md"
737
+
738
+
718
739
  def _build_workflow_overview(workflow_path: Path) -> str:
719
740
  """Inject only the compact Phase Index summary for SessionStart."""
720
741
  content = read_file(workflow_path)
@@ -800,7 +821,7 @@ Trellis compact SessionStart context. Use it to orient the session; load details
800
821
  output.write("\n</current-state>\n\n")
801
822
 
802
823
  output.write("<trellis-workflow>\n")
803
- output.write(_build_workflow_overview(trellis_dir / "workflow.md"))
824
+ output.write(_build_workflow_overview(_resolve_workflow_md(project_dir, hook_input)))
804
825
  output.write("\n</trellis-workflow>\n\n")
805
826
 
806
827
  output.write("<guidelines>\n")
@@ -76,6 +76,21 @@ max_journal_lines: 2000
76
76
  # Default package used when --package is not specified.
77
77
  # default_package: frontend
78
78
 
79
+ #-------------------------------------------------------------------------------
80
+ # Default workflow
81
+ #-------------------------------------------------------------------------------
82
+ # Team-shared default workflow for tasks that do not pin one. The id names a
83
+ # variant file in `.trellis/workflows/<id>.md` (populate it with
84
+ # `trellis workflow --save <id>`). This value is committed, so the whole team
85
+ # shares the same default. A per-developer override lives in the gitignored
86
+ # `.developer` file as a `workflow=<id>` line and takes precedence over this.
87
+ #
88
+ # Resolution precedence: per-task (task.json `workflow`) > personal
89
+ # (`.developer` `workflow=`) > this `default_workflow` > global
90
+ # `.trellis/workflow.md`.
91
+ #
92
+ # default_workflow: native
93
+
79
94
  #-------------------------------------------------------------------------------
80
95
  # Channel worker OOM guard
81
96
  #-------------------------------------------------------------------------------
@@ -145,6 +160,38 @@ channel:
145
160
  # max_artifact_bytes: 65536 # per task artifact (prd.md / design.md / implement.md)
146
161
  # max_total_bytes: 131072 # whole injected payload; overflow degrades to index lines
147
162
 
163
+ #-------------------------------------------------------------------------------
164
+ # Path-scoped spec injection
165
+ #-------------------------------------------------------------------------------
166
+ # When the agent touches a file (Read/Edit/Write/MultiEdit), spec .md files
167
+ # under .trellis/spec/ whose frontmatter `paths:` globs match the touched path
168
+ # are surfaced into the session right then. The first time a spec matches it is
169
+ # injected in full; while its content is unchanged and the refresh window has
170
+ # not elapsed it stays silent; once the window elapses a short `<spec-ticket>`
171
+ # reminder is emitted to counter recency decay. Editing the spec itself — or a
172
+ # SessionStart after /clear or /compact — re-injects the full text.
173
+ # Oversized specs are truncated with a notice; once the per-event payload cap
174
+ # is reached, remaining full bodies degrade to index lines (path + description)
175
+ # instead of being inlined.
176
+ #
177
+ # Character values: the ceiling this budget respects is Claude Code's
178
+ # documented 10,000-CHARACTER additionalContext limit, so the caps count
179
+ # characters too (byte caps made CJK specs pay 3x for the same text).
180
+ # `0` disables the corresponding limit.
181
+ # The refresh window uses wall-clock seconds. `0` disables time-based reminders;
182
+ # SessionStart resets after /clear or /compact still force a full re-injection.
183
+ #
184
+ # spec_injection:
185
+ # enabled: true # false disables injection entirely
186
+ # max_spec_chars: 9400 # per matched spec file
187
+ # max_total_chars: 9500 # whole per-event payload; overflow degrades to index lines
188
+ # refresh_window_seconds: 2700 # touches past this interval re-emit a ticket
189
+ # tools: # tool events that trigger injection
190
+ # - Read
191
+ # - Edit
192
+ # - Write
193
+ # - MultiEdit
194
+
148
195
  #-------------------------------------------------------------------------------
149
196
  # Per-turn prompt injection
150
197
  #-------------------------------------------------------------------------------
@@ -15,8 +15,7 @@
15
15
  * ├── scripts-shell-archive/ # Archived shell scripts (for reference)
16
16
  * ├── workflow.md # Workflow guide
17
17
  * ├── config.yaml # Trellis configuration
18
- * ├── gitignore.txt # .gitignore content
19
- * └── gitattributes.txt # project-root .gitattributes content
18
+ * └── gitignore.txt # .gitignore content
20
19
  */
21
20
  export declare const scriptsInit: string;
22
21
  export declare const commonInit: string;
@@ -38,7 +37,10 @@ export declare const commonTaskStore: string;
38
37
  export declare const commonSessionContext: string;
39
38
  export declare const commonPackagesContext: string;
40
39
  export declare const commonWorkflowPhase: string;
40
+ export declare const commonWorkflowSelection: string;
41
41
  export declare const commonTrellisConfig: string;
42
+ export declare const commonSpecMatch: string;
43
+ export declare const commonSpecInject: string;
42
44
  export declare const commonSafeCommit: string;
43
45
  export declare const getDeveloperScript: string;
44
46
  export declare const initDeveloperScript: string;
@@ -48,7 +50,6 @@ export declare const addSessionScript: string;
48
50
  export declare const workflowMdTemplate: string;
49
51
  export declare const configYamlTemplate: string;
50
52
  export declare const gitignoreTemplate: string;
51
- export declare const gitattributesTemplate: string;
52
53
  export declare const implementAgentTemplate: string;
53
54
  export declare const checkAgentTemplate: string;
54
55
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAcH,eAAO,MAAM,WAAW,QAAsC,CAAC;AAG/D,eAAO,MAAM,UAAU,QAA6C,CAAC;AACrE,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,eAAe,QAA8C,CAAC;AAC3E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,YAAY,QAA2C,CAAC;AACrE,eAAO,MAAM,QAAQ,QAAuC,CAAC;AAC7D,eAAO,MAAM,SAAS,QAAwC,CAAC;AAC/D,eAAO,MAAM,SAAS,QAAwC,CAAC;AAC/D,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,iBAAiB,QAAiD,CAAC;AAChF,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AACF,eAAO,MAAM,qBAAqB,QAEjC,CAAC;AACF,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AACF,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AACF,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAG9E,eAAO,MAAM,kBAAkB,QAA2C,CAAC;AAC3E,eAAO,MAAM,mBAAmB,QAA4C,CAAC;AAC7E,eAAO,MAAM,UAAU,QAAkC,CAAC;AAC1D,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AACvE,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AAGvE,eAAO,MAAM,kBAAkB,QAA8B,CAAC;AAC9D,eAAO,MAAM,kBAAkB,QAA8B,CAAC;AAC9D,eAAO,MAAM,iBAAiB,QAAgC,CAAC;AAC/D,eAAO,MAAM,qBAAqB,QAAoC,CAAC;AAMvE,eAAO,MAAM,sBAAsB,QAAsC,CAAC;AAC1E,eAAO,MAAM,kBAAkB,QAAkC,CAAC;AAElE;;GAEG;AACH,wBAAgB,aAAa,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAqCnD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAKlD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAcH,eAAO,MAAM,WAAW,QAAsC,CAAC;AAG/D,eAAO,MAAM,UAAU,QAA6C,CAAC;AACrE,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,eAAe,QAA8C,CAAC;AAC3E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,YAAY,QAA2C,CAAC;AACrE,eAAO,MAAM,QAAQ,QAAuC,CAAC;AAC7D,eAAO,MAAM,SAAS,QAAwC,CAAC;AAC/D,eAAO,MAAM,SAAS,QAAwC,CAAC;AAC/D,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,iBAAiB,QAAiD,CAAC;AAChF,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AACF,eAAO,MAAM,qBAAqB,QAEjC,CAAC;AACF,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AACF,eAAO,MAAM,uBAAuB,QAEnC,CAAC;AACF,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AACF,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAG9E,eAAO,MAAM,kBAAkB,QAA2C,CAAC;AAC3E,eAAO,MAAM,mBAAmB,QAA4C,CAAC;AAC7E,eAAO,MAAM,UAAU,QAAkC,CAAC;AAC1D,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AACvE,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AAGvE,eAAO,MAAM,kBAAkB,QAA8B,CAAC;AAC9D,eAAO,MAAM,kBAAkB,QAA8B,CAAC;AAC9D,eAAO,MAAM,iBAAiB,QAAgC,CAAC;AAM/D,eAAO,MAAM,sBAAsB,QAAsC,CAAC;AAC1E,eAAO,MAAM,kBAAkB,QAAkC,CAAC;AAElE;;GAEG;AACH,wBAAgB,aAAa,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAwCnD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAKlD"}
@@ -15,8 +15,7 @@
15
15
  * ├── scripts-shell-archive/ # Archived shell scripts (for reference)
16
16
  * ├── workflow.md # Workflow guide
17
17
  * ├── config.yaml # Trellis configuration
18
- * ├── gitignore.txt # .gitignore content
19
- * └── gitattributes.txt # project-root .gitattributes content
18
+ * └── gitignore.txt # .gitignore content
20
19
  */
21
20
  import { readFileSync } from "node:fs";
22
21
  import { dirname, join } from "node:path";
@@ -48,7 +47,10 @@ export const commonTaskStore = readTemplate("scripts/common/task_store.py");
48
47
  export const commonSessionContext = readTemplate("scripts/common/session_context.py");
49
48
  export const commonPackagesContext = readTemplate("scripts/common/packages_context.py");
50
49
  export const commonWorkflowPhase = readTemplate("scripts/common/workflow_phase.py");
50
+ export const commonWorkflowSelection = readTemplate("scripts/common/workflow_selection.py");
51
51
  export const commonTrellisConfig = readTemplate("scripts/common/trellis_config.py");
52
+ export const commonSpecMatch = readTemplate("scripts/common/spec_match.py");
53
+ export const commonSpecInject = readTemplate("scripts/common/spec_inject.py");
52
54
  export const commonSafeCommit = readTemplate("scripts/common/safe_commit.py");
53
55
  // Python scripts - main
54
56
  export const getDeveloperScript = readTemplate("scripts/get_developer.py");
@@ -60,7 +62,6 @@ export const addSessionScript = readTemplate("scripts/add_session.py");
60
62
  export const workflowMdTemplate = readTemplate("workflow.md");
61
63
  export const configYamlTemplate = readTemplate("config.yaml");
62
64
  export const gitignoreTemplate = readTemplate("gitignore.txt");
63
- export const gitattributesTemplate = readTemplate("gitattributes.txt");
64
65
  // Channel runtime agent definitions (loaded by
65
66
  // `packages/cli/src/commands/channel/agent-loader.ts` from `.trellis/agents/`).
66
67
  // These are platform-agnostic Trellis runtime files dispatched at `trellis init`
@@ -94,7 +95,10 @@ export function getAllScripts() {
94
95
  scripts.set("common/session_context.py", commonSessionContext);
95
96
  scripts.set("common/packages_context.py", commonPackagesContext);
96
97
  scripts.set("common/workflow_phase.py", commonWorkflowPhase);
98
+ scripts.set("common/workflow_selection.py", commonWorkflowSelection);
97
99
  scripts.set("common/trellis_config.py", commonTrellisConfig);
100
+ scripts.set("common/spec_match.py", commonSpecMatch);
101
+ scripts.set("common/spec_inject.py", commonSpecInject);
98
102
  scripts.set("common/safe_commit.py", commonSafeCommit);
99
103
  // Main
100
104
  scripts.set("get_developer.py", getDeveloperScript);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"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"}
@@ -41,7 +41,6 @@ from common.paths import (
41
41
  )
42
42
  from common.developer import ensure_developer
43
43
  from common.git import run_git
44
- from common.log import Colors, colored
45
44
  from common.safe_commit import (
46
45
  print_gitignore_warning,
47
46
  safe_git_add,
@@ -181,49 +180,6 @@ def resolve_session_branch(
181
180
  return None
182
181
 
183
182
 
184
- def is_git_worktree(repo_root: Path) -> bool:
185
- """Return True when repo_root is a linked worktree (not the main working tree).
186
-
187
- Standard test: `git rev-parse --git-dir` (per-worktree) differs from
188
- `git rev-parse --git-common-dir` (shared across all worktrees) once both
189
- are resolved to absolute paths. In the main working tree these are the
190
- same directory.
191
- """
192
- rc_dir, git_dir, _ = run_git(["rev-parse", "--git-dir"], cwd=repo_root)
193
- rc_common, git_common_dir, _ = run_git(
194
- ["rev-parse", "--git-common-dir"], cwd=repo_root
195
- )
196
- if rc_dir != 0 or rc_common != 0:
197
- return False
198
-
199
- git_dir_path = (repo_root / git_dir.strip()).resolve()
200
- git_common_dir_path = (repo_root / git_common_dir.strip()).resolve()
201
- return git_dir_path != git_common_dir_path
202
-
203
-
204
- def warn_if_parallel_worktree(repo_root: Path) -> None:
205
- """Non-blocking note: index.md conflicts across parallel worktrees/branches
206
- are expected and safe. Only fires when running in a linked git worktree
207
- (not the main tree) with `session_auto_commit` enabled (#415 quick-fix tier).
208
- """
209
- if not get_session_auto_commit(repo_root):
210
- return
211
- if not is_git_worktree(repo_root):
212
- return
213
- print(
214
- colored(
215
- "[NOTE] Running in a git worktree with session_auto_commit enabled: "
216
- "journal-*.md files auto-merge via .gitattributes, but index.md "
217
- "conflicts across parallel worktrees/branches are expected and safe "
218
- "to resolve by picking either side (task state lives in task.json, "
219
- "not index.md). See .trellis/spec/cli/backend/directory-structure.md "
220
- '("Workspace Journal Merge Behavior").',
221
- Colors.YELLOW,
222
- ),
223
- file=sys.stderr,
224
- )
225
-
226
-
227
183
  def create_new_journal_file(
228
184
  dev_dir: Path, num: int, developer: str, today: str, max_lines: int = 2000,
229
185
  ) -> Path:
@@ -520,7 +476,6 @@ def add_session(
520
476
  ) -> int:
521
477
  """Add a new session."""
522
478
  repo_root = get_repo_root()
523
- warn_if_parallel_worktree(repo_root)
524
479
  ensure_developer(repo_root)
525
480
 
526
481
  developer = get_developer(repo_root)
@@ -612,13 +612,16 @@ def clear_active_task(
612
612
  platform_input: dict[str, Any] | None = None,
613
613
  platform: str | None = None,
614
614
  ) -> ActiveTask:
615
- """Clear the active task by deleting the current session context file."""
615
+ """Clear the active task by deleting its resolved session context file."""
616
616
  context_key = resolve_context_key(platform_input, platform)
617
617
  if not context_key:
618
618
  return ActiveTask(None, "none")
619
619
 
620
620
  previous = resolve_active_task(repo_root, platform_input, platform)
621
- context_path = _context_path(repo_root, context_key)
621
+ if not previous.task_path or not previous.context_key:
622
+ return previous
623
+
624
+ context_path = _context_path(repo_root, previous.context_key)
622
625
  if context_path.is_file():
623
626
  _remove_file(context_path)
624
627
  return previous
@@ -281,6 +281,24 @@ def get_codex_dispatch_mode(repo_root: Path | None = None) -> str:
281
281
  return "inline"
282
282
 
283
283
 
284
+ def get_default_workflow(repo_root: Path | None = None) -> str | None:
285
+ """Return the team-shared default workflow id from config.yaml.
286
+
287
+ Reads the top-level ``default_workflow`` key — a slug naming a variant in
288
+ ``.trellis/workflows/<id>.md``. Returns None when unset or blank. This is
289
+ the git-tracked, team-shared default; a per-developer override lives in the
290
+ gitignored ``.developer`` file (``workflow=<id>``, see
291
+ ``paths.get_developer_workflow``) and outranks it. Fail-open: a missing or
292
+ non-string value simply means "no team default" — no warning (this is read
293
+ on every turn by hooks and must not add per-turn noise).
294
+ """
295
+ config = _load_config(repo_root)
296
+ raw = config.get("default_workflow")
297
+ if not isinstance(raw, str):
298
+ return None
299
+ return raw.strip() or None
300
+
301
+
284
302
  DEFAULT_CONTEXT_INJECTION_MAX_FILE_BYTES = 32768
285
303
  DEFAULT_CONTEXT_INJECTION_MAX_ARTIFACT_BYTES = 65536
286
304
  DEFAULT_CONTEXT_INJECTION_MAX_TOTAL_BYTES = 131072
@@ -27,6 +27,8 @@ from .packages_context import (
27
27
  get_context_packages_text,
28
28
  get_context_packages_json,
29
29
  )
30
+ from .paths import get_repo_root
31
+ from .spec_match import match_specs_for_file
30
32
  from .trellis_config import read_trellis_config
31
33
  from .workflow_phase import (
32
34
  filter_platform,
@@ -57,9 +59,9 @@ def main() -> None:
57
59
  parser.add_argument(
58
60
  "--mode",
59
61
  "-m",
60
- choices=["default", "record", "packages", "phase"],
62
+ choices=["default", "record", "packages", "phase", "spec"],
61
63
  default="default",
62
- help="Output mode: default (full context), record (for record-session), packages (package info only), phase (workflow step extraction)",
64
+ help="Output mode: default (full context), record (for record-session), packages (package info only), phase (workflow step extraction), spec (specs governing a file)",
63
65
  )
64
66
  parser.add_argument(
65
67
  "--step",
@@ -69,6 +71,10 @@ def main() -> None:
69
71
  "--platform",
70
72
  help="Platform name for --mode phase, e.g. cursor, claude-code. Filters platform-tagged blocks.",
71
73
  )
74
+ parser.add_argument(
75
+ "--file",
76
+ help="File path (absolute or repo-relative) for --mode spec. Lists spec files whose frontmatter paths match it.",
77
+ )
72
78
 
73
79
  args = parser.parse_args()
74
80
 
@@ -95,6 +101,32 @@ def main() -> None:
95
101
  )
96
102
  content = filter_platform(content, effective)
97
103
  print(content, end="")
104
+ elif args.mode == "spec":
105
+ if not args.file:
106
+ parser.error("--file is required with --mode spec")
107
+ matches = match_specs_for_file(get_repo_root(), args.file)
108
+ if args.json:
109
+ print(
110
+ json.dumps(
111
+ {
112
+ "file": args.file,
113
+ "matches": [
114
+ {
115
+ "path": match.rel_path,
116
+ "description": match.description,
117
+ }
118
+ for match in matches
119
+ ],
120
+ },
121
+ indent=2,
122
+ ensure_ascii=False,
123
+ )
124
+ )
125
+ elif matches:
126
+ for match in matches:
127
+ print(f"{match.rel_path} — {match.description or '(no description)'}")
128
+ else:
129
+ print(f"No spec files declare paths matching {args.file}.")
98
130
  else:
99
131
  if args.json:
100
132
  output_json()
@@ -94,6 +94,34 @@ def get_developer(repo_root: Path | None = None) -> str | None:
94
94
  return None
95
95
 
96
96
 
97
+ def get_developer_workflow(repo_root: Path | None = None) -> str | None:
98
+ """Get the personal workflow override from the .developer file.
99
+
100
+ Reads an optional ``workflow=<id>`` line from the gitignored ``.developer``
101
+ file — the per-developer, git-excluded override that outranks the
102
+ team-shared ``default_workflow`` in config.yaml. Returns None when the file
103
+ or the line is absent/blank. Never raises. Additive to ``get_developer``:
104
+ the ``name=`` reader ignores this line and vice versa.
105
+ """
106
+ if repo_root is None:
107
+ repo_root = get_repo_root()
108
+
109
+ dev_file = repo_root / DIR_WORKFLOW / FILE_DEVELOPER
110
+
111
+ if not dev_file.is_file():
112
+ return None
113
+
114
+ try:
115
+ content = dev_file.read_text(encoding="utf-8")
116
+ for line in content.splitlines():
117
+ if line.startswith("workflow="):
118
+ return line.split("=", 1)[1].strip() or None
119
+ except (OSError, IOError):
120
+ pass
121
+
122
+ return None
123
+
124
+
97
125
  def check_developer(repo_root: Path | None = None) -> bool:
98
126
  """Check if developer is initialized.
99
127