@prateek_ai/agents-maker 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +659 -0
- package/agents/architect_agent.md +175 -0
- package/agents/code_agent.md +178 -0
- package/agents/compression_agent.md +226 -0
- package/agents/execution_agent.md +157 -0
- package/agents/orchestrator.md +406 -0
- package/agents/reviewer_agent.md +134 -0
- package/agents/ui_agent.md +147 -0
- package/agents/ux_agent.md +144 -0
- package/bin/cli.js +79 -0
- package/config/agents.yaml +388 -0
- package/config/domain_profiles.yaml +394 -0
- package/config/project.yaml.example +16 -0
- package/config/token_policies.yaml +325 -0
- package/context_loaders/__init__.py +15 -0
- package/context_loaders/__pycache__/__init__.cpython-314.pyc +0 -0
- package/context_loaders/__pycache__/file_chunker.cpython-314.pyc +0 -0
- package/context_loaders/__pycache__/project_summary.cpython-314.pyc +0 -0
- package/context_loaders/__pycache__/repo_tree.cpython-314.pyc +0 -0
- package/context_loaders/file_chunker.py +252 -0
- package/context_loaders/project_summary.py +369 -0
- package/context_loaders/repo_tree.py +203 -0
- package/package.json +46 -0
- package/quickstart.ps1 +252 -0
- package/quickstart.sh +232 -0
- package/requirements.txt +3 -0
- package/skills/analyze_repo.md +86 -0
- package/skills/animated_website.md +285 -0
- package/skills/compare_approaches.md +86 -0
- package/skills/define_data_schema.md +99 -0
- package/skills/design_api.md +126 -0
- package/skills/improve_copy.md +69 -0
- package/skills/review_code.md +83 -0
- package/skills/review_layout.md +89 -0
- package/skills/suggest_next.md +105 -0
- package/skills/summarize_history.md +89 -0
- package/skills/write_process_map.md +105 -0
- package/skills/write_tests.md +97 -0
- package/token_optimization/__pycache__/compressor.cpython-314.pyc +0 -0
- package/token_optimization/compressor.py +502 -0
- package/token_optimization/output_styles.md +80 -0
- package/tools/__pycache__/domain_utils.cpython-314.pyc +0 -0
- package/tools/__pycache__/generate_claude_md.cpython-314.pyc +0 -0
- package/tools/__pycache__/validate_kit.cpython-314.pyc +0 -0
- package/tools/domain_utils.py +141 -0
- package/tools/generate_claude_md.py +269 -0
- package/tools/generate_platform_configs.py +467 -0
- package/tools/generate_prompt.py +461 -0
- package/tools/init_project.py +454 -0
- package/tools/test_kit.py +363 -0
- package/tools/validate_kit.py +504 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""
|
|
2
|
+
tools/domain_utils.py
|
|
3
|
+
|
|
4
|
+
Shared domain scoring and detection logic — used by generate_prompt.py,
|
|
5
|
+
init_project.py, and validate_kit.py to avoid triplicated scoring code.
|
|
6
|
+
|
|
7
|
+
Usage (as a module):
|
|
8
|
+
from tools.domain_utils import detect_domain, score_domain
|
|
9
|
+
|
|
10
|
+
Usage (standalone — prints detected domain for a given message):
|
|
11
|
+
python tools/domain_utils.py "build a REST API for user auth"
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
__version__ = "1.0.0"
|
|
17
|
+
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
# Allow running as a script from any working directory
|
|
22
|
+
_HERE = Path(__file__).resolve().parent
|
|
23
|
+
_KIT_DIR = _HERE.parent
|
|
24
|
+
if str(_KIT_DIR) not in sys.path:
|
|
25
|
+
sys.path.insert(0, str(_KIT_DIR))
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
import yaml
|
|
29
|
+
except ImportError:
|
|
30
|
+
print("[ERROR] pyyaml is required: pip install pyyaml", file=sys.stderr)
|
|
31
|
+
sys.exit(1)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _load_yaml(path: Path) -> dict:
|
|
35
|
+
try:
|
|
36
|
+
with open(path, encoding="utf-8") as f:
|
|
37
|
+
return yaml.safe_load(f) or {}
|
|
38
|
+
except yaml.YAMLError as e:
|
|
39
|
+
print(f"[ERROR] YAML parse error in {path}: {e}", file=sys.stderr)
|
|
40
|
+
return {}
|
|
41
|
+
except FileNotFoundError:
|
|
42
|
+
return {}
|
|
43
|
+
except PermissionError:
|
|
44
|
+
print(f"[ERROR] Permission denied reading {path}", file=sys.stderr)
|
|
45
|
+
return {}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def score_domain(
|
|
49
|
+
message: str,
|
|
50
|
+
domains: dict,
|
|
51
|
+
settings: dict,
|
|
52
|
+
*,
|
|
53
|
+
include_score: bool = False,
|
|
54
|
+
) -> tuple[str, str] | tuple[str, str, float]:
|
|
55
|
+
"""
|
|
56
|
+
Score `message` against domain detection signals and return the best match.
|
|
57
|
+
|
|
58
|
+
Returns (domain, confidence) by default.
|
|
59
|
+
Returns (domain, confidence, score) when include_score=True.
|
|
60
|
+
|
|
61
|
+
confidence values: "high" | "medium" | "low"
|
|
62
|
+
"""
|
|
63
|
+
msg_lower = message.lower()
|
|
64
|
+
scores: dict[str, float] = {}
|
|
65
|
+
|
|
66
|
+
for d, cfg in domains.items():
|
|
67
|
+
if d == "general":
|
|
68
|
+
continue
|
|
69
|
+
strong = cfg.get("detection_signals", {}).get("strong", [])
|
|
70
|
+
weak = cfg.get("detection_signals", {}).get("weak", [])
|
|
71
|
+
s = sum(1.0 for sig in strong if sig in msg_lower)
|
|
72
|
+
w = sum(0.4 for sig in weak if sig in msg_lower)
|
|
73
|
+
scores[d] = (s + w) / 3
|
|
74
|
+
|
|
75
|
+
if not scores:
|
|
76
|
+
return ("general", "low", 0.0) if include_score else ("general", "low")
|
|
77
|
+
|
|
78
|
+
ranked = sorted(scores.items(), key=lambda x: -x[1])
|
|
79
|
+
top_d, top_s = ranked[0]
|
|
80
|
+
_, sec_s = ranked[1] if len(ranked) > 1 else ("general", 0.0)
|
|
81
|
+
|
|
82
|
+
conf_t = settings.get("confidence_threshold", 0.40)
|
|
83
|
+
amb_t = settings.get("ambiguity_threshold", 0.10)
|
|
84
|
+
|
|
85
|
+
if top_s < conf_t:
|
|
86
|
+
confidence = "low"
|
|
87
|
+
top_d = "general"
|
|
88
|
+
elif (top_s - sec_s) < amb_t:
|
|
89
|
+
confidence = "medium"
|
|
90
|
+
else:
|
|
91
|
+
confidence = "high"
|
|
92
|
+
|
|
93
|
+
if include_score:
|
|
94
|
+
return top_d, confidence, top_s
|
|
95
|
+
return top_d, confidence
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def detect_domain(
|
|
99
|
+
message: str,
|
|
100
|
+
*,
|
|
101
|
+
kit_dir: Path | None = None,
|
|
102
|
+
include_score: bool = False,
|
|
103
|
+
) -> tuple[str, str] | tuple[str, str, float]:
|
|
104
|
+
"""
|
|
105
|
+
Load domain_profiles.yaml from the kit directory and score `message`.
|
|
106
|
+
|
|
107
|
+
kit_dir defaults to the parent of this file's directory (agents-maker root).
|
|
108
|
+
Returns (domain, confidence) or (domain, confidence, score) when include_score=True.
|
|
109
|
+
"""
|
|
110
|
+
base = kit_dir or _KIT_DIR
|
|
111
|
+
domain_cfg_path = base / "config" / "domain_profiles.yaml"
|
|
112
|
+
|
|
113
|
+
if not domain_cfg_path.exists():
|
|
114
|
+
print(f"[WARN] domain_profiles.yaml not found at {domain_cfg_path} — defaulting to 'general'", file=sys.stderr)
|
|
115
|
+
return ("general", "low", 0.0) if include_score else ("general", "low")
|
|
116
|
+
|
|
117
|
+
raw = _load_yaml(domain_cfg_path)
|
|
118
|
+
if not raw:
|
|
119
|
+
return ("general", "low", 0.0) if include_score else ("general", "low")
|
|
120
|
+
|
|
121
|
+
domains = raw.get("domains", {})
|
|
122
|
+
settings = raw.get("settings", {})
|
|
123
|
+
return score_domain(message, domains, settings, include_score=include_score)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if __name__ == "__main__":
|
|
127
|
+
import argparse
|
|
128
|
+
|
|
129
|
+
parser = argparse.ArgumentParser(description="Detect the domain of a message.")
|
|
130
|
+
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
131
|
+
parser.add_argument("message", nargs="?", help="Message to classify (reads stdin if omitted)")
|
|
132
|
+
args = parser.parse_args()
|
|
133
|
+
|
|
134
|
+
text = args.message or sys.stdin.read().strip()
|
|
135
|
+
if not text:
|
|
136
|
+
print("[ERROR] Provide a message as argument or via stdin.", file=sys.stderr)
|
|
137
|
+
sys.exit(1)
|
|
138
|
+
|
|
139
|
+
result = detect_domain(text, include_score=True)
|
|
140
|
+
domain, confidence, score = result # type: ignore[misc]
|
|
141
|
+
print(f"Domain: {domain} Confidence: {confidence} Score: {score:.3f}")
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
generate_claude_md.py — Writes CLAUDE.md to your project root for Claude Code integration.
|
|
4
|
+
|
|
5
|
+
Claude Code auto-reads CLAUDE.md every session, so agents-maker domain/phase/stack
|
|
6
|
+
context loads automatically — no copy-paste, no CLI step before every message.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python agents-maker/tools/generate_claude_md.py
|
|
10
|
+
python agents-maker/tools/generate_claude_md.py --path /your/project
|
|
11
|
+
python agents-maker/tools/generate_claude_md.py --dry-run
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import argparse
|
|
17
|
+
import os
|
|
18
|
+
import re
|
|
19
|
+
import sys
|
|
20
|
+
import tempfile
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
__version__ = "1.0.0"
|
|
24
|
+
|
|
25
|
+
SCRIPT_DIR = Path(__file__).resolve().parent # agents-maker/tools/
|
|
26
|
+
KIT_DIR = SCRIPT_DIR.parent # agents-maker/
|
|
27
|
+
sys.path.insert(0, str(KIT_DIR))
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
import yaml
|
|
31
|
+
except ImportError:
|
|
32
|
+
print("[ERROR] pyyaml is required: pip install pyyaml", file=sys.stderr)
|
|
33
|
+
sys.exit(1)
|
|
34
|
+
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
# Helpers
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
def _load_yaml(path: Path) -> dict:
|
|
40
|
+
if not path.exists():
|
|
41
|
+
return {}
|
|
42
|
+
try:
|
|
43
|
+
with open(path, encoding="utf-8") as f:
|
|
44
|
+
return yaml.safe_load(f) or {}
|
|
45
|
+
except Exception:
|
|
46
|
+
return {}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _parse_phase(state_text: str) -> str:
|
|
50
|
+
m = re.search(r"##\s+Current Phase\s*\n+(\S+)", state_text)
|
|
51
|
+
if m:
|
|
52
|
+
return m.group(1).strip()
|
|
53
|
+
return "task_framing"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# Phase → domain → active agents mapping (derived from agents.yaml lifecycle)
|
|
57
|
+
_PHASE_AGENTS: dict[str, dict[str, list[str]]] = {
|
|
58
|
+
"task_framing": {"_all": ["orchestrator"]},
|
|
59
|
+
"requirements": {"_all": ["orchestrator", "architect_agent"]},
|
|
60
|
+
"solution_design": {
|
|
61
|
+
"_all": ["architect_agent"],
|
|
62
|
+
"software": ["architect_agent", "ui_agent"],
|
|
63
|
+
"product_design":["architect_agent", "ui_agent", "ux_agent"],
|
|
64
|
+
"marketing": ["architect_agent", "ux_agent"],
|
|
65
|
+
},
|
|
66
|
+
"implementation": {
|
|
67
|
+
"_all": ["execution_agent"],
|
|
68
|
+
"software": ["code_agent"],
|
|
69
|
+
"data_analytics":["code_agent"],
|
|
70
|
+
},
|
|
71
|
+
"review_refinement":{
|
|
72
|
+
"_all": ["reviewer_agent"],
|
|
73
|
+
"software": ["reviewer_agent", "code_agent"],
|
|
74
|
+
"product_design":["reviewer_agent", "ui_agent", "ux_agent"],
|
|
75
|
+
"marketing": ["reviewer_agent", "ux_agent"],
|
|
76
|
+
},
|
|
77
|
+
"handoff": {"_all": ["orchestrator", "execution_agent"]},
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
_PHASE_LABELS: dict[str, str] = {
|
|
81
|
+
"task_framing": "Task Framing",
|
|
82
|
+
"requirements": "Requirements",
|
|
83
|
+
"solution_design": "Solution Design",
|
|
84
|
+
"implementation": "Implementation",
|
|
85
|
+
"review_refinement":"Review & Refinement",
|
|
86
|
+
"handoff": "Handoff",
|
|
87
|
+
# aliases accepted by generate_prompt.py
|
|
88
|
+
"framing": "Task Framing",
|
|
89
|
+
"design": "Solution Design",
|
|
90
|
+
"implement": "Implementation",
|
|
91
|
+
"review": "Review & Refinement",
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
_AGENT_ROLES: dict[str, str] = {
|
|
95
|
+
"orchestrator": "routing",
|
|
96
|
+
"architect_agent": "design",
|
|
97
|
+
"code_agent": "implementation",
|
|
98
|
+
"execution_agent": "execution",
|
|
99
|
+
"ui_agent": "UI",
|
|
100
|
+
"ux_agent": "UX",
|
|
101
|
+
"reviewer_agent": "QA",
|
|
102
|
+
"compression_agent": "compression",
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _py(kit_rel: str, tool: str) -> str:
|
|
107
|
+
"""Return a shell-safe 'python ...' invocation. Quotes the full path when it contains spaces."""
|
|
108
|
+
path = f"{kit_rel}/tools/{tool}"
|
|
109
|
+
return f'python "{path}"' if " " in path else f"python {path}"
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _active_agents(domain: str, phase: str) -> list[str]:
|
|
113
|
+
phase_map = _PHASE_AGENTS.get(phase, {"_all": ["orchestrator"]})
|
|
114
|
+
return phase_map.get(domain, phase_map["_all"])
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# ---------------------------------------------------------------------------
|
|
118
|
+
# Builder
|
|
119
|
+
# ---------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
def build_claude_md(
|
|
122
|
+
project_name: str,
|
|
123
|
+
domain: str,
|
|
124
|
+
confidence: str,
|
|
125
|
+
stack: list[str],
|
|
126
|
+
phase: str,
|
|
127
|
+
kit_rel_path: str,
|
|
128
|
+
) -> str:
|
|
129
|
+
stack_str = ", ".join(stack) if stack else "unknown"
|
|
130
|
+
phase_label = _PHASE_LABELS.get(phase, phase)
|
|
131
|
+
agents = _active_agents(domain, phase)
|
|
132
|
+
agent_list = ", ".join(
|
|
133
|
+
f"{a} ({_AGENT_ROLES.get(a, 'specialist')})" for a in agents
|
|
134
|
+
)
|
|
135
|
+
regen_cmd = _py(kit_rel_path, "generate_claude_md.py")
|
|
136
|
+
prompt_cmd = _py(kit_rel_path, "generate_prompt.py")
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
f"# agents-maker — Project AI Config\n"
|
|
140
|
+
f"# Auto-generated: {regen_cmd}\n"
|
|
141
|
+
f"# Regenerate after domain/phase changes: {regen_cmd}\n"
|
|
142
|
+
f"\n"
|
|
143
|
+
f"## Active Domain\n"
|
|
144
|
+
f"{domain} (confidence: {confidence})\n"
|
|
145
|
+
f"\n"
|
|
146
|
+
f"## Stack\n"
|
|
147
|
+
f"{stack_str}\n"
|
|
148
|
+
f"\n"
|
|
149
|
+
f"## Current Phase\n"
|
|
150
|
+
f"{phase_label} (`{phase}`)\n"
|
|
151
|
+
f"\n"
|
|
152
|
+
f"## Agent Routing\n"
|
|
153
|
+
f"All tasks in this project route through the agents-maker multi-agent kit.\n"
|
|
154
|
+
f"Orchestrator is always active. Specialist agents for this phase: {agent_list}.\n"
|
|
155
|
+
f"\n"
|
|
156
|
+
f"## Session Instructions\n"
|
|
157
|
+
f"- Apply domain routing and phase context from agents-maker before every task.\n"
|
|
158
|
+
f"- After every response: append a [Companion] block with 3 ranked next steps.\n"
|
|
159
|
+
f"- Include a `Command:` line the user can copy to continue the workflow.\n"
|
|
160
|
+
f"\n"
|
|
161
|
+
f"## Kit Location\n"
|
|
162
|
+
f"{kit_rel_path}/ (relative to project root)\n"
|
|
163
|
+
f'Generate a fresh prompt: `{prompt_cmd} "your task"`\n'
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# ---------------------------------------------------------------------------
|
|
168
|
+
# Main
|
|
169
|
+
# ---------------------------------------------------------------------------
|
|
170
|
+
|
|
171
|
+
def main() -> None:
|
|
172
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
173
|
+
|
|
174
|
+
parser = argparse.ArgumentParser(
|
|
175
|
+
description="Write CLAUDE.md to your project root for Claude Code integration.",
|
|
176
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
177
|
+
epilog=(
|
|
178
|
+
"Examples:\n"
|
|
179
|
+
" python agents-maker/tools/generate_claude_md.py\n"
|
|
180
|
+
" python agents-maker/tools/generate_claude_md.py --path /my/project\n"
|
|
181
|
+
" python agents-maker/tools/generate_claude_md.py --dry-run\n"
|
|
182
|
+
),
|
|
183
|
+
)
|
|
184
|
+
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
185
|
+
parser.add_argument(
|
|
186
|
+
"--path",
|
|
187
|
+
help="Project root directory (default: parent of agents-maker/)",
|
|
188
|
+
)
|
|
189
|
+
parser.add_argument(
|
|
190
|
+
"--dry-run",
|
|
191
|
+
action="store_true",
|
|
192
|
+
help="Print generated CLAUDE.md to stdout without writing the file.",
|
|
193
|
+
)
|
|
194
|
+
args = parser.parse_args()
|
|
195
|
+
|
|
196
|
+
# Resolve project root
|
|
197
|
+
if args.path:
|
|
198
|
+
project_root = Path(args.path).resolve()
|
|
199
|
+
else:
|
|
200
|
+
project_root = KIT_DIR.parent
|
|
201
|
+
|
|
202
|
+
if not project_root.exists() or not project_root.is_dir():
|
|
203
|
+
print(
|
|
204
|
+
f"[ERROR] Project root does not exist or is not a directory: {project_root}",
|
|
205
|
+
file=sys.stderr,
|
|
206
|
+
)
|
|
207
|
+
sys.exit(1)
|
|
208
|
+
|
|
209
|
+
# Load project config
|
|
210
|
+
project_cfg = _load_yaml(KIT_DIR / "config" / "project.yaml")
|
|
211
|
+
if not project_cfg:
|
|
212
|
+
print(
|
|
213
|
+
"[WARN] config/project.yaml not found — run init_project.py first.",
|
|
214
|
+
file=sys.stderr,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
domain = project_cfg.get("primary_domain", "general")
|
|
218
|
+
stack = project_cfg.get("stack", [])
|
|
219
|
+
if isinstance(stack, str):
|
|
220
|
+
stack = [s.strip() for s in stack.split(",") if s.strip()]
|
|
221
|
+
|
|
222
|
+
# domain_confidence is not stored in project.yaml yet; infer from presence of the key
|
|
223
|
+
confidence = "high" if project_cfg.get("primary_domain") else "low"
|
|
224
|
+
|
|
225
|
+
# Read current phase from project_state.md
|
|
226
|
+
state_path = KIT_DIR / "project_state.md"
|
|
227
|
+
if state_path.exists():
|
|
228
|
+
phase = _parse_phase(state_path.read_text(encoding="utf-8"))
|
|
229
|
+
else:
|
|
230
|
+
phase = "task_framing"
|
|
231
|
+
|
|
232
|
+
# Compute kit path relative to project root (for portability)
|
|
233
|
+
try:
|
|
234
|
+
kit_rel = KIT_DIR.relative_to(project_root)
|
|
235
|
+
kit_rel_path = str(kit_rel).replace("\\", "/")
|
|
236
|
+
except ValueError:
|
|
237
|
+
kit_rel_path = "agents-maker"
|
|
238
|
+
|
|
239
|
+
content = build_claude_md(
|
|
240
|
+
project_name=project_root.name,
|
|
241
|
+
domain=domain,
|
|
242
|
+
confidence=confidence,
|
|
243
|
+
stack=stack,
|
|
244
|
+
phase=phase,
|
|
245
|
+
kit_rel_path=kit_rel_path,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
if args.dry_run:
|
|
249
|
+
print(content)
|
|
250
|
+
return
|
|
251
|
+
|
|
252
|
+
out_path = project_root / "CLAUDE.md"
|
|
253
|
+
with tempfile.NamedTemporaryFile("w", dir=out_path.parent, delete=False, suffix=".tmp", encoding="utf-8") as f:
|
|
254
|
+
f.write(content)
|
|
255
|
+
tmp = f.name
|
|
256
|
+
os.replace(tmp, out_path)
|
|
257
|
+
|
|
258
|
+
print(f"\nWritten: {out_path}")
|
|
259
|
+
print(f" Domain : {domain} (confidence: {confidence})")
|
|
260
|
+
print(f" Stack : {', '.join(stack) if stack else 'unknown'}")
|
|
261
|
+
print(f" Phase : {phase}")
|
|
262
|
+
print()
|
|
263
|
+
print("Claude Code will auto-load this context on every session.")
|
|
264
|
+
print("Commit CLAUDE.md to git — it is project config, not private state.")
|
|
265
|
+
print()
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
if __name__ == "__main__":
|
|
269
|
+
main()
|