atomadic-forge 0.3.2__py3-none-any.whl
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.
- atomadic_forge/__init__.py +12 -0
- atomadic_forge/__main__.py +5 -0
- atomadic_forge/a0_qk_constants/__init__.py +1 -0
- atomadic_forge/a0_qk_constants/agent_plan_schema.py +120 -0
- atomadic_forge/a0_qk_constants/commandsmith_types.py +49 -0
- atomadic_forge/a0_qk_constants/config_defaults.py +38 -0
- atomadic_forge/a0_qk_constants/emergent_types.py +77 -0
- atomadic_forge/a0_qk_constants/error_codes.py +296 -0
- atomadic_forge/a0_qk_constants/forge_types.py +89 -0
- atomadic_forge/a0_qk_constants/gen_language.py +116 -0
- atomadic_forge/a0_qk_constants/lang_extensions.py +150 -0
- atomadic_forge/a0_qk_constants/policy_schema.py +48 -0
- atomadic_forge/a0_qk_constants/receipt_schema.py +311 -0
- atomadic_forge/a0_qk_constants/roi_constants.py +96 -0
- atomadic_forge/a0_qk_constants/semantic_types.py +61 -0
- atomadic_forge/a0_qk_constants/sidecar_schema.py +81 -0
- atomadic_forge/a0_qk_constants/synergy_types.py +62 -0
- atomadic_forge/a0_qk_constants/tier_names.py +47 -0
- atomadic_forge/a1_at_functions/__init__.py +1 -0
- atomadic_forge/a1_at_functions/agent_context_pack.py +193 -0
- atomadic_forge/a1_at_functions/agent_memory.py +139 -0
- atomadic_forge/a1_at_functions/agent_plan_emitter.py +324 -0
- atomadic_forge/a1_at_functions/agent_summary.py +277 -0
- atomadic_forge/a1_at_functions/body_extractor.py +306 -0
- atomadic_forge/a1_at_functions/card_renderer.py +210 -0
- atomadic_forge/a1_at_functions/certify_checks.py +445 -0
- atomadic_forge/a1_at_functions/chat_context.py +170 -0
- atomadic_forge/a1_at_functions/cherry_pick.py +71 -0
- atomadic_forge/a1_at_functions/classify_tier.py +115 -0
- atomadic_forge/a1_at_functions/commandsmith_discover.py +167 -0
- atomadic_forge/a1_at_functions/commandsmith_render.py +267 -0
- atomadic_forge/a1_at_functions/compiler_feedback.py +94 -0
- atomadic_forge/a1_at_functions/compliance_checker.py +228 -0
- atomadic_forge/a1_at_functions/config_io.py +68 -0
- atomadic_forge/a1_at_functions/cs1_renderer.py +588 -0
- atomadic_forge/a1_at_functions/doc_synthesizer.py +205 -0
- atomadic_forge/a1_at_functions/emergent_compose.py +192 -0
- atomadic_forge/a1_at_functions/emergent_rank.py +116 -0
- atomadic_forge/a1_at_functions/emergent_signature_extract.py +242 -0
- atomadic_forge/a1_at_functions/emergent_synthesize.py +88 -0
- atomadic_forge/a1_at_functions/enforce_planner.py +208 -0
- atomadic_forge/a1_at_functions/error_hints.py +105 -0
- atomadic_forge/a1_at_functions/evolution_log.py +94 -0
- atomadic_forge/a1_at_functions/forge_feedback.py +433 -0
- atomadic_forge/a1_at_functions/generation_quality.py +322 -0
- atomadic_forge/a1_at_functions/import_repair.py +211 -0
- atomadic_forge/a1_at_functions/import_smoke.py +102 -0
- atomadic_forge/a1_at_functions/js_parser.py +539 -0
- atomadic_forge/a1_at_functions/lineage_chain.py +144 -0
- atomadic_forge/a1_at_functions/lineage_reader.py +107 -0
- atomadic_forge/a1_at_functions/llm_client.py +554 -0
- atomadic_forge/a1_at_functions/local_signer.py +134 -0
- atomadic_forge/a1_at_functions/lsp_protocol.py +379 -0
- atomadic_forge/a1_at_functions/manifest_diff.py +314 -0
- atomadic_forge/a1_at_functions/mcp_protocol.py +1066 -0
- atomadic_forge/a1_at_functions/patch_scorer.py +267 -0
- atomadic_forge/a1_at_functions/plan_adapter.py +75 -0
- atomadic_forge/a1_at_functions/policy_loader.py +107 -0
- atomadic_forge/a1_at_functions/preflight_change.py +227 -0
- atomadic_forge/a1_at_functions/progress_reporter.py +81 -0
- atomadic_forge/a1_at_functions/provider_detect.py +157 -0
- atomadic_forge/a1_at_functions/provider_resolver.py +48 -0
- atomadic_forge/a1_at_functions/receipt_emitter.py +291 -0
- atomadic_forge/a1_at_functions/recipes.py +186 -0
- atomadic_forge/a1_at_functions/repo_explainer.py +124 -0
- atomadic_forge/a1_at_functions/roi_calculator.py +265 -0
- atomadic_forge/a1_at_functions/rollback_planner.py +147 -0
- atomadic_forge/a1_at_functions/sbom_emitter.py +155 -0
- atomadic_forge/a1_at_functions/scaffold_js.py +55 -0
- atomadic_forge/a1_at_functions/scaffold_pyproject.py +62 -0
- atomadic_forge/a1_at_functions/scaffold_starter.py +94 -0
- atomadic_forge/a1_at_functions/scout_walk.py +309 -0
- atomadic_forge/a1_at_functions/sidecar_parser.py +161 -0
- atomadic_forge/a1_at_functions/sidecar_validator.py +202 -0
- atomadic_forge/a1_at_functions/stub_detector.py +158 -0
- atomadic_forge/a1_at_functions/synergy_detect.py +166 -0
- atomadic_forge/a1_at_functions/synergy_render.py +252 -0
- atomadic_forge/a1_at_functions/synergy_surface_extract.py +163 -0
- atomadic_forge/a1_at_functions/test_runner.py +196 -0
- atomadic_forge/a1_at_functions/test_selector.py +122 -0
- atomadic_forge/a1_at_functions/tier_init_rebuild.py +122 -0
- atomadic_forge/a1_at_functions/tool_composer.py +130 -0
- atomadic_forge/a1_at_functions/transcript_log.py +70 -0
- atomadic_forge/a1_at_functions/wire_check.py +260 -0
- atomadic_forge/a2_mo_composites/__init__.py +1 -0
- atomadic_forge/a2_mo_composites/lineage_chain_store.py +122 -0
- atomadic_forge/a2_mo_composites/manifest_store.py +46 -0
- atomadic_forge/a2_mo_composites/plan_store.py +164 -0
- atomadic_forge/a2_mo_composites/receipt_signer.py +231 -0
- atomadic_forge/a3_og_features/__init__.py +1 -0
- atomadic_forge/a3_og_features/commandsmith_feature.py +267 -0
- atomadic_forge/a3_og_features/demo_packages/mixed_py_js/src/mixed_pkg/__init__.py +3 -0
- atomadic_forge/a3_og_features/demo_packages/mixed_py_js/src/mixed_pkg/a0_qk_constants/__init__.py +4 -0
- atomadic_forge/a3_og_features/demo_packages/mixed_py_js/src/mixed_pkg/a1_at_functions/__init__.py +14 -0
- atomadic_forge/a3_og_features/demo_packages/mixed_py_js/tests/conftest.py +10 -0
- atomadic_forge/a3_og_features/demo_packages/mixed_py_js/tests/test_mixed.py +18 -0
- atomadic_forge/a3_og_features/demo_runner.py +502 -0
- atomadic_forge/a3_og_features/emergent_feature.py +95 -0
- atomadic_forge/a3_og_features/emergent_pipeline_integration.py +154 -0
- atomadic_forge/a3_og_features/forge_enforce.py +107 -0
- atomadic_forge/a3_og_features/forge_evolve.py +176 -0
- atomadic_forge/a3_og_features/forge_loop.py +528 -0
- atomadic_forge/a3_og_features/forge_pipeline.py +295 -0
- atomadic_forge/a3_og_features/forge_plan_apply.py +222 -0
- atomadic_forge/a3_og_features/lsp_server.py +98 -0
- atomadic_forge/a3_og_features/mcp_server.py +160 -0
- atomadic_forge/a3_og_features/setup_wizard.py +337 -0
- atomadic_forge/a3_og_features/synergy_feature.py +65 -0
- atomadic_forge/a4_sy_orchestration/__init__.py +1 -0
- atomadic_forge/a4_sy_orchestration/cli.py +1284 -0
- atomadic_forge/commands/__init__.py +1 -0
- atomadic_forge/commands/_registry.py +36 -0
- atomadic_forge/commands/audit.py +142 -0
- atomadic_forge/commands/chat.py +133 -0
- atomadic_forge/commands/commandsmith.py +178 -0
- atomadic_forge/commands/config_cmd.py +145 -0
- atomadic_forge/commands/demo.py +142 -0
- atomadic_forge/commands/emergent.py +124 -0
- atomadic_forge/commands/emergent_then_synergy.py +70 -0
- atomadic_forge/commands/evolve.py +122 -0
- atomadic_forge/commands/evolve_then_iterate.py +70 -0
- atomadic_forge/commands/feature_then_emergent.py +111 -0
- atomadic_forge/commands/iterate.py +140 -0
- atomadic_forge/commands/synergy.py +96 -0
- atomadic_forge/commands/synergy_then_emergent.py +70 -0
- atomadic_forge-0.3.2.dist-info/METADATA +471 -0
- atomadic_forge-0.3.2.dist-info/RECORD +131 -0
- atomadic_forge-0.3.2.dist-info/WHEEL +5 -0
- atomadic_forge-0.3.2.dist-info/entry_points.txt +3 -0
- atomadic_forge-0.3.2.dist-info/licenses/LICENSE +15 -0
- atomadic_forge-0.3.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""``forge demo`` — one-shot launch-video verb.
|
|
2
|
+
|
|
3
|
+
Run a preset evolve trajectory + post-run CLI invocation + DEMO.md artifact.
|
|
4
|
+
Designed to produce a recordable 90-second showcase from a single command.
|
|
5
|
+
|
|
6
|
+
forge demo run # default preset (calc), auto provider
|
|
7
|
+
forge demo run --preset kv # KV-store preset
|
|
8
|
+
forge demo run --preset slug --provider gemini
|
|
9
|
+
forge demo list # show all presets
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Annotated
|
|
17
|
+
|
|
18
|
+
import click
|
|
19
|
+
import typer
|
|
20
|
+
|
|
21
|
+
from atomadic_forge.a1_at_functions.provider_resolver import (
|
|
22
|
+
PROVIDER_HELP,
|
|
23
|
+
resolve_provider,
|
|
24
|
+
)
|
|
25
|
+
from atomadic_forge.a3_og_features.demo_runner import (
|
|
26
|
+
DemoResult,
|
|
27
|
+
list_presets,
|
|
28
|
+
run_demo,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
COMMAND_NAME = "demo"
|
|
32
|
+
COMMAND_HELP = "One-shot launch-video verb: preset evolve + DEMO.md artifact."
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
app = typer.Typer(no_args_is_help=True, help=COMMAND_HELP)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _cli_demo_failed(result: DemoResult) -> bool:
|
|
39
|
+
return bool(result.cli_demo_command) and result.cli_demo_returncode != 0
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _resolve_provider(name: str) -> object:
|
|
43
|
+
try:
|
|
44
|
+
return resolve_provider(name)
|
|
45
|
+
except ValueError as exc:
|
|
46
|
+
raise typer.BadParameter(str(exc)) from exc
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@app.command("list")
|
|
50
|
+
def list_cmd(
|
|
51
|
+
json_out: Annotated[bool, typer.Option("--json")] = False,
|
|
52
|
+
) -> None:
|
|
53
|
+
"""List available demo presets."""
|
|
54
|
+
presets = list_presets()
|
|
55
|
+
if json_out:
|
|
56
|
+
typer.echo(json.dumps([
|
|
57
|
+
{"name": p.name, "headline": p.headline, "package": p.package,
|
|
58
|
+
"rounds": p.rounds, "iterations": p.iterations,
|
|
59
|
+
"target_score": p.target_score}
|
|
60
|
+
for p in presets
|
|
61
|
+
], indent=2))
|
|
62
|
+
return
|
|
63
|
+
typer.echo("\nForge demo presets:")
|
|
64
|
+
for p in presets:
|
|
65
|
+
typer.echo(f" {p.name:8s} — {p.headline}")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@app.command("run")
|
|
69
|
+
def run_cmd(
|
|
70
|
+
preset: Annotated[str, typer.Option("--preset", "-p",
|
|
71
|
+
help="Preset name (use `forge demo list` to see options).")] = "calc",
|
|
72
|
+
output: Annotated[Path | None, typer.Option("--output", "-o",
|
|
73
|
+
help="Output directory. Defaults to ./forge-demo-<preset>.",
|
|
74
|
+
file_okay=False, dir_okay=True, resolve_path=True)] = None,
|
|
75
|
+
provider: Annotated[str, typer.Option("--provider",
|
|
76
|
+
help=PROVIDER_HELP)] = "auto",
|
|
77
|
+
rounds: Annotated[int | None, typer.Option("--rounds",
|
|
78
|
+
help="Override the preset's round count.")] = None,
|
|
79
|
+
iterations: Annotated[int | None, typer.Option("--iterations",
|
|
80
|
+
help="Override the preset's iterations-per-round.")] = None,
|
|
81
|
+
skip_cli_demo: Annotated[bool, typer.Option("--skip-cli-demo")] = False,
|
|
82
|
+
json_out: Annotated[bool, typer.Option("--json")] = False,
|
|
83
|
+
) -> None:
|
|
84
|
+
"""Run the preset and produce a DEMO.md artifact."""
|
|
85
|
+
llm = _resolve_provider(provider)
|
|
86
|
+
try:
|
|
87
|
+
result = run_demo(
|
|
88
|
+
preset_name=preset,
|
|
89
|
+
output=output,
|
|
90
|
+
llm=llm, # type: ignore[arg-type]
|
|
91
|
+
rounds=rounds,
|
|
92
|
+
iterations=iterations,
|
|
93
|
+
skip_cli_demo=skip_cli_demo,
|
|
94
|
+
)
|
|
95
|
+
except RuntimeError as exc:
|
|
96
|
+
raise click.ClickException(str(exc)) from exc
|
|
97
|
+
if json_out:
|
|
98
|
+
from dataclasses import asdict
|
|
99
|
+
typer.echo(json.dumps(asdict(result), indent=2, default=str))
|
|
100
|
+
if _cli_demo_failed(result):
|
|
101
|
+
raise typer.Exit(code=1)
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
arc = " → ".join(f"{s:.0f}" for s in result.score_trajectory)
|
|
105
|
+
# Showcase presets bypass the LLM entirely — print "static showcase"
|
|
106
|
+
# in the LLM slot so output stays consistent across both kinds.
|
|
107
|
+
from atomadic_forge.a3_og_features.demo_runner import get_preset
|
|
108
|
+
try:
|
|
109
|
+
preset_obj = get_preset(preset)
|
|
110
|
+
except KeyError:
|
|
111
|
+
preset_obj = None
|
|
112
|
+
is_showcase = preset_obj is not None and preset_obj.kind == "showcase"
|
|
113
|
+
llm_label = "static showcase (no LLM)" if is_showcase else llm.name
|
|
114
|
+
|
|
115
|
+
typer.echo("")
|
|
116
|
+
typer.echo("=" * 60)
|
|
117
|
+
typer.echo(f" forge demo: {preset}")
|
|
118
|
+
typer.echo("=" * 60)
|
|
119
|
+
typer.echo("")
|
|
120
|
+
typer.echo(f" {result.headline}")
|
|
121
|
+
typer.echo("")
|
|
122
|
+
typer.echo(f" llm: {llm_label}")
|
|
123
|
+
typer.echo(f" package: {result.package}")
|
|
124
|
+
typer.echo(f" rounds: {result.rounds_completed}")
|
|
125
|
+
typer.echo(f" trajectory: {arc}")
|
|
126
|
+
typer.echo(f" final score: {result.final_score:.0f}/100")
|
|
127
|
+
typer.echo(f" converged: {result.converged}")
|
|
128
|
+
typer.echo(f" duration: {result.duration_s:.1f}s")
|
|
129
|
+
typer.echo("")
|
|
130
|
+
if result.cli_demo_command:
|
|
131
|
+
typer.echo(" Generated CLI:")
|
|
132
|
+
typer.echo(f" $ {' '.join(result.cli_demo_command)}")
|
|
133
|
+
for line in (result.cli_demo_stdout or "(no output)").splitlines()[:6]:
|
|
134
|
+
typer.echo(f" {line}")
|
|
135
|
+
if _cli_demo_failed(result):
|
|
136
|
+
typer.echo(" generated CLI demo failed")
|
|
137
|
+
typer.echo("")
|
|
138
|
+
typer.echo(f" Artifact: {result.artifact_md_path}")
|
|
139
|
+
typer.echo(f" Output: {result.output_root}")
|
|
140
|
+
typer.echo("")
|
|
141
|
+
if _cli_demo_failed(result):
|
|
142
|
+
raise typer.Exit(code=1)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""``atomadic-forge emergent`` — discover and synthesise emergent feature candidates.
|
|
2
|
+
|
|
3
|
+
Once you've assimilated a sibling repo (or just grown the catalog), every
|
|
4
|
+
function and class in the tier folders is a *building block*. This command
|
|
5
|
+
walks the catalog, finds typed compositions nobody wrote yet, scores them on
|
|
6
|
+
cross-domain / cross-tier / purity / novelty, and (optionally) materialises
|
|
7
|
+
the top candidate as a new ``a3_og_features/<name>_emergent.py`` for review.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Annotated
|
|
15
|
+
|
|
16
|
+
import typer
|
|
17
|
+
|
|
18
|
+
from atomadic_forge.a3_og_features.emergent_feature import EmergentScan
|
|
19
|
+
|
|
20
|
+
COMMAND_NAME = "emergent"
|
|
21
|
+
COMMAND_HELP = "Synthesise new feature candidates from existing components."
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
app = typer.Typer(no_args_is_help=True, help=COMMAND_HELP)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _resolve_src_root() -> Path:
|
|
28
|
+
"""Locate ``<repo>/src`` from this file's location."""
|
|
29
|
+
here = Path(__file__).resolve()
|
|
30
|
+
return here.parent.parent.parent # commands/ -> atomadic_forge/ -> src/
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@app.command("scan")
|
|
34
|
+
def scan_cmd(
|
|
35
|
+
package: Annotated[str, typer.Option("--package",
|
|
36
|
+
help="Importable package under src/ to scan.")] = "atomadic_forge",
|
|
37
|
+
src_root: Annotated[Path, typer.Option("--src-root",
|
|
38
|
+
help="Path to the src directory containing the package.",
|
|
39
|
+
exists=True, file_okay=False, dir_okay=True, resolve_path=True)] = None, # type: ignore[assignment]
|
|
40
|
+
max_depth: Annotated[int, typer.Option("--max-depth",
|
|
41
|
+
help="Max chain length.")] = 3,
|
|
42
|
+
top_n: Annotated[int, typer.Option("--top-n",
|
|
43
|
+
help="Return at most this many candidates.")] = 15,
|
|
44
|
+
require_pure: Annotated[bool, typer.Option("--require-pure",
|
|
45
|
+
help="Restrict to chains where every step is heuristically pure.")] = False,
|
|
46
|
+
no_domain_jump: Annotated[bool, typer.Option("--no-domain-jump",
|
|
47
|
+
help="Allow same-domain chains (default: require ≥2 distinct domains).")] = False,
|
|
48
|
+
json_out: Annotated[bool, typer.Option("--json",
|
|
49
|
+
help="Emit machine-readable JSON.")] = False,
|
|
50
|
+
save: Annotated[Path | None, typer.Option("--save",
|
|
51
|
+
help="Persist the report at this path.",
|
|
52
|
+
file_okay=True, dir_okay=False, resolve_path=True)] = None,
|
|
53
|
+
) -> None:
|
|
54
|
+
"""Walk the catalog and report top-scoring composition candidates."""
|
|
55
|
+
root = src_root or _resolve_src_root()
|
|
56
|
+
scanner = EmergentScan(src_root=root, package=package)
|
|
57
|
+
report = scanner.scan(
|
|
58
|
+
max_depth=max_depth,
|
|
59
|
+
top_n=top_n,
|
|
60
|
+
require_pure=require_pure,
|
|
61
|
+
domain_jump_required=not no_domain_jump,
|
|
62
|
+
)
|
|
63
|
+
if save:
|
|
64
|
+
EmergentScan.save_report(report, save)
|
|
65
|
+
|
|
66
|
+
if json_out:
|
|
67
|
+
typer.echo(json.dumps(report, indent=2, default=str))
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
typer.echo(f"\nEmergent scan — {package}")
|
|
71
|
+
typer.echo("-" * 60)
|
|
72
|
+
typer.echo(f" catalog size: {report['catalog_size']}")
|
|
73
|
+
typer.echo(f" chains considered: {report['chain_count_considered']}")
|
|
74
|
+
typer.echo(f" domains: {len(report['domain_inventory'])}")
|
|
75
|
+
typer.echo(f" candidates: {len(report['candidates'])}\n")
|
|
76
|
+
|
|
77
|
+
for i, c in enumerate(report["candidates"], 1):
|
|
78
|
+
typer.echo(f" #{i:2d} {c['candidate_id']} score={c['score']:.0f}")
|
|
79
|
+
typer.echo(f" name: {c['name']}")
|
|
80
|
+
typer.echo(f" tier: {c['suggested_tier']}")
|
|
81
|
+
typer.echo(f" chain: {' → '.join(c['chain']['domains'])}")
|
|
82
|
+
if c["novelty_signals"]:
|
|
83
|
+
typer.echo(f" why: {'; '.join(c['novelty_signals'])}")
|
|
84
|
+
typer.echo("")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@app.command("synthesize")
|
|
88
|
+
def synthesize_cmd(
|
|
89
|
+
candidate_id: Annotated[str, typer.Argument(
|
|
90
|
+
help="Candidate id from a prior `scan` (e.g. emrg-abc12345).")],
|
|
91
|
+
report_path: Annotated[Path, typer.Argument(
|
|
92
|
+
help="Saved report JSON from `scan --save …`.",
|
|
93
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True)],
|
|
94
|
+
package: Annotated[str, typer.Option("--package")] = "atomadic_forge",
|
|
95
|
+
src_root: Annotated[Path | None, typer.Option("--src-root",
|
|
96
|
+
exists=True, file_okay=False, dir_okay=True, resolve_path=True)] = None,
|
|
97
|
+
out_dir: Annotated[Path | None, typer.Option("--out-dir",
|
|
98
|
+
help="Where to write the new feature file (defaults to a3_og_features/).",
|
|
99
|
+
file_okay=False, dir_okay=True, resolve_path=True)] = None,
|
|
100
|
+
) -> None:
|
|
101
|
+
"""Materialize a candidate as a new a3 feature module."""
|
|
102
|
+
report = json.loads(report_path.read_text(encoding="utf-8"))
|
|
103
|
+
scanner = EmergentScan(src_root=src_root or _resolve_src_root(),
|
|
104
|
+
package=package)
|
|
105
|
+
target = scanner.synthesize(candidate_id, report, out_dir=out_dir)
|
|
106
|
+
typer.echo(f"Wrote {target}")
|
|
107
|
+
typer.echo("Review carefully before committing — this is scaffolding,")
|
|
108
|
+
typer.echo("not a finished feature. Methods need their receiver wired.")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@app.command("show")
|
|
112
|
+
def show_cmd(
|
|
113
|
+
candidate_id: Annotated[str, typer.Argument()],
|
|
114
|
+
report_path: Annotated[Path, typer.Argument(
|
|
115
|
+
exists=True, file_okay=True, dir_okay=False, resolve_path=True)],
|
|
116
|
+
) -> None:
|
|
117
|
+
"""Print one candidate's full chain + score breakdown."""
|
|
118
|
+
report = json.loads(report_path.read_text(encoding="utf-8"))
|
|
119
|
+
match = next((c for c in report["candidates"] if c["candidate_id"] == candidate_id),
|
|
120
|
+
None)
|
|
121
|
+
if match is None:
|
|
122
|
+
typer.secho(f"candidate {candidate_id} not in report", fg="red", err=True)
|
|
123
|
+
raise typer.Exit(1)
|
|
124
|
+
typer.echo(json.dumps(match, indent=2))
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Auto-synthesized synergy adapter (syn-e3622070).
|
|
3
|
+
|
|
4
|
+
Producer: emergent
|
|
5
|
+
Consumer: synergy
|
|
6
|
+
Kind: json_artifact
|
|
7
|
+
Score: 42
|
|
8
|
+
|
|
9
|
+
Why this synergy was detected:
|
|
10
|
+
- emergent emits json-out
|
|
11
|
+
- synergy accepts json_out
|
|
12
|
+
|
|
13
|
+
Re-emit with ``atomadic-forge synergy implement <id>`` after surfaces change.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
import tempfile
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Annotated
|
|
24
|
+
|
|
25
|
+
import typer
|
|
26
|
+
|
|
27
|
+
COMMAND_NAME = 'emergent-then-synergy'
|
|
28
|
+
COMMAND_HELP = 'Run emergent to emit a JSON artifact, then feed it to synergy for the next phase.'
|
|
29
|
+
|
|
30
|
+
app = typer.Typer(no_args_is_help=False, help='Run emergent to emit a JSON artifact, then feed it to synergy for the next phase.')
|
|
31
|
+
|
|
32
|
+
@app.callback(invoke_without_command=True)
|
|
33
|
+
def run(
|
|
34
|
+
ctx: typer.Context,
|
|
35
|
+
producer_args: Annotated[list[str] | None, typer.Argument(
|
|
36
|
+
help='Args forwarded to the producer command.')] = None,
|
|
37
|
+
) -> None:
|
|
38
|
+
"""Run emergent → capture artifact → feed to synergy."""
|
|
39
|
+
if ctx.invoked_subcommand is not None:
|
|
40
|
+
return
|
|
41
|
+
producer_args = producer_args or []
|
|
42
|
+
with tempfile.TemporaryDirectory(prefix='synergy-') as tmp:
|
|
43
|
+
artifact = Path(tmp) / 'producer.json'
|
|
44
|
+
cmd_a = [sys.executable, '-m',
|
|
45
|
+
'atomadic_forge.a4_sy_orchestration.cli',
|
|
46
|
+
'emergent', *producer_args,
|
|
47
|
+
'--json-out', str(artifact)]
|
|
48
|
+
rc = subprocess.run(cmd_a, capture_output=False).returncode
|
|
49
|
+
if rc != 0:
|
|
50
|
+
typer.secho(f'producer exited {rc}', fg='red', err=True)
|
|
51
|
+
raise typer.Exit(rc)
|
|
52
|
+
cmd_b = [sys.executable, '-m',
|
|
53
|
+
'atomadic_forge.a4_sy_orchestration.cli',
|
|
54
|
+
'synergy', str(artifact)]
|
|
55
|
+
rc = subprocess.run(cmd_b, capture_output=False).returncode
|
|
56
|
+
if rc != 0:
|
|
57
|
+
typer.secho(f'consumer exited {rc}', fg='red', err=True)
|
|
58
|
+
raise typer.Exit(rc)
|
|
59
|
+
try:
|
|
60
|
+
data = json.loads(artifact.read_text(encoding='utf-8'))
|
|
61
|
+
except (OSError, json.JSONDecodeError):
|
|
62
|
+
data = None
|
|
63
|
+
typer.echo(json.dumps({
|
|
64
|
+
'synergy': 'syn-e3622070',
|
|
65
|
+
'producer': 'emergent',
|
|
66
|
+
'consumer': 'synergy',
|
|
67
|
+
'artifact_size_bytes': artifact.stat().st_size,
|
|
68
|
+
'producer_payload_keys': sorted(data.keys()) if isinstance(data, dict) else None,
|
|
69
|
+
}, indent=2))
|
|
70
|
+
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""``forge evolve`` — recursive self-improvement: iterate that iterates.
|
|
2
|
+
|
|
3
|
+
Each round, the package generated last time becomes the seed catalog for
|
|
4
|
+
the next round. The LLM sees its own prior output as building blocks via
|
|
5
|
+
emergent + reuse feedback. The catalog grows; compositions multiply; the
|
|
6
|
+
LLM (in principle) gets richer context every round.
|
|
7
|
+
|
|
8
|
+
This is **narrow self-improvement** — same shape as AlphaEvolve / Voyager.
|
|
9
|
+
NOT a path to AGI. Bounded by underlying LLM quality and the realism of
|
|
10
|
+
the certify/wire signals. Honest about what it is.
|
|
11
|
+
|
|
12
|
+
Examples
|
|
13
|
+
--------
|
|
14
|
+
forge evolve "discord bot that summarises PDFs" ./out --auto 5
|
|
15
|
+
|
|
16
|
+
# Sit-back mode: 8 rounds, 4 LLM turns per round, max score 90:
|
|
17
|
+
forge evolve "..." ./out --auto 8 --target-score 90
|
|
18
|
+
|
|
19
|
+
# Stop early if score regresses:
|
|
20
|
+
forge evolve "..." ./out --auto 5 --stop-on-regression
|
|
21
|
+
|
|
22
|
+
# Resume from an existing growing seed:
|
|
23
|
+
forge evolve "..." ./out --auto 3 --seed ./out/src/evolved
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import json
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
from typing import Annotated
|
|
31
|
+
|
|
32
|
+
import click
|
|
33
|
+
import typer
|
|
34
|
+
|
|
35
|
+
from atomadic_forge.a1_at_functions.provider_resolver import (
|
|
36
|
+
PROVIDER_HELP,
|
|
37
|
+
resolve_provider,
|
|
38
|
+
)
|
|
39
|
+
from atomadic_forge.a3_og_features.forge_evolve import run_evolve
|
|
40
|
+
|
|
41
|
+
COMMAND_NAME = "evolve"
|
|
42
|
+
COMMAND_HELP = ("Recursive self-improvement: run iterate N times, each "
|
|
43
|
+
"round seeded by the previous round's growing catalog.")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
app = typer.Typer(no_args_is_help=True, help=COMMAND_HELP)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _resolve_provider(name: str) -> object:
|
|
50
|
+
try:
|
|
51
|
+
return resolve_provider(name)
|
|
52
|
+
except ValueError as exc:
|
|
53
|
+
raise typer.BadParameter(str(exc)) from exc
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@app.command("run")
|
|
57
|
+
def run_cmd(
|
|
58
|
+
intent: Annotated[str, typer.Argument(help="One-paragraph description.")],
|
|
59
|
+
output: Annotated[Path, typer.Argument(
|
|
60
|
+
file_okay=False, dir_okay=True, resolve_path=True)],
|
|
61
|
+
auto: Annotated[int, typer.Option("--auto",
|
|
62
|
+
help="Number of evolve rounds — sit-back parameter.")] = 3,
|
|
63
|
+
iterations_per_round: Annotated[int, typer.Option("--iterations")] = 4,
|
|
64
|
+
target_score: Annotated[float, typer.Option("--target-score")] = 75.0,
|
|
65
|
+
package: Annotated[str, typer.Option("--package")] = "evolved",
|
|
66
|
+
seed_repo: Annotated[Path | None, typer.Option("--seed",
|
|
67
|
+
exists=True, file_okay=False, dir_okay=True, resolve_path=True)] = None,
|
|
68
|
+
provider: Annotated[str, typer.Option("--provider",
|
|
69
|
+
help=PROVIDER_HELP)] = "auto",
|
|
70
|
+
language: Annotated[str, typer.Option("--language", "-l",
|
|
71
|
+
help="Output language: python | javascript | typescript")] = "python",
|
|
72
|
+
stop_on_regression: Annotated[bool, typer.Option("--stop-on-regression")] = False,
|
|
73
|
+
json_out: Annotated[bool, typer.Option("--json")] = False,
|
|
74
|
+
) -> None:
|
|
75
|
+
"""Run the recursive evolve loop and watch the show."""
|
|
76
|
+
output.mkdir(parents=True, exist_ok=True)
|
|
77
|
+
llm = _resolve_provider(provider)
|
|
78
|
+
try:
|
|
79
|
+
report = run_evolve(
|
|
80
|
+
intent,
|
|
81
|
+
output=output,
|
|
82
|
+
package=package,
|
|
83
|
+
seed_repo=seed_repo,
|
|
84
|
+
llm=llm, # type: ignore[arg-type]
|
|
85
|
+
rounds=auto,
|
|
86
|
+
iterations_per_round=iterations_per_round,
|
|
87
|
+
target_score=target_score,
|
|
88
|
+
stop_on_regression=stop_on_regression,
|
|
89
|
+
language=language,
|
|
90
|
+
)
|
|
91
|
+
except RuntimeError as exc:
|
|
92
|
+
raise click.ClickException(str(exc)) from exc
|
|
93
|
+
if json_out:
|
|
94
|
+
typer.echo(json.dumps(report, indent=2, default=str))
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
# Output path varies by language: python uses src/, JS/TS uses pkg root directly.
|
|
98
|
+
lang = report.get("language", "python")
|
|
99
|
+
pkg_path = (f"src/{report['package']}" if lang == "python"
|
|
100
|
+
else report["package"])
|
|
101
|
+
typer.echo(f"\nForge evolve — {auto} round(s) requested, "
|
|
102
|
+
f"{report['rounds_completed']} completed")
|
|
103
|
+
typer.echo("-" * 60)
|
|
104
|
+
typer.echo(f" llm: {report['llm']}")
|
|
105
|
+
typer.echo(f" package: {report['package']}")
|
|
106
|
+
typer.echo(f" language: {lang}")
|
|
107
|
+
typer.echo(f" output: {report['output_root']}/{pkg_path}")
|
|
108
|
+
typer.echo("")
|
|
109
|
+
typer.echo(" Round │ Iters │ Files │ Symbols (Δ) │ Score (Δ) │ Wire │ Conv")
|
|
110
|
+
typer.echo(" ──────┼───────┼───────┼────────────────┼────────────────┼──────┼──────")
|
|
111
|
+
for r in report["rounds"]:
|
|
112
|
+
sym = f"{r['symbol_count']:3d} ({r['delta_symbols']:+d})"
|
|
113
|
+
sco = f"{r['score']:.0f} ({r['delta_score']:+.1f})"
|
|
114
|
+
wire = r["wire_verdict"][:4]
|
|
115
|
+
conv = "Y" if r["converged"] else "n"
|
|
116
|
+
typer.echo(f" {r['round']:3d} │ {r['iterations']:3d} │ {r['files_written']:3d} │ "
|
|
117
|
+
f"{sym:<14s} │ {sco:<14s} │ {wire:<4s} │ {conv}")
|
|
118
|
+
typer.echo("")
|
|
119
|
+
typer.echo(f" final score: {report['final_score']:.0f}/100")
|
|
120
|
+
typer.echo(f" final symbols: {report['final_symbol_count']}")
|
|
121
|
+
typer.echo(f" trajectory: {report['score_trajectory']}")
|
|
122
|
+
typer.echo(f" converged: {report['converged']}")
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Auto-synthesized synergy adapter (syn-f69aff67).
|
|
3
|
+
|
|
4
|
+
Producer: evolve
|
|
5
|
+
Consumer: iterate
|
|
6
|
+
Kind: json_artifact
|
|
7
|
+
Score: 39
|
|
8
|
+
|
|
9
|
+
Why this synergy was detected:
|
|
10
|
+
- evolve emits json-out
|
|
11
|
+
- iterate accepts json_out
|
|
12
|
+
|
|
13
|
+
Re-emit with ``atomadic-forge synergy implement <id>`` after surfaces change.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
import tempfile
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Annotated
|
|
24
|
+
|
|
25
|
+
import typer
|
|
26
|
+
|
|
27
|
+
COMMAND_NAME = 'evolve-then-iterate'
|
|
28
|
+
COMMAND_HELP = 'Run evolve to emit a JSON artifact, then feed it to iterate for the next phase.'
|
|
29
|
+
|
|
30
|
+
app = typer.Typer(no_args_is_help=False, help='Run evolve to emit a JSON artifact, then feed it to iterate for the next phase.')
|
|
31
|
+
|
|
32
|
+
@app.callback(invoke_without_command=True)
|
|
33
|
+
def run(
|
|
34
|
+
ctx: typer.Context,
|
|
35
|
+
producer_args: Annotated[list[str] | None, typer.Argument(
|
|
36
|
+
help='Args forwarded to the producer command.')] = None,
|
|
37
|
+
) -> None:
|
|
38
|
+
"""Run evolve → capture artifact → feed to iterate."""
|
|
39
|
+
if ctx.invoked_subcommand is not None:
|
|
40
|
+
return
|
|
41
|
+
producer_args = producer_args or []
|
|
42
|
+
with tempfile.TemporaryDirectory(prefix='synergy-') as tmp:
|
|
43
|
+
artifact = Path(tmp) / 'producer.json'
|
|
44
|
+
cmd_a = [sys.executable, '-m',
|
|
45
|
+
'atomadic_forge.a4_sy_orchestration.cli',
|
|
46
|
+
'evolve', *producer_args,
|
|
47
|
+
'--json-out', str(artifact)]
|
|
48
|
+
rc = subprocess.run(cmd_a, capture_output=False).returncode
|
|
49
|
+
if rc != 0:
|
|
50
|
+
typer.secho(f'producer exited {rc}', fg='red', err=True)
|
|
51
|
+
raise typer.Exit(rc)
|
|
52
|
+
cmd_b = [sys.executable, '-m',
|
|
53
|
+
'atomadic_forge.a4_sy_orchestration.cli',
|
|
54
|
+
'iterate', str(artifact)]
|
|
55
|
+
rc = subprocess.run(cmd_b, capture_output=False).returncode
|
|
56
|
+
if rc != 0:
|
|
57
|
+
typer.secho(f'consumer exited {rc}', fg='red', err=True)
|
|
58
|
+
raise typer.Exit(rc)
|
|
59
|
+
try:
|
|
60
|
+
data = json.loads(artifact.read_text(encoding='utf-8'))
|
|
61
|
+
except (OSError, json.JSONDecodeError):
|
|
62
|
+
data = None
|
|
63
|
+
typer.echo(json.dumps({
|
|
64
|
+
'synergy': 'syn-f69aff67',
|
|
65
|
+
'producer': 'evolve',
|
|
66
|
+
'consumer': 'iterate',
|
|
67
|
+
'artifact_size_bytes': artifact.stat().st_size,
|
|
68
|
+
'producer_payload_keys': sorted(data.keys()) if isinstance(data, dict) else None,
|
|
69
|
+
}, indent=2))
|
|
70
|
+
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""``atomadic-forge feature-then-emergent`` — universal pipeline:
|
|
2
|
+
run any feature's JSON-emitting verb, then fan its payload into emergent scan.
|
|
3
|
+
|
|
4
|
+
This is the Item-1 synergy from the roadmap: every feature in the catalog
|
|
5
|
+
becomes a producer; emergent scan becomes the universal consumer. The
|
|
6
|
+
adapter is generic (it doesn't bake in any single feature) so adding a new
|
|
7
|
+
feature later automatically participates without code changes.
|
|
8
|
+
|
|
9
|
+
Examples:
|
|
10
|
+
atomadic-forge feature-then-emergent scout C:/path/to/repo --no-llm
|
|
11
|
+
atomadic-forge feature-then-emergent commandsmith discover --json
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
19
|
+
import tempfile
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Annotated
|
|
22
|
+
|
|
23
|
+
import typer
|
|
24
|
+
|
|
25
|
+
from atomadic_forge.a3_og_features.emergent_pipeline_integration import (
|
|
26
|
+
emergent_overlay_for_path,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
COMMAND_NAME = "feature-then-emergent"
|
|
30
|
+
COMMAND_HELP = ("Run any feature, then fan its JSON output into emergent "
|
|
31
|
+
"scan to surface novel compositions.")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
app = typer.Typer(no_args_is_help=False, help=COMMAND_HELP)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _try_extract_repo_root(payload: dict) -> Path | None:
|
|
38
|
+
"""Try to find a repo path in the producer's JSON payload.
|
|
39
|
+
|
|
40
|
+
We probe a few common keys (``repo``, ``src_root``, ``project_path``,
|
|
41
|
+
``primary_root``). This makes the adapter useful even when the producer
|
|
42
|
+
isn't scout-shaped.
|
|
43
|
+
"""
|
|
44
|
+
for key in ("src_root", "primary_root", "repo", "project_path", "repo_root"):
|
|
45
|
+
if key in payload and isinstance(payload[key], str):
|
|
46
|
+
p = Path(payload[key])
|
|
47
|
+
if p.exists():
|
|
48
|
+
return p
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@app.command("run",
|
|
53
|
+
context_settings={"allow_extra_args": True,
|
|
54
|
+
"ignore_unknown_options": True})
|
|
55
|
+
def run(
|
|
56
|
+
ctx: typer.Context,
|
|
57
|
+
feature: Annotated[str, typer.Argument(
|
|
58
|
+
help="ASS-ADE verb to run as the producer (e.g. scout, commandsmith).")],
|
|
59
|
+
explicit_repo: Annotated[Path | None, typer.Option("--repo",
|
|
60
|
+
help="Override repo root used for the emergent scan; otherwise inferred.",
|
|
61
|
+
exists=True, file_okay=False, dir_okay=True, resolve_path=True)] = None,
|
|
62
|
+
package: Annotated[str, typer.Option("--package")] = "atomadic_forge",
|
|
63
|
+
top_n: Annotated[int, typer.Option("--top-n")] = 10,
|
|
64
|
+
) -> None:
|
|
65
|
+
"""Run ``atomadic-forge <feature> <args>``, then run emergent scan over its scope.
|
|
66
|
+
|
|
67
|
+
Forwarded args go after ``--``. Example::
|
|
68
|
+
|
|
69
|
+
atomadic-forge feature-then-emergent run scout --top-n 3 -- C:/repo --no-llm
|
|
70
|
+
# → producer args = ["C:/repo", "--no-llm"], adapter top_n = 3
|
|
71
|
+
"""
|
|
72
|
+
feature_args = list(ctx.args) # everything after the recognised options
|
|
73
|
+
with tempfile.TemporaryDirectory(prefix="feature-emergent-") as tmp:
|
|
74
|
+
artifact = Path(tmp) / "producer.json"
|
|
75
|
+
cmd = [sys.executable, "-m",
|
|
76
|
+
"atomadic_forge.a4_sy_orchestration.unified_cli",
|
|
77
|
+
feature, *feature_args, "--json-out", str(artifact)]
|
|
78
|
+
rc = subprocess.run(cmd, capture_output=False).returncode
|
|
79
|
+
if rc != 0:
|
|
80
|
+
typer.secho(f"producer `{feature}` exited {rc}", fg="red", err=True)
|
|
81
|
+
raise typer.Exit(rc)
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
payload = json.loads(artifact.read_text(encoding="utf-8"))
|
|
85
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
86
|
+
typer.secho(f"producer JSON invalid: {exc}", fg="red", err=True)
|
|
87
|
+
raise typer.Exit(2) from exc
|
|
88
|
+
|
|
89
|
+
repo_root = explicit_repo or _try_extract_repo_root(payload)
|
|
90
|
+
if repo_root is None:
|
|
91
|
+
typer.secho(
|
|
92
|
+
"could not infer a repo root from producer payload; "
|
|
93
|
+
"pass --repo PATH to set it explicitly",
|
|
94
|
+
fg="yellow", err=True,
|
|
95
|
+
)
|
|
96
|
+
raise typer.Exit(2)
|
|
97
|
+
|
|
98
|
+
overlay = emergent_overlay_for_path(repo_root, phase="scout",
|
|
99
|
+
package=package)
|
|
100
|
+
# Only keep the top-N candidates the operator actually wants to see.
|
|
101
|
+
overlay["candidates"] = overlay["candidates"][:top_n]
|
|
102
|
+
|
|
103
|
+
typer.echo(json.dumps({
|
|
104
|
+
"synergy": "feature_then_emergent",
|
|
105
|
+
"feature": feature,
|
|
106
|
+
"feature_args": feature_args,
|
|
107
|
+
"producer_payload_keys": sorted(payload.keys()) if isinstance(payload, dict) else None,
|
|
108
|
+
"emergent_repo_root": str(repo_root),
|
|
109
|
+
"emergent_summary": overlay.get("summary_line"),
|
|
110
|
+
"candidates": overlay["candidates"],
|
|
111
|
+
}, indent=2, default=str))
|