@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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +659 -0
  3. package/agents/architect_agent.md +175 -0
  4. package/agents/code_agent.md +178 -0
  5. package/agents/compression_agent.md +226 -0
  6. package/agents/execution_agent.md +157 -0
  7. package/agents/orchestrator.md +406 -0
  8. package/agents/reviewer_agent.md +134 -0
  9. package/agents/ui_agent.md +147 -0
  10. package/agents/ux_agent.md +144 -0
  11. package/bin/cli.js +79 -0
  12. package/config/agents.yaml +388 -0
  13. package/config/domain_profiles.yaml +394 -0
  14. package/config/project.yaml.example +16 -0
  15. package/config/token_policies.yaml +325 -0
  16. package/context_loaders/__init__.py +15 -0
  17. package/context_loaders/__pycache__/__init__.cpython-314.pyc +0 -0
  18. package/context_loaders/__pycache__/file_chunker.cpython-314.pyc +0 -0
  19. package/context_loaders/__pycache__/project_summary.cpython-314.pyc +0 -0
  20. package/context_loaders/__pycache__/repo_tree.cpython-314.pyc +0 -0
  21. package/context_loaders/file_chunker.py +252 -0
  22. package/context_loaders/project_summary.py +369 -0
  23. package/context_loaders/repo_tree.py +203 -0
  24. package/package.json +46 -0
  25. package/quickstart.ps1 +252 -0
  26. package/quickstart.sh +232 -0
  27. package/requirements.txt +3 -0
  28. package/skills/analyze_repo.md +86 -0
  29. package/skills/animated_website.md +285 -0
  30. package/skills/compare_approaches.md +86 -0
  31. package/skills/define_data_schema.md +99 -0
  32. package/skills/design_api.md +126 -0
  33. package/skills/improve_copy.md +69 -0
  34. package/skills/review_code.md +83 -0
  35. package/skills/review_layout.md +89 -0
  36. package/skills/suggest_next.md +105 -0
  37. package/skills/summarize_history.md +89 -0
  38. package/skills/write_process_map.md +105 -0
  39. package/skills/write_tests.md +97 -0
  40. package/token_optimization/__pycache__/compressor.cpython-314.pyc +0 -0
  41. package/token_optimization/compressor.py +502 -0
  42. package/token_optimization/output_styles.md +80 -0
  43. package/tools/__pycache__/domain_utils.cpython-314.pyc +0 -0
  44. package/tools/__pycache__/generate_claude_md.cpython-314.pyc +0 -0
  45. package/tools/__pycache__/validate_kit.cpython-314.pyc +0 -0
  46. package/tools/domain_utils.py +141 -0
  47. package/tools/generate_claude_md.py +269 -0
  48. package/tools/generate_platform_configs.py +467 -0
  49. package/tools/generate_prompt.py +461 -0
  50. package/tools/init_project.py +454 -0
  51. package/tools/test_kit.py +363 -0
  52. 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()