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:
@@ -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
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
@@ -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*
|
||||
@@ -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 d’ancrage 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 l’exposition des actions YiACAD via `App Intents`/automatisation locale.
|
||||
- [ ] `T-UX-008` Qualifier l’usage d’un modèle local/on-device pour assistance de review.
|
||||
- [x] `T-UX-007` Identifier puis intégrer les points d’ancrage 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 l’exposition des actions YiACAD via `App Intents`/automatisation locale. → `docs/YIACAD_APP_INTENTS_STUDY.md` section 1
|
||||
- [x] `T-UX-008` Qualifier l’usage d’un 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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
Executable
+128
@@ -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"
|
||||
Reference in New Issue
Block a user