@mindfoldhq/trellis 0.4.0-beta.1 → 0.4.0-beta.10
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 +19 -5
- package/dist/cli/index.js +3 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +48 -23
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +52 -41
- package/dist/commands/update.js.map +1 -1
- package/dist/configurators/codebuddy.d.ts +11 -0
- package/dist/configurators/codebuddy.d.ts.map +1 -0
- package/dist/configurators/codebuddy.js +58 -0
- package/dist/configurators/codebuddy.js.map +1 -0
- package/dist/configurators/codex.d.ts +7 -4
- package/dist/configurators/codex.d.ts.map +1 -1
- package/dist/configurators/codex.js +40 -10
- 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 +11 -1
- package/dist/configurators/index.d.ts.map +1 -1
- package/dist/configurators/index.js +72 -4
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/opencode.d.ts +1 -1
- package/dist/configurators/opencode.js +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/configurators/workflow.d.ts +1 -1
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +4 -2
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/migrations/manifests/0.3.10.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.10.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.2.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.3.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.4.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.5.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.6.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.7.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.8.json +34 -0
- package/dist/migrations/manifests/0.4.0-beta.9.json +9 -0
- package/dist/templates/claude/commands/trellis/record-session.md +3 -2
- 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 +33 -9
- package/dist/templates/claude/hooks/statusline.py +211 -0
- package/dist/templates/claude/settings.json +4 -0
- package/dist/templates/codebuddy/commands/trellis/before-dev.md +29 -0
- package/dist/templates/codebuddy/commands/trellis/brainstorm.md +487 -0
- package/dist/templates/codebuddy/commands/trellis/break-loop.md +107 -0
- package/dist/templates/codebuddy/commands/trellis/check-cross-layer.md +153 -0
- package/dist/templates/codebuddy/commands/trellis/check.md +25 -0
- package/dist/templates/codebuddy/commands/trellis/create-command.md +154 -0
- package/dist/templates/codebuddy/commands/trellis/finish-work.md +143 -0
- package/dist/templates/codebuddy/commands/trellis/integrate-skill.md +219 -0
- package/dist/templates/codebuddy/commands/trellis/onboard.md +358 -0
- package/dist/templates/codebuddy/commands/trellis/record-session.md +61 -0
- package/dist/templates/codebuddy/commands/trellis/start.md +373 -0
- package/dist/templates/codebuddy/commands/trellis/update-spec.md +354 -0
- package/dist/templates/codebuddy/index.d.ts +25 -0
- package/dist/templates/codebuddy/index.d.ts.map +1 -0
- package/dist/templates/codebuddy/index.js +45 -0
- package/dist/templates/codebuddy/index.js.map +1 -0
- package/dist/templates/codex/agents/check.toml +23 -0
- package/dist/templates/codex/agents/implement.toml +19 -0
- package/dist/templates/codex/agents/research.toml +26 -0
- package/dist/templates/codex/codex-skills/parallel/SKILL.md +194 -0
- package/dist/templates/codex/config.toml +5 -0
- package/dist/templates/codex/hooks/session-start.py +228 -0
- package/dist/templates/codex/hooks.json +16 -0
- package/dist/templates/codex/index.d.ts +27 -5
- package/dist/templates/codex/index.d.ts.map +1 -1
- package/dist/templates/codex/index.js +60 -8
- package/dist/templates/codex/index.js.map +1 -1
- package/dist/templates/codex/skills/before-dev/SKILL.md +1 -1
- package/dist/templates/codex/skills/brainstorm/SKILL.md +1 -1
- package/dist/templates/codex/skills/break-loop/SKILL.md +1 -1
- package/dist/templates/codex/skills/check/SKILL.md +1 -1
- package/dist/templates/codex/skills/check-cross-layer/SKILL.md +1 -1
- package/dist/templates/codex/skills/create-command/SKILL.md +1 -1
- package/dist/templates/codex/skills/finish-work/SKILL.md +1 -1
- package/dist/templates/codex/skills/improve-ut/SKILL.md +69 -0
- package/dist/templates/codex/skills/integrate-skill/SKILL.md +1 -1
- package/dist/templates/codex/skills/onboard/SKILL.md +1 -1
- package/dist/templates/codex/skills/record-session/SKILL.md +4 -3
- package/dist/templates/codex/skills/start/SKILL.md +1 -1
- package/dist/templates/codex/skills/update-spec/SKILL.md +1 -1
- package/dist/templates/copilot/hooks/session-start.py +218 -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/cursor/commands/trellis-record-session.md +3 -2
- package/dist/templates/extract.d.ts +36 -0
- package/dist/templates/extract.d.ts.map +1 -1
- package/dist/templates/extract.js +64 -0
- package/dist/templates/extract.js.map +1 -1
- package/dist/templates/gemini/commands/trellis/record-session.toml +3 -2
- package/dist/templates/iflow/commands/trellis/record-session.md +3 -2
- 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 +33 -9
- package/dist/templates/kilo/workflows/record-session.md +3 -2
- package/dist/templates/kiro/skills/before-dev/SKILL.md +1 -1
- package/dist/templates/kiro/skills/brainstorm/SKILL.md +1 -1
- package/dist/templates/kiro/skills/break-loop/SKILL.md +1 -1
- package/dist/templates/kiro/skills/check/SKILL.md +1 -1
- package/dist/templates/kiro/skills/check-cross-layer/SKILL.md +1 -1
- package/dist/templates/kiro/skills/create-command/SKILL.md +1 -1
- package/dist/templates/kiro/skills/finish-work/SKILL.md +1 -1
- package/dist/templates/kiro/skills/integrate-skill/SKILL.md +1 -1
- package/dist/templates/kiro/skills/onboard/SKILL.md +1 -1
- package/dist/templates/kiro/skills/record-session/SKILL.md +4 -3
- package/dist/templates/kiro/skills/start/SKILL.md +1 -1
- package/dist/templates/kiro/skills/update-spec/SKILL.md +1 -1
- package/dist/templates/markdown/agents.md +4 -0
- package/dist/templates/markdown/spec/backend/directory-structure.md +1 -1
- package/dist/templates/markdown/workspace-index.md +2 -0
- package/dist/templates/opencode/agents/dispatch.md +20 -19
- package/dist/templates/opencode/commands/trellis/record-session.md +3 -2
- package/dist/templates/opencode/lib/trellis-context.js +42 -2
- package/dist/templates/opencode/{plugin → plugins}/session-start.js +7 -27
- package/dist/templates/qoder/skills/before-dev/SKILL.md +1 -1
- package/dist/templates/qoder/skills/brainstorm/SKILL.md +1 -1
- package/dist/templates/qoder/skills/break-loop/SKILL.md +1 -1
- package/dist/templates/qoder/skills/check/SKILL.md +1 -1
- package/dist/templates/qoder/skills/check-cross-layer/SKILL.md +1 -1
- package/dist/templates/qoder/skills/create-command/SKILL.md +1 -1
- package/dist/templates/qoder/skills/finish-work/SKILL.md +1 -1
- package/dist/templates/qoder/skills/integrate-skill/SKILL.md +1 -1
- package/dist/templates/qoder/skills/onboard/SKILL.md +1 -1
- package/dist/templates/qoder/skills/record-session/SKILL.md +4 -3
- package/dist/templates/qoder/skills/start/SKILL.md +1 -1
- package/dist/templates/qoder/skills/update-spec/SKILL.md +1 -1
- package/dist/templates/trellis/scripts/add_session.py +69 -16
- package/dist/templates/trellis/scripts/common/__init__.py +2 -0
- package/dist/templates/trellis/scripts/common/cli_adapter.py +133 -21
- package/dist/templates/trellis/scripts/common/config.py +40 -0
- package/dist/templates/trellis/scripts/common/developer.py +2 -2
- package/dist/templates/trellis/scripts/common/packages_context.py +9 -4
- package/dist/templates/trellis/scripts/common/paths.py +57 -6
- package/dist/templates/trellis/scripts/common/session_context.py +98 -2
- package/dist/templates/trellis/scripts/common/task_context.py +27 -1
- 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/create_bootstrap.py +1 -1
- package/dist/templates/trellis/scripts/multi_agent/plan.py +7 -6
- package/dist/templates/trellis/scripts/multi_agent/start.py +16 -11
- package/dist/templates/trellis/scripts/task.py +1 -1
- package/dist/templates/trellis/scripts-shell-archive/create-bootstrap.sh +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 +15 -3
- package/dist/types/ai-tools.d.ts.map +1 -1
- package/dist/types/ai-tools.js +42 -2
- package/dist/types/ai-tools.js.map +1 -1
- package/dist/utils/project-detector.d.ts +5 -0
- package/dist/utils/project-detector.d.ts.map +1 -1
- package/dist/utils/project-detector.js +7 -0
- package/dist/utils/project-detector.js.map +1 -1
- package/dist/utils/template-fetcher.d.ts +24 -3
- package/dist/utils/template-fetcher.d.ts.map +1 -1
- package/dist/utils/template-fetcher.js +129 -16
- package/dist/utils/template-fetcher.js.map +1 -1
- package/package.json +1 -1
- /package/dist/templates/opencode/{plugin → plugins}/inject-subagent-context.js +0 -0
|
@@ -13,7 +13,7 @@ from __future__ import annotations
|
|
|
13
13
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
|
-
from .config import get_default_package, get_packages, get_spec_scope
|
|
16
|
+
from .config import _is_true_config_value, get_default_package, get_packages, get_spec_scope
|
|
17
17
|
from .paths import (
|
|
18
18
|
DIR_SPEC,
|
|
19
19
|
DIR_WORKFLOW,
|
|
@@ -91,7 +91,8 @@ def _resolve_scope_set(
|
|
|
91
91
|
def get_packages_info(repo_root: Path) -> list[dict]:
|
|
92
92
|
"""Get structured package info for monorepo projects.
|
|
93
93
|
|
|
94
|
-
Returns list of dicts with keys: name, path, type, default, specLayers,
|
|
94
|
+
Returns list of dicts with keys: name, path, type, default, specLayers,
|
|
95
|
+
isSubmodule, isGitRepo.
|
|
95
96
|
Returns empty list for single-repo projects.
|
|
96
97
|
"""
|
|
97
98
|
packages = get_packages(repo_root)
|
|
@@ -105,6 +106,7 @@ def get_packages_info(repo_root: Path) -> list[dict]:
|
|
|
105
106
|
for pkg_name, pkg_config in packages.items():
|
|
106
107
|
pkg_path = pkg_config.get("path", pkg_name) if isinstance(pkg_config, dict) else str(pkg_config)
|
|
107
108
|
pkg_type = pkg_config.get("type", "local") if isinstance(pkg_config, dict) else "local"
|
|
109
|
+
pkg_git = pkg_config.get("git", False) if isinstance(pkg_config, dict) else False
|
|
108
110
|
layers = _scan_spec_layers(spec_dir, pkg_name)
|
|
109
111
|
|
|
110
112
|
result.append({
|
|
@@ -114,6 +116,7 @@ def get_packages_info(repo_root: Path) -> list[dict]:
|
|
|
114
116
|
"default": pkg_name == default_pkg,
|
|
115
117
|
"specLayers": layers,
|
|
116
118
|
"isSubmodule": pkg_type == "submodule",
|
|
119
|
+
"isGitRepo": _is_true_config_value(pkg_git),
|
|
117
120
|
})
|
|
118
121
|
|
|
119
122
|
return result
|
|
@@ -139,9 +142,10 @@ def get_packages_section(repo_root: Path) -> str:
|
|
|
139
142
|
for pkg in pkg_info:
|
|
140
143
|
layers_str = f" [{', '.join(pkg['specLayers'])}]" if pkg["specLayers"] else ""
|
|
141
144
|
submodule_tag = " (submodule)" if pkg["isSubmodule"] else ""
|
|
145
|
+
git_repo_tag = " (git repo)" if pkg["isGitRepo"] else ""
|
|
142
146
|
default_tag = " *" if pkg["default"] else ""
|
|
143
147
|
lines.append(
|
|
144
|
-
f"- {pkg['name']:<16} {pkg['path']:<20}{layers_str}{submodule_tag}{default_tag}"
|
|
148
|
+
f"- {pkg['name']:<16} {pkg['path']:<20}{layers_str}{submodule_tag}{git_repo_tag}{default_tag}"
|
|
145
149
|
)
|
|
146
150
|
|
|
147
151
|
if default_pkg:
|
|
@@ -179,13 +183,14 @@ def get_context_packages_text(repo_root: Path | None = None) -> str:
|
|
|
179
183
|
for pkg in pkg_info:
|
|
180
184
|
default_tag = " (default)" if pkg["default"] else ""
|
|
181
185
|
type_tag = f" [{pkg['type']}]" if pkg["type"] != "local" else ""
|
|
186
|
+
git_tag = " [git repo]" if pkg["isGitRepo"] else ""
|
|
182
187
|
|
|
183
188
|
# Scope annotation
|
|
184
189
|
scope_tag = ""
|
|
185
190
|
if scope_set is not None and pkg["name"] not in scope_set:
|
|
186
191
|
scope_tag = " (out of scope)"
|
|
187
192
|
|
|
188
|
-
lines.append(f"### {pkg['name']}{default_tag}{type_tag}{scope_tag}")
|
|
193
|
+
lines.append(f"### {pkg['name']}{default_tag}{type_tag}{git_tag}{scope_tag}")
|
|
189
194
|
lines.append(f"Path: {pkg['path']}")
|
|
190
195
|
if pkg["specLayers"]:
|
|
191
196
|
lines.append(f"Spec layers: {', '.join(pkg['specLayers'])}")
|
|
@@ -221,6 +221,50 @@ def _get_current_task_file(repo_root: Path | None = None) -> Path:
|
|
|
221
221
|
return repo_root / DIR_WORKFLOW / FILE_CURRENT_TASK
|
|
222
222
|
|
|
223
223
|
|
|
224
|
+
def normalize_task_ref(task_ref: str) -> str:
|
|
225
|
+
"""Normalize a task ref for stable storage in .current-task.
|
|
226
|
+
|
|
227
|
+
Stored refs should prefer repo-relative POSIX paths like
|
|
228
|
+
`.trellis/tasks/03-27-my-task`, even on Windows. Absolute paths are preserved
|
|
229
|
+
unless they can later be converted back to repo-relative form by callers.
|
|
230
|
+
"""
|
|
231
|
+
normalized = task_ref.strip()
|
|
232
|
+
if not normalized:
|
|
233
|
+
return ""
|
|
234
|
+
|
|
235
|
+
path_obj = Path(normalized)
|
|
236
|
+
if path_obj.is_absolute():
|
|
237
|
+
return str(path_obj)
|
|
238
|
+
|
|
239
|
+
normalized = normalized.replace("\\", "/")
|
|
240
|
+
while normalized.startswith("./"):
|
|
241
|
+
normalized = normalized[2:]
|
|
242
|
+
|
|
243
|
+
if normalized.startswith(f"{DIR_TASKS}/"):
|
|
244
|
+
return f"{DIR_WORKFLOW}/{normalized}"
|
|
245
|
+
|
|
246
|
+
return normalized
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def resolve_task_ref(task_ref: str, repo_root: Path | None = None) -> Path | None:
|
|
250
|
+
"""Resolve a task ref from .current-task to an absolute task directory path."""
|
|
251
|
+
if repo_root is None:
|
|
252
|
+
repo_root = get_repo_root()
|
|
253
|
+
|
|
254
|
+
normalized = normalize_task_ref(task_ref)
|
|
255
|
+
if not normalized:
|
|
256
|
+
return None
|
|
257
|
+
|
|
258
|
+
path_obj = Path(normalized)
|
|
259
|
+
if path_obj.is_absolute():
|
|
260
|
+
return path_obj
|
|
261
|
+
|
|
262
|
+
if normalized.startswith(f"{DIR_WORKFLOW}/"):
|
|
263
|
+
return repo_root / path_obj
|
|
264
|
+
|
|
265
|
+
return repo_root / DIR_WORKFLOW / DIR_TASKS / path_obj
|
|
266
|
+
|
|
267
|
+
|
|
224
268
|
def get_current_task(repo_root: Path | None = None) -> str | None:
|
|
225
269
|
"""Get current task directory path (relative to repo_root).
|
|
226
270
|
|
|
@@ -236,7 +280,8 @@ def get_current_task(repo_root: Path | None = None) -> str | None:
|
|
|
236
280
|
return None
|
|
237
281
|
|
|
238
282
|
try:
|
|
239
|
-
|
|
283
|
+
content = current_file.read_text(encoding="utf-8").strip()
|
|
284
|
+
return normalize_task_ref(content) if content else None
|
|
240
285
|
except (OSError, IOError):
|
|
241
286
|
return None
|
|
242
287
|
|
|
@@ -255,7 +300,7 @@ def get_current_task_abs(repo_root: Path | None = None) -> Path | None:
|
|
|
255
300
|
|
|
256
301
|
relative = get_current_task(repo_root)
|
|
257
302
|
if relative:
|
|
258
|
-
return repo_root
|
|
303
|
+
return resolve_task_ref(relative, repo_root)
|
|
259
304
|
return None
|
|
260
305
|
|
|
261
306
|
|
|
@@ -272,18 +317,24 @@ def set_current_task(task_path: str, repo_root: Path | None = None) -> bool:
|
|
|
272
317
|
if repo_root is None:
|
|
273
318
|
repo_root = get_repo_root()
|
|
274
319
|
|
|
275
|
-
|
|
320
|
+
normalized = normalize_task_ref(task_path)
|
|
321
|
+
if not normalized:
|
|
276
322
|
return False
|
|
277
323
|
|
|
278
324
|
# Verify task directory exists
|
|
279
|
-
full_path = repo_root
|
|
280
|
-
if not full_path.is_dir():
|
|
325
|
+
full_path = resolve_task_ref(normalized, repo_root)
|
|
326
|
+
if full_path is None or not full_path.is_dir():
|
|
281
327
|
return False
|
|
282
328
|
|
|
329
|
+
try:
|
|
330
|
+
normalized = full_path.relative_to(repo_root).as_posix()
|
|
331
|
+
except ValueError:
|
|
332
|
+
normalized = str(full_path)
|
|
333
|
+
|
|
283
334
|
current_file = _get_current_task_file(repo_root)
|
|
284
335
|
|
|
285
336
|
try:
|
|
286
|
-
current_file.write_text(
|
|
337
|
+
current_file.write_text(normalized, encoding="utf-8")
|
|
287
338
|
return True
|
|
288
339
|
except (OSError, IOError):
|
|
289
340
|
return False
|
|
@@ -16,6 +16,7 @@ from __future__ import annotations
|
|
|
16
16
|
import json
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
|
|
19
|
+
from .config import get_git_packages
|
|
19
20
|
from .git import run_git
|
|
20
21
|
from .packages_context import get_packages_section
|
|
21
22
|
from .tasks import iter_active_tasks, load_task, get_all_statuses, children_progress
|
|
@@ -34,6 +35,79 @@ from .paths import (
|
|
|
34
35
|
)
|
|
35
36
|
|
|
36
37
|
|
|
38
|
+
# =============================================================================
|
|
39
|
+
# Helpers
|
|
40
|
+
# =============================================================================
|
|
41
|
+
|
|
42
|
+
def _collect_package_git_info(repo_root: Path) -> list[dict]:
|
|
43
|
+
"""Collect git status and recent commits for packages with independent git repos.
|
|
44
|
+
|
|
45
|
+
Only packages marked with ``git: true`` in config.yaml are included.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
List of dicts with keys: name, path, branch, isClean,
|
|
49
|
+
uncommittedChanges, recentCommits.
|
|
50
|
+
Empty list if no git-repo packages are configured.
|
|
51
|
+
"""
|
|
52
|
+
git_pkgs = get_git_packages(repo_root)
|
|
53
|
+
if not git_pkgs:
|
|
54
|
+
return []
|
|
55
|
+
|
|
56
|
+
result = []
|
|
57
|
+
for pkg_name, pkg_path in git_pkgs.items():
|
|
58
|
+
pkg_dir = repo_root / pkg_path
|
|
59
|
+
if not (pkg_dir / ".git").exists():
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
_, branch_out, _ = run_git(["branch", "--show-current"], cwd=pkg_dir)
|
|
63
|
+
branch = branch_out.strip() or "unknown"
|
|
64
|
+
|
|
65
|
+
_, status_out, _ = run_git(["status", "--porcelain"], cwd=pkg_dir)
|
|
66
|
+
changes = len([l for l in status_out.splitlines() if l.strip()])
|
|
67
|
+
|
|
68
|
+
_, log_out, _ = run_git(["log", "--oneline", "-5"], cwd=pkg_dir)
|
|
69
|
+
commits = []
|
|
70
|
+
for line in log_out.splitlines():
|
|
71
|
+
if line.strip():
|
|
72
|
+
parts = line.split(" ", 1)
|
|
73
|
+
if len(parts) >= 2:
|
|
74
|
+
commits.append({"hash": parts[0], "message": parts[1]})
|
|
75
|
+
elif len(parts) == 1:
|
|
76
|
+
commits.append({"hash": parts[0], "message": ""})
|
|
77
|
+
|
|
78
|
+
result.append({
|
|
79
|
+
"name": pkg_name,
|
|
80
|
+
"path": pkg_path,
|
|
81
|
+
"branch": branch,
|
|
82
|
+
"isClean": changes == 0,
|
|
83
|
+
"uncommittedChanges": changes,
|
|
84
|
+
"recentCommits": commits,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
return result
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _append_package_git_context(lines: list[str], package_git_info: list[dict]) -> None:
|
|
91
|
+
"""Append Git status and recent commits for package repositories."""
|
|
92
|
+
for pkg in package_git_info:
|
|
93
|
+
lines.append(f"## GIT STATUS ({pkg['name']}: {pkg['path']})")
|
|
94
|
+
lines.append(f"Branch: {pkg['branch']}")
|
|
95
|
+
if pkg["isClean"]:
|
|
96
|
+
lines.append("Working directory: Clean")
|
|
97
|
+
else:
|
|
98
|
+
lines.append(
|
|
99
|
+
f"Working directory: {pkg['uncommittedChanges']} uncommitted change(s)"
|
|
100
|
+
)
|
|
101
|
+
lines.append("")
|
|
102
|
+
lines.append(f"## RECENT COMMITS ({pkg['name']}: {pkg['path']})")
|
|
103
|
+
if pkg["recentCommits"]:
|
|
104
|
+
for commit in pkg["recentCommits"]:
|
|
105
|
+
lines.append(f"{commit['hash']} {commit['message']}")
|
|
106
|
+
else:
|
|
107
|
+
lines.append("(no commits)")
|
|
108
|
+
lines.append("")
|
|
109
|
+
|
|
110
|
+
|
|
37
111
|
# =============================================================================
|
|
38
112
|
# JSON Output
|
|
39
113
|
# =============================================================================
|
|
@@ -93,7 +167,10 @@ def get_context_json(repo_root: Path | None = None) -> dict:
|
|
|
93
167
|
for t in iter_active_tasks(tasks_dir)
|
|
94
168
|
]
|
|
95
169
|
|
|
96
|
-
|
|
170
|
+
# Package git repos (independent sub-repositories)
|
|
171
|
+
pkg_git_info = _collect_package_git_info(repo_root)
|
|
172
|
+
|
|
173
|
+
result = {
|
|
97
174
|
"developer": developer or "",
|
|
98
175
|
"git": {
|
|
99
176
|
"branch": branch,
|
|
@@ -112,6 +189,11 @@ def get_context_json(repo_root: Path | None = None) -> dict:
|
|
|
112
189
|
},
|
|
113
190
|
}
|
|
114
191
|
|
|
192
|
+
if pkg_git_info:
|
|
193
|
+
result["packageGit"] = pkg_git_info
|
|
194
|
+
|
|
195
|
+
return result
|
|
196
|
+
|
|
115
197
|
|
|
116
198
|
def output_json(repo_root: Path | None = None) -> None:
|
|
117
199
|
"""Output context in JSON format.
|
|
@@ -189,6 +271,9 @@ def get_context_text(repo_root: Path | None = None) -> str:
|
|
|
189
271
|
lines.append("(no commits)")
|
|
190
272
|
lines.append("")
|
|
191
273
|
|
|
274
|
+
# Package git repos — independent sub-repositories
|
|
275
|
+
_append_package_git_context(lines, _collect_package_git_info(repo_root))
|
|
276
|
+
|
|
192
277
|
# Current task
|
|
193
278
|
lines.append("## CURRENT TASK")
|
|
194
279
|
current_task = get_current_task(repo_root)
|
|
@@ -352,7 +437,10 @@ def get_context_record_json(repo_root: Path | None = None) -> dict:
|
|
|
352
437
|
"status": ct.status,
|
|
353
438
|
}
|
|
354
439
|
|
|
355
|
-
|
|
440
|
+
# Package git repos
|
|
441
|
+
pkg_git_info = _collect_package_git_info(repo_root)
|
|
442
|
+
|
|
443
|
+
result = {
|
|
356
444
|
"developer": developer or "",
|
|
357
445
|
"git": {
|
|
358
446
|
"branch": branch,
|
|
@@ -364,6 +452,11 @@ def get_context_record_json(repo_root: Path | None = None) -> dict:
|
|
|
364
452
|
"currentTask": current_task_info,
|
|
365
453
|
}
|
|
366
454
|
|
|
455
|
+
if pkg_git_info:
|
|
456
|
+
result["packageGit"] = pkg_git_info
|
|
457
|
+
|
|
458
|
+
return result
|
|
459
|
+
|
|
367
460
|
|
|
368
461
|
def get_context_text_record(repo_root: Path | None = None) -> str:
|
|
369
462
|
"""Get context as formatted text for record-session mode.
|
|
@@ -439,6 +532,9 @@ def get_context_text_record(repo_root: Path | None = None) -> str:
|
|
|
439
532
|
lines.append("(no commits)")
|
|
440
533
|
lines.append("")
|
|
441
534
|
|
|
535
|
+
# Package git repos — independent sub-repositories
|
|
536
|
+
_append_package_git_context(lines, _collect_package_git_info(repo_root))
|
|
537
|
+
|
|
442
538
|
# CURRENT TASK
|
|
443
539
|
lines.append("## CURRENT TASK")
|
|
444
540
|
current_task = get_current_task(repo_root)
|
|
@@ -194,8 +194,34 @@ def cmd_init_context(args: argparse.Namespace) -> int:
|
|
|
194
194
|
print()
|
|
195
195
|
print(colored("✓ All context files created", Colors.GREEN))
|
|
196
196
|
print()
|
|
197
|
+
|
|
198
|
+
# Show what was auto-injected
|
|
199
|
+
all_injected = [e["file"] for e in implement_entries]
|
|
200
|
+
print(colored("Auto-injected (defaults only):", Colors.YELLOW))
|
|
201
|
+
for f in all_injected:
|
|
202
|
+
print(f" - {f}")
|
|
203
|
+
print()
|
|
204
|
+
|
|
205
|
+
# Scan spec directory for available spec files the AI should consider
|
|
206
|
+
spec_base = repo_root / DIR_WORKFLOW / DIR_SPEC
|
|
207
|
+
if package:
|
|
208
|
+
spec_base = spec_base / package
|
|
209
|
+
available_specs: list[str] = []
|
|
210
|
+
if spec_base.is_dir():
|
|
211
|
+
for md_file in sorted(spec_base.rglob("*.md")):
|
|
212
|
+
rel = str(md_file.relative_to(repo_root))
|
|
213
|
+
if rel not in all_injected:
|
|
214
|
+
available_specs.append(rel)
|
|
215
|
+
|
|
216
|
+
if available_specs:
|
|
217
|
+
print(colored("Available spec files (not yet injected):", Colors.BLUE))
|
|
218
|
+
for spec in available_specs:
|
|
219
|
+
print(f" - {spec}")
|
|
220
|
+
print()
|
|
221
|
+
|
|
197
222
|
print(colored("Next steps:", Colors.BLUE))
|
|
198
|
-
print(" 1.
|
|
223
|
+
print(" 1. Review the spec files above and add relevant ones for your task:")
|
|
224
|
+
print(f" python3 task.py add-context <dir> implement <spec-path> \"<reason>\"")
|
|
199
225
|
print(" 2. Set as current: python3 task.py start <dir>")
|
|
200
226
|
|
|
201
227
|
return 0
|
|
@@ -163,10 +163,12 @@ def cmd_create(args: argparse.Namespace) -> int:
|
|
|
163
163
|
"worktree_path": None,
|
|
164
164
|
"current_phase": 0,
|
|
165
165
|
"next_action": [
|
|
166
|
-
{"phase": 1, "action": "
|
|
167
|
-
{"phase": 2, "action": "
|
|
168
|
-
{"phase": 3, "action": "
|
|
169
|
-
{"phase": 4, "action": "
|
|
166
|
+
{"phase": 1, "action": "brainstorm"},
|
|
167
|
+
{"phase": 2, "action": "research"},
|
|
168
|
+
{"phase": 3, "action": "implement"},
|
|
169
|
+
{"phase": 4, "action": "check"},
|
|
170
|
+
{"phase": 5, "action": "update-spec"},
|
|
171
|
+
{"phase": 6, "action": "record-session"},
|
|
170
172
|
],
|
|
171
173
|
"commit": None,
|
|
172
174
|
"pr_url": None,
|
|
@@ -37,23 +37,25 @@ def is_safe_task_path(task_path: str, repo_root: Path | None = None) -> bool:
|
|
|
37
37
|
if repo_root is None:
|
|
38
38
|
repo_root = get_repo_root()
|
|
39
39
|
|
|
40
|
+
normalized = task_path.replace("\\", "/")
|
|
41
|
+
|
|
40
42
|
# Check empty or null
|
|
41
|
-
if not
|
|
43
|
+
if not normalized or normalized == "null":
|
|
42
44
|
print("Error: empty or null task path", file=sys.stderr)
|
|
43
45
|
return False
|
|
44
46
|
|
|
45
47
|
# Reject absolute paths
|
|
46
|
-
if task_path.
|
|
48
|
+
if Path(task_path).is_absolute():
|
|
47
49
|
print(f"Error: absolute path not allowed: {task_path}", file=sys.stderr)
|
|
48
50
|
return False
|
|
49
51
|
|
|
50
52
|
# Reject ".", "..", paths starting with "./" or "../", or containing ".."
|
|
51
|
-
if
|
|
53
|
+
if normalized in (".", "..") or normalized.startswith("./") or normalized.startswith("../") or ".." in normalized:
|
|
52
54
|
print(f"Error: path traversal not allowed: {task_path}", file=sys.stderr)
|
|
53
55
|
return False
|
|
54
56
|
|
|
55
57
|
# Final check: ensure resolved path is not the repo root
|
|
56
|
-
abs_path = repo_root /
|
|
58
|
+
abs_path = repo_root / Path(normalized)
|
|
57
59
|
if abs_path.exists():
|
|
58
60
|
try:
|
|
59
61
|
resolved = abs_path.resolve()
|
|
@@ -187,13 +189,17 @@ def resolve_task_dir(target_dir: str, repo_root: Path) -> Path:
|
|
|
187
189
|
if not target_dir:
|
|
188
190
|
return Path()
|
|
189
191
|
|
|
192
|
+
normalized = target_dir.replace("\\", "/")
|
|
193
|
+
while normalized.startswith("./"):
|
|
194
|
+
normalized = normalized[2:]
|
|
195
|
+
|
|
190
196
|
# Absolute path
|
|
191
|
-
if target_dir.
|
|
197
|
+
if Path(target_dir).is_absolute():
|
|
192
198
|
return Path(target_dir)
|
|
193
199
|
|
|
194
200
|
# Relative path (contains path separator or starts with .trellis)
|
|
195
|
-
if "/" in
|
|
196
|
-
return repo_root /
|
|
201
|
+
if "/" in normalized or normalized.startswith(".trellis"):
|
|
202
|
+
return repo_root / Path(normalized)
|
|
197
203
|
|
|
198
204
|
# Task name - try to find in tasks directory
|
|
199
205
|
tasks_dir = get_tasks_dir(repo_root)
|
|
@@ -202,7 +208,7 @@ def resolve_task_dir(target_dir: str, repo_root: Path) -> Path:
|
|
|
202
208
|
return found
|
|
203
209
|
|
|
204
210
|
# Fallback to treating as relative path
|
|
205
|
-
return repo_root /
|
|
211
|
+
return repo_root / Path(normalized)
|
|
206
212
|
|
|
207
213
|
|
|
208
214
|
# =============================================================================
|
|
@@ -59,7 +59,7 @@ def write_prd_header() -> str:
|
|
|
59
59
|
Welcome to Trellis! This is your first task.
|
|
60
60
|
|
|
61
61
|
AI agents use `.trellis/spec/` to understand YOUR project's coding conventions.
|
|
62
|
-
**
|
|
62
|
+
**Starting from scratch = AI writes generic code that doesn't match your project style.**
|
|
63
63
|
|
|
64
64
|
Filling these guidelines is a one-time setup that pays off for every future AI session.
|
|
65
65
|
|
|
@@ -53,7 +53,7 @@ def main() -> int:
|
|
|
53
53
|
parser.add_argument("--requirement", "-r", required=True, help="Requirement description")
|
|
54
54
|
parser.add_argument(
|
|
55
55
|
"--platform", "-p",
|
|
56
|
-
choices=["claude", "cursor", "iflow", "opencode", "qoder"],
|
|
56
|
+
choices=["claude", "cursor", "iflow", "opencode", "codex", "qoder"],
|
|
57
57
|
default=DEFAULT_PLATFORM,
|
|
58
58
|
help="Platform to use (default: claude)"
|
|
59
59
|
)
|
|
@@ -76,11 +76,12 @@ def main() -> int:
|
|
|
76
76
|
project_root = get_repo_root()
|
|
77
77
|
|
|
78
78
|
# Check plan agent exists (path varies by platform)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
if adapter.requires_agent_definition_file:
|
|
80
|
+
plan_md = adapter.get_agent_path("plan", project_root)
|
|
81
|
+
if not plan_md.is_file():
|
|
82
|
+
log_error(f"Agent definition not found at {plan_md}")
|
|
83
|
+
log_info(f"Platform: {platform}")
|
|
84
|
+
return 1
|
|
84
85
|
|
|
85
86
|
ensure_developer(project_root)
|
|
86
87
|
|
|
@@ -187,7 +187,7 @@ def main() -> int:
|
|
|
187
187
|
parser.add_argument("task_dir", help="Task directory path")
|
|
188
188
|
parser.add_argument(
|
|
189
189
|
"--platform", "-p",
|
|
190
|
-
choices=["claude", "cursor", "iflow", "opencode", "qoder"],
|
|
190
|
+
choices=["claude", "cursor", "iflow", "opencode", "codex", "qoder"],
|
|
191
191
|
default=DEFAULT_PLATFORM,
|
|
192
192
|
help="Platform to use (default: claude)"
|
|
193
193
|
)
|
|
@@ -202,12 +202,16 @@ def main() -> int:
|
|
|
202
202
|
project_root = get_repo_root()
|
|
203
203
|
|
|
204
204
|
# Normalize paths
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
task_dir_abs =
|
|
205
|
+
task_dir_path = Path(task_dir_arg)
|
|
206
|
+
if task_dir_path.is_absolute():
|
|
207
|
+
task_dir_abs = task_dir_path
|
|
208
208
|
else:
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
task_dir_abs = project_root / task_dir_path
|
|
210
|
+
|
|
211
|
+
try:
|
|
212
|
+
task_dir_relative = task_dir_abs.relative_to(project_root).as_posix()
|
|
213
|
+
except ValueError:
|
|
214
|
+
task_dir_relative = str(task_dir_abs)
|
|
211
215
|
|
|
212
216
|
task_json_path = task_dir_abs / FILE_TASK_JSON
|
|
213
217
|
|
|
@@ -218,11 +222,12 @@ def main() -> int:
|
|
|
218
222
|
log_error(f"task.json not found at {task_json_path}")
|
|
219
223
|
return 1
|
|
220
224
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
225
|
+
if adapter.requires_agent_definition_file:
|
|
226
|
+
dispatch_md = adapter.get_agent_path("dispatch", project_root)
|
|
227
|
+
if not dispatch_md.is_file():
|
|
228
|
+
log_error(f"Agent definition not found at {dispatch_md}")
|
|
229
|
+
log_info(f"Platform: {platform}")
|
|
230
|
+
return 1
|
|
226
231
|
|
|
227
232
|
config_file = get_worktree_config(project_root)
|
|
228
233
|
if not config_file.is_file():
|
|
@@ -84,7 +84,7 @@ def cmd_start(args: argparse.Namespace) -> int:
|
|
|
84
84
|
|
|
85
85
|
# Convert to relative path for storage
|
|
86
86
|
try:
|
|
87
|
-
task_dir =
|
|
87
|
+
task_dir = full_path.relative_to(repo_root).as_posix()
|
|
88
88
|
except ValueError:
|
|
89
89
|
task_dir = str(full_path)
|
|
90
90
|
|
|
@@ -59,7 +59,7 @@ write_prd_header() {
|
|
|
59
59
|
Welcome to Trellis! This is your first task.
|
|
60
60
|
|
|
61
61
|
AI agents use `.trellis/spec/` to understand YOUR project's coding conventions.
|
|
62
|
-
**
|
|
62
|
+
**Starting from scratch = AI writes generic code that doesn't match your project style.**
|
|
63
63
|
|
|
64
64
|
Filling these guidelines is a one-time setup that pays off for every future AI session.
|
|
65
65
|
|
|
@@ -196,21 +196,30 @@ python3 ./.trellis/scripts/task.py create "<title>" --slug <task-name>
|
|
|
196
196
|
1. Create or select task
|
|
197
197
|
--> python3 ./.trellis/scripts/task.py create "<title>" --slug <name> or list
|
|
198
198
|
|
|
199
|
-
2.
|
|
199
|
+
2. Start task (mark as current)
|
|
200
|
+
--> python3 ./.trellis/scripts/task.py start <name>
|
|
201
|
+
--> Writes .trellis/.current-task; future sessions see it in <current-state>
|
|
202
|
+
|
|
203
|
+
3. Write code according to guidelines
|
|
200
204
|
--> Read .trellis/spec/ docs relevant to your task
|
|
201
205
|
--> For cross-layer: read .trellis/spec/guides/
|
|
202
206
|
|
|
203
|
-
|
|
207
|
+
4. Self-test
|
|
204
208
|
--> Run project's lint/test commands (see spec docs)
|
|
205
209
|
--> Manual feature testing
|
|
206
210
|
|
|
207
|
-
|
|
211
|
+
5. Commit code
|
|
208
212
|
--> git add <files>
|
|
209
213
|
--> git commit -m "type(scope): description"
|
|
210
214
|
Format: feat/fix/docs/refactor/test/chore
|
|
211
215
|
|
|
212
|
-
|
|
216
|
+
6. Record session (one command)
|
|
213
217
|
--> python3 ./.trellis/scripts/add_session.py --title "Title" --commit "hash"
|
|
218
|
+
|
|
219
|
+
7. Finish task (clear current)
|
|
220
|
+
--> python3 ./.trellis/scripts/task.py finish
|
|
221
|
+
--> Only when the task is fully done; otherwise leave it set so the
|
|
222
|
+
next session resumes where you left off
|
|
214
223
|
```
|
|
215
224
|
|
|
216
225
|
### Code Quality Checklist
|
|
@@ -315,11 +324,15 @@ tasks/
|
|
|
315
324
|
**Commands**:
|
|
316
325
|
```bash
|
|
317
326
|
python3 ./.trellis/scripts/task.py create "<title>" [--slug <name>] # Create task directory
|
|
327
|
+
python3 ./.trellis/scripts/task.py start <name> # Set as current task (writes .current-task, triggers after_start hooks)
|
|
328
|
+
python3 ./.trellis/scripts/task.py finish # Clear current task (triggers after_finish hooks)
|
|
318
329
|
python3 ./.trellis/scripts/task.py archive <name> # Archive to archive/{year-month}/
|
|
319
330
|
python3 ./.trellis/scripts/task.py list # List active tasks
|
|
320
331
|
python3 ./.trellis/scripts/task.py list-archive # List archived tasks
|
|
321
332
|
```
|
|
322
333
|
|
|
334
|
+
**Current task mechanism**: `task.py start <name>` writes the selected task path to `.trellis/.current-task`. The SessionStart hook reads this file to inject `## CURRENT TASK` into every new session's context, so the AI immediately knows what you're working on without being told. Run `task.py finish` when you're done — subsequent sessions will show `(none)` until you start another task.
|
|
335
|
+
|
|
323
336
|
---
|
|
324
337
|
|
|
325
338
|
## Best Practices
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windsurf workflow templates
|
|
3
|
+
*
|
|
4
|
+
* These are GENERIC templates for user projects.
|
|
5
|
+
* Do NOT use Trellis project's own .windsurf/ directory (which may be customized).
|
|
6
|
+
*
|
|
7
|
+
* Directory structure:
|
|
8
|
+
* windsurf/
|
|
9
|
+
* └── workflows/ # Workflow files
|
|
10
|
+
*/
|
|
11
|
+
export interface WorkflowTemplate {
|
|
12
|
+
name: string;
|
|
13
|
+
content: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get all workflow templates.
|
|
17
|
+
* Workflow names match their filename stem
|
|
18
|
+
* (e.g. trellis-start.md -> /trellis-start).
|
|
19
|
+
*/
|
|
20
|
+
export declare function getAllWorkflows(): WorkflowTemplate[];
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/windsurf/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAqBH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,gBAAgB,EAAE,CAcpD"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windsurf workflow templates
|
|
3
|
+
*
|
|
4
|
+
* These are GENERIC templates for user projects.
|
|
5
|
+
* Do NOT use Trellis project's own .windsurf/ directory (which may be customized).
|
|
6
|
+
*
|
|
7
|
+
* Directory structure:
|
|
8
|
+
* windsurf/
|
|
9
|
+
* └── workflows/ # Workflow files
|
|
10
|
+
*/
|
|
11
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = dirname(__filename);
|
|
16
|
+
function readTemplate(relativePath) {
|
|
17
|
+
return readFileSync(join(__dirname, relativePath), "utf-8");
|
|
18
|
+
}
|
|
19
|
+
function listFiles(dir) {
|
|
20
|
+
try {
|
|
21
|
+
return readdirSync(join(__dirname, dir));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get all workflow templates.
|
|
29
|
+
* Workflow names match their filename stem
|
|
30
|
+
* (e.g. trellis-start.md -> /trellis-start).
|
|
31
|
+
*/
|
|
32
|
+
export function getAllWorkflows() {
|
|
33
|
+
const workflows = [];
|
|
34
|
+
for (const file of listFiles("workflows")) {
|
|
35
|
+
if (!file.endsWith(".md")) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const name = file.replace(".md", "");
|
|
39
|
+
const content = readTemplate(`workflows/${file}`);
|
|
40
|
+
workflows.push({ name, content });
|
|
41
|
+
}
|
|
42
|
+
return workflows;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/windsurf/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;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;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAOD;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,SAAS,GAAuB,EAAE,CAAC;IAEzC,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,SAAS;QACX,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACrC,MAAM,OAAO,GAAG,YAAY,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC;QAClD,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
|