app
AI Flow
Public Made by Adomby adom
Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board.
← Commit history
Merge PR #277: 0.2.11: signed-in project page, ASCII parts tab, bridge 0.2.53 guard
7 files changed
+38−18
Cargo.lock+10−10@@ -4,7 +4,7 @@ version = 4 [[package]] name = "adom-aiflow"-version = "0.2.10"+version = "0.2.11" dependencies = [ "aiflow-analyze", "aiflow-board",@@ -25,7 +25,7 @@ dependencies = [ [[package]] name = "aiflow-analyze"-version = "0.2.10"+version = "0.2.11" dependencies = [ "aiflow-board", "serde",@@ -35,7 +35,7 @@ dependencies = [ [[package]] name = "aiflow-board"-version = "0.2.10"+version = "0.2.11" dependencies = [ "roxmltree", "serde",@@ -44,7 +44,7 @@ dependencies = [ [[package]] name = "aiflow-bridge"-version = "0.2.10"+version = "0.2.11" dependencies = [ "aiflow-board", "serde",@@ -53,7 +53,7 @@ dependencies = [ [[package]] name = "aiflow-copper"-version = "0.2.10"+version = "0.2.11" dependencies = [ "aiflow-board", "aiflow-grid",@@ -63,7 +63,7 @@ dependencies = [ [[package]] name = "aiflow-grid"-version = "0.2.10"+version = "0.2.11" dependencies = [ "aiflow-board", "serde",@@ -72,7 +72,7 @@ dependencies = [ [[package]] name = "aiflow-place"-version = "0.2.10"+version = "0.2.11" dependencies = [ "aiflow-board", "serde",@@ -81,7 +81,7 @@ dependencies = [ [[package]] name = "aiflow-pours"-version = "0.2.10"+version = "0.2.11" dependencies = [ "aiflow-board", "aiflow-copper",@@ -91,7 +91,7 @@ dependencies = [ [[package]] name = "aiflow-router"-version = "0.2.10"+version = "0.2.11" dependencies = [ "aiflow-board", "aiflow-grid",@@ -101,7 +101,7 @@ dependencies = [ [[package]] name = "aiflow-run"-version = "0.2.10"+version = "0.2.11" dependencies = [ "serde", "serde_json",
Cargo.toml+1−1@@ -14,7 +14,7 @@ members = [ ] [workspace.package]-version = "0.2.10"+version = "0.2.11" edition = "2021" license = "MIT" repository = "https://wiki.adom.inc/adom/adom-aiflow"
crates/adom-aiflow/src/main.rs+17−2@@ -2077,6 +2077,15 @@ fn board_of(r: &Run) -> Board { Board::load(&r.current_board()).unwrap_or_else(|e| err(&e, &[])) } +/// A single-use link that signs a browser surface (a Hydrogen webview tab, pup) into the wiki and lands on+/// `page`: `adom-wiki auth login --page <owner/slug>`. A credential until redeemed: never print or log it.+fn signed_in_wiki_url(page: &str) -> Option<String> {+ let o = std::process::Command::new("adom-wiki").args(["auth", "login", "--page", page, "--json"]).stdin(std::process::Stdio::null()).output().ok()?;+ let v: Value = serde_json::from_slice(&o.stdout).ok()?;+ let d = if v["data"].is_object() { &v["data"] } else { &v };+ d["logged_in_url"].as_str().filter(|u| u.starts_with("https://wiki.adom.inc/")).map(str::to_string)+}+ /// The run's live board pulled fresh through its EDA backend: (the tool's native file, the plan board /// rendered from it). None when the run's plan board is itself the board of record (KiCad). fn live_board(r: &Run, cli: &Cli, dir: &std::path::Path) -> Option<(String, String)> {@@ -2909,7 +2918,9 @@ fn main() { r.save().unwrap(); if *show { // one named tab walks through the pages (never a tab per part)- let name = format!("Parts · {}", &r.data["id"].as_str().unwrap_or("run")[..8.min(r.data["id"].as_str().unwrap_or("run").len())]);+ // ASCII only: Hydrogen's open-or-refresh never matches a name with a non-ASCII character+ // and opens a new tab per call (adom/hydrogen#137)+ let name = format!("Parts - {}", &r.data["id"].as_str().unwrap_or("run")[..8.min(r.data["id"].as_str().unwrap_or("run").len())]); for row in rows.iter().filter(|x| x["wiki"].is_string()) { let url = format!("https://wiki.adom.inc/{}", row["wiki"].as_str().unwrap()); let _ = std::process::Command::new("adom-cli").args(["hydrogen", "webview", "open-or-refresh", "--name", &name, "--url", &url, "--ai-thread", &t, "--reason", "adom-aiflow: show the chosen parts"]).stdin(std::process::Stdio::null()).output();@@ -2957,7 +2968,11 @@ fn main() { if let Some(l) = v["cloudLibrary"].as_str().filter(|l| l.starts_with("lib_")) { r.data["cloudLibrary"] = json!(l); } r.save().unwrap(); if !no_push {- let _ = std::process::Command::new("adom-cli").args(["hydrogen", "webview", "open-or-refresh", "--name", "Project page", "--url", v["url"].as_str().unwrap_or(""), "--ai-thread", &t, "--reason", "adom-aiflow: the board's project page, revision 1"]).stdin(std::process::Stdio::null()).output();+ // a webview tab has no wiki session: a private page would show "Page not found", so open the page+ // through a single-use browser-session link (adom-wiki auth login; never printed)+ // (every time, not only with --private: an --existing page may be private too)+ let url = signed_in_wiki_url(page).unwrap_or_else(|| v["url"].as_str().unwrap_or("").to_string());+ let _ = std::process::Command::new("adom-cli").args(["hydrogen", "webview", "open-or-refresh", "--name", "Project page", "--url", &url, "--ai-thread", &t, "--reason", "adom-aiflow: the board's project page, revision 1"]).stdin(std::process::Stdio::null()).output(); } if lane == "altium" { let from_lib: Vec<&str> = v["projectLibParts"].as_array().into_iter().flatten().filter_map(|x| x.as_str()).collect();
docs/release-0.2.11.mdadded+5@@ -0,0 +1,5 @@+# adom-aiflow 0.2.11++- **The project page opens signed in.** A Hydrogen webview tab has no wiki session, so a private project page (`project-page --private`) opened as "Page not found" with a Login button. `project-page` now opens the page through a single-use `adom-wiki auth login --page` link (never printed). See adom/hydrogen#138, adom/adom-cli#20.+- **One parts tab, not one per call.** The `parts --show` walkthrough's tab is named `Parts - <run>` (ASCII): Hydrogen's `open-or-refresh` never matched the old `Parts · <run>` name and opened a new tab each time (adom/hydrogen#137).+- **The symbol check's bridge guard is 0.2.53.** The signed Altium Bridge 0.2.52 shipped only the 3D-body fix; library pin types and `symbol_prims`' drawn-mode body shipped in 0.2.53. On a bridge before 0.2.53, pin-type findings (and the sheet-body findings of a symbol with no kept spec) are warnings that say to update the bridge.
package.json+1−1@@ -1,7 +1,7 @@ { "slug": "adom-aiflow", "type": "app",- "version": "0.2.10",+ "version": "0.2.11", "title": "AI Flow", "description": "Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board. One Rust binary with a crate per step (placement helpers, a grid router with Kelvin taps, pours with keepouts, KiCad's DRC gate, live landing through the KiCad Bridge, copper measurement, current and thermal analysis) and a finish line that refuses an unfinished board. Every command answers with hints for the AI; every turn, its thinking time and every rework loop go into run.jsonl, so Claude, Codex and any other engine are compared on the same flow. KiCad, Fusion 360 and Altium Designer today (the same steps and gates through each tool's bridge); Adom's own web apps next.", "summary": "Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board. The AI thinks its way from placement through routing, pours, current and thermal analysis to a delivered video; the binary does the fast, deterministic parts of every step, hands back hints, and keeps a ledger of every turn, every return to an earlier step, and the clock from the prompt to done.",
page.json+1−1@@ -1,7 +1,7 @@ { "slug": "adom-aiflow", "type": "app",- "version": "0.2.10",+ "version": "0.2.11", "title": "AI Flow", "description": "Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board. One Rust binary with a crate per step (placement helpers, a grid router with Kelvin taps, pours with keepouts, KiCad's DRC gate, live landing through the KiCad Bridge, copper measurement, current and thermal analysis) and a finish line that refuses an unfinished board. Every command answers with hints for the AI; every turn, its thinking time and every rework loop go into run.jsonl, so Claude, Codex and any other engine are compared on the same flow. KiCad, Fusion 360 and Altium Designer today (the same steps and gates through each tool's bridge); Adom's own web apps next.", "summary": "Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board. The AI thinks its way from placement through routing, pours, current and thermal analysis to a delivered video; the binary does the fast, deterministic parts of every step, hands back hints, and keeps a ledger of every turn, every return to an earlier step, and the clock from the prompt to done.",
tools/altium-project.py+3−3@@ -386,17 +386,17 @@ def symbol_check(st, sch): pd = p.get('data') if isinstance(p.get('data'), dict) else p if p.get('success') and pd.get('ok', True) is not False: prims[lr] = pd rep = check_symbols(comps, specs, prims, run_spec())- # a bridge before 0.2.52 makes every library pin Passive and answers symbol_prims' body from+ # a bridge before 0.2.53 makes every library pin Passive (0.2.52 shipped only the 3D-body fix) and answers symbol_prims' body from # any display mode: its pin types and its sheet body say nothing, so those findings warn ver = str(sd.get('bridgeVersion') or data.get('bridgeVersion') or '')- old = tuple(int(x) for x in re.findall(r'\d+', ver)[:3]) < (0, 2, 52) if ver else False+ old = tuple(int(x) for x in re.findall(r'\d+', ver)[:3]) < (0, 2, 53) if ver else False if old: keep, moved = [], [] for e in rep['errors']: (moved if e.startswith('pin type') or ('as drawn on the sheet' in e and not specs) else keep).append(e) if moved: rep['errors'] = keep- rep['warnings'] = ['Altium Bridge %s predates 0.2.52 (every library pin Passive, the body from any display mode); update the bridge, then `altium library --again` and `altium symbols`: %s' % (ver, m) for m in moved] + rep['warnings']+ rep['warnings'] = ['Altium Bridge %s predates 0.2.53 (every library pin Passive, the body from any display mode); update the bridge, then `altium library --again` and `altium symbols`: %s' % (ver, m) for m in moved] + rep['warnings'] rep['ok'] = not keep rep['bridgeVersion'] = ver or None (RUN / 'schematic-check.json').write_text(json.dumps(rep, indent=1))