@prateek_ai/agents-maker 1.0.0 → 1.0.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/PROMPT_TEMPLATE.md +143 -0
- package/README.md +92 -25
- package/agents/brain.md +90 -0
- package/agents/orchestrator.md +24 -3
- package/agents/planpro.md +91 -0
- package/bin/cli.js +40 -4
- package/claude/agents/architect.md +182 -0
- package/claude/agents/brain.md +97 -0
- package/claude/agents/code.md +185 -0
- package/claude/agents/compress.md +233 -0
- package/claude/agents/execute.md +164 -0
- package/claude/agents/orchestrate.md +434 -0
- package/claude/agents/planpro.md +98 -0
- package/claude/agents/review.md +141 -0
- package/claude/agents/ui.md +154 -0
- package/claude/agents/ux.md +151 -0
- package/claude/commands/architect.md +15 -0
- package/claude/commands/brain.md +15 -0
- package/claude/commands/code.md +15 -0
- package/claude/commands/compress.md +15 -0
- package/claude/commands/execute.md +15 -0
- package/claude/commands/orchestrate.md +15 -0
- package/claude/commands/planpro.md +15 -0
- package/claude/commands/review.md +15 -0
- package/claude/commands/ui.md +15 -0
- package/claude/commands/ux.md +15 -0
- package/config/agents.yaml +56 -0
- package/context_loaders/project_summary.py +5 -0
- package/docs/architecture.md +318 -0
- package/docs/domains.md +154 -0
- package/docs/workflows.md +548 -0
- package/examples/generic_project_lifecycle.md +518 -0
- package/examples/proof/README.md +138 -0
- package/examples/proof/implement-a-python-function-that-parses-an-iso-8/grade.md +14 -0
- package/examples/proof/implement-a-python-function-that-parses-an-iso-8/naive_output.md +211 -0
- package/examples/proof/implement-a-python-function-that-parses-an-iso-8/naive_prompt.txt +1 -0
- package/examples/proof/implement-a-python-function-that-parses-an-iso-8/structured_output.md +210 -0
- package/examples/proof/implement-a-python-function-that-parses-an-iso-8/structured_user.txt +35 -0
- package/examples/proof/write-a-runbook-for-recovering-from-a-redis-prim/grade.md +14 -0
- package/examples/proof/write-a-runbook-for-recovering-from-a-redis-prim/naive_output.md +274 -0
- package/examples/proof/write-a-runbook-for-recovering-from-a-redis-prim/naive_prompt.txt +1 -0
- package/examples/proof/write-a-runbook-for-recovering-from-a-redis-prim/structured_output.md +127 -0
- package/examples/proof/write-a-runbook-for-recovering-from-a-redis-prim/structured_user.txt +35 -0
- package/package.json +10 -2
- package/platforms/antigravity.md +195 -0
- package/platforms/claude.md +305 -0
- package/platforms/openai.md +333 -0
- package/skills/analyze_repo.md +86 -86
- package/token_optimization/compressor.py +4 -7
- package/tools/_core.py +102 -0
- package/tools/compare_prompts.py +196 -0
- package/tools/generate_claude_agents.py +162 -0
- package/tools/generate_claude_md.py +14 -80
- package/tools/generate_platform_configs.py +26 -36
- package/tools/generate_prompt.py +79 -79
- package/tools/grade_proof.py +118 -0
- package/tools/init_project.py +11 -58
- package/tools/routing.py +103 -0
- package/tools/test_kit.py +392 -363
- package/tools/validate_kit.py +15 -43
- 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/token_optimization/__pycache__/compressor.cpython-314.pyc +0 -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/init_project.py
CHANGED
|
@@ -11,10 +11,7 @@ Usage:
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
13
|
import argparse
|
|
14
|
-
import hashlib
|
|
15
|
-
import os
|
|
16
14
|
import sys
|
|
17
|
-
import tempfile
|
|
18
15
|
from datetime import date
|
|
19
16
|
from pathlib import Path
|
|
20
17
|
|
|
@@ -28,12 +25,6 @@ SCRIPT_DIR = Path(__file__).resolve().parent # agents-maker/tools/
|
|
|
28
25
|
KIT_DIR = SCRIPT_DIR.parent # agents-maker/
|
|
29
26
|
sys.path.insert(0, str(KIT_DIR))
|
|
30
27
|
|
|
31
|
-
try:
|
|
32
|
-
import yaml
|
|
33
|
-
except ImportError:
|
|
34
|
-
print("[ERROR] pyyaml is required: pip install pyyaml", file=sys.stderr)
|
|
35
|
-
sys.exit(1)
|
|
36
|
-
|
|
37
28
|
try:
|
|
38
29
|
from context_loaders.project_summary import build_summary
|
|
39
30
|
from context_loaders.repo_tree import walk_tree
|
|
@@ -43,10 +34,10 @@ except ImportError as e:
|
|
|
43
34
|
sys.exit(1)
|
|
44
35
|
|
|
45
36
|
try:
|
|
46
|
-
from tools.
|
|
37
|
+
from tools._core import atomic_write, atomic_write_yaml, load_yaml, source_hash
|
|
47
38
|
from tools.domain_utils import detect_domain as _detect_domain
|
|
48
39
|
except ImportError:
|
|
49
|
-
from
|
|
40
|
+
from _core import atomic_write, atomic_write_yaml, load_yaml, source_hash
|
|
50
41
|
from domain_utils import detect_domain as _detect_domain
|
|
51
42
|
|
|
52
43
|
|
|
@@ -82,46 +73,6 @@ def _sanitize_yaml_str(value: str, field: str) -> str:
|
|
|
82
73
|
return value
|
|
83
74
|
|
|
84
75
|
|
|
85
|
-
# ---------------------------------------------------------------------------
|
|
86
|
-
# Source hash — lets validate_kit.py detect stale system_prompt.md
|
|
87
|
-
# ---------------------------------------------------------------------------
|
|
88
|
-
|
|
89
|
-
def _compute_source_hash(kit_dir: Path) -> str:
|
|
90
|
-
h = hashlib.sha256()
|
|
91
|
-
agent_files = sorted((kit_dir / "agents").glob("*.md")) if (kit_dir / "agents").is_dir() else []
|
|
92
|
-
skill_files = sorted((kit_dir / "skills").glob("*.md")) if (kit_dir / "skills").is_dir() else []
|
|
93
|
-
for path in agent_files + skill_files:
|
|
94
|
-
try:
|
|
95
|
-
h.update(path.read_bytes().replace(b"\r\n", b"\n"))
|
|
96
|
-
except (OSError, PermissionError):
|
|
97
|
-
pass
|
|
98
|
-
return h.hexdigest()[:16]
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
# ---------------------------------------------------------------------------
|
|
102
|
-
# Atomic write helpers (write to temp then os.replace — crash-safe)
|
|
103
|
-
# ---------------------------------------------------------------------------
|
|
104
|
-
|
|
105
|
-
def _atomic_write_text(path: Path, content: str) -> None:
|
|
106
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
107
|
-
with tempfile.NamedTemporaryFile(
|
|
108
|
-
"w", dir=path.parent, delete=False, suffix=".tmp", encoding="utf-8"
|
|
109
|
-
) as f:
|
|
110
|
-
f.write(content)
|
|
111
|
-
tmp = f.name
|
|
112
|
-
os.replace(tmp, path)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def _atomic_write_yaml(path: Path, data: dict) -> None:
|
|
116
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
117
|
-
with tempfile.NamedTemporaryFile(
|
|
118
|
-
"w", dir=path.parent, delete=False, suffix=".tmp", encoding="utf-8"
|
|
119
|
-
) as f:
|
|
120
|
-
yaml.dump(data, f, default_flow_style=False, allow_unicode=True, sort_keys=False)
|
|
121
|
-
tmp = f.name
|
|
122
|
-
os.replace(tmp, path)
|
|
123
|
-
|
|
124
|
-
|
|
125
76
|
# ---------------------------------------------------------------------------
|
|
126
77
|
# system_prompt.md builder
|
|
127
78
|
# ---------------------------------------------------------------------------
|
|
@@ -130,6 +81,8 @@ def build_system_prompt(project_name: str, domain: str, stack: list[str]) -> str
|
|
|
130
81
|
agents_dir = KIT_DIR / "agents"
|
|
131
82
|
agent_order = [
|
|
132
83
|
"orchestrator.md",
|
|
84
|
+
"brain.md",
|
|
85
|
+
"planpro.md",
|
|
133
86
|
"architect_agent.md",
|
|
134
87
|
"code_agent.md",
|
|
135
88
|
"execution_agent.md",
|
|
@@ -139,10 +92,10 @@ def build_system_prompt(project_name: str, domain: str, stack: list[str]) -> str
|
|
|
139
92
|
"compression_agent.md",
|
|
140
93
|
]
|
|
141
94
|
skill_count = len(list((KIT_DIR / "skills").glob("*.md"))) if (KIT_DIR / "skills").exists() else 0
|
|
142
|
-
|
|
95
|
+
src_hash = source_hash(KIT_DIR)
|
|
143
96
|
version_header = (
|
|
144
97
|
f"# agents-maker system_prompt.md\n"
|
|
145
|
-
f"# Version: 1.0 | Generated: {date.today().isoformat()} | Source hash: {
|
|
98
|
+
f"# Version: 1.0 | Generated: {date.today().isoformat()} | Source hash: {src_hash}\n"
|
|
146
99
|
f"# Regenerate: python agents-maker/tools/init_project.py --update\n"
|
|
147
100
|
f"# Contains: {len(agent_order)} agents + {skill_count} skills\n"
|
|
148
101
|
f"#\n"
|
|
@@ -332,7 +285,7 @@ def main() -> None:
|
|
|
332
285
|
project_yaml_path = config_dir / "project.yaml"
|
|
333
286
|
|
|
334
287
|
# Preserve session_count if updating
|
|
335
|
-
existing_cfg =
|
|
288
|
+
existing_cfg = load_yaml(project_yaml_path)
|
|
336
289
|
session_count = existing_cfg.get("session_count", 0) if args.update else 0
|
|
337
290
|
created_at = existing_cfg.get("created_at", date.today().isoformat()) if args.update else date.today().isoformat()
|
|
338
291
|
|
|
@@ -349,7 +302,7 @@ def main() -> None:
|
|
|
349
302
|
"last_session": existing_cfg.get("last_session") if args.update else None,
|
|
350
303
|
}
|
|
351
304
|
try:
|
|
352
|
-
|
|
305
|
+
atomic_write_yaml(project_yaml_path, project_cfg)
|
|
353
306
|
except OSError as e:
|
|
354
307
|
print(f"[ERROR] Could not write project.yaml: {e}", file=sys.stderr)
|
|
355
308
|
sys.exit(1)
|
|
@@ -359,7 +312,7 @@ def main() -> None:
|
|
|
359
312
|
if not system_prompt_path.exists() or args.update:
|
|
360
313
|
system_prompt_text = build_system_prompt(project_name, final_domain, stack)
|
|
361
314
|
try:
|
|
362
|
-
|
|
315
|
+
atomic_write(system_prompt_path, system_prompt_text)
|
|
363
316
|
except OSError as e:
|
|
364
317
|
print(f"[ERROR] Could not write system_prompt.md: {e}", file=sys.stderr)
|
|
365
318
|
sys.exit(1)
|
|
@@ -375,7 +328,7 @@ def main() -> None:
|
|
|
375
328
|
state_path = KIT_DIR / "project_state.md"
|
|
376
329
|
if not state_path.exists():
|
|
377
330
|
try:
|
|
378
|
-
|
|
331
|
+
atomic_write(state_path, STATE_TEMPLATE)
|
|
379
332
|
state_created = True
|
|
380
333
|
except OSError as e:
|
|
381
334
|
print(f"[ERROR] Could not write project_state.md: {e}", file=sys.stderr)
|
|
@@ -437,7 +390,7 @@ def main() -> None:
|
|
|
437
390
|
)
|
|
438
391
|
claude_md_path = project_root / "CLAUDE.md"
|
|
439
392
|
try:
|
|
440
|
-
|
|
393
|
+
atomic_write(claude_md_path, claude_md_content)
|
|
441
394
|
print(f" [DONE] CLAUDE.md written to {claude_md_path}")
|
|
442
395
|
print(" Claude Code will auto-load domain/phase/stack on every session.")
|
|
443
396
|
print(" Commit CLAUDE.md to git — it is project config, not private state.")
|
package/tools/routing.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
tools/routing.py
|
|
3
|
+
|
|
4
|
+
Single source of truth for lifecycle routing metadata: which specialist
|
|
5
|
+
agents are active for a given (phase, domain), plus phase labels and agent
|
|
6
|
+
role labels used across the generators.
|
|
7
|
+
|
|
8
|
+
Previously this mapping was hardcoded independently in three places
|
|
9
|
+
(generate_prompt.py, generate_claude_md.py, generate_platform_configs.py),
|
|
10
|
+
and the three copies had drifted apart. Centralizing them here makes the
|
|
11
|
+
routing consistent across every generated artifact (the pasted prompt,
|
|
12
|
+
CLAUDE.md, Copilot/Cursor rules, and the agkit config).
|
|
13
|
+
|
|
14
|
+
Two views are exposed because the tools present routing differently:
|
|
15
|
+
* phase_agents(phase, domain) -> the phase's agent list as declared in the
|
|
16
|
+
table (used where the orchestrator is mentioned separately, e.g. CLAUDE.md).
|
|
17
|
+
* active_agents(phase, domain) -> orchestrator-first, deduplicated (used for
|
|
18
|
+
the "Active agents" line of a generated prompt).
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
# Phase -> domain -> agents. "_all" is the default for domains without an override.
|
|
24
|
+
PHASE_AGENTS: dict[str, dict[str, list[str]]] = {
|
|
25
|
+
"task_framing": {"_all": ["orchestrator"]},
|
|
26
|
+
"requirements": {"_all": ["orchestrator", "architect_agent"]},
|
|
27
|
+
"solution_design": {
|
|
28
|
+
"_all": ["architect_agent"],
|
|
29
|
+
"software": ["architect_agent", "ui_agent"],
|
|
30
|
+
"product_design": ["architect_agent", "ui_agent", "ux_agent"],
|
|
31
|
+
"marketing": ["architect_agent", "ux_agent"],
|
|
32
|
+
},
|
|
33
|
+
"implementation": {
|
|
34
|
+
"_all": ["execution_agent"],
|
|
35
|
+
"software": ["code_agent"],
|
|
36
|
+
"data_analytics": ["code_agent"],
|
|
37
|
+
},
|
|
38
|
+
"review_refinement": {
|
|
39
|
+
"_all": ["reviewer_agent"],
|
|
40
|
+
"software": ["reviewer_agent", "code_agent"],
|
|
41
|
+
"product_design": ["reviewer_agent", "ui_agent", "ux_agent"],
|
|
42
|
+
"marketing": ["reviewer_agent", "ux_agent"],
|
|
43
|
+
},
|
|
44
|
+
"handoff": {"_all": ["orchestrator", "execution_agent"]},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
PHASE_LABELS: dict[str, str] = {
|
|
48
|
+
"task_framing": "Task Framing",
|
|
49
|
+
"requirements": "Requirements",
|
|
50
|
+
"solution_design": "Solution Design",
|
|
51
|
+
"implementation": "Implementation",
|
|
52
|
+
"review_refinement": "Review & Refinement",
|
|
53
|
+
"handoff": "Handoff",
|
|
54
|
+
# aliases accepted by generate_prompt.py
|
|
55
|
+
"framing": "Task Framing",
|
|
56
|
+
"design": "Solution Design",
|
|
57
|
+
"implement": "Implementation",
|
|
58
|
+
"review": "Review & Refinement",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
AGENT_ROLES: dict[str, str] = {
|
|
62
|
+
"orchestrator": "routing",
|
|
63
|
+
"architect_agent": "design",
|
|
64
|
+
"code_agent": "implementation",
|
|
65
|
+
"execution_agent": "execution",
|
|
66
|
+
"ui_agent": "UI",
|
|
67
|
+
"ux_agent": "UX",
|
|
68
|
+
"reviewer_agent": "QA",
|
|
69
|
+
"compression_agent": "compression",
|
|
70
|
+
"brain": "brainstorm",
|
|
71
|
+
"planpro": "planning",
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def phase_agents(phase: str, domain: str) -> list[str]:
|
|
76
|
+
"""Agents declared for (phase, domain), falling back to the phase's `_all` list."""
|
|
77
|
+
phase_map = PHASE_AGENTS.get(phase, {"_all": ["orchestrator"]})
|
|
78
|
+
return list(phase_map.get(domain, phase_map["_all"]))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def active_agents(phase: str, domain: str) -> list[str]:
|
|
82
|
+
"""Orchestrator-first, de-duplicated agent list for a generated prompt."""
|
|
83
|
+
agents = ["orchestrator"]
|
|
84
|
+
for a in phase_agents(phase, domain):
|
|
85
|
+
if a not in agents:
|
|
86
|
+
agents.append(a)
|
|
87
|
+
return agents
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def domain_agents(domain: str) -> list[str]:
|
|
91
|
+
"""Every agent this domain uses across all phases (union), orchestrator first.
|
|
92
|
+
|
|
93
|
+
Used to scope a self-contained/Direct-Task-Mode system prompt: it must carry
|
|
94
|
+
the domain's implementation + review specialists (and their skills), not just
|
|
95
|
+
the phase-0 orchestrator — otherwise the skills that add value get dropped.
|
|
96
|
+
Still excludes agents from other domains, so it stays smaller than the full kit.
|
|
97
|
+
"""
|
|
98
|
+
seen: list[str] = ["orchestrator"]
|
|
99
|
+
for phase in PHASE_AGENTS:
|
|
100
|
+
for a in active_agents(phase, domain):
|
|
101
|
+
if a not in seen:
|
|
102
|
+
seen.append(a)
|
|
103
|
+
return seen
|