buildwithtrace 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # buildwithtrace
2
+
3
+ AI-powered PCB design CLI. Design, review, convert, and manufacture circuit boards from your terminal.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # Via npm (recommended)
9
+ npx buildwithtrace
10
+
11
+ # Or install globally
12
+ npm install -g buildwithtrace
13
+
14
+ # Or via pip
15
+ pip install buildwithtrace
16
+
17
+ # Or via standalone installer
18
+ curl -fsSL https://buildwithtrace.com/install.sh | sh
19
+ ```
20
+
21
+ ## Requirements
22
+
23
+ - **Node.js 18+** (for the npm wrapper)
24
+ - **Python 3.10+** (automatically detected and used to create a virtual environment)
25
+
26
+ ## Usage
27
+
28
+ ```bash
29
+ # Sign in
30
+ buildwithtrace auth login
31
+
32
+ # Ask the AI about your design (read-only)
33
+ buildwithtrace ask "What decoupling caps do I need for this STM32?"
34
+
35
+ # Generate a symbol or footprint
36
+ buildwithtrace generate symbol "STM32F401"
37
+
38
+ # Run electrical / design rule checks (local engine)
39
+ buildwithtrace erc ./my-project.kicad_sch
40
+ buildwithtrace drc ./my-project.kicad_pcb
41
+
42
+ # Convert an Altium schematic to KiCad
43
+ buildwithtrace convert schematic ./design.SchDoc
44
+
45
+ # Check the design against a manufacturer's capabilities
46
+ buildwithtrace dfm check ./my-project/ -m pcbway
47
+ ```
48
+
49
+ > The command is `buildwithtrace`. We don't ship a `trace` binary (it collides with the macOS system `/usr/bin/trace`); add `alias trace=buildwithtrace` yourself if you want a shorter name.
50
+
51
+ ## Commands
52
+
53
+ | Command | Description |
54
+ | ------------- | ------------------------------------------------ |
55
+ | `ask` | Ask the AI about a design (read-only) |
56
+ | `chat` | Interactive AI design session |
57
+ | `agent`/`plan`| Execute edits / multi-step plan mode |
58
+ | `generate` | Generate symbols and footprints |
59
+ | `erc`/`drc` | Local electrical / design rule checks |
60
+ | `gerbers`/`export` | Manufacturing + PDF/SVG/STEP export |
61
+ | `convert` | Altium ↔ KiCad ↔ Trace JSON conversion |
62
+ | `dfm`/`order` | DFM checks and manufacturing orders |
63
+ | `review` | AI-powered design review |
64
+ | `auth` | Authenticate with your Trace account |
65
+
66
+ ## How It Works
67
+
68
+ The npm package is a lightweight wrapper that:
69
+
70
+ 1. Detects Python 3.10+ on your system
71
+ 2. Creates an isolated virtual environment
72
+ 3. Downloads and installs the `buildwithtrace` Python wheel
73
+ 4. Proxies all commands to the Python CLI
74
+
75
+ If Python isn't available, it falls back to a system-installed `buildwithtrace` binary.
76
+
77
+ ## Documentation
78
+
79
+ Full documentation: [docs.buildwithtrace.com](https://docs.buildwithtrace.com)
80
+
81
+ ## License
82
+
83
+ See [LICENSE](https://github.com/buildwithtrace/cli/blob/main/LICENSE) for details.
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { spawnSync } = require("child_process");
5
+ const path = require("path");
6
+ const fs = require("fs");
7
+
8
+ const VENV_DIR = path.join(
9
+ __dirname,
10
+ "..",
11
+ "node_modules",
12
+ ".cache",
13
+ "buildwithtrace",
14
+ "venv"
15
+ );
16
+
17
+ function getVenvPython() {
18
+ if (process.platform === "win32") {
19
+ return path.join(VENV_DIR, "Scripts", "python.exe");
20
+ }
21
+ return path.join(VENV_DIR, "bin", "python3");
22
+ }
23
+
24
+ function run() {
25
+ const args = process.argv.slice(2);
26
+ const venvPython = getVenvPython();
27
+
28
+ if (fs.existsSync(venvPython)) {
29
+ const result = spawnSync(venvPython, ["-m", "buildwithtrace", ...args], {
30
+ stdio: "inherit",
31
+ env: { ...process.env },
32
+ });
33
+ process.exit(result.status ?? 1);
34
+ }
35
+
36
+ // Fallback: try system-installed buildwithtrace
37
+ const cmd = process.platform === "win32" ? "buildwithtrace.exe" : "buildwithtrace";
38
+ const result = spawnSync(cmd, args, {
39
+ stdio: "inherit",
40
+ env: { ...process.env },
41
+ });
42
+
43
+ if (result.error) {
44
+ console.error(
45
+ "[buildwithtrace] CLI not found. Run `npx buildwithtrace` to trigger installation,\n" +
46
+ "or install directly: pip install buildwithtrace"
47
+ );
48
+ process.exit(1);
49
+ }
50
+
51
+ process.exit(result.status ?? 1);
52
+ }
53
+
54
+ run();
package/install.js ADDED
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { execSync, execFileSync } = require("child_process");
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+ const { pipeline } = require("stream/promises");
8
+
9
+ // Derive the wheel name from package.json so a version bump (release.yml runs
10
+ // `npm version` before publishing) downloads the matching release asset instead of a
11
+ // stale, hardcoded one. `latest/download/<file>` resolves to the newest release's asset.
12
+ const PKG_VERSION = require("./package.json").version;
13
+ const WHEEL_NAME = `buildwithtrace-${PKG_VERSION}-py3-none-any.whl`;
14
+ const WHEEL_URL = `https://github.com/buildwithtrace/cli/releases/latest/download/${WHEEL_NAME}`;
15
+ const VENV_DIR = path.join(
16
+ __dirname,
17
+ "node_modules",
18
+ ".cache",
19
+ "buildwithtrace",
20
+ "venv"
21
+ );
22
+ const MAX_RETRIES = 3;
23
+ const MIN_PYTHON_VERSION = [3, 10];
24
+
25
+ function log(msg) {
26
+ console.log(`[buildwithtrace] ${msg}`);
27
+ }
28
+
29
+ function warn(msg) {
30
+ console.warn(`[buildwithtrace] ⚠ ${msg}`);
31
+ }
32
+
33
+ function findPython() {
34
+ const candidates =
35
+ process.platform === "win32"
36
+ ? ["python3", "python", "py -3"]
37
+ : ["python3", "python"];
38
+
39
+ for (const cmd of candidates) {
40
+ try {
41
+ const parts = cmd.split(" ");
42
+ const result = execFileSync(parts[0], [...parts.slice(1), "--version"], {
43
+ encoding: "utf-8",
44
+ stdio: ["pipe", "pipe", "pipe"],
45
+ });
46
+ const match = result.match(/Python (\d+)\.(\d+)\.(\d+)/);
47
+ if (match) {
48
+ const major = parseInt(match[1], 10);
49
+ const minor = parseInt(match[2], 10);
50
+ if (
51
+ major > MIN_PYTHON_VERSION[0] ||
52
+ (major === MIN_PYTHON_VERSION[0] && minor >= MIN_PYTHON_VERSION[1])
53
+ ) {
54
+ log(`Found Python ${match[1]}.${match[2]}.${match[3]} (${cmd})`);
55
+ return cmd;
56
+ }
57
+ }
58
+ } catch {
59
+ // Not found, try next
60
+ }
61
+ }
62
+ return null;
63
+ }
64
+
65
+ function getPythonBin() {
66
+ if (process.platform === "win32") {
67
+ return path.join(VENV_DIR, "Scripts", "python.exe");
68
+ }
69
+ return path.join(VENV_DIR, "bin", "python3");
70
+ }
71
+
72
+ function getPipBin() {
73
+ if (process.platform === "win32") {
74
+ return path.join(VENV_DIR, "Scripts", "pip.exe");
75
+ }
76
+ return path.join(VENV_DIR, "bin", "pip");
77
+ }
78
+
79
+ async function downloadWithRetry(url, destPath) {
80
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
81
+ try {
82
+ log(
83
+ `Downloading wheel (attempt ${attempt}/${MAX_RETRIES})...`
84
+ );
85
+
86
+ const response = await fetch(url, { redirect: "follow" });
87
+ if (!response.ok) {
88
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
89
+ }
90
+
91
+ const fileStream = fs.createWriteStream(destPath);
92
+ await pipeline(response.body, fileStream);
93
+
94
+ const stats = fs.statSync(destPath);
95
+ if (stats.size === 0) {
96
+ throw new Error("Downloaded file is empty");
97
+ }
98
+
99
+ log(`Downloaded ${(stats.size / 1024 / 1024).toFixed(1)} MB`);
100
+ return;
101
+ } catch (err) {
102
+ if (attempt < MAX_RETRIES) {
103
+ const delay = attempt * 2000;
104
+ warn(`Download failed: ${err.message}. Retrying in ${delay / 1000}s...`);
105
+ await new Promise((r) => setTimeout(r, delay));
106
+ } else {
107
+ throw new Error(
108
+ `Failed to download after ${MAX_RETRIES} attempts: ${err.message}`
109
+ );
110
+ }
111
+ }
112
+ }
113
+ }
114
+
115
+ async function main() {
116
+ log("Setting up buildwithtrace CLI...");
117
+
118
+ const python = findPython();
119
+ if (!python) {
120
+ console.error(`
121
+ ╔══════════════════════════════════════════════════════════════════╗
122
+ ║ buildwithtrace requires Python 3.10 or later ║
123
+ ╠══════════════════════════════════════════════════════════════════╣
124
+ ║ ║
125
+ ║ Python was not found on your system. Install options: ║
126
+ ║ ║
127
+ ║ 1. Install Python: https://www.python.org/downloads/ ║
128
+ ║ ║
129
+ ║ 2. Install directly via pip (if Python is available elsewhere): ║
130
+ ║ pip install buildwithtrace ║
131
+ ║ ║
132
+ ║ 3. Use the standalone installer: ║
133
+ ║ curl -fsSL https://buildwithtrace.com/install.sh | sh ║
134
+ ║ ║
135
+ ║ After installing Python, run: npx buildwithtrace ║
136
+ ╚══════════════════════════════════════════════════════════════════╝
137
+ `);
138
+ process.exit(0); // Exit 0 so npm install doesn't fail
139
+ }
140
+
141
+ // Create venv
142
+ log("Creating virtual environment...");
143
+ fs.mkdirSync(path.dirname(VENV_DIR), { recursive: true });
144
+
145
+ const pythonParts = python.split(" ");
146
+ try {
147
+ execFileSync(pythonParts[0], [...pythonParts.slice(1), "-m", "venv", VENV_DIR], {
148
+ stdio: "pipe",
149
+ });
150
+ } catch (err) {
151
+ warn(`Failed to create venv: ${err.message}`);
152
+ warn("The CLI will fall back to system-installed buildwithtrace.");
153
+ process.exit(0);
154
+ }
155
+
156
+ // Download wheel
157
+ const wheelDir = path.join(VENV_DIR, ".wheels");
158
+ fs.mkdirSync(wheelDir, { recursive: true });
159
+ const wheelPath = path.join(wheelDir, WHEEL_NAME);
160
+
161
+ try {
162
+ await downloadWithRetry(WHEEL_URL, wheelPath);
163
+ } catch (err) {
164
+ warn(`${err.message}`);
165
+ warn("The CLI will fall back to system-installed buildwithtrace.");
166
+ process.exit(0);
167
+ }
168
+
169
+ // Install wheel into venv
170
+ log("Installing buildwithtrace into virtual environment...");
171
+ const pip = getPipBin();
172
+ try {
173
+ execFileSync(pip, ["install", "--quiet", "--no-cache-dir", wheelPath], {
174
+ stdio: "pipe",
175
+ });
176
+ } catch (err) {
177
+ warn(`pip install failed: ${err.message}`);
178
+ warn("The CLI will fall back to system-installed buildwithtrace.");
179
+ process.exit(0);
180
+ }
181
+
182
+ log("Installation complete! Run `buildwithtrace --help` to get started.");
183
+ }
184
+
185
+ main().catch((err) => {
186
+ warn(`Unexpected error: ${err.message}`);
187
+ process.exit(0);
188
+ });
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "buildwithtrace",
3
+ "version": "0.1.1",
4
+ "description": "AI-powered PCB design CLI. Design, review, convert, and manufacture circuit boards from your terminal.",
5
+ "bin": {
6
+ "buildwithtrace": "./bin/buildwithtrace.js"
7
+ },
8
+ "keywords": [
9
+ "pcb",
10
+ "eda",
11
+ "kicad",
12
+ "altium",
13
+ "electronics",
14
+ "hardware",
15
+ "circuit"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/buildwithtrace/cli"
20
+ },
21
+ "homepage": "https://buildwithtrace.com",
22
+ "bugs": {
23
+ "url": "https://github.com/buildwithtrace/cli/issues"
24
+ },
25
+ "license": "SEE LICENSE IN LICENSE",
26
+ "engines": {
27
+ "node": ">=18.0.0"
28
+ },
29
+ "os": [
30
+ "darwin",
31
+ "linux",
32
+ "win32"
33
+ ],
34
+ "files": [
35
+ "bin/",
36
+ "install.js",
37
+ "README.md",
38
+ "LICENSE"
39
+ ],
40
+ "scripts": {
41
+ "postinstall": "node install.js"
42
+ }
43
+ }