@pennixrv/trellis 0.7.1-beta.4 → 0.7.1-beta.6
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/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +14 -6
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/task-progress.d.ts +1 -0
- package/dist/commands/task-progress.d.ts.map +1 -1
- package/dist/commands/task-progress.js +67 -25
- package/dist/commands/task-progress.js.map +1 -1
- package/dist/commands/update.js +2 -2
- package/dist/commands/update.js.map +1 -1
- package/dist/constants/paths.d.ts +3 -10
- package/dist/constants/paths.d.ts.map +1 -1
- package/dist/constants/paths.js +4 -13
- package/dist/constants/paths.js.map +1 -1
- package/dist/migrations/manifests/0.7.1-beta.5.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.6.json +10 -0
- package/dist/templates/claude/hooks/statusline.py +9 -4
- package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/add-project-local-conventions.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-context-loading.md +5 -5
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +3 -2
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/context-injection.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +8 -2
- package/dist/templates/common/bundled-skills/trellis-teamwork/references/teammate-processes.md +2 -2
- package/dist/templates/common/bundled-skills/trellis-teamwork/references/teamwork-plan-construction.md +1 -1
- package/dist/templates/omp/extensions/trellis/index.ts.txt +1 -1
- package/dist/templates/opencode/lib/session-utils.js +301 -209
- package/dist/templates/snow/SNOW.md +1 -1
- package/dist/templates/trellis/agents/teammate.md +5 -5
- package/dist/templates/trellis/index.d.ts +3 -0
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +6 -0
- package/dist/templates/trellis/index.js.map +1 -1
- package/dist/templates/trellis/scripts/add_session.py +1 -1
- package/dist/templates/trellis/scripts/common/active_task.py +3 -47
- package/dist/templates/trellis/scripts/common/paths.py +9 -25
- package/dist/templates/trellis/scripts/common/safe_commit.py +8 -8
- package/dist/templates/trellis/scripts/common/task_context.py +11 -51
- package/dist/templates/trellis/scripts/common/task_continuity.py +53 -45
- package/dist/templates/trellis/scripts/common/task_layout.py +80 -0
- package/dist/templates/trellis/scripts/common/task_move.py +345 -0
- package/dist/templates/trellis/scripts/common/task_store.py +28 -55
- package/dist/templates/trellis/scripts/common/task_utils.py +23 -19
- package/dist/templates/trellis/scripts/common/task_workflow.py +28 -20
- package/dist/templates/trellis/scripts/common/tasks.py +6 -5
- package/dist/templates/trellis/scripts/hooks/linear_sync.py +13 -3
- package/dist/templates/trellis/scripts/task.py +41 -23
- package/dist/templates/trellis/workflow.md +4 -2
- package/package.json +2 -2
|
@@ -19,6 +19,7 @@ from pathlib import Path
|
|
|
19
19
|
from .io import describe_json_read_failure, read_json_checked
|
|
20
20
|
from .paths import FILE_TASK_JSON
|
|
21
21
|
from .types import TaskInfo
|
|
22
|
+
from .task_layout import iter_task_dirs, task_status_dir
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
def load_task(task_dir: Path) -> TaskInfo | None:
|
|
@@ -72,14 +73,14 @@ def iter_active_tasks(tasks_dir: Path) -> Iterator[TaskInfo]:
|
|
|
72
73
|
Yields:
|
|
73
74
|
TaskInfo for each valid task.
|
|
74
75
|
"""
|
|
75
|
-
|
|
76
|
-
return
|
|
76
|
+
from .task_move import require_settled_layout
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
continue
|
|
78
|
+
require_settled_layout(tasks_dir.parent.parent)
|
|
79
|
+
for d in iter_task_dirs(tasks_dir):
|
|
81
80
|
info = load_task(d)
|
|
82
81
|
if info is not None:
|
|
82
|
+
if info.status != task_status_dir(d, tasks_dir):
|
|
83
|
+
print(f"[WARN] Task status/directory mismatch: {d}", file=sys.stderr)
|
|
83
84
|
yield info
|
|
84
85
|
|
|
85
86
|
|
|
@@ -9,7 +9,7 @@ Usage (called automatically by task.py hooks):
|
|
|
9
9
|
python3 .trellis/scripts/hooks/linear_sync.py complete
|
|
10
10
|
|
|
11
11
|
Manual usage:
|
|
12
|
-
TASK_JSON_PATH=.trellis/tasks/<
|
|
12
|
+
TASK_JSON_PATH=.trellis/tasks/<task-dir>/task.json python3 .trellis/scripts/hooks/linear_sync.py sync
|
|
13
13
|
|
|
14
14
|
Environment:
|
|
15
15
|
TASK_JSON_PATH - Absolute path to task.json (set by task.py)
|
|
@@ -211,8 +211,18 @@ def _resolve_parent_linear_issue(task: dict) -> str | None:
|
|
|
211
211
|
return None
|
|
212
212
|
|
|
213
213
|
current_task_dir = Path(task_json_path).parent
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
scripts_dir = Path(__file__).resolve().parents[1]
|
|
215
|
+
if str(scripts_dir) not in sys.path:
|
|
216
|
+
sys.path.insert(0, str(scripts_dir))
|
|
217
|
+
from common.paths import get_tasks_dir
|
|
218
|
+
from common.task_utils import find_task_by_name
|
|
219
|
+
workflow_dir = next((parent for parent in current_task_dir.parents if parent.name == ".trellis"), None)
|
|
220
|
+
if workflow_dir is None:
|
|
221
|
+
return None
|
|
222
|
+
parent_dir = find_task_by_name(parent_name, get_tasks_dir(workflow_dir.parent))
|
|
223
|
+
if parent_dir is None:
|
|
224
|
+
return None
|
|
225
|
+
parent_json = parent_dir / "task.json"
|
|
216
226
|
|
|
217
227
|
if parent_json.exists():
|
|
218
228
|
try:
|
|
@@ -101,7 +101,7 @@ def _record_start_state(
|
|
|
101
101
|
task_json_path: Path,
|
|
102
102
|
repo_root: Path,
|
|
103
103
|
label: str = "",
|
|
104
|
-
) ->
|
|
104
|
+
) -> Path:
|
|
105
105
|
"""Enter implementation and record its branch after current authorization.
|
|
106
106
|
|
|
107
107
|
Planning and analysis tasks keep their lifecycle status while the active
|
|
@@ -121,7 +121,7 @@ def _record_start_state(
|
|
|
121
121
|
file=sys.stderr,
|
|
122
122
|
)
|
|
123
123
|
print(hint, file=sys.stderr)
|
|
124
|
-
return
|
|
124
|
+
return task_json_path
|
|
125
125
|
|
|
126
126
|
applied: list[str] = []
|
|
127
127
|
|
|
@@ -132,7 +132,7 @@ def _record_start_state(
|
|
|
132
132
|
colored(f"Warning: Implementation start withheld; task.json not updated: {error.code}: {error}", Colors.YELLOW),
|
|
133
133
|
file=sys.stderr,
|
|
134
134
|
)
|
|
135
|
-
return
|
|
135
|
+
return task_json_path
|
|
136
136
|
if data.get("status") == "planning" and implementation_ready:
|
|
137
137
|
data["status"] = "in_progress"
|
|
138
138
|
applied.append(f"✓ Status: planning → in_progress{label}")
|
|
@@ -158,15 +158,18 @@ def _record_start_state(
|
|
|
158
158
|
)
|
|
159
159
|
|
|
160
160
|
if not applied:
|
|
161
|
-
return
|
|
161
|
+
return task_json_path
|
|
162
162
|
|
|
163
163
|
try:
|
|
164
164
|
assert_task_mutation_allowed(repo_root, task_json_path.parent, data)
|
|
165
165
|
except TaskWorkflowError as error:
|
|
166
166
|
print(colored(f"{error.code}: task metadata update withheld: {error}", Colors.YELLOW), file=sys.stderr)
|
|
167
|
-
return
|
|
167
|
+
return task_json_path
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
from common.task_move import relocate_task
|
|
170
|
+
relocated = relocate_task(repo_root, task_json_path.parent, data)
|
|
171
|
+
task_json_path = relocated / FILE_TASK_JSON
|
|
172
|
+
if not task_json_path.is_file():
|
|
170
173
|
print(
|
|
171
174
|
colored(
|
|
172
175
|
f"Warning: Failed to write {task_json_path}; "
|
|
@@ -175,7 +178,7 @@ def _record_start_state(
|
|
|
175
178
|
),
|
|
176
179
|
file=sys.stderr,
|
|
177
180
|
)
|
|
178
|
-
return
|
|
181
|
+
return task_json_path
|
|
179
182
|
|
|
180
183
|
for line in applied:
|
|
181
184
|
print(colored(line, Colors.GREEN))
|
|
@@ -190,12 +193,15 @@ def _record_start_state(
|
|
|
190
193
|
),
|
|
191
194
|
file=sys.stderr,
|
|
192
195
|
)
|
|
196
|
+
|
|
193
197
|
print(
|
|
194
198
|
f"Once you branch off, run: python3 {DIR_WORKFLOW}/scripts/task.py "
|
|
195
199
|
"set-branch <task> <feature-branch>",
|
|
196
200
|
file=sys.stderr,
|
|
197
201
|
)
|
|
198
202
|
|
|
203
|
+
return task_json_path
|
|
204
|
+
|
|
199
205
|
|
|
200
206
|
def cmd_start(args: argparse.Namespace) -> int:
|
|
201
207
|
"""Set active task."""
|
|
@@ -256,18 +262,17 @@ def cmd_start(args: argparse.Namespace) -> int:
|
|
|
256
262
|
|
|
257
263
|
# Enter implementation only when the accepted phase contract authorizes it.
|
|
258
264
|
if task_json_path.is_file():
|
|
259
|
-
_record_start_state(task_json_path, repo_root, " (degraded)")
|
|
265
|
+
task_json_path = _record_start_state(task_json_path, repo_root, " (degraded)")
|
|
260
266
|
run_task_hooks("after_start", task_json_path, repo_root)
|
|
261
267
|
return 0
|
|
262
268
|
|
|
263
269
|
active = set_active_task(task_dir, repo_root)
|
|
264
270
|
if active:
|
|
265
|
-
print(colored(f"✓ Current task set to: {task_dir}", Colors.GREEN))
|
|
266
|
-
print(f"Source: {active.source}")
|
|
267
|
-
|
|
268
271
|
if task_json_path.is_file():
|
|
269
|
-
_record_start_state(task_json_path, repo_root)
|
|
272
|
+
task_json_path = _record_start_state(task_json_path, repo_root)
|
|
270
273
|
|
|
274
|
+
print(colored(f"✓ Current task set to: {task_json_path.parent.relative_to(repo_root).as_posix()}", Colors.GREEN))
|
|
275
|
+
print(f"Source: {active.source}")
|
|
271
276
|
print()
|
|
272
277
|
print(colored("The hook will now inject context from this task's jsonl files.", Colors.BLUE))
|
|
273
278
|
|
|
@@ -499,7 +504,7 @@ def cmd_list(args: argparse.Namespace) -> int:
|
|
|
499
504
|
if filter_status and t.status != filter_status:
|
|
500
505
|
continue
|
|
501
506
|
items.append({
|
|
502
|
-
"dir":
|
|
507
|
+
"dir": t.directory.relative_to(repo_root).as_posix(),
|
|
503
508
|
"id": t.raw.get("id") or dir_name,
|
|
504
509
|
"title": t.title,
|
|
505
510
|
"status": t.status,
|
|
@@ -538,7 +543,7 @@ def cmd_list(args: argparse.Namespace) -> int:
|
|
|
538
543
|
if filter_status and t.status != filter_status:
|
|
539
544
|
return
|
|
540
545
|
|
|
541
|
-
relative_path =
|
|
546
|
+
relative_path = t.directory.relative_to(repo_root).as_posix()
|
|
542
547
|
marker = ""
|
|
543
548
|
if relative_path == current_task:
|
|
544
549
|
marker = f" {colored('<- current', Colors.GREEN)}"
|
|
@@ -622,7 +627,7 @@ Rename options:
|
|
|
622
627
|
|
|
623
628
|
List options:
|
|
624
629
|
--mine, -m Show only tasks assigned to current developer
|
|
625
|
-
--status, -s <s> Filter by status (planning, in_progress,
|
|
630
|
+
--status, -s <s> Filter by status (planning, in_progress, completed, cancelled)
|
|
626
631
|
--json Output machine-readable JSON (also available on `current`)
|
|
627
632
|
|
|
628
633
|
Examples:
|
|
@@ -849,16 +854,18 @@ def main() -> int:
|
|
|
849
854
|
p_complete = subparsers.add_parser("complete", help="Complete after Delivery Review and On-Site Closeout Phase acceptance")
|
|
850
855
|
p_complete.add_argument("task_dir", help="Task directory or task name")
|
|
851
856
|
|
|
857
|
+
p_migrate = subparsers.add_parser("migrate-layout", help="Preview or explicitly apply task status directory migration")
|
|
858
|
+
migration_mode = p_migrate.add_mutually_exclusive_group()
|
|
859
|
+
migration_mode.add_argument("--apply", action="store_true", help="Recheck and apply the migration")
|
|
860
|
+
migration_mode.add_argument("--recover", action="store_true", help="Roll back an interrupted relocation")
|
|
861
|
+
|
|
852
862
|
args = parser.parse_args()
|
|
853
863
|
|
|
854
864
|
if not args.command:
|
|
855
865
|
show_usage()
|
|
856
866
|
return 1
|
|
857
867
|
|
|
858
|
-
|
|
859
|
-
return continuity_show(args) if args.continuity_action == "show" else continuity_capture(args)
|
|
860
|
-
if args.command == "transfer":
|
|
861
|
-
return transfer_task_ownership(args)
|
|
868
|
+
from common.task_move import cmd_migrate_layout, layout_lock, require_settled_layout
|
|
862
869
|
|
|
863
870
|
commands = {
|
|
864
871
|
"create": cmd_create,
|
|
@@ -881,12 +888,23 @@ def main() -> int:
|
|
|
881
888
|
"add-subtask": cmd_add_subtask,
|
|
882
889
|
"remove-subtask": cmd_remove_subtask,
|
|
883
890
|
"list": cmd_list,
|
|
891
|
+
"migrate-layout": cmd_migrate_layout,
|
|
884
892
|
}
|
|
885
893
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
894
|
+
try:
|
|
895
|
+
with layout_lock(get_repo_root()):
|
|
896
|
+
if args.command != "migrate-layout":
|
|
897
|
+
require_settled_layout(get_repo_root())
|
|
898
|
+
if args.command == "continuity":
|
|
899
|
+
return continuity_show(args) if args.continuity_action == "show" else continuity_capture(args)
|
|
900
|
+
if args.command == "transfer":
|
|
901
|
+
return transfer_task_ownership(args)
|
|
902
|
+
if args.command in commands:
|
|
903
|
+
return commands[args.command](args)
|
|
904
|
+
show_usage()
|
|
905
|
+
return 1
|
|
906
|
+
except (ValueError, OSError) as error:
|
|
907
|
+
print(f"TASK_LAYOUT_WITHHELD: {error}", file=sys.stderr)
|
|
890
908
|
return 1
|
|
891
909
|
|
|
892
910
|
|
|
@@ -20,7 +20,7 @@ python3 ./.trellis/scripts/get_context.py --mode spec --file <path>
|
|
|
20
20
|
|
|
21
21
|
### 任务记录
|
|
22
22
|
|
|
23
|
-
Trellis task
|
|
23
|
+
Trellis task 按 `task.json.status` 存放在 `.trellis/tasks/planning/<task>`、`in_progress/<task>`、`completed/YYYY-MM/<task>` 或 `cancelled/<task>`。创建进入 `planning`,获准实施后启动进入 `in_progress`;目录仅是状态的投影。每个任务包含 `task.json`、需求以及任务所需的设计、实施、研究或上下文资产。使用 Trellis CLI 创建和启动任务、初始化和推进阶段路线,以及完成或取消任务。不得手工修改任务状态或阶段状态。
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
python3 ./.trellis/scripts/task.py create "<title>" [--slug <name>] [--parent <task>]
|
|
@@ -34,7 +34,9 @@ python3 ./.trellis/scripts/task.py cancel <task> --reason "<reason>" --evidence-
|
|
|
34
34
|
|
|
35
35
|
使用 `phase artifact`、`accept`、`skip`、`wait`、`block`、`resume`、`return`、`route` 和 `authorize` 执行对应的阶段操作。准确参数以 `task.py --help` 为准。跳过阶段必须提供理由和证据;目标探索与需求核对阶段不可省略。
|
|
36
36
|
|
|
37
|
-
只有阶段路线已到达获准的实施入口时,`task.py start` 才会将任务从 `planning` 改为 `in_progress`。分析任务可在交付审查与现场收尾阶段后仍处于 `planning` 时完成。`complete`
|
|
37
|
+
只有阶段路线已到达获准的实施入口时,`task.py start` 才会将任务从 `planning` 改为 `in_progress`。分析任务可在交付审查与现场收尾阶段后仍处于 `planning` 时完成。`complete` 将任务移入 `completed/YYYY-MM/<task>`,`cancel` 将任务移入 `cancelled/<task>`,并在同一事务中清除任务的运行时会话指针。
|
|
38
|
+
|
|
39
|
+
旧布局须先预览 `task.py migrate-layout`,核对后执行 `task.py migrate-layout --apply`;旧 `archive/YYYY-MM` 中已完成的任务保留原月份,其他任务按真实状态放置。迁移会同步 JSON/JSONL 机器引用、会话指针和运行态索引,保留任务身份与父子关系,不重授历史授权。未完成事务会阻止普通任务命令,须用 `task.py migrate-layout --recover` 恢复;输入被外部改动时停止并保留现场。路径或产物内容改变导致接受证据失效时,须经原生阶段接受或 continuity 核对重新收敛。
|
|
38
40
|
|
|
39
41
|
在支持原生实施或检查 agent 的平台上,任务上下文清单(`implement.jsonl`、`check.jsonl`)用于提供相关规范和研究资料。只有这些 agent 会读取清单时才需要维护;清单不能替代任务资产或阶段证据。
|
|
40
42
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pennixrv/trellis",
|
|
3
|
-
"version": "0.7.1-beta.
|
|
3
|
+
"version": "0.7.1-beta.6",
|
|
4
4
|
"description": "AI capabilities grow like ivy — Trellis provides the structure to guide them along a disciplined path",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"inquirer": "^9.3.7",
|
|
35
35
|
"undici": "^6.21.0",
|
|
36
36
|
"zod": "^4.4.2",
|
|
37
|
-
"@pennixrv/trellis-core": "0.7.1-beta.
|
|
37
|
+
"@pennixrv/trellis-core": "0.7.1-beta.6"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@eslint/js": "^9.18.0",
|