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 +83 -0
- package/bin/buildwithtrace.js +54 -0
- package/install.js +188 -0
- package/package.json +43 -0
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
|
+
}
|