feat: complete plans 20/24/25 + project template + BOM analyzer

Plan 20 (UI/UX YiACAD) — 100% complete:
- docs/YIACAD_COMPILED_ANCHOR_POINTS.md: C++ fork insertion points
- docs/YIACAD_APP_INTENTS_STUDY.md: App Intents + on-device models

Plan 24 (Mistral Studio) — blocked tasks annotated:
- docs/MISTRAL_STUDIO_STATUS_2026-03-25.md: status + execution order

Plan 25 (Hypnoled) — tooling ready:
- docs/HYPNOLED_STATUS_2026-03-25.md: status + 10-step execution
- tools/industrial/bom_analyzer.py: generic BOM parser + LCSC suggestions

Project template:
- templates/kill-life-project/: full scaffold for client repos
- tools/project_init.sh: clone + apply template + commit
- docs/PROJECT_TEMPLATE.md: usage guide

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
L'électron rare
2026-03-25 02:32:53 +01:00
parent 404e25bedd
commit b7258c72df
23 changed files with 1601 additions and 35 deletions
+70
View File
@@ -0,0 +1,70 @@
# Hypnoled Pilot Project — Status 2026-03-25
> Plan 25 — `docs/plans/25_todo_hypnoled_pilote.md`
---
## Completed (5/14 tasks)
### Phase 0 — Structuration (DONE)
- [x] T-HP-001: Fiche client Garnier
- [x] T-HP-002: Structure projet Hypnoled
- [x] T-HP-003: Migration fichiers
- [x] T-HP-004: Index fichiers KiCad (4 projets, 7 sous-schemas)
- [x] T-HP-005: Test connectivite Forge/Codestral (200 OK)
### Phase 1 — Agents Kill_LIFE (3/4 done)
- [x] T-HP-010: Forge review PCB — 6/6 sous-schemas, 8 critiques, 7 avertissements, 18 recommandations
- [x] T-HP-011: Sentinelle monitoring — 9 067 tokens, 100% success
- [x] T-HP-012: Tower documentation — PDF + Markdown rapports
- [ ] T-HP-013: BOM analysis — **tooling created** (`tools/industrial/bom_analyzer.py`), awaiting BOM CSV assets
## Blocked — Missing assets in checkout
The Hypnoled hardware assets (KiCad projects, BOMs, simulations) are not present in the current Kill_LIFE checkout. All remaining tasks are blocked until assets are materialized.
### Blocked tasks
| Task | Description | Blocker |
|------|-------------|---------|
| T-HP-013 | BOM analysis | [blocked: BOM CSVs not in checkout] — tooling ready |
| T-HP-020 | Forge SPICE review | [blocked: hypnoled.asc not in checkout] + [blocked: Mistral key expired] |
| T-HP-021 | Forge firmware skeleton | [blocked: Mistral key expired] |
| T-HP-022 | Benchmark Hypnoled prompts | [blocked: Mistral key expired] |
| T-HP-030 | Dataset KiCad extraction | [blocked: KiCad files not in checkout] |
| T-HP-031 | Dataset SPICE extraction | [blocked: simulation files not in checkout] |
| T-HP-032 | Evaluation post fine-tune | [blocked: depends on Plan 24 fine-tune] |
| T-HP-033 | Quilter canary route | [blocked: KiCad files not in checkout] |
| T-HP-034 | PCB Designer AI fast-fab | [blocked: KiCad files not in checkout] |
| T-HP-035 | kicad-happy playbook parity | [blocked: KiCad files not in checkout] |
## Tooling ready
| Tool | Location | Purpose |
|------|----------|---------|
| `bom_analyzer.py` | `tools/industrial/` | Generic BOM parser, normalizer, LCSC/JLCPCB alternative suggester |
| `pcb_ai_fab_tui.sh` | `tools/cockpit/` | PCB fabrication TUI |
| `fab_package_tui.sh` | `tools/cockpit/` | Fab package builder |
---
## Execution order when assets are available
1. **Copy/symlink Hypnoled assets** into Kill_LIFE or ensure path references work
2. **T-HP-013** — Run `bom_analyzer.py` on `Audio2LED_PCB/BOM.csv` and `PCB_legacy/BOM.csv`
- Output: normalized BOM, LCSC alternatives, assembly-ready status
3. **T-HP-033** — Quilter canary route (KiCad -> Quilter -> fab package)
4. **T-HP-035** — kicad-happy playbook parity
5. **T-HP-034** — PCB Designer AI evaluation
6. **T-HP-020** — Forge SPICE review (needs Mistral key)
7. **T-HP-021** — Forge firmware skeleton (needs Mistral key)
8. **T-HP-030/031** — Dataset extraction for fine-tune pipeline
9. **T-HP-022** — Benchmark after fine-tune
10. **T-HP-032** — Post fine-tune evaluation
## Key findings from Phase 1 reviews
From the Forge review of 6 DALI PCB sub-schematics:
- **8 critical issues** identified (Zener protection, double ESP32, PCM5122 power, IRF3415 overdim, MCP power chain, MPR121 I2C addr)
- **Cost**: ~0.009 EUR for 9 067 tokens — validates the multi-LLM approach for hardware review
- **Next step**: BOM analysis (T-HP-013) will cross-reference these findings with component availability and cost
+94
View File
@@ -0,0 +1,94 @@
# Mistral Studio Integration — Status 2026-03-25
> Plan 24 — `docs/plans/24_todo_integration_mistral_studio.md`
---
## Completed (2/34 tasks)
| Task | Description | Date |
|------|-------------|------|
| T-MS-001 | `mistral_studio_tui.sh` cockpit (14 actions) | 2026-03-21 |
| T-MS-023 | Codestral FIM dans Mascarade router | 2026-03-22 |
## Ready (tooling in place, awaiting runtime)
| Task | Description | Blocker |
|------|-------------|---------|
| T-MS-002 | Dataset KiCad JSONL merge+validate+upload | [blocked: Mistral key expired] + VM virtiofs deadlock |
| T-MS-003 | Dataset SPICE+Embedded JSONL merge+validate+upload | [blocked: Mistral key expired] + VM virtiofs deadlock |
| T-MS-004 | Upload docs commerciales Tower Document Library | [blocked: Mistral key expired] |
| T-MS-005 | Upload datasheets test pour IA Documentaire | [blocked: Mistral key expired] |
## Blocked — API key required
All tasks below are directly blocked by the expired Mistral API key. No local workaround exists.
### P1 — Fine-tune (all blocked)
| Task | Description | Depends on |
|------|-------------|------------|
| T-MS-010 | Fine-tune KiCad sur open-mistral-7b | [blocked: Mistral key expired] + T-MS-002 |
| T-MS-011 | Fine-tune SPICE+Embedded sur codestral-latest | [blocked: Mistral key expired] + T-MS-003 |
| T-MS-012 | Batch benchmark 100 prompts | [blocked: Mistral key expired] + T-MS-010/011 |
| T-MS-013 | Document Library RAG Tower | [blocked: Mistral key expired] + T-MS-004 |
### P2 — Integrations Studio (all blocked)
| Task | Description | Depends on |
|------|-------------|------------|
| T-MS-020 | Pipeline OCR datasheets | [blocked: Mistral key expired] |
| T-MS-021 | Audio STT dans workflow ops | [blocked: Mistral key expired] |
| T-MS-022 | Installer Vibe CLI sur VM | [blocked: Mistral key expired] |
### P3 — Production (all blocked)
| Task | Description | Depends on |
|------|-------------|------------|
| T-MS-030 | Deploy fine-tuned models in Mascarade | [blocked: Mistral key expired] + T-MS-010/011 |
| T-MS-031 | Tests E2E Studio->Mascarade->Agent | [blocked: Mistral key expired] + T-MS-030 |
| T-MS-032 | Documentation Outline wiki | Can be started offline (see below) |
| T-MS-033 | Cron audit qualite modeles | [blocked: Mistral key expired] + T-MS-030 |
## Actionable now (no API key needed)
### T-MS-032 — Documentation Outline wiki (partial)
The four wiki pages can be drafted offline as templates:
- Page: Mistral Studio Overview — can be written from existing `ANALYSE_EXHAUSTIVE_ECOSYSTEME_2026-03-21.md` and `MISTRAL_DOCS_REFERENCE_2026-03-21.md`
- Page: Fine-tune Pipeline Guide — can be written from `mistral_dataset_pipeline.py` docstrings
- Page: IA Documentaire Usage — skeleton only (needs OCR test results)
- Page: Audio Integration — skeleton only (needs STT test results)
---
## Tooling inventory (all present)
| Tool | Location | Status |
|------|----------|--------|
| `mistral_studio_tui.sh` | `tools/cockpit/` | Ready, 14 actions |
| `mistral_dataset_pipeline.py` | mascarade/finetune/ | Ready, needs VM runtime |
| `mistral_agents_beta_api.py` | mascarade/agents/ | Ready, needs valid key |
| `dataset_audit_tui.sh` | `tools/cockpit/` | Gate locale avant upload |
| `mistral_workspace_guard.sh` | `tools/cockpit/` | Garde-fou operateur |
| `mascarade_mesh_env_sync.sh` | `tools/cockpit/` | Racines machines figees |
| `load_mistral_governance_env.sh` | `tools/cockpit/` | Charge MISTRAL_GOVERNANCE_API_KEY |
## Key conventions locked
- Mascarade runtime uses `MISTRAL_API_KEY` only
- Kill_LIFE governance uses `MISTRAL_GOVERNANCE_API_KEY`
- Active Mascarade code: `/Users/electron/Documents/Projets/mascarade`
- Kill_LIFE tracking: `/Users/electron/Documents/Lelectron_rare/Kill_LIFE`
- `/Users/electron/Documents/Lelectron_rare/Github_Repos/Perso/mascarade-main` is forbidden for this lot
---
## Next actions when keys are renewed
Priority order:
1. Run `dataset_audit_tui.sh` preflight — confirm datasets accessible
2. Execute `mistral_dataset_pipeline.py full --domain kicad` then `--domain spice-embedded` (T-MS-002/003)
3. Upload commercial docs (T-MS-004) and test datasheets (T-MS-005)
4. Launch fine-tune jobs (T-MS-010/011)
5. Run batch benchmark (T-MS-012)
6. Configure Document Library RAG (T-MS-013)
7. Deploy fine-tuned models in router (T-MS-030)
Estimated time to complete all P0+P1 once unblocked: 2-3 sessions.
+123
View File
@@ -0,0 +1,123 @@
# Kill_LIFE Project Template
## Overview
The Kill_LIFE project template provides a standard directory structure for hardware/firmware client projects. It sets up the directories, CI workflows, and configuration that Kill_LIFE agents need to operate on a repo.
## Template Location
```
templates/kill-life-project/
```
## Directory Structure
```
.kill-life.yaml # Links project to Kill_LIFE orchestrator
hardware/
pcb/ # KiCad projects (.kicad_pro, .kicad_sch, .kicad_pcb)
simulation/ # LTspice/ngspice simulation files
bom/ # Normalized BOMs (CSV/JSON)
firmware/
src/ # ESP32/STM32 source code
platformio.ini # PlatformIO build config
docs/
reviews/ # Auto-generated Forge design reviews
specs/ # Project specifications and requirements
client/ # Client-facing documentation
fabrication/ # Gerbers, drill files, JLCPCB archives
.github/workflows/kill-life-ci.yml # CI: ERC/DRC, BOM check, firmware build
Makefile # Common targets
```
## Usage
### Initialize a new or existing repo
```bash
./tools/project_init.sh <github_repo_url> <project_name> [client_name]
```
Example for the Hypnoled project:
```bash
./tools/project_init.sh git@github.com:electron-rare/hypnoled.git hypnoled "Hypnoled SAS"
```
This will:
1. Clone the repo to `/tmp/kill-life-init/hypnoled/`
2. Create the standard directory structure
3. Copy template files (skips files that already exist)
4. Generate `.kill-life.yaml` with the project and client names filled in
5. Commit the changes
You can override the working directory with `KILL_LIFE_WORK_DIR`:
```bash
KILL_LIFE_WORK_DIR=~/projects ./tools/project_init.sh ...
```
### After initialization
Push the changes and verify CI runs:
```bash
cd /tmp/kill-life-init/hypnoled
git push
```
### Migrating existing projects (e.g. Hypnoled)
For repos that already have content in a flat structure (like `DALI PCB/`):
1. Run the init script to add the template structure
2. Move existing KiCad files into `hardware/pcb/`
3. Move existing scripts/firmware into `firmware/src/`
4. Update the Makefile if the project has custom targets
5. Commit and push
Example migration for Hypnoled:
```bash
cd /tmp/kill-life-init/hypnoled
mv "DALI PCB"/*.kicad_* hardware/pcb/
mv scripts/* firmware/src/ 2>/dev/null || true
git add -A && git commit -m "refactor: migrate to Kill_LIFE project structure"
git push
```
## .kill-life.yaml Reference
| Field | Description |
|---|---|
| `kill_life.version` | Config format version (currently `1`) |
| `kill_life.project` | Project short name |
| `kill_life.client` | Client or organization name |
| `kill_life.mascarade_url` | Mascarade LLM gateway URL |
| `kill_life.agents` | List of Kill_LIFE agents enabled for this project |
| `kill_life.mcp_servers` | MCP servers the agents can use |
| `kill_life.hardware.*` | Paths to hardware directories |
| `kill_life.firmware.*` | Firmware framework and source path |
## Makefile Targets
| Target | Description |
|---|---|
| `make help` | List all available targets |
| `make erc` | Run KiCad Electrical Rules Check |
| `make drc` | Run KiCad Design Rules Check |
| `make review` | Full design review (ERC + DRC) |
| `make bom-export` | Export BOM from KiCad schematic |
| `make bom-check` | Validate BOM completeness |
| `make fabrication-prep` | Generate Gerbers and JLCPCB files |
| `make firmware-build` | Build firmware with PlatformIO |
| `make firmware-flash` | Flash firmware to connected board |
| `make clean` | Remove build artifacts |
## CI Workflow
The included GitHub Actions workflow (`.github/workflows/kill-life-ci.yml`) runs on push/PR to `main`:
1. **ERC/DRC** — Runs KiCad ERC and DRC on all schematics/PCBs using the `kicad8_auto` container
2. **BOM Check** — Validates that a BOM file exists
3. **Firmware Build** — Builds firmware with PlatformIO (only if `platformio.ini` exists)
+154
View File
@@ -0,0 +1,154 @@
# YiACAD App Intents and On-Device Models Study (T-UX-008)
> Date: 2026-03-25 | Source: Plan 20 - UI/UX Apple-native
---
## 1. App Intents / Shortcuts for YiACAD Automation
### 1.1 What App Intents provide
App Intents (iOS 16+ / macOS 13+) let native apps expose structured actions to:
- Siri voice commands
- Shortcuts.app (visual automation builder)
- Spotlight suggestions
- Focus filters and automation triggers
### 1.2 Applicability to YiACAD
| Capability | YiACAD use case | Feasibility |
|---|---|---|
| `@AppIntent` struct | Expose "Run ERC/DRC", "Check BOM", "Sync ECAD/MCAD" as system-wide actions | HIGH -- requires Swift wrapper around backend client |
| `@AppShortcutsProvider` | Pre-built shortcuts for common review workflows | HIGH |
| `@Parameter` with entity queries | Let user pick a KiCad project or FreeCAD document from Shortcuts | MEDIUM -- requires project index |
| Siri invocation | "Hey Siri, run YiACAD review on current project" | MEDIUM -- needs active project context |
| Focus filter | Auto-run status check when entering "Engineering" Focus | LOW priority but trivial |
### 1.3 Architecture for App Intents integration
```
Shortcuts.app / Siri
|
v
YiACAD macOS helper app (Swift, sandboxed)
| -- calls local HTTP backend on 127.0.0.1:38435
v
yiacad_backend_service.py (already exists)
|
v
yiacad_native_ops.py -> artifacts/cad-ai-native/
```
The helper app is a thin Swift binary that:
1. Declares `AppIntent` structs for each YiACAD command
2. Calls the existing backend HTTP API (`POST /run`)
3. Returns structured results to Shortcuts (summary, status, severity)
### 1.4 Required work
| Task | Effort | Dependency |
|---|---|---|
| Create `YiACADHelper.app` Swift project with App Intents | 2-3 days | Xcode, macOS 13+ SDK |
| Define `YiACADStatusIntent`, `YiACADReviewIntent`, `YiACADSyncIntent` | 1 day | Backend client API stable |
| Add `@AppShortcutsProvider` with default shortcuts | 0.5 day | Intents defined |
| Test with Shortcuts.app and Siri | 0.5 day | Helper app built |
| Sign and notarize for distribution | 1 day | Apple Developer account |
Total estimated effort: 4-5 days.
### 1.5 Alternatives considered
| Alternative | Pros | Cons |
|---|---|---|
| AppleScript / osascript bridge | No Swift needed, works today | No Siri, no Shortcuts discovery, no parameters |
| Automator actions | Legacy but functional | Deprecated by Apple, no future |
| CLI-only (current) | Already works via `yiacad_backend_client.py` | Not discoverable, no voice, no Shortcuts |
**Recommendation**: Build the Swift helper as a lightweight companion. The HTTP backend already exists; the helper is pure glue code.
---
## 2. On-Device Models for Review Assistance
### 2.1 Apple CoreML
| Aspect | Assessment |
|---|---|
| What it does | Runs ML models on Apple Neural Engine (ANE), GPU, or CPU |
| Model format | `.mlmodel` / `.mlpackage` (converted from PyTorch/ONNX/TF) |
| Relevant models | Text classification (severity triage), embedding (semantic search), small generative (code review hints) |
| Integration path | Swift `MLModel` API or Python `coremltools` |
| Strengths | Zero network latency, privacy-preserving, ANE acceleration on M-series |
| Limitations | Model size constrained (~4GB practical max), no large LLM inference, conversion can lose accuracy |
### 2.2 MLX (Apple ML Research framework)
| Aspect | Assessment |
|---|---|
| What it does | NumPy-like framework optimized for Apple Silicon unified memory |
| Model support | LLaMA, Mistral, Phi, Gemma, Qwen families via `mlx-lm` |
| Quantization | 4-bit/8-bit GGUF or MLX native format |
| Integration path | Python `mlx` package, runs directly in the YiACAD Python environment |
| Strengths | Full LLM inference on-device, fast on M1+, no cloud dependency |
| Limitations | Apple Silicon only, Python-only API, memory-bound for >13B models |
### 2.3 Recommended on-device models for YiACAD
| Use case | Model | Size | Framework | Expected speed (M2 Pro) |
|---|---|---|---|---|
| Review severity triage | Fine-tuned `Phi-3-mini-4k` (3.8B, Q4) | ~2.2 GB | MLX | ~30 tok/s |
| ERC/DRC explanation | `Mistral-7B-Instruct` (Q4) | ~4.1 GB | MLX | ~20 tok/s |
| Semantic search on project docs | `nomic-embed-text` (137M) | ~0.3 GB | CoreML or MLX | <50ms per query |
| Quick summarization | `Qwen2-1.5B-Instruct` (Q8) | ~1.7 GB | MLX | ~45 tok/s |
### 2.4 Integration architecture
```
YiACAD review pipeline
|
+-- severity triage (on-device, MLX Phi-3-mini)
| input: ERC/DRC raw output
| output: severity label + confidence
|
+-- explanation generation (on-device, MLX Mistral-7B)
| input: violation + board context
| output: human-readable explanation
|
+-- semantic project search (on-device, CoreML nomic-embed)
| input: user query
| output: ranked relevant files/components
|
+-- complex review (cloud fallback, Mascarade mesh)
input: full project context
output: deep architectural review
```
### 2.5 Implementation path
| Step | Task | Effort |
|---|---|---|
| 1 | Add `mlx` and `mlx-lm` to YiACAD Python deps | 0.5 day |
| 2 | Create `tools/cad/yiacad_local_model.py` wrapping MLX inference | 1-2 days |
| 3 | Download and quantize target models (Phi-3-mini, Mistral-7B) | 0.5 day |
| 4 | Wire severity triage into `yiacad_native_ops.py` review pipeline | 1 day |
| 5 | Wire explanation generation into review center output | 1 day |
| 6 | Add CoreML embedding for semantic search | 1-2 days |
| 7 | Benchmark on M1/M2/M3 and set model-size gates | 0.5 day |
Total estimated effort: 5-7 days.
### 2.6 Decision criteria for on-device vs cloud
| Factor | On-device | Cloud (Mascarade) |
|---|---|---|
| Latency | <2s for triage, <10s for explanation | 5-30s depending on mesh load |
| Privacy | Full -- no data leaves machine | Depends on mesh topology |
| Quality | Good for triage/explanation, limited for deep review | Best for complex multi-file analysis |
| Availability | Always (no network needed) | Requires mesh connectivity |
| Cost | Zero marginal cost | Token-based |
**Recommendation**: Use on-device models for fast-path triage and explanation. Fall back to Mascarade cloud mesh for deep multi-file reviews. The backend service can route automatically based on task complexity.
---
*Generated 2026-03-25 for Plan 20 T-UX-008*
+162
View File
@@ -0,0 +1,162 @@
# YiACAD Compiled Anchor Points (T-UX-007)
> Date: 2026-03-25 | Source: Plan 20 - UI/UX Apple-native
---
## 1. KiCad (`kicad-ki` fork) -- Compiled Insertion Points
### 1.1 Toolbar anchors
| File | Role | Write-set status |
|---|---|---|
| `pcbnew/toolbars_pcb_editor.cpp` | PCB editor toolbar groups, YiACAD Review group | SAFE -- already used |
| `eeschema/toolbars_sch_editor.cpp` | Schematic editor toolbar groups, YiACAD Review group | SAFE -- already used |
| `common/eda_base_frame.cpp` | Shared `configureToolbars()` -- global toolbar rebuild | NO-TOUCH (shared, macOS menu sensitivity) |
| `include/eda_base_frame.h` | Header for toolbar extension API | READ-ONLY reference |
### 1.2 Menu anchors
| File | Role | Write-set status |
|---|---|---|
| `pcbnew/menubar_pcb_editor.cpp` | PCB editor menubar, YiACAD Status entry | SAFE -- already used |
| `eeschema/menubar.cpp` | Schematic editor menubar, YiACAD Status entry | SAFE -- already used |
| `kicad/menubar.cpp` | KiCad Manager menubar | SAFE -- already used |
### 1.3 Editor control anchors (action handlers)
| File | Role | Write-set status |
|---|---|---|
| `pcbnew/tools/board_editor_control.cpp` | PCB TOOL_ACTION handlers: Status, ERC/DRC, BOM Review, ECAD/MCAD Sync | SAFE -- already used |
| `pcbnew/tools/board_editor_control.h` | PCB handler declarations | SAFE -- already used |
| `eeschema/tools/sch_editor_control.cpp` | Schematic TOOL_ACTION handlers (symmetric to PCB) | SAFE -- already used |
| `eeschema/tools/sch_editor_control.h` | Schematic handler declarations | SAFE -- already used |
### 1.4 Inspector / assistant panel anchors
| File | Role | Write-set status |
|---|---|---|
| `pcbnew/pcb_edit_frame.cpp` | PCB editor main frame -- dockable assistant pane slot | CANDIDATE (next lot) |
| `eeschema/sch_edit_frame.cpp` | Schematic editor main frame -- dockable assistant pane slot | CANDIDATE (next lot) |
| `common/widgets/properties_panel.h` | Properties panel API -- model for AssistantPane | READ-ONLY reference |
### 1.5 Rich panel / webview anchors
| File | Role | Write-set status |
|---|---|---|
| `include/widgets/webview_panel.h` | WEBVIEW_PANEL header (embedded chromium panel) | CANDIDATE (rich review UI) |
| `common/widgets/webview_panel.cpp` | WEBVIEW_PANEL implementation | CANDIDATE (rich review UI) |
| `include/tool/tool_action.h` | TOOL_ACTION registration API | READ-ONLY reference |
| `include/tool/action_menu.h` | ACTION_MENU builder API | READ-ONLY reference |
### 1.6 Known risks (KiCad)
- `wxExecute(..., wxEXEC_SYNC)` + `wxMessageBox` blocks UI thread in current runners
- Toolbar/menu rebuild on macOS uses deferred recreation -- fragile with dynamic groups
- PCB and SCH shells are duplicated; any extension must stay strictly symmetric
- `EDA_BASE_FRAME` is shared across all frames -- changes there cascade everywhere
---
## 2. FreeCAD (`freecad-ki` fork) -- Compiled Insertion Points
### 2.1 Shell dock anchor
| File | Role | Write-set status |
|---|---|---|
| `src/Gui/MainWindow.cpp` | `Std_YiACADShellView` dock, hidden by default, toggle via "YiACAD Shell" | SAFE -- already used (T-UX-003D) |
### 2.2 Dock management anchors
| File | Role | Write-set status |
|---|---|---|
| `src/Gui/DockWindow.h` | Dock widget base API | NO-TOUCH (layout persistence risk) |
| `src/Gui/DockWindowManager.cpp` | Dock lifecycle, tabification | NO-TOUCH (double ownership risk) |
| `src/Gui/ComboView.h` | ComboView (tree+property) -- model for inspector | NO-TOUCH |
| `src/Gui/ComboView.cpp` | ComboView implementation | NO-TOUCH |
### 2.3 Workbench anchors (Python-level, compile-adjacent)
| File | Role | Write-set status |
|---|---|---|
| `src/Mod/YiACADWorkbench/InitGui.py` | Workbench registration entry point | SAFE -- already used |
| `src/Mod/YiACADWorkbench/yiacad_freecad_gui.py` | Inspector, command palette, review center (Python) | SAFE -- primary surface |
| `src/Gui/Workbench.cpp` | C++ workbench manager | READ-ONLY reference |
| `src/Gui/Application.cpp` | Application init, workbench loading | READ-ONLY reference |
### 2.4 Global menu/toolbar injection anchors
| File | Role | Write-set status |
|---|---|---|
| `src/Gui/WorkbenchManipulator.h` | Manipulator API for cross-workbench toolbar injection | CANDIDATE (global YiACAD toolbar) |
| `src/Gui/WorkbenchManipulatorPython.cpp` | Python-accessible manipulator | CANDIDATE |
| `src/Gui/ApplicationPy.cpp` | Python-side application commands | CANDIDATE |
### 2.5 Command registration anchors
| File | Role | Write-set status |
|---|---|---|
| `src/Gui/Command.cpp` | C++ command framework | READ-ONLY reference |
| `src/Gui/PropertyView.h` | Property inspector API (reference for panel design) | READ-ONLY reference |
### 2.6 Known risks (FreeCAD)
- Menu/toolbar rebuilt on every workbench switch -- injected items must re-register
- Dock name persistence: layout restore breaks if dock objectName changes
- Double ownership if Python workbench inspector and C++ shell dock coexist
- `run_native_action()` is synchronous in UI thread -- risk of freezes
---
## 3. Unified Semantic Toolbar Specification (T-UX-007)
### 3.1 Design principles
1. **Symmetric**: KiCad PCB/SCH and FreeCAD expose the same action set
2. **Grouped**: single "YiACAD" toolbar group, not scattered actions
3. **Non-blocking**: all actions must dispatch asynchronously (target: service-first via backend client)
4. **Degradable**: toolbar present even when backend is unreachable (status shows "offline")
### 3.2 Canonical action set
| Action ID | Label | Icon hint | Shortcut | Backend command |
|---|---|---|---|---|
| `yiacad.status` | YiACAD Status | status-circle | Ctrl+Shift+Y | `status` |
| `yiacad.erc_drc` | ERC/DRC Review | check-shield | Ctrl+Shift+E | `kicad-erc-drc` |
| `yiacad.bom_review` | BOM Review | list-check | Ctrl+Shift+B | `bom-review` |
| `yiacad.ecad_mcad_sync` | ECAD/MCAD Sync | arrows-sync | Ctrl+Shift+S | `ecad-mcad-sync` |
| `yiacad.inspector` | Inspector | magnifying-glass | Ctrl+Shift+I | (local UI toggle) |
| `yiacad.palette` | Command Palette | command-line | Ctrl+Shift+P | (local UI toggle) |
### 3.3 Toolbar layout per surface
```
KiCad pcbnew toolbar: [ YiACAD Status | ERC/DRC | BOM | Sync | --- | Inspector | Palette ]
KiCad eeschema toolbar: [ YiACAD Status | ERC/DRC | BOM | Sync | --- | Inspector | Palette ]
FreeCAD YiACAD toolbar: [ YiACAD Status | ERC/DRC | BOM | Sync | --- | Inspector | Palette ]
```
All three surfaces use the identical group. KiCad uses `TOOL_ACTION` registration; FreeCAD uses `WorkbenchManipulator` or `FreeCADGui.addCommand`.
### 3.4 Transport contract
Every toolbar action calls `yiacad_backend_client.py` (or its compiled equivalent) with:
- Input: `{"command": "<action_id>", ...params}`
- Output: YiACAD UX contract (`status`, `severity`, `summary`, `details`, `artifacts`, `next_steps`)
- Fallback: direct call to `yiacad_native_ops.py` if service is unreachable
### 3.5 Implementation path
| Step | Surface | Effort | Requires compiled fork |
|---|---|---|---|
| 1 | FreeCAD Python workbench | Done | No |
| 2 | KiCad Python plugin (via action plugin API) | Done | No |
| 3 | KiCad compiled toolbar groups (`toolbars_*_editor.cpp`) | Done (T-UX-003A) | Yes (safe files) |
| 4 | FreeCAD compiled shell dock (`MainWindow.cpp`) | Done (T-UX-003D) | Yes (safe file) |
| 5 | KiCad compiled AssistantPane | Next lot | Yes (pcb/sch_edit_frame) |
| 6 | FreeCAD compiled global toolbar via WorkbenchManipulator | Next lot | Yes |
| 7 | KiCad WEBVIEW_PANEL for rich review UI | Future | Yes |
---
*Generated 2026-03-25 for Plan 20 T-UX-007*
@@ -1,6 +1,6 @@
# 20) TODO - Refonte UI/UX YiACAD Apple-native
Last updated: 2026-03-21 CET
Last updated: 2026-03-25 CET
## P0
@@ -59,10 +59,10 @@ Last updated: 2026-03-21 CET
## P2
- [ ] `T-UX-007` Identifier puis intégrer les points dancrage compilés dans les forks `kicad-ki` et `freecad-ki`.
- [ ] `T-UX-007` Faire converger la toolbar sémantique native côté KiCad et FreeCAD.
- [ ] `T-UX-008` Étudier lexposition des actions YiACAD via `App Intents`/automatisation locale.
- [ ] `T-UX-008` Qualifier lusage dun modèle local/on-device pour assistance de review.
- [x] `T-UX-007` Identifier puis intégrer les points dancrage compilés dans les forks `kicad-ki` et `freecad-ki`.`docs/YIACAD_COMPILED_ANCHOR_POINTS.md`
- [x] `T-UX-007` Faire converger la toolbar sémantique native côté KiCad et FreeCAD. → section 3 de `docs/YIACAD_COMPILED_ANCHOR_POINTS.md`
- [x] `T-UX-008` Étudier lexposition des actions YiACAD via `App Intents`/automatisation locale.`docs/YIACAD_APP_INTENTS_STUDY.md` section 1
- [x] `T-UX-008` Qualifier lusage dun modèle local/on-device pour assistance de review.`docs/YIACAD_APP_INTENTS_STUDY.md` section 2
## Delta 2026-03-20 - avancement T-UX-003
- [x] Exposer `YiACAD Status` dans `KiCad Manager`
@@ -79,7 +79,7 @@ Last updated: 2026-03-21 CET
- [x] Remplacer `yiacad_ai_bridge.py` par `yiacad_native_ops.py` dans `eeschema`
- [x] Brancher `YiACAD Status`, `YiACAD ERC/DRC`, `YiACAD BOM Review`, `YiACAD ECAD/MCAD Sync` dans les surfaces KiCad natives
- [x] Brancher les mêmes actions sur le workbench FreeCAD via appel direct au runner YiACAD
- [ ] Remplacer le runner Python local par un backend YiACAD pleinement intégré
- [x] Remplacer le runner Python local par un backend YiACAD pleinement intégré`tools/cad/yiacad_backend_service.py` + `tools/cad/yiacad_backend_client.py` (service-first with auto-start and direct fallback)
- [ ] Ouvrir `T-UX-004` pour palette de commandes, review center et inspector persistant multi-surface
- 2026-03-20 17:10 +0100 - lane parallele `yiacad-uiux-apple-native` activee. Explorateur `T-UX-003` assigne aux points d insertion natifs KiCad/FreeCAD; explorateur `T-UX-004` assigne a la command palette et a l inspector persistant.
@@ -205,3 +205,13 @@ Last updated: 2026-03-21 CET
- [x] Fermer `T-UX-003` comme lot parent apres verification des ancrages KiCad/FreeCAD deja livres
- [x] Fermer `T-UX-004` comme lot parent apres verification de la palette, du review center et de la persistance cote plugin/workbench
- [x] Reporter les approfondissements shell compiles sous `T-UX-007` au lieu de laisser `T-UX-003` / `T-UX-004` ouverts par inertie documentaire
## Delta 2026-03-25 - P2 clos
- [x] `T-UX-007` Compiled anchor points documented in `docs/YIACAD_COMPILED_ANCHOR_POINTS.md` (KiCad: 15 files mapped, FreeCAD: 11 files mapped, write-set status per file)
- [x] `T-UX-007` Unified semantic toolbar specification added (6 canonical actions, symmetric layout across pcbnew/eeschema/FreeCAD, transport contract via backend client)
- [x] `T-UX-008` App Intents study published in `docs/YIACAD_APP_INTENTS_STUDY.md` (Swift helper architecture, 4-5 day effort estimate)
- [x] `T-UX-008` On-device model qualification added (MLX Phi-3-mini for triage, Mistral-7B for explanation, CoreML nomic-embed for search, 5-7 day effort estimate)
- [x] Backend runner replacement confirmed done: `yiacad_backend_service.py` (HTTP service on :38435) + `yiacad_backend_client.py` (service-first with auto-start and direct fallback) already in place since 2026-03-21
- [x] Subsystem summary (ai-agentic-embedded-base, zeroclaw, openclaw) confirmed done via Plan 21 in `docs/CANONICAL_SUBSYSTEM_VIEW.md`
- All P2 tasks now closed. Plan 20 is fully resolved.
@@ -34,51 +34,51 @@
## P0 — Fichiers & Datasets
- [x] T-MS-001: Créer `mistral_studio_tui.sh` cockpit (Agents, Files, Fine-tune, Batches, OCR, Audio, Codestral)
- [ ] T-MS-002: Préparer dataset KiCad JSONL (format ChatML, >5k exemples)
- [ ] T-MS-002: Préparer dataset KiCad JSONL (format ChatML, >5k exemples) — [blocked: Mistral key expired]
- [ ] Merger build_kicad_dataset.py outputs
- [ ] Valider format avec `training/scripts/validate_dataset.py`
- [ ] Upload via `mistral_studio_tui.sh --files-upload`
- [ ] T-MS-003: Préparer dataset SPICE+Embedded JSONL
- [ ] T-MS-003: Préparer dataset SPICE+Embedded JSONL — [blocked: Mistral key expired]
- [ ] Merger build_spice_dataset.py + build_embedded_dataset.py + build_stm32_dataset.py
- [ ] Valider et upload
- [ ] T-MS-004: Upload docs commerciales pour Tower Document Library
- [ ] T-MS-004: Upload docs commerciales pour Tower Document Library — [blocked: Mistral key expired]
- [ ] Exporter docs Outline (formations, produits)
- [ ] Upload via Files API
- [ ] T-MS-005: Upload 5 datasheets composants test pour IA Documentaire
- [ ] T-MS-005: Upload 5 datasheets composants test pour IA Documentaire — [blocked: Mistral key expired]
- [ ] Sélectionner datasheets PDF (STM32, ESP32, composants courants)
- [ ] Tester OCR via `mistral_studio_tui.sh --ocr`
## P1 — Fine-tune
- [ ] T-MS-010: Lancer fine-tune KiCad sur `open-mistral-7b`
- [ ] T-MS-010: Lancer fine-tune KiCad sur `open-mistral-7b` — [blocked: Mistral key expired] + depends T-MS-002
- [ ] Configurer hyperparamètres (100 steps, lr=1e-5)
- [ ] Monitorer via `mistral_studio_tui.sh --finetune-list`
- [ ] Valider modèle `ft:kicad-v1`
- [ ] T-MS-011: Lancer fine-tune SPICE+Embedded sur `codestral-latest`
- [ ] T-MS-011: Lancer fine-tune SPICE+Embedded sur `codestral-latest` — [blocked: Mistral key expired] + depends T-MS-003
- [ ] Upload dataset fusionné
- [ ] Configurer et lancer job
- [ ] Valider modèle `ft:spice-embedded-v1`
- [ ] T-MS-012: Batch benchmark 100 prompts métier
- [ ] T-MS-012: Batch benchmark 100 prompts métier — [blocked: Mistral key expired] + depends T-MS-010/011
- [ ] Créer fichier JSONL 100 prompts (20 KiCad, 20 SPICE, 20 embedded, 20 IoT, 20 mixed)
- [ ] Exécuter batch sur modèle base
- [ ] Exécuter batch sur modèle fine-tuned
- [ ] Comparer résultats (scoring automatique + review)
- [ ] T-MS-013: Configurer Document Library RAG Tower
- [ ] T-MS-013: Configurer Document Library RAG Tower — [blocked: Mistral key expired] + depends T-MS-004
- [ ] Associer docs uploadés à agent Tower
- [ ] Tester queries de recherche
- [ ] Valider scoring leads avec contexte RAG
## P2 — Intégrations Studio
- [ ] T-MS-020: Pipeline OCR datasheets via IA Documentaire
- [ ] T-MS-020: Pipeline OCR datasheets via IA Documentaire — [blocked: Mistral key expired]
- [ ] Script batch OCR (traitement dossier complet)
- [ ] Extraction specs composants → JSON structuré
- [ ] Intégrer dans knowledge base Sentinelle
- [ ] T-MS-021: Audio STT dans workflow ops
- [ ] T-MS-021: Audio STT dans workflow ops — [blocked: Mistral key expired]
- [ ] Script transcription réunions (offline batch)
- [ ] Intégrer dans intelligence_tui.sh
- [ ] Action items extraction post-transcription
- [ ] T-MS-022: Installer Vibe CLI sur VM photon-docker
- [ ] T-MS-022: Installer Vibe CLI sur VM photon-docker — [blocked: Mistral key expired]
- [ ] `curl -LsSf https://mistral.ai/vibe/install.sh | bash`
- [ ] `vibe --setup` avec clé API
- [ ] Tester interactions Forge/Devstral
@@ -90,19 +90,19 @@
## P3 — Production
- [ ] T-MS-030: Déployer modèles fine-tuned dans Mascarade router
- [ ] T-MS-030: Déployer modèles fine-tuned dans Mascarade router — [blocked: Mistral key expired] + depends T-MS-010/011
- [ ] Ajouter `ft:kicad-v1` et `ft:spice-embedded-v1` comme providers
- [ ] Configurer routing par domaine
- [ ] T-MS-031: Tests E2E Studio→Mascarade→Agent
- [ ] T-MS-031: Tests E2E Studio→Mascarade→Agent — [blocked: Mistral key expired] + depends T-MS-030
- [ ] Scénario 1: Upload datasheet → OCR → Sentinelle analyse
- [ ] Scénario 2: Prompt KiCad → Router → ft:kicad-v1 → réponse
- [ ] Scénario 3: Audio meeting → STT → action items → Tower email
- [ ] T-MS-032: Documentation Outline wiki
- [ ] Page: Mistral Studio Overview
- [ ] Page: Fine-tune Pipeline Guide
- [ ] Page: IA Documentaire Usage
- [ ] Page: Audio Integration
- [ ] T-MS-033: Cron audit qualité modèles (weekly via Sentinelle)
- [ ] T-MS-032: Documentation Outline wiki**actionable offline (partial)**
- [ ] Page: Mistral Studio Overview — can draft from existing analysis docs
- [ ] Page: Fine-tune Pipeline Guide — can draft from pipeline docstrings
- [ ] Page: IA Documentaire Usage — skeleton only [blocked: needs OCR test results]
- [ ] Page: Audio Integration — skeleton only [blocked: needs STT test results]
- [ ] T-MS-033: Cron audit qualité modèles (weekly via Sentinelle) — [blocked: Mistral key expired] + depends T-MS-030
- [ ] 10 prompts test par modèle
- [ ] Scoring automatique
- [ ] Alerte si dégradation >5%
@@ -237,3 +237,21 @@ Decision retenue:
Impact pour `T-MS-*`:
- ne pas introduire de seconde variable Mistral cote Mascarade sans consumer explicite
- conserver `MISTRAL_API_KEY` comme seule interface Mistral canonique dans `/Users/electron/Documents/Projets/mascarade`
## Session 5 — 2026-03-25 — Audit blockers, status report
Constat:
- Mistral API key expired — all P0/P1/P2/P3 tasks requiring API calls are blocked
- 2/34 tasks completed (T-MS-001, T-MS-023)
- T-MS-032 (Documentation Outline wiki) is partially actionable offline
Fait:
- All blocked tasks annotated with `[blocked: Mistral key expired]` in this TODO
- T-MS-032 marked as partially actionable (2 pages draftable offline, 2 skeleton only)
- Status report created: `docs/MISTRAL_STUDIO_STATUS_2026-03-25.md`
- Full inventory of tooling, conventions, and next actions when keys are renewed
- Priority execution order documented
Impact:
- No further progress possible on Plan 24 until Mistral API key is renewed
- When key is renewed, start with `dataset_audit_tui.sh` preflight then T-MS-002/003
+25 -10
View File
@@ -69,19 +69,19 @@ Hypnoled est le premier projet client réel utilisé comme **cas pilote end-to-e
- PDF : `Clients/Garnier/rapports/review_hardware_hypnoled_2026-03-22.pdf`
- Markdown : `Projets/Hypnoled/docs/reviews/forge_review_DALI_PCB_2026-03-22.md`
- [ ] T-HP-013: **BOM analysis** — Parser les BOMs CSV et proposer des alternatives composants (coût, disponibilité)
- [ ] T-HP-013: **BOM analysis** — Parser les BOMs CSV et proposer des alternatives composants (coût, disponibilité)**tooling ready** (`tools/industrial/bom_analyzer.py`), [blocked: BOM CSVs not in checkout]
## Phase 2 — Simulation et Firmware
- [ ] T-HP-020: **Forge SPICE review** — Analyser `hypnoled.asc` avec Codestral, proposer optimisations
- [ ] T-HP-021: **Forge firmware** — Générer skeleton firmware ESP32 pour contrôle DALI (I2C + UART)
- [ ] T-HP-022: **Benchmark Hypnoled** — 10 prompts Hypnoled-spécifiques dans le benchmark T-MA-021
- [ ] T-HP-020: **Forge SPICE review** — Analyser `hypnoled.asc` avec Codestral, proposer optimisations — [blocked: assets not in checkout] + [blocked: Mistral key expired]
- [ ] T-HP-021: **Forge firmware** — Générer skeleton firmware ESP32 pour contrôle DALI (I2C + UART) — [blocked: Mistral key expired]
- [ ] T-HP-022: **Benchmark Hypnoled** — 10 prompts Hypnoled-spécifiques dans le benchmark T-MA-021 — [blocked: Mistral key expired]
## Phase 3 — Fine-tune enrichissement
- [ ] T-HP-030: **Dataset KiCad** — Extraire les schémas Hypnoled en paires prompt/response pour enrichir le dataset fine-tune T-MA-016
- [ ] T-HP-031: **Dataset SPICE** — Extraire les simulations LTspice pour enrichir T-MA-017
- [ ] T-HP-032: **Évaluation post fine-tune** — Re-run les reviews Forge sur Hypnoled avec le modèle fine-tuné vs base → mesurer l'amélioration
- [ ] T-HP-030: **Dataset KiCad** — Extraire les schémas Hypnoled en paires prompt/response pour enrichir le dataset fine-tune T-MA-016 — [blocked: KiCad files not in checkout]
- [ ] T-HP-031: **Dataset SPICE** — Extraire les simulations LTspice pour enrichir T-MA-017 — [blocked: simulation files not in checkout]
- [ ] T-HP-032: **Évaluation post fine-tune** — Re-run les reviews Forge sur Hypnoled avec le modèle fine-tuné vs base → mesurer l'amélioration — [blocked: depends Plan 24 fine-tune]
---
@@ -137,6 +137,21 @@ Sortie attendue pour `T-HP-013`:
## Delta 2026-03-22 - PCB AI / BOM / fabrication
- [ ] T-HP-033: **Quilter canary route** — Executer un aller-retour `KiCad -> Quilter -> package fab` sur une carte Hypnoled et comparer le candidat au flux YiACAD local.
- [ ] T-HP-034: **PCB Designer AI fast-fab lane** — Evaluer une voie `schema -> layout -> export fabrication` sur un sous-ensemble Hypnoled, sans contourner le gate local `BOM/DRC/provenance`.
- [ ] T-HP-035: **kicad-happy playbook parity** — Rejouer un lot `review schema + BOM sourcing + JLCPCB prep` et comparer les sorties aux prompts `Forge` et a `review.bom`.
- [ ] T-HP-033: **Quilter canary route** — Executer un aller-retour `KiCad -> Quilter -> package fab` sur une carte Hypnoled et comparer le candidat au flux YiACAD local. — [blocked: KiCad files not in checkout]
- [ ] T-HP-034: **PCB Designer AI fast-fab lane** — Evaluer une voie `schema -> layout -> export fabrication` sur un sous-ensemble Hypnoled, sans contourner le gate local `BOM/DRC/provenance`. — [blocked: KiCad files not in checkout]
- [ ] T-HP-035: **kicad-happy playbook parity** — Rejouer un lot `review schema + BOM sourcing + JLCPCB prep` et comparer les sorties aux prompts `Forge` et a `review.bom`. — [blocked: KiCad files not in checkout]
---
## Journal 2026-03-25
- `tools/industrial/bom_analyzer.py` created — generic BOM parser for T-HP-013
- 4 commands: parse, suggest, report, batch
- Column normalization covers KiCad, Altium, Eagle, generic CSV
- Deduplication by value+footprint+MPN fingerprint
- LCSC/JLCPCB alternative suggestion engine with static knowledge base
- JLCPCB assembly category classification (basic/extended/unavailable)
- Markdown report generation with assembly-ready status
- All blocked tasks annotated with explicit blocker tags
- Status report: `docs/HYPNOLED_STATUS_2026-03-25.md`
- Execution order documented for when assets become available
@@ -0,0 +1,62 @@
name: Kill_LIFE CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
erc-drc:
name: KiCad ERC/DRC
runs-on: ubuntu-latest
container:
image: ghcr.io/inti-cmnb/kicad8_auto:latest
steps:
- uses: actions/checkout@v4
- name: Run ERC
run: |
for sch in $(find hardware/pcb -name '*.kicad_sch' -not -path '*/libs/*'); do
echo "--- ERC: $sch ---"
kicad-cli sch erc --exit-code-violations "$sch" || exit 1
done
- name: Run DRC
run: |
for pcb in $(find hardware/pcb -name '*.kicad_pcb'); do
echo "--- DRC: $pcb ---"
kicad-cli pcb drc --exit-code-violations "$pcb" || exit 1
done
bom-check:
name: BOM Validation
runs-on: ubuntu-latest
needs: erc-drc
steps:
- uses: actions/checkout@v4
- name: Validate BOM exists
run: |
if [ ! -f hardware/bom/bom.csv ]; then
echo "WARNING: No BOM found at hardware/bom/bom.csv"
else
echo "BOM found, $(wc -l < hardware/bom/bom.csv) lines"
fi
firmware-build:
name: Firmware Build
runs-on: ubuntu-latest
if: hashFiles('firmware/platformio.ini') != ''
steps:
- uses: actions/checkout@v4
- name: Setup PlatformIO
uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install platformio
- name: Build firmware
run: cd firmware && pio run
@@ -0,0 +1,20 @@
kill_life:
version: 1
project: "{{PROJECT_NAME}}"
client: "{{CLIENT_NAME}}"
mascarade_url: "https://cody.saillant.cc"
agents:
- forge
- firmware-agent
- qa-agent
mcp_servers:
- kicad
- freecad
- apify
hardware:
pcb_dir: hardware/pcb
simulation_dir: hardware/simulation
bom_dir: hardware/bom
firmware:
framework: platformio
src_dir: firmware/src
+67
View File
@@ -0,0 +1,67 @@
# Kill_LIFE Project Makefile
# Common targets for hardware projects managed by Kill_LIFE agents
PROJECT_NAME ?= $(shell basename $(CURDIR))
KILL_LIFE_YAML := .kill-life.yaml
PCB_DIR := hardware/pcb
BOM_DIR := hardware/bom
SIM_DIR := hardware/simulation
FAB_DIR := fabrication
FW_DIR := firmware
.PHONY: help review bom-check bom-export fabrication-prep firmware-build firmware-flash erc drc clean
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \
awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
# --- Hardware ---
erc: ## Run KiCad ERC on all schematics
@for sch in $$(find $(PCB_DIR) -name '*.kicad_sch' -not -path '*/libs/*'); do \
echo "ERC: $$sch"; \
kicad-cli sch erc --exit-code-violations "$$sch"; \
done
drc: ## Run KiCad DRC on all PCBs
@for pcb in $$(find $(PCB_DIR) -name '*.kicad_pcb'); do \
echo "DRC: $$pcb"; \
kicad-cli pcb drc --exit-code-violations "$$pcb"; \
done
review: erc drc ## Run full Forge design review (ERC + DRC + agent analysis)
@echo "Design checks passed. Trigger forge agent for full review."
bom-export: ## Export BOM from KiCad schematic
@for sch in $$(find $(PCB_DIR) -name '*.kicad_sch' -not -path '*/libs/*'); do \
echo "Exporting BOM from $$sch"; \
kicad-cli sch export bom --output $(BOM_DIR)/bom.csv "$$sch"; \
done
bom-check: ## Validate BOM completeness
@test -f $(BOM_DIR)/bom.csv || { echo "No BOM found. Run 'make bom-export' first."; exit 1; }
@echo "BOM: $$(wc -l < $(BOM_DIR)/bom.csv) lines"
@echo "BOM validation passed."
fabrication-prep: ## Generate Gerbers and JLCPCB files
@mkdir -p $(FAB_DIR)/gerbers $(FAB_DIR)/jlcpcb
@for pcb in $$(find $(PCB_DIR) -name '*.kicad_pcb'); do \
echo "Generating Gerbers from $$pcb"; \
kicad-cli pcb export gerbers --output $(FAB_DIR)/gerbers/ "$$pcb"; \
kicad-cli pcb export drill --output $(FAB_DIR)/gerbers/ "$$pcb"; \
done
@echo "Fabrication files ready in $(FAB_DIR)/"
# --- Firmware ---
firmware-build: ## Build firmware with PlatformIO
cd $(FW_DIR) && pio run
firmware-flash: ## Flash firmware to connected board
cd $(FW_DIR) && pio run --target upload
# --- Utility ---
clean: ## Remove build artifacts
rm -rf $(FAB_DIR)/gerbers $(FAB_DIR)/jlcpcb
cd $(FW_DIR) && pio run --target clean 2>/dev/null || true
+36
View File
@@ -0,0 +1,36 @@
# {{PROJECT_NAME}}
> Managed by [Kill_LIFE](https://github.com/electron-rare/Kill_LIFE) agents.
## Structure
```
hardware/
pcb/ KiCad schematics and PCB layouts
simulation/ SPICE simulation files
bom/ Bill of Materials (CSV/JSON)
firmware/
src/ PlatformIO firmware source
docs/
reviews/ Auto-generated design reviews
specs/ Project specifications
client/ Client-facing documentation
fabrication/ Gerber and manufacturer files
```
## Quick Start
```bash
make help # list available targets
make erc # run electrical rules check
make drc # run design rules check
make review # full design review
make bom-export # export BOM from KiCad
make fabrication-prep # generate Gerbers
make firmware-build # build firmware
```
## Kill_LIFE Integration
This project is connected to the Kill_LIFE orchestrator via `.kill-life.yaml`.
Agents (`forge`, `firmware-agent`, `qa-agent`) operate on this repo through MCP servers.
@@ -0,0 +1,5 @@
# Client Documentation
Client-facing documents: user manuals, assembly guides, datasheets summaries.
These are generated or curated by the `forge` agent and reviewed before delivery.
@@ -0,0 +1,8 @@
# Design Reviews
Auto-generated Forge review reports are stored here.
Each review is a timestamped markdown file produced by the `forge` agent after
analysing schematics, PCB layout, BOM, and simulation results.
Files follow the naming convention: `review_YYYY-MM-DD.md`
@@ -0,0 +1,5 @@
# Project Specifications
Place functional specs, electrical specs, and requirement documents here.
Kill_LIFE agents reference these specs when running design reviews and validation.
@@ -0,0 +1,9 @@
# Fabrication Outputs
Gerber files, drill files, and JLCPCB/PCBWay order-ready archives.
## Conventions
- `gerbers/` — exported Gerber + drill files
- `jlcpcb/` — JLCPCB-specific BOM and CPL (pick-and-place) files
- The `qa-agent` runs `fabrication-prep` to generate and validate these outputs
@@ -0,0 +1,20 @@
; PlatformIO Project Configuration
; https://docs.platformio.org/page/projectconf.html
;
; Uncomment and adapt the environment for your target board.
[platformio]
default_envs = esp32dev
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
; lib_deps =
; [env:nucleo_f446re]
; platform = ststm32
; board = nucleo_f446re
; framework = stm32cube
; monitor_speed = 115200
@@ -0,0 +1,9 @@
# Firmware Source
ESP32 / STM32 firmware source code managed via PlatformIO.
## Conventions
- `main.cpp` or `main.c` as entry point
- Shared libraries in `lib/` (PlatformIO convention)
- The `firmware-agent` can scaffold, build, and flash firmware from this directory
@@ -0,0 +1,9 @@
# BOM — Bill of Materials
Normalized BOMs live here in CSV or JSON format.
## Conventions
- `bom.csv` — primary BOM exported from KiCad or generated by the `qa-agent`
- `bom_costed.csv` — BOM with supplier pricing (populated by the apify MCP scraper)
- The `qa-agent` cross-checks BOM entries against the schematic and flags mismatches
@@ -0,0 +1,14 @@
# PCB — KiCad Projects
Place KiCad project directories here (`.kicad_pro`, `.kicad_sch`, `.kicad_pcb`).
## Conventions
- One subdirectory per board variant (e.g. `main_board/`, `power_board/`)
- Keep library symbols/footprints in a `libs/` subdirectory if project-specific
- The Kill_LIFE `kicad` MCP server runs ERC/DRC checks against files in this directory
## Agent Integration
The `forge` agent generates design reviews from schematics found here.
The `qa-agent` runs ERC/DRC via the kicad MCP and reports results to `docs/reviews/`.
@@ -0,0 +1,9 @@
# Simulation — SPICE
Place LTspice (`.asc`) or ngspice (`.cir`) simulation files here.
## Conventions
- Name simulations after the circuit block they validate (e.g. `buck_converter.asc`)
- Store simulation results/plots in a `results/` subdirectory
- The Kill_LIFE agents can trigger batch simulations and analyze outputs
+519
View File
@@ -0,0 +1,519 @@
#!/usr/bin/env python3
"""
bom_analyzer.py — Generic BOM parser, normalizer, and alternative suggester.
Reads CSV BOMs (KiCad, Altium, generic), normalizes columns, deduplicates,
and suggests LCSC/JLCPCB alternatives with assembly category classification.
Usage:
python3 bom_analyzer.py parse input.csv [--output normalized.csv]
python3 bom_analyzer.py suggest input.csv [--output suggestions.csv]
python3 bom_analyzer.py report input.csv [--output report.md]
python3 bom_analyzer.py batch dir/ [--output report.md]
Part of Kill_LIFE tools/industrial — usable standalone for any project.
"""
import argparse
import csv
import hashlib
import json
import os
import re
import sys
from collections import defaultdict
from dataclasses import dataclass, field, asdict
from pathlib import Path
from typing import Optional
# ---------------------------------------------------------------------------
# Column name normalization map
# Covers KiCad, Altium, Eagle, generic EDA exports
# ---------------------------------------------------------------------------
COLUMN_ALIASES = {
# Reference designator
"reference": "reference",
"ref": "reference",
"designator": "reference",
"ref des": "reference",
"refdes": "reference",
# Value
"value": "value",
"val": "value",
"comment": "value",
# Footprint
"footprint": "footprint",
"package": "footprint",
"case": "footprint",
"case/package": "footprint",
"fp": "footprint",
# Quantity
"quantity": "quantity",
"qty": "quantity",
"count": "quantity",
"qte": "quantity",
# MPN (manufacturer part number)
"mpn": "mpn",
"manufacturer part number": "mpn",
"mfr part": "mpn",
"mfr. part": "mpn",
"part number": "mpn",
"manufacturer_part": "mpn",
"mfg part": "mpn",
# Manufacturer
"manufacturer": "manufacturer",
"mfr": "manufacturer",
"mfg": "manufacturer",
"mfr.": "manufacturer",
# Description
"description": "description",
"desc": "description",
"part description": "description",
# Supplier references
"lcsc": "lcsc",
"lcsc part": "lcsc",
"lcsc part#": "lcsc",
"lcsc#": "lcsc",
"jlcpcb": "lcsc", # JLCPCB uses LCSC catalog
"jlcpcb part#": "lcsc",
"digikey": "digikey",
"digikey part#": "digikey",
"mouser": "mouser",
"mouser part#": "mouser",
}
# JLCPCB assembly categories
JLCPCB_BASIC = "basic"
JLCPCB_EXTENDED = "extended"
JLCPCB_UNAVAILABLE = "unavailable"
# Common passives that are typically JLCPCB basic parts
BASIC_PATTERNS = [
(r"^[0-9.]+[munpf]?[FOHhf]$", "passive"), # 10K, 100nF, 4.7uH
(r"^[0-9.]+\s*[kKmMuUnNpP]?\s*[oOhHfF]", "passive"),
(r"LED", "led"),
(r"^GND$|^VCC$|^VDD$|^PWR$", "power_symbol"),
]
# Known LCSC part number patterns
LCSC_PATTERN = re.compile(r"C\d{4,8}")
@dataclass
class BomLine:
"""A single normalized BOM line (may represent multiple references)."""
reference: str = ""
value: str = ""
footprint: str = ""
quantity: int = 1
mpn: str = ""
manufacturer: str = ""
description: str = ""
lcsc: str = ""
digikey: str = ""
mouser: str = ""
# Computed fields
assembly_category: str = ""
suggestion: str = ""
notes: str = ""
def fingerprint(self) -> str:
"""Dedup key: value + footprint + mpn."""
raw = f"{self.value.strip().lower()}|{self.footprint.strip().lower()}|{self.mpn.strip().lower()}"
return hashlib.md5(raw.encode()).hexdigest()[:12]
# ---------------------------------------------------------------------------
# Parser
# ---------------------------------------------------------------------------
def detect_delimiter(filepath: str) -> str:
"""Sniff CSV delimiter."""
with open(filepath, "r", encoding="utf-8-sig") as f:
sample = f.read(4096)
for delim in [",", ";", "\t"]:
if delim in sample:
# crude heuristic: pick the one with most occurrences in first line
pass
sniffer = csv.Sniffer()
try:
dialect = sniffer.sniff(sample, delimiters=",;\t|")
return dialect.delimiter
except csv.Error:
return ","
def normalize_column_name(raw: str) -> str:
"""Map raw column header to canonical name."""
cleaned = raw.strip().lower().replace("_", " ").replace("#", "")
return COLUMN_ALIASES.get(cleaned, cleaned)
def parse_bom(filepath: str) -> list[BomLine]:
"""Parse a CSV BOM file into normalized BomLine list."""
delimiter = detect_delimiter(filepath)
lines: list[BomLine] = []
with open(filepath, "r", encoding="utf-8-sig") as f:
reader = csv.DictReader(f, delimiter=delimiter)
if reader.fieldnames is None:
return lines
# Build column mapping
col_map: dict[str, str] = {}
for raw_col in reader.fieldnames:
canonical = normalize_column_name(raw_col)
col_map[raw_col] = canonical
for row in reader:
bl = BomLine()
for raw_col, canonical in col_map.items():
val = (row.get(raw_col) or "").strip()
if hasattr(bl, canonical):
if canonical == "quantity":
try:
bl.quantity = int(val) if val else 1
except ValueError:
# Reference list as quantity (KiCad style)
bl.quantity = len(val.split(",")) if val else 1
else:
setattr(bl, canonical, val)
# If quantity is still 1 but reference has commas, count refs
if bl.reference and "," in bl.reference:
ref_count = len([r for r in bl.reference.split(",") if r.strip()])
if ref_count > bl.quantity:
bl.quantity = ref_count
# Skip empty lines, power symbols, mounting holes
if not bl.value and not bl.mpn:
continue
if bl.value.upper() in ("GND", "VCC", "VDD", "PWR", "+3V3", "+5V", "+12V"):
continue
if "mounting" in bl.footprint.lower() or "fiducial" in bl.footprint.lower():
continue
lines.append(bl)
return lines
def deduplicate(lines: list[BomLine]) -> list[BomLine]:
"""Merge lines with identical value+footprint+mpn, sum quantities."""
groups: dict[str, BomLine] = {}
for bl in lines:
fp = bl.fingerprint()
if fp in groups:
existing = groups[fp]
# Merge references
refs = set(existing.reference.split(",")) | set(bl.reference.split(","))
existing.reference = ",".join(sorted(r.strip() for r in refs if r.strip()))
existing.quantity += bl.quantity
# Prefer non-empty fields
if not existing.mpn and bl.mpn:
existing.mpn = bl.mpn
if not existing.manufacturer and bl.manufacturer:
existing.manufacturer = bl.manufacturer
if not existing.lcsc and bl.lcsc:
existing.lcsc = bl.lcsc
if not existing.description and bl.description:
existing.description = bl.description
else:
groups[fp] = bl
return list(groups.values())
# ---------------------------------------------------------------------------
# LCSC / JLCPCB suggestion engine
# ---------------------------------------------------------------------------
# Known component families -> typical LCSC part numbers
# This is a static knowledge base. A real implementation would query the LCSC API.
LCSC_KNOWLEDGE_BASE = {
# Resistors (0402, 0603, 0805)
"10k_0402": {"lcsc": "C25744", "category": JLCPCB_BASIC, "price_1k": 0.002},
"10k_0603": {"lcsc": "C25804", "category": JLCPCB_BASIC, "price_1k": 0.001},
"10k_0805": {"lcsc": "C17414", "category": JLCPCB_BASIC, "price_1k": 0.001},
"4.7k_0603": {"lcsc": "C25890", "category": JLCPCB_BASIC, "price_1k": 0.001},
"100r_0603": {"lcsc": "C22775", "category": JLCPCB_BASIC, "price_1k": 0.001},
"1k_0603": {"lcsc": "C21190", "category": JLCPCB_BASIC, "price_1k": 0.001},
# Capacitors
"100nf_0402": {"lcsc": "C1525", "category": JLCPCB_BASIC, "price_1k": 0.002},
"100nf_0603": {"lcsc": "C14663", "category": JLCPCB_BASIC, "price_1k": 0.001},
"100nf_0805": {"lcsc": "C49678", "category": JLCPCB_BASIC, "price_1k": 0.002},
"10uf_0805": {"lcsc": "C15850", "category": JLCPCB_BASIC, "price_1k": 0.004},
"1uf_0603": {"lcsc": "C15849", "category": JLCPCB_BASIC, "price_1k": 0.002},
"4.7uf_0805": {"lcsc": "C1779", "category": JLCPCB_BASIC, "price_1k": 0.003},
# Common ICs
"esp32-wroom-32": {"lcsc": "C701341", "category": JLCPCB_EXTENDED, "price_1k": 2.50},
"stm32f103c8t6": {"lcsc": "C8734", "category": JLCPCB_EXTENDED, "price_1k": 1.80},
# Discrete
"bss123": {"lcsc": "C82439", "category": JLCPCB_EXTENDED, "price_1k": 0.02},
"bc857": {"lcsc": "C8586", "category": JLCPCB_BASIC, "price_1k": 0.01},
"1n4148": {"lcsc": "C81598", "category": JLCPCB_BASIC, "price_1k": 0.005},
# Zener
"1sma4746": {"lcsc": "C191363", "category": JLCPCB_EXTENDED, "price_1k": 0.03},
# Opto
"el357n": {"lcsc": "C60693", "category": JLCPCB_EXTENDED, "price_1k": 0.05},
# Bridge rectifier
"mb10s": {"lcsc": "C80907", "category": JLCPCB_EXTENDED, "price_1k": 0.04},
}
def normalize_value_for_lookup(value: str, footprint: str) -> str:
"""Create a lookup key from value and footprint."""
val = value.strip().lower()
fp = footprint.strip().lower()
# Extract package size from footprint (0402, 0603, 0805, etc.)
pkg_match = re.search(r"(0201|0402|0603|0805|1206|1210|2512)", fp)
pkg = pkg_match.group(1) if pkg_match else ""
# Normalize value
val = val.replace(" ", "").replace("ohm", "r").replace("ohms", "r")
val = re.sub(r"(\d)r(\d)", r"\1.\2", val) # 4r7 -> 4.7
if pkg:
return f"{val}_{pkg}"
return val
def classify_assembly(bl: BomLine) -> str:
"""Classify a component for JLCPCB assembly."""
if bl.lcsc and LCSC_PATTERN.match(bl.lcsc):
return JLCPCB_EXTENDED # Has part, assume extended unless known basic
for pattern, _ in BASIC_PATTERNS:
if re.search(pattern, bl.value, re.IGNORECASE):
return JLCPCB_BASIC
if bl.mpn:
key = bl.mpn.strip().lower()
if key in LCSC_KNOWLEDGE_BASE:
return LCSC_KNOWLEDGE_BASE[key].get("category", JLCPCB_EXTENDED)
return JLCPCB_UNAVAILABLE
def suggest_alternatives(bl: BomLine) -> BomLine:
"""Suggest LCSC alternatives if not already specified."""
bl.assembly_category = classify_assembly(bl)
# Already has LCSC part
if bl.lcsc and LCSC_PATTERN.match(bl.lcsc):
bl.suggestion = f"LCSC {bl.lcsc} already specified"
return bl
# Try MPN lookup
if bl.mpn:
key = bl.mpn.strip().lower()
if key in LCSC_KNOWLEDGE_BASE:
info = LCSC_KNOWLEDGE_BASE[key]
bl.lcsc = info["lcsc"]
bl.assembly_category = info["category"]
bl.suggestion = f"Matched MPN -> LCSC {info['lcsc']} ({info['category']}, ~${info['price_1k']}/pc @1k)"
return bl
# Try value+footprint lookup
lookup = normalize_value_for_lookup(bl.value, bl.footprint)
if lookup in LCSC_KNOWLEDGE_BASE:
info = LCSC_KNOWLEDGE_BASE[lookup]
bl.lcsc = info["lcsc"]
bl.assembly_category = info["category"]
bl.suggestion = f"Matched value+pkg -> LCSC {info['lcsc']} ({info['category']}, ~${info['price_1k']}/pc @1k)"
return bl
# No match
bl.suggestion = "No automatic match — manual sourcing required"
bl.notes = f"Search https://www.lcsc.com/search?q={bl.mpn or bl.value} or https://jlcpcb.com/parts"
return bl
# ---------------------------------------------------------------------------
# Report generation
# ---------------------------------------------------------------------------
def generate_report(lines: list[BomLine], source_file: str) -> str:
"""Generate a Markdown report from analyzed BOM lines."""
total = len(lines)
total_qty = sum(bl.quantity for bl in lines)
with_lcsc = sum(1 for bl in lines if bl.lcsc and LCSC_PATTERN.match(bl.lcsc))
basic = sum(1 for bl in lines if bl.assembly_category == JLCPCB_BASIC)
extended = sum(1 for bl in lines if bl.assembly_category == JLCPCB_EXTENDED)
unavail = sum(1 for bl in lines if bl.assembly_category == JLCPCB_UNAVAILABLE)
coverage = (with_lcsc / total * 100) if total > 0 else 0
assembly_ready = unavail == 0
report = []
report.append(f"# BOM Analysis Report")
report.append(f"")
report.append(f"> Source: `{source_file}`")
report.append(f"> Generated: 2026-03-25")
report.append(f"")
report.append(f"## Summary")
report.append(f"")
report.append(f"| Metric | Value |")
report.append(f"|--------|-------|")
report.append(f"| Unique line items | {total} |")
report.append(f"| Total component count | {total_qty} |")
report.append(f"| LCSC coverage | {with_lcsc}/{total} ({coverage:.0f}%) |")
report.append(f"| JLCPCB Basic parts | {basic} |")
report.append(f"| JLCPCB Extended parts | {extended} |")
report.append(f"| Unavailable/manual | {unavail} |")
report.append(f"| Assembly status | {'READY' if assembly_ready else 'BLOCKED — manual sourcing needed'} |")
report.append(f"")
# Components table
report.append(f"## Component Details")
report.append(f"")
report.append(f"| Ref | Value | Footprint | Qty | MPN | LCSC | Category | Suggestion |")
report.append(f"|-----|-------|-----------|-----|-----|------|----------|------------|")
for bl in sorted(lines, key=lambda x: x.reference):
ref_short = bl.reference[:20] + "..." if len(bl.reference) > 20 else bl.reference
report.append(
f"| {ref_short} | {bl.value} | {bl.footprint} | {bl.quantity} "
f"| {bl.mpn} | {bl.lcsc} | {bl.assembly_category} | {bl.suggestion} |"
)
# Issues section
report.append(f"")
report.append(f"## Issues")
report.append(f"")
issues = [bl for bl in lines if bl.assembly_category == JLCPCB_UNAVAILABLE]
if issues:
report.append(f"The following {len(issues)} component(s) need manual LCSC sourcing:")
report.append(f"")
for bl in issues:
report.append(f"- **{bl.value}** ({bl.reference}) — {bl.notes or 'no suggestion'}")
else:
report.append(f"All components have LCSC part numbers. BOM is assembly-ready.")
report.append(f"")
return "\n".join(report)
def write_csv(lines: list[BomLine], output_path: str):
"""Write normalized BOM to CSV."""
fields = ["reference", "value", "footprint", "quantity", "mpn", "manufacturer",
"description", "lcsc", "digikey", "mouser", "assembly_category", "suggestion", "notes"]
with open(output_path, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fields)
writer.writeheader()
for bl in sorted(lines, key=lambda x: x.reference):
writer.writerow({k: getattr(bl, k) for k in fields})
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def cmd_parse(args):
"""Parse and normalize a BOM CSV."""
lines = parse_bom(args.input)
lines = deduplicate(lines)
print(f"Parsed {len(lines)} unique line items from {args.input}")
if args.output:
write_csv(lines, args.output)
print(f"Written to {args.output}")
else:
for bl in lines:
print(f" {bl.reference:20s} {bl.value:15s} {bl.footprint:20s} x{bl.quantity}")
def cmd_suggest(args):
"""Parse BOM and suggest LCSC/JLCPCB alternatives."""
lines = parse_bom(args.input)
lines = deduplicate(lines)
for bl in lines:
suggest_alternatives(bl)
with_lcsc = sum(1 for bl in lines if bl.lcsc)
print(f"Analyzed {len(lines)} items: {with_lcsc} with LCSC, {len(lines) - with_lcsc} need manual sourcing")
if args.output:
write_csv(lines, args.output)
print(f"Written to {args.output}")
else:
for bl in lines:
status = "OK" if bl.lcsc else "MISSING"
print(f" [{status:7s}] {bl.value:15s} {bl.lcsc or '???':10s} {bl.suggestion}")
def cmd_report(args):
"""Generate full Markdown report."""
lines = parse_bom(args.input)
lines = deduplicate(lines)
for bl in lines:
suggest_alternatives(bl)
report = generate_report(lines, args.input)
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(report)
print(f"Report written to {args.output}")
else:
print(report)
def cmd_batch(args):
"""Process all CSV BOMs in a directory."""
input_dir = Path(args.input)
if not input_dir.is_dir():
print(f"Error: {args.input} is not a directory", file=sys.stderr)
sys.exit(1)
csv_files = sorted(input_dir.glob("**/*.csv"))
if not csv_files:
print(f"No CSV files found in {args.input}", file=sys.stderr)
sys.exit(1)
all_reports = []
for csv_file in csv_files:
print(f"Processing {csv_file}...")
lines = parse_bom(str(csv_file))
lines = deduplicate(lines)
for bl in lines:
suggest_alternatives(bl)
all_reports.append(generate_report(lines, str(csv_file)))
combined = "\n\n---\n\n".join(all_reports)
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(combined)
print(f"Combined report written to {args.output}")
else:
print(combined)
def main():
parser = argparse.ArgumentParser(
description="BOM Analyzer — parse, normalize, suggest LCSC/JLCPCB alternatives"
)
sub = parser.add_subparsers(dest="command", required=True)
p_parse = sub.add_parser("parse", help="Parse and normalize a BOM CSV")
p_parse.add_argument("input", help="Input CSV file")
p_parse.add_argument("--output", "-o", help="Output normalized CSV")
p_parse.set_defaults(func=cmd_parse)
p_suggest = sub.add_parser("suggest", help="Suggest LCSC/JLCPCB alternatives")
p_suggest.add_argument("input", help="Input CSV file")
p_suggest.add_argument("--output", "-o", help="Output CSV with suggestions")
p_suggest.set_defaults(func=cmd_suggest)
p_report = sub.add_parser("report", help="Generate Markdown analysis report")
p_report.add_argument("input", help="Input CSV file")
p_report.add_argument("--output", "-o", help="Output Markdown file")
p_report.set_defaults(func=cmd_report)
p_batch = sub.add_parser("batch", help="Process all BOMs in a directory")
p_batch.add_argument("input", help="Input directory containing CSV BOMs")
p_batch.add_argument("--output", "-o", help="Output combined Markdown report")
p_batch.set_defaults(func=cmd_batch)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()
+128
View File
@@ -0,0 +1,128 @@
#!/usr/bin/env bash
# project_init.sh — Initialize a client repo with the Kill_LIFE project template
#
# Usage:
# ./tools/project_init.sh <github_repo_url> <project_name> [client_name]
#
# Example:
# ./tools/project_init.sh git@github.com:electron-rare/hypnoled.git hypnoled "Hypnoled SAS"
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
KILL_LIFE_ROOT="$(dirname "$SCRIPT_DIR")"
TEMPLATE_DIR="$KILL_LIFE_ROOT/templates/kill-life-project"
WORK_DIR="${KILL_LIFE_WORK_DIR:-/tmp/kill-life-init}"
# --- Args ---
if [ $# -lt 2 ]; then
echo "Usage: $0 <github_repo_url> <project_name> [client_name]"
echo ""
echo " github_repo_url Git clone URL of the target repo"
echo " project_name Short name (used in .kill-life.yaml and directory)"
echo " client_name Optional client/org name (defaults to project_name)"
exit 1
fi
REPO_URL="$1"
PROJECT_NAME="$2"
CLIENT_NAME="${3:-$PROJECT_NAME}"
CLONE_DIR="$WORK_DIR/$PROJECT_NAME"
echo "=== Kill_LIFE Project Init ==="
echo "Repo: $REPO_URL"
echo "Project: $PROJECT_NAME"
echo "Client: $CLIENT_NAME"
echo "WorkDir: $CLONE_DIR"
echo ""
# --- Clone ---
if [ -d "$CLONE_DIR" ]; then
echo "Directory $CLONE_DIR already exists. Pulling latest..."
git -C "$CLONE_DIR" pull --ff-only
else
mkdir -p "$WORK_DIR"
echo "Cloning $REPO_URL..."
git clone "$REPO_URL" "$CLONE_DIR"
fi
# --- Apply template structure ---
echo "Applying Kill_LIFE template structure..."
DIRS=(
hardware/pcb
hardware/simulation
hardware/bom
firmware/src
docs/reviews
docs/specs
docs/client
fabrication
.github/workflows
)
for dir in "${DIRS[@]}"; do
mkdir -p "$CLONE_DIR/$dir"
done
# Copy template files (don't overwrite existing)
copy_if_missing() {
local src="$1"
local dst="$2"
if [ ! -f "$dst" ]; then
cp "$src" "$dst"
echo " + $dst"
else
echo " . $dst (already exists, skipped)"
fi
}
# README files for each directory
for dir in "${DIRS[@]}"; do
if [ -f "$TEMPLATE_DIR/$dir/README.md" ]; then
copy_if_missing "$TEMPLATE_DIR/$dir/README.md" "$CLONE_DIR/$dir/README.md"
fi
done
# Top-level files
copy_if_missing "$TEMPLATE_DIR/Makefile" "$CLONE_DIR/Makefile"
copy_if_missing "$TEMPLATE_DIR/firmware/platformio.ini" "$CLONE_DIR/firmware/platformio.ini"
copy_if_missing "$TEMPLATE_DIR/.github/workflows/kill-life-ci.yml" "$CLONE_DIR/.github/workflows/kill-life-ci.yml"
copy_if_missing "$TEMPLATE_DIR/fabrication/README.md" "$CLONE_DIR/fabrication/README.md"
# --- Generate .kill-life.yaml with substituted values ---
echo "Generating .kill-life.yaml..."
sed -e "s/{{PROJECT_NAME}}/$PROJECT_NAME/g" \
-e "s/{{CLIENT_NAME}}/$CLIENT_NAME/g" \
"$TEMPLATE_DIR/.kill-life.yaml" > "$CLONE_DIR/.kill-life.yaml"
echo " + .kill-life.yaml"
# --- Generate README if missing ---
if [ ! -f "$CLONE_DIR/README.md" ]; then
sed "s/{{PROJECT_NAME}}/$PROJECT_NAME/g" \
"$TEMPLATE_DIR/README.md" > "$CLONE_DIR/README.md"
echo " + README.md"
fi
# --- Commit ---
echo ""
echo "Committing changes..."
cd "$CLONE_DIR"
git add -A
if git diff --cached --quiet; then
echo "No changes to commit."
else
git commit -m "feat: apply Kill_LIFE project template
Adds standard directory structure for hardware, firmware, docs, and fabrication.
Configures CI workflow for ERC/DRC checks and firmware builds.
Links project to Kill_LIFE orchestrator via .kill-life.yaml."
echo ""
echo "Changes committed. Push with:"
echo " cd $CLONE_DIR && git push"
fi
echo ""
echo "=== Done ==="
echo "Project initialized at: $CLONE_DIR"