@blxzer/cursor-trellis 0.2.9 → 0.3.2
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/CHANGELOG.md +94 -0
- package/README.md +85 -19
- package/README.zh-CN.md +60 -21
- package/bin/smart-search.js +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +46 -18
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/capability-smoke.d.ts +24 -0
- package/dist/commands/capability-smoke.d.ts.map +1 -0
- package/dist/commands/capability-smoke.js +83 -0
- package/dist/commands/capability-smoke.js.map +1 -0
- package/dist/commands/channel/agent-loader.d.ts +1 -1
- package/dist/commands/channel/agent-loader.js +4 -4
- package/dist/commands/channel/agent-loader.js.map +1 -1
- package/dist/commands/channel/guard.d.ts +2 -2
- package/dist/commands/channel/guard.js +3 -3
- package/dist/commands/channel/guard.js.map +1 -1
- package/dist/commands/channel/index.js +3 -3
- package/dist/commands/channel/index.js.map +1 -1
- package/dist/commands/channel/list.d.ts +1 -1
- package/dist/commands/channel/list.js +1 -1
- package/dist/commands/channel/rm.d.ts +1 -1
- package/dist/commands/channel/rm.js +1 -1
- package/dist/commands/channel/store/lock.d.ts +1 -1
- package/dist/commands/channel/store/lock.js +1 -1
- package/dist/commands/channel/store/paths.js +1 -1
- package/dist/commands/channel/store/paths.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +197 -91
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/uninstall.d.ts +4 -4
- package/dist/commands/uninstall.js +12 -12
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.d.ts +7 -2
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +161 -61
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/upgrade.js +2 -2
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/commands/workflow.d.ts +4 -4
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +10 -9
- package/dist/commands/workflow.js.map +1 -1
- package/dist/configurators/cursor.d.ts +2 -2
- package/dist/configurators/cursor.js +4 -4
- package/dist/configurators/cursor.js.map +1 -1
- package/dist/configurators/index.d.ts.map +1 -1
- package/dist/configurators/index.js +4 -4
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/shared.d.ts +10 -10
- package/dist/configurators/shared.d.ts.map +1 -1
- package/dist/configurators/shared.js +32 -32
- package/dist/configurators/shared.js.map +1 -1
- package/dist/configurators/workflow.d.ts +6 -6
- package/dist/configurators/workflow.js +1 -1
- package/dist/constants/paths.d.ts +24 -24
- package/dist/constants/paths.d.ts.map +1 -1
- package/dist/constants/paths.js +16 -16
- package/dist/constants/paths.js.map +1 -1
- package/dist/migrations/manifests/0.2.10.json +374 -0
- package/dist/migrations/manifests/0.3.0.json +89 -0
- package/dist/migrations/manifests/0.3.1.json +32 -0
- package/dist/templates/common/bundled-skills/{trellis-cursor2plus-setup → cstl-cursor2plus-setup}/SKILL.md +15 -14
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/SKILL.md +75 -75
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/add-project-local-conventions.md +19 -19
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/change-agents.md +8 -8
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/change-context-loading.md +26 -26
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/change-hooks.md +9 -9
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/change-skills-or-commands.md +8 -8
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/change-spec-structure.md +17 -17
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/change-task-lifecycle.md +46 -46
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/change-workflow.md +18 -18
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/customize-local/overview.md +55 -55
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/local-architecture/context-injection.md +20 -20
- package/dist/templates/common/bundled-skills/cstl-meta/references/local-architecture/generated-files.md +83 -0
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/local-architecture/overview.md +15 -15
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/local-architecture/spec-system.md +12 -12
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/local-architecture/task-system.md +34 -34
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/local-architecture/workflow.md +8 -8
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/local-architecture/workspace-memory.md +19 -19
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/platform-files/agents.md +12 -12
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/platform-files/hooks-and-settings.md +7 -7
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/platform-files/overview.md +9 -9
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/platform-files/platform-map.md +7 -7
- package/dist/templates/common/bundled-skills/{trellis-meta → cstl-meta}/references/platform-files/skills-and-commands.md +19 -19
- package/dist/templates/common/bundled-skills/{trellis-micro-grill → cstl-micro-grill}/SKILL.md +62 -47
- package/dist/templates/common/bundled-skills/{trellis-skill-creator → cstl-skill-creator}/SKILL.md +44 -44
- package/dist/templates/common/bundled-skills/{trellis-skill-creator → cstl-skill-creator}/references/authoring-rules.md +97 -97
- package/dist/templates/common/bundled-skills/{trellis-skill-creator/references/trellis-skill-locations.md → cstl-skill-creator/references/cstl-skill-locations.md} +52 -52
- package/dist/templates/common/bundled-skills/{trellis-skill-creator → cstl-skill-creator}/references/review-checklist.md +47 -47
- package/dist/templates/common/bundled-skills/{trellis-spec-bootstrap → cstl-spec-bootstrap}/SKILL.md +6 -6
- package/dist/templates/common/bundled-skills/{trellis-spec-bootstrap → cstl-spec-bootstrap}/references/repository-analysis.md +1 -1
- package/dist/templates/common/bundled-skills/{trellis-spec-bootstrap → cstl-spec-bootstrap}/references/spec-task-planning.md +3 -3
- package/dist/templates/common/bundled-skills/{trellis-spec-bootstrap → cstl-spec-bootstrap}/references/spec-writing.md +1 -1
- package/dist/templates/common/commands/continue.md +24 -24
- package/dist/templates/common/commands/finish-work.md +5 -5
- package/dist/templates/common/commands/start.md +20 -20
- package/dist/templates/common/index.d.ts +1 -1
- package/dist/templates/common/index.d.ts.map +1 -1
- package/dist/templates/common/skills/before-dev.md +4 -4
- package/dist/templates/common/skills/brainstorm.md +188 -164
- package/dist/templates/common/skills/break-loop.md +3 -3
- package/dist/templates/common/skills/check.md +5 -5
- package/dist/templates/common/skills/update-spec.md +4 -4
- package/dist/templates/cursor/agents/{trellis-check.md → cstl-check.md} +14 -14
- package/dist/templates/cursor/agents/{trellis-implement.md → cstl-implement.md} +14 -14
- package/dist/templates/cursor/agents/{trellis-research.md → cstl-research.md} +11 -11
- package/dist/templates/cursor/commands/cursor2plus-setup.md +15 -15
- package/dist/templates/cursor/fixtures/expected-rules.d.ts +1 -1
- package/dist/templates/cursor/fixtures/expected-rules.d.ts.map +1 -1
- package/dist/templates/cursor/fixtures/expected-rules.js +2 -2
- package/dist/templates/cursor/fixtures/expected-rules.js.map +1 -1
- package/dist/templates/cursor/rules/{trellis-subagent-dispatch.mdc → cstl-subagent-dispatch.mdc} +4 -4
- package/dist/templates/cursor/rules/{trellis-triage.mdc → cstl-triage.mdc} +3 -3
- package/dist/templates/cursor/rules/retrieval-routing.mdc +4 -4
- package/dist/templates/extract.d.ts +1 -1
- package/dist/templates/extract.js +1 -1
- package/dist/templates/markdown/agents.md +13 -13
- package/dist/templates/markdown/prompts/run-semantic-slice-12.md.txt +1 -1
- package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +634 -634
- package/dist/templates/markdown/spec/guides/cursor-context-injection-guide.md.txt +5 -5
- package/dist/templates/markdown/spec/guides/cursor-semantic-compliance.md.txt +7 -1
- package/dist/templates/markdown/spec/guides/cursor-subagent-policy.md.txt +393 -391
- package/dist/templates/markdown/spec/guides/durable-learning-decision-guide.md.txt +14 -14
- package/dist/templates/markdown/spec/guides/execution-strategy.md.txt +5 -5
- package/dist/templates/markdown/spec/guides/index.md.txt +3 -2
- package/dist/templates/markdown/spec/guides/retrieval-daily-guide.md.txt +10 -10
- package/dist/templates/markdown/workspace-index.md +3 -3
- package/dist/templates/markdown/worktree.yaml.txt +1 -1
- package/dist/templates/shared-hooks/index.d.ts +2 -2
- package/dist/templates/shared-hooks/index.js +2 -2
- package/dist/templates/shared-hooks/inject-retrieval-plan.py +3 -3
- package/dist/templates/shared-hooks/inject-shell-session-context.py +7 -7
- package/dist/templates/shared-hooks/inject-subagent-context.py +4 -4
- package/dist/templates/shared-hooks/inject-workflow-state.py +37 -37
- package/dist/templates/shared-hooks/research-end-retrieval-pack.py +2 -2
- package/dist/templates/shared-hooks/session-start.py +16 -16
- package/dist/templates/trellis/config/execution-strategy-rules.json +1 -1
- package/dist/templates/trellis/config.yaml +5 -25
- package/dist/templates/trellis/index.d.ts +2 -2
- package/dist/templates/trellis/index.js +2 -2
- package/dist/templates/trellis/local/README.md +15 -13
- package/dist/templates/trellis/local/patch_wpelc8.py +7 -7
- package/dist/templates/trellis/local/subagent-models.json.example +1 -1
- package/dist/templates/trellis/local/trellis-task-models.json.example +3 -3
- package/dist/templates/trellis/local/trellis-task-models.json5.example +5 -5
- package/dist/templates/trellis/local/trellis_task_models_config.py +6 -6
- package/dist/templates/trellis/scripts/add_session.py +20 -20
- package/dist/templates/trellis/scripts/common/active_task.py +2 -2
- package/dist/templates/trellis/scripts/common/artifact_search.py +587 -587
- package/dist/templates/trellis/scripts/common/cli_adapter.py +3 -3
- package/dist/templates/trellis/scripts/common/codebase_retrieval_router.py +6 -6
- package/dist/templates/trellis/scripts/common/config.py +3 -3
- package/dist/templates/trellis/scripts/common/developer.py +2 -2
- package/dist/templates/trellis/scripts/common/execution_strategy.py +1 -1
- package/dist/templates/trellis/scripts/common/packages_context.py +23 -23
- package/dist/templates/trellis/scripts/common/parent_orchestration.py +702 -702
- package/dist/templates/trellis/scripts/common/paths.py +107 -107
- package/dist/templates/trellis/scripts/common/project_file_stats.py +1 -1
- package/dist/templates/trellis/scripts/common/retrieval_agent_instructions.py +1 -1
- package/dist/templates/trellis/scripts/common/safe_commit.py +31 -31
- package/dist/templates/trellis/scripts/common/session_context.py +1159 -1159
- package/dist/templates/trellis/scripts/common/session_memory.py +360 -360
- package/dist/templates/trellis/scripts/common/smart_search_evidence.py +514 -514
- package/dist/templates/trellis/scripts/common/smart_search_resolve.py +1 -1
- package/dist/templates/trellis/scripts/common/subagent_dispatch.py +7 -7
- package/dist/templates/trellis/scripts/common/task_context.py +1 -1
- package/dist/templates/trellis/scripts/common/task_dashboard.py +140 -140
- package/dist/templates/trellis/scripts/common/task_gates.py +2056 -2034
- package/dist/templates/trellis/scripts/common/task_store.py +1594 -1594
- package/dist/templates/trellis/scripts/common/task_utils.py +3 -3
- package/dist/templates/trellis/scripts/common/tasks.py +1 -1
- package/dist/templates/trellis/scripts/common/trellis_config.py +3 -3
- package/dist/templates/trellis/scripts/common/workflow_phase.py +1 -1
- package/dist/templates/trellis/scripts/cursor_retrieval_probe.py +3 -3
- package/dist/templates/trellis/scripts/cursor_retrieval_probe_prompt.md +11 -11
- package/dist/templates/trellis/scripts/hooks/linear_sync.py +8 -8
- package/dist/templates/trellis/scripts/init_developer.py +2 -2
- package/dist/templates/trellis/scripts/retrieval_probe_matrix_template.json +4 -4
- package/dist/templates/trellis/scripts/route_codebase_retrieval.py +1 -1
- package/dist/templates/trellis/scripts/task.py +918 -918
- package/dist/templates/trellis/tasks/templates/release-readiness/implement.md +2 -2
- package/dist/templates/trellis/workflow.md +805 -805
- package/dist/types/ai-tools.d.ts +1 -1
- package/dist/types/ai-tools.d.ts.map +1 -1
- package/dist/types/ai-tools.js +1 -1
- package/dist/types/ai-tools.js.map +1 -1
- package/dist/types/migration.d.ts +1 -1
- package/dist/types/migration.d.ts.map +1 -1
- package/dist/utils/agents-md.d.ts +37 -0
- package/dist/utils/agents-md.d.ts.map +1 -0
- package/dist/utils/agents-md.js +82 -0
- package/dist/utils/agents-md.js.map +1 -0
- package/dist/utils/codebase-retrieval-router.js +5 -5
- package/dist/utils/codebase-retrieval-router.js.map +1 -1
- package/dist/utils/manifest-prune.d.ts +2 -2
- package/dist/utils/manifest-prune.js +4 -4
- package/dist/utils/manifest-prune.js.map +1 -1
- package/dist/utils/mirror-check.d.ts +3 -1
- package/dist/utils/mirror-check.d.ts.map +1 -1
- package/dist/utils/mirror-check.js +22 -11
- package/dist/utils/mirror-check.js.map +1 -1
- package/dist/utils/project-capabilities.d.ts +14 -3
- package/dist/utils/project-capabilities.d.ts.map +1 -1
- package/dist/utils/project-capabilities.js +127 -17
- package/dist/utils/project-capabilities.js.map +1 -1
- package/dist/utils/readiness.d.ts +10 -0
- package/dist/utils/readiness.d.ts.map +1 -1
- package/dist/utils/readiness.js +6 -3
- package/dist/utils/readiness.js.map +1 -1
- package/dist/utils/retrieval-agent-instructions.js +1 -1
- package/dist/utils/retrieval-agent-instructions.js.map +1 -1
- package/dist/utils/retrieval-execution-telemetry.js +1 -1
- package/dist/utils/retrieval-execution-telemetry.js.map +1 -1
- package/dist/utils/template-fetcher.js +1 -1
- package/dist/utils/template-fetcher.js.map +1 -1
- package/dist/utils/template-hash.d.ts +2 -2
- package/dist/utils/template-hash.d.ts.map +1 -1
- package/dist/utils/template-hash.js +12 -10
- package/dist/utils/template-hash.js.map +1 -1
- package/dist/utils/workflow-dir.d.ts +13 -0
- package/dist/utils/workflow-dir.d.ts.map +1 -0
- package/dist/utils/workflow-dir.js +35 -0
- package/dist/utils/workflow-dir.js.map +1 -0
- package/dist/utils/workflow-ownership.d.ts +36 -0
- package/dist/utils/workflow-ownership.d.ts.map +1 -0
- package/dist/utils/workflow-ownership.js +137 -0
- package/dist/utils/workflow-ownership.js.map +1 -0
- package/dist/utils/workflow-resolver.d.ts +1 -1
- package/dist/utils/workflow-resolver.js +1 -1
- package/package.json +3 -4
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +0 -83
- /package/bin/{trellis.js → cstl.js} +0 -0
- /package/dist/templates/common/bundled-skills/{trellis-spec-bootstrap → cstl-spec-bootstrap}/references/mcp-setup.md +0 -0
|
@@ -1,918 +1,918 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
Task Management Script.
|
|
5
|
-
|
|
6
|
-
Usage:
|
|
7
|
-
python task.py create "<title>" [--slug <name>] [--assignee <dev>] [--priority P0|P1|P2|P3] [--parent <dir>] [--package <pkg>]
|
|
8
|
-
python task.py add-context <dir> <file> <path> [reason] # Add jsonl entry
|
|
9
|
-
python task.py validate <dir> # Validate jsonl files
|
|
10
|
-
python task.py list-context <dir> # List jsonl entries
|
|
11
|
-
python task.py dashboard # Show Task Dashboard
|
|
12
|
-
python task.py select <dir> # Select task for this live session
|
|
13
|
-
python task.py selected [--source] # Show selected task
|
|
14
|
-
python task.py start-execution <dir> --approved # Start approved execution
|
|
15
|
-
python task.py record-gate <dir> --transition <key> --gate <gate> --result PASS|FAIL|SKIPPED --reviewer <id> --evidence <ref> [--root-cause <cause>]
|
|
16
|
-
python task.py exit # Clear selected task
|
|
17
|
-
python task.py set-branch <dir> <branch> # Set git branch
|
|
18
|
-
python task.py set-base-branch <dir> <branch> # Set PR target branch
|
|
19
|
-
python task.py set-scope <dir> <scope> # Set scope for PR title
|
|
20
|
-
python task.py archive <task-dir> [--check] [--archive-integrated-children] # Check or archive completed task
|
|
21
|
-
python task.py prepare-archive-evidence <task-dir> [--dry-run] # Draft missing verify.md archive evidence
|
|
22
|
-
python task.py prepare-learning-scaffold <task-dir> [--trigger <text>] # Print spec-capture checklist (stdout only)
|
|
23
|
-
python task.py list # List active tasks
|
|
24
|
-
python task.py list-archive [month] # List archived tasks
|
|
25
|
-
python task.py add-subtask <parent-dir> <child-dir> # Link child to parent
|
|
26
|
-
python task.py remove-subtask <parent-dir> <child-dir> # Unlink child from parent
|
|
27
|
-
python task.py prepare-child-worktree <parent-dir> <child-dir> --branch <branch>
|
|
28
|
-
python task.py set-child-state <parent-dir> <child-dir> <state> --evidence <ref>
|
|
29
|
-
python task.py integrate-child <parent-dir> <child-dir> <state> --evidence <ref>
|
|
30
|
-
python task.py generate-child-prompt <parent-dir> <child-dir> [--mode inline|subagent]
|
|
31
|
-
python task.py generate-dispatch-prompt <task-dir> <role> [--scope TEXT] [--finish] [--max-chars N]
|
|
32
|
-
python task.py parent-status <parent-dir>
|
|
33
|
-
python task.py review-child <parent-dir> <child-dir> [--check] [--decision accept|changes|cancel|integrate-through]
|
|
34
|
-
"""
|
|
35
|
-
|
|
36
|
-
from __future__ import annotations
|
|
37
|
-
|
|
38
|
-
import argparse
|
|
39
|
-
import sys
|
|
40
|
-
from datetime import datetime, timezone
|
|
41
|
-
from pathlib import Path
|
|
42
|
-
|
|
43
|
-
from common.log import Colors, colored
|
|
44
|
-
from common.paths import (
|
|
45
|
-
DIR_WORKFLOW,
|
|
46
|
-
DIR_TASKS,
|
|
47
|
-
FILE_TASK_JSON,
|
|
48
|
-
get_repo_root,
|
|
49
|
-
get_developer,
|
|
50
|
-
get_tasks_dir,
|
|
51
|
-
)
|
|
52
|
-
from common.active_task import (
|
|
53
|
-
clear_selected_task,
|
|
54
|
-
resolve_context_key,
|
|
55
|
-
resolve_selected_task,
|
|
56
|
-
set_selected_task,
|
|
57
|
-
)
|
|
58
|
-
from common.io import read_json, write_json
|
|
59
|
-
from common.task_dashboard import render_task_dashboard
|
|
60
|
-
from common.cli_environment import optional_capability_note
|
|
61
|
-
from common.task_gates import (
|
|
62
|
-
BASELINE_GATE,
|
|
63
|
-
build_reviewer_gate_record,
|
|
64
|
-
read_strategy_contract,
|
|
65
|
-
start_execution_repair_hints,
|
|
66
|
-
validate_start_execution,
|
|
67
|
-
validate_start_execution_check,
|
|
68
|
-
write_gate_record,
|
|
69
|
-
)
|
|
70
|
-
from common.task_utils import resolve_task_dir, run_task_hooks
|
|
71
|
-
from common.tasks import (
|
|
72
|
-
children_progress,
|
|
73
|
-
format_child_task_display,
|
|
74
|
-
iter_active_tasks,
|
|
75
|
-
load_parent_child_integration_states,
|
|
76
|
-
)
|
|
77
|
-
from common.task_map import get_child_state
|
|
78
|
-
|
|
79
|
-
# Import command handlers from split modules (also re-exports for plan.py compatibility)
|
|
80
|
-
from common.task_store import (
|
|
81
|
-
cmd_create,
|
|
82
|
-
cmd_archive,
|
|
83
|
-
cmd_prepare_archive_evidence,
|
|
84
|
-
cmd_prepare_learning_scaffold,
|
|
85
|
-
cmd_set_branch,
|
|
86
|
-
cmd_set_base_branch,
|
|
87
|
-
cmd_set_scope,
|
|
88
|
-
cmd_add_subtask,
|
|
89
|
-
cmd_remove_subtask,
|
|
90
|
-
cmd_prepare_child_worktree,
|
|
91
|
-
cmd_set_child_state,
|
|
92
|
-
cmd_integrate_child,
|
|
93
|
-
cmd_generate_child_prompt,
|
|
94
|
-
cmd_generate_dispatch_prompt,
|
|
95
|
-
cmd_suggest_execution_strategy,
|
|
96
|
-
cmd_parent_status,
|
|
97
|
-
cmd_review_child,
|
|
98
|
-
)
|
|
99
|
-
from common.task_context import (
|
|
100
|
-
cmd_add_context,
|
|
101
|
-
cmd_validate,
|
|
102
|
-
cmd_list_context,
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
# =============================================================================
|
|
107
|
-
# Command: dashboard / select / selected / start-execution / record-gate / exit
|
|
108
|
-
# =============================================================================
|
|
109
|
-
|
|
110
|
-
def _repo_relative(path, repo_root) -> str:
|
|
111
|
-
try:
|
|
112
|
-
return path.relative_to(repo_root).as_posix()
|
|
113
|
-
except ValueError:
|
|
114
|
-
return str(path)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def _print_select_dispatch_fallback_tip() -> None:
|
|
118
|
-
print(
|
|
119
|
-
"Tip: if no session identity, use "
|
|
120
|
-
"'python ./.
|
|
121
|
-
"to dispatch directly.",
|
|
122
|
-
file=sys.stderr,
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
def _resolve_existing_task(task_input: str, repo_root):
|
|
127
|
-
full_path = resolve_task_dir(task_input, repo_root)
|
|
128
|
-
if not full_path.is_dir():
|
|
129
|
-
print(colored(f"Error: Task not found: {task_input}", Colors.RED), file=sys.stderr)
|
|
130
|
-
print("Hint: Use task name (e.g., 'my-task') or full path (e.g., '.
|
|
131
|
-
return None
|
|
132
|
-
return full_path
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
def cmd_dashboard(args: argparse.Namespace) -> int:
|
|
136
|
-
"""Show Task Dashboard without mutating selection or status."""
|
|
137
|
-
_ = args
|
|
138
|
-
print(render_task_dashboard(get_repo_root()))
|
|
139
|
-
return 0
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def cmd_select(args: argparse.Namespace) -> int:
|
|
143
|
-
"""Select a task for this live session without changing task.status."""
|
|
144
|
-
repo_root = get_repo_root()
|
|
145
|
-
task_input = args.dir
|
|
146
|
-
full_path = _resolve_existing_task(task_input, repo_root)
|
|
147
|
-
if full_path is None:
|
|
148
|
-
_print_select_dispatch_fallback_tip()
|
|
149
|
-
return 1
|
|
150
|
-
|
|
151
|
-
if not resolve_context_key():
|
|
152
|
-
print(
|
|
153
|
-
colored("Error: session identity not available; selected_task was not persisted.", Colors.RED),
|
|
154
|
-
file=sys.stderr,
|
|
155
|
-
)
|
|
156
|
-
print(
|
|
157
|
-
"Hint: run inside an AI session that exposes session identity, or set TRELLIS_CONTEXT_ID before running task.py select.",
|
|
158
|
-
file=sys.stderr,
|
|
159
|
-
)
|
|
160
|
-
_print_select_dispatch_fallback_tip()
|
|
161
|
-
return 1
|
|
162
|
-
|
|
163
|
-
selected = set_selected_task(_repo_relative(full_path, repo_root), repo_root)
|
|
164
|
-
if not selected:
|
|
165
|
-
print(colored("Error: failed to select task", Colors.RED), file=sys.stderr)
|
|
166
|
-
_print_select_dispatch_fallback_tip()
|
|
167
|
-
return 1
|
|
168
|
-
|
|
169
|
-
print(colored(f"✓ Selected task: {selected.task_path}", Colors.GREEN))
|
|
170
|
-
print(f"Source: {selected.source}")
|
|
171
|
-
print("Task status unchanged.")
|
|
172
|
-
return 0
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def _print_no_selected_task_guidance() -> None:
|
|
176
|
-
"""Explain why no task is selected and what to run next."""
|
|
177
|
-
print(colored("No task selected for this live session.", Colors.YELLOW), file=sys.stderr)
|
|
178
|
-
print("Next actions:", file=sys.stderr)
|
|
179
|
-
print(
|
|
180
|
-
" - Route work: python ./.
|
|
181
|
-
file=sys.stderr,
|
|
182
|
-
)
|
|
183
|
-
print(
|
|
184
|
-
" - Select a task: python ./.
|
|
185
|
-
file=sys.stderr,
|
|
186
|
-
)
|
|
187
|
-
print(
|
|
188
|
-
" - List active tasks: python ./.
|
|
189
|
-
file=sys.stderr,
|
|
190
|
-
)
|
|
191
|
-
print(
|
|
192
|
-
" - Persist selection in shells: set TRELLIS_CONTEXT_ID (or use your platform session hook)",
|
|
193
|
-
file=sys.stderr,
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
def cmd_selected(args: argparse.Namespace) -> int:
|
|
198
|
-
"""Show selected task."""
|
|
199
|
-
repo_root = get_repo_root()
|
|
200
|
-
selected = resolve_selected_task(repo_root)
|
|
201
|
-
|
|
202
|
-
if args.source:
|
|
203
|
-
print(f"Selected task: {selected.task_path or '(none)'}")
|
|
204
|
-
print(f"Source: {selected.source}")
|
|
205
|
-
if selected.stale:
|
|
206
|
-
print("State: stale")
|
|
207
|
-
if not selected.task_path:
|
|
208
|
-
_print_no_selected_task_guidance()
|
|
209
|
-
return 0 if selected.task_path else 1
|
|
210
|
-
|
|
211
|
-
if selected.task_path:
|
|
212
|
-
print(selected.task_path)
|
|
213
|
-
return 0
|
|
214
|
-
|
|
215
|
-
_print_no_selected_task_guidance()
|
|
216
|
-
return 1
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
def cmd_exit(args: argparse.Namespace) -> int:
|
|
220
|
-
"""Clear selected task for this live session without changing task.status."""
|
|
221
|
-
_ = args
|
|
222
|
-
repo_root = get_repo_root()
|
|
223
|
-
selected = clear_selected_task(repo_root)
|
|
224
|
-
if not selected.task_path:
|
|
225
|
-
print(colored("No selected task set", Colors.YELLOW))
|
|
226
|
-
return 0
|
|
227
|
-
|
|
228
|
-
print(colored(f"✓ Cleared selected task (was: {selected.task_path})", Colors.GREEN))
|
|
229
|
-
print(f"Source: {selected.source}")
|
|
230
|
-
print("Task status unchanged.")
|
|
231
|
-
return 0
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
def _print_guard_errors(items: list[str], stream=None) -> None:
|
|
235
|
-
if stream is None:
|
|
236
|
-
stream = sys.stdout
|
|
237
|
-
for item in items:
|
|
238
|
-
print(f" - {item}", file=stream)
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
def cmd_start_execution(args: argparse.Namespace) -> int:
|
|
242
|
-
"""Start approved task execution after a non-mutating readiness check."""
|
|
243
|
-
repo_root = get_repo_root()
|
|
244
|
-
task_dir = _resolve_existing_task(args.dir, repo_root)
|
|
245
|
-
if task_dir is None:
|
|
246
|
-
return 1
|
|
247
|
-
|
|
248
|
-
task_json_path = task_dir / FILE_TASK_JSON
|
|
249
|
-
task_data = read_json(task_json_path) if task_json_path.is_file() else None
|
|
250
|
-
|
|
251
|
-
if args.check:
|
|
252
|
-
guard = validate_start_execution_check(task_dir, task_data)
|
|
253
|
-
if not guard.ok:
|
|
254
|
-
print(colored("Start-execution check: FAIL", Colors.RED))
|
|
255
|
-
_print_guard_errors(guard.errors)
|
|
256
|
-
for hint in start_execution_repair_hints(guard.errors, task_dir):
|
|
257
|
-
print(f" Hint: {hint}")
|
|
258
|
-
return 1
|
|
259
|
-
print(colored("Start-execution check: PASS", Colors.GREEN))
|
|
260
|
-
print(f"Contract fingerprint: {guard.contract_fingerprint}")
|
|
261
|
-
baseline_fingerprint = guard.artifact_fingerprints.get(BASELINE_GATE)
|
|
262
|
-
if baseline_fingerprint:
|
|
263
|
-
print(f"Artifact fingerprint: {baseline_fingerprint}")
|
|
264
|
-
if guard.required_gates:
|
|
265
|
-
print(f"Required reviewer gates: {', '.join(guard.required_gates)}")
|
|
266
|
-
if task_dir is not None:
|
|
267
|
-
contract, _ = read_strategy_contract(task_dir)
|
|
268
|
-
cap_note = optional_capability_note(contract.get("optional_capabilities"))
|
|
269
|
-
if cap_note:
|
|
270
|
-
print(colored(f"Note: {cap_note}", Colors.YELLOW))
|
|
271
|
-
from common.execution_strategy import (
|
|
272
|
-
contract_drift_warnings,
|
|
273
|
-
validate_strategy_pair,
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
if contract:
|
|
277
|
-
for item in contract_drift_warnings(
|
|
278
|
-
repo_root, task_dir, task_data or {}, contract
|
|
279
|
-
):
|
|
280
|
-
print(f"[execution-strategy] WARN: {item}", file=sys.stderr)
|
|
281
|
-
mode = contract.get("execution_mode")
|
|
282
|
-
iso = contract.get("isolation")
|
|
283
|
-
if isinstance(mode, str) and isinstance(iso, str):
|
|
284
|
-
for item in validate_strategy_pair(mode, iso):
|
|
285
|
-
print(f"[execution-strategy] WARN: {item}", file=sys.stderr)
|
|
286
|
-
print("Artifact gates are ready. Ask the user for explicit execution approval before running `task.py start-execution <task> --approved`.")
|
|
287
|
-
return 0
|
|
288
|
-
|
|
289
|
-
if not args.approved:
|
|
290
|
-
print(colored("Error: no action selected for start-execution.", Colors.RED), file=sys.stderr)
|
|
291
|
-
print("Run with --check for non-mutating preflight or --approved after explicit user approval.", file=sys.stderr)
|
|
292
|
-
return 1
|
|
293
|
-
|
|
294
|
-
guard = validate_start_execution(task_dir, task_data, approved=True)
|
|
295
|
-
if not guard.ok:
|
|
296
|
-
print(colored("Error: cannot start execution; readiness check failed.", Colors.RED), file=sys.stderr)
|
|
297
|
-
_print_guard_errors(guard.errors, stream=sys.stderr)
|
|
298
|
-
return 1
|
|
299
|
-
|
|
300
|
-
assert task_data is not None
|
|
301
|
-
if guard.baseline_record:
|
|
302
|
-
write_gate_record(task_data, "start-execution", BASELINE_GATE, guard.baseline_record)
|
|
303
|
-
for gate, record in guard.auto_gate_records.items():
|
|
304
|
-
write_gate_record(task_data, "start-execution", gate, record)
|
|
305
|
-
task_data["execution_approval"] = {
|
|
306
|
-
"schema_version": 1,
|
|
307
|
-
"transition": "start-execution",
|
|
308
|
-
"approved_at": datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z"),
|
|
309
|
-
"approved_by": "user",
|
|
310
|
-
"approval_source": "task.py start-execution --approved",
|
|
311
|
-
"contract_fingerprint": guard.contract_fingerprint,
|
|
312
|
-
"artifact_fingerprint": guard.artifact_fingerprints.get(BASELINE_GATE),
|
|
313
|
-
}
|
|
314
|
-
if task_data.get("status") == "planning":
|
|
315
|
-
task_data["status"] = "in_progress"
|
|
316
|
-
if not write_json(task_json_path, task_data):
|
|
317
|
-
print(colored("Error: failed to write task.json", Colors.RED), file=sys.stderr)
|
|
318
|
-
return 1
|
|
319
|
-
|
|
320
|
-
print(colored(f"✓ Execution approved for: {_repo_relative(task_dir, repo_root)}", Colors.GREEN))
|
|
321
|
-
print(f"Status: {task_data.get('status')}")
|
|
322
|
-
run_task_hooks("after_start", task_json_path, repo_root)
|
|
323
|
-
return 0
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
def cmd_record_gate(args: argparse.Namespace) -> int:
|
|
327
|
-
"""Record a non-baseline reviewer gate result."""
|
|
328
|
-
repo_root = get_repo_root()
|
|
329
|
-
task_dir = _resolve_existing_task(args.dir, repo_root)
|
|
330
|
-
if task_dir is None:
|
|
331
|
-
return 1
|
|
332
|
-
|
|
333
|
-
task_json_path = task_dir / FILE_TASK_JSON
|
|
334
|
-
task_data = read_json(task_json_path) if task_json_path.is_file() else None
|
|
335
|
-
if task_data is None:
|
|
336
|
-
print(colored("Error: task.json not found or invalid", Colors.RED), file=sys.stderr)
|
|
337
|
-
return 1
|
|
338
|
-
|
|
339
|
-
record, errors, warnings = build_reviewer_gate_record(
|
|
340
|
-
task_dir=task_dir,
|
|
341
|
-
task_data=task_data,
|
|
342
|
-
transition=args.transition,
|
|
343
|
-
gate=args.gate,
|
|
344
|
-
result=args.result,
|
|
345
|
-
reviewer=args.reviewer,
|
|
346
|
-
evidence=args.evidence,
|
|
347
|
-
issue_fingerprint=args.issue_fingerprint,
|
|
348
|
-
issue_summary=args.issue_summary,
|
|
349
|
-
root_cause=args.root_cause,
|
|
350
|
-
skip_approved_by=args.skip_approved_by,
|
|
351
|
-
skip_reason=args.skip_reason,
|
|
352
|
-
contract_fingerprint=args.contract_fingerprint,
|
|
353
|
-
artifact_fingerprint=args.artifact_fingerprint,
|
|
354
|
-
)
|
|
355
|
-
if errors:
|
|
356
|
-
print(colored("Error: cannot record quality gate.", Colors.RED), file=sys.stderr)
|
|
357
|
-
_print_guard_errors(errors, stream=sys.stderr)
|
|
358
|
-
return 1
|
|
359
|
-
|
|
360
|
-
assert record is not None
|
|
361
|
-
write_gate_record(task_data, args.transition, args.gate, record)
|
|
362
|
-
if not write_json(task_json_path, task_data):
|
|
363
|
-
print(colored("Error: failed to write task.json", Colors.RED), file=sys.stderr)
|
|
364
|
-
return 1
|
|
365
|
-
|
|
366
|
-
print(colored(f"✓ Recorded gate: {args.transition}/{args.gate} = {record['result']}", Colors.GREEN))
|
|
367
|
-
print(f"Evidence: {record['evidence']}")
|
|
368
|
-
print(f"Contract fingerprint: {record['contract_fingerprint']}")
|
|
369
|
-
print(f"Artifact fingerprint: {record['artifact_fingerprint']}")
|
|
370
|
-
if record.get("route"):
|
|
371
|
-
print(f"Route: {record['route']}")
|
|
372
|
-
for warning in warnings:
|
|
373
|
-
print(colored(f"Warning: {warning}", Colors.YELLOW), file=sys.stderr)
|
|
374
|
-
return 0
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
# =============================================================================
|
|
378
|
-
# Command: list
|
|
379
|
-
# =============================================================================
|
|
380
|
-
|
|
381
|
-
def cmd_list(args: argparse.Namespace) -> int:
|
|
382
|
-
"""List active tasks."""
|
|
383
|
-
repo_root = get_repo_root()
|
|
384
|
-
tasks_dir = get_tasks_dir(repo_root)
|
|
385
|
-
selected_task = resolve_selected_task(repo_root).task_path
|
|
386
|
-
developer = get_developer(repo_root)
|
|
387
|
-
filter_mine = args.mine
|
|
388
|
-
filter_status = args.status
|
|
389
|
-
|
|
390
|
-
if filter_mine:
|
|
391
|
-
if not developer:
|
|
392
|
-
print(colored("Error: No developer set. Run init_developer.py first", Colors.RED), file=sys.stderr)
|
|
393
|
-
return 1
|
|
394
|
-
print(colored(f"My tasks (assignee: {developer}):", Colors.BLUE))
|
|
395
|
-
else:
|
|
396
|
-
print(colored("All active tasks:", Colors.BLUE))
|
|
397
|
-
print()
|
|
398
|
-
|
|
399
|
-
# Single pass: collect all tasks via shared iterator
|
|
400
|
-
all_tasks = {t.dir_name: t for t in iter_active_tasks(tasks_dir)}
|
|
401
|
-
all_statuses = {name: t.status for name, t in all_tasks.items()}
|
|
402
|
-
|
|
403
|
-
# Display tasks hierarchically
|
|
404
|
-
count = 0
|
|
405
|
-
|
|
406
|
-
def _print_task(
|
|
407
|
-
dir_name: str,
|
|
408
|
-
indent: int = 0,
|
|
409
|
-
parent_dir: Path | None = None,
|
|
410
|
-
) -> None:
|
|
411
|
-
nonlocal count
|
|
412
|
-
t = all_tasks[dir_name]
|
|
413
|
-
|
|
414
|
-
# Apply --mine filter
|
|
415
|
-
if filter_mine and (t.assignee or "-") != developer:
|
|
416
|
-
return
|
|
417
|
-
|
|
418
|
-
# Apply --status filter
|
|
419
|
-
if filter_status and t.status != filter_status:
|
|
420
|
-
return
|
|
421
|
-
|
|
422
|
-
relative_path = f"{DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}"
|
|
423
|
-
marker = ""
|
|
424
|
-
if relative_path == selected_task:
|
|
425
|
-
marker = f" {colored('<- selected', Colors.GREEN)}"
|
|
426
|
-
|
|
427
|
-
integration_states = None
|
|
428
|
-
if t.children:
|
|
429
|
-
integration_states = load_parent_child_integration_states(
|
|
430
|
-
t.directory, t.children
|
|
431
|
-
)
|
|
432
|
-
progress = children_progress(
|
|
433
|
-
t.children, all_statuses, integration_states
|
|
434
|
-
)
|
|
435
|
-
|
|
436
|
-
integration_state = None
|
|
437
|
-
if parent_dir is not None:
|
|
438
|
-
integration_state = get_child_state(parent_dir, dir_name)
|
|
439
|
-
status_display = format_child_task_display(t.status, integration_state)
|
|
440
|
-
|
|
441
|
-
# Package tag
|
|
442
|
-
pkg_tag = f" @{t.package}" if t.package else ""
|
|
443
|
-
|
|
444
|
-
prefix = " " * indent + " - "
|
|
445
|
-
|
|
446
|
-
if filter_mine:
|
|
447
|
-
print(
|
|
448
|
-
f"{prefix}{dir_name}/ ({status_display}){pkg_tag}{progress}{marker}"
|
|
449
|
-
)
|
|
450
|
-
else:
|
|
451
|
-
print(
|
|
452
|
-
f"{prefix}{dir_name}/ ({status_display}){pkg_tag}{progress} "
|
|
453
|
-
f"[{colored(t.assignee or '-', Colors.CYAN)}]{marker}"
|
|
454
|
-
)
|
|
455
|
-
count += 1
|
|
456
|
-
|
|
457
|
-
# Print children indented
|
|
458
|
-
for child_name in t.children:
|
|
459
|
-
if child_name in all_tasks:
|
|
460
|
-
_print_task(child_name, indent + 1, parent_dir=t.directory)
|
|
461
|
-
|
|
462
|
-
# Display only top-level tasks (those without a parent)
|
|
463
|
-
for dir_name in sorted(all_tasks.keys()):
|
|
464
|
-
if not all_tasks[dir_name].parent:
|
|
465
|
-
_print_task(dir_name)
|
|
466
|
-
|
|
467
|
-
if count == 0:
|
|
468
|
-
if filter_mine:
|
|
469
|
-
print(" (no tasks assigned to you)")
|
|
470
|
-
else:
|
|
471
|
-
print(" (no active tasks)")
|
|
472
|
-
|
|
473
|
-
print()
|
|
474
|
-
print(f"Total: {count} task(s)")
|
|
475
|
-
return 0
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
# =============================================================================
|
|
479
|
-
# Command: list-archive
|
|
480
|
-
# =============================================================================
|
|
481
|
-
|
|
482
|
-
def cmd_list_archive(args: argparse.Namespace) -> int:
|
|
483
|
-
"""List archived tasks."""
|
|
484
|
-
repo_root = get_repo_root()
|
|
485
|
-
tasks_dir = get_tasks_dir(repo_root)
|
|
486
|
-
archive_dir = tasks_dir / "archive"
|
|
487
|
-
month = args.month
|
|
488
|
-
|
|
489
|
-
print(colored("Archived tasks:", Colors.BLUE))
|
|
490
|
-
print()
|
|
491
|
-
|
|
492
|
-
if month:
|
|
493
|
-
month_dir = archive_dir / month
|
|
494
|
-
if month_dir.is_dir():
|
|
495
|
-
print(f"[{month}]")
|
|
496
|
-
for d in sorted(month_dir.iterdir()):
|
|
497
|
-
if d.is_dir():
|
|
498
|
-
print(f" - {d.name}/")
|
|
499
|
-
else:
|
|
500
|
-
print(f" No archives for {month}")
|
|
501
|
-
else:
|
|
502
|
-
if archive_dir.is_dir():
|
|
503
|
-
for month_dir in sorted(archive_dir.iterdir()):
|
|
504
|
-
if month_dir.is_dir():
|
|
505
|
-
month_name = month_dir.name
|
|
506
|
-
count = sum(1 for d in month_dir.iterdir() if d.is_dir())
|
|
507
|
-
print(f"[{month_name}] - {count} task(s)")
|
|
508
|
-
|
|
509
|
-
return 0
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
# =============================================================================
|
|
513
|
-
# Help
|
|
514
|
-
# =============================================================================
|
|
515
|
-
|
|
516
|
-
def show_usage() -> None:
|
|
517
|
-
"""Show usage help."""
|
|
518
|
-
print("""Task Management Script
|
|
519
|
-
|
|
520
|
-
Usage:
|
|
521
|
-
python task.py create <title> Create new task directory
|
|
522
|
-
python task.py create <title> --package <pkg> Create task for a specific package
|
|
523
|
-
python task.py create <title> --parent <dir> Create task as child of parent
|
|
524
|
-
python task.py add-context <dir> <jsonl> <path> [reason] Add entry to jsonl
|
|
525
|
-
python task.py validate <dir> Validate jsonl files
|
|
526
|
-
python task.py list-context <dir> List jsonl entries
|
|
527
|
-
python task.py dashboard Show Task Dashboard
|
|
528
|
-
python task.py select <dir> Select task for this live session
|
|
529
|
-
python task.py selected [--source] Show selected task
|
|
530
|
-
python task.py start-execution <dir> --check Check execution readiness
|
|
531
|
-
python task.py start-execution <dir> --approved Start approved execution
|
|
532
|
-
python task.py record-gate <dir> --transition <key> --gate <gate> --result PASS|FAIL|SKIPPED --reviewer <id> --evidence <ref> [--root-cause <cause>]
|
|
533
|
-
python task.py exit Clear selected task
|
|
534
|
-
python task.py set-branch <dir> <branch> Set git branch
|
|
535
|
-
python task.py set-base-branch <dir> <branch> Set PR target branch
|
|
536
|
-
python task.py set-scope <dir> <scope> Set scope for PR title
|
|
537
|
-
python task.py archive <task-dir> [--check] Check or archive completed task
|
|
538
|
-
python task.py add-subtask <parent> <child> Link child task to parent
|
|
539
|
-
python task.py remove-subtask <parent> <child> Unlink child from parent
|
|
540
|
-
python task.py prepare-child-worktree <parent> <child> --branch <branch>
|
|
541
|
-
python task.py set-child-state <parent> <child> <state> --evidence <ref>
|
|
542
|
-
python task.py integrate-child <parent> <child> <state> --evidence <ref>
|
|
543
|
-
python task.py list [--mine] [--status <status>] List tasks
|
|
544
|
-
python task.py list-archive [YYYY-MM] List archived tasks
|
|
545
|
-
|
|
546
|
-
Monorepo options:
|
|
547
|
-
--package <pkg> Package name (validated against config.yaml packages)
|
|
548
|
-
|
|
549
|
-
List options:
|
|
550
|
-
--mine, -m Show only tasks assigned to current developer
|
|
551
|
-
--status, -s <s> Filter by status (planning, in_progress, review, completed)
|
|
552
|
-
|
|
553
|
-
Examples:
|
|
554
|
-
python task.py create "Add login feature" --slug add-login
|
|
555
|
-
python task.py create "Add login feature" --slug add-login --package cli
|
|
556
|
-
python task.py create "Child task" --slug child --parent .
|
|
557
|
-
python task.py add-context <dir> implement .
|
|
558
|
-
python task.py set-branch <dir> task/add-login
|
|
559
|
-
python task.py dashboard
|
|
560
|
-
python task.py select .
|
|
561
|
-
python task.py selected --source
|
|
562
|
-
python task.py start-execution .
|
|
563
|
-
python task.py start-execution .
|
|
564
|
-
python task.py record-gate .
|
|
565
|
-
python task.py exit
|
|
566
|
-
python task.py archive add-login --check
|
|
567
|
-
python task.py archive add-login
|
|
568
|
-
python task.py add-subtask parent-task child-task # Link existing tasks
|
|
569
|
-
python task.py remove-subtask parent-task child-task
|
|
570
|
-
python task.py prepare-child-worktree parent-task child-task --branch child-task
|
|
571
|
-
python task.py set-child-state parent-task child-task review --evidence verify.md
|
|
572
|
-
python task.py integrate-child parent-task child-task accepted --evidence handoff.md --ref child-branch
|
|
573
|
-
python task.py integrate-child parent-task child-task integrated --evidence task-map.md --ref child-branch --execute-merge
|
|
574
|
-
python task.py generate-child-prompt parent-task child-task --mode inline
|
|
575
|
-
python task.py parent-status parent-task
|
|
576
|
-
python task.py review-child parent-task child-task --check
|
|
577
|
-
python task.py review-child parent-task child-task --decision accept --ref child-branch
|
|
578
|
-
python task.py list # List all active tasks
|
|
579
|
-
python task.py list --mine # List my tasks only
|
|
580
|
-
python task.py list --mine --status in_progress # List my in-progress tasks
|
|
581
|
-
""")
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
# =============================================================================
|
|
585
|
-
# Main Entry
|
|
586
|
-
# =============================================================================
|
|
587
|
-
|
|
588
|
-
def main() -> int:
|
|
589
|
-
"""CLI entry point."""
|
|
590
|
-
# Deprecation guard: `init-context` was removed in v0.5.0-beta.12.
|
|
591
|
-
# Detect early so argparse doesn't mask the real reason with a generic
|
|
592
|
-
# "invalid choice" error.
|
|
593
|
-
if len(sys.argv) >= 2 and sys.argv[1] == "init-context":
|
|
594
|
-
print(
|
|
595
|
-
colored(
|
|
596
|
-
"Error: `task.py init-context` was removed in v0.5.0-beta.12.",
|
|
597
|
-
Colors.RED,
|
|
598
|
-
),
|
|
599
|
-
file=sys.stderr,
|
|
600
|
-
)
|
|
601
|
-
print(
|
|
602
|
-
"implement.jsonl / check.jsonl are now seeded on `task.py create` for",
|
|
603
|
-
file=sys.stderr,
|
|
604
|
-
)
|
|
605
|
-
print(
|
|
606
|
-
"sub-agent-capable platforms and curated by the AI during planning when needed.",
|
|
607
|
-
file=sys.stderr,
|
|
608
|
-
)
|
|
609
|
-
print("See .
|
|
610
|
-
print(
|
|
611
|
-
" python ./.
|
|
612
|
-
file=sys.stderr,
|
|
613
|
-
)
|
|
614
|
-
print(
|
|
615
|
-
"Use `task.py add-context <dir> implement|check <path> <reason>` to append entries.",
|
|
616
|
-
file=sys.stderr,
|
|
617
|
-
)
|
|
618
|
-
return 2
|
|
619
|
-
|
|
620
|
-
parser = argparse.ArgumentParser(
|
|
621
|
-
description="Task Management Script",
|
|
622
|
-
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
623
|
-
)
|
|
624
|
-
subparsers = parser.add_subparsers(dest="command", help="Commands")
|
|
625
|
-
|
|
626
|
-
# create
|
|
627
|
-
p_create = subparsers.add_parser("create", help="Create new task")
|
|
628
|
-
p_create.add_argument("title", help="Task title")
|
|
629
|
-
p_create.add_argument("--slug", "-s", help="Task slug")
|
|
630
|
-
p_create.add_argument("--assignee", "-a", help="Assignee developer")
|
|
631
|
-
p_create.add_argument("--priority", "-p", default="P2", help="Priority (P0-P3)")
|
|
632
|
-
p_create.add_argument("--description", "-d", help="Task description")
|
|
633
|
-
p_create.add_argument("--parent", help="Parent task directory (establishes subtask link)")
|
|
634
|
-
p_create.add_argument("--package", help="Package name for monorepo projects")
|
|
635
|
-
|
|
636
|
-
# add-context
|
|
637
|
-
p_add = subparsers.add_parser("add-context", help="Add context entry")
|
|
638
|
-
p_add.add_argument("dir", help="Task directory")
|
|
639
|
-
p_add.add_argument("file", help="JSONL file (implement|check)")
|
|
640
|
-
p_add.add_argument("path", help="File path to add")
|
|
641
|
-
p_add.add_argument("reason", nargs="?", help="Reason for adding")
|
|
642
|
-
|
|
643
|
-
# validate
|
|
644
|
-
p_validate = subparsers.add_parser("validate", help="Validate context files")
|
|
645
|
-
p_validate.add_argument("dir", help="Task directory")
|
|
646
|
-
|
|
647
|
-
# list-context
|
|
648
|
-
p_listctx = subparsers.add_parser("list-context", help="List context entries")
|
|
649
|
-
p_listctx.add_argument("dir", help="Task directory")
|
|
650
|
-
|
|
651
|
-
# dashboard
|
|
652
|
-
subparsers.add_parser("dashboard", help="Show Task Dashboard")
|
|
653
|
-
|
|
654
|
-
# select
|
|
655
|
-
p_select = subparsers.add_parser("select", help="Select task for this live session")
|
|
656
|
-
p_select.add_argument("dir", help="Task directory")
|
|
657
|
-
|
|
658
|
-
# selected
|
|
659
|
-
p_selected = subparsers.add_parser("selected", help="Show selected task")
|
|
660
|
-
p_selected.add_argument("--source", action="store_true",
|
|
661
|
-
help="Show selected task source")
|
|
662
|
-
|
|
663
|
-
# start-execution
|
|
664
|
-
p_start_execution = subparsers.add_parser("start-execution", help="Start approved task execution")
|
|
665
|
-
p_start_execution.add_argument("dir", help="Task directory")
|
|
666
|
-
p_start_execution.add_argument("--check", action="store_true",
|
|
667
|
-
help="Run non-mutating execution readiness check")
|
|
668
|
-
p_start_execution.add_argument("--approved", action="store_true",
|
|
669
|
-
help="Record explicit approval and start execution")
|
|
670
|
-
|
|
671
|
-
# record-gate
|
|
672
|
-
p_record_gate = subparsers.add_parser("record-gate", help="Record reviewer quality gate")
|
|
673
|
-
p_record_gate.add_argument("dir", help="Task directory")
|
|
674
|
-
p_record_gate.add_argument("--transition", required=True, help="Transition key")
|
|
675
|
-
p_record_gate.add_argument("--gate", required=True, help="Gate name")
|
|
676
|
-
p_record_gate.add_argument("--result", required=True, help="PASS, FAIL, or SKIPPED")
|
|
677
|
-
p_record_gate.add_argument("--reviewer", required=True, help="Reviewer identifier")
|
|
678
|
-
p_record_gate.add_argument("--evidence", required=True, help="Short evidence reference")
|
|
679
|
-
p_record_gate.add_argument("--issue-fingerprint", help="Required for FAIL")
|
|
680
|
-
p_record_gate.add_argument("--issue-summary", help="Optional short issue summary for FAIL")
|
|
681
|
-
p_record_gate.add_argument("--root-cause",
|
|
682
|
-
help="Required for FAIL: implementation-defect, contract-changing-defect, or validation-environment-blocker")
|
|
683
|
-
p_record_gate.add_argument("--skip-approved-by", help="Must be 'user' for SKIPPED")
|
|
684
|
-
p_record_gate.add_argument("--skip-reason", help="Required reason for SKIPPED")
|
|
685
|
-
p_record_gate.add_argument("--contract-fingerprint", help="Optional current contract fingerprint assertion")
|
|
686
|
-
p_record_gate.add_argument("--artifact-fingerprint", help="Optional current artifact fingerprint assertion")
|
|
687
|
-
|
|
688
|
-
# exit
|
|
689
|
-
subparsers.add_parser("exit", help="Clear selected task")
|
|
690
|
-
|
|
691
|
-
# set-branch
|
|
692
|
-
p_branch = subparsers.add_parser("set-branch", help="Set git branch")
|
|
693
|
-
p_branch.add_argument("dir", help="Task directory")
|
|
694
|
-
p_branch.add_argument("branch", help="Branch name")
|
|
695
|
-
|
|
696
|
-
# set-base-branch
|
|
697
|
-
p_base = subparsers.add_parser("set-base-branch", help="Set PR target branch")
|
|
698
|
-
p_base.add_argument("dir", help="Task directory")
|
|
699
|
-
p_base.add_argument("base_branch", help="Base branch name (PR target)")
|
|
700
|
-
|
|
701
|
-
# set-scope
|
|
702
|
-
p_scope = subparsers.add_parser("set-scope", help="Set scope")
|
|
703
|
-
p_scope.add_argument("dir", help="Task directory")
|
|
704
|
-
p_scope.add_argument("scope", help="Scope name")
|
|
705
|
-
|
|
706
|
-
# archive
|
|
707
|
-
p_archive = subparsers.add_parser("archive", help="Archive task")
|
|
708
|
-
p_archive.add_argument("name", help="Task directory or name")
|
|
709
|
-
p_archive.add_argument("--check", action="store_true", help="Run non-mutating archive readiness check")
|
|
710
|
-
p_archive.add_argument("--no-commit", action="store_true", help="Skip auto git commit after archive")
|
|
711
|
-
p_archive.add_argument(
|
|
712
|
-
"--archive-integrated-children",
|
|
713
|
-
action="store_true",
|
|
714
|
-
help="When archiving a parent, also archive integrated children that pass archive --check",
|
|
715
|
-
)
|
|
716
|
-
|
|
717
|
-
# prepare-archive-evidence
|
|
718
|
-
p_prepare_archive = subparsers.add_parser(
|
|
719
|
-
"prepare-archive-evidence",
|
|
720
|
-
help="Append missing archive evidence sections to verify.md",
|
|
721
|
-
)
|
|
722
|
-
p_prepare_archive.add_argument("name", help="Task directory or name")
|
|
723
|
-
p_prepare_archive.add_argument(
|
|
724
|
-
"--dry-run",
|
|
725
|
-
action="store_true",
|
|
726
|
-
help="Show what would be appended without writing verify.md",
|
|
727
|
-
)
|
|
728
|
-
|
|
729
|
-
# prepare-learning-scaffold
|
|
730
|
-
p_learning_scaffold = subparsers.add_parser(
|
|
731
|
-
"prepare-learning-scaffold",
|
|
732
|
-
help="Print durable-learning / spec-update checklist (does not edit specs)",
|
|
733
|
-
)
|
|
734
|
-
p_learning_scaffold.add_argument("name", help="Task directory or name")
|
|
735
|
-
p_learning_scaffold.add_argument(
|
|
736
|
-
"--trigger",
|
|
737
|
-
help="Optional reason (e.g. parent review changes, repeated workflow bug)",
|
|
738
|
-
)
|
|
739
|
-
|
|
740
|
-
# list
|
|
741
|
-
p_list = subparsers.add_parser("list", help="List tasks")
|
|
742
|
-
p_list.add_argument("--mine", "-m", action="store_true", help="My tasks only")
|
|
743
|
-
p_list.add_argument("--status", "-s", help="Filter by status")
|
|
744
|
-
|
|
745
|
-
# add-subtask
|
|
746
|
-
p_addsub = subparsers.add_parser("add-subtask", help="Link child task to parent")
|
|
747
|
-
p_addsub.add_argument("parent_dir", help="Parent task directory")
|
|
748
|
-
p_addsub.add_argument("child_dir", help="Child task directory")
|
|
749
|
-
|
|
750
|
-
# remove-subtask
|
|
751
|
-
p_rmsub = subparsers.add_parser("remove-subtask", help="Unlink child task from parent")
|
|
752
|
-
p_rmsub.add_argument("parent_dir", help="Parent task directory")
|
|
753
|
-
p_rmsub.add_argument("child_dir", help="Child task directory")
|
|
754
|
-
|
|
755
|
-
# prepare-child-worktree
|
|
756
|
-
p_prepare_worktree = subparsers.add_parser("prepare-child-worktree", help="Create/register a Child git worktree")
|
|
757
|
-
p_prepare_worktree.add_argument("parent_dir", help="Parent task directory")
|
|
758
|
-
p_prepare_worktree.add_argument("child_dir", help="Child task directory")
|
|
759
|
-
p_prepare_worktree.add_argument("--branch", required=True, help="Child git branch to create or checkout")
|
|
760
|
-
p_prepare_worktree.add_argument("--base", help="Base ref for a new Child branch")
|
|
761
|
-
p_prepare_worktree.add_argument("--path", help="Worktree path under .
|
|
762
|
-
p_prepare_worktree.add_argument("--check", action="store_true", help="Run non-mutating worktree readiness check")
|
|
763
|
-
|
|
764
|
-
# set-child-state
|
|
765
|
-
p_child_state = subparsers.add_parser("set-child-state", help="Set Child Worker state in Parent task-map.md")
|
|
766
|
-
p_child_state.add_argument("parent_dir", help="Parent task directory")
|
|
767
|
-
p_child_state.add_argument("child_dir", help="Child task directory")
|
|
768
|
-
p_child_state.add_argument("state", help="Child state")
|
|
769
|
-
p_child_state.add_argument("--evidence", required=True, help="Short evidence reference")
|
|
770
|
-
p_child_state.add_argument("--reason", help="Optional short reason")
|
|
771
|
-
|
|
772
|
-
# integrate-child
|
|
773
|
-
p_integrate_child = subparsers.add_parser("integrate-child", help="Set Parent-controlled Child integration state")
|
|
774
|
-
p_integrate_child.add_argument("parent_dir", help="Parent task directory")
|
|
775
|
-
p_integrate_child.add_argument("child_dir", help="Child task directory")
|
|
776
|
-
p_integrate_child.add_argument("state", help="Parent-controlled Child state")
|
|
777
|
-
p_integrate_child.add_argument("--evidence", required=True, help="Short evidence reference")
|
|
778
|
-
p_integrate_child.add_argument("--ref", help="Child git ref or reviewed diff reference")
|
|
779
|
-
p_integrate_child.add_argument("--reason", help="Optional short reason")
|
|
780
|
-
p_integrate_child.add_argument("--execute-merge", action="store_true", help="Execute git merge --no-ff --no-commit for an integrated Child")
|
|
781
|
-
p_integrate_child.add_argument("--check", action="store_true", help="Run non-mutating integration readiness check")
|
|
782
|
-
|
|
783
|
-
# suggest-execution-strategy
|
|
784
|
-
p_suggest_strategy = subparsers.add_parser(
|
|
785
|
-
"suggest-execution-strategy",
|
|
786
|
-
help="Suggest execution_mode and isolation for Development Strategy Contract",
|
|
787
|
-
)
|
|
788
|
-
p_suggest_strategy.add_argument("task_dir", help="Task directory")
|
|
789
|
-
p_suggest_strategy.add_argument(
|
|
790
|
-
"--json",
|
|
791
|
-
action="store_true",
|
|
792
|
-
help="Emit machine-readable JSON",
|
|
793
|
-
)
|
|
794
|
-
|
|
795
|
-
# generate-dispatch-prompt
|
|
796
|
-
p_dispatch_prompt = subparsers.add_parser(
|
|
797
|
-
"generate-dispatch-prompt",
|
|
798
|
-
help="Build full Task dispatch prompt (Agent-facing)",
|
|
799
|
-
)
|
|
800
|
-
p_dispatch_prompt.add_argument("task_dir", help="Task directory")
|
|
801
|
-
p_dispatch_prompt.add_argument(
|
|
802
|
-
"role",
|
|
803
|
-
choices=["implement", "check", "research"],
|
|
804
|
-
help="Subagent role",
|
|
805
|
-
)
|
|
806
|
-
p_dispatch_prompt.add_argument("--scope", help="One-line task instruction for subagent")
|
|
807
|
-
p_dispatch_prompt.add_argument(
|
|
808
|
-
"--finish",
|
|
809
|
-
action="store_true",
|
|
810
|
-
help="Use finish check context (role=check only)",
|
|
811
|
-
)
|
|
812
|
-
p_dispatch_prompt.add_argument(
|
|
813
|
-
"--max-chars",
|
|
814
|
-
type=int,
|
|
815
|
-
help="Hard truncate embedded context block",
|
|
816
|
-
)
|
|
817
|
-
|
|
818
|
-
# generate-child-prompt
|
|
819
|
-
p_gen_prompt = subparsers.add_parser(
|
|
820
|
-
"generate-child-prompt",
|
|
821
|
-
help="Generate child implementation prompt for parent orchestration",
|
|
822
|
-
)
|
|
823
|
-
p_gen_prompt.add_argument("parent_dir", help="Parent task directory")
|
|
824
|
-
p_gen_prompt.add_argument("child_dir", help="Child task directory")
|
|
825
|
-
p_gen_prompt.add_argument(
|
|
826
|
-
"--mode",
|
|
827
|
-
choices=["inline", "subagent"],
|
|
828
|
-
default="inline",
|
|
829
|
-
help="Delivery mode hint (inline manual handoff vs optional subagent)",
|
|
830
|
-
)
|
|
831
|
-
p_gen_prompt.add_argument(
|
|
832
|
-
"--include-artifacts",
|
|
833
|
-
action="store_true",
|
|
834
|
-
help="Embed child artifact bodies in the generated prompt",
|
|
835
|
-
)
|
|
836
|
-
p_gen_prompt.add_argument("--output", "-o", help="Write prompt to file instead of stdout")
|
|
837
|
-
|
|
838
|
-
# parent-status
|
|
839
|
-
p_parent_status = subparsers.add_parser("parent-status", help="Show parent task-map orchestration status")
|
|
840
|
-
p_parent_status.add_argument("parent_dir", help="Parent task directory")
|
|
841
|
-
|
|
842
|
-
# review-child
|
|
843
|
-
p_review_child = subparsers.add_parser(
|
|
844
|
-
"review-child",
|
|
845
|
-
help="Review child handoff and optionally advance integration states",
|
|
846
|
-
)
|
|
847
|
-
p_review_child.add_argument("parent_dir", help="Parent task directory")
|
|
848
|
-
p_review_child.add_argument("child_dir", help="Child task directory")
|
|
849
|
-
p_review_child.add_argument("--check", action="store_true", help="Non-mutating review readiness check")
|
|
850
|
-
p_review_child.add_argument(
|
|
851
|
-
"--decision",
|
|
852
|
-
choices=["accept", "changes", "cancel", "integrate-through"],
|
|
853
|
-
help="Parent review decision (runs integrate-child steps when valid)",
|
|
854
|
-
)
|
|
855
|
-
p_review_child.add_argument("--ref", help="Child git ref for accept / integrate-through")
|
|
856
|
-
p_review_child.add_argument("--reason", help="Required for changes or cancel decisions")
|
|
857
|
-
p_review_child.add_argument("--notes", help="Short parent review notes included in the report")
|
|
858
|
-
p_review_child.add_argument(
|
|
859
|
-
"--write-artifact",
|
|
860
|
-
action="store_true",
|
|
861
|
-
help="Also write review-<child>.md under the parent task directory",
|
|
862
|
-
)
|
|
863
|
-
p_review_child.add_argument(
|
|
864
|
-
"--no-append-parent-verify",
|
|
865
|
-
action="store_true",
|
|
866
|
-
help="Do not append review notes to parent verify.md",
|
|
867
|
-
)
|
|
868
|
-
|
|
869
|
-
# list-archive
|
|
870
|
-
p_listarch = subparsers.add_parser("list-archive", help="List archived tasks")
|
|
871
|
-
p_listarch.add_argument("month", nargs="?", help="Month (YYYY-MM)")
|
|
872
|
-
|
|
873
|
-
args = parser.parse_args()
|
|
874
|
-
|
|
875
|
-
if not args.command:
|
|
876
|
-
show_usage()
|
|
877
|
-
return 1
|
|
878
|
-
|
|
879
|
-
commands = {
|
|
880
|
-
"create": cmd_create,
|
|
881
|
-
"add-context": cmd_add_context,
|
|
882
|
-
"validate": cmd_validate,
|
|
883
|
-
"list-context": cmd_list_context,
|
|
884
|
-
"dashboard": cmd_dashboard,
|
|
885
|
-
"select": cmd_select,
|
|
886
|
-
"selected": cmd_selected,
|
|
887
|
-
"start-execution": cmd_start_execution,
|
|
888
|
-
"record-gate": cmd_record_gate,
|
|
889
|
-
"exit": cmd_exit,
|
|
890
|
-
"set-branch": cmd_set_branch,
|
|
891
|
-
"set-base-branch": cmd_set_base_branch,
|
|
892
|
-
"set-scope": cmd_set_scope,
|
|
893
|
-
"archive": cmd_archive,
|
|
894
|
-
"prepare-archive-evidence": cmd_prepare_archive_evidence,
|
|
895
|
-
"prepare-learning-scaffold": cmd_prepare_learning_scaffold,
|
|
896
|
-
"add-subtask": cmd_add_subtask,
|
|
897
|
-
"remove-subtask": cmd_remove_subtask,
|
|
898
|
-
"prepare-child-worktree": cmd_prepare_child_worktree,
|
|
899
|
-
"set-child-state": cmd_set_child_state,
|
|
900
|
-
"integrate-child": cmd_integrate_child,
|
|
901
|
-
"generate-child-prompt": cmd_generate_child_prompt,
|
|
902
|
-
"generate-dispatch-prompt": cmd_generate_dispatch_prompt,
|
|
903
|
-
"suggest-execution-strategy": cmd_suggest_execution_strategy,
|
|
904
|
-
"parent-status": cmd_parent_status,
|
|
905
|
-
"review-child": cmd_review_child,
|
|
906
|
-
"list": cmd_list,
|
|
907
|
-
"list-archive": cmd_list_archive,
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
if args.command in commands:
|
|
911
|
-
return commands[args.command](args)
|
|
912
|
-
else:
|
|
913
|
-
show_usage()
|
|
914
|
-
return 1
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
if __name__ == "__main__":
|
|
918
|
-
sys.exit(main())
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Task Management Script.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
python task.py create "<title>" [--slug <name>] [--assignee <dev>] [--priority P0|P1|P2|P3] [--parent <dir>] [--package <pkg>]
|
|
8
|
+
python task.py add-context <dir> <file> <path> [reason] # Add jsonl entry
|
|
9
|
+
python task.py validate <dir> # Validate jsonl files
|
|
10
|
+
python task.py list-context <dir> # List jsonl entries
|
|
11
|
+
python task.py dashboard # Show Task Dashboard
|
|
12
|
+
python task.py select <dir> # Select task for this live session
|
|
13
|
+
python task.py selected [--source] # Show selected task
|
|
14
|
+
python task.py start-execution <dir> --approved # Start approved execution
|
|
15
|
+
python task.py record-gate <dir> --transition <key> --gate <gate> --result PASS|FAIL|SKIPPED --reviewer <id> --evidence <ref> [--root-cause <cause>]
|
|
16
|
+
python task.py exit # Clear selected task
|
|
17
|
+
python task.py set-branch <dir> <branch> # Set git branch
|
|
18
|
+
python task.py set-base-branch <dir> <branch> # Set PR target branch
|
|
19
|
+
python task.py set-scope <dir> <scope> # Set scope for PR title
|
|
20
|
+
python task.py archive <task-dir> [--check] [--archive-integrated-children] # Check or archive completed task
|
|
21
|
+
python task.py prepare-archive-evidence <task-dir> [--dry-run] # Draft missing verify.md archive evidence
|
|
22
|
+
python task.py prepare-learning-scaffold <task-dir> [--trigger <text>] # Print spec-capture checklist (stdout only)
|
|
23
|
+
python task.py list # List active tasks
|
|
24
|
+
python task.py list-archive [month] # List archived tasks
|
|
25
|
+
python task.py add-subtask <parent-dir> <child-dir> # Link child to parent
|
|
26
|
+
python task.py remove-subtask <parent-dir> <child-dir> # Unlink child from parent
|
|
27
|
+
python task.py prepare-child-worktree <parent-dir> <child-dir> --branch <branch>
|
|
28
|
+
python task.py set-child-state <parent-dir> <child-dir> <state> --evidence <ref>
|
|
29
|
+
python task.py integrate-child <parent-dir> <child-dir> <state> --evidence <ref>
|
|
30
|
+
python task.py generate-child-prompt <parent-dir> <child-dir> [--mode inline|subagent]
|
|
31
|
+
python task.py generate-dispatch-prompt <task-dir> <role> [--scope TEXT] [--finish] [--max-chars N]
|
|
32
|
+
python task.py parent-status <parent-dir>
|
|
33
|
+
python task.py review-child <parent-dir> <child-dir> [--check] [--decision accept|changes|cancel|integrate-through]
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
|
|
38
|
+
import argparse
|
|
39
|
+
import sys
|
|
40
|
+
from datetime import datetime, timezone
|
|
41
|
+
from pathlib import Path
|
|
42
|
+
|
|
43
|
+
from common.log import Colors, colored
|
|
44
|
+
from common.paths import (
|
|
45
|
+
DIR_WORKFLOW,
|
|
46
|
+
DIR_TASKS,
|
|
47
|
+
FILE_TASK_JSON,
|
|
48
|
+
get_repo_root,
|
|
49
|
+
get_developer,
|
|
50
|
+
get_tasks_dir,
|
|
51
|
+
)
|
|
52
|
+
from common.active_task import (
|
|
53
|
+
clear_selected_task,
|
|
54
|
+
resolve_context_key,
|
|
55
|
+
resolve_selected_task,
|
|
56
|
+
set_selected_task,
|
|
57
|
+
)
|
|
58
|
+
from common.io import read_json, write_json
|
|
59
|
+
from common.task_dashboard import render_task_dashboard
|
|
60
|
+
from common.cli_environment import optional_capability_note
|
|
61
|
+
from common.task_gates import (
|
|
62
|
+
BASELINE_GATE,
|
|
63
|
+
build_reviewer_gate_record,
|
|
64
|
+
read_strategy_contract,
|
|
65
|
+
start_execution_repair_hints,
|
|
66
|
+
validate_start_execution,
|
|
67
|
+
validate_start_execution_check,
|
|
68
|
+
write_gate_record,
|
|
69
|
+
)
|
|
70
|
+
from common.task_utils import resolve_task_dir, run_task_hooks
|
|
71
|
+
from common.tasks import (
|
|
72
|
+
children_progress,
|
|
73
|
+
format_child_task_display,
|
|
74
|
+
iter_active_tasks,
|
|
75
|
+
load_parent_child_integration_states,
|
|
76
|
+
)
|
|
77
|
+
from common.task_map import get_child_state
|
|
78
|
+
|
|
79
|
+
# Import command handlers from split modules (also re-exports for plan.py compatibility)
|
|
80
|
+
from common.task_store import (
|
|
81
|
+
cmd_create,
|
|
82
|
+
cmd_archive,
|
|
83
|
+
cmd_prepare_archive_evidence,
|
|
84
|
+
cmd_prepare_learning_scaffold,
|
|
85
|
+
cmd_set_branch,
|
|
86
|
+
cmd_set_base_branch,
|
|
87
|
+
cmd_set_scope,
|
|
88
|
+
cmd_add_subtask,
|
|
89
|
+
cmd_remove_subtask,
|
|
90
|
+
cmd_prepare_child_worktree,
|
|
91
|
+
cmd_set_child_state,
|
|
92
|
+
cmd_integrate_child,
|
|
93
|
+
cmd_generate_child_prompt,
|
|
94
|
+
cmd_generate_dispatch_prompt,
|
|
95
|
+
cmd_suggest_execution_strategy,
|
|
96
|
+
cmd_parent_status,
|
|
97
|
+
cmd_review_child,
|
|
98
|
+
)
|
|
99
|
+
from common.task_context import (
|
|
100
|
+
cmd_add_context,
|
|
101
|
+
cmd_validate,
|
|
102
|
+
cmd_list_context,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# =============================================================================
|
|
107
|
+
# Command: dashboard / select / selected / start-execution / record-gate / exit
|
|
108
|
+
# =============================================================================
|
|
109
|
+
|
|
110
|
+
def _repo_relative(path, repo_root) -> str:
|
|
111
|
+
try:
|
|
112
|
+
return path.relative_to(repo_root).as_posix()
|
|
113
|
+
except ValueError:
|
|
114
|
+
return str(path)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _print_select_dispatch_fallback_tip() -> None:
|
|
118
|
+
print(
|
|
119
|
+
"Tip: if no session identity, use "
|
|
120
|
+
"'python ./.cstl/scripts/generate_dispatch_prompt.py --task <path>' "
|
|
121
|
+
"to dispatch directly.",
|
|
122
|
+
file=sys.stderr,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _resolve_existing_task(task_input: str, repo_root):
|
|
127
|
+
full_path = resolve_task_dir(task_input, repo_root)
|
|
128
|
+
if not full_path.is_dir():
|
|
129
|
+
print(colored(f"Error: Task not found: {task_input}", Colors.RED), file=sys.stderr)
|
|
130
|
+
print("Hint: Use task name (e.g., 'my-task') or full path (e.g., '.cstl/tasks/01-31-my-task')", file=sys.stderr)
|
|
131
|
+
return None
|
|
132
|
+
return full_path
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def cmd_dashboard(args: argparse.Namespace) -> int:
|
|
136
|
+
"""Show Task Dashboard without mutating selection or status."""
|
|
137
|
+
_ = args
|
|
138
|
+
print(render_task_dashboard(get_repo_root()))
|
|
139
|
+
return 0
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def cmd_select(args: argparse.Namespace) -> int:
|
|
143
|
+
"""Select a task for this live session without changing task.status."""
|
|
144
|
+
repo_root = get_repo_root()
|
|
145
|
+
task_input = args.dir
|
|
146
|
+
full_path = _resolve_existing_task(task_input, repo_root)
|
|
147
|
+
if full_path is None:
|
|
148
|
+
_print_select_dispatch_fallback_tip()
|
|
149
|
+
return 1
|
|
150
|
+
|
|
151
|
+
if not resolve_context_key():
|
|
152
|
+
print(
|
|
153
|
+
colored("Error: session identity not available; selected_task was not persisted.", Colors.RED),
|
|
154
|
+
file=sys.stderr,
|
|
155
|
+
)
|
|
156
|
+
print(
|
|
157
|
+
"Hint: run inside an AI session that exposes session identity, or set TRELLIS_CONTEXT_ID before running task.py select.",
|
|
158
|
+
file=sys.stderr,
|
|
159
|
+
)
|
|
160
|
+
_print_select_dispatch_fallback_tip()
|
|
161
|
+
return 1
|
|
162
|
+
|
|
163
|
+
selected = set_selected_task(_repo_relative(full_path, repo_root), repo_root)
|
|
164
|
+
if not selected:
|
|
165
|
+
print(colored("Error: failed to select task", Colors.RED), file=sys.stderr)
|
|
166
|
+
_print_select_dispatch_fallback_tip()
|
|
167
|
+
return 1
|
|
168
|
+
|
|
169
|
+
print(colored(f"✓ Selected task: {selected.task_path}", Colors.GREEN))
|
|
170
|
+
print(f"Source: {selected.source}")
|
|
171
|
+
print("Task status unchanged.")
|
|
172
|
+
return 0
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _print_no_selected_task_guidance() -> None:
|
|
176
|
+
"""Explain why no task is selected and what to run next."""
|
|
177
|
+
print(colored("No task selected for this live session.", Colors.YELLOW), file=sys.stderr)
|
|
178
|
+
print("Next actions:", file=sys.stderr)
|
|
179
|
+
print(
|
|
180
|
+
" - Route work: python ./.cstl/scripts/task.py dashboard",
|
|
181
|
+
file=sys.stderr,
|
|
182
|
+
)
|
|
183
|
+
print(
|
|
184
|
+
" - Select a task: python ./.cstl/scripts/task.py select <task-dir>",
|
|
185
|
+
file=sys.stderr,
|
|
186
|
+
)
|
|
187
|
+
print(
|
|
188
|
+
" - List active tasks: python ./.cstl/scripts/task.py list",
|
|
189
|
+
file=sys.stderr,
|
|
190
|
+
)
|
|
191
|
+
print(
|
|
192
|
+
" - Persist selection in shells: set TRELLIS_CONTEXT_ID (or use your platform session hook)",
|
|
193
|
+
file=sys.stderr,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def cmd_selected(args: argparse.Namespace) -> int:
|
|
198
|
+
"""Show selected task."""
|
|
199
|
+
repo_root = get_repo_root()
|
|
200
|
+
selected = resolve_selected_task(repo_root)
|
|
201
|
+
|
|
202
|
+
if args.source:
|
|
203
|
+
print(f"Selected task: {selected.task_path or '(none)'}")
|
|
204
|
+
print(f"Source: {selected.source}")
|
|
205
|
+
if selected.stale:
|
|
206
|
+
print("State: stale")
|
|
207
|
+
if not selected.task_path:
|
|
208
|
+
_print_no_selected_task_guidance()
|
|
209
|
+
return 0 if selected.task_path else 1
|
|
210
|
+
|
|
211
|
+
if selected.task_path:
|
|
212
|
+
print(selected.task_path)
|
|
213
|
+
return 0
|
|
214
|
+
|
|
215
|
+
_print_no_selected_task_guidance()
|
|
216
|
+
return 1
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def cmd_exit(args: argparse.Namespace) -> int:
|
|
220
|
+
"""Clear selected task for this live session without changing task.status."""
|
|
221
|
+
_ = args
|
|
222
|
+
repo_root = get_repo_root()
|
|
223
|
+
selected = clear_selected_task(repo_root)
|
|
224
|
+
if not selected.task_path:
|
|
225
|
+
print(colored("No selected task set", Colors.YELLOW))
|
|
226
|
+
return 0
|
|
227
|
+
|
|
228
|
+
print(colored(f"✓ Cleared selected task (was: {selected.task_path})", Colors.GREEN))
|
|
229
|
+
print(f"Source: {selected.source}")
|
|
230
|
+
print("Task status unchanged.")
|
|
231
|
+
return 0
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _print_guard_errors(items: list[str], stream=None) -> None:
|
|
235
|
+
if stream is None:
|
|
236
|
+
stream = sys.stdout
|
|
237
|
+
for item in items:
|
|
238
|
+
print(f" - {item}", file=stream)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def cmd_start_execution(args: argparse.Namespace) -> int:
|
|
242
|
+
"""Start approved task execution after a non-mutating readiness check."""
|
|
243
|
+
repo_root = get_repo_root()
|
|
244
|
+
task_dir = _resolve_existing_task(args.dir, repo_root)
|
|
245
|
+
if task_dir is None:
|
|
246
|
+
return 1
|
|
247
|
+
|
|
248
|
+
task_json_path = task_dir / FILE_TASK_JSON
|
|
249
|
+
task_data = read_json(task_json_path) if task_json_path.is_file() else None
|
|
250
|
+
|
|
251
|
+
if args.check:
|
|
252
|
+
guard = validate_start_execution_check(task_dir, task_data)
|
|
253
|
+
if not guard.ok:
|
|
254
|
+
print(colored("Start-execution check: FAIL", Colors.RED))
|
|
255
|
+
_print_guard_errors(guard.errors)
|
|
256
|
+
for hint in start_execution_repair_hints(guard.errors, task_dir):
|
|
257
|
+
print(f" Hint: {hint}")
|
|
258
|
+
return 1
|
|
259
|
+
print(colored("Start-execution check: PASS", Colors.GREEN))
|
|
260
|
+
print(f"Contract fingerprint: {guard.contract_fingerprint}")
|
|
261
|
+
baseline_fingerprint = guard.artifact_fingerprints.get(BASELINE_GATE)
|
|
262
|
+
if baseline_fingerprint:
|
|
263
|
+
print(f"Artifact fingerprint: {baseline_fingerprint}")
|
|
264
|
+
if guard.required_gates:
|
|
265
|
+
print(f"Required reviewer gates: {', '.join(guard.required_gates)}")
|
|
266
|
+
if task_dir is not None:
|
|
267
|
+
contract, _ = read_strategy_contract(task_dir)
|
|
268
|
+
cap_note = optional_capability_note(contract.get("optional_capabilities"))
|
|
269
|
+
if cap_note:
|
|
270
|
+
print(colored(f"Note: {cap_note}", Colors.YELLOW))
|
|
271
|
+
from common.execution_strategy import (
|
|
272
|
+
contract_drift_warnings,
|
|
273
|
+
validate_strategy_pair,
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
if contract:
|
|
277
|
+
for item in contract_drift_warnings(
|
|
278
|
+
repo_root, task_dir, task_data or {}, contract
|
|
279
|
+
):
|
|
280
|
+
print(f"[execution-strategy] WARN: {item}", file=sys.stderr)
|
|
281
|
+
mode = contract.get("execution_mode")
|
|
282
|
+
iso = contract.get("isolation")
|
|
283
|
+
if isinstance(mode, str) and isinstance(iso, str):
|
|
284
|
+
for item in validate_strategy_pair(mode, iso):
|
|
285
|
+
print(f"[execution-strategy] WARN: {item}", file=sys.stderr)
|
|
286
|
+
print("Artifact gates are ready. Ask the user for explicit execution approval before running `task.py start-execution <task> --approved`.")
|
|
287
|
+
return 0
|
|
288
|
+
|
|
289
|
+
if not args.approved:
|
|
290
|
+
print(colored("Error: no action selected for start-execution.", Colors.RED), file=sys.stderr)
|
|
291
|
+
print("Run with --check for non-mutating preflight or --approved after explicit user approval.", file=sys.stderr)
|
|
292
|
+
return 1
|
|
293
|
+
|
|
294
|
+
guard = validate_start_execution(task_dir, task_data, approved=True)
|
|
295
|
+
if not guard.ok:
|
|
296
|
+
print(colored("Error: cannot start execution; readiness check failed.", Colors.RED), file=sys.stderr)
|
|
297
|
+
_print_guard_errors(guard.errors, stream=sys.stderr)
|
|
298
|
+
return 1
|
|
299
|
+
|
|
300
|
+
assert task_data is not None
|
|
301
|
+
if guard.baseline_record:
|
|
302
|
+
write_gate_record(task_data, "start-execution", BASELINE_GATE, guard.baseline_record)
|
|
303
|
+
for gate, record in guard.auto_gate_records.items():
|
|
304
|
+
write_gate_record(task_data, "start-execution", gate, record)
|
|
305
|
+
task_data["execution_approval"] = {
|
|
306
|
+
"schema_version": 1,
|
|
307
|
+
"transition": "start-execution",
|
|
308
|
+
"approved_at": datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z"),
|
|
309
|
+
"approved_by": "user",
|
|
310
|
+
"approval_source": "task.py start-execution --approved",
|
|
311
|
+
"contract_fingerprint": guard.contract_fingerprint,
|
|
312
|
+
"artifact_fingerprint": guard.artifact_fingerprints.get(BASELINE_GATE),
|
|
313
|
+
}
|
|
314
|
+
if task_data.get("status") == "planning":
|
|
315
|
+
task_data["status"] = "in_progress"
|
|
316
|
+
if not write_json(task_json_path, task_data):
|
|
317
|
+
print(colored("Error: failed to write task.json", Colors.RED), file=sys.stderr)
|
|
318
|
+
return 1
|
|
319
|
+
|
|
320
|
+
print(colored(f"✓ Execution approved for: {_repo_relative(task_dir, repo_root)}", Colors.GREEN))
|
|
321
|
+
print(f"Status: {task_data.get('status')}")
|
|
322
|
+
run_task_hooks("after_start", task_json_path, repo_root)
|
|
323
|
+
return 0
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def cmd_record_gate(args: argparse.Namespace) -> int:
|
|
327
|
+
"""Record a non-baseline reviewer gate result."""
|
|
328
|
+
repo_root = get_repo_root()
|
|
329
|
+
task_dir = _resolve_existing_task(args.dir, repo_root)
|
|
330
|
+
if task_dir is None:
|
|
331
|
+
return 1
|
|
332
|
+
|
|
333
|
+
task_json_path = task_dir / FILE_TASK_JSON
|
|
334
|
+
task_data = read_json(task_json_path) if task_json_path.is_file() else None
|
|
335
|
+
if task_data is None:
|
|
336
|
+
print(colored("Error: task.json not found or invalid", Colors.RED), file=sys.stderr)
|
|
337
|
+
return 1
|
|
338
|
+
|
|
339
|
+
record, errors, warnings = build_reviewer_gate_record(
|
|
340
|
+
task_dir=task_dir,
|
|
341
|
+
task_data=task_data,
|
|
342
|
+
transition=args.transition,
|
|
343
|
+
gate=args.gate,
|
|
344
|
+
result=args.result,
|
|
345
|
+
reviewer=args.reviewer,
|
|
346
|
+
evidence=args.evidence,
|
|
347
|
+
issue_fingerprint=args.issue_fingerprint,
|
|
348
|
+
issue_summary=args.issue_summary,
|
|
349
|
+
root_cause=args.root_cause,
|
|
350
|
+
skip_approved_by=args.skip_approved_by,
|
|
351
|
+
skip_reason=args.skip_reason,
|
|
352
|
+
contract_fingerprint=args.contract_fingerprint,
|
|
353
|
+
artifact_fingerprint=args.artifact_fingerprint,
|
|
354
|
+
)
|
|
355
|
+
if errors:
|
|
356
|
+
print(colored("Error: cannot record quality gate.", Colors.RED), file=sys.stderr)
|
|
357
|
+
_print_guard_errors(errors, stream=sys.stderr)
|
|
358
|
+
return 1
|
|
359
|
+
|
|
360
|
+
assert record is not None
|
|
361
|
+
write_gate_record(task_data, args.transition, args.gate, record)
|
|
362
|
+
if not write_json(task_json_path, task_data):
|
|
363
|
+
print(colored("Error: failed to write task.json", Colors.RED), file=sys.stderr)
|
|
364
|
+
return 1
|
|
365
|
+
|
|
366
|
+
print(colored(f"✓ Recorded gate: {args.transition}/{args.gate} = {record['result']}", Colors.GREEN))
|
|
367
|
+
print(f"Evidence: {record['evidence']}")
|
|
368
|
+
print(f"Contract fingerprint: {record['contract_fingerprint']}")
|
|
369
|
+
print(f"Artifact fingerprint: {record['artifact_fingerprint']}")
|
|
370
|
+
if record.get("route"):
|
|
371
|
+
print(f"Route: {record['route']}")
|
|
372
|
+
for warning in warnings:
|
|
373
|
+
print(colored(f"Warning: {warning}", Colors.YELLOW), file=sys.stderr)
|
|
374
|
+
return 0
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
# =============================================================================
|
|
378
|
+
# Command: list
|
|
379
|
+
# =============================================================================
|
|
380
|
+
|
|
381
|
+
def cmd_list(args: argparse.Namespace) -> int:
|
|
382
|
+
"""List active tasks."""
|
|
383
|
+
repo_root = get_repo_root()
|
|
384
|
+
tasks_dir = get_tasks_dir(repo_root)
|
|
385
|
+
selected_task = resolve_selected_task(repo_root).task_path
|
|
386
|
+
developer = get_developer(repo_root)
|
|
387
|
+
filter_mine = args.mine
|
|
388
|
+
filter_status = args.status
|
|
389
|
+
|
|
390
|
+
if filter_mine:
|
|
391
|
+
if not developer:
|
|
392
|
+
print(colored("Error: No developer set. Run init_developer.py first", Colors.RED), file=sys.stderr)
|
|
393
|
+
return 1
|
|
394
|
+
print(colored(f"My tasks (assignee: {developer}):", Colors.BLUE))
|
|
395
|
+
else:
|
|
396
|
+
print(colored("All active tasks:", Colors.BLUE))
|
|
397
|
+
print()
|
|
398
|
+
|
|
399
|
+
# Single pass: collect all tasks via shared iterator
|
|
400
|
+
all_tasks = {t.dir_name: t for t in iter_active_tasks(tasks_dir)}
|
|
401
|
+
all_statuses = {name: t.status for name, t in all_tasks.items()}
|
|
402
|
+
|
|
403
|
+
# Display tasks hierarchically
|
|
404
|
+
count = 0
|
|
405
|
+
|
|
406
|
+
def _print_task(
|
|
407
|
+
dir_name: str,
|
|
408
|
+
indent: int = 0,
|
|
409
|
+
parent_dir: Path | None = None,
|
|
410
|
+
) -> None:
|
|
411
|
+
nonlocal count
|
|
412
|
+
t = all_tasks[dir_name]
|
|
413
|
+
|
|
414
|
+
# Apply --mine filter
|
|
415
|
+
if filter_mine and (t.assignee or "-") != developer:
|
|
416
|
+
return
|
|
417
|
+
|
|
418
|
+
# Apply --status filter
|
|
419
|
+
if filter_status and t.status != filter_status:
|
|
420
|
+
return
|
|
421
|
+
|
|
422
|
+
relative_path = f"{DIR_WORKFLOW}/{DIR_TASKS}/{dir_name}"
|
|
423
|
+
marker = ""
|
|
424
|
+
if relative_path == selected_task:
|
|
425
|
+
marker = f" {colored('<- selected', Colors.GREEN)}"
|
|
426
|
+
|
|
427
|
+
integration_states = None
|
|
428
|
+
if t.children:
|
|
429
|
+
integration_states = load_parent_child_integration_states(
|
|
430
|
+
t.directory, t.children
|
|
431
|
+
)
|
|
432
|
+
progress = children_progress(
|
|
433
|
+
t.children, all_statuses, integration_states
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
integration_state = None
|
|
437
|
+
if parent_dir is not None:
|
|
438
|
+
integration_state = get_child_state(parent_dir, dir_name)
|
|
439
|
+
status_display = format_child_task_display(t.status, integration_state)
|
|
440
|
+
|
|
441
|
+
# Package tag
|
|
442
|
+
pkg_tag = f" @{t.package}" if t.package else ""
|
|
443
|
+
|
|
444
|
+
prefix = " " * indent + " - "
|
|
445
|
+
|
|
446
|
+
if filter_mine:
|
|
447
|
+
print(
|
|
448
|
+
f"{prefix}{dir_name}/ ({status_display}){pkg_tag}{progress}{marker}"
|
|
449
|
+
)
|
|
450
|
+
else:
|
|
451
|
+
print(
|
|
452
|
+
f"{prefix}{dir_name}/ ({status_display}){pkg_tag}{progress} "
|
|
453
|
+
f"[{colored(t.assignee or '-', Colors.CYAN)}]{marker}"
|
|
454
|
+
)
|
|
455
|
+
count += 1
|
|
456
|
+
|
|
457
|
+
# Print children indented
|
|
458
|
+
for child_name in t.children:
|
|
459
|
+
if child_name in all_tasks:
|
|
460
|
+
_print_task(child_name, indent + 1, parent_dir=t.directory)
|
|
461
|
+
|
|
462
|
+
# Display only top-level tasks (those without a parent)
|
|
463
|
+
for dir_name in sorted(all_tasks.keys()):
|
|
464
|
+
if not all_tasks[dir_name].parent:
|
|
465
|
+
_print_task(dir_name)
|
|
466
|
+
|
|
467
|
+
if count == 0:
|
|
468
|
+
if filter_mine:
|
|
469
|
+
print(" (no tasks assigned to you)")
|
|
470
|
+
else:
|
|
471
|
+
print(" (no active tasks)")
|
|
472
|
+
|
|
473
|
+
print()
|
|
474
|
+
print(f"Total: {count} task(s)")
|
|
475
|
+
return 0
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
# =============================================================================
|
|
479
|
+
# Command: list-archive
|
|
480
|
+
# =============================================================================
|
|
481
|
+
|
|
482
|
+
def cmd_list_archive(args: argparse.Namespace) -> int:
|
|
483
|
+
"""List archived tasks."""
|
|
484
|
+
repo_root = get_repo_root()
|
|
485
|
+
tasks_dir = get_tasks_dir(repo_root)
|
|
486
|
+
archive_dir = tasks_dir / "archive"
|
|
487
|
+
month = args.month
|
|
488
|
+
|
|
489
|
+
print(colored("Archived tasks:", Colors.BLUE))
|
|
490
|
+
print()
|
|
491
|
+
|
|
492
|
+
if month:
|
|
493
|
+
month_dir = archive_dir / month
|
|
494
|
+
if month_dir.is_dir():
|
|
495
|
+
print(f"[{month}]")
|
|
496
|
+
for d in sorted(month_dir.iterdir()):
|
|
497
|
+
if d.is_dir():
|
|
498
|
+
print(f" - {d.name}/")
|
|
499
|
+
else:
|
|
500
|
+
print(f" No archives for {month}")
|
|
501
|
+
else:
|
|
502
|
+
if archive_dir.is_dir():
|
|
503
|
+
for month_dir in sorted(archive_dir.iterdir()):
|
|
504
|
+
if month_dir.is_dir():
|
|
505
|
+
month_name = month_dir.name
|
|
506
|
+
count = sum(1 for d in month_dir.iterdir() if d.is_dir())
|
|
507
|
+
print(f"[{month_name}] - {count} task(s)")
|
|
508
|
+
|
|
509
|
+
return 0
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
# =============================================================================
|
|
513
|
+
# Help
|
|
514
|
+
# =============================================================================
|
|
515
|
+
|
|
516
|
+
def show_usage() -> None:
|
|
517
|
+
"""Show usage help."""
|
|
518
|
+
print("""Task Management Script
|
|
519
|
+
|
|
520
|
+
Usage:
|
|
521
|
+
python task.py create <title> Create new task directory
|
|
522
|
+
python task.py create <title> --package <pkg> Create task for a specific package
|
|
523
|
+
python task.py create <title> --parent <dir> Create task as child of parent
|
|
524
|
+
python task.py add-context <dir> <jsonl> <path> [reason] Add entry to jsonl
|
|
525
|
+
python task.py validate <dir> Validate jsonl files
|
|
526
|
+
python task.py list-context <dir> List jsonl entries
|
|
527
|
+
python task.py dashboard Show Task Dashboard
|
|
528
|
+
python task.py select <dir> Select task for this live session
|
|
529
|
+
python task.py selected [--source] Show selected task
|
|
530
|
+
python task.py start-execution <dir> --check Check execution readiness
|
|
531
|
+
python task.py start-execution <dir> --approved Start approved execution
|
|
532
|
+
python task.py record-gate <dir> --transition <key> --gate <gate> --result PASS|FAIL|SKIPPED --reviewer <id> --evidence <ref> [--root-cause <cause>]
|
|
533
|
+
python task.py exit Clear selected task
|
|
534
|
+
python task.py set-branch <dir> <branch> Set git branch
|
|
535
|
+
python task.py set-base-branch <dir> <branch> Set PR target branch
|
|
536
|
+
python task.py set-scope <dir> <scope> Set scope for PR title
|
|
537
|
+
python task.py archive <task-dir> [--check] Check or archive completed task
|
|
538
|
+
python task.py add-subtask <parent> <child> Link child task to parent
|
|
539
|
+
python task.py remove-subtask <parent> <child> Unlink child from parent
|
|
540
|
+
python task.py prepare-child-worktree <parent> <child> --branch <branch>
|
|
541
|
+
python task.py set-child-state <parent> <child> <state> --evidence <ref>
|
|
542
|
+
python task.py integrate-child <parent> <child> <state> --evidence <ref>
|
|
543
|
+
python task.py list [--mine] [--status <status>] List tasks
|
|
544
|
+
python task.py list-archive [YYYY-MM] List archived tasks
|
|
545
|
+
|
|
546
|
+
Monorepo options:
|
|
547
|
+
--package <pkg> Package name (validated against config.yaml packages)
|
|
548
|
+
|
|
549
|
+
List options:
|
|
550
|
+
--mine, -m Show only tasks assigned to current developer
|
|
551
|
+
--status, -s <s> Filter by status (planning, in_progress, review, completed)
|
|
552
|
+
|
|
553
|
+
Examples:
|
|
554
|
+
python task.py create "Add login feature" --slug add-login
|
|
555
|
+
python task.py create "Add login feature" --slug add-login --package cli
|
|
556
|
+
python task.py create "Child task" --slug child --parent .cstl/tasks/01-21-parent
|
|
557
|
+
python task.py add-context <dir> implement .cstl/spec/cli/backend/auth.md "Auth guidelines"
|
|
558
|
+
python task.py set-branch <dir> task/add-login
|
|
559
|
+
python task.py dashboard
|
|
560
|
+
python task.py select .cstl/tasks/01-21-add-login
|
|
561
|
+
python task.py selected --source
|
|
562
|
+
python task.py start-execution .cstl/tasks/01-21-add-login --check
|
|
563
|
+
python task.py start-execution .cstl/tasks/01-21-add-login --approved
|
|
564
|
+
python task.py record-gate .cstl/tasks/01-21-add-login --transition full-task-complete --gate code-review --result FAIL --reviewer codex --evidence verify.md --issue-fingerprint auth-branch-1 --root-cause implementation-defect
|
|
565
|
+
python task.py exit
|
|
566
|
+
python task.py archive add-login --check
|
|
567
|
+
python task.py archive add-login
|
|
568
|
+
python task.py add-subtask parent-task child-task # Link existing tasks
|
|
569
|
+
python task.py remove-subtask parent-task child-task
|
|
570
|
+
python task.py prepare-child-worktree parent-task child-task --branch child-task
|
|
571
|
+
python task.py set-child-state parent-task child-task review --evidence verify.md
|
|
572
|
+
python task.py integrate-child parent-task child-task accepted --evidence handoff.md --ref child-branch
|
|
573
|
+
python task.py integrate-child parent-task child-task integrated --evidence task-map.md --ref child-branch --execute-merge
|
|
574
|
+
python task.py generate-child-prompt parent-task child-task --mode inline
|
|
575
|
+
python task.py parent-status parent-task
|
|
576
|
+
python task.py review-child parent-task child-task --check
|
|
577
|
+
python task.py review-child parent-task child-task --decision accept --ref child-branch
|
|
578
|
+
python task.py list # List all active tasks
|
|
579
|
+
python task.py list --mine # List my tasks only
|
|
580
|
+
python task.py list --mine --status in_progress # List my in-progress tasks
|
|
581
|
+
""")
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
# =============================================================================
|
|
585
|
+
# Main Entry
|
|
586
|
+
# =============================================================================
|
|
587
|
+
|
|
588
|
+
def main() -> int:
|
|
589
|
+
"""CLI entry point."""
|
|
590
|
+
# Deprecation guard: `init-context` was removed in v0.5.0-beta.12.
|
|
591
|
+
# Detect early so argparse doesn't mask the real reason with a generic
|
|
592
|
+
# "invalid choice" error.
|
|
593
|
+
if len(sys.argv) >= 2 and sys.argv[1] == "init-context":
|
|
594
|
+
print(
|
|
595
|
+
colored(
|
|
596
|
+
"Error: `task.py init-context` was removed in v0.5.0-beta.12.",
|
|
597
|
+
Colors.RED,
|
|
598
|
+
),
|
|
599
|
+
file=sys.stderr,
|
|
600
|
+
)
|
|
601
|
+
print(
|
|
602
|
+
"implement.jsonl / check.jsonl are now seeded on `task.py create` for",
|
|
603
|
+
file=sys.stderr,
|
|
604
|
+
)
|
|
605
|
+
print(
|
|
606
|
+
"sub-agent-capable platforms and curated by the AI during planning when needed.",
|
|
607
|
+
file=sys.stderr,
|
|
608
|
+
)
|
|
609
|
+
print("See .cstl/workflow.md planning artifact guidance or run:", file=sys.stderr)
|
|
610
|
+
print(
|
|
611
|
+
" python ./.cstl/scripts/get_context.py --mode phase --step 1",
|
|
612
|
+
file=sys.stderr,
|
|
613
|
+
)
|
|
614
|
+
print(
|
|
615
|
+
"Use `task.py add-context <dir> implement|check <path> <reason>` to append entries.",
|
|
616
|
+
file=sys.stderr,
|
|
617
|
+
)
|
|
618
|
+
return 2
|
|
619
|
+
|
|
620
|
+
parser = argparse.ArgumentParser(
|
|
621
|
+
description="Task Management Script",
|
|
622
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
623
|
+
)
|
|
624
|
+
subparsers = parser.add_subparsers(dest="command", help="Commands")
|
|
625
|
+
|
|
626
|
+
# create
|
|
627
|
+
p_create = subparsers.add_parser("create", help="Create new task")
|
|
628
|
+
p_create.add_argument("title", help="Task title")
|
|
629
|
+
p_create.add_argument("--slug", "-s", help="Task slug")
|
|
630
|
+
p_create.add_argument("--assignee", "-a", help="Assignee developer")
|
|
631
|
+
p_create.add_argument("--priority", "-p", default="P2", help="Priority (P0-P3)")
|
|
632
|
+
p_create.add_argument("--description", "-d", help="Task description")
|
|
633
|
+
p_create.add_argument("--parent", help="Parent task directory (establishes subtask link)")
|
|
634
|
+
p_create.add_argument("--package", help="Package name for monorepo projects")
|
|
635
|
+
|
|
636
|
+
# add-context
|
|
637
|
+
p_add = subparsers.add_parser("add-context", help="Add context entry")
|
|
638
|
+
p_add.add_argument("dir", help="Task directory")
|
|
639
|
+
p_add.add_argument("file", help="JSONL file (implement|check)")
|
|
640
|
+
p_add.add_argument("path", help="File path to add")
|
|
641
|
+
p_add.add_argument("reason", nargs="?", help="Reason for adding")
|
|
642
|
+
|
|
643
|
+
# validate
|
|
644
|
+
p_validate = subparsers.add_parser("validate", help="Validate context files")
|
|
645
|
+
p_validate.add_argument("dir", help="Task directory")
|
|
646
|
+
|
|
647
|
+
# list-context
|
|
648
|
+
p_listctx = subparsers.add_parser("list-context", help="List context entries")
|
|
649
|
+
p_listctx.add_argument("dir", help="Task directory")
|
|
650
|
+
|
|
651
|
+
# dashboard
|
|
652
|
+
subparsers.add_parser("dashboard", help="Show Task Dashboard")
|
|
653
|
+
|
|
654
|
+
# select
|
|
655
|
+
p_select = subparsers.add_parser("select", help="Select task for this live session")
|
|
656
|
+
p_select.add_argument("dir", help="Task directory")
|
|
657
|
+
|
|
658
|
+
# selected
|
|
659
|
+
p_selected = subparsers.add_parser("selected", help="Show selected task")
|
|
660
|
+
p_selected.add_argument("--source", action="store_true",
|
|
661
|
+
help="Show selected task source")
|
|
662
|
+
|
|
663
|
+
# start-execution
|
|
664
|
+
p_start_execution = subparsers.add_parser("start-execution", help="Start approved task execution")
|
|
665
|
+
p_start_execution.add_argument("dir", help="Task directory")
|
|
666
|
+
p_start_execution.add_argument("--check", action="store_true",
|
|
667
|
+
help="Run non-mutating execution readiness check")
|
|
668
|
+
p_start_execution.add_argument("--approved", action="store_true",
|
|
669
|
+
help="Record explicit approval and start execution")
|
|
670
|
+
|
|
671
|
+
# record-gate
|
|
672
|
+
p_record_gate = subparsers.add_parser("record-gate", help="Record reviewer quality gate")
|
|
673
|
+
p_record_gate.add_argument("dir", help="Task directory")
|
|
674
|
+
p_record_gate.add_argument("--transition", required=True, help="Transition key")
|
|
675
|
+
p_record_gate.add_argument("--gate", required=True, help="Gate name")
|
|
676
|
+
p_record_gate.add_argument("--result", required=True, help="PASS, FAIL, or SKIPPED")
|
|
677
|
+
p_record_gate.add_argument("--reviewer", required=True, help="Reviewer identifier")
|
|
678
|
+
p_record_gate.add_argument("--evidence", required=True, help="Short evidence reference")
|
|
679
|
+
p_record_gate.add_argument("--issue-fingerprint", help="Required for FAIL")
|
|
680
|
+
p_record_gate.add_argument("--issue-summary", help="Optional short issue summary for FAIL")
|
|
681
|
+
p_record_gate.add_argument("--root-cause",
|
|
682
|
+
help="Required for FAIL: implementation-defect, contract-changing-defect, or validation-environment-blocker")
|
|
683
|
+
p_record_gate.add_argument("--skip-approved-by", help="Must be 'user' for SKIPPED")
|
|
684
|
+
p_record_gate.add_argument("--skip-reason", help="Required reason for SKIPPED")
|
|
685
|
+
p_record_gate.add_argument("--contract-fingerprint", help="Optional current contract fingerprint assertion")
|
|
686
|
+
p_record_gate.add_argument("--artifact-fingerprint", help="Optional current artifact fingerprint assertion")
|
|
687
|
+
|
|
688
|
+
# exit
|
|
689
|
+
subparsers.add_parser("exit", help="Clear selected task")
|
|
690
|
+
|
|
691
|
+
# set-branch
|
|
692
|
+
p_branch = subparsers.add_parser("set-branch", help="Set git branch")
|
|
693
|
+
p_branch.add_argument("dir", help="Task directory")
|
|
694
|
+
p_branch.add_argument("branch", help="Branch name")
|
|
695
|
+
|
|
696
|
+
# set-base-branch
|
|
697
|
+
p_base = subparsers.add_parser("set-base-branch", help="Set PR target branch")
|
|
698
|
+
p_base.add_argument("dir", help="Task directory")
|
|
699
|
+
p_base.add_argument("base_branch", help="Base branch name (PR target)")
|
|
700
|
+
|
|
701
|
+
# set-scope
|
|
702
|
+
p_scope = subparsers.add_parser("set-scope", help="Set scope")
|
|
703
|
+
p_scope.add_argument("dir", help="Task directory")
|
|
704
|
+
p_scope.add_argument("scope", help="Scope name")
|
|
705
|
+
|
|
706
|
+
# archive
|
|
707
|
+
p_archive = subparsers.add_parser("archive", help="Archive task")
|
|
708
|
+
p_archive.add_argument("name", help="Task directory or name")
|
|
709
|
+
p_archive.add_argument("--check", action="store_true", help="Run non-mutating archive readiness check")
|
|
710
|
+
p_archive.add_argument("--no-commit", action="store_true", help="Skip auto git commit after archive")
|
|
711
|
+
p_archive.add_argument(
|
|
712
|
+
"--archive-integrated-children",
|
|
713
|
+
action="store_true",
|
|
714
|
+
help="When archiving a parent, also archive integrated children that pass archive --check",
|
|
715
|
+
)
|
|
716
|
+
|
|
717
|
+
# prepare-archive-evidence
|
|
718
|
+
p_prepare_archive = subparsers.add_parser(
|
|
719
|
+
"prepare-archive-evidence",
|
|
720
|
+
help="Append missing archive evidence sections to verify.md",
|
|
721
|
+
)
|
|
722
|
+
p_prepare_archive.add_argument("name", help="Task directory or name")
|
|
723
|
+
p_prepare_archive.add_argument(
|
|
724
|
+
"--dry-run",
|
|
725
|
+
action="store_true",
|
|
726
|
+
help="Show what would be appended without writing verify.md",
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
# prepare-learning-scaffold
|
|
730
|
+
p_learning_scaffold = subparsers.add_parser(
|
|
731
|
+
"prepare-learning-scaffold",
|
|
732
|
+
help="Print durable-learning / spec-update checklist (does not edit specs)",
|
|
733
|
+
)
|
|
734
|
+
p_learning_scaffold.add_argument("name", help="Task directory or name")
|
|
735
|
+
p_learning_scaffold.add_argument(
|
|
736
|
+
"--trigger",
|
|
737
|
+
help="Optional reason (e.g. parent review changes, repeated workflow bug)",
|
|
738
|
+
)
|
|
739
|
+
|
|
740
|
+
# list
|
|
741
|
+
p_list = subparsers.add_parser("list", help="List tasks")
|
|
742
|
+
p_list.add_argument("--mine", "-m", action="store_true", help="My tasks only")
|
|
743
|
+
p_list.add_argument("--status", "-s", help="Filter by status")
|
|
744
|
+
|
|
745
|
+
# add-subtask
|
|
746
|
+
p_addsub = subparsers.add_parser("add-subtask", help="Link child task to parent")
|
|
747
|
+
p_addsub.add_argument("parent_dir", help="Parent task directory")
|
|
748
|
+
p_addsub.add_argument("child_dir", help="Child task directory")
|
|
749
|
+
|
|
750
|
+
# remove-subtask
|
|
751
|
+
p_rmsub = subparsers.add_parser("remove-subtask", help="Unlink child task from parent")
|
|
752
|
+
p_rmsub.add_argument("parent_dir", help="Parent task directory")
|
|
753
|
+
p_rmsub.add_argument("child_dir", help="Child task directory")
|
|
754
|
+
|
|
755
|
+
# prepare-child-worktree
|
|
756
|
+
p_prepare_worktree = subparsers.add_parser("prepare-child-worktree", help="Create/register a Child git worktree")
|
|
757
|
+
p_prepare_worktree.add_argument("parent_dir", help="Parent task directory")
|
|
758
|
+
p_prepare_worktree.add_argument("child_dir", help="Child task directory")
|
|
759
|
+
p_prepare_worktree.add_argument("--branch", required=True, help="Child git branch to create or checkout")
|
|
760
|
+
p_prepare_worktree.add_argument("--base", help="Base ref for a new Child branch")
|
|
761
|
+
p_prepare_worktree.add_argument("--path", help="Worktree path under .cstl/worktrees/")
|
|
762
|
+
p_prepare_worktree.add_argument("--check", action="store_true", help="Run non-mutating worktree readiness check")
|
|
763
|
+
|
|
764
|
+
# set-child-state
|
|
765
|
+
p_child_state = subparsers.add_parser("set-child-state", help="Set Child Worker state in Parent task-map.md")
|
|
766
|
+
p_child_state.add_argument("parent_dir", help="Parent task directory")
|
|
767
|
+
p_child_state.add_argument("child_dir", help="Child task directory")
|
|
768
|
+
p_child_state.add_argument("state", help="Child state")
|
|
769
|
+
p_child_state.add_argument("--evidence", required=True, help="Short evidence reference")
|
|
770
|
+
p_child_state.add_argument("--reason", help="Optional short reason")
|
|
771
|
+
|
|
772
|
+
# integrate-child
|
|
773
|
+
p_integrate_child = subparsers.add_parser("integrate-child", help="Set Parent-controlled Child integration state")
|
|
774
|
+
p_integrate_child.add_argument("parent_dir", help="Parent task directory")
|
|
775
|
+
p_integrate_child.add_argument("child_dir", help="Child task directory")
|
|
776
|
+
p_integrate_child.add_argument("state", help="Parent-controlled Child state")
|
|
777
|
+
p_integrate_child.add_argument("--evidence", required=True, help="Short evidence reference")
|
|
778
|
+
p_integrate_child.add_argument("--ref", help="Child git ref or reviewed diff reference")
|
|
779
|
+
p_integrate_child.add_argument("--reason", help="Optional short reason")
|
|
780
|
+
p_integrate_child.add_argument("--execute-merge", action="store_true", help="Execute git merge --no-ff --no-commit for an integrated Child")
|
|
781
|
+
p_integrate_child.add_argument("--check", action="store_true", help="Run non-mutating integration readiness check")
|
|
782
|
+
|
|
783
|
+
# suggest-execution-strategy
|
|
784
|
+
p_suggest_strategy = subparsers.add_parser(
|
|
785
|
+
"suggest-execution-strategy",
|
|
786
|
+
help="Suggest execution_mode and isolation for Development Strategy Contract",
|
|
787
|
+
)
|
|
788
|
+
p_suggest_strategy.add_argument("task_dir", help="Task directory")
|
|
789
|
+
p_suggest_strategy.add_argument(
|
|
790
|
+
"--json",
|
|
791
|
+
action="store_true",
|
|
792
|
+
help="Emit machine-readable JSON",
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
# generate-dispatch-prompt
|
|
796
|
+
p_dispatch_prompt = subparsers.add_parser(
|
|
797
|
+
"generate-dispatch-prompt",
|
|
798
|
+
help="Build full Task dispatch prompt (Agent-facing)",
|
|
799
|
+
)
|
|
800
|
+
p_dispatch_prompt.add_argument("task_dir", help="Task directory")
|
|
801
|
+
p_dispatch_prompt.add_argument(
|
|
802
|
+
"role",
|
|
803
|
+
choices=["implement", "check", "research"],
|
|
804
|
+
help="Subagent role",
|
|
805
|
+
)
|
|
806
|
+
p_dispatch_prompt.add_argument("--scope", help="One-line task instruction for subagent")
|
|
807
|
+
p_dispatch_prompt.add_argument(
|
|
808
|
+
"--finish",
|
|
809
|
+
action="store_true",
|
|
810
|
+
help="Use finish check context (role=check only)",
|
|
811
|
+
)
|
|
812
|
+
p_dispatch_prompt.add_argument(
|
|
813
|
+
"--max-chars",
|
|
814
|
+
type=int,
|
|
815
|
+
help="Hard truncate embedded context block",
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
# generate-child-prompt
|
|
819
|
+
p_gen_prompt = subparsers.add_parser(
|
|
820
|
+
"generate-child-prompt",
|
|
821
|
+
help="Generate child implementation prompt for parent orchestration",
|
|
822
|
+
)
|
|
823
|
+
p_gen_prompt.add_argument("parent_dir", help="Parent task directory")
|
|
824
|
+
p_gen_prompt.add_argument("child_dir", help="Child task directory")
|
|
825
|
+
p_gen_prompt.add_argument(
|
|
826
|
+
"--mode",
|
|
827
|
+
choices=["inline", "subagent"],
|
|
828
|
+
default="inline",
|
|
829
|
+
help="Delivery mode hint (inline manual handoff vs optional subagent)",
|
|
830
|
+
)
|
|
831
|
+
p_gen_prompt.add_argument(
|
|
832
|
+
"--include-artifacts",
|
|
833
|
+
action="store_true",
|
|
834
|
+
help="Embed child artifact bodies in the generated prompt",
|
|
835
|
+
)
|
|
836
|
+
p_gen_prompt.add_argument("--output", "-o", help="Write prompt to file instead of stdout")
|
|
837
|
+
|
|
838
|
+
# parent-status
|
|
839
|
+
p_parent_status = subparsers.add_parser("parent-status", help="Show parent task-map orchestration status")
|
|
840
|
+
p_parent_status.add_argument("parent_dir", help="Parent task directory")
|
|
841
|
+
|
|
842
|
+
# review-child
|
|
843
|
+
p_review_child = subparsers.add_parser(
|
|
844
|
+
"review-child",
|
|
845
|
+
help="Review child handoff and optionally advance integration states",
|
|
846
|
+
)
|
|
847
|
+
p_review_child.add_argument("parent_dir", help="Parent task directory")
|
|
848
|
+
p_review_child.add_argument("child_dir", help="Child task directory")
|
|
849
|
+
p_review_child.add_argument("--check", action="store_true", help="Non-mutating review readiness check")
|
|
850
|
+
p_review_child.add_argument(
|
|
851
|
+
"--decision",
|
|
852
|
+
choices=["accept", "changes", "cancel", "integrate-through"],
|
|
853
|
+
help="Parent review decision (runs integrate-child steps when valid)",
|
|
854
|
+
)
|
|
855
|
+
p_review_child.add_argument("--ref", help="Child git ref for accept / integrate-through")
|
|
856
|
+
p_review_child.add_argument("--reason", help="Required for changes or cancel decisions")
|
|
857
|
+
p_review_child.add_argument("--notes", help="Short parent review notes included in the report")
|
|
858
|
+
p_review_child.add_argument(
|
|
859
|
+
"--write-artifact",
|
|
860
|
+
action="store_true",
|
|
861
|
+
help="Also write review-<child>.md under the parent task directory",
|
|
862
|
+
)
|
|
863
|
+
p_review_child.add_argument(
|
|
864
|
+
"--no-append-parent-verify",
|
|
865
|
+
action="store_true",
|
|
866
|
+
help="Do not append review notes to parent verify.md",
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
# list-archive
|
|
870
|
+
p_listarch = subparsers.add_parser("list-archive", help="List archived tasks")
|
|
871
|
+
p_listarch.add_argument("month", nargs="?", help="Month (YYYY-MM)")
|
|
872
|
+
|
|
873
|
+
args = parser.parse_args()
|
|
874
|
+
|
|
875
|
+
if not args.command:
|
|
876
|
+
show_usage()
|
|
877
|
+
return 1
|
|
878
|
+
|
|
879
|
+
commands = {
|
|
880
|
+
"create": cmd_create,
|
|
881
|
+
"add-context": cmd_add_context,
|
|
882
|
+
"validate": cmd_validate,
|
|
883
|
+
"list-context": cmd_list_context,
|
|
884
|
+
"dashboard": cmd_dashboard,
|
|
885
|
+
"select": cmd_select,
|
|
886
|
+
"selected": cmd_selected,
|
|
887
|
+
"start-execution": cmd_start_execution,
|
|
888
|
+
"record-gate": cmd_record_gate,
|
|
889
|
+
"exit": cmd_exit,
|
|
890
|
+
"set-branch": cmd_set_branch,
|
|
891
|
+
"set-base-branch": cmd_set_base_branch,
|
|
892
|
+
"set-scope": cmd_set_scope,
|
|
893
|
+
"archive": cmd_archive,
|
|
894
|
+
"prepare-archive-evidence": cmd_prepare_archive_evidence,
|
|
895
|
+
"prepare-learning-scaffold": cmd_prepare_learning_scaffold,
|
|
896
|
+
"add-subtask": cmd_add_subtask,
|
|
897
|
+
"remove-subtask": cmd_remove_subtask,
|
|
898
|
+
"prepare-child-worktree": cmd_prepare_child_worktree,
|
|
899
|
+
"set-child-state": cmd_set_child_state,
|
|
900
|
+
"integrate-child": cmd_integrate_child,
|
|
901
|
+
"generate-child-prompt": cmd_generate_child_prompt,
|
|
902
|
+
"generate-dispatch-prompt": cmd_generate_dispatch_prompt,
|
|
903
|
+
"suggest-execution-strategy": cmd_suggest_execution_strategy,
|
|
904
|
+
"parent-status": cmd_parent_status,
|
|
905
|
+
"review-child": cmd_review_child,
|
|
906
|
+
"list": cmd_list,
|
|
907
|
+
"list-archive": cmd_list_archive,
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
if args.command in commands:
|
|
911
|
+
return commands[args.command](args)
|
|
912
|
+
else:
|
|
913
|
+
show_usage()
|
|
914
|
+
return 1
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
if __name__ == "__main__":
|
|
918
|
+
sys.exit(main())
|