@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,502 @@
1
+ """
2
+ token_optimization/compressor.py
3
+
4
+ Skeleton for the programmatic token optimization layer.
5
+ Applies token policies from config/token_policies.yaml before sending
6
+ a prompt to any LLM provider.
7
+
8
+ Usage (dry run, no API calls):
9
+ python token_optimization/compressor.py --dry-run \\
10
+ --policy feature_implementation \\
11
+ --context-file /path/to/context.txt \\
12
+ --query "Add soft-delete to UserService"
13
+
14
+ This module has NO live HTTP calls. Implement a ProviderAdapter subclass
15
+ and call adapter.send(compressed_context) to integrate with a real API.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import argparse
21
+ import json
22
+ import sys
23
+ from abc import ABC, abstractmethod
24
+ from dataclasses import dataclass, field
25
+ from pathlib import Path
26
+ from typing import Any
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # Data types
30
+ # ---------------------------------------------------------------------------
31
+
32
+ @dataclass
33
+ class FileEntry:
34
+ path: str
35
+ content: str
36
+ relevance_score: float = 0.0
37
+ truncated: bool = False
38
+ lines_omitted: int = 0
39
+
40
+
41
+ @dataclass
42
+ class TokenPolicy:
43
+ workflow: str
44
+ max_input_files: int = 8
45
+ max_input_tokens: int = 24000
46
+ history_summarize_after_turns: int = 6
47
+ output_style: str = "standard"
48
+ relevance_drop_threshold: float = 0.35
49
+ snippet_max_lines: int = 200
50
+ snippet_head_lines: int = 40
51
+ snippet_tail_lines: int = 40
52
+
53
+
54
+ @dataclass
55
+ class ContextBlock:
56
+ project_state: str = ""
57
+ files: list[FileEntry] = field(default_factory=list)
58
+ conversation_state: str = ""
59
+ active_query: str = ""
60
+
61
+
62
+ @dataclass
63
+ class CompressionReport:
64
+ turns_summarized: int = 0
65
+ files_dropped: list[tuple[str, float]] = field(default_factory=list)
66
+ files_truncated: list[tuple[str, int]] = field(default_factory=list)
67
+ files_retained: int = 0
68
+ estimated_token_reduction_pct: float = 0.0
69
+ warnings: list[str] = field(default_factory=list)
70
+
71
+
72
+ # ---------------------------------------------------------------------------
73
+ # Policy loader
74
+ # ---------------------------------------------------------------------------
75
+
76
+ class PolicyLoader:
77
+ """Loads token policies from config/token_policies.yaml."""
78
+
79
+ def __init__(self, config_path: str | Path = "config/token_policies.yaml") -> None:
80
+ self._config_path = Path(config_path)
81
+ self._raw: dict[str, Any] = {}
82
+
83
+ def load(self) -> None:
84
+ """Parse the YAML config file. Requires PyYAML if used; falls back to empty defaults."""
85
+ try:
86
+ import yaml # optional dep
87
+ with self._config_path.open() as f:
88
+ self._raw = yaml.safe_load(f) or {}
89
+ except ImportError:
90
+ print(
91
+ "[compressor] PyYAML not installed — using built-in defaults. "
92
+ "Install pyyaml to load token_policies.yaml.",
93
+ file=sys.stderr,
94
+ )
95
+ except FileNotFoundError:
96
+ print(
97
+ f"[compressor] config file not found at {self._config_path} — using defaults.",
98
+ file=sys.stderr,
99
+ )
100
+
101
+ def get_workflow_policy(self, workflow: str) -> TokenPolicy:
102
+ """Return a TokenPolicy for the given workflow name."""
103
+ defaults = self._raw.get("defaults", {})
104
+ workflow_overrides = self._raw.get("workflows", {}).get(workflow, {})
105
+ merged = {**defaults, **workflow_overrides}
106
+ return TokenPolicy(
107
+ workflow=workflow,
108
+ max_input_files=merged.get("max_input_files", 8),
109
+ max_input_tokens=merged.get("max_input_tokens", 24000),
110
+ history_summarize_after_turns=merged.get("history_summarize_after_turns", 6),
111
+ output_style=merged.get("output_style", "standard"),
112
+ relevance_drop_threshold=merged.get("relevance_drop_threshold", 0.35),
113
+ snippet_max_lines=merged.get("snippet_max_lines", 200),
114
+ snippet_head_lines=merged.get("snippet_head_lines", 40),
115
+ snippet_tail_lines=merged.get("snippet_tail_lines", 40),
116
+ )
117
+
118
+
119
+ # ---------------------------------------------------------------------------
120
+ # Relevance filter
121
+ # ---------------------------------------------------------------------------
122
+
123
+ class RelevanceFilter:
124
+ """
125
+ Scores and selects files for inclusion in the context block.
126
+ See token_optimization/relevance_filter.md for the full scoring model.
127
+ """
128
+
129
+ weights = {
130
+ "lexical_overlap": 0.35,
131
+ "direct_reference": 0.30,
132
+ "symbol_mention": 0.20,
133
+ "recency": 0.10,
134
+ "structural_importance": 0.05,
135
+ }
136
+
137
+ _structural_patterns = ("pyproject.toml", "package.json", "setup.py", "Makefile", "schema")
138
+
139
+ def __init__(self, policy: TokenPolicy) -> None:
140
+ self.policy = policy
141
+
142
+ def score_files(self, files: list[FileEntry], query: str) -> list[FileEntry]:
143
+ """Compute relevance scores for each file in-place and return the list."""
144
+ query_tokens = set(query.lower().split())
145
+ for f in files:
146
+ f.relevance_score = self._score(f, query_tokens, query)
147
+ return files
148
+
149
+ def select(self, scored_files: list[FileEntry]) -> tuple[list[FileEntry], list[FileEntry]]:
150
+ """
151
+ Partition scored files into (retained, dropped).
152
+ Retained files are sorted by score descending and capped at max_input_files.
153
+ """
154
+ above_threshold = [
155
+ f for f in scored_files
156
+ if f.relevance_score >= self.policy.relevance_drop_threshold
157
+ ]
158
+ dropped = [
159
+ f for f in scored_files
160
+ if f.relevance_score < self.policy.relevance_drop_threshold
161
+ ]
162
+ above_threshold.sort(key=lambda f: f.relevance_score, reverse=True)
163
+ retained = above_threshold[: self.policy.max_input_files]
164
+ dropped += above_threshold[self.policy.max_input_files :]
165
+ return retained, dropped
166
+
167
+ # ------------------------------------------------------------------
168
+ # Private helpers
169
+ # ------------------------------------------------------------------
170
+
171
+ def _score(self, f: FileEntry, query_tokens: set[str], raw_query: str) -> float:
172
+ content_lower = f.content.lower()
173
+ path_lower = f.path.lower()
174
+
175
+ lexical = self._lexical_overlap(content_lower, query_tokens)
176
+ direct = 1.0 if f.path in raw_query or Path(f.path).stem.lower() in raw_query.lower() else 0.0
177
+ symbol = self._symbol_mention(content_lower, raw_query)
178
+ structural = 1.0 if any(p in path_lower for p in self._structural_patterns) else 0.0
179
+
180
+ # recency: caller must set f.relevance_score > 0 before calling this method
181
+ # to use it as a recency hint; otherwise defaults to 0.
182
+ recency = 0.0
183
+
184
+ return (
185
+ self.weights["lexical_overlap"] * lexical
186
+ + self.weights["direct_reference"] * direct
187
+ + self.weights["symbol_mention"] * symbol
188
+ + self.weights["recency"] * recency
189
+ + self.weights["structural_importance"] * structural
190
+ )
191
+
192
+ @staticmethod
193
+ def _lexical_overlap(content: str, query_tokens: set[str]) -> float:
194
+ if not query_tokens:
195
+ return 0.0
196
+ matches = sum(1 for t in query_tokens if t in content)
197
+ return matches / len(query_tokens)
198
+
199
+ @staticmethod
200
+ def _symbol_mention(content: str, query: str) -> float:
201
+ """Detect if any identifier from content appears in the query."""
202
+ import re
203
+ identifiers = set(re.findall(r"\b[a-zA-Z_][a-zA-Z0-9_]{3,}\b", content))
204
+ query_lower = query.lower()
205
+ for ident in identifiers:
206
+ if ident.lower() in query_lower:
207
+ return 1.0
208
+ return 0.0
209
+
210
+
211
+ # ---------------------------------------------------------------------------
212
+ # Snippet truncator
213
+ # ---------------------------------------------------------------------------
214
+
215
+ class SnippetTruncator:
216
+ """Truncates large file snippets according to the active token policy."""
217
+
218
+ def __init__(self, policy: TokenPolicy) -> None:
219
+ self.policy = policy
220
+
221
+ def truncate(self, f: FileEntry) -> FileEntry:
222
+ lines = f.content.splitlines()
223
+ if len(lines) <= self.policy.snippet_max_lines:
224
+ return f
225
+
226
+ head = lines[: self.policy.snippet_head_lines]
227
+ tail = lines[-self.policy.snippet_tail_lines :]
228
+ omitted = len(lines) - self.policy.snippet_head_lines - self.policy.snippet_tail_lines
229
+ gap = f"# ... [{omitted} lines omitted — request full file if needed] ..."
230
+ f.content = "\n".join(head + [gap] + tail)
231
+ f.truncated = True
232
+ f.lines_omitted = omitted
233
+ return f
234
+
235
+
236
+ # ---------------------------------------------------------------------------
237
+ # Context assembler
238
+ # ---------------------------------------------------------------------------
239
+
240
+ class ContextAssembler:
241
+ """Builds the final context block string from its components."""
242
+
243
+ def build(
244
+ self,
245
+ project_state: str,
246
+ retained_files: list[FileEntry],
247
+ conversation_state: str,
248
+ total_files: int,
249
+ policy: TokenPolicy,
250
+ ) -> str:
251
+ parts: list[str] = []
252
+
253
+ parts.append("## Project State")
254
+ parts.append(project_state.strip() or "(not provided)")
255
+
256
+ header = f"## Relevant Files ({len(retained_files)} of {total_files} retained; threshold: {policy.relevance_drop_threshold})"
257
+ parts.append(header)
258
+ for f in retained_files:
259
+ ext = Path(f.path).suffix.lstrip(".") or "text"
260
+ score_tag = f"(score: {f.relevance_score:.2f})"
261
+ parts.append(f"\n### {f.path} {score_tag}")
262
+ parts.append(f"```{ext}")
263
+ parts.append(f.content)
264
+ parts.append("```")
265
+
266
+ parts.append("## Conversation State")
267
+ parts.append(conversation_state.strip() or "Session start.")
268
+
269
+ return "\n\n".join(parts)
270
+
271
+
272
+ # ---------------------------------------------------------------------------
273
+ # Provider adapters (stubs — no live HTTP calls)
274
+ # ---------------------------------------------------------------------------
275
+
276
+ class ProviderAdapter(ABC):
277
+ """
278
+ Base class for LLM provider adapters.
279
+ Subclass this to integrate with a specific provider's API.
280
+ """
281
+
282
+ @abstractmethod
283
+ def format_messages(self, system_prompt: str, context: str, user_query: str) -> Any:
284
+ """Convert context + query into the provider's message format."""
285
+
286
+ @abstractmethod
287
+ def send(self, messages: Any, output_style: str) -> str:
288
+ """
289
+ Send messages to the provider and return the completion text.
290
+ Implement with httpx or the provider's SDK.
291
+ Raises NotImplementedError in this skeleton.
292
+ """
293
+ raise NotImplementedError("Implement send() in your ProviderAdapter subclass.")
294
+
295
+
296
+ class ClaudeAdapter(ProviderAdapter):
297
+ """
298
+ Adapter for Anthropic Claude (claude-sonnet-4-6, claude-opus-4-7, etc.).
299
+ See platforms/claude.md for full integration guide.
300
+ """
301
+
302
+ def __init__(self, model: str = "claude-sonnet-4-6", api_key: str = "") -> None:
303
+ self.model = model
304
+ self.api_key = api_key
305
+
306
+ def format_messages(self, system_prompt: str, context: str, user_query: str) -> dict:
307
+ return {
308
+ "model": self.model,
309
+ "system": system_prompt,
310
+ "messages": [
311
+ {"role": "user", "content": f"{context}\n\n---\n\n{user_query}"},
312
+ ],
313
+ "max_tokens": 4096,
314
+ }
315
+
316
+ def send(self, messages: dict, output_style: str) -> str:
317
+ raise NotImplementedError(
318
+ "ClaudeAdapter.send() is a stub. "
319
+ "Install anthropic SDK and implement: "
320
+ "client = anthropic.Anthropic(api_key=self.api_key); "
321
+ "return client.messages.create(**messages).content[0].text"
322
+ )
323
+
324
+
325
+ class OpenAIAdapter(ProviderAdapter):
326
+ """
327
+ Adapter for OpenAI (gpt-4o, gpt-4-turbo, etc.).
328
+ See platforms/openai.md for full integration guide.
329
+ """
330
+
331
+ def __init__(self, model: str = "gpt-4o", api_key: str = "") -> None:
332
+ self.model = model
333
+ self.api_key = api_key
334
+
335
+ def format_messages(self, system_prompt: str, context: str, user_query: str) -> list[dict]:
336
+ return [
337
+ {"role": "system", "content": system_prompt},
338
+ {"role": "user", "content": f"{context}\n\n---\n\n{user_query}"},
339
+ ]
340
+
341
+ def send(self, messages: list[dict], output_style: str) -> str:
342
+ raise NotImplementedError(
343
+ "OpenAIAdapter.send() is a stub. "
344
+ "Install openai SDK and implement: "
345
+ "client = openai.OpenAI(api_key=self.api_key); "
346
+ "return client.chat.completions.create(model=self.model, messages=messages).choices[0].message.content"
347
+ )
348
+
349
+
350
+ class GenericAdapter(ProviderAdapter):
351
+ """
352
+ Adapter for any OpenAI-compatible API endpoint.
353
+ Set base_url to the provider's chat completions URL.
354
+ """
355
+
356
+ def __init__(self, base_url: str, model: str, api_key: str = "") -> None:
357
+ self.base_url = base_url
358
+ self.model = model
359
+ self.api_key = api_key
360
+
361
+ def format_messages(self, system_prompt: str, context: str, user_query: str) -> list[dict]:
362
+ return [
363
+ {"role": "system", "content": system_prompt},
364
+ {"role": "user", "content": f"{context}\n\n---\n\n{user_query}"},
365
+ ]
366
+
367
+ def send(self, messages: list[dict], output_style: str) -> str:
368
+ raise NotImplementedError(
369
+ "GenericAdapter.send() is a stub. "
370
+ "Implement using httpx: "
371
+ "httpx.post(self.base_url, json={'model': self.model, 'messages': messages}, "
372
+ "headers={'Authorization': f'Bearer {self.api_key}'})"
373
+ )
374
+
375
+
376
+ # ---------------------------------------------------------------------------
377
+ # Main compressor
378
+ # ---------------------------------------------------------------------------
379
+
380
+ class Compressor:
381
+ """
382
+ Orchestrates the full compression pipeline:
383
+ 1. Relevance filtering
384
+ 2. Snippet truncation
385
+ 3. Context assembly
386
+ 4. Report generation
387
+ """
388
+
389
+ def __init__(self, policy: TokenPolicy) -> None:
390
+ self.policy = policy
391
+ self._filter = RelevanceFilter(policy)
392
+ self._truncator = SnippetTruncator(policy)
393
+ self._assembler = ContextAssembler()
394
+
395
+ def compress(
396
+ self,
397
+ context: ContextBlock,
398
+ ) -> tuple[str, CompressionReport]:
399
+ """
400
+ Apply the full compression pipeline to a ContextBlock.
401
+ Returns (compressed_context_string, report).
402
+ """
403
+ report = CompressionReport()
404
+
405
+ # Score and filter files
406
+ scored = self._filter.score_files(context.files, context.active_query)
407
+ retained, dropped = self._filter.select(scored)
408
+ report.files_dropped = [(f.path, f.relevance_score) for f in dropped]
409
+ report.files_retained = len(retained)
410
+
411
+ # Truncate large files
412
+ truncated_files = [self._truncator.truncate(f) for f in retained]
413
+ report.files_truncated = [
414
+ (f.path, f.lines_omitted) for f in truncated_files if f.truncated
415
+ ]
416
+
417
+ # Estimate token reduction from truncation only (not from file drops).
418
+ # Comparing retained files before vs after truncation gives an accurate
419
+ # measure of how much the snippet truncator saved.
420
+ pre_trunc_chars = sum(len(f.content) for f in retained)
421
+ post_trunc_chars = sum(len(f.content) for f in truncated_files)
422
+ if pre_trunc_chars > 0:
423
+ report.estimated_token_reduction_pct = round(
424
+ (1 - post_trunc_chars / pre_trunc_chars) * 100, 1
425
+ )
426
+
427
+ compressed = self._assembler.build(
428
+ project_state=context.project_state,
429
+ retained_files=truncated_files,
430
+ conversation_state=context.conversation_state,
431
+ total_files=len(context.files),
432
+ policy=self.policy,
433
+ )
434
+
435
+ return compressed, report
436
+
437
+
438
+ # ---------------------------------------------------------------------------
439
+ # CLI dry-run entry point
440
+ # ---------------------------------------------------------------------------
441
+
442
+ def _build_arg_parser() -> argparse.ArgumentParser:
443
+ p = argparse.ArgumentParser(
444
+ description="Dry-run the compressor: shows what would be sent to an LLM without making any API calls."
445
+ )
446
+ p.add_argument("--dry-run", action="store_true", help="Required to run without API calls.")
447
+ p.add_argument("--policy", default="defaults", help="Workflow name from token_policies.yaml.")
448
+ p.add_argument("--context-file", help="Path to a text file containing a raw context block.")
449
+ p.add_argument("--query", default="", help="The active user query.")
450
+ p.add_argument("--config", default="config/token_policies.yaml", help="Path to token_policies.yaml.")
451
+ return p
452
+
453
+
454
+ def main() -> None:
455
+ parser = _build_arg_parser()
456
+ args = parser.parse_args()
457
+
458
+ if not args.dry_run:
459
+ print("Pass --dry-run to use this script without API calls.", file=sys.stderr)
460
+ sys.exit(1)
461
+
462
+ loader = PolicyLoader(args.config)
463
+ loader.load()
464
+ policy = loader.get_workflow_policy(args.policy)
465
+
466
+ raw_context = ""
467
+ if args.context_file:
468
+ raw_context = Path(args.context_file).read_text(encoding="utf-8")
469
+
470
+ # Build a minimal ContextBlock from the raw text (single pseudo-file)
471
+ demo_file = FileEntry(path="<context>", content=raw_context)
472
+ block = ContextBlock(
473
+ project_state="(loaded from --context-file)",
474
+ files=[demo_file],
475
+ conversation_state="Session start.",
476
+ active_query=args.query,
477
+ )
478
+
479
+ compressor = Compressor(policy)
480
+ compressed, report = compressor.compress(block)
481
+
482
+ print("=== COMPRESSED CONTEXT START ===")
483
+ print(compressed)
484
+ print("=== COMPRESSED CONTEXT END ===\n")
485
+
486
+ print("=== COMPRESSION REPORT ===")
487
+ print(json.dumps(
488
+ {
489
+ "workflow": policy.workflow,
490
+ "output_style": policy.output_style,
491
+ "files_retained": report.files_retained,
492
+ "files_dropped": report.files_dropped,
493
+ "files_truncated": report.files_truncated,
494
+ "estimated_token_reduction_pct": report.estimated_token_reduction_pct,
495
+ "warnings": report.warnings,
496
+ },
497
+ indent=2,
498
+ ))
499
+
500
+
501
+ if __name__ == "__main__":
502
+ main()
@@ -0,0 +1,80 @@
1
+ # Output Styles
2
+
3
+ ## Token Cost Tier Definitions
4
+
5
+ Used in skill cards and [Companion] block next-step options to set user expectations:
6
+
7
+ | Tier | Approx. output tokens | Typical context needed |
8
+ |------|----------------------|------------------------|
9
+ | **Low** | < 500 | Conversation only — no file reads required |
10
+ | **Medium** | 500–1,500 | 3–5 source files needed; normal session size |
11
+ | **High** | > 1,500 | Full repo scan or large diff; use `project_summary.py` first |
12
+
13
+ These tiers are intentionally coarse — they indicate session preparation cost, not response quality.
14
+
15
+ ---
16
+
17
+ ## Purpose
18
+
19
+ Named verbosity presets that control the format and length of agent responses. Each style is referenced by name in `config/token_policies.yaml` and in agent spec default settings.
20
+
21
+ Full style definitions (rules, token limits, format templates) live in `config/token_policies.yaml` under `output_styles:`. This file is the usage guide — not the source of truth.
22
+
23
+ ---
24
+
25
+ ## Style Quick Reference
26
+
27
+ | Style key | Best for |
28
+ |---|---|
29
+ | `concise_bullets` | UX critique, status updates, compression reports |
30
+ | `standard` | General Q&A, workflow explanations, mixed responses |
31
+ | `detailed_with_code` | Implementation, refactoring, test generation |
32
+ | `design_brief` | Architecture outputs, API contracts, ADRs |
33
+ | `review_checklist` | Code reviews, layout reviews, security audits |
34
+ | `qa_brief` | Phase 0 task framing — numbered question list |
35
+ | `requirements_spec` | Phase 1 requirements gathering |
36
+ | `solution_design` | Phase 2 solution design output |
37
+ | `implementation_slice` | Phase 3 incremental delivery |
38
+ | `critique_summary` | Phase 4 severity-rated review |
39
+ | `handoff_package` | Phase 5 packaging and handoff |
40
+
41
+ ---
42
+
43
+ ## How to Apply a Style
44
+
45
+ **In conversational use** — append to the agent's system prompt or first message:
46
+
47
+ ```
48
+ Output style: <style_name>
49
+ Max response length: <max_response_tokens> tokens
50
+ ```
51
+
52
+ **In programmatic use** — via `compressor.py`:
53
+
54
+ ```python
55
+ policy = token_policies.get_workflow_policy("ux_critique")
56
+ style = policy.output_style # → "concise_bullets"
57
+ style_config = token_policies.get_output_style(style)
58
+ ```
59
+
60
+ The user can override for any turn: "Use `<style_name>` for this response."
61
+
62
+ ---
63
+
64
+ ## Style Selection by Workflow
65
+
66
+ | Workflow | Phase | Default style |
67
+ |---|---|---|
68
+ | `code_review` | — | `review_checklist` |
69
+ | `feature_implementation` | — | `detailed_with_code` |
70
+ | `feature_design` | — | `design_brief` |
71
+ | `ui_improvement` | — | `design_brief` |
72
+ | `ux_critique` | — | `concise_bullets` |
73
+ | `refactoring` | — | `detailed_with_code` |
74
+ | `test_generation` | — | `detailed_with_code` |
75
+ | `generic_project_lifecycle` | task_framing | `qa_brief` |
76
+ | `generic_project_lifecycle` | requirements | `requirements_spec` |
77
+ | `generic_project_lifecycle` | solution_design | `solution_design` |
78
+ | `generic_project_lifecycle` | implementation | `implementation_slice` |
79
+ | `generic_project_lifecycle` | review_refinement | `critique_summary` |
80
+ | `generic_project_lifecycle` | handoff | `handoff_package` |