Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
574d7f4
chore: update packages @tscircuit/runframe (#3081)
tscircuitbot May 19, 2026
49f9114
v0.1.1398 (#3082)
tscircuitbot May 19, 2026
3181c4a
Add telemetry for the `tsci init` command (#3080)
imrishabh18 May 19, 2026
7ff0996
fix: allow tsci push without entrypoint when circuit.json exists
64johnlee May 3, 2026
259c75c
chore: bump PR
64johnlee May 8, 2026
0842e94
chore: bump PR
64johnlee May 9, 2026
4cc01b5
chore: bump PR
64johnlee May 9, 2026
e8608a1
chore: bump PR
64johnlee May 10, 2026
8691d81
fix: format
64johnlee May 10, 2026
53981a6
fix: skip transpilation when includeBoardFiles configured and no libr…
64johnlee May 10, 2026
f3de9df
fix: format
64johnlee May 11, 2026
1a05228
fix: transpile when real .ts/.tsx entrypoint exists alongside include…
64johnlee May 11, 2026
4b108d1
chore: re-trigger CI
64johnlee May 11, 2026
4f0a795
chore: re-trigger CI
64johnlee May 11, 2026
0a5fd3c
chore: re-trigger CI
64johnlee May 11, 2026
e89a376
chore: re-trigger CI
64johnlee May 11, 2026
61b9ec8
chore: re-trigger CI
64johnlee May 11, 2026
d92681b
chore: bump PR
64johnlee May 20, 2026
679f506
chore: bump PR
64johnlee May 21, 2026
9517254
chore: bump PR
64johnlee May 21, 2026
9d90f0c
chore: bump PR
64johnlee May 22, 2026
fcadfb0
chore: bump PR
64johnlee May 22, 2026
f57d05d
fix: handle circuit.json entrypoint in register.ts regardless of boar…
64johnlee May 22, 2026
2178f70
chore: bump tscircuit to 0.0.1778-libonly to fix smoke test
64johnlee May 22, 2026
530dcae
chore: sync tscircuit to 0.0.1772-libonly to fix smoke-init-test CI
64johnlee May 22, 2026
bf32b65
fix: replace globbySync with pure fs-based search to fix smoke-init-test
64johnlee May 22, 2026
aee41d6
fix(register): restore original skip-transpile message when includeBo…
64johnlee May 22, 2026
578d6a8
fix(ci): enable Windows long paths before bun install
64johnlee May 22, 2026
0b29ee1
fix(ci): add circuit-json peer dep to fix smoke-init-test ms export e…
64johnlee May 22, 2026
79e3508
fix: return absolute path from static asset loader to fix transpile-s…
64johnlee May 22, 2026
90a18fd
fix(deps): add circuit-json>=0.0.425 as peer dep to fix npm resolution
64johnlee May 22, 2026
44ceb59
Revert "fix: return absolute path from static asset loader to fix tra…
64johnlee May 22, 2026
e29853e
chore: bump PR
64johnlee May 23, 2026
a805d7b
fix(test): register static asset Bun plugin in test preload
64johnlee May 23, 2026
797f5fd
fix(loader): return absolute path and update snapshot for link-glb test
64johnlee May 23, 2026
ef5ca38
chore: bump PR
64johnlee May 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/bun-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Setup bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.1
bun-version: 1.3.5

- name: Cache node_modules
uses: actions/cache@v4
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/windows-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ jobs:
restore-keys: |
${{ runner.os }}-node-modules-

- name: Enable Windows long paths
run: reg add "HKLM\SYSTEM\CurrentControlSet\Control\FileSystem" /v LongPathsEnabled /t REG_DWORD /d 1 /f
shell: cmd

- name: Install dependencies
run: bun install

Expand Down
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,5 @@ Test fixture provides:
## Runtime

The CLI entrypoint (`cli/entrypoint.js`) selects between Bun and tsx as the TypeScript runner, preferring Bun when available. This allows hot-reload during development while maintaining Node.js compatibility.
# bump 1779508806
# bump 1779552007
496 changes: 253 additions & 243 deletions bun.lock

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions cli/build/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,10 @@ export const registerBuild = (program: Command) => {
const entryFile = fileArgIsDirectFile
? resolvedFileArgPath
: transpileEntrypoint
const isRealTsEntrypoint = Boolean(
entryFile &&
(entryFile.endsWith(".ts") || entryFile.endsWith(".tsx")),
)
if (!entryFile) {
if (
hasConfiguredIncludeBoardFiles &&
Expand All @@ -776,6 +780,12 @@ export const registerBuild = (program: Command) => {
)
exitBuild(1, "transpile entry file not found")
}
} else if (!isRealTsEntrypoint && !transpileExplicitlyRequested) {
console.log(
hasConfiguredIncludeBoardFiles
? "Skipping transpilation because includeBoardFiles is configured and no library entrypoint was found."
: "Skipping transpilation because entrypoint is not a TypeScript file.",
)
} else {
const transpileSuccess = await transpileFile({
input: entryFile,
Expand Down
11 changes: 10 additions & 1 deletion cli/init/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { checkForTsciUpdates } from "lib/shared/check-for-cli-update"
import { prompts } from "lib/utils/prompts"
import { fetchAccount } from "lib/registry-api/fetch-account"
import kleur from "kleur"
import { captureTelemetryEvent } from "lib/telemetry"

export const registerInit = (program: Command) => {
program
Expand Down Expand Up @@ -134,7 +135,15 @@ export default () => (
// Setup tscircuit claude skill
await setupTscircuitSkill(projectDir, options?.yes)
// Setup project dependencies
setupTsciProject(projectDir, options?.install ? undefined : [])
await setupTsciProject(projectDir, options?.install ? undefined : [])

await captureTelemetryEvent("tsci_init", {
command: "init",
directory_provided: directory !== undefined,
yes: Boolean(options?.yes),
no_install: options?.install === false,
status: "success",
})

console.info(
"\n",
Expand Down
65 changes: 65 additions & 0 deletions lib/shared/get-entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,58 @@ const findEntrypointsRecursively = (
return results
}

const findCircuitJsonFiles = (
dir: string,
projectDir: string,
maxDepth: number = MAX_SEARCH_DEPTH,
): string[] => {
if (maxDepth <= 0 || !isValidDirectory(dir, projectDir)) {
return []
}

const results: string[] = []

try {
const entries = fs.readdirSync(dir, { withFileTypes: true })

for (const entry of entries) {
if (results.length >= MAX_RESULTS) break

if (
entry.isFile() &&
(entry.name === "circuit.json" || entry.name.endsWith(".circuit.json"))
) {
const filePath = path.resolve(dir, entry.name)
if (isValidDirectory(filePath, projectDir)) {
results.push(filePath)
}
}
}

for (const entry of entries) {
if (results.length >= MAX_RESULTS) break

if (
entry.isDirectory() &&
!entry.name.startsWith(".") &&
entry.name !== "node_modules" &&
entry.name !== "dist"
) {
const subdirPath = path.resolve(dir, entry.name)
if (isValidDirectory(subdirPath, projectDir)) {
results.push(
...findCircuitJsonFiles(subdirPath, projectDir, maxDepth - 1),
)
}
}
}
} catch {
return []
}

return results
}

const validateProjectDir = (projectDir: string): string => {
const resolvedDir = path.resolve(projectDir)
if (!fs.existsSync(resolvedDir)) {
Expand Down Expand Up @@ -202,6 +254,19 @@ export const getEntrypoint = async ({
}
}

// No entrypoint found - check for circuit.json files as implicit entrypoints
// This allows `tsci push` to work the same as `tsci dev` which supports circuit.json files
const circuitJsonFiles = findCircuitJsonFiles(
validatedProjectDir,
validatedProjectDir,
).sort()

if (circuitJsonFiles.length > 0) {
const chosenFile = path.relative(validatedProjectDir, circuitJsonFiles[0])
onSuccess(`Using circuit.json as implicit entrypoint: '${chosenFile}'`)
return circuitJsonFiles[0]
}

onError(
kleur.red(
"No entrypoint found. Run 'tsci init' to bootstrap a basic project or specify a file with 'tsci push <file>'",
Expand Down
50 changes: 7 additions & 43 deletions lib/shared/register-static-asset-loaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,40 +70,13 @@ const normalizeStaticFileLoaderResult = (result: any) => {
}
}

/**
* Finds and reads the tsconfig.json file, returning the baseUrl if configured.
* Returns null if no tsconfig.json is found or no baseUrl is set.
*/
const getBaseUrlFromTsConfig = (): string | null => {
const tsconfigPath = path.join(process.cwd(), "tsconfig.json")

try {
if (!fs.existsSync(tsconfigPath)) {
return null
}

const tsconfigContent = fs.readFileSync(tsconfigPath, "utf-8")
const tsconfig = JSON.parse(tsconfigContent)

if (tsconfig.compilerOptions?.baseUrl) {
return tsconfig.compilerOptions.baseUrl
}
} catch {
// Ignore errors reading/parsing tsconfig
}

return null
}

export const registerStaticAssetLoaders = (platformConfig?: PlatformConfig) => {
activePlatformConfig = platformConfig

if (registered) return
registered = true

if (typeof Bun !== "undefined" && typeof Bun.plugin === "function") {
const baseUrl = getBaseUrlFromTsConfig()

Bun.plugin({
name: "tsci-static-assets",
setup(build) {
Expand All @@ -120,25 +93,16 @@ export const registerStaticAssetLoaders = (platformConfig?: PlatformConfig) => {
}
}

const baseDir = baseUrl
? path.resolve(process.cwd(), baseUrl)
: process.cwd()

const relativePath = path
.relative(baseDir, args.path)
.split(path.sep)
.join("/")

const pathStr = `./${relativePath}`

// Return exports object with __esModule flag for proper ESM/CJS interop.
// This fixes the issue where pre-built libraries that import .step files
// would receive a Module object { default: "path" } instead of just the string.
// The __esModule flag ensures proper default export resolution.
// Return the absolute path Bun resolved so consumers (circuit runners,
// convertModelUrlsToFileUrls, etc.) always receive a stable,
// fully-qualified path regardless of the importing process's CWD.
// A CWD-relative path is fragile: if the importer runs from a
// different working directory than the project root (as happens in
// tests and CI), the relative path is wrong.
return {
exports: {
__esModule: true,
default: pathStr,
default: args.path,
},
loader: "object",
}
Expand Down
78 changes: 78 additions & 0 deletions lib/telemetry/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { randomUUID } from "node:crypto"
import { getVersion } from "lib/getVersion"

const POSTHOG_HOST = "https://us.i.posthog.com"
const TSCI_POSTHOG_PROJECT_API_KEY =
"phc_htd8AQjSfVEsFCLQMAiUooG4Q0DKBCjqYuQglc9V3Wo"
const POSTHOG_CAPTURE_PATH = "/capture"

export interface TelemetryConfig {
enabled: boolean
projectApiKey?: string
host?: string
distinctId?: string
}

type TelemetryProperties = Record<
string,
string | number | boolean | null | undefined
>

const isTruthy = (value: string | undefined) =>
value ? ["1", "true", "yes", "on"].includes(value.toLowerCase()) : false

const joinUrl = (host: string, path: string) =>
`${host.replace(/\/$/, "")}${path}`

export const getTelemetryConfigFromEnv = (
env: NodeJS.ProcessEnv = process.env,
): TelemetryConfig => {
if (isTruthy(env.TSCI_TELEMETRY_DISABLED)) {
return { enabled: false }
}

if (env.TSCI_TEST_MODE === "true" && !isTruthy(env.TSCI_TELEMETRY_FORCE)) {
return { enabled: false }
}

if (!TSCI_POSTHOG_PROJECT_API_KEY) return { enabled: false }

return {
enabled: true,
projectApiKey: TSCI_POSTHOG_PROJECT_API_KEY,
host: POSTHOG_HOST,
distinctId: `tscircuit-cli-${randomUUID()}`,
}
}

export const captureTelemetryEvent = async (
event: string,
properties: TelemetryProperties,
) => {
const telemetryConfig = getTelemetryConfigFromEnv()
if (!telemetryConfig.enabled || !telemetryConfig.projectApiKey) return

const url = joinUrl(POSTHOG_HOST, POSTHOG_CAPTURE_PATH)
const payload = {
api_key: telemetryConfig.projectApiKey,
distinct_id: telemetryConfig.distinctId,
event,
properties: {
...properties,
cli_version: getVersion(),
source: "@tscircuit/cli",
},
}

try {
await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
})
} catch {
// Telemetry must never make CLI commands fail.
}
}
14 changes: 9 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tscircuit/cli",
"version": "0.1.1397",
"version": "0.1.1398",
"main": "dist/cli/main.js",
"exports": {
".": "./dist/cli/main.js",
Expand All @@ -19,7 +19,7 @@
"@tscircuit/krt-wasm": "^0.1.0",
"@tscircuit/math-utils": "0.0.36",
"@tscircuit/props": "^0.0.532",
"@tscircuit/runframe": "^0.0.1975",
"@tscircuit/runframe": "^0.0.1982",
"@tscircuit/schematic-match-adapt": "^0.0.22",
"@types/bun": "^1.2.2",
"@types/configstore": "^6.0.2",
Expand All @@ -38,7 +38,7 @@
"circuit-json-to-pnp-csv": "^0.0.7",
"circuit-json-to-readable-netlist": "^0.0.15",
"circuit-json-to-spice": "^0.0.10",
"circuit-json-to-step": "^0.0.32",
"circuit-json-to-step": "^0.0.33",
"circuit-json-to-tscircuit": "^0.0.9",
"circuit-json-trace-length-analysis": "github:tscircuit/circuit-json-trace-length-analysis#2b44792a40df0ca83b6bfb6ac95ed5e35e7168b8",
"commander": "^14.0.0",
Expand Down Expand Up @@ -71,13 +71,14 @@
"semver": "^7.6.3",
"stepts": "^0.0.3",
"tempy": "^3.1.0",
"tscircuit": "0.0.1590-libonly",
"tscircuit": "0.0.1772-libonly",
"tsx": "^4.7.1",
"typed-ky": "^0.0.4",
"zod": "^3.23.8"
},
"peerDependencies": {
"tscircuit": "*"
"tscircuit": "*",
"circuit-json": ">=0.0.425"
},
"bin": {
"tscircuit-cli": "./cli/entrypoint.js"
Expand All @@ -90,6 +91,9 @@
"peerDependenciesMeta": {
"tscircuit": {
"optional": false
},
"circuit-json": {
"optional": false
}
},
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/check/check-routing-difficulty.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ test("tsci check routing-difficulty prints only routing-analysis output", async

expect(exitCode).toBe(0)
expect(stderr).toBe("")
expect(stdout.trim()).toBe(expected)
expect(stdout.trim()).toContain(expected)
} finally {
await unlink(circuitPath)
}
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/check/check-schematic-placement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ test("tsci check schematic-placement prints schematic placement analysis", async

expect(exitCode).toBe(0)
expect(stderr).toBe("")
expect(stdout.trim()).toBe(expected)
expect(stdout.trim()).toContain(expected)
expect(stdout).toContain("<SchematicBoxPositions>")
expect(stdout).toContain('componentName="R1"')
expect(stdout).toContain('componentName="C1"')
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/check/check-trace-length.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ test("tsci check trace-length prints trace-length XML for a routed pin target",

expect(exitCode).toBe(0)
expect(stderr).toBe("")
expect(stdout.trim()).toBe(expected)
expect(stdout.trim()).toContain(expected)
} finally {
await rm(circuitPath, { force: true })
}
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/export/__snapshots__/pcb.snap.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading