claude-code-arcane 2.9.8 → 2.9.9

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 (128) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/agents/business/business-ops-lead.md +1 -14
  3. package/agents/game/gameplay-programmer.md +3 -4
  4. package/agents/game/technical-artist.md +3 -4
  5. package/dist/cli.js +41 -6
  6. package/docs/USER-GUIDE.md +1 -1
  7. package/docs/coordination-rules.md +1 -1
  8. package/docs/directory-structure.md +93 -122
  9. package/package.json +1 -1
  10. package/profiles/backend-go.yaml +1 -0
  11. package/rules/gamedev/engine-code.md +1 -1
  12. package/rules/gamedev/shader-code.md +1 -2
  13. package/rules/gamedev/vn-code.md +3 -2
  14. package/rules/implementation-workflow.md +1 -1
  15. package/rules/{ai-code.md → llm-code.md} +5 -5
  16. package/skills/agent-designer/README.md +2 -1
  17. package/skills/agent-designer/agent_evaluator.py +0 -5
  18. package/skills/ai-sdk-setup/SKILL.md +4 -4
  19. package/skills/architecture-decision/SKILL.md +3 -4
  20. package/skills/architecture-review/SKILL.md +1 -1
  21. package/skills/asset-audit/SKILL.md +0 -1
  22. package/skills/balance-check/SKILL.md +1 -1
  23. package/skills/brainstorm/SKILL.md +3 -3
  24. package/skills/brainstorm/references/next-steps-pipeline.md +8 -9
  25. package/skills/consistency-check/SKILL.md +2 -2
  26. package/skills/design-system/SKILL.md +1 -1
  27. package/skills/design-system/references/post-design-validation.md +2 -3
  28. package/skills/frontend-dev/SKILL.md +27 -28
  29. package/skills/frontend-dev/references/asset-prompt-guide.md +1 -1
  30. package/skills/frontend-dev/references/motion-recipes.md +1 -1
  31. package/skills/help/SKILL.md +1 -2
  32. package/skills/hotfix/SKILL.md +2 -2
  33. package/skills/milestone-review/SKILL.md +1 -1
  34. package/skills/patch-notes/SKILL.md +1 -1
  35. package/skills/playtest-report/SKILL.md +2 -3
  36. package/skills/playwright-pro/CLAUDE.md +2 -2
  37. package/skills/playwright-pro/SKILL.md +13 -9
  38. package/skills/playwright-pro/agents/migration-planner.md +2 -2
  39. package/skills/playwright-pro/agents/test-architect.md +1 -2
  40. package/skills/playwright-pro/agents/test-debugger.md +1 -1
  41. package/skills/playwright-pro/hooks/detect-playwright.sh +1 -1
  42. package/skills/playwright-pro/skills/browserstack/SKILL.md +5 -5
  43. package/skills/playwright-pro/skills/migrate/SKILL.md +1 -1
  44. package/skills/playwright-pro/skills/report/SKILL.md +2 -2
  45. package/skills/playwright-pro/skills/testrail/SKILL.md +5 -5
  46. package/skills/playwright-pro/templates/README.md +2 -2
  47. package/skills/prototype/SKILL.md +1 -1
  48. package/skills/quick-design/SKILL.md +1 -1
  49. package/skills/release-checklist/SKILL.md +0 -1
  50. package/skills/security-audit/SKILL.md +1 -1
  51. package/skills/self-improving-agent/CLAUDE.md +12 -12
  52. package/skills/self-improving-agent/SKILL.md +16 -12
  53. package/skills/self-improving-agent/agents/memory-analyst.md +1 -1
  54. package/skills/self-improving-agent/hooks/error-capture.sh +2 -2
  55. package/skills/self-improving-agent/reference/memory-architecture.md +2 -2
  56. package/skills/self-improving-agent/reference/promotion-rules.md +3 -3
  57. package/skills/self-improving-agent/reference/rules-directory-patterns.md +1 -1
  58. package/skills/self-improving-agent/settings.json +5 -5
  59. package/skills/self-improving-agent/skills/extract/SKILL.md +7 -7
  60. package/skills/self-improving-agent/skills/promote/SKILL.md +5 -5
  61. package/skills/self-improving-agent/skills/remember/SKILL.md +9 -9
  62. package/skills/self-improving-agent/skills/review/SKILL.md +7 -7
  63. package/skills/self-improving-agent/skills/status/SKILL.md +8 -8
  64. package/skills/senior-ml-engineer/references/llm_integration_guide.md +14 -31
  65. package/skills/senior-prompt-engineer/SKILL.md +10 -10
  66. package/skills/senior-prompt-engineer/references/prompt_engineering_patterns.md +1 -1
  67. package/skills/senior-prompt-engineer/scripts/agent_orchestrator.py +43 -40
  68. package/skills/senior-prompt-engineer/scripts/prompt_optimizer.py +23 -66
  69. package/skills/shader-dev/reference/fluid-simulation.md +4 -4
  70. package/skills/shader-dev/reference/simulation-physics.md +6 -6
  71. package/skills/shader-dev/reference/voxel-rendering.md +1 -1
  72. package/skills/shader-dev/techniques/ambient-occlusion.md +1 -1
  73. package/skills/shader-dev/techniques/analytic-ray-tracing.md +2 -2
  74. package/skills/shader-dev/techniques/domain-warping.md +2 -2
  75. package/skills/shader-dev/techniques/fluid-simulation.md +45 -45
  76. package/skills/shader-dev/techniques/multipass-buffer.md +24 -24
  77. package/skills/shader-dev/techniques/normal-estimation.md +1 -1
  78. package/skills/shader-dev/techniques/particle-system.md +16 -16
  79. package/skills/shader-dev/techniques/polar-uv-manipulation.md +1 -1
  80. package/skills/shader-dev/techniques/post-processing.md +7 -7
  81. package/skills/shader-dev/techniques/sdf-3d.md +1 -1
  82. package/skills/shader-dev/techniques/simulation-physics.md +24 -24
  83. package/skills/shader-dev/techniques/sound-synthesis.md +2 -2
  84. package/skills/shader-dev/techniques/voronoi-cellular-noise.md +1 -1
  85. package/skills/shader-dev/techniques/voxel-rendering.md +60 -60
  86. package/skills/smoke-check/SKILL.md +1 -1
  87. package/skills/sprint-plan/SKILL.md +1 -2
  88. package/skills/start/SKILL.md +7 -7
  89. package/skills/story-done/SKILL.md +1 -1
  90. package/skills/test-helpers/SKILL.md +7 -2
  91. package/skills/test-setup/SKILL.md +1 -1
  92. package/skills/ue-actor-component-architecture/SKILL.md +0 -1
  93. package/skills/ue-ai-navigation/SKILL.md +0 -1
  94. package/skills/ue-animation-system/SKILL.md +0 -1
  95. package/skills/ue-async-threading/SKILL.md +0 -1
  96. package/skills/ue-audio-system/SKILL.md +0 -1
  97. package/skills/ue-blueprints/SKILL.md +0 -1
  98. package/skills/ue-character-movement/SKILL.md +0 -1
  99. package/skills/ue-cpp-foundations/SKILL.md +0 -1
  100. package/skills/ue-data-assets-tables/SKILL.md +0 -1
  101. package/skills/ue-editor-tools/SKILL.md +0 -1
  102. package/skills/ue-game-features/SKILL.md +0 -1
  103. package/skills/ue-gameplay-abilities/SKILL.md +0 -1
  104. package/skills/ue-gameplay-framework/SKILL.md +0 -1
  105. package/skills/ue-input-system/SKILL.md +0 -1
  106. package/skills/ue-mass-entity/SKILL.md +0 -1
  107. package/skills/ue-materials-rendering/SKILL.md +0 -1
  108. package/skills/ue-module-build-system/SKILL.md +0 -1
  109. package/skills/ue-naming-conventions/SKILL.md +0 -1
  110. package/skills/ue-networking-replication/SKILL.md +0 -1
  111. package/skills/ue-niagara-effects/SKILL.md +0 -1
  112. package/skills/ue-packaging/SKILL.md +0 -1
  113. package/skills/ue-physics-collision/SKILL.md +0 -1
  114. package/skills/ue-procedural-generation/SKILL.md +0 -1
  115. package/skills/ue-project-context/SKILL.md +0 -1
  116. package/skills/ue-sequencer-cinematics/SKILL.md +0 -1
  117. package/skills/ue-serialization-savegames/SKILL.md +0 -1
  118. package/skills/ue-state-trees/SKILL.md +0 -1
  119. package/skills/ue-testing-debugging/SKILL.md +0 -1
  120. package/skills/ue-ui-umg-slate/SKILL.md +0 -1
  121. package/skills/ue-world-level-streaming/SKILL.md +0 -1
  122. package/skills/ux-review/SKILL.md +1 -1
  123. package/docs/RELEASE-SETUP.md +0 -99
  124. package/docs/presentations/arcane-overview-12.pptx +0 -0
  125. package/docs/presentations/arcane-overview.pptx +0 -0
  126. package/docs/presentations/build_arcane_deck.py +0 -310
  127. package/docs/presentations/build_arcane_deck_12.py +0 -399
  128. package/docs/trusted-publishing-setup.md +0 -163
@@ -55,7 +55,7 @@ class AgentConfig:
55
55
  max_iterations: int = 10
56
56
  system_prompt: str = ""
57
57
  temperature: float = 0.7
58
- model: str = "gpt-4"
58
+ model: str = ""
59
59
 
60
60
 
61
61
  @dataclass
@@ -177,7 +177,7 @@ def load_config(path: Path) -> AgentConfig:
177
177
  max_iterations=int(data.get('max_iterations', 10)),
178
178
  system_prompt=data.get('system_prompt', ''),
179
179
  temperature=float(data.get('temperature', 0.7)),
180
- model=data.get('model', 'gpt-4')
180
+ model=data.get('model', '')
181
181
  )
182
182
 
183
183
 
@@ -369,44 +369,39 @@ def generate_mermaid_diagram(config: AgentConfig) -> str:
369
369
  return '\n'.join(lines)
370
370
 
371
371
 
372
- def estimate_cost(config: AgentConfig, runs: int = 100) -> Dict[str, Any]:
373
- """Estimate token costs for agent runs"""
372
+ def estimate_cost(config: AgentConfig, runs: int = 100,
373
+ price_in: Optional[float] = None, price_out: Optional[float] = None) -> Dict[str, Any]:
374
+ """Estimate tokens per run; dollars only when the caller supplies current prices.
375
+
376
+ Prices are USD per million tokens. They are not built in because provider prices
377
+ change and a stale table gives confidently wrong numbers.
378
+ """
374
379
  validation = validate_agent(config)
375
380
  min_tokens, max_tokens = validation.estimated_tokens_per_run
376
381
 
377
- # Cost per 1K tokens
378
- costs = {
379
- 'gpt-4': {'input': 0.03, 'output': 0.06},
380
- 'gpt-4-turbo': {'input': 0.01, 'output': 0.03},
381
- 'gpt-3.5-turbo': {'input': 0.0005, 'output': 0.0015},
382
- 'claude-3-opus': {'input': 0.015, 'output': 0.075},
383
- 'claude-3-sonnet': {'input': 0.003, 'output': 0.015},
384
- }
385
-
386
- model_cost = costs.get(config.model, costs['gpt-4'])
387
-
388
- # Assume 60% input, 40% output
389
- input_tokens = min_tokens * 0.6
390
- output_tokens = min_tokens * 0.4
382
+ def dollars(tokens: float) -> Optional[float]:
383
+ if price_in is None or price_out is None:
384
+ return None
385
+ # Assume 60% input, 40% output
386
+ return (tokens * 0.6 * price_in + tokens * 0.4 * price_out) / 1_000_000
391
387
 
392
- cost_per_run_min = (input_tokens / 1000 * model_cost['input'] +
393
- output_tokens / 1000 * model_cost['output'])
388
+ per_run_min, per_run_max = dollars(min_tokens), dollars(max_tokens)
389
+ monthly_runs = runs * 30
394
390
 
395
- input_tokens_max = max_tokens * 0.6
396
- output_tokens_max = max_tokens * 0.4
397
- cost_per_run_max = (input_tokens_max / 1000 * model_cost['input'] +
398
- output_tokens_max / 1000 * model_cost['output'])
399
-
400
- return {
401
- 'model': config.model,
391
+ result = {
392
+ 'model': config.model or None,
402
393
  'tokens_per_run': {'min': min_tokens, 'max': max_tokens},
403
- 'cost_per_run': {'min': round(cost_per_run_min, 4), 'max': round(cost_per_run_max, 4)},
404
394
  'estimated_monthly': {
405
- 'runs': runs * 30,
406
- 'cost_min': round(cost_per_run_min * runs * 30, 2),
407
- 'cost_max': round(cost_per_run_max * runs * 30, 2)
408
- }
395
+ 'runs': monthly_runs,
396
+ 'tokens_min': min_tokens * monthly_runs,
397
+ 'tokens_max': max_tokens * monthly_runs,
398
+ },
409
399
  }
400
+ if per_run_min is not None:
401
+ result['cost_per_run'] = {'min': round(per_run_min, 4), 'max': round(per_run_max, 4)}
402
+ result['estimated_monthly']['cost_min'] = round(per_run_min * monthly_runs, 2)
403
+ result['estimated_monthly']['cost_max'] = round(per_run_max * monthly_runs, 2)
404
+ return result
410
405
 
411
406
 
412
407
  def format_validation_report(config: AgentConfig, result: ValidationResult) -> str:
@@ -461,6 +456,9 @@ def format_validation_report(config: AgentConfig, result: ValidationResult) -> s
461
456
 
462
457
 
463
458
  def main():
459
+ # The report uses emoji; a Windows console defaults to cp1252 and cannot print them.
460
+ if hasattr(sys.stdout, "reconfigure"):
461
+ sys.stdout.reconfigure(encoding="utf-8")
464
462
  parser = argparse.ArgumentParser(
465
463
  description="Agent Orchestrator - Design and validate agent workflows",
466
464
  formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -469,13 +467,13 @@ Examples:
469
467
  %(prog)s agent.yaml --validate
470
468
  %(prog)s agent.yaml --visualize
471
469
  %(prog)s agent.yaml --visualize --format mermaid
472
- %(prog)s agent.yaml --estimate-cost --runs 100
470
+ %(prog)s agent.yaml --estimate-cost --runs 100 --price-in 4 --price-out 20
473
471
 
474
472
  Agent config format (YAML):
475
473
 
476
474
  name: research_assistant
477
475
  pattern: react
478
- model: gpt-4
476
+ model: claude-opus-5-5
479
477
  max_iterations: 10
480
478
  tools:
481
479
  - name: web_search
@@ -493,6 +491,8 @@ tools:
493
491
  help='Visualization format (default: ascii)')
494
492
  parser.add_argument('--estimate-cost', '-e', action='store_true', help='Estimate token costs')
495
493
  parser.add_argument('--runs', '-r', type=int, default=100, help='Daily runs for cost estimation')
494
+ parser.add_argument('--price-in', type=float, help='Current input price, USD per million tokens')
495
+ parser.add_argument('--price-out', type=float, help='Current output price, USD per million tokens')
496
496
  parser.add_argument('--output', '-o', help='Output file path')
497
497
  parser.add_argument('--json', '-j', action='store_true', help='Output as JSON')
498
498
 
@@ -534,18 +534,21 @@ tools:
534
534
 
535
535
  # Cost estimation
536
536
  if args.estimate_cost:
537
- costs = estimate_cost(config, args.runs)
537
+ costs = estimate_cost(config, args.runs, args.price_in, args.price_out)
538
538
  if args.json:
539
539
  output_parts.append(json.dumps(costs, indent=2))
540
540
  else:
541
541
  output_parts.append("")
542
542
  output_parts.append("💰 COST ESTIMATION")
543
- output_parts.append(f" Model: {costs['model']}")
543
+ output_parts.append(f" Model: {costs['model'] or '(not set)'}")
544
544
  output_parts.append(f" Tokens per run: {costs['tokens_per_run']['min']:,} - {costs['tokens_per_run']['max']:,}")
545
- output_parts.append(f" Cost per run: ${costs['cost_per_run']['min']:.4f} - ${costs['cost_per_run']['max']:.4f}")
546
- output_parts.append(f" Monthly ({costs['estimated_monthly']['runs']:,} runs):")
547
- output_parts.append(f" Min: ${costs['estimated_monthly']['cost_min']:.2f}")
548
- output_parts.append(f" Max: ${costs['estimated_monthly']['cost_max']:.2f}")
545
+ monthly = costs['estimated_monthly']
546
+ output_parts.append(f" Monthly ({monthly['runs']:,} runs): {monthly['tokens_min']:,} - {monthly['tokens_max']:,} tokens")
547
+ if 'cost_per_run' in costs:
548
+ output_parts.append(f" Cost per run: ${costs['cost_per_run']['min']:.4f} - ${costs['cost_per_run']['max']:.4f}")
549
+ output_parts.append(f" Monthly cost: ${monthly['cost_min']:.2f} - ${monthly['cost_max']:.2f}")
550
+ else:
551
+ output_parts.append(" Cost: pass --price-in and --price-out (USD per million tokens) to get dollars")
549
552
 
550
553
  # Output
551
554
  output = '\n'.join(output_parts)
@@ -3,7 +3,7 @@
3
3
  Prompt Optimizer - Static analysis tool for prompt engineering
4
4
 
5
5
  Features:
6
- - Token estimation (GPT-4/Claude approximation)
6
+ - Approximate token count (characters / 4, not a real tokenizer)
7
7
  - Prompt structure analysis
8
8
  - Clarity scoring
9
9
  - Few-shot example extraction and management
@@ -11,7 +11,7 @@ Features:
11
11
 
12
12
  Usage:
13
13
  python prompt_optimizer.py prompt.txt --analyze
14
- python prompt_optimizer.py prompt.txt --tokens --model gpt-4
14
+ python prompt_optimizer.py prompt.txt --tokens
15
15
  python prompt_optimizer.py prompt.txt --optimize --output optimized.txt
16
16
  python prompt_optimizer.py prompt.txt --extract-examples --output examples.json
17
17
  """
@@ -25,32 +25,15 @@ from typing import Dict, List, Optional, Tuple
25
25
  from dataclasses import dataclass, asdict
26
26
 
27
27
 
28
- # Token estimation ratios (chars per token approximation)
29
- TOKEN_RATIOS = {
30
- 'gpt-4': 4.0,
31
- 'gpt-3.5': 4.0,
32
- 'claude': 3.5,
33
- 'default': 4.0
34
- }
35
-
36
- # Cost per 1K tokens (input)
37
- COST_PER_1K = {
38
- 'gpt-4': 0.03,
39
- 'gpt-4-turbo': 0.01,
40
- 'gpt-3.5-turbo': 0.0005,
41
- 'claude-3-opus': 0.015,
42
- 'claude-3-sonnet': 0.003,
43
- 'claude-3-haiku': 0.00025,
44
- 'default': 0.01
45
- }
28
+ # Rough characters-per-token ratio. Real tokenizers differ by model and by text, so
29
+ # every count here is an estimate; use the provider's token-counting API for exact numbers.
30
+ CHARS_PER_TOKEN = 4.0
46
31
 
47
32
 
48
33
  @dataclass
49
34
  class PromptAnalysis:
50
35
  """Results of prompt analysis"""
51
36
  token_count: int
52
- estimated_cost: float
53
- model: str
54
37
  clarity_score: int
55
38
  structure_score: int
56
39
  issues: List[Dict[str, str]]
@@ -71,16 +54,9 @@ class FewShotExample:
71
54
  index: int
72
55
 
73
56
 
74
- def estimate_tokens(text: str, model: str = 'default') -> int:
75
- """Estimate token count based on character ratio"""
76
- ratio = TOKEN_RATIOS.get(model, TOKEN_RATIOS['default'])
77
- return int(len(text) / ratio)
78
-
79
-
80
- def estimate_cost(token_count: int, model: str = 'default') -> float:
81
- """Estimate cost based on token count"""
82
- cost_per_1k = COST_PER_1K.get(model, COST_PER_1K['default'])
83
- return round((token_count / 1000) * cost_per_1k, 6)
57
+ def estimate_tokens(text: str) -> int:
58
+ """Approximate token count from the character count"""
59
+ return int(len(text) / CHARS_PER_TOKEN)
84
60
 
85
61
 
86
62
  def find_ambiguous_instructions(text: str) -> List[Dict[str, str]]:
@@ -89,11 +65,9 @@ def find_ambiguous_instructions(text: str) -> List[Dict[str, str]]:
89
65
 
90
66
  # Vague verbs that need specificity
91
67
  vague_patterns = [
92
- (r'\b(analyze|process|handle|deal with)\b', 'Vague verb - specify the exact action'),
93
68
  (r'\b(good|nice|appropriate|suitable)\b', 'Subjective term - define specific criteria'),
94
69
  (r'\b(etc\.|and so on|and more)\b', 'Open-ended list - enumerate all items explicitly'),
95
70
  (r'\b(if needed|as necessary|when appropriate)\b', 'Conditional without criteria - specify when'),
96
- (r'\b(some|several|many|few|various)\b', 'Vague quantity - use specific numbers'),
97
71
  ]
98
72
 
99
73
  lines = text.split('\n')
@@ -155,7 +129,7 @@ def check_output_format(text: str) -> Tuple[bool, List[str]]:
155
129
  has_format = any(re.search(p, text, re.IGNORECASE) for p in format_indicators)
156
130
 
157
131
  if not has_format:
158
- suggestions.append('Add explicit output format specification (e.g., "Respond in JSON with keys: ...")')
132
+ suggestions.append('State the expected output format (structure, length, what to include)')
159
133
 
160
134
  return has_format, suggestions
161
135
 
@@ -277,7 +251,7 @@ def generate_suggestions(analysis: PromptAnalysis) -> List[str]:
277
251
  suggestions = []
278
252
 
279
253
  if not analysis.has_output_format:
280
- suggestions.append('Add explicit output format: "Respond in JSON with keys: ..."')
254
+ suggestions.append('State the output format. If code will parse the response as JSON, enforce it with structured outputs instead of describing it in the prompt')
281
255
 
282
256
  if analysis.example_count == 0:
283
257
  suggestions.append('Consider adding 2-3 few-shot examples for consistent outputs')
@@ -289,22 +263,14 @@ def generate_suggestions(analysis: PromptAnalysis) -> List[str]:
289
263
  if analysis.clarity_score < 70:
290
264
  suggestions.append('Improve clarity: replace vague terms with specific instructions')
291
265
 
292
- if analysis.token_count > 2000:
293
- suggestions.append(f'Prompt is {analysis.token_count} tokens - consider condensing for cost efficiency')
294
-
295
- # Check for role prompting
296
- if not re.search(r'you are|act as|as a\s+\w+', analysis.sections[0].get('content', [''])[0] if analysis.sections else '', re.IGNORECASE):
297
- suggestions.append('Consider adding role context: "You are an expert..."')
298
-
299
266
  return suggestions
300
267
 
301
268
 
302
- def analyze_prompt(text: str, model: str = 'gpt-4') -> PromptAnalysis:
269
+ def analyze_prompt(text: str) -> PromptAnalysis:
303
270
  """Perform comprehensive prompt analysis"""
304
271
 
305
272
  # Basic metrics
306
- token_count = estimate_tokens(text, model)
307
- cost = estimate_cost(token_count, model)
273
+ token_count = estimate_tokens(text)
308
274
  word_count = len(text.split())
309
275
  line_count = len(text.split('\n'))
310
276
 
@@ -324,8 +290,6 @@ def analyze_prompt(text: str, model: str = 'gpt-4') -> PromptAnalysis:
324
290
 
325
291
  analysis = PromptAnalysis(
326
292
  token_count=token_count,
327
- estimated_cost=cost,
328
- model=model,
329
293
  clarity_score=clarity_score,
330
294
  structure_score=structure_score,
331
295
  issues=all_issues,
@@ -367,8 +331,7 @@ def format_report(analysis: PromptAnalysis) -> str:
367
331
  report.append("")
368
332
 
369
333
  report.append("📊 METRICS")
370
- report.append(f" Token count: {analysis.token_count:,}")
371
- report.append(f" Estimated cost: ${analysis.estimated_cost:.4f} ({analysis.model})")
334
+ report.append(f" Tokens (approx): {analysis.token_count:,}")
372
335
  report.append(f" Word count: {analysis.word_count:,}")
373
336
  report.append(f" Line count: {analysis.line_count}")
374
337
  report.append("")
@@ -411,13 +374,16 @@ def format_report(analysis: PromptAnalysis) -> str:
411
374
 
412
375
 
413
376
  def main():
377
+ # The report uses emoji; a Windows console defaults to cp1252 and cannot print them.
378
+ if hasattr(sys.stdout, "reconfigure"):
379
+ sys.stdout.reconfigure(encoding="utf-8")
414
380
  parser = argparse.ArgumentParser(
415
381
  description="Prompt Optimizer - Analyze and optimize prompts",
416
382
  formatter_class=argparse.RawDescriptionHelpFormatter,
417
383
  epilog="""
418
384
  Examples:
419
385
  %(prog)s prompt.txt --analyze
420
- %(prog)s prompt.txt --tokens --model claude-3-sonnet
386
+ %(prog)s prompt.txt --tokens
421
387
  %(prog)s prompt.txt --optimize --output optimized.txt
422
388
  %(prog)s prompt.txt --extract-examples --output examples.json
423
389
  """
@@ -428,9 +394,6 @@ Examples:
428
394
  parser.add_argument('--tokens', '-t', action='store_true', help='Count tokens only')
429
395
  parser.add_argument('--optimize', '-O', action='store_true', help='Generate optimized version')
430
396
  parser.add_argument('--extract-examples', '-e', action='store_true', help='Extract few-shot examples')
431
- parser.add_argument('--model', '-m', default='gpt-4',
432
- choices=['gpt-4', 'gpt-4-turbo', 'gpt-3.5-turbo', 'claude-3-opus', 'claude-3-sonnet', 'claude-3-haiku'],
433
- help='Model for token/cost estimation')
434
397
  parser.add_argument('--output', '-o', help='Output file path')
435
398
  parser.add_argument('--json', '-j', action='store_true', help='Output as JSON')
436
399
  parser.add_argument('--compare', '-c', help='Compare with baseline analysis JSON')
@@ -447,17 +410,11 @@ Examples:
447
410
 
448
411
  # Tokens only
449
412
  if args.tokens:
450
- token_count = estimate_tokens(text, args.model)
451
- cost = estimate_cost(token_count, args.model)
413
+ token_count = estimate_tokens(text)
452
414
  if args.json:
453
- print(json.dumps({
454
- 'tokens': token_count,
455
- 'cost': cost,
456
- 'model': args.model
457
- }, indent=2))
415
+ print(json.dumps({'tokens_approx': token_count}, indent=2))
458
416
  else:
459
- print(f"Tokens: {token_count:,}")
460
- print(f"Estimated cost: ${cost:.4f} ({args.model})")
417
+ print(f"Tokens (approx): {token_count:,}")
461
418
  sys.exit(0)
462
419
 
463
420
  # Extract examples
@@ -481,8 +438,8 @@ Examples:
481
438
  print(f"Optimized prompt written to {args.output}")
482
439
 
483
440
  # Show comparison
484
- orig_tokens = estimate_tokens(text, args.model)
485
- new_tokens = estimate_tokens(optimized, args.model)
441
+ orig_tokens = estimate_tokens(text)
442
+ new_tokens = estimate_tokens(optimized)
486
443
  saved = orig_tokens - new_tokens
487
444
  print(f"Tokens: {orig_tokens:,} -> {new_tokens:,} (saved {saved:,})")
488
445
  else:
@@ -490,7 +447,7 @@ Examples:
490
447
  sys.exit(0)
491
448
 
492
449
  # Default: full analysis
493
- analysis = analyze_prompt(text, args.model)
450
+ analysis = analyze_prompt(text)
494
451
 
495
452
  # Compare with baseline
496
453
  if args.compare:
@@ -180,7 +180,7 @@ if (p.x < 1.0 || p.y < 1.0 ||
180
180
  c.xyw *= 0.0;
181
181
  }
182
182
 
183
- // IMPORTANT: Ink decay: must use multiplicative decay; subtractive decay causes saturation in high-concentration areas and overly fast decay in low-concentration areas
183
+ // Ink decay: must use multiplicative decay; subtractive decay causes saturation in high-concentration areas and overly fast decay in low-concentration areas
184
184
  c.w *= 0.995; // 0.5% decay per frame, adjustable [0.99=fast dissipation, 0.999=persistent]
185
185
 
186
186
  // Numerical clamping (prevent explosion)
@@ -199,14 +199,14 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
199
199
  vec2 uv = fragCoord / iResolution.xy;
200
200
  vec4 c = texture(iChannel0, uv);
201
201
 
202
- // IMPORTANT: Color base must be bright enough! 0.5+0.5*cos produces bright colors in [0,1] range
202
+ // Color base must be bright enough! 0.5+0.5*cos produces bright colors in [0,1] range
203
203
  // Never use extremely dark base colors like vec3(0.02, 0.01, 0.08) — multiplied by ink, they become invisible
204
204
  vec3 col = 0.5 + 0.5 * cos(atan(c.y, c.x) + vec3(0.0, 2.1, 4.2));
205
- // IMPORTANT: Use smoothstep instead of linear division to preserve gradient variation
205
+ // Use smoothstep instead of linear division to preserve gradient variation
206
206
  float ink = smoothstep(0.0, 2.0, c.w);
207
207
  col *= ink;
208
208
 
209
- // IMPORTANT: Background color must be visible to the eye (RGB at least > 5/255 ≈ 0.02), otherwise users think the page is all black
209
+ // Background color must be visible to the eye (RGB at least > 5/255 ≈ 0.02), otherwise users think the page is all black
210
210
  col = max(col, vec3(0.02, 0.012, 0.035));
211
211
 
212
212
  fragColor = vec4(col, 1.0);
@@ -50,12 +50,12 @@ Numerical viscosity over-smooths small-scale vortices. Vorticity confinement com
50
50
 
51
51
  **Why**: GPU shaders cannot simultaneously read and write the same buffer. The ping-pong strategy reads from one buffer (previous frame's data) and writes to the other each frame, then swaps on the next frame.
52
52
 
53
- **IMPORTANT: Key Difference Between ShaderToy and WebGL2**: In ShaderToy, Buffer A/B are two independent passes with separate write targets, so `iChannel0=self, iChannel1=other` doesn't conflict. However, in WebGL2 there's only one shader program doing ping-pong, and the write target texture cannot be simultaneously read. The solution is **dual-channel encoding** (R=current height, G=previous frame height).
53
+ **Key Difference Between ShaderToy and WebGL2**: In ShaderToy, Buffer A/B are two independent passes with separate write targets, so `iChannel0=self, iChannel1=other` doesn't conflict. However, in WebGL2 there's only one shader program doing ping-pong, and the write target texture cannot be simultaneously read. The solution is **dual-channel encoding** (R=current height, G=previous frame height).
54
54
 
55
55
  **Code** (WebGL2-safe version, reads only from iChannel0, with RGBA8-compatible encoding):
56
56
  ```glsl
57
- // IMPORTANT: Only use iChannel0 (read currentBuf), write to nextBuf (must be different!)
58
- // IMPORTANT: encode/decode ensure signed values aren't clipped on RGBA8 (no float textures/SwiftShader)
57
+ // Only use iChannel0 (read currentBuf), write to nextBuf (must be different!)
58
+ // encode/decode ensure signed values aren't clipped on RGBA8 (no float textures/SwiftShader)
59
59
  uniform int useFloatTex;
60
60
  float decode(float v) { return useFloatTex == 1 ? v : v * 2.0 - 1.0; }
61
61
  float encode(float v) { return useFloatTex == 1 ? v : v * 0.5 + 0.5; }
@@ -244,7 +244,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord)
244
244
  #define SIZY 64.0 // adjustable: cloth height (particle count)
245
245
 
246
246
  // Left half [0, SIZX) stores positions, right half [SIZX, 2*SIZX) stores velocities
247
- // IMPORTANT: In WebGL2, getpos/getvel both read from iChannel0 (currentBuf, read-only),
247
+ // In WebGL2, getpos/getvel both read from iChannel0 (currentBuf, read-only),
248
248
  // write target is nextBuf (separate buffer), avoiding read-write conflict
249
249
  vec3 getpos(vec2 id)
250
250
  {
@@ -298,7 +298,7 @@ void edge(vec2 dif)
298
298
  vec3 posdif = getpos(dif + c) - pos;
299
299
  vec3 veldif = getvel(dif + c) - ovel;
300
300
 
301
- // IMPORTANT: Must check for zero length, otherwise normalize(vec3(0)) produces NaN
301
+ // Must check for zero length, otherwise normalize(vec3(0)) produces NaN
302
302
  float plen = length(posdif);
303
303
  if (plen < 0.0001) return;
304
304
  vec3 dir = posdif / plen;
@@ -498,7 +498,7 @@ if (length(pos - ballPos) < ballRadius) {
498
498
  }
499
499
  ```
500
500
 
501
- > **IMPORTANT: Common Pitfalls**:
501
+ > **Common Pitfalls**:
502
502
  > - **Cloth Image Pass must project world coordinates to screen**: You cannot use `uv * vec2(SIZX, SIZY)` to map screen UV to grid ID, because particles have moved from their initial positions, producing scattered fragments. You must iterate over mesh faces, projecting vertex world coordinates to screen space for triangle rasterization
503
503
  > - GLSL is strictly typed; you cannot write `float / vec2`. Wrong example: `length(dif) / vec2(SIZX, SIZY).x` will first execute float/vec2 causing a compile error; use `length(dif) / SIZX` instead
504
504
  > - `normalize(vec3(0))` produces NaN; all `normalize()` calls must include a length check beforehand
@@ -619,7 +619,7 @@ A complete voxel water scene with surface wave reflections, underwater refractio
619
619
  float waterY = 0.0;
620
620
 
621
621
  // Underwater voxel scene (sand + seaweed)
622
- // IMPORTANT: c.xz returns vec2, which only has .x/.y components — never use .z!
622
+ // c.xz returns vec2, which only has .x/.y components — never use .z!
623
623
  int getVoxel(vec3 c) {
624
624
  float sandHeight = -3.0 + 0.5 * sin(c.x * 0.3) * cos(c.z * 0.4);
625
625
  if (c.y < sandHeight) return 1; // Sand interior
@@ -1,6 +1,6 @@
1
1
  ## WebGL2 Adaptation Requirements
2
2
 
3
- **IMPORTANT: GLSL Type Strictness**: float and vec types cannot be implicitly converted. `vec3 v = 1.0;` is illegal; you must use the vector form (e.g., `vec3(1.0)`, `vec3(1.0) * x`, `value * vec3(1.0)`).
3
+ **GLSL Type Strictness**: float and vec types cannot be implicitly converted. `vec3 v = 1.0;` is illegal; you must use the vector form (e.g., `vec3(1.0)`, `vec3(1.0) * x`, `value * vec3(1.0)`).
4
4
 
5
5
  The code templates in this document use ShaderToy GLSL style. When generating standalone HTML pages, you must adapt for WebGL2:
6
6
 
@@ -193,7 +193,7 @@ vec3 shade(vec3 pos, vec3 normal, vec3 rd, vec3 albedo) {
193
193
  }
194
194
  ```
195
195
 
196
- > **IMPORTANT: Critical pitfall**: `d.xy` must be passed as distBound, and `d.y` must be updated each time a closer intersection is found! If the deployed code passes the original `dist` directly without updating, the intersection logic will fail (all object distance tests become invalid), resulting in a completely black screen.
196
+ > **Critical pitfall**: `d.xy` must be passed as distBound, and `d.y` must be updated each time a closer intersection is found! If the deployed code passes the original `dist` directly without updating, the intersection logic will fail (all object distance tests become invalid), resulting in a completely black screen.
197
197
 
198
198
  ```glsl
199
199
  #define MAX_BOUNCES 4
@@ -229,7 +229,7 @@ vec3 radiance(vec3 ro, vec3 rd) {
229
229
 
230
230
  Runs directly on ShaderToy, includes sphere, plane, and box primitives with reflection and Blinn-Phong shading.
231
231
 
232
- > **IMPORTANT: Must follow**: All intersection function calls must use `d.xy` as the `distBound` parameter, and update `d.y` after each closer intersection is found. Incorrect usage: `iSphere(ro, rd, dist, ...)` (always using the original dist). Correct usage: `iSphere(ro, rd, d.xy, ...)` followed by `if (t < d.y) { d.y = t; ... }` to update.
232
+ > **Must follow**: All intersection function calls must use `d.xy` as the `distBound` parameter, and update `d.y` after each closer intersection is found. Incorrect usage: `iSphere(ro, rd, dist, ...)` (always using the original dist). Correct usage: `iSphere(ro, rd, d.xy, ...)` followed by `if (t < d.y) { d.y = t; ... }` to update.
233
233
 
234
234
  ```glsl
235
235
  // Analytic Ray Tracing - Complete ShaderToy Template
@@ -117,8 +117,8 @@ float fbm(vec2 p) {
117
117
 
118
118
  ```glsl
119
119
  // Map scalar field (0~1) to color using a mix chain
120
- // IMPORTANT: Note: GLSL is strictly typed. Variable declarations must be complete, e.g. vec3 col = vec3(0.2, 0.1, 0.4)
121
- // IMPORTANT: Decimals must be written as 0.x, not .x (division by zero errors)
120
+ // GLSL is strictly typed. Variable declarations must be complete, e.g. vec3 col = vec3(0.2, 0.1, 0.4)
121
+ // Decimals must be written as 0.x, not .x (division by zero errors)
122
122
  vec3 palette(float t) {
123
123
  vec3 col = vec3(0.2, 0.1, 0.4); // deep purple base
124
124
  col = mix(col, vec3(0.3, 0.05, 0.05), t); // dark red