@fifine/aim-studio 0.0.1
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/LICENSE +21 -0
- package/README.md +159 -0
- package/bin/aim.js +3 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +89 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +513 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/update.d.ts +27 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +1275 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/configurators/claude.d.ts +32 -0
- package/dist/configurators/claude.d.ts.map +1 -0
- package/dist/configurators/claude.js +98 -0
- package/dist/configurators/claude.js.map +1 -0
- package/dist/configurators/index.d.ts +51 -0
- package/dist/configurators/index.d.ts.map +1 -0
- package/dist/configurators/index.js +113 -0
- package/dist/configurators/index.js.map +1 -0
- package/dist/configurators/shared.d.ts +12 -0
- package/dist/configurators/shared.d.ts.map +1 -0
- package/dist/configurators/shared.js +21 -0
- package/dist/configurators/shared.js.map +1 -0
- package/dist/configurators/workflow.d.ts +28 -0
- package/dist/configurators/workflow.d.ts.map +1 -0
- package/dist/configurators/workflow.js +147 -0
- package/dist/configurators/workflow.js.map +1 -0
- package/dist/constants/paths.d.ts +68 -0
- package/dist/constants/paths.d.ts.map +1 -0
- package/dist/constants/paths.js +77 -0
- package/dist/constants/paths.js.map +1 -0
- package/dist/constants/version.d.ts +9 -0
- package/dist/constants/version.d.ts.map +1 -0
- package/dist/constants/version.js +15 -0
- package/dist/constants/version.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/index.d.ts +54 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +160 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/migrations/manifests/0.0.1.json +9 -0
- package/dist/migrations/manifests/0.1.9.json +30 -0
- package/dist/migrations/manifests/0.2.0.json +49 -0
- package/dist/migrations/manifests/0.2.12.json +9 -0
- package/dist/migrations/manifests/0.2.13.json +9 -0
- package/dist/migrations/manifests/0.2.14.json +175 -0
- package/dist/migrations/manifests/0.2.15.json +33 -0
- package/dist/migrations/manifests/0.3.0-beta.0.json +278 -0
- package/dist/migrations/manifests/0.3.0-beta.1.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.10.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.11.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.12.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.13.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.14.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.15.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.16.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.2.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.3.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.4.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.5.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.6.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.7.json +11 -0
- package/dist/migrations/manifests/0.3.0-beta.8.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.9.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.0.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.1.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.2.json +9 -0
- package/dist/templates/CLAUDE.md +71 -0
- package/dist/templates/aim/gitignore.txt +29 -0
- package/dist/templates/aim/index.d.ts +49 -0
- package/dist/templates/aim/index.d.ts.map +1 -0
- package/dist/templates/aim/index.js +92 -0
- package/dist/templates/aim/index.js.map +1 -0
- package/dist/templates/aim/scripts/__init__.py +5 -0
- package/dist/templates/aim/scripts/add_session.py +392 -0
- package/dist/templates/aim/scripts/common/__init__.py +80 -0
- package/dist/templates/aim/scripts/common/cli_adapter.py +435 -0
- package/dist/templates/aim/scripts/common/developer.py +190 -0
- package/dist/templates/aim/scripts/common/git_context.py +383 -0
- package/dist/templates/aim/scripts/common/paths.py +347 -0
- package/dist/templates/aim/scripts/common/phase.py +253 -0
- package/dist/templates/aim/scripts/common/registry.py +366 -0
- package/dist/templates/aim/scripts/common/task_queue.py +255 -0
- package/dist/templates/aim/scripts/common/task_utils.py +178 -0
- package/dist/templates/aim/scripts/common/worktree.py +219 -0
- package/dist/templates/aim/scripts/create_bootstrap.py +290 -0
- package/dist/templates/aim/scripts/get_context.py +16 -0
- package/dist/templates/aim/scripts/get_developer.py +26 -0
- package/dist/templates/aim/scripts/init_developer.py +51 -0
- package/dist/templates/aim/scripts/multi_agent/__init__.py +5 -0
- package/dist/templates/aim/scripts/multi_agent/cleanup.py +403 -0
- package/dist/templates/aim/scripts/multi_agent/create_pr.py +329 -0
- package/dist/templates/aim/scripts/multi_agent/plan.py +233 -0
- package/dist/templates/aim/scripts/multi_agent/start.py +461 -0
- package/dist/templates/aim/scripts/multi_agent/status.py +817 -0
- package/dist/templates/aim/scripts/task.py +1068 -0
- package/dist/templates/aim/scripts-shell-archive/add-session.sh +384 -0
- package/dist/templates/aim/scripts-shell-archive/common/developer.sh +129 -0
- package/dist/templates/aim/scripts-shell-archive/common/git-context.sh +263 -0
- package/dist/templates/aim/scripts-shell-archive/common/paths.sh +208 -0
- package/dist/templates/aim/scripts-shell-archive/common/phase.sh +150 -0
- package/dist/templates/aim/scripts-shell-archive/common/registry.sh +247 -0
- package/dist/templates/aim/scripts-shell-archive/common/task-queue.sh +142 -0
- package/dist/templates/aim/scripts-shell-archive/common/task-utils.sh +151 -0
- package/dist/templates/aim/scripts-shell-archive/common/worktree.sh +128 -0
- package/dist/templates/aim/scripts-shell-archive/create-bootstrap.sh +299 -0
- package/dist/templates/aim/scripts-shell-archive/get-context.sh +7 -0
- package/dist/templates/aim/scripts-shell-archive/get-developer.sh +15 -0
- package/dist/templates/aim/scripts-shell-archive/init-developer.sh +34 -0
- package/dist/templates/aim/scripts-shell-archive/multi-agent/cleanup.sh +396 -0
- package/dist/templates/aim/scripts-shell-archive/multi-agent/create-pr.sh +241 -0
- package/dist/templates/aim/scripts-shell-archive/multi-agent/plan.sh +207 -0
- package/dist/templates/aim/scripts-shell-archive/multi-agent/start.sh +317 -0
- package/dist/templates/aim/scripts-shell-archive/multi-agent/status.sh +828 -0
- package/dist/templates/aim/scripts-shell-archive/task.sh +1204 -0
- package/dist/templates/aim/tasks/.gitkeep +0 -0
- package/dist/templates/aim/workflow.md +258 -0
- package/dist/templates/aim/worktree.yaml +47 -0
- package/dist/templates/claude/agents/check.md +122 -0
- package/dist/templates/claude/agents/debug.md +106 -0
- package/dist/templates/claude/agents/dispatch.md +230 -0
- package/dist/templates/claude/agents/implement.md +96 -0
- package/dist/templates/claude/agents/plan.md +396 -0
- package/dist/templates/claude/agents/research.md +120 -0
- package/dist/templates/claude/agents/story.md +53 -0
- package/dist/templates/claude/commands/aim/before-backend-dev.md +13 -0
- package/dist/templates/claude/commands/aim/before-frontend-dev.md +13 -0
- package/dist/templates/claude/commands/aim/break-loop.md +153 -0
- package/dist/templates/claude/commands/aim/check-backend.md +13 -0
- package/dist/templates/claude/commands/aim/check-cross-layer.md +153 -0
- package/dist/templates/claude/commands/aim/check-frontend.md +13 -0
- package/dist/templates/claude/commands/aim/check-story.md +59 -0
- package/dist/templates/claude/commands/aim/create-command.md +154 -0
- package/dist/templates/claude/commands/aim/export.md +187 -0
- package/dist/templates/claude/commands/aim/finish-work.md +104 -0
- package/dist/templates/claude/commands/aim/integrate-skill.md +219 -0
- package/dist/templates/claude/commands/aim/onboard.md +358 -0
- package/dist/templates/claude/commands/aim/parallel.md +217 -0
- package/dist/templates/claude/commands/aim/portrait.md +170 -0
- package/dist/templates/claude/commands/aim/record-session.md +92 -0
- package/dist/templates/claude/commands/aim/start.md +112 -0
- package/dist/templates/claude/commands/aim/story.md +140 -0
- package/dist/templates/claude/commands/aim/update-spec.md +285 -0
- package/dist/templates/claude/commands/aim/visualize.md +182 -0
- package/dist/templates/claude/commands/trellis/before-backend-dev.md +13 -0
- package/dist/templates/claude/commands/trellis/before-frontend-dev.md +13 -0
- package/dist/templates/claude/commands/trellis/break-loop.md +125 -0
- package/dist/templates/claude/commands/trellis/check-backend.md +13 -0
- package/dist/templates/claude/commands/trellis/check-cross-layer.md +153 -0
- package/dist/templates/claude/commands/trellis/check-frontend.md +13 -0
- package/dist/templates/claude/commands/trellis/create-command.md +154 -0
- package/dist/templates/claude/commands/trellis/finish-work.md +129 -0
- package/dist/templates/claude/commands/trellis/integrate-skill.md +219 -0
- package/dist/templates/claude/commands/trellis/onboard.md +358 -0
- package/dist/templates/claude/commands/trellis/parallel.md +193 -0
- package/dist/templates/claude/commands/trellis/record-session.md +62 -0
- package/dist/templates/claude/commands/trellis/start.md +280 -0
- package/dist/templates/claude/commands/trellis/update-spec.md +285 -0
- package/dist/templates/claude/hooks/inject-subagent-context.py +772 -0
- package/dist/templates/claude/hooks/ralph-loop.py +388 -0
- package/dist/templates/claude/hooks/session-start.py +142 -0
- package/dist/templates/claude/index.d.ts +54 -0
- package/dist/templates/claude/index.d.ts.map +1 -0
- package/dist/templates/claude/index.js +85 -0
- package/dist/templates/claude/index.js.map +1 -0
- package/dist/templates/claude/settings.json +41 -0
- package/dist/templates/extract.d.ts +68 -0
- package/dist/templates/extract.d.ts.map +1 -0
- package/dist/templates/extract.js +128 -0
- package/dist/templates/extract.js.map +1 -0
- package/dist/templates/markdown/agents.md +25 -0
- package/dist/templates/markdown/gitignore.txt +12 -0
- package/dist/templates/markdown/index.d.ts +32 -0
- package/dist/templates/markdown/index.d.ts.map +1 -0
- package/dist/templates/markdown/index.js +58 -0
- package/dist/templates/markdown/index.js.map +1 -0
- package/dist/templates/markdown/spec/backend/database-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/backend/directory-structure.md.txt +54 -0
- package/dist/templates/markdown/spec/backend/error-handling.md.txt +51 -0
- package/dist/templates/markdown/spec/backend/index.md +40 -0
- package/dist/templates/markdown/spec/backend/index.md.txt +38 -0
- package/dist/templates/markdown/spec/backend/logging-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/backend/script-conventions.md +467 -0
- package/dist/templates/markdown/spec/frontend/component-guidelines.md.txt +59 -0
- package/dist/templates/markdown/spec/frontend/directory-structure.md.txt +54 -0
- package/dist/templates/markdown/spec/frontend/hook-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/frontend/index.md.txt +39 -0
- package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/frontend/state-management.md.txt +51 -0
- package/dist/templates/markdown/spec/frontend/type-safety.md.txt +51 -0
- package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md +118 -0
- package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +92 -0
- package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +94 -0
- package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md +394 -0
- package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +319 -0
- package/dist/templates/markdown/spec/guides/index.md.txt +89 -0
- package/dist/templates/markdown/spec/story/character.md.txt +95 -0
- package/dist/templates/markdown/spec/story/index.md.txt +31 -0
- package/dist/templates/markdown/spec/story/script.md.txt +313 -0
- package/dist/templates/markdown/spec/story/world.md.txt +92 -0
- package/dist/templates/markdown/workspace-index.md +123 -0
- package/dist/templates/markdown/worktree.yaml.txt +58 -0
- package/dist/templates/trellis/gitignore.txt +29 -0
- package/dist/templates/trellis/index.d.ts +49 -0
- package/dist/templates/trellis/index.d.ts.map +1 -0
- package/dist/templates/trellis/index.js +92 -0
- package/dist/templates/trellis/index.js.map +1 -0
- package/dist/templates/trellis/scripts/__init__.py +5 -0
- package/dist/templates/trellis/scripts/add_session.py +392 -0
- package/dist/templates/trellis/scripts/common/__init__.py +80 -0
- package/dist/templates/trellis/scripts/common/cli_adapter.py +435 -0
- package/dist/templates/trellis/scripts/common/developer.py +190 -0
- package/dist/templates/trellis/scripts/common/git_context.py +383 -0
- package/dist/templates/trellis/scripts/common/paths.py +347 -0
- package/dist/templates/trellis/scripts/common/phase.py +253 -0
- package/dist/templates/trellis/scripts/common/registry.py +366 -0
- package/dist/templates/trellis/scripts/common/task_queue.py +255 -0
- package/dist/templates/trellis/scripts/common/task_utils.py +178 -0
- package/dist/templates/trellis/scripts/common/worktree.py +219 -0
- package/dist/templates/trellis/scripts/create_bootstrap.py +290 -0
- package/dist/templates/trellis/scripts/get_context.py +16 -0
- package/dist/templates/trellis/scripts/get_developer.py +26 -0
- package/dist/templates/trellis/scripts/init_developer.py +51 -0
- package/dist/templates/trellis/scripts/multi_agent/__init__.py +5 -0
- package/dist/templates/trellis/scripts/multi_agent/cleanup.py +403 -0
- package/dist/templates/trellis/scripts/multi_agent/create_pr.py +329 -0
- package/dist/templates/trellis/scripts/multi_agent/plan.py +233 -0
- package/dist/templates/trellis/scripts/multi_agent/start.py +461 -0
- package/dist/templates/trellis/scripts/multi_agent/status.py +817 -0
- package/dist/templates/trellis/scripts/task.py +1056 -0
- package/dist/templates/trellis/scripts-shell-archive/add-session.sh +384 -0
- package/dist/templates/trellis/scripts-shell-archive/common/developer.sh +129 -0
- package/dist/templates/trellis/scripts-shell-archive/common/git-context.sh +263 -0
- package/dist/templates/trellis/scripts-shell-archive/common/paths.sh +208 -0
- package/dist/templates/trellis/scripts-shell-archive/common/phase.sh +150 -0
- package/dist/templates/trellis/scripts-shell-archive/common/registry.sh +247 -0
- package/dist/templates/trellis/scripts-shell-archive/common/task-queue.sh +142 -0
- package/dist/templates/trellis/scripts-shell-archive/common/task-utils.sh +151 -0
- package/dist/templates/trellis/scripts-shell-archive/common/worktree.sh +128 -0
- package/dist/templates/trellis/scripts-shell-archive/create-bootstrap.sh +299 -0
- package/dist/templates/trellis/scripts-shell-archive/get-context.sh +7 -0
- package/dist/templates/trellis/scripts-shell-archive/get-developer.sh +15 -0
- package/dist/templates/trellis/scripts-shell-archive/init-developer.sh +34 -0
- package/dist/templates/trellis/scripts-shell-archive/multi-agent/cleanup.sh +396 -0
- package/dist/templates/trellis/scripts-shell-archive/multi-agent/create-pr.sh +241 -0
- package/dist/templates/trellis/scripts-shell-archive/multi-agent/plan.sh +207 -0
- package/dist/templates/trellis/scripts-shell-archive/multi-agent/start.sh +317 -0
- package/dist/templates/trellis/scripts-shell-archive/multi-agent/status.sh +828 -0
- package/dist/templates/trellis/scripts-shell-archive/task.sh +1204 -0
- package/dist/templates/trellis/tasks/.gitkeep +0 -0
- package/dist/templates/trellis/workflow.md +416 -0
- package/dist/templates/trellis/worktree.yaml +47 -0
- package/dist/types/ai-tools.d.ts +48 -0
- package/dist/types/ai-tools.d.ts.map +1 -0
- package/dist/types/ai-tools.js +32 -0
- package/dist/types/ai-tools.js.map +1 -0
- package/dist/types/migration.d.ts +86 -0
- package/dist/types/migration.d.ts.map +1 -0
- package/dist/types/migration.js +8 -0
- package/dist/types/migration.js.map +1 -0
- package/dist/utils/compare-versions.d.ts +12 -0
- package/dist/utils/compare-versions.d.ts.map +1 -0
- package/dist/utils/compare-versions.js +76 -0
- package/dist/utils/compare-versions.js.map +1 -0
- package/dist/utils/file-writer.d.ts +23 -0
- package/dist/utils/file-writer.d.ts.map +1 -0
- package/dist/utils/file-writer.js +140 -0
- package/dist/utils/file-writer.js.map +1 -0
- package/dist/utils/project-detector.d.ts +16 -0
- package/dist/utils/project-detector.d.ts.map +1 -0
- package/dist/utils/project-detector.js +188 -0
- package/dist/utils/project-detector.js.map +1 -0
- package/dist/utils/template-fetcher.d.ts +51 -0
- package/dist/utils/template-fetcher.d.ts.map +1 -0
- package/dist/utils/template-fetcher.js +174 -0
- package/dist/utils/template-fetcher.js.map +1 -0
- package/dist/utils/template-hash.d.ts +78 -0
- package/dist/utils/template-hash.d.ts.map +1 -0
- package/dist/utils/template-hash.js +239 -0
- package/dist/utils/template-hash.js.map +1 -0
- package/package.json +87 -0
|
@@ -0,0 +1,772 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Multi-Agent Pipeline Context Injection Hook
|
|
5
|
+
|
|
6
|
+
Core Design Philosophy:
|
|
7
|
+
- Dispatch becomes a pure dispatcher, only responsible for "calling subagents"
|
|
8
|
+
- Hook is responsible for injecting all context, subagent works autonomously with complete info
|
|
9
|
+
- Each agent has a dedicated jsonl file defining its context
|
|
10
|
+
- No resume needed, no segmentation, behavior controlled by code not prompt
|
|
11
|
+
|
|
12
|
+
Trigger: PreToolUse (before Task tool call)
|
|
13
|
+
|
|
14
|
+
Context Source: .aim-studio/.current-task points to task directory
|
|
15
|
+
- implement.jsonl - Implement agent dedicated context
|
|
16
|
+
- check.jsonl - Check agent dedicated context
|
|
17
|
+
- debug.jsonl - Debug agent dedicated context
|
|
18
|
+
- research.jsonl - Research agent dedicated context (optional, usually not needed)
|
|
19
|
+
- cr.jsonl - Code review dedicated context
|
|
20
|
+
- prd.md - Requirements document
|
|
21
|
+
- info.md - Technical design
|
|
22
|
+
- codex-review-output.txt - Code Review results
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
# IMPORTANT: Suppress all warnings FIRST
|
|
26
|
+
import warnings
|
|
27
|
+
warnings.filterwarnings("ignore")
|
|
28
|
+
|
|
29
|
+
import json
|
|
30
|
+
import os
|
|
31
|
+
import sys
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
|
|
34
|
+
# IMPORTANT: Force stdout to use UTF-8 on Windows
|
|
35
|
+
# This fixes UnicodeEncodeError when outputting non-ASCII characters
|
|
36
|
+
if sys.platform == "win32":
|
|
37
|
+
import io as _io
|
|
38
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
39
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
|
|
40
|
+
elif hasattr(sys.stdout, "detach"):
|
|
41
|
+
sys.stdout = _io.TextIOWrapper(sys.stdout.detach(), encoding="utf-8", errors="replace") # type: ignore[union-attr]
|
|
42
|
+
|
|
43
|
+
# =============================================================================
|
|
44
|
+
# Path Constants (change here to rename directories)
|
|
45
|
+
# =============================================================================
|
|
46
|
+
|
|
47
|
+
DIR_WORKFLOW = ".aim-studio"
|
|
48
|
+
DIR_WORKSPACE = "workspace"
|
|
49
|
+
DIR_TASKS = "tasks"
|
|
50
|
+
DIR_SPEC = "spec"
|
|
51
|
+
FILE_CURRENT_TASK = ".current-task"
|
|
52
|
+
FILE_TASK_JSON = "task.json"
|
|
53
|
+
|
|
54
|
+
# Agents that don't update phase (can be called at any time)
|
|
55
|
+
AGENTS_NO_PHASE_UPDATE = {"debug", "research"}
|
|
56
|
+
|
|
57
|
+
# =============================================================================
|
|
58
|
+
# Subagent Constants (change here to rename subagent types)
|
|
59
|
+
# =============================================================================
|
|
60
|
+
|
|
61
|
+
AGENT_IMPLEMENT = "implement"
|
|
62
|
+
AGENT_CHECK = "check"
|
|
63
|
+
AGENT_DEBUG = "debug"
|
|
64
|
+
AGENT_RESEARCH = "research"
|
|
65
|
+
|
|
66
|
+
# Agents that require a task directory
|
|
67
|
+
AGENTS_REQUIRE_TASK = (AGENT_IMPLEMENT, AGENT_CHECK, AGENT_DEBUG)
|
|
68
|
+
# All supported agents
|
|
69
|
+
AGENTS_ALL = (AGENT_IMPLEMENT, AGENT_CHECK, AGENT_DEBUG, AGENT_RESEARCH)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def find_repo_root(start_path: str) -> str | None:
|
|
73
|
+
"""
|
|
74
|
+
Find git repo root from start_path upwards
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Repo root path, or None if not found
|
|
78
|
+
"""
|
|
79
|
+
current = Path(start_path).resolve()
|
|
80
|
+
while current != current.parent:
|
|
81
|
+
if (current / ".git").exists():
|
|
82
|
+
return str(current)
|
|
83
|
+
current = current.parent
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def get_current_task(repo_root: str) -> str | None:
|
|
88
|
+
"""
|
|
89
|
+
Read current task directory path from .aim-studio/.current-task
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Task directory relative path (relative to repo_root)
|
|
93
|
+
None if not set
|
|
94
|
+
"""
|
|
95
|
+
current_task_file = os.path.join(repo_root, DIR_WORKFLOW, FILE_CURRENT_TASK)
|
|
96
|
+
if not os.path.exists(current_task_file):
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
with open(current_task_file, "r", encoding="utf-8") as f:
|
|
101
|
+
content = f.read().strip()
|
|
102
|
+
return content if content else None
|
|
103
|
+
except Exception:
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def update_current_phase(repo_root: str, task_dir: str, subagent_type: str) -> None:
|
|
108
|
+
"""
|
|
109
|
+
Update current_phase in task.json based on subagent_type.
|
|
110
|
+
|
|
111
|
+
This ensures phase tracking is always accurate, regardless of whether
|
|
112
|
+
dispatch agent remembers to update it.
|
|
113
|
+
|
|
114
|
+
Logic:
|
|
115
|
+
- Read next_action array from task.json
|
|
116
|
+
- Find the next phase whose action matches subagent_type
|
|
117
|
+
- Only move forward, never backward
|
|
118
|
+
- Some agents (debug, research) don't update phase
|
|
119
|
+
"""
|
|
120
|
+
if subagent_type in AGENTS_NO_PHASE_UPDATE:
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
task_json_path = os.path.join(repo_root, task_dir, FILE_TASK_JSON)
|
|
124
|
+
if not os.path.exists(task_json_path):
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
with open(task_json_path, "r", encoding="utf-8") as f:
|
|
129
|
+
task_data = json.load(f)
|
|
130
|
+
|
|
131
|
+
current_phase = task_data.get("current_phase", 0)
|
|
132
|
+
next_actions = task_data.get("next_action", [])
|
|
133
|
+
|
|
134
|
+
# Map action names to subagent types
|
|
135
|
+
# "implement" -> "implement", "check" -> "check", "finish" -> "check"
|
|
136
|
+
action_to_agent = {
|
|
137
|
+
"implement": "implement",
|
|
138
|
+
"check": "check",
|
|
139
|
+
"finish": "check", # finish uses check agent
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# Find the next phase that matches this subagent_type
|
|
143
|
+
new_phase = None
|
|
144
|
+
for action in next_actions:
|
|
145
|
+
phase_num = action.get("phase", 0)
|
|
146
|
+
action_name = action.get("action", "")
|
|
147
|
+
expected_agent = action_to_agent.get(action_name)
|
|
148
|
+
|
|
149
|
+
# Only consider phases after current_phase
|
|
150
|
+
if phase_num > current_phase and expected_agent == subagent_type:
|
|
151
|
+
new_phase = phase_num
|
|
152
|
+
break
|
|
153
|
+
|
|
154
|
+
if new_phase is not None:
|
|
155
|
+
task_data["current_phase"] = new_phase
|
|
156
|
+
|
|
157
|
+
with open(task_json_path, "w", encoding="utf-8") as f:
|
|
158
|
+
json.dump(task_data, f, indent=2, ensure_ascii=False)
|
|
159
|
+
except Exception:
|
|
160
|
+
# Don't fail the hook if phase update fails
|
|
161
|
+
pass
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def read_file_content(base_path: str, file_path: str) -> str | None:
|
|
165
|
+
"""Read file content, return None if file doesn't exist"""
|
|
166
|
+
full_path = os.path.join(base_path, file_path)
|
|
167
|
+
if os.path.exists(full_path) and os.path.isfile(full_path):
|
|
168
|
+
try:
|
|
169
|
+
with open(full_path, "r", encoding="utf-8") as f:
|
|
170
|
+
return f.read()
|
|
171
|
+
except Exception:
|
|
172
|
+
return None
|
|
173
|
+
return None
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def read_directory_contents(
|
|
177
|
+
base_path: str, dir_path: str, max_files: int = 20
|
|
178
|
+
) -> list[tuple[str, str]]:
|
|
179
|
+
"""
|
|
180
|
+
Read all .md files in a directory
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
base_path: Base path (usually repo_root)
|
|
184
|
+
dir_path: Directory relative path
|
|
185
|
+
max_files: Max files to read (prevent huge directories)
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
[(file_path, content), ...]
|
|
189
|
+
"""
|
|
190
|
+
full_path = os.path.join(base_path, dir_path)
|
|
191
|
+
if not os.path.exists(full_path) or not os.path.isdir(full_path):
|
|
192
|
+
return []
|
|
193
|
+
|
|
194
|
+
results = []
|
|
195
|
+
try:
|
|
196
|
+
# Only read .md files, sorted by filename
|
|
197
|
+
md_files = sorted(
|
|
198
|
+
[
|
|
199
|
+
f
|
|
200
|
+
for f in os.listdir(full_path)
|
|
201
|
+
if f.endswith(".md") and os.path.isfile(os.path.join(full_path, f))
|
|
202
|
+
]
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
for filename in md_files[:max_files]:
|
|
206
|
+
file_full_path = os.path.join(full_path, filename)
|
|
207
|
+
relative_path = os.path.join(dir_path, filename)
|
|
208
|
+
try:
|
|
209
|
+
with open(file_full_path, "r", encoding="utf-8") as f:
|
|
210
|
+
content = f.read()
|
|
211
|
+
results.append((relative_path, content))
|
|
212
|
+
except Exception:
|
|
213
|
+
continue
|
|
214
|
+
except Exception:
|
|
215
|
+
pass
|
|
216
|
+
|
|
217
|
+
return results
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def read_jsonl_entries(base_path: str, jsonl_path: str) -> list[tuple[str, str]]:
|
|
221
|
+
"""
|
|
222
|
+
Read all file/directory contents referenced in jsonl file
|
|
223
|
+
|
|
224
|
+
Schema:
|
|
225
|
+
{"file": "path/to/file.md", "reason": "..."}
|
|
226
|
+
{"file": "path/to/dir/", "type": "directory", "reason": "..."}
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
[(path, content), ...]
|
|
230
|
+
"""
|
|
231
|
+
full_path = os.path.join(base_path, jsonl_path)
|
|
232
|
+
if not os.path.exists(full_path):
|
|
233
|
+
return []
|
|
234
|
+
|
|
235
|
+
results = []
|
|
236
|
+
try:
|
|
237
|
+
with open(full_path, "r", encoding="utf-8") as f:
|
|
238
|
+
for line in f:
|
|
239
|
+
line = line.strip()
|
|
240
|
+
if not line:
|
|
241
|
+
continue
|
|
242
|
+
try:
|
|
243
|
+
item = json.loads(line)
|
|
244
|
+
file_path = item.get("file") or item.get("path")
|
|
245
|
+
entry_type = item.get("type", "file")
|
|
246
|
+
|
|
247
|
+
if not file_path:
|
|
248
|
+
continue
|
|
249
|
+
|
|
250
|
+
if entry_type == "directory":
|
|
251
|
+
# Read all .md files in directory
|
|
252
|
+
dir_contents = read_directory_contents(base_path, file_path)
|
|
253
|
+
results.extend(dir_contents)
|
|
254
|
+
else:
|
|
255
|
+
# Read single file
|
|
256
|
+
content = read_file_content(base_path, file_path)
|
|
257
|
+
if content:
|
|
258
|
+
results.append((file_path, content))
|
|
259
|
+
except json.JSONDecodeError:
|
|
260
|
+
continue
|
|
261
|
+
except Exception:
|
|
262
|
+
pass
|
|
263
|
+
|
|
264
|
+
return results
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def get_agent_context(repo_root: str, task_dir: str, agent_type: str) -> str:
|
|
268
|
+
"""
|
|
269
|
+
Get complete context for specified agent
|
|
270
|
+
|
|
271
|
+
Prioritize agent-specific jsonl, fallback to spec.jsonl if not exists
|
|
272
|
+
"""
|
|
273
|
+
context_parts = []
|
|
274
|
+
|
|
275
|
+
# 1. Try agent-specific jsonl
|
|
276
|
+
agent_jsonl = f"{task_dir}/{agent_type}.jsonl"
|
|
277
|
+
agent_entries = read_jsonl_entries(repo_root, agent_jsonl)
|
|
278
|
+
|
|
279
|
+
# 2. If agent-specific jsonl doesn't exist or empty, fallback to spec.jsonl
|
|
280
|
+
if not agent_entries:
|
|
281
|
+
agent_entries = read_jsonl_entries(repo_root, f"{task_dir}/spec.jsonl")
|
|
282
|
+
|
|
283
|
+
# 3. Add all files from jsonl
|
|
284
|
+
for file_path, content in agent_entries:
|
|
285
|
+
context_parts.append(f"=== {file_path} ===\n{content}")
|
|
286
|
+
|
|
287
|
+
return "\n\n".join(context_parts)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def get_implement_context(repo_root: str, task_dir: str) -> str:
|
|
291
|
+
"""
|
|
292
|
+
Complete context for Implement Agent
|
|
293
|
+
|
|
294
|
+
Read order:
|
|
295
|
+
1. All files in implement.jsonl (dev specs)
|
|
296
|
+
2. prd.md (requirements)
|
|
297
|
+
3. info.md (technical design)
|
|
298
|
+
"""
|
|
299
|
+
context_parts = []
|
|
300
|
+
|
|
301
|
+
# 1. Read implement.jsonl (or fallback to spec.jsonl)
|
|
302
|
+
base_context = get_agent_context(repo_root, task_dir, "implement")
|
|
303
|
+
if base_context:
|
|
304
|
+
context_parts.append(base_context)
|
|
305
|
+
|
|
306
|
+
# 2. Requirements document
|
|
307
|
+
prd_content = read_file_content(repo_root, f"{task_dir}/prd.md")
|
|
308
|
+
if prd_content:
|
|
309
|
+
context_parts.append(f"=== {task_dir}/prd.md (Requirements) ===\n{prd_content}")
|
|
310
|
+
|
|
311
|
+
# 3. Technical design
|
|
312
|
+
info_content = read_file_content(repo_root, f"{task_dir}/info.md")
|
|
313
|
+
if info_content:
|
|
314
|
+
context_parts.append(
|
|
315
|
+
f"=== {task_dir}/info.md (Technical Design) ===\n{info_content}"
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
return "\n\n".join(context_parts)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def get_check_context(repo_root: str, task_dir: str) -> str:
|
|
322
|
+
"""
|
|
323
|
+
Complete context for Check Agent
|
|
324
|
+
|
|
325
|
+
Read order:
|
|
326
|
+
1. All files in check.jsonl (check specs + dev specs)
|
|
327
|
+
2. prd.md (for understanding task intent)
|
|
328
|
+
"""
|
|
329
|
+
context_parts = []
|
|
330
|
+
|
|
331
|
+
# 1. Read check.jsonl (or fallback to spec.jsonl + hardcoded check files)
|
|
332
|
+
check_entries = read_jsonl_entries(repo_root, f"{task_dir}/check.jsonl")
|
|
333
|
+
|
|
334
|
+
if check_entries:
|
|
335
|
+
for file_path, content in check_entries:
|
|
336
|
+
context_parts.append(f"=== {file_path} ===\n{content}")
|
|
337
|
+
else:
|
|
338
|
+
# Fallback: use hardcoded check files + spec.jsonl
|
|
339
|
+
check_files = [
|
|
340
|
+
(".claude/commands/trellis/finish-work.md", "Finish work checklist"),
|
|
341
|
+
(".claude/commands/trellis/check-cross-layer.md", "Cross-layer check spec"),
|
|
342
|
+
(".claude/commands/trellis/check-backend.md", "Backend check spec"),
|
|
343
|
+
(".claude/commands/trellis/check-frontend.md", "Frontend check spec"),
|
|
344
|
+
]
|
|
345
|
+
for file_path, description in check_files:
|
|
346
|
+
content = read_file_content(repo_root, file_path)
|
|
347
|
+
if content:
|
|
348
|
+
context_parts.append(f"=== {file_path} ({description}) ===\n{content}")
|
|
349
|
+
|
|
350
|
+
# Add spec.jsonl
|
|
351
|
+
spec_entries = read_jsonl_entries(repo_root, f"{task_dir}/spec.jsonl")
|
|
352
|
+
for file_path, content in spec_entries:
|
|
353
|
+
context_parts.append(f"=== {file_path} (Dev spec) ===\n{content}")
|
|
354
|
+
|
|
355
|
+
# 2. Requirements document (for understanding task intent)
|
|
356
|
+
prd_content = read_file_content(repo_root, f"{task_dir}/prd.md")
|
|
357
|
+
if prd_content:
|
|
358
|
+
context_parts.append(
|
|
359
|
+
f"=== {task_dir}/prd.md (Requirements - for understanding intent) ===\n{prd_content}"
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
return "\n\n".join(context_parts)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def get_finish_context(repo_root: str, task_dir: str) -> str:
|
|
366
|
+
"""
|
|
367
|
+
Complete context for Finish phase (final check before PR)
|
|
368
|
+
|
|
369
|
+
Read order:
|
|
370
|
+
1. All files in finish.jsonl (if exists)
|
|
371
|
+
2. Fallback to finish-work.md only (lightweight final check)
|
|
372
|
+
3. prd.md (for verifying requirements are met)
|
|
373
|
+
"""
|
|
374
|
+
context_parts = []
|
|
375
|
+
|
|
376
|
+
# 1. Try finish.jsonl first
|
|
377
|
+
finish_entries = read_jsonl_entries(repo_root, f"{task_dir}/finish.jsonl")
|
|
378
|
+
|
|
379
|
+
if finish_entries:
|
|
380
|
+
for file_path, content in finish_entries:
|
|
381
|
+
context_parts.append(f"=== {file_path} ===\n{content}")
|
|
382
|
+
else:
|
|
383
|
+
# Fallback: only finish-work.md (lightweight)
|
|
384
|
+
finish_work = read_file_content(
|
|
385
|
+
repo_root, ".claude/commands/trellis/finish-work.md"
|
|
386
|
+
)
|
|
387
|
+
if finish_work:
|
|
388
|
+
context_parts.append(
|
|
389
|
+
f"=== .claude/commands/trellis/finish-work.md (Finish checklist) ===\n{finish_work}"
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
# 2. Requirements document (for verifying requirements are met)
|
|
393
|
+
prd_content = read_file_content(repo_root, f"{task_dir}/prd.md")
|
|
394
|
+
if prd_content:
|
|
395
|
+
context_parts.append(
|
|
396
|
+
f"=== {task_dir}/prd.md (Requirements - verify all met) ===\n{prd_content}"
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
return "\n\n".join(context_parts)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def get_debug_context(repo_root: str, task_dir: str) -> str:
|
|
403
|
+
"""
|
|
404
|
+
Complete context for Debug Agent
|
|
405
|
+
|
|
406
|
+
Read order:
|
|
407
|
+
1. All files in debug.jsonl (specs needed for fixing)
|
|
408
|
+
2. codex-review-output.txt (Codex Review results)
|
|
409
|
+
"""
|
|
410
|
+
context_parts = []
|
|
411
|
+
|
|
412
|
+
# 1. Read debug.jsonl (or fallback to spec.jsonl + hardcoded check files)
|
|
413
|
+
debug_entries = read_jsonl_entries(repo_root, f"{task_dir}/debug.jsonl")
|
|
414
|
+
|
|
415
|
+
if debug_entries:
|
|
416
|
+
for file_path, content in debug_entries:
|
|
417
|
+
context_parts.append(f"=== {file_path} ===\n{content}")
|
|
418
|
+
else:
|
|
419
|
+
# Fallback: use spec.jsonl + hardcoded check files
|
|
420
|
+
spec_entries = read_jsonl_entries(repo_root, f"{task_dir}/spec.jsonl")
|
|
421
|
+
for file_path, content in spec_entries:
|
|
422
|
+
context_parts.append(f"=== {file_path} (Dev spec) ===\n{content}")
|
|
423
|
+
|
|
424
|
+
check_files = [
|
|
425
|
+
(".claude/commands/trellis/check-backend.md", "Backend check spec"),
|
|
426
|
+
(".claude/commands/trellis/check-frontend.md", "Frontend check spec"),
|
|
427
|
+
(".claude/commands/trellis/check-cross-layer.md", "Cross-layer check spec"),
|
|
428
|
+
]
|
|
429
|
+
for file_path, description in check_files:
|
|
430
|
+
content = read_file_content(repo_root, file_path)
|
|
431
|
+
if content:
|
|
432
|
+
context_parts.append(f"=== {file_path} ({description}) ===\n{content}")
|
|
433
|
+
|
|
434
|
+
# 2. Codex review output (if exists)
|
|
435
|
+
codex_output = read_file_content(repo_root, f"{task_dir}/codex-review-output.txt")
|
|
436
|
+
if codex_output:
|
|
437
|
+
context_parts.append(
|
|
438
|
+
f"=== {task_dir}/codex-review-output.txt (Codex Review Results) ===\n{codex_output}"
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
return "\n\n".join(context_parts)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def build_implement_prompt(original_prompt: str, context: str) -> str:
|
|
445
|
+
"""Build complete prompt for Implement"""
|
|
446
|
+
return f"""# Implement Agent Task
|
|
447
|
+
|
|
448
|
+
You are the Implement Agent in the Multi-Agent Pipeline.
|
|
449
|
+
|
|
450
|
+
## Your Context
|
|
451
|
+
|
|
452
|
+
All the information you need has been prepared for you:
|
|
453
|
+
|
|
454
|
+
{context}
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## Your Task
|
|
459
|
+
|
|
460
|
+
{original_prompt}
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
## Workflow
|
|
465
|
+
|
|
466
|
+
1. **Understand specs** - All dev specs are injected above, understand them
|
|
467
|
+
2. **Understand requirements** - Read requirements document and technical design
|
|
468
|
+
3. **Implement feature** - Implement following specs and design
|
|
469
|
+
4. **Self-check** - Ensure code quality against check specs
|
|
470
|
+
|
|
471
|
+
## Important Constraints
|
|
472
|
+
|
|
473
|
+
- Do NOT execute git commit, only code modifications
|
|
474
|
+
- Follow all dev specs injected above
|
|
475
|
+
- Report list of modified/created files when done"""
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def build_check_prompt(original_prompt: str, context: str) -> str:
|
|
479
|
+
"""Build complete prompt for Check"""
|
|
480
|
+
return f"""# Check Agent Task
|
|
481
|
+
|
|
482
|
+
You are the Check Agent in the Multi-Agent Pipeline (code and cross-layer checker).
|
|
483
|
+
|
|
484
|
+
## Your Context
|
|
485
|
+
|
|
486
|
+
All check specs and dev specs you need:
|
|
487
|
+
|
|
488
|
+
{context}
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## Your Task
|
|
493
|
+
|
|
494
|
+
{original_prompt}
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## Workflow
|
|
499
|
+
|
|
500
|
+
1. **Get changes** - Run `git diff --name-only` and `git diff` to get code changes
|
|
501
|
+
2. **Check against specs** - Check item by item against specs above
|
|
502
|
+
3. **Self-fix** - Fix issues directly, don't just report
|
|
503
|
+
4. **Run verification** - Run project's lint and typecheck commands
|
|
504
|
+
|
|
505
|
+
## Important Constraints
|
|
506
|
+
|
|
507
|
+
- Fix issues yourself, don't just report
|
|
508
|
+
- Must execute complete checklist in check specs
|
|
509
|
+
- Pay special attention to impact radius analysis (L1-L5)"""
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def build_finish_prompt(original_prompt: str, context: str) -> str:
|
|
513
|
+
"""Build complete prompt for Finish (final check before PR)"""
|
|
514
|
+
return f"""# Finish Agent Task
|
|
515
|
+
|
|
516
|
+
You are performing the final check before creating a PR.
|
|
517
|
+
|
|
518
|
+
## Your Context
|
|
519
|
+
|
|
520
|
+
Finish checklist and requirements:
|
|
521
|
+
|
|
522
|
+
{context}
|
|
523
|
+
|
|
524
|
+
---
|
|
525
|
+
|
|
526
|
+
## Your Task
|
|
527
|
+
|
|
528
|
+
{original_prompt}
|
|
529
|
+
|
|
530
|
+
---
|
|
531
|
+
|
|
532
|
+
## Workflow
|
|
533
|
+
|
|
534
|
+
1. **Review changes** - Run `git diff --name-only` to see all changed files
|
|
535
|
+
2. **Verify requirements** - Check each requirement in prd.md is implemented
|
|
536
|
+
3. **Run final checks** - Execute finish-work.md checklist
|
|
537
|
+
4. **Confirm ready** - Ensure code is ready for PR
|
|
538
|
+
|
|
539
|
+
## Important Constraints
|
|
540
|
+
|
|
541
|
+
- This is a final verification, not a fix phase
|
|
542
|
+
- If critical issues found, report them clearly
|
|
543
|
+
- Verify all acceptance criteria in prd.md are met"""
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
def build_debug_prompt(original_prompt: str, context: str) -> str:
|
|
547
|
+
"""Build complete prompt for Debug"""
|
|
548
|
+
return f"""# Debug Agent Task
|
|
549
|
+
|
|
550
|
+
You are the Debug Agent in the Multi-Agent Pipeline (issue fixer).
|
|
551
|
+
|
|
552
|
+
## Your Context
|
|
553
|
+
|
|
554
|
+
Dev specs and Codex Review results:
|
|
555
|
+
|
|
556
|
+
{context}
|
|
557
|
+
|
|
558
|
+
---
|
|
559
|
+
|
|
560
|
+
## Your Task
|
|
561
|
+
|
|
562
|
+
{original_prompt}
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
566
|
+
## Workflow
|
|
567
|
+
|
|
568
|
+
1. **Understand issues** - Analyze issues pointed out in Codex Review
|
|
569
|
+
2. **Locate code** - Find positions that need fixing
|
|
570
|
+
3. **Fix against specs** - Fix issues following dev specs
|
|
571
|
+
4. **Verify fixes** - Run typecheck to ensure no new issues
|
|
572
|
+
|
|
573
|
+
## Important Constraints
|
|
574
|
+
|
|
575
|
+
- Do NOT execute git commit, only code modifications
|
|
576
|
+
- Run typecheck after each fix to verify
|
|
577
|
+
- Report which issues were fixed and which files were modified"""
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
def get_research_context(repo_root: str, task_dir: str | None) -> str:
|
|
581
|
+
"""
|
|
582
|
+
Context for Research Agent
|
|
583
|
+
|
|
584
|
+
Research doesn't need much preset context, only needs:
|
|
585
|
+
1. Project structure overview (where spec directories are)
|
|
586
|
+
2. Optional research.jsonl (if there are specific search needs)
|
|
587
|
+
"""
|
|
588
|
+
context_parts = []
|
|
589
|
+
|
|
590
|
+
# 1. Project structure overview (uses constants for paths)
|
|
591
|
+
spec_path = f"{DIR_WORKFLOW}/{DIR_SPEC}"
|
|
592
|
+
project_structure = f"""## Project Spec Directory Structure
|
|
593
|
+
|
|
594
|
+
```
|
|
595
|
+
{spec_path}/
|
|
596
|
+
├── shared/ # Cross-project common specs (TypeScript, code quality, git)
|
|
597
|
+
├── frontend/ # Frontend standards
|
|
598
|
+
├── backend/ # Backend standards
|
|
599
|
+
└── guides/ # Thinking guides (cross-layer, code reuse, etc.)
|
|
600
|
+
|
|
601
|
+
{DIR_WORKFLOW}/big-question/ # Known issues and pitfalls
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
## Search Tips
|
|
605
|
+
|
|
606
|
+
- Spec files: `{spec_path}/**/*.md`
|
|
607
|
+
- Known issues: `{DIR_WORKFLOW}/big-question/`
|
|
608
|
+
- Code search: Use Glob and Grep tools
|
|
609
|
+
- Tech solutions: Use mcp__exa__web_search_exa or mcp__exa__get_code_context_exa"""
|
|
610
|
+
|
|
611
|
+
context_parts.append(project_structure)
|
|
612
|
+
|
|
613
|
+
# 2. If task directory exists, try reading research.jsonl (optional)
|
|
614
|
+
if task_dir:
|
|
615
|
+
research_entries = read_jsonl_entries(repo_root, f"{task_dir}/research.jsonl")
|
|
616
|
+
if research_entries:
|
|
617
|
+
context_parts.append(
|
|
618
|
+
"\n## Additional Search Context (from research.jsonl)\n"
|
|
619
|
+
)
|
|
620
|
+
for file_path, content in research_entries:
|
|
621
|
+
context_parts.append(f"=== {file_path} ===\n{content}")
|
|
622
|
+
|
|
623
|
+
return "\n\n".join(context_parts)
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
def build_research_prompt(original_prompt: str, context: str) -> str:
|
|
627
|
+
"""Build complete prompt for Research"""
|
|
628
|
+
return f"""# Research Agent Task
|
|
629
|
+
|
|
630
|
+
You are the Research Agent in the Multi-Agent Pipeline (search researcher).
|
|
631
|
+
|
|
632
|
+
## Core Principle
|
|
633
|
+
|
|
634
|
+
**You do one thing: find and explain information.**
|
|
635
|
+
|
|
636
|
+
You are a documenter, not a reviewer.
|
|
637
|
+
|
|
638
|
+
## Project Info
|
|
639
|
+
|
|
640
|
+
{context}
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
## Your Task
|
|
645
|
+
|
|
646
|
+
{original_prompt}
|
|
647
|
+
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
## Workflow
|
|
651
|
+
|
|
652
|
+
1. **Understand query** - Determine search type (internal/external) and scope
|
|
653
|
+
2. **Plan search** - List search steps for complex queries
|
|
654
|
+
3. **Execute search** - Execute multiple independent searches in parallel
|
|
655
|
+
4. **Organize results** - Output structured report
|
|
656
|
+
|
|
657
|
+
## Search Tools
|
|
658
|
+
|
|
659
|
+
| Tool | Purpose |
|
|
660
|
+
|------|---------|
|
|
661
|
+
| Glob | Search by filename pattern |
|
|
662
|
+
| Grep | Search by content |
|
|
663
|
+
| Read | Read file content |
|
|
664
|
+
| mcp__exa__web_search_exa | External web search |
|
|
665
|
+
| mcp__exa__get_code_context_exa | External code/doc search |
|
|
666
|
+
|
|
667
|
+
## Strict Boundaries
|
|
668
|
+
|
|
669
|
+
**Only allowed**: Describe what exists, where it is, how it works
|
|
670
|
+
|
|
671
|
+
**Forbidden** (unless explicitly asked):
|
|
672
|
+
- Suggest improvements
|
|
673
|
+
- Criticize implementation
|
|
674
|
+
- Recommend refactoring
|
|
675
|
+
- Modify any files
|
|
676
|
+
|
|
677
|
+
## Report Format
|
|
678
|
+
|
|
679
|
+
Provide structured search results including:
|
|
680
|
+
- List of files found (with paths)
|
|
681
|
+
- Code pattern analysis (if applicable)
|
|
682
|
+
- Related spec documents
|
|
683
|
+
- External references (if any)"""
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def main():
|
|
687
|
+
try:
|
|
688
|
+
input_data = json.load(sys.stdin)
|
|
689
|
+
except json.JSONDecodeError:
|
|
690
|
+
sys.exit(0)
|
|
691
|
+
|
|
692
|
+
tool_name = input_data.get("tool_name", "")
|
|
693
|
+
|
|
694
|
+
if tool_name != "Task":
|
|
695
|
+
sys.exit(0)
|
|
696
|
+
|
|
697
|
+
tool_input = input_data.get("tool_input", {})
|
|
698
|
+
subagent_type = tool_input.get("subagent_type", "")
|
|
699
|
+
original_prompt = tool_input.get("prompt", "")
|
|
700
|
+
cwd = input_data.get("cwd", os.getcwd())
|
|
701
|
+
|
|
702
|
+
# Only handle subagent types we care about
|
|
703
|
+
if subagent_type not in AGENTS_ALL:
|
|
704
|
+
sys.exit(0)
|
|
705
|
+
|
|
706
|
+
# Find repo root
|
|
707
|
+
repo_root = find_repo_root(cwd)
|
|
708
|
+
if not repo_root:
|
|
709
|
+
sys.exit(0)
|
|
710
|
+
|
|
711
|
+
# Get current task directory (research doesn't require it)
|
|
712
|
+
task_dir = get_current_task(repo_root)
|
|
713
|
+
|
|
714
|
+
# implement/check/debug need task directory
|
|
715
|
+
if subagent_type in AGENTS_REQUIRE_TASK:
|
|
716
|
+
if not task_dir:
|
|
717
|
+
sys.exit(0)
|
|
718
|
+
# Check if task directory exists
|
|
719
|
+
task_dir_full = os.path.join(repo_root, task_dir)
|
|
720
|
+
if not os.path.exists(task_dir_full):
|
|
721
|
+
sys.exit(0)
|
|
722
|
+
|
|
723
|
+
# Update current_phase in task.json (system-level enforcement)
|
|
724
|
+
update_current_phase(repo_root, task_dir, subagent_type)
|
|
725
|
+
|
|
726
|
+
# Check for [finish] marker in prompt (check agent with finish context)
|
|
727
|
+
is_finish_phase = "[finish]" in original_prompt.lower()
|
|
728
|
+
|
|
729
|
+
# Get context and build prompt based on subagent type
|
|
730
|
+
if subagent_type == AGENT_IMPLEMENT:
|
|
731
|
+
assert task_dir is not None # validated above
|
|
732
|
+
context = get_implement_context(repo_root, task_dir)
|
|
733
|
+
new_prompt = build_implement_prompt(original_prompt, context)
|
|
734
|
+
elif subagent_type == AGENT_CHECK:
|
|
735
|
+
assert task_dir is not None # validated above
|
|
736
|
+
if is_finish_phase:
|
|
737
|
+
# Finish phase: use finish context (lighter, focused on final verification)
|
|
738
|
+
context = get_finish_context(repo_root, task_dir)
|
|
739
|
+
new_prompt = build_finish_prompt(original_prompt, context)
|
|
740
|
+
else:
|
|
741
|
+
# Regular check phase: use check context (full specs for self-fix loop)
|
|
742
|
+
context = get_check_context(repo_root, task_dir)
|
|
743
|
+
new_prompt = build_check_prompt(original_prompt, context)
|
|
744
|
+
elif subagent_type == AGENT_DEBUG:
|
|
745
|
+
assert task_dir is not None # validated above
|
|
746
|
+
context = get_debug_context(repo_root, task_dir)
|
|
747
|
+
new_prompt = build_debug_prompt(original_prompt, context)
|
|
748
|
+
elif subagent_type == AGENT_RESEARCH:
|
|
749
|
+
# Research can work without task directory
|
|
750
|
+
context = get_research_context(repo_root, task_dir)
|
|
751
|
+
new_prompt = build_research_prompt(original_prompt, context)
|
|
752
|
+
else:
|
|
753
|
+
sys.exit(0)
|
|
754
|
+
|
|
755
|
+
if not context:
|
|
756
|
+
sys.exit(0)
|
|
757
|
+
|
|
758
|
+
# Return updated input with correct Claude Code PreToolUse format
|
|
759
|
+
output = {
|
|
760
|
+
"hookSpecificOutput": {
|
|
761
|
+
"hookEventName": "PreToolUse",
|
|
762
|
+
"permissionDecision": "allow",
|
|
763
|
+
"updatedInput": {**tool_input, "prompt": new_prompt},
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
print(json.dumps(output, ensure_ascii=False))
|
|
768
|
+
sys.exit(0)
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
if __name__ == "__main__":
|
|
772
|
+
main()
|