diff --git a/.docs/provider-architecture.md b/.docs/provider-architecture.md index 794b6aa5d5..ac29c5e72a 100644 --- a/.docs/provider-architecture.md +++ b/.docs/provider-architecture.md @@ -7,7 +7,7 @@ The web app communicates with the server via WebSocket using a simple JSON-RPC-s Push channels: `server.welcome`, `server.configUpdated`, `terminal.event`, `orchestration.domainEvent`. Payloads are schema-validated at the transport boundary (`wsTransport.ts`). Decode failures produce structured `WsDecodeDiagnostic` with `code`, `reason`, and path info. -Methods mirror the `NativeApi` interface defined in `@t3tools/contracts`: +Methods mirror the `NativeApi` interface defined in `@tero/contracts`: - `providers.startSession`, `providers.sendTurn`, `providers.interruptTurn` - `providers.respondToRequest`, `providers.stopSession` diff --git a/.docs/workspace-layout.md b/.docs/workspace-layout.md index be88f2b603..8ce85ab3ab 100644 --- a/.docs/workspace-layout.md +++ b/.docs/workspace-layout.md @@ -4,4 +4,4 @@ - `/apps/web`: React + Vite UI. Session control, conversation, and provider event rendering. Connects to the server via WebSocket. - `/apps/desktop`: Electron shell. Spawns a desktop-scoped `t3` backend process and loads the shared web app. - `/packages/contracts`: Shared effect/Schema schemas and TypeScript contracts for provider events, WebSocket protocol, and model/session types. -- `/packages/shared`: Shared runtime utilities consumed by both server and web. Uses explicit subpath exports (e.g. `@t3tools/shared/git`, `@t3tools/shared/DrainableWorker`) — no barrel index. +- `/packages/shared`: Shared runtime utilities consumed by both server and web. Uses explicit subpath exports (e.g. `@tero/shared/git`, `@tero/shared/DrainableWorker`) — no barrel index. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 504952e3aa..5f6da60282 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -251,7 +251,7 @@ jobs: run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}" - name: Build CLI package - run: bun run build --filter=@t3tools/web --filter=t3 + run: bun run build --filter=@tero/web --filter=t3 - name: Publish CLI package run: node apps/server/scripts/cli.ts publish --tag latest --app-version "${{ needs.preflight.outputs.version }}" --verbose diff --git a/.gitignore b/.gitignore index 471855de15..1ca0ca3172 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ packages/*/dist build/ .logs/ release/ -.t3 +.tero .idea/ apps/web/.playwright apps/web/playwright-report diff --git a/AGENTS.md b/AGENTS.md index cea5090cce..cc8db3161e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,7 +28,7 @@ Long term maintainability is a core priority. If you add new functionality, firs - `apps/server`: Node.js WebSocket server. Wraps Codex app-server (JSON-RPC over stdio), serves the React web app, and manages provider sessions. - `apps/web`: React/Vite UI. Owns session UX, conversation/event rendering, and client-side state. Connects to the server via WebSocket. - `packages/contracts`: Shared effect/Schema schemas and TypeScript contracts for provider events, WebSocket protocol, and model/session types. Keep this package schema-only — no runtime logic. -- `packages/shared`: Shared runtime utilities consumed by both server and web. Uses explicit subpath exports (e.g. `@t3tools/shared/git`) — no barrel index. +- `packages/shared`: Shared runtime utilities consumed by both server and web. Uses explicit subpath exports (e.g. `@tero/shared/git`) — no barrel index. ## Codex App Server (Important) diff --git a/CURRENT-TASK.md b/CURRENT-TASK.md new file mode 100644 index 0000000000..e755f2b446 --- /dev/null +++ b/CURRENT-TASK.md @@ -0,0 +1,448 @@ +# CURRENT TASK + +## User Summary + +- The last attempt to rename this project from `t3code` / `T3 Code` to `tero` appears to have contributed to breaking the installed T3 Code app that was being used to talk here. +- After that breakage, the user manually removed and reinstalled the local app/environment, which fixed the installed app: + - `/Applications/T3 Code (Alpha).app` + - `~/.t3` + - `~/Library/Application Support/t3code` + - `~/Library/Preferences/com.t3tools.t3code.plist` +- The goal now is to retry the rename carefully and keep a persistent log in this repository so the task can be resumed if context is lost again. +- A second breakage reportedly happened again very quickly, roughly within 2 minutes of starting work on this repo, before any substantial rename pass had even happened. +- On the second recovery attempt, the user did **not** delete the installed app bundle. They removed only local support/state: + - `~/.t3` + - `~/Library/Application Support/t3code` + - `~/Library/Preferences/com.t3tools.t3code.plist` + - They explicitly kept `/Applications/T3 Code (Alpha).app` +- After deleting only local state, the installed app worked again. This is strong evidence that the `.app` bundle itself was not the thing being damaged. + +## Current Objective + +- Keep this file updated with a concise running summary of: + - what the user asked for + - what was found in the repo + - what was changed + - any suspected cause of breakage + - what remains to do +- Rename scope clarification from user: + - not only `t3code` / `T3 Code` + - also project-specific `t3` references should be considered in-scope + - avoid blindly renaming unrelated third-party/library/framework strings that merely contain `t3` + +## Findings So Far + +- This repository is not in a clean state. `git status --short` already shows many modified files across `apps/desktop`, `apps/server`, `apps/web`, `packages/shared`, root docs, and scripts. +- There is currently no root `CURRENT-TASK.md` other than this file. +- The rename is only partially complete: + - some values already use `tero` + - some values still use `t3code` / `T3 Code` +- There are also still project-specific `t3` references in package scopes, app IDs, repo/org names, storage keys, and test fixture prefixes. +- Examples observed: + - root [`package.json`](/Users/raulrodrigues/workspace/personal/t3code-ero/package.json) has scripts filtered to `tero` + - [`apps/desktop/package.json`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/package.json) has `productName: "Tero (Alpha)"` + - docs like [`README.md`](/Users/raulrodrigues/workspace/personal/t3code-ero/README.md) and [`REMOTE.md`](/Users/raulrodrigues/workspace/personal/t3code-ero/REMOTE.md) still mention `T3 Code` + - multiple source files still contain `T3 Code`, `t3code`, or old config-path references such as `.t3code-keybindings.json` +- Additional runtime-sensitive leftovers identified during audit: + - [`apps/server/src/codexAppServerManager.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/codexAppServerManager.ts) still reports Codex client info as `t3code_desktop` / `T3 Code Desktop` + - [`apps/server/src/orchestration/Layers/ProviderCommandReactor.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts) still uses `t3code` as the worktree branch prefix + - [`apps/web/src/store.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/web/src/store.ts) intentionally keeps old `t3code:*` localStorage keys as legacy migration inputs while writing new `tero:*` keys + - [`apps/desktop/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/src/main.ts) already prefers `TERO_HOME` / `.tero` but still falls back to `T3CODE_HOME` for compatibility +- I have not yet verified the exact prior failure mode. The earlier mention of "effect" is not yet confirmed from the current workspace snapshot. +- The second recovery significantly narrows the problem: + - removing persisted local state restored the installed app + - keeping the `.app` bundle in place did not prevent recovery + - that strongly suggests corrupted or incompatible persisted state, not a permanently broken packaged binary +- Current startup/storage findings relevant to this theory: + - [`apps/server/src/os-jank.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/os-jank.ts) resolves the default base directory to `~/.tero` + - [`apps/server/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/main.ts) aliases old `T3CODE_*` env vars into `TERO_*` + - [`apps/server/src/config.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/config.ts) derives runtime paths such as `userdata/state.sqlite`, `keybindings.json`, logs, attachments, and worktrees from the chosen base dir + - [`apps/desktop/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/src/main.ts) sets `BASE_DIR` from `TERO_HOME`, then `T3CODE_HOME`, then `~/.tero` + - [`apps/desktop/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/src/main.ts) also overrides Electron `userData` selection but still reuses a legacy path if it already exists, specifically to preserve Chromium profile data +- There are still branding/identity mismatches in the current snapshot that may matter during startup: + - [`apps/web/src/branding.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/web/src/branding.ts) still says `T3 Code` + - [`apps/web/index.html`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/web/index.html) still says `T3 Code (Alpha)` + - several desktop/web strings and support paths still mention `T3 Code`, `t3code`, or `.t3code-*` + +## State Path / Restore Matrix + +| Surface | Packaged app behavior | Dev behavior | Legacy `t3code` / `t3` path | New `tero` path | Env fallback | Collision risk | +| ------------------------------ | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| Server base dir | Desktop child server gets `TERO_HOME` from desktop main | CLI/dev server resolves via `--home-dir` or env | `T3CODE_HOME` is still accepted and aliased in server startup | `TERO_HOME`, default `~/.tero` | `TERO_HOME ?? T3CODE_HOME ?? ~/.tero` | High if installed app and dev runs share `TERO_HOME` or legacy env | +| Server state dir | Packaged server uses `/userdata` | Dev server uses `/dev` when `VITE_DEV_SERVER_URL` is set | Old env can still point at old base dir | `/userdata` or `/dev` | derived from resolved base dir | Medium if base dir collides; lower between `userdata` and `dev` themselves | +| SQLite DB | Packaged startup reads `/userdata/state.sqlite` | Dev reads `/dev/state.sqlite` | Legacy data can survive if base dir still points at old location | `state.sqlite` under resolved state dir | same as above | High because schema decode/migration failures poison startup immediately | +| Desktop Chromium `userData` | Electron overrides to clean app-data dir but reuses legacy dir if present | Dev uses separate `tero-dev` target but still prefers legacy dev dir when present | `~/Library/Application Support/T3 Code (Alpha)` or `T3 Code (Dev)` may still be reused | `~/Library/Application Support/tero` or `tero-dev` | no env here; path selected by platform + existence check | High because packaged app can intentionally reattach to legacy Chromium profile data | +| Web renderer localStorage | Browser/Electron renderer loads persisted renderer state on startup | Same codepath in dev renderer | reads old `t3code:renderer-state:v*` keys only as migration inputs | writes `tero:renderer-state:v8` | none | Medium; stale UI state can survive via Chromium profile reuse | +| Desktop logs | Packaged app writes under `/userdata/logs` | Dev logs mostly go to terminal; packaged capture disabled | old base dir via env alias still possible | `/userdata/logs` | same base-dir fallback | Low for poisoning, high for evidence value | +| Provider metadata / Codex init | Server still identifies as `t3code_desktop` | same in dev | legacy client name remains | none yet | none | Low direct poisoning risk, medium for mixed identity/debug confusion | +| Worktree branch prefix | Packaged and dev server both mint `t3code/...` branches | same | `t3code/...` still hard-coded | no `tero/...` yet | none | Low for startup poisoning, medium for repo-state confusion | + +## Ranked Startup Poisoning Mechanisms + +1. Cross-version SQLite schema incompatibility between the installed app and this repo snapshot. + - Evidence: + - repo-launched desktop app ran migration `16_CanonicalizeModelSelections` against `~/.t3` + - installed app then failed with `ProjectionThreadRepository.getById:query ... no such column: model` + - Code references: + - [`apps/server/src/persistence/Migrations/016_CanonicalizeModelSelections.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/persistence/Migrations/016_CanonicalizeModelSelections.ts) + - [`apps/server/src/persistence/Layers/ProjectionThreads.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/persistence/Layers/ProjectionThreads.ts) + - Why highest risk: this is proven, immediate, and breaks the installed app even without a broad rename pass. +2. Persisted orchestration event rows that no longer decode under current schemas. + - Evidence: previous packaged log contained `OrchestrationEventStore.readFromSequence:rowToEvent: Missing key at ["payload"]["defaultModel"]`. + - Code references: + - [`apps/server/src/persistence/Layers/OrchestrationEventStore.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/persistence/Layers/OrchestrationEventStore.ts) + - [`apps/server/src/persistence/Migrations/016_CanonicalizeModelSelections.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/persistence/Migrations/016_CanonicalizeModelSelections.ts) + - Why high risk: startup replays persisted events before the app becomes usable. +3. Installed app reusing legacy Electron `userData`, which preserves localStorage/cookies/session state across rename attempts. + - Code references: + - [`apps/desktop/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/src/main.ts) + - [`apps/web/src/store.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/web/src/store.ts) + - Why high risk: deleting only `~/.t3` would not clear Chromium-side state if `~/Library/Application Support/T3 Code (Alpha)` remains. +4. Base-dir collisions caused by compatibility env aliases (`TERO_HOME` vs `T3CODE_HOME`) between installed and dev/server runs. + - Code references: + - [`apps/server/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/main.ts) + - [`apps/server/src/os-jank.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/os-jank.ts) + - [`apps/desktop/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/src/main.ts) + - Why high risk: one run can silently point another at the same persisted state root. +5. Partial rename drift between packaged identity, storage keys, and protocol/client identifiers. + - Code references: + - [`apps/server/src/codexAppServerManager.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/codexAppServerManager.ts) + - [`apps/server/src/orchestration/Layers/ProviderCommandReactor.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts) + - Why medium risk: likely not the primary poisoner, but it increases ambiguity and complicates recovery. + +## Investigation Log + +- 2026-03-27 batch 1 + - Scope: static forensic audit only. + - New findings: + - Packaged and dev server intentionally diverge only at `userdata` vs `dev` under the same resolved base dir. + - Packaged desktop intentionally reuses legacy Electron `userData` when the old app-support folder still exists. + - A migration already exists for the exact old `defaultModel` payload shape implicated by the earlier decode error. + - Small code changes prepared for better evidence collection: + - desktop main now logs chosen base dir and whether legacy Electron `userData` was reused + - server startup now warns if `TERO_*` and `T3CODE_*` env vars conflict + - Command results: + - `bun fmt`: passed + - `bun lint`: passed + - `bun typecheck`: failed outside this batch in [`packages/shared/src/model.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/packages/shared/src/model.ts) and [`packages/shared/src/model.test.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/packages/shared/src/model.test.ts) + - Failure shape: + - `Cannot find module '@tero/contracts'` + - several implicit `any` callback params + - one `Object is possibly 'undefined'` + - Installed-app outcome: + - User reported the installed app "broke" immediately after this batch. + - Follow-up log inspection did **not** show a backend startup failure. + - Packaged server evidence: + - migrations ran successfully + - orchestration engine started cleanly + - packaged app resumed a Codex-backed thread for `/Users/raulrodrigues/workspace/personal/t3code-ero` + - no SQL/decode crash was present in the latest startup logs + - App bundle status: kept in place. + - Recovery actions: not yet performed for this occurrence. + - Updated implication: + - this occurrence appears different from the earlier decode/crash-loop failure + - the installed app may be landing on the blank `/_chat/` route ("No active thread") even while backend/session restore succeeded + - current concrete UI-restore suspect is root bootstrap navigation only redirecting from `/`, not from `/_chat/` +- 2026-03-27 batch 2 + - Scope: narrow UI-restore mitigation based on latest installed-app evidence. + - New finding: + - [`apps/web/src/routes/__root.tsx`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/web/src/routes/__root.tsx) only auto-navigated into `payload.bootstrapThreadId` when `pathname === "/"`. + - The empty-state screen the user reported is rendered by [`apps/web/src/routes/_chat.index.tsx`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/web/src/routes/_chat.index.tsx), i.e. the `/_chat/` route. + - That means a successful backend/session restore can still strand the UI on "No active thread" if startup lands on `/_chat/` instead of `/`. + - Small code change: + - [`apps/web/src/routes/__root.tsx`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/web/src/routes/__root.tsx) now treats `/`, `/_chat`, and `/_chat/` as bootstrap-eligible paths for auto-navigation into the restored thread. + - Why this matters: + - It does not change persistence format. + - It directly targets the latest observed blank-screen mechanism without touching backend state. + - Command results: + - `bun fmt`: passed + - `bun lint`: passed + - `bun typecheck`: failed with the same pre-existing errors in [`packages/shared/src/model.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/packages/shared/src/model.ts) and [`packages/shared/src/model.test.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/packages/shared/src/model.test.ts) + - Installed-app outcome: + - User reported the installed app was still broken. + - User then cleared installed-app support/state again while keeping the app bundle. + - After relaunch, the app worked. + - Important clarification: + - the deleted support folders were expected to be recreated on successful launch + - their reappearance after recovery is normal and does not mean deletion failed + - Updated implication: + - clearing support/state remains a reliable recovery + - the blank-screen failure is consistent with poisoned restore state rather than permanent bundle damage + +- 2026-03-27 batch 3 + - Scope: welcome/bootstrap evidence logging. + - New finding: + - The renderer can only auto-enter a restored thread if `server.welcome` includes `bootstrapThreadId`. + - Current server tests cover IDs being present for auto-bootstrap paths, but runtime logs did not yet explicitly say whether welcome omitted them. + - Small code change: + - [`apps/server/src/wsServer.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/wsServer.ts) now logs `server.welcome prepared` with: + - `bootstrapSource` + - whether bootstrap project/thread IDs were present + - the IDs themselves when present + - Why this matters: + - On the next incident, we can distinguish: + - server offered a valid bootstrap thread and the UI still stranded itself + - server never offered bootstrap IDs at all + - Command results: + - `bun fmt`: passed + - `bun lint`: passed + - `bun typecheck`: failed with the same pre-existing errors in [`packages/shared/src/model.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/packages/shared/src/model.ts) and [`packages/shared/src/model.test.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/packages/shared/src/model.test.ts) + - Installed-app outcome: pending user test after this batch. + +- 2026-03-27 batch 4 + - Scope: fallback thread restore when `server.welcome` omits bootstrap ids. + - New finding: + - Packaged startup logs previously showed `autoBootstrapProjectFromCwd: false`. + - In that mode, the server may legitimately send `server.welcome` without `bootstrapProjectId` / `bootstrapThreadId`. + - Before this batch, the renderer did nothing in that case, even if the freshly synced snapshot already contained non-deleted threads. + - Small code change: + - [`apps/web/src/routes/__root.tsx`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/web/src/routes/__root.tsx) now falls back to the most recently updated non-deleted thread from the synced snapshot when: + - current route is `/`, `/_chat`, or `/_chat/` + - `server.welcome` does not provide bootstrap ids + - Why this matters: + - It directly targets the “backend/session exists but UI stays on No active thread” failure mode. + - It still does not change SQLite shape, migrations, or support-folder layout. + - Command results: + - `bun fmt`: passed + - `bun lint`: passed + - `bun typecheck`: failed with the same pre-existing errors in [`packages/shared/src/model.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/packages/shared/src/model.ts) and [`packages/shared/src/model.test.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/packages/shared/src/model.test.ts) + - Installed-app outcome: pending user test after this batch. + +- 2026-03-27 incident A + - Trigger: + - The repo desktop app was launched from [`apps/desktop/scripts/start-electron.mjs`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/scripts/start-electron.mjs) using `TERO_HOME=/tmp/tero-dev-probe`. + - Critical finding: + - Despite the isolated env var, the launched repo app actually came up on `baseDir: /Users/raulrodrigues/.t3`. + - The repo app therefore touched the **same** SQLite state as the installed `/Applications/T3 Code (Alpha).app`. + - Evidence: + - Repo-run server log showed: + - `baseDir: '/Users/raulrodrigues/.t3'` + - `Running all migrations...` + - `Migrations ran successfully { migrations: [ '16_CanonicalizeModelSelections' ] }` + - Shortly after that, the installed app failed with: + - `Error: SQL error in ProjectionThreadRepository.getById:query` + - underlying SQLite error: `no such column: model` + - Schema implication: + - Migration 16 in [`apps/server/src/persistence/Migrations/016_CanonicalizeModelSelections.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/persistence/Migrations/016_CanonicalizeModelSelections.ts) drops `projection_threads.model`. + - The installed app binary still appears to query that old column in its packaged `ProjectionThreadRepository.getById`. + - Therefore the installed app and this repo snapshot are on **incompatible DB schemas**. + - What this proves: + - Launching the repo app can poison the installed app **even without any broad rename pass**, simply by sharing `~/.t3`. + - The breakage is not hypothetical and not just UI state; it is a concrete cross-version SQLite schema incompatibility. + - Recovery: + - Keep the installed app bundle. + - Delete supporting state (`~/.t3`, `~/Library/Application Support/t3code`, preference plist). + - Relaunch the installed app. + - New top-priority mitigation: + - Never run the repo desktop app against `~/.t3`. + - Before any further repo desktop probing, guarantee isolated runtime state at both: + - server base dir / SQLite path + - Electron `userData` / Chromium profile path + +- 2026-03-27 incident B + - Trigger: + - Controlled local repo desktop launches via [`apps/desktop/package.json`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/package.json) `start`. + - New evidence: + - The Electron main process now correctly identifies local-dev mode and logs `baseDir: /Users/raulrodrigues/.tero-dev`. + - Despite that, process inspection showed the backend child still running as: + - `.../electron/dist/Electron.app/Contents/MacOS/Electron /Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/dist/index.mjs` + - This matches the user-visible symptom where a browser tab opens while the Electron app also runs. + - Updated implication: + - The remaining isolation failure is no longer the desktop main base-dir selection. + - The remaining failure is backend child process resolution: local-dev desktop runs must not fall back to Electron's own `process.execPath`, because that spawns another Electron-hosted server process. + - Small code change: + - [`apps/desktop/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/src/main.ts) now resolves the backend executable differently in local-dev mode: + - prefer explicit `TERO_DESKTOP_SERVER_EXECUTABLE` + - otherwise try `BUN`, `npm_node_execpath`, then `which bun`, then `which node` + - only fall back to `process.execPath` as a last resort + - The same file now logs the exact backend executable choice for local-dev probes. + - Why this matters: + - It directly targets the browser-tab symptom and the risk of a second unintended Electron-hosted server process. + +- 2026-03-27 batch 5 + - Scope: force backend child into desktop mode via CLI, not only env. + - New finding: + - After the previous local-dev executable fix, the spawned backend still logged: + - `mode: "web"` + - `baseDir: "/Users/raulrodrigues/.t3"` + - `noBrowser: false` + - That proves the backend child was not reliably honoring the intended desktop env contract. + - Small code change: + - [`apps/desktop/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/src/main.ts) now spawns the backend with explicit CLI args: + - `--mode desktop` + - `--no-browser` + - `--port ` + - `--home-dir ` + - `--auth-token ` + - Why this matters: + - Even if env propagation is partially broken under the local Electron/Bun launch path, these flags should still force the backend into the safe desktop state and prevent browser auto-open plus `~/.t3` reuse. + - Probe outcome: + - After rebuilding and relaunching, the repo desktop app logged: + - `mode: "desktop"` + - `baseDir: "/Users/raulrodrigues/.tero-dev"` + - `noBrowser: true` + - `autoBootstrapProjectFromCwd: false` + - No separate `web`-mode `~/.t3` server appeared in the observed process tree for this run. + - A repeat launch after clearing the installed app support folders again produced the same safe result: + - no browser tab opened + - backend still ran in desktop mode + - backend still stayed on `~/.tero-dev` + - Updated implication: + - The local repo desktop launch path now appears isolated from the installed app's `~/.t3` state. + - The next required probe is whether the installed `/Applications/T3 Code (Alpha).app` stays healthy after this isolated repo launch. + +- 2026-03-27 batch 5 + - Scope: default local desktop isolation. + - New finding: + - Local repo desktop runs were still defaulting to the same identity surfaces as the installed app. + - In particular, unpackaged local desktop runs could still fall back to the packaged defaults for: + - base dir (`~/.tero` / legacy `~/.t3`) + - Electron `userData` profile (`tero` / legacy `Tero (Alpha)`) + - Small code change: + - [`apps/desktop/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/src/main.ts) now separates packaged vs unpackaged defaults using `app.isPackaged`: + - packaged default base dir: `~/.tero` + - unpackaged default base dir: `~/.tero-dev` + - packaged userData dir: `tero` + - unpackaged userData dir: `tero-dev` + - display name also follows packaged vs unpackaged mode + - startup path logging now records: + - `packaged` + - `rendererDevServer` + - chosen base dir / state dir / userData path + - Why this matters: + - It is a direct mitigation against rerunning the repo desktop app into the installed app’s live state when env overrides are absent or ineffective. + - It narrows the next probe to one question: whether unpackaged repo runs still somehow land on `~/.t3` after this change. + - Command results: + - `bun fmt`: passed + - `bun lint`: passed + - `bun typecheck`: failed with the same pre-existing errors in [`packages/shared/src/model.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/packages/shared/src/model.ts) and [`packages/shared/src/model.test.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/packages/shared/src/model.test.ts) + - Installed-app outcome: pending user validation after state reset. + +- 2026-03-27 batch 6 + - Scope: unblock desktop runtime probing by removing broken workspace-package resolution from the desktop app. + - New finding: + - Rebuilt desktop runtime initially failed before startup with: + - `Cannot find module '@tero/shared/Net'` + - Root cause: this checkout does not currently have working workspace links for `@tero/shared` / `@tero/contracts` in `node_modules`, so the desktop runtime could not resolve those runtime imports. + - Small code change: + - [`apps/desktop/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/src/main.ts) now imports shared runtime modules via direct repo-relative paths: + - `../../../packages/shared/src/Net` + - `../../../packages/shared/src/logging` + - [`apps/desktop/src/syncShellEnvironment.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/src/syncShellEnvironment.ts) now imports: + - `../../../packages/shared/src/shell` + - Desktop bundle was rebuilt successfully after that change. + - Why this matters: + - It removes the immediate launch blocker so we can continue testing desktop state isolation. + - It does not by itself address the installed-app poisoning issue; it only restores the ability to probe the repo desktop runtime. + - Command results: + - desktop rebuild: passed + - `bun fmt`: passed + - `bun lint`: passed + - `bun typecheck`: failed with the same pre-existing errors in [`packages/shared/src/model.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/packages/shared/src/model.ts) and [`packages/shared/src/model.test.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/packages/shared/src/model.test.ts) + - Installed-app outcome: not re-tested yet after this batch. + +- 2026-03-27 batch 7 + - Scope: isolate non-desktop dev paths by default. + - New finding: + - Desktop local-dev probes are now isolated on `~/.tero-dev`, but the generic dev runner and direct server dev path still defaulted to the production-style home unless explicitly overridden. + - That meant `dev`, `dev:web`, and direct `VITE_DEV_SERVER_URL` server runs were still weaker from a state-isolation perspective. + - Small code changes: + - [`scripts/dev-runner.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/scripts/dev-runner.ts) now defaults local dev runs to `~/.tero-dev`. + - [`apps/server/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/main.ts) now defaults to `~/.tero-dev` whenever a dev URL is active and no explicit home-dir / `TERO_HOME` is provided. + - Added/updated tests in: + - [`scripts/dev-runner.test.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/scripts/dev-runner.test.ts) + - [`apps/server/src/main.test.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/main.test.ts) + - Why this matters: + - It makes local repo web/dev behavior match the desktop isolation model more closely. + - It reduces the risk that a plain dev server run can silently collide with packaged-app state. + - Follow-up finding: + - A direct `bun run dev:web` probe still failed before launch because [`scripts/dev-runner.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/scripts/dev-runner.ts) could not resolve the runtime import `@tero/shared/Net` in this checkout. + - Follow-up code change: + - [`scripts/dev-runner.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/scripts/dev-runner.ts) now imports the shared runtime net service via a direct repo-relative path: `../packages/shared/src/Net`. + - Updated implication: + - The next web probe should test the actual isolated dev behavior instead of failing at startup due to broken workspace-package linking. + - Additional follow-up: + - Node ESM resolution for the direct import also required the file extension in this script entrypoint. + - [`scripts/dev-runner.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/scripts/dev-runner.ts) now uses `../packages/shared/src/Net.ts`. + - Web-specific follow-up: + - The web dev server could start, but Vite still failed dependency resolution for: + - `@tero/contracts` + - `@tero/shared/model` + - `@tero/shared/schemaJson` + - `@tero/shared/git` + - [`apps/web/vite.config.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/web/vite.config.ts) now defines explicit source aliases for: + - `@tero/contracts` + - `@tero/shared/*` + - This is a targeted frontend workaround for the same broken workspace runtime-linking issue tracked in [`TODO.md`](/Users/raulrodrigues/workspace/personal/t3code-ero/TODO.md). + +- 2026-03-27 batch 8 + - Scope: replace runtime import hacks with an actual workspace-link fix. + - New finding: + - The underlying problem was not limited to one app. Root `node_modules` simply lacked the workspace package links entirely: + - no `node_modules/@tero/contracts` + - no `node_modules/@tero/shared` + - no `node_modules/@tero/*` scope directory at all + - Small code changes: + - Added [`scripts/sync-workspace-links.mjs`](/Users/raulrodrigues/workspace/personal/t3code-ero/scripts/sync-workspace-links.mjs) + - scans monorepo workspaces + - creates root `node_modules` symlinks for workspace packages + - uses platform-correct symlink type (`dir` on macOS/Linux, `junction` on Windows) + - Added root [`package.json`](/Users/raulrodrigues/workspace/personal/t3code-ero/package.json) `postinstall` hook: + - `node scripts/sync-workspace-links.mjs` + - Reverted temporary runtime workarounds back to package imports in: + - [`apps/desktop/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/src/main.ts) + - [`apps/desktop/src/syncShellEnvironment.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/src/syncShellEnvironment.ts) + - [`scripts/dev-runner.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/scripts/dev-runner.ts) + - [`apps/web/vite.config.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/web/vite.config.ts) + - Verification so far: + - running the sync script now creates: + - `node_modules/@tero/contracts` + - `node_modules/@tero/shared` + - other workspace package links under `node_modules/@tero/*` + - With the links present, workspace typecheck now progresses past the old `@tero/*` resolution errors and reports real app-level issues instead. + - Follow-up type fix: + - [`apps/web/src/routes/__root.tsx`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/web/src/routes/__root.tsx) now correctly types `flushSnapshotSync()` as returning `OrchestrationReadModel | null` because it can early-return when the effect is already disposed. + - [`apps/server/src/main.test.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/main.test.ts) now imports `homedir` for the new `~/.tero-dev` default-path assertion. + - Why this matters: + - It replaces the repo-relative import and Vite alias debt with a shared runtime fix at the workspace boundary. + - Cleanup follow-up: + - Removed the temporary forensic logs that were added only to chase the poisoning issue: + - local desktop startup / backend-spawn console dumps in [`apps/desktop/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/desktop/src/main.ts) + - `server.welcome prepared` logging in [`apps/server/src/wsServer.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/wsServer.ts) + - Kept the conflicting-env warning in [`apps/server/src/main.ts`](/Users/raulrodrigues/workspace/personal/t3code-ero/apps/server/src/main.ts) as an intentional guardrail. + +## Working Hypothesis + +- The repo currently looks like it was renamed inconsistently. A mixed state like this could plausibly break runtime assumptions, persisted paths, desktop bundle identity, or string-based protocol/config references. +- Some old identifiers should probably remain temporarily as compatibility fallbacks rather than being hard-removed immediately, especially persisted storage keys and environment-variable fallbacks. +- The earlier theory that the problem might have been a late validation freeze is now weaker. +- The stronger current theory is that working on this repo causes the app to write or read incompatible persisted state very early in startup, and that stale state then poisons the next launch until it is deleted. +- More concrete sub-theories: + - the installed app and the in-repo/dev environment may still be sharing or colliding on the same logical state directories + - a partial rename may be causing startup code to write to one path and restore from another + - an incompatible SQLite/session/config payload may be getting restored on launch + - a bad project/thread/session associated with this repo may be restored eagerly and wedge the app +- Based on the second recovery, the current best guess is **not** "the app bundle was damaged". The best guess is "persisted state became incompatible/corrupted, and deleting that state cleared the failure." + +## Constraints + +- Before considering the task complete, `bun fmt`, `bun lint`, and `bun typecheck` must pass. +- Per repo instructions, never run `bun test`; use `bun run test` if tests are needed. +- Be careful not to make changes that could interfere with the installed T3 Code app outside this repository. +- New practical safety constraint from user: avoid running this project in dev during this investigation unless absolutely necessary, because doing so may break the very Codex/T3 Code instance being used to work on the repo and terminate the session. +- Until proven otherwise, assume local dev startup is a high-risk action that can poison shared persisted state. + +## Next Steps + +- Treat this as a forensic/debug task first, not a broad rename pass. +- Identify exactly which persisted files/directories are read on startup and which ones could poison relaunch. +- If the app breaks again, preserve/copy the broken state before deleting it so logs and DB contents can be inspected. +- Focus especially on: + - SQLite state under the derived `userdata` directory + - keybindings/config files + - provider/session restore paths + - Electron userData/profile directories + - any place where legacy `t3code` and new `tero` paths/keys may be mixed +- Avoid launching the app/dev stack casually while collecting evidence; prefer static inspection first. +- Only after the startup/persistence risk is understood should the rename resume. +- Update this file as findings and changes accumulate. diff --git a/KEYBINDINGS.md b/KEYBINDINGS.md index 0c00fed4e7..8f3f8433a1 100644 --- a/KEYBINDINGS.md +++ b/KEYBINDINGS.md @@ -2,7 +2,7 @@ T3 Code reads keybindings from: -- `~/.t3/keybindings.json` +- `~/.tero/keybindings.json` The file must be a JSON array of rules: diff --git a/README.md b/README.md index 5f842e3709..2b4a5f7281 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ T3 Code is a minimal web GUI for coding agents (currently Codex and Claude, more > You need to have [Codex CLI](https://github.com/openai/codex) installed and authorized for T3 Code to work. ```bash -npx t3 +npx tero ``` You can also just install the desktop app. It's cooler. diff --git a/REMOTE.md b/REMOTE.md index 5eed2f803e..a9a546cd26 100644 --- a/REMOTE.md +++ b/REMOTE.md @@ -8,13 +8,13 @@ The T3 Code CLI accepts the following configuration options, available either as | CLI flag | Env var | Notes | | ----------------------- | --------------------- | ---------------------------------- | -| `--mode ` | `T3CODE_MODE` | Runtime mode. | -| `--port ` | `T3CODE_PORT` | HTTP/WebSocket port. | -| `--host
` | `T3CODE_HOST` | Bind interface/address. | -| `--base-dir ` | `T3CODE_HOME` | Base directory. | +| `--mode ` | `TERO_MODE` | Runtime mode. | +| `--port ` | `TERO_PORT` | HTTP/WebSocket port. | +| `--host
` | `TERO_HOST` | Bind interface/address. | +| `--base-dir ` | `TERO_HOME` | Base directory. | | `--dev-url ` | `VITE_DEV_SERVER_URL` | Dev web URL redirect/proxy target. | -| `--no-browser` | `T3CODE_NO_BROWSER` | Disable auto-open browser. | -| `--auth-token ` | `T3CODE_AUTH_TOKEN` | WebSocket auth token. | +| `--no-browser` | `TERO_NO_BROWSER` | Disable auto-open browser. | +| `--auth-token ` | `TERO_AUTH_TOKEN` | WebSocket auth token. | > TIP: Use the `--help` flag to see all available options and their descriptions. diff --git a/TODO.md b/TODO.md index 3d856996d8..0eddcfc3ef 100644 --- a/TODO.md +++ b/TODO.md @@ -1,13 +1,14 @@ # TODO -## Small things - -- [ ] Submitting new messages should scroll to bottom -- [ ] Only show last 10 threads for a given project -- [ ] Thread archiving -- [ ] New projects should go on top -- [ ] Projects should be sorted by latest thread update - -## Bigger things - -- [ ] Queueing messages +- Fix broken runtime workspace-package resolution for local entrypoints. + - Current symptom: scripts and desktop runtime code cannot reliably import workspace packages such as `@tero/shared` at runtime in this checkout. + - Evidence: + - `scripts/dev-runner.ts` failed to resolve `@tero/shared/Net` + - desktop runtime previously failed to resolve `@tero/shared/Net` and related shared imports + - Current workaround: + - some runtime imports were switched to direct repo-relative paths + - Desired fix: + - restore a proper workspace/runtime linking story so local Node/Electron entrypoints can use workspace package imports without repo-relative fallbacks + - Why it matters: + - this is separate from the T3 Code state-poisoning issue + - it makes local dev fragile and forces temporary path hacks into runtime entrypoints diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 188e701e7e..4ccf850915 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,5 +1,5 @@ { - "name": "@t3tools/desktop", + "name": "@tero/desktop", "version": "0.0.14", "private": true, "main": "dist-electron/main.js", @@ -19,13 +19,13 @@ "electron-updater": "^6.6.2" }, "devDependencies": { - "@t3tools/contracts": "workspace:*", - "@t3tools/shared": "workspace:*", + "@tero/contracts": "workspace:*", + "@tero/shared": "workspace:*", "@types/node": "catalog:", "tsdown": "catalog:", "typescript": "catalog:", "vitest": "catalog:", "wait-on": "^8.0.2" }, - "productName": "T3 Code (Alpha)" + "productName": "Tero (Alpha)" } diff --git a/apps/desktop/scripts/dev-electron.mjs b/apps/desktop/scripts/dev-electron.mjs index 12d4753509..c7f8bcb8a7 100644 --- a/apps/desktop/scripts/dev-electron.mjs +++ b/apps/desktop/scripts/dev-electron.mjs @@ -26,6 +26,7 @@ await waitOn({ const childEnv = { ...process.env }; delete childEnv.ELECTRON_RUN_AS_NODE; +process.env.TERO_DESKTOP_LOCAL_DEV = "1"; let shuttingDown = false; let restartTimer = null; @@ -47,7 +48,7 @@ function cleanupStaleDevApps() { return; } - spawnSync("pkill", ["-f", "--", `--t3code-dev-root=${desktopDir}`], { stdio: "ignore" }); + spawnSync("pkill", ["-f", "--", `--tero-dev-root=${desktopDir}`], { stdio: "ignore" }); } function startApp() { @@ -57,11 +58,13 @@ function startApp() { const app = spawn( resolveElectronPath(), - [`--t3code-dev-root=${desktopDir}`, "dist-electron/main.js"], + ["--tero-local-dev", `--tero-dev-root=${desktopDir}`, "dist-electron/main.js"], { cwd: desktopDir, env: { ...childEnv, + TERO_DESKTOP_LOCAL_DEV: "1", + TERO_DESKTOP_SERVER_EXECUTABLE: process.execPath, VITE_DEV_SERVER_URL: devServerUrl, }, stdio: "inherit", diff --git a/apps/desktop/scripts/electron-launcher.mjs b/apps/desktop/scripts/electron-launcher.mjs index 9d7c522781..e812f325ff 100644 --- a/apps/desktop/scripts/electron-launcher.mjs +++ b/apps/desktop/scripts/electron-launcher.mjs @@ -1,4 +1,4 @@ -// This file mostly exists because we want dev mode to say "T3 Code (Dev)" instead of "electron" +// This file mostly exists because we want dev mode to say "Tero (Dev)" instead of "electron" import { spawnSync } from "node:child_process"; import { @@ -17,8 +17,8 @@ import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; const isDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL); -const APP_DISPLAY_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)"; -const APP_BUNDLE_ID = "com.t3tools.t3code"; +const APP_DISPLAY_NAME = isDevelopment ? "Tero (Dev)" : "Tero (Alpha)"; +const APP_BUNDLE_ID = "com.tero.desktop"; const LAUNCHER_VERSION = 1; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -140,5 +140,9 @@ export function resolveElectronPath() { return electronBinaryPath; } + if (process.env.TERO_DESKTOP_LOCAL_DEV === "1") { + return electronBinaryPath; + } + return buildMacLauncher(electronBinaryPath); } diff --git a/apps/desktop/scripts/start-electron.mjs b/apps/desktop/scripts/start-electron.mjs index bf93adb6b0..494fc514df 100644 --- a/apps/desktop/scripts/start-electron.mjs +++ b/apps/desktop/scripts/start-electron.mjs @@ -4,11 +4,16 @@ import { desktopDir, resolveElectronPath } from "./electron-launcher.mjs"; const childEnv = { ...process.env }; delete childEnv.ELECTRON_RUN_AS_NODE; +process.env.TERO_DESKTOP_LOCAL_DEV = "1"; const child = spawn(resolveElectronPath(), ["dist-electron/main.js"], { stdio: "inherit", cwd: desktopDir, - env: childEnv, + env: { + ...childEnv, + TERO_DESKTOP_LOCAL_DEV: "1", + TERO_DESKTOP_SERVER_EXECUTABLE: process.execPath, + }, }); child.on("exit", (code, signal) => { diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 062c79fa69..d9ff34b82b 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -16,17 +16,14 @@ import { shell, } from "electron"; import type { MenuItemConstructorOptions } from "electron"; +import type { ContextMenuItem } from "@tero/contracts"; import * as Effect from "effect/Effect"; -import type { - DesktopTheme, - DesktopUpdateActionResult, - DesktopUpdateState, -} from "@t3tools/contracts"; +import type { DesktopTheme, DesktopUpdateActionResult, DesktopUpdateState } from "@tero/contracts"; +import { NetService } from "@tero/shared/Net"; +import { RotatingFileSink } from "@tero/shared/logging"; +import { getDefaultTeroHomePath, normalizeEnvAliases } from "@tero/shared/runtime"; import { autoUpdater } from "electron-updater"; -import type { ContextMenuItem } from "@t3tools/contracts"; -import { NetService } from "@t3tools/shared/Net"; -import { RotatingFileSink } from "@t3tools/shared/logging"; import { showDesktopConfirmDialog } from "./confirmDialog"; import { syncShellEnvironment } from "./syncShellEnvironment"; import { getAutoUpdateDisabledReason, shouldBroadcastDownloadProgress } from "./updateState"; @@ -56,15 +53,27 @@ const UPDATE_STATE_CHANNEL = "desktop:update-state"; const UPDATE_GET_STATE_CHANNEL = "desktop:update-get-state"; const UPDATE_DOWNLOAD_CHANNEL = "desktop:update-download"; const UPDATE_INSTALL_CHANNEL = "desktop:update-install"; -const BASE_DIR = process.env.T3CODE_HOME?.trim() || Path.join(OS.homedir(), ".t3"); +normalizeEnvAliases([ + ["TERO_HOME", "T3CODE_HOME"], + ["TERO_COMMIT_HASH", "T3CODE_COMMIT_HASH"], + ["TERO_DISABLE_AUTO_UPDATE", "T3CODE_DISABLE_AUTO_UPDATE"], + ["TERO_DESKTOP_UPDATE_GITHUB_TOKEN", "T3CODE_DESKTOP_UPDATE_GITHUB_TOKEN"], +]); +const isLocalDesktopRuntime = + process.env.TERO_DESKTOP_LOCAL_DEV === "1" || process.argv.includes("--tero-local-dev"); +const isPackaged = app.isPackaged && !isLocalDesktopRuntime; +const isRendererDevServer = Boolean(process.env.VITE_DEV_SERVER_URL); +const BASE_DIR = + process.env.TERO_HOME?.trim() ?? + getDefaultTeroHomePath(isPackaged ? "production" : "development", OS.homedir()); const STATE_DIR = Path.join(BASE_DIR, "userdata"); -const DESKTOP_SCHEME = "t3"; +const DESKTOP_SCHEME = "tero"; const ROOT_DIR = Path.resolve(__dirname, "../../.."); -const isDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL); -const APP_DISPLAY_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)"; -const APP_USER_MODEL_ID = "com.t3tools.t3code"; -const USER_DATA_DIR_NAME = isDevelopment ? "t3code-dev" : "t3code"; -const LEGACY_USER_DATA_DIR_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)"; +const isDevelopment = isRendererDevServer; +const APP_DISPLAY_NAME = isPackaged ? "Tero (Alpha)" : "Tero (Dev)"; +const APP_USER_MODEL_ID = "com.tero.desktop"; +const USER_DATA_DIR_NAME = isPackaged ? "tero" : "tero-dev"; +const LEGACY_USER_DATA_DIR_NAME = isPackaged ? "Tero (Alpha)" : "Tero (Dev)"; const COMMIT_HASH_PATTERN = /^[0-9a-f]{7,40}$/i; const COMMIT_HASH_DISPLAY_LENGTH = 12; const LOG_DIR = Path.join(STATE_DIR, "logs"); @@ -77,6 +86,12 @@ const DESKTOP_UPDATE_CHANNEL = "latest"; const DESKTOP_UPDATE_ALLOW_PRERELEASE = false; type DesktopUpdateErrorContext = DesktopUpdateState["errorContext"]; +type UserDataPathResolution = { + readonly appDataBase: string; + readonly legacyPath: string; + readonly chosenPath: string; + readonly usedLegacyPath: boolean; +}; let mainWindow: BrowserWindow | null = null; let backendProcess: ChildProcess.ChildProcess | null = null; @@ -344,8 +359,8 @@ function resolveEmbeddedCommitHash(): string | null { try { const raw = FS.readFileSync(packageJsonPath, "utf8"); - const parsed = JSON.parse(raw) as { t3codeCommitHash?: unknown }; - return normalizeCommitHash(parsed.t3codeCommitHash); + const parsed = JSON.parse(raw) as { teroCommitHash?: unknown; t3codeCommitHash?: unknown }; + return normalizeCommitHash(parsed.teroCommitHash ?? parsed.t3codeCommitHash); } catch { return null; } @@ -356,7 +371,7 @@ function resolveAboutCommitHash(): string | null { return aboutCommitHashCache; } - const envCommitHash = normalizeCommitHash(process.env.T3CODE_COMMIT_HASH); + const envCommitHash = normalizeCommitHash(process.env.TERO_COMMIT_HASH); if (envCommitHash) { aboutCommitHashCache = envCommitHash; return aboutCommitHashCache; @@ -440,7 +455,7 @@ function handleFatalStartupError(stage: string, error: unknown): void { console.error(`[desktop] fatal startup error (${stage})`, error); if (!isQuitting) { isQuitting = true; - dialog.showErrorBox("T3 Code failed to start", `Stage: ${stage}\n${message}${detail}`); + dialog.showErrorBox("Tero failed to start", `Stage: ${stage}\n${message}${detail}`); } stopBackend(); restoreStdIoCapture?.(); @@ -518,7 +533,7 @@ function handleCheckForUpdatesMenuClick(): void { isPackaged: app.isPackaged, platform: process.platform, appImage: process.env.APPIMAGE, - disabledByEnv: process.env.T3CODE_DISABLE_AUTO_UPDATE === "1", + disabledByEnv: process.env.TERO_DISABLE_AUTO_UPDATE === "1", }); if (disabledReason) { console.info("[desktop-updater] Manual update check requested, but updates are disabled."); @@ -545,7 +560,7 @@ async function checkForUpdatesFromMenu(): Promise { void dialog.showMessageBox({ type: "info", title: "You're up to date!", - message: `T3 Code ${updateState.currentVersion} is currently the newest version available.`, + message: `Tero ${updateState.currentVersion} is currently the newest version available.`, buttons: ["OK"], }); } else if (updateState.status === "error") { @@ -663,14 +678,18 @@ function resolveIconPath(ext: "ico" | "icns" | "png"): string | null { * * Electron derives the default userData path from `productName` in * package.json, which currently produces directories with spaces and - * parentheses (e.g. `~/.config/T3 Code (Alpha)` on Linux). This is + * parentheses (e.g. `~/.config/Tero (Alpha)` on Linux). This is * unfriendly for shell usage and violates Linux naming conventions. * - * We override it to a clean lowercase name (`t3code`). If the legacy + * We override it to a clean lowercase name (`tero`). If the legacy * directory already exists we keep using it so existing users don't * lose their Chromium profile data (localStorage, cookies, sessions). */ function resolveUserDataPath(): string { + return resolveUserDataPathDetails().chosenPath; +} + +function resolveUserDataPathDetails(): UserDataPathResolution { const appDataBase = process.platform === "win32" ? process.env.APPDATA || Path.join(OS.homedir(), "AppData", "Roaming") @@ -680,10 +699,20 @@ function resolveUserDataPath(): string { const legacyPath = Path.join(appDataBase, LEGACY_USER_DATA_DIR_NAME); if (FS.existsSync(legacyPath)) { - return legacyPath; + return { + appDataBase, + legacyPath, + chosenPath: legacyPath, + usedLegacyPath: true, + }; } - return Path.join(appDataBase, USER_DATA_DIR_NAME); + return { + appDataBase, + legacyPath, + chosenPath: Path.join(appDataBase, USER_DATA_DIR_NAME), + usedLegacyPath: false, + }; } function configureAppIdentity(): void { @@ -737,7 +766,7 @@ function shouldEnableAutoUpdates(): boolean { isPackaged: app.isPackaged, platform: process.platform, appImage: process.env.APPIMAGE, - disabledByEnv: process.env.T3CODE_DISABLE_AUTO_UPDATE === "1", + disabledByEnv: process.env.TERO_DISABLE_AUTO_UPDATE === "1", }) === null ); } @@ -822,7 +851,7 @@ function configureAutoUpdater(): void { updaterConfigured = true; const githubToken = - process.env.T3CODE_DESKTOP_UPDATE_GITHUB_TOKEN?.trim() || process.env.GH_TOKEN?.trim() || ""; + process.env.TERO_DESKTOP_UPDATE_GITHUB_TOKEN?.trim() || process.env.GH_TOKEN?.trim() || ""; if (githubToken) { // When a token is provided, re-configure the feed with `private: true` so // electron-updater uses the GitHub API (api.github.com) instead of the @@ -921,11 +950,32 @@ function configureAutoUpdater(): void { function backendEnv(): NodeJS.ProcessEnv { return { ...process.env, - T3CODE_MODE: "desktop", - T3CODE_NO_BROWSER: "1", - T3CODE_PORT: String(backendPort), - T3CODE_HOME: BASE_DIR, - T3CODE_AUTH_TOKEN: backendAuthToken, + TERO_MODE: "desktop", + TERO_NO_BROWSER: "1", + TERO_PORT: String(backendPort), + TERO_HOME: BASE_DIR, + TERO_AUTH_TOKEN: backendAuthToken, + }; +} + +function resolveBackendExecutable(): { executable: string; useElectronRunAsNode: boolean } { + const explicitExecutable = process.env.TERO_DESKTOP_SERVER_EXECUTABLE?.trim(); + if (explicitExecutable) { + return { + executable: explicitExecutable, + useElectronRunAsNode: explicitExecutable === process.execPath, + }; + } + + if (isLocalDesktopRuntime) { + throw new Error( + "Local desktop runtime requires TERO_DESKTOP_SERVER_EXECUTABLE to be set. Launch through the provided desktop scripts so the backend process uses Node instead of Electron.", + ); + } + + return { + executable: process.execPath, + useElectronRunAsNode: true, }; } @@ -952,13 +1002,31 @@ function startBackend(): void { } const captureBackendLogs = app.isPackaged && backendLogSink !== null; - const child = ChildProcess.spawn(process.execPath, [backendEntry], { + let backendExecutable: string; + let useElectronRunAsNode: boolean; + try { + ({ executable: backendExecutable, useElectronRunAsNode } = resolveBackendExecutable()); + } catch (error) { + handleFatalStartupError("resolve backend executable", error); + return; + } + const backendArgs = [ + backendEntry, + "--mode", + "desktop", + "--no-browser", + "--port", + String(backendPort), + "--home-dir", + BASE_DIR, + "--auth-token", + backendAuthToken, + ]; + const child = ChildProcess.spawn(backendExecutable, backendArgs, { cwd: resolveBackendCwd(), - // In Electron main, process.execPath points to the Electron binary. - // Run the child in Node mode so this backend process does not become a GUI app instance. env: { ...backendEnv(), - ELECTRON_RUN_AS_NODE: "1", + ...(useElectronRunAsNode ? { ELECTRON_RUN_AS_NODE: "1" } : {}), }, stdio: captureBackendLogs ? ["ignore", "pipe", "pipe"] : "inherit", }); @@ -1312,7 +1380,11 @@ app.setPath("userData", resolveUserDataPath()); configureAppIdentity(); async function bootstrap(): Promise { + const userDataPath = resolveUserDataPathDetails(); writeDesktopLogHeader("bootstrap start"); + writeDesktopLogHeader( + `startup paths packaged=${String(isPackaged)} localDesktopRuntime=${String(isLocalDesktopRuntime)} rendererDevServer=${String(isRendererDevServer)} baseDir=${BASE_DIR} stateDir=${STATE_DIR} userData=${userDataPath.chosenPath} legacyUserData=${userDataPath.legacyPath} legacyUserDataReused=${String(userDataPath.usedLegacyPath)}`, + ); backendPort = await Effect.service(NetService).pipe( Effect.flatMap((net) => net.reserveLoopbackPort()), Effect.provide(NetService.layer), @@ -1321,7 +1393,7 @@ async function bootstrap(): Promise { writeDesktopLogHeader(`reserved backend port via NetService port=${backendPort}`); backendAuthToken = Crypto.randomBytes(24).toString("hex"); backendWsUrl = `ws://127.0.0.1:${backendPort}/?token=${encodeURIComponent(backendAuthToken)}`; - process.env.T3CODE_DESKTOP_WS_URL = backendWsUrl; + process.env.TERO_DESKTOP_WS_URL = backendWsUrl; writeDesktopLogHeader(`bootstrap resolved websocket url=${backendWsUrl}`); registerIpcHandlers(); diff --git a/apps/desktop/src/preload.ts b/apps/desktop/src/preload.ts index 1e1bb3bd8e..44132295d8 100644 --- a/apps/desktop/src/preload.ts +++ b/apps/desktop/src/preload.ts @@ -1,5 +1,5 @@ import { contextBridge, ipcRenderer } from "electron"; -import type { DesktopBridge } from "@t3tools/contracts"; +import type { DesktopBridge } from "@tero/contracts"; const PICK_FOLDER_CHANNEL = "desktop:pick-folder"; const CONFIRM_CHANNEL = "desktop:confirm"; @@ -11,7 +11,7 @@ const UPDATE_STATE_CHANNEL = "desktop:update-state"; const UPDATE_GET_STATE_CHANNEL = "desktop:update-get-state"; const UPDATE_DOWNLOAD_CHANNEL = "desktop:update-download"; const UPDATE_INSTALL_CHANNEL = "desktop:update-install"; -const wsUrl = process.env.T3CODE_DESKTOP_WS_URL ?? null; +const wsUrl = process.env.TERO_DESKTOP_WS_URL ?? process.env.T3CODE_DESKTOP_WS_URL ?? null; contextBridge.exposeInMainWorld("desktopBridge", { getWsUrl: () => wsUrl, diff --git a/apps/desktop/src/rotatingFileSink.test.ts b/apps/desktop/src/rotatingFileSink.test.ts index 53dd98ade8..491a0e0e0a 100644 --- a/apps/desktop/src/rotatingFileSink.test.ts +++ b/apps/desktop/src/rotatingFileSink.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import { RotatingFileSink } from "@t3tools/shared/logging"; +import { RotatingFileSink } from "@tero/shared/logging"; import { afterEach, describe, expect, it } from "vitest"; const tempRoots: string[] = []; diff --git a/apps/desktop/src/runtimeArch.ts b/apps/desktop/src/runtimeArch.ts index 127abf51ab..d0547a0c53 100644 --- a/apps/desktop/src/runtimeArch.ts +++ b/apps/desktop/src/runtimeArch.ts @@ -1,4 +1,4 @@ -import type { DesktopRuntimeArch, DesktopRuntimeInfo } from "@t3tools/contracts"; +import type { DesktopRuntimeArch, DesktopRuntimeInfo } from "@tero/contracts"; interface ResolveDesktopRuntimeInfoInput { readonly platform: NodeJS.Platform; diff --git a/apps/desktop/src/syncShellEnvironment.ts b/apps/desktop/src/syncShellEnvironment.ts index 2181bea0ca..c6274079e6 100644 --- a/apps/desktop/src/syncShellEnvironment.ts +++ b/apps/desktop/src/syncShellEnvironment.ts @@ -1,4 +1,4 @@ -import { readEnvironmentFromLoginShell, ShellEnvironmentReader } from "@t3tools/shared/shell"; +import { readEnvironmentFromLoginShell, ShellEnvironmentReader } from "@tero/shared/shell"; export function syncShellEnvironment( env: NodeJS.ProcessEnv = process.env, diff --git a/apps/desktop/src/updateMachine.ts b/apps/desktop/src/updateMachine.ts index f13b420281..4dc142ee47 100644 --- a/apps/desktop/src/updateMachine.ts +++ b/apps/desktop/src/updateMachine.ts @@ -1,4 +1,4 @@ -import type { DesktopRuntimeInfo, DesktopUpdateState } from "@t3tools/contracts"; +import type { DesktopRuntimeInfo, DesktopUpdateState } from "@tero/contracts"; import { getCanRetryAfterDownloadFailure, nextStatusAfterDownloadFailure } from "./updateState"; diff --git a/apps/desktop/src/updateState.test.ts b/apps/desktop/src/updateState.test.ts index 43b718bd00..7e38477c3e 100644 --- a/apps/desktop/src/updateState.test.ts +++ b/apps/desktop/src/updateState.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import type { DesktopUpdateState } from "@t3tools/contracts"; +import type { DesktopUpdateState } from "@tero/contracts"; import { getCanRetryAfterDownloadFailure, @@ -84,7 +84,7 @@ describe("getAutoUpdateDisabledReason", () => { appImage: undefined, disabledByEnv: true, }), - ).toContain("T3CODE_DISABLE_AUTO_UPDATE"); + ).toContain("TERO_DISABLE_AUTO_UPDATE"); }); it("reports linux non-AppImage builds as disabled", () => { diff --git a/apps/desktop/src/updateState.ts b/apps/desktop/src/updateState.ts index 8c8ef1ddc9..9dbbc0033c 100644 --- a/apps/desktop/src/updateState.ts +++ b/apps/desktop/src/updateState.ts @@ -1,4 +1,4 @@ -import type { DesktopUpdateState } from "@t3tools/contracts"; +import type { DesktopUpdateState } from "@tero/contracts"; export function shouldBroadcastDownloadProgress( currentState: DesktopUpdateState, @@ -39,7 +39,7 @@ export function getAutoUpdateDisabledReason(args: { return "Automatic updates are only available in packaged production builds."; } if (args.disabledByEnv) { - return "Automatic updates are disabled by the T3CODE_DISABLE_AUTO_UPDATE setting."; + return "Automatic updates are disabled by the TERO_DISABLE_AUTO_UPDATE setting."; } if (args.platform === "linux" && !args.appImage) { return "Automatic updates on Linux require running the AppImage build."; diff --git a/apps/desktop/tsdown.config.ts b/apps/desktop/tsdown.config.ts index f3ebc97325..0fec83f452 100644 --- a/apps/desktop/tsdown.config.ts +++ b/apps/desktop/tsdown.config.ts @@ -12,7 +12,7 @@ export default defineConfig([ ...shared, entry: ["src/main.ts"], clean: true, - noExternal: (id) => id.startsWith("@t3tools/"), + noExternal: (id) => id.startsWith("@tero/"), }, { ...shared, diff --git a/apps/desktop/turbo.jsonc b/apps/desktop/turbo.jsonc index 2ff803229e..034f045940 100644 --- a/apps/desktop/turbo.jsonc +++ b/apps/desktop/turbo.jsonc @@ -7,16 +7,16 @@ "outputs": ["dist-electron/**"], }, "dev": { - "dependsOn": ["t3#build"], + "dependsOn": ["tero#build"], "persistent": true, }, "start": { - "dependsOn": ["build", "@t3tools/web#build", "t3#build"], + "dependsOn": ["build", "@tero/web#build", "tero#build"], "cache": false, "persistent": true, }, "smoke-test": { - "dependsOn": ["build", "@t3tools/web#build", "t3#build"], + "dependsOn": ["build", "@tero/web#build", "tero#build"], "cache": false, "outputs": [], }, diff --git a/apps/marketing/package.json b/apps/marketing/package.json index 1763a00102..389659c8b9 100644 --- a/apps/marketing/package.json +++ b/apps/marketing/package.json @@ -1,5 +1,5 @@ { - "name": "@t3tools/marketing", + "name": "@tero/marketing", "version": "0.0.0", "private": true, "type": "module", diff --git a/apps/server/integration/OrchestrationEngineHarness.integration.ts b/apps/server/integration/OrchestrationEngineHarness.integration.ts index f540685b79..fde5570e6a 100644 --- a/apps/server/integration/OrchestrationEngineHarness.integration.ts +++ b/apps/server/integration/OrchestrationEngineHarness.integration.ts @@ -6,7 +6,7 @@ import { ProviderKind, type OrchestrationEvent, type OrchestrationThread, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect, Exit, diff --git a/apps/server/integration/TestProviderAdapter.integration.ts b/apps/server/integration/TestProviderAdapter.integration.ts index 9c87d9821a..bcc1fc5fe4 100644 --- a/apps/server/integration/TestProviderAdapter.integration.ts +++ b/apps/server/integration/TestProviderAdapter.integration.ts @@ -11,7 +11,7 @@ import { ThreadId, TurnId, ProviderKind, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect, Queue, Stream } from "effect"; import { diff --git a/apps/server/integration/fixtures/providerRuntime.ts b/apps/server/integration/fixtures/providerRuntime.ts index 42e7ecd87d..c3b6766605 100644 --- a/apps/server/integration/fixtures/providerRuntime.ts +++ b/apps/server/integration/fixtures/providerRuntime.ts @@ -1,4 +1,4 @@ -import { EventId, RuntimeRequestId } from "@t3tools/contracts"; +import { EventId, RuntimeRequestId } from "@tero/contracts"; import type { LegacyProviderRuntimeEvent } from "../TestProviderAdapter.integration.ts"; const PROVIDER = "codex" as const; diff --git a/apps/server/integration/orchestrationEngine.integration.test.ts b/apps/server/integration/orchestrationEngine.integration.test.ts index d6b1004749..813acabc1b 100644 --- a/apps/server/integration/orchestrationEngine.integration.test.ts +++ b/apps/server/integration/orchestrationEngine.integration.test.ts @@ -12,7 +12,7 @@ import { ProviderKind, ThreadId, ModelSelection, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { assert, it } from "@effect/vitest"; import { Effect, Option, Schema } from "effect"; diff --git a/apps/server/integration/providerService.integration.test.ts b/apps/server/integration/providerService.integration.test.ts index 53b4f30a80..ba9db67af5 100644 --- a/apps/server/integration/providerService.integration.test.ts +++ b/apps/server/integration/providerService.integration.test.ts @@ -1,5 +1,5 @@ -import type { ProviderRuntimeEvent } from "@t3tools/contracts"; -import { ThreadId } from "@t3tools/contracts"; +import type { ProviderRuntimeEvent } from "@tero/contracts"; +import { ThreadId } from "@tero/contracts"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { it, assert } from "@effect/vitest"; import { Effect, FileSystem, Layer, Path, Queue, Stream } from "effect"; diff --git a/apps/server/package.json b/apps/server/package.json index ea818b7d3e..0253e14c90 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -1,14 +1,14 @@ { - "name": "t3", + "name": "tero", "version": "0.0.14", "license": "MIT", "repository": { "type": "git", - "url": "https://github.com/pingdotgg/t3code", + "url": "https://github.com/pingdotgg/tero", "directory": "apps/server" }, "bin": { - "t3": "./dist/index.mjs" + "tero": "./dist/index.mjs" }, "files": [ "dist" @@ -35,9 +35,9 @@ "devDependencies": { "@effect/language-service": "catalog:", "@effect/vitest": "catalog:", - "@t3tools/contracts": "workspace:*", - "@t3tools/shared": "workspace:*", - "@t3tools/web": "workspace:*", + "@tero/contracts": "workspace:*", + "@tero/shared": "workspace:*", + "@tero/web": "workspace:*", "@types/bun": "catalog:", "@types/node": "catalog:", "@types/ws": "^8.5.13", diff --git a/apps/server/src/attachmentStore.ts b/apps/server/src/attachmentStore.ts index aa85b8c51a..198bf63b87 100644 --- a/apps/server/src/attachmentStore.ts +++ b/apps/server/src/attachmentStore.ts @@ -1,7 +1,7 @@ import { randomUUID } from "node:crypto"; import { existsSync } from "node:fs"; -import type { ChatAttachment } from "@t3tools/contracts"; +import type { ChatAttachment } from "@tero/contracts"; import { normalizeAttachmentRelativePath, diff --git a/apps/server/src/checkpointing/Layers/CheckpointDiffQuery.test.ts b/apps/server/src/checkpointing/Layers/CheckpointDiffQuery.test.ts index daa6eb5f4c..920ce01751 100644 --- a/apps/server/src/checkpointing/Layers/CheckpointDiffQuery.test.ts +++ b/apps/server/src/checkpointing/Layers/CheckpointDiffQuery.test.ts @@ -5,7 +5,7 @@ import { ThreadId, TurnId, type OrchestrationReadModel, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect, Layer } from "effect"; import { describe, expect, it } from "vitest"; diff --git a/apps/server/src/checkpointing/Layers/CheckpointDiffQuery.ts b/apps/server/src/checkpointing/Layers/CheckpointDiffQuery.ts index 5cf26c86da..2a6fb99c74 100644 --- a/apps/server/src/checkpointing/Layers/CheckpointDiffQuery.ts +++ b/apps/server/src/checkpointing/Layers/CheckpointDiffQuery.ts @@ -3,7 +3,7 @@ import { type OrchestrationGetFullThreadDiffInput, type OrchestrationGetFullThreadDiffResult, type OrchestrationGetTurnDiffResult as OrchestrationGetTurnDiffResultType, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect, Layer, Schema } from "effect"; import { ProjectionSnapshotQuery } from "../../orchestration/Services/ProjectionSnapshotQuery.ts"; diff --git a/apps/server/src/checkpointing/Layers/CheckpointStore.ts b/apps/server/src/checkpointing/Layers/CheckpointStore.ts index b20204780c..6a9135073e 100644 --- a/apps/server/src/checkpointing/Layers/CheckpointStore.ts +++ b/apps/server/src/checkpointing/Layers/CheckpointStore.ts @@ -17,7 +17,7 @@ import { CheckpointInvariantError } from "../Errors.ts"; import { GitCommandError } from "../../git/Errors.ts"; import { GitCore } from "../../git/Services/GitCore.ts"; import { CheckpointStore, type CheckpointStoreShape } from "../Services/CheckpointStore.ts"; -import { CheckpointRef } from "@t3tools/contracts"; +import { CheckpointRef } from "@tero/contracts"; const makeCheckpointStore = Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; @@ -91,17 +91,17 @@ const makeCheckpointStore = Effect.gen(function* () { const operation = "CheckpointStore.captureCheckpoint"; yield* Effect.acquireUseRelease( - fs.makeTempDirectory({ prefix: "t3-fs-checkpoint-" }), + fs.makeTempDirectory({ prefix: "tero-fs-checkpoint-" }), (tempDir) => Effect.gen(function* () { const tempIndexPath = path.join(tempDir, `index-${randomUUID()}`); const commitEnv: NodeJS.ProcessEnv = { ...process.env, GIT_INDEX_FILE: tempIndexPath, - GIT_AUTHOR_NAME: "T3 Code", - GIT_AUTHOR_EMAIL: "t3code@users.noreply.github.com", - GIT_COMMITTER_NAME: "T3 Code", - GIT_COMMITTER_EMAIL: "t3code@users.noreply.github.com", + GIT_AUTHOR_NAME: "Tero", + GIT_AUTHOR_EMAIL: "tero@users.noreply.github.com", + GIT_COMMITTER_NAME: "Tero", + GIT_COMMITTER_EMAIL: "tero@users.noreply.github.com", }; const headExists = yield* hasHeadCommit(input.cwd); @@ -137,7 +137,7 @@ const makeCheckpointStore = Effect.gen(function* () { }); } - const message = `t3 checkpoint ref=${input.checkpointRef}`; + const message = `tero checkpoint ref=${input.checkpointRef}`; const commitTreeResult = yield* git.execute({ operation, cwd: input.cwd, diff --git a/apps/server/src/checkpointing/Services/CheckpointDiffQuery.ts b/apps/server/src/checkpointing/Services/CheckpointDiffQuery.ts index 2204294a43..25baf3f2dc 100644 --- a/apps/server/src/checkpointing/Services/CheckpointDiffQuery.ts +++ b/apps/server/src/checkpointing/Services/CheckpointDiffQuery.ts @@ -11,7 +11,7 @@ import type { OrchestrationGetFullThreadDiffResult, OrchestrationGetTurnDiffInput, OrchestrationGetTurnDiffResult, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -46,4 +46,4 @@ export interface CheckpointDiffQueryShape { export class CheckpointDiffQuery extends ServiceMap.Service< CheckpointDiffQuery, CheckpointDiffQueryShape ->()("t3/checkpointing/Services/CheckpointDiffQuery") {} +>()("tero/checkpointing/Services/CheckpointDiffQuery") {} diff --git a/apps/server/src/checkpointing/Services/CheckpointStore.ts b/apps/server/src/checkpointing/Services/CheckpointStore.ts index 284198c145..5911cd6fbd 100644 --- a/apps/server/src/checkpointing/Services/CheckpointStore.ts +++ b/apps/server/src/checkpointing/Services/CheckpointStore.ts @@ -14,7 +14,7 @@ import { ServiceMap } from "effect"; import type { Effect } from "effect"; import type { CheckpointStoreError } from "../Errors.ts"; -import { CheckpointRef } from "@t3tools/contracts"; +import { CheckpointRef } from "@tero/contracts"; export interface CaptureCheckpointInput { readonly cwd: string; @@ -96,5 +96,5 @@ export interface CheckpointStoreShape { * CheckpointStore - Service tag for checkpoint persistence and restore operations. */ export class CheckpointStore extends ServiceMap.Service()( - "t3/checkpointing/Services/CheckpointStore", + "tero/checkpointing/Services/CheckpointStore", ) {} diff --git a/apps/server/src/checkpointing/Utils.ts b/apps/server/src/checkpointing/Utils.ts index 3cd92f8510..d890491880 100644 --- a/apps/server/src/checkpointing/Utils.ts +++ b/apps/server/src/checkpointing/Utils.ts @@ -1,7 +1,7 @@ import { Encoding } from "effect"; -import { CheckpointRef, ProjectId, type ThreadId } from "@t3tools/contracts"; +import { CheckpointRef, ProjectId, type ThreadId } from "@tero/contracts"; -export const CHECKPOINT_REFS_PREFIX = "refs/t3/checkpoints"; +export const CHECKPOINT_REFS_PREFIX = "refs/tero/checkpoints"; export function checkpointRefForThreadTurn(threadId: ThreadId, turnCount: number): CheckpointRef { return CheckpointRef.makeUnsafe( diff --git a/apps/server/src/codexAppServerManager.test.ts b/apps/server/src/codexAppServerManager.test.ts index 80323c7441..0329ab57d0 100644 --- a/apps/server/src/codexAppServerManager.test.ts +++ b/apps/server/src/codexAppServerManager.test.ts @@ -3,7 +3,7 @@ import { randomUUID } from "node:crypto"; import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; -import { ApprovalRequestId, ThreadId } from "@t3tools/contracts"; +import { ApprovalRequestId, ThreadId } from "@tero/contracts"; import { buildCodexInitializeParams, diff --git a/apps/server/src/codexAppServerManager.ts b/apps/server/src/codexAppServerManager.ts index 0ac37db3e8..27af663d64 100644 --- a/apps/server/src/codexAppServerManager.ts +++ b/apps/server/src/codexAppServerManager.ts @@ -18,8 +18,8 @@ import { type ProviderTurnStartResult, RuntimeMode, ProviderInteractionMode, -} from "@t3tools/contracts"; -import { normalizeModelSlug } from "@t3tools/shared/model"; +} from "@tero/contracts"; +import { normalizeModelSlug } from "@tero/shared/model"; import { Effect, ServiceMap } from "effect"; import { diff --git a/apps/server/src/config.ts b/apps/server/src/config.ts index 8553ce9667..a773283b1d 100644 --- a/apps/server/src/config.ts +++ b/apps/server/src/config.ts @@ -75,7 +75,7 @@ export const deriveServerPaths = Effect.fn(function* ( * ServerConfig - Service tag for server runtime configuration. */ export class ServerConfig extends ServiceMap.Service()( - "t3/config/ServerConfig", + "tero/config/ServerConfig", ) { static readonly layerTest = (cwd: string, baseDirOrPrefix: string | { prefix: string }) => Layer.effect( diff --git a/apps/server/src/git/Layers/CodexTextGeneration.ts b/apps/server/src/git/Layers/CodexTextGeneration.ts index 373c191236..26feee9e3e 100644 --- a/apps/server/src/git/Layers/CodexTextGeneration.ts +++ b/apps/server/src/git/Layers/CodexTextGeneration.ts @@ -3,8 +3,8 @@ import { randomUUID } from "node:crypto"; import { Effect, FileSystem, Layer, Option, Path, Schema, Stream } from "effect"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; -import { DEFAULT_GIT_TEXT_GENERATION_MODEL } from "@t3tools/contracts"; -import { sanitizeBranchFragment, sanitizeFeatureBranchName } from "@t3tools/shared/git"; +import { DEFAULT_GIT_TEXT_GENERATION_MODEL } from "@tero/contracts"; +import { sanitizeBranchFragment, sanitizeFeatureBranchName } from "@tero/shared/git"; import { resolveAttachmentPath } from "../../attachmentStore.ts"; import { ServerConfig } from "../../config.ts"; diff --git a/apps/server/src/git/Layers/GitCore.ts b/apps/server/src/git/Layers/GitCore.ts index 8bb5844228..86cbfe019d 100644 --- a/apps/server/src/git/Layers/GitCore.ts +++ b/apps/server/src/git/Layers/GitCore.ts @@ -28,7 +28,7 @@ import { type ExecuteGitResult, } from "../Services/GitCore.ts"; import { ServerConfig } from "../../config.ts"; -import { decodeJsonResult } from "@t3tools/shared/schemaJson"; +import { decodeJsonResult } from "@tero/shared/schemaJson"; const DEFAULT_TIMEOUT_MS = 30_000; const DEFAULT_MAX_OUTPUT_BYTES = 1_000_000; diff --git a/apps/server/src/git/Layers/GitHubCli.ts b/apps/server/src/git/Layers/GitHubCli.ts index 80ce43659e..0e768afb3e 100644 --- a/apps/server/src/git/Layers/GitHubCli.ts +++ b/apps/server/src/git/Layers/GitHubCli.ts @@ -1,5 +1,5 @@ import { Effect, Layer, Schema } from "effect"; -import { PositiveInt, TrimmedNonEmptyString } from "@t3tools/contracts"; +import { PositiveInt, TrimmedNonEmptyString } from "@tero/contracts"; import { runProcess } from "../../processRunner"; import { GitHubCliError } from "../Errors.ts"; diff --git a/apps/server/src/git/Layers/GitManager.test.ts b/apps/server/src/git/Layers/GitManager.test.ts index ce252f739f..dbb28858ba 100644 --- a/apps/server/src/git/Layers/GitManager.test.ts +++ b/apps/server/src/git/Layers/GitManager.test.ts @@ -6,7 +6,7 @@ import * as NodeServices from "@effect/platform-node/NodeServices"; import { it } from "@effect/vitest"; import { Effect, FileSystem, Layer, PlatformError, Scope } from "effect"; import { expect } from "vitest"; -import type { GitActionProgressEvent } from "@t3tools/contracts"; +import type { GitActionProgressEvent } from "@tero/contracts"; import { GitCommandError, GitHubCliError, TextGenerationError } from "../Errors.ts"; import { type GitManagerShape } from "../Services/GitManager.ts"; diff --git a/apps/server/src/git/Layers/GitManager.ts b/apps/server/src/git/Layers/GitManager.ts index 4f240e0044..b4c075da06 100644 --- a/apps/server/src/git/Layers/GitManager.ts +++ b/apps/server/src/git/Layers/GitManager.ts @@ -2,12 +2,12 @@ import { randomUUID } from "node:crypto"; import { realpathSync } from "node:fs"; import { Effect, FileSystem, Layer, Path } from "effect"; -import type { GitActionProgressEvent, GitActionProgressPhase } from "@t3tools/contracts"; +import type { GitActionProgressEvent, GitActionProgressPhase } from "@tero/contracts"; import { resolveAutoFeatureBranchName, sanitizeBranchFragment, sanitizeFeatureBranchName, -} from "@t3tools/shared/git"; +} from "@tero/shared/git"; import { GitManagerError } from "../Errors.ts"; import { diff --git a/apps/server/src/git/Services/GitCore.ts b/apps/server/src/git/Services/GitCore.ts index b74c526897..e73760004f 100644 --- a/apps/server/src/git/Services/GitCore.ts +++ b/apps/server/src/git/Services/GitCore.ts @@ -20,7 +20,7 @@ import type { GitRemoveWorktreeInput, GitStatusInput, GitStatusResult, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import type { GitCommandError } from "../Errors.ts"; @@ -273,5 +273,5 @@ export interface GitCoreShape { * GitCore - Service tag for low-level Git repository operations. */ export class GitCore extends ServiceMap.Service()( - "t3/git/Services/GitCore", + "tero/git/Services/GitCore", ) {} diff --git a/apps/server/src/git/Services/GitHubCli.ts b/apps/server/src/git/Services/GitHubCli.ts index f10339af47..6d90e3fd86 100644 --- a/apps/server/src/git/Services/GitHubCli.ts +++ b/apps/server/src/git/Services/GitHubCli.ts @@ -99,5 +99,5 @@ export interface GitHubCliShape { * GitHubCli - Service tag for GitHub CLI process execution. */ export class GitHubCli extends ServiceMap.Service()( - "t3/git/Services/GitHubCli", + "tero/git/Services/GitHubCli", ) {} diff --git a/apps/server/src/git/Services/GitManager.ts b/apps/server/src/git/Services/GitManager.ts index 2e83b78c3b..01e89f8f9e 100644 --- a/apps/server/src/git/Services/GitManager.ts +++ b/apps/server/src/git/Services/GitManager.ts @@ -16,7 +16,7 @@ import { GitRunStackedActionResult, GitStatusInput, GitStatusResult, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { ServiceMap } from "effect"; import type { Effect } from "effect"; import type { GitManagerServiceError } from "../Errors.ts"; @@ -69,5 +69,5 @@ export interface GitManagerShape { * GitManager - Service tag for stacked Git workflow orchestration. */ export class GitManager extends ServiceMap.Service()( - "t3/git/Services/GitManager", + "tero/git/Services/GitManager", ) {} diff --git a/apps/server/src/git/Services/TextGeneration.ts b/apps/server/src/git/Services/TextGeneration.ts index b4650ed570..0fd46c1ecf 100644 --- a/apps/server/src/git/Services/TextGeneration.ts +++ b/apps/server/src/git/Services/TextGeneration.ts @@ -8,7 +8,7 @@ */ import { ServiceMap } from "effect"; import type { Effect } from "effect"; -import type { ChatAttachment } from "@t3tools/contracts"; +import type { ChatAttachment } from "@tero/contracts"; import type { TextGenerationError } from "../Errors.ts"; @@ -96,5 +96,5 @@ export interface TextGenerationShape { * TextGeneration - Service tag for commit and PR text generation. */ export class TextGeneration extends ServiceMap.Service()( - "t3/git/Services/TextGeneration", + "tero/git/Services/TextGeneration", ) {} diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index 363a07ee38..0ef317b1a6 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -3,12 +3,12 @@ import * as NodeServices from "@effect/platform-node/NodeServices"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; -import { CliConfig, t3Cli } from "./main"; +import { CliConfig, teroCli } from "./main"; import { OpenLive } from "./open"; import { Command } from "effect/unstable/cli"; import { version } from "../package.json" with { type: "json" }; import { ServerLive } from "./wsServer"; -import { NetService } from "@t3tools/shared/Net"; +import { NetService } from "@tero/shared/Net"; import { FetchHttpClient } from "effect/unstable/http"; const RuntimeLayer = Layer.empty.pipe( @@ -20,4 +20,4 @@ const RuntimeLayer = Layer.empty.pipe( Layer.provideMerge(FetchHttpClient.layer), ); -Command.run(t3Cli, { version }).pipe(Effect.provide(RuntimeLayer), NodeRuntime.runMain); +Command.run(teroCli, { version }).pipe(Effect.provide(RuntimeLayer), NodeRuntime.runMain); diff --git a/apps/server/src/keybindings.test.ts b/apps/server/src/keybindings.test.ts index 846c3778b0..bbf9063adf 100644 --- a/apps/server/src/keybindings.test.ts +++ b/apps/server/src/keybindings.test.ts @@ -1,4 +1,4 @@ -import { KeybindingCommand, KeybindingRule, KeybindingsConfig } from "@t3tools/contracts"; +import { KeybindingCommand, KeybindingRule, KeybindingsConfig } from "@tero/contracts"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it } from "@effect/vitest"; import { assertFailure } from "@effect/vitest/utils"; diff --git a/apps/server/src/keybindings.ts b/apps/server/src/keybindings.ts index bf58467825..a1155cac70 100644 --- a/apps/server/src/keybindings.ts +++ b/apps/server/src/keybindings.ts @@ -16,7 +16,7 @@ import { ResolvedKeybindingRule, ResolvedKeybindingsConfig, type ServerConfigIssue, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Mutable } from "effect/Types"; import { Array, @@ -525,7 +525,7 @@ export interface KeybindingsShape { * Keybindings - Service tag for keybinding configuration operations. */ export class Keybindings extends ServiceMap.Service()( - "t3/keybindings", + "tero/keybindings", ) {} const makeKeybindings = Effect.gen(function* () { diff --git a/apps/server/src/main.test.ts b/apps/server/src/main.test.ts index b1e5da0c87..400d80401a 100644 --- a/apps/server/src/main.test.ts +++ b/apps/server/src/main.test.ts @@ -1,16 +1,17 @@ import * as Http from "node:http"; +import { homedir } from "node:os"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it, vi } from "@effect/vitest"; -import type { OrchestrationReadModel } from "@t3tools/contracts"; +import type { OrchestrationReadModel } from "@tero/contracts"; import * as ConfigProvider from "effect/ConfigProvider"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Command from "effect/unstable/cli/Command"; import { FetchHttpClient } from "effect/unstable/http"; import { beforeEach } from "vitest"; -import { NetService } from "@t3tools/shared/Net"; +import { NetService } from "@tero/shared/Net"; -import { CliConfig, recordStartupHeartbeat, t3Cli, type CliConfigShape } from "./main"; +import { CliConfig, recordStartupHeartbeat, teroCli, type CliConfigShape } from "./main"; import { ServerConfig, type ServerConfigShape } from "./config"; import { Open, type OpenShape } from "./open"; import { ProjectionSnapshotQuery } from "./orchestration/Services/ProjectionSnapshotQuery"; @@ -33,7 +34,7 @@ const findAvailablePort = vi.fn((preferred: number) => Effect.succeed(preferred) // Shared service layer used by this CLI test suite. const testLayer = Layer.mergeAll( Layer.succeed(CliConfig, { - cwd: "/tmp/t3-test-workspace", + cwd: "/tmp/tero-test-workspace", fixPath: Effect.void, resolveStaticDir: Effect.undefined, } satisfies CliConfigShape), @@ -58,9 +59,9 @@ const testLayer = Layer.mergeAll( const runCli = ( args: ReadonlyArray, - env: Record = { T3CODE_NO_BROWSER: "true" }, + env: Record = { TERO_NO_BROWSER: "true" }, ) => { - return Command.runWith(t3Cli, { version: "0.0.0-test" })(args).pipe( + return Command.runWith(teroCli, { version: "0.0.0-test" })(args).pipe( Effect.provide( ConfigProvider.layer( ConfigProvider.fromEnv({ @@ -92,7 +93,7 @@ it.layer(testLayer)("server CLI command", (it) => { "--host", "0.0.0.0", "--home-dir", - "/tmp/t3-cli-home", + "/tmp/tero-cli-home", "--dev-url", "http://127.0.0.1:5173", "--no-browser", @@ -104,8 +105,8 @@ it.layer(testLayer)("server CLI command", (it) => { assert.equal(resolvedConfig?.mode, "desktop"); assert.equal(resolvedConfig?.port, 4010); assert.equal(resolvedConfig?.host, "0.0.0.0"); - assert.equal(resolvedConfig?.baseDir, "/tmp/t3-cli-home"); - assert.equal(resolvedConfig?.stateDir, "/tmp/t3-cli-home/dev"); + assert.equal(resolvedConfig?.baseDir, "/tmp/tero-cli-home"); + assert.equal(resolvedConfig?.stateDir, "/tmp/tero-cli-home/dev"); assert.equal(resolvedConfig?.devUrl?.toString(), "http://127.0.0.1:5173/"); assert.equal(resolvedConfig?.noBrowser, true); assert.equal(resolvedConfig?.authToken, "auth-secret"); @@ -127,21 +128,21 @@ it.layer(testLayer)("server CLI command", (it) => { it.effect("uses env fallbacks when flags are not provided", () => Effect.gen(function* () { yield* runCli([], { - T3CODE_MODE: "desktop", - T3CODE_PORT: "4999", - T3CODE_HOST: "100.88.10.4", - T3CODE_HOME: "/tmp/t3-env-home", + TERO_MODE: "desktop", + TERO_PORT: "4999", + TERO_HOST: "100.88.10.4", + TERO_HOME: "/tmp/tero-env-home", VITE_DEV_SERVER_URL: "http://localhost:5173", - T3CODE_NO_BROWSER: "true", - T3CODE_AUTH_TOKEN: "env-token", + TERO_NO_BROWSER: "true", + TERO_AUTH_TOKEN: "env-token", }); assert.equal(start.mock.calls.length, 1); assert.equal(resolvedConfig?.mode, "desktop"); assert.equal(resolvedConfig?.port, 4999); assert.equal(resolvedConfig?.host, "100.88.10.4"); - assert.equal(resolvedConfig?.baseDir, "/tmp/t3-env-home"); - assert.equal(resolvedConfig?.stateDir, "/tmp/t3-env-home/dev"); + assert.equal(resolvedConfig?.baseDir, "/tmp/tero-env-home"); + assert.equal(resolvedConfig?.stateDir, "/tmp/tero-env-home/dev"); assert.equal(resolvedConfig?.devUrl?.toString(), "http://localhost:5173/"); assert.equal(resolvedConfig?.noBrowser, true); assert.equal(resolvedConfig?.authToken, "env-token"); @@ -151,12 +152,26 @@ it.layer(testLayer)("server CLI command", (it) => { }), ); - it.effect("prefers --mode over T3CODE_MODE", () => + it.effect("defaults dev-url startup to ~/.tero-dev when no home-dir is provided", () => + Effect.gen(function* () { + yield* runCli([], { + VITE_DEV_SERVER_URL: "http://localhost:5173", + TERO_NO_BROWSER: "true", + }); + + assert.equal(start.mock.calls.length, 1); + assert.equal(resolvedConfig?.baseDir, `${homedir()}/.tero-dev`); + assert.equal(resolvedConfig?.stateDir, `${homedir()}/.tero-dev/dev`); + assert.equal(resolvedConfig?.devUrl?.toString(), "http://localhost:5173/"); + }), + ); + + it.effect("prefers --mode over TERO_MODE", () => Effect.gen(function* () { findAvailablePort.mockImplementation((_preferred: number) => Effect.succeed(4666)); yield* runCli(["--mode", "web"], { - T3CODE_MODE: "desktop", - T3CODE_NO_BROWSER: "true", + TERO_MODE: "desktop", + TERO_NO_BROWSER: "true", }); assert.deepStrictEqual(findAvailablePort.mock.calls, [[3773]]); @@ -167,10 +182,10 @@ it.layer(testLayer)("server CLI command", (it) => { }), ); - it.effect("prefers --no-browser over T3CODE_NO_BROWSER", () => + it.effect("prefers --no-browser over TERO_NO_BROWSER", () => Effect.gen(function* () { yield* runCli(["--no-browser"], { - T3CODE_NO_BROWSER: "false", + TERO_NO_BROWSER: "false", }); assert.equal(start.mock.calls.length, 1); @@ -193,8 +208,8 @@ it.layer(testLayer)("server CLI command", (it) => { it.effect("uses fixed localhost defaults in desktop mode", () => Effect.gen(function* () { yield* runCli([], { - T3CODE_MODE: "desktop", - T3CODE_NO_BROWSER: "true", + TERO_MODE: "desktop", + TERO_NO_BROWSER: "true", }); assert.equal(findAvailablePort.mock.calls.length, 0); @@ -208,8 +223,8 @@ it.layer(testLayer)("server CLI command", (it) => { it.effect("allows overriding desktop host with --host", () => Effect.gen(function* () { yield* runCli(["--host", "0.0.0.0"], { - T3CODE_MODE: "desktop", - T3CODE_NO_BROWSER: "true", + TERO_MODE: "desktop", + TERO_NO_BROWSER: "true", }); assert.equal(start.mock.calls.length, 1); @@ -221,10 +236,10 @@ it.layer(testLayer)("server CLI command", (it) => { it.effect("supports CLI and env for bootstrap/log websocket toggles", () => Effect.gen(function* () { yield* runCli(["--auto-bootstrap-project-from-cwd"], { - T3CODE_MODE: "desktop", - T3CODE_LOG_WS_EVENTS: "false", - T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "false", - T3CODE_NO_BROWSER: "true", + TERO_MODE: "desktop", + TERO_LOG_WS_EVENTS: "false", + TERO_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "false", + TERO_NO_BROWSER: "true", }); assert.equal(start.mock.calls.length, 1); diff --git a/apps/server/src/main.ts b/apps/server/src/main.ts index 17bf7f32f7..2971695283 100644 --- a/apps/server/src/main.ts +++ b/apps/server/src/main.ts @@ -8,7 +8,12 @@ */ import { Config, Data, Effect, FileSystem, Layer, Option, Path, Schema, ServiceMap } from "effect"; import { Command, Flag } from "effect/unstable/cli"; -import { NetService } from "@t3tools/shared/Net"; +import { NetService } from "@tero/shared/Net"; +import { + getDefaultTeroHomePath, + normalizeEnvAliases, + TERO_RUNTIME_ENV_ALIASES, +} from "@tero/shared/runtime"; import { DEFAULT_PORT, deriveServerPaths, @@ -37,7 +42,7 @@ interface CliInput { readonly mode: Option.Option; readonly port: Option.Option; readonly host: Option.Option; - readonly t3Home: Option.Option; + readonly teroHome: Option.Option; readonly devUrl: Option.Option; readonly noBrowser: Option.Option; readonly authToken: Option.Option; @@ -69,7 +74,7 @@ export interface CliConfigShape { * CliConfig - Service tag for startup CLI/runtime helpers. */ export class CliConfig extends ServiceMap.Service()( - "t3/main/CliConfig", + "tero/main/CliConfig", ) { static readonly layer = Layer.effect( CliConfig, @@ -88,8 +93,20 @@ export class CliConfig extends ServiceMap.Service()( ); } +normalizeEnvAliases(TERO_RUNTIME_ENV_ALIASES, { + onConflict: ({ preferred, legacy }) => { + console.warn( + `[tero] conflicting environment values detected; preferring ${preferred} over ${legacy}`, + { + preferred, + legacy, + }, + ); + }, +}); + const CliEnvConfig = Config.all({ - mode: Config.string("T3CODE_MODE").pipe( + mode: Config.string("TERO_MODE").pipe( Config.option, Config.map( Option.match({ @@ -98,23 +115,23 @@ const CliEnvConfig = Config.all({ }), ), ), - port: Config.port("T3CODE_PORT").pipe(Config.option, Config.map(Option.getOrUndefined)), - host: Config.string("T3CODE_HOST").pipe(Config.option, Config.map(Option.getOrUndefined)), - t3Home: Config.string("T3CODE_HOME").pipe(Config.option, Config.map(Option.getOrUndefined)), + port: Config.port("TERO_PORT").pipe(Config.option, Config.map(Option.getOrUndefined)), + host: Config.string("TERO_HOST").pipe(Config.option, Config.map(Option.getOrUndefined)), + teroHome: Config.string("TERO_HOME").pipe(Config.option, Config.map(Option.getOrUndefined)), devUrl: Config.url("VITE_DEV_SERVER_URL").pipe(Config.option, Config.map(Option.getOrUndefined)), - noBrowser: Config.boolean("T3CODE_NO_BROWSER").pipe( + noBrowser: Config.boolean("TERO_NO_BROWSER").pipe( Config.option, Config.map(Option.getOrUndefined), ), - authToken: Config.string("T3CODE_AUTH_TOKEN").pipe( + authToken: Config.string("TERO_AUTH_TOKEN").pipe( Config.option, Config.map(Option.getOrUndefined), ), - autoBootstrapProjectFromCwd: Config.boolean("T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD").pipe( + autoBootstrapProjectFromCwd: Config.boolean("TERO_AUTO_BOOTSTRAP_PROJECT_FROM_CWD").pipe( Config.option, Config.map(Option.getOrUndefined), ), - logWebSocketEvents: Config.boolean("T3CODE_LOG_WS_EVENTS").pipe( + logWebSocketEvents: Config.boolean("TERO_LOG_WS_EVENTS").pipe( Config.option, Config.map(Option.getOrUndefined), ), @@ -123,6 +140,23 @@ const CliEnvConfig = Config.all({ const resolveBooleanFlag = (flag: Option.Option, envValue: boolean) => Option.getOrElse(Option.filter(flag, Boolean), () => envValue); +const resolveRuntimeBaseDirInput = ( + input: CliInput, + envTeroHome: string | undefined, + devUrl: URL | undefined, +) => { + const explicitHome = Option.getOrUndefined(input.teroHome) ?? envTeroHome; + if (explicitHome !== undefined) { + return explicitHome; + } + + if (devUrl !== undefined) { + return getDefaultTeroHomePath("development"); + } + + return undefined; +}; + const ServerConfigLive = (input: CliInput) => Layer.effect( ServerConfig, @@ -152,7 +186,9 @@ const ServerConfigLive = (input: CliInput) => }); const devUrl = Option.getOrElse(input.devUrl, () => env.devUrl); - const baseDir = yield* resolveBaseDir(Option.getOrUndefined(input.t3Home) ?? env.t3Home); + const baseDir = yield* resolveBaseDir( + resolveRuntimeBaseDirInput(input, env.teroHome, devUrl), + ); const derivedPaths = yield* deriveServerPaths(baseDir, devUrl); const noBrowser = resolveBooleanFlag(input.noBrowser, env.noBrowser ?? mode === "desktop"); const authToken = Option.getOrUndefined(input.authToken) ?? env.authToken; @@ -258,7 +294,7 @@ const makeServerProgram = (input: CliInput) => ? `http://${formatHostForUrl(config.host)}:${config.port}` : localUrl; const { authToken, devUrl, ...safeConfig } = config; - yield* Effect.logInfo("T3 Code running", { + yield* Effect.logInfo("Tero running", { ...safeConfig, devUrl: devUrl?.toString(), authEnabled: Boolean(authToken), @@ -295,8 +331,8 @@ const hostFlag = Flag.string("host").pipe( Flag.withDescription("Host/interface to bind (for example 127.0.0.1, 0.0.0.0, or a Tailnet IP)."), Flag.optional, ); -const t3HomeFlag = Flag.string("home-dir").pipe( - Flag.withDescription("Base directory for all T3 Code data (equivalent to T3CODE_HOME)."), +const teroHomeFlag = Flag.string("home-dir").pipe( + Flag.withDescription("Base directory for all Tero data (equivalent to TERO_HOME)."), Flag.optional, ); const devUrlFlag = Flag.string("dev-url").pipe( @@ -321,23 +357,23 @@ const autoBootstrapProjectFromCwdFlag = Flag.boolean("auto-bootstrap-project-fro ); const logWebSocketEventsFlag = Flag.boolean("log-websocket-events").pipe( Flag.withDescription( - "Emit server-side logs for outbound WebSocket push traffic (equivalent to T3CODE_LOG_WS_EVENTS).", + "Emit server-side logs for outbound WebSocket push traffic (equivalent to TERO_LOG_WS_EVENTS).", ), Flag.withAlias("log-ws-events"), Flag.optional, ); -export const t3Cli = Command.make("t3", { +export const teroCli = Command.make("tero", { mode: modeFlag, port: portFlag, host: hostFlag, - t3Home: t3HomeFlag, + teroHome: teroHomeFlag, devUrl: devUrlFlag, noBrowser: noBrowserFlag, authToken: authTokenFlag, autoBootstrapProjectFromCwd: autoBootstrapProjectFromCwdFlag, logWebSocketEvents: logWebSocketEventsFlag, }).pipe( - Command.withDescription("Run the T3 Code server."), + Command.withDescription("Run the Tero server."), Command.withHandler((input) => Effect.scoped(makeServerProgram(input))), ); diff --git a/apps/server/src/open.ts b/apps/server/src/open.ts index e7238c04b2..614d03ef8e 100644 --- a/apps/server/src/open.ts +++ b/apps/server/src/open.ts @@ -10,7 +10,7 @@ import { spawn } from "node:child_process"; import { accessSync, constants, statSync } from "node:fs"; import { extname, join } from "node:path"; -import { EDITORS, type EditorId } from "@t3tools/contracts"; +import { EDITORS, type EditorId } from "@tero/contracts"; import { ServiceMap, Schema, Effect, Layer } from "effect"; // ============================== @@ -197,7 +197,7 @@ export interface OpenShape { /** * Open - Service tag for browser/editor launch operations. */ -export class Open extends ServiceMap.Service()("t3/open") {} +export class Open extends ServiceMap.Service()("tero/open") {} // ============================== // Implementations diff --git a/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts b/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts index 69360ebf6a..800de4f2c2 100644 --- a/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts +++ b/apps/server/src/orchestration/Layers/CheckpointReactor.test.ts @@ -3,7 +3,7 @@ import os from "node:os"; import path from "node:path"; import { execFileSync } from "node:child_process"; -import type { ProviderKind, ProviderRuntimeEvent, ProviderSession } from "@t3tools/contracts"; +import type { ProviderKind, ProviderRuntimeEvent, ProviderSession } from "@tero/contracts"; import { CommandId, DEFAULT_PROVIDER_INTERACTION_MODE, @@ -12,7 +12,7 @@ import { ProjectId, ThreadId, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { Effect, Exit, Layer, ManagedRuntime, PubSub, Scope, Stream } from "effect"; import { afterEach, describe, expect, it, vi } from "vitest"; diff --git a/apps/server/src/orchestration/Layers/CheckpointReactor.ts b/apps/server/src/orchestration/Layers/CheckpointReactor.ts index ab38c10332..2f020ec040 100644 --- a/apps/server/src/orchestration/Layers/CheckpointReactor.ts +++ b/apps/server/src/orchestration/Layers/CheckpointReactor.ts @@ -7,9 +7,9 @@ import { TurnId, type OrchestrationEvent, type ProviderRuntimeEvent, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Cause, Effect, Layer, Option, Stream } from "effect"; -import { makeDrainableWorker } from "@t3tools/shared/DrainableWorker"; +import { makeDrainableWorker } from "@tero/shared/DrainableWorker"; import { parseTurnDiffFilesFromUnifiedDiff } from "../../checkpointing/Diffs.ts"; import { diff --git a/apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts b/apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts index a1cbfa002d..4665900ef0 100644 --- a/apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts +++ b/apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts @@ -7,7 +7,7 @@ import { ThreadId, TurnId, type OrchestrationEvent, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect, Layer, ManagedRuntime, Queue, Stream } from "effect"; import { describe, expect, it } from "vitest"; diff --git a/apps/server/src/orchestration/Layers/OrchestrationEngine.ts b/apps/server/src/orchestration/Layers/OrchestrationEngine.ts index 69b28b9d3c..ce73bf9342 100644 --- a/apps/server/src/orchestration/Layers/OrchestrationEngine.ts +++ b/apps/server/src/orchestration/Layers/OrchestrationEngine.ts @@ -3,8 +3,8 @@ import type { OrchestrationReadModel, ProjectId, ThreadId, -} from "@t3tools/contracts"; -import { OrchestrationCommand } from "@t3tools/contracts"; +} from "@tero/contracts"; +import { OrchestrationCommand } from "@tero/contracts"; import { Deferred, Effect, Layer, Option, PubSub, Queue, Schema, Stream } from "effect"; import * as SqlClient from "effect/unstable/sql/SqlClient"; diff --git a/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts b/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts index 77b5d4d619..30fa6dede0 100644 --- a/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts +++ b/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts @@ -7,7 +7,7 @@ import { ProjectId, ThreadId, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it } from "@effect/vitest"; import { Effect, FileSystem, Layer, Path } from "effect"; diff --git a/apps/server/src/orchestration/Layers/ProjectionPipeline.ts b/apps/server/src/orchestration/Layers/ProjectionPipeline.ts index ce68d654ef..4e1aecaa29 100644 --- a/apps/server/src/orchestration/Layers/ProjectionPipeline.ts +++ b/apps/server/src/orchestration/Layers/ProjectionPipeline.ts @@ -1,8 +1,4 @@ -import { - ApprovalRequestId, - type ChatAttachment, - type OrchestrationEvent, -} from "@t3tools/contracts"; +import { ApprovalRequestId, type ChatAttachment, type OrchestrationEvent } from "@tero/contracts"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { Effect, FileSystem, Layer, Option, Path, Stream } from "effect"; import * as SqlClient from "effect/unstable/sql/SqlClient"; diff --git a/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts b/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts index 5080ea8c48..ff7615e47c 100644 --- a/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts +++ b/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts @@ -1,4 +1,4 @@ -import { CheckpointRef, EventId, MessageId, ProjectId, ThreadId, TurnId } from "@t3tools/contracts"; +import { CheckpointRef, EventId, MessageId, ProjectId, ThreadId, TurnId } from "@tero/contracts"; import { assert, it } from "@effect/vitest"; import { Effect, Layer } from "effect"; import * as SqlClient from "effect/unstable/sql/SqlClient"; diff --git a/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts b/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts index cc2f4f87e7..6440dbc3e1 100644 --- a/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts +++ b/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts @@ -18,7 +18,7 @@ import { type OrchestrationThread, type OrchestrationThreadActivity, ModelSelection, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect, Layer, Schema, Struct } from "effect"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as SqlSchema from "effect/unstable/sql/SqlSchema"; diff --git a/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts b/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts index b58c2522cb..368e4a6d44 100644 --- a/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts +++ b/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import type { ModelSelection, ProviderRuntimeEvent, ProviderSession } from "@t3tools/contracts"; +import type { ModelSelection, ProviderRuntimeEvent, ProviderSession } from "@tero/contracts"; import { ApprovalRequestId, CommandId, @@ -12,7 +12,7 @@ import { ProjectId, ThreadId, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect, Exit, Layer, ManagedRuntime, PubSub, Scope, Stream } from "effect"; import { afterEach, describe, expect, it, vi } from "vitest"; diff --git a/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts b/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts index 9399bcc280..d241272d7d 100644 --- a/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts +++ b/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts @@ -12,9 +12,9 @@ import { type ProviderSession, type RuntimeMode, type TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Cache, Cause, Duration, Effect, Equal, Layer, Option, Schema, Stream } from "effect"; -import { makeDrainableWorker } from "@t3tools/shared/DrainableWorker"; +import { makeDrainableWorker } from "@tero/shared/DrainableWorker"; import { resolveThreadWorkspaceCwd } from "../../checkpointing/Utils.ts"; import { GitCore } from "../../git/Services/GitCore.ts"; diff --git a/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts b/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts index b29df5c8fe..6c964f4f33 100644 --- a/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts +++ b/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts @@ -7,7 +7,7 @@ import type { ProviderKind, ProviderRuntimeEvent, ProviderSession, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { ApprovalRequestId, CommandId, @@ -18,7 +18,7 @@ import { ProviderItemId, ThreadId, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect, Exit, Layer, ManagedRuntime, PubSub, Scope, Stream } from "effect"; import { afterEach, describe, expect, it } from "vitest"; diff --git a/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts b/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts index 170b5bc717..6c33e9e1a3 100644 --- a/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts +++ b/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts @@ -12,9 +12,9 @@ import { TurnId, type OrchestrationThreadActivity, type ProviderRuntimeEvent, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Cache, Cause, Duration, Effect, Layer, Option, Ref, Stream } from "effect"; -import { makeDrainableWorker } from "@t3tools/shared/DrainableWorker"; +import { makeDrainableWorker } from "@tero/shared/DrainableWorker"; import { ProviderService } from "../../provider/Services/ProviderService.ts"; import { ProjectionTurnRepository } from "../../persistence/Services/ProjectionTurns.ts"; diff --git a/apps/server/src/orchestration/Schemas.ts b/apps/server/src/orchestration/Schemas.ts index c96385cad1..64d3c98d67 100644 --- a/apps/server/src/orchestration/Schemas.ts +++ b/apps/server/src/orchestration/Schemas.ts @@ -18,7 +18,7 @@ import { ThreadApprovalResponseRequestedPayload as ContractsThreadApprovalResponseRequestedPayloadSchema, ThreadCheckpointRevertRequestedPayload as ContractsThreadCheckpointRevertRequestedPayloadSchema, ThreadSessionStopRequestedPayload as ContractsThreadSessionStopRequestedPayloadSchema, -} from "@t3tools/contracts"; +} from "@tero/contracts"; // Server-internal alias surface, backed by contract schemas as the source of truth. export const ProjectCreatedPayload = ContractsProjectCreatedPayloadSchema; diff --git a/apps/server/src/orchestration/Services/CheckpointReactor.ts b/apps/server/src/orchestration/Services/CheckpointReactor.ts index 612bc22acb..50159329f8 100644 --- a/apps/server/src/orchestration/Services/CheckpointReactor.ts +++ b/apps/server/src/orchestration/Services/CheckpointReactor.ts @@ -37,4 +37,4 @@ export interface CheckpointReactorShape { export class CheckpointReactor extends ServiceMap.Service< CheckpointReactor, CheckpointReactorShape ->()("t3/orchestration/Services/CheckpointReactor") {} +>()("tero/orchestration/Services/CheckpointReactor") {} diff --git a/apps/server/src/orchestration/Services/OrchestrationEngine.ts b/apps/server/src/orchestration/Services/OrchestrationEngine.ts index 33ac75049f..de90b892da 100644 --- a/apps/server/src/orchestration/Services/OrchestrationEngine.ts +++ b/apps/server/src/orchestration/Services/OrchestrationEngine.ts @@ -14,7 +14,7 @@ import type { OrchestrationCommand, OrchestrationEvent, OrchestrationReadModel, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { ServiceMap } from "effect"; import type { Effect, Stream } from "effect"; @@ -77,4 +77,4 @@ export interface OrchestrationEngineShape { export class OrchestrationEngineService extends ServiceMap.Service< OrchestrationEngineService, OrchestrationEngineShape ->()("t3/orchestration/Services/OrchestrationEngine/OrchestrationEngineService") {} +>()("tero/orchestration/Services/OrchestrationEngine/OrchestrationEngineService") {} diff --git a/apps/server/src/orchestration/Services/OrchestrationReactor.ts b/apps/server/src/orchestration/Services/OrchestrationReactor.ts index 258ef2d614..4f7f3608c5 100644 --- a/apps/server/src/orchestration/Services/OrchestrationReactor.ts +++ b/apps/server/src/orchestration/Services/OrchestrationReactor.ts @@ -28,4 +28,4 @@ export interface OrchestrationReactorShape { export class OrchestrationReactor extends ServiceMap.Service< OrchestrationReactor, OrchestrationReactorShape ->()("t3/orchestration/Services/OrchestrationReactor") {} +>()("tero/orchestration/Services/OrchestrationReactor") {} diff --git a/apps/server/src/orchestration/Services/ProjectionPipeline.ts b/apps/server/src/orchestration/Services/ProjectionPipeline.ts index f8c86bf09b..7e60755eee 100644 --- a/apps/server/src/orchestration/Services/ProjectionPipeline.ts +++ b/apps/server/src/orchestration/Services/ProjectionPipeline.ts @@ -6,7 +6,7 @@ * * @module OrchestrationProjectionPipeline */ -import type { OrchestrationEvent } from "@t3tools/contracts"; +import type { OrchestrationEvent } from "@tero/contracts"; import { ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -39,4 +39,4 @@ export interface OrchestrationProjectionPipelineShape { export class OrchestrationProjectionPipeline extends ServiceMap.Service< OrchestrationProjectionPipeline, OrchestrationProjectionPipelineShape ->()("t3/orchestration/Services/ProjectionPipeline/OrchestrationProjectionPipeline") {} +>()("tero/orchestration/Services/ProjectionPipeline/OrchestrationProjectionPipeline") {} diff --git a/apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts b/apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts index 91e42f02ff..b0535a8979 100644 --- a/apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts +++ b/apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts @@ -6,7 +6,7 @@ * * @module ProjectionSnapshotQuery */ -import type { OrchestrationReadModel } from "@t3tools/contracts"; +import type { OrchestrationReadModel } from "@tero/contracts"; import { ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -31,4 +31,4 @@ export interface ProjectionSnapshotQueryShape { export class ProjectionSnapshotQuery extends ServiceMap.Service< ProjectionSnapshotQuery, ProjectionSnapshotQueryShape ->()("t3/orchestration/Services/ProjectionSnapshotQuery") {} +>()("tero/orchestration/Services/ProjectionSnapshotQuery") {} diff --git a/apps/server/src/orchestration/Services/ProviderCommandReactor.ts b/apps/server/src/orchestration/Services/ProviderCommandReactor.ts index 8f20571c15..33a57329e6 100644 --- a/apps/server/src/orchestration/Services/ProviderCommandReactor.ts +++ b/apps/server/src/orchestration/Services/ProviderCommandReactor.ts @@ -37,4 +37,4 @@ export interface ProviderCommandReactorShape { export class ProviderCommandReactor extends ServiceMap.Service< ProviderCommandReactor, ProviderCommandReactorShape ->()("t3/orchestration/Services/ProviderCommandReactor") {} +>()("tero/orchestration/Services/ProviderCommandReactor") {} diff --git a/apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts b/apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts index 2bbb157f10..6b83501f52 100644 --- a/apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts +++ b/apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts @@ -37,4 +37,4 @@ export interface ProviderRuntimeIngestionShape { export class ProviderRuntimeIngestionService extends ServiceMap.Service< ProviderRuntimeIngestionService, ProviderRuntimeIngestionShape ->()("t3/orchestration/Services/ProviderRuntimeIngestion/ProviderRuntimeIngestionService") {} +>()("tero/orchestration/Services/ProviderRuntimeIngestion/ProviderRuntimeIngestionService") {} diff --git a/apps/server/src/orchestration/Services/RuntimeReceiptBus.ts b/apps/server/src/orchestration/Services/RuntimeReceiptBus.ts index a08ff91685..073b6460d8 100644 --- a/apps/server/src/orchestration/Services/RuntimeReceiptBus.ts +++ b/apps/server/src/orchestration/Services/RuntimeReceiptBus.ts @@ -1,4 +1,4 @@ -import { CheckpointRef, IsoDateTime, NonNegativeInt, ThreadId, TurnId } from "@t3tools/contracts"; +import { CheckpointRef, IsoDateTime, NonNegativeInt, ThreadId, TurnId } from "@tero/contracts"; import { Schema, ServiceMap } from "effect"; import type { Effect, Stream } from "effect"; @@ -46,4 +46,4 @@ export interface RuntimeReceiptBusShape { export class RuntimeReceiptBus extends ServiceMap.Service< RuntimeReceiptBus, RuntimeReceiptBusShape ->()("t3/orchestration/Services/RuntimeReceiptBus") {} +>()("tero/orchestration/Services/RuntimeReceiptBus") {} diff --git a/apps/server/src/orchestration/commandInvariants.test.ts b/apps/server/src/orchestration/commandInvariants.test.ts index b07eb4234f..ef408bfd50 100644 --- a/apps/server/src/orchestration/commandInvariants.test.ts +++ b/apps/server/src/orchestration/commandInvariants.test.ts @@ -7,7 +7,7 @@ import { ThreadId, type OrchestrationCommand, type OrchestrationReadModel, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect } from "effect"; import { diff --git a/apps/server/src/orchestration/commandInvariants.ts b/apps/server/src/orchestration/commandInvariants.ts index 6acb9c82ba..36a88c775c 100644 --- a/apps/server/src/orchestration/commandInvariants.ts +++ b/apps/server/src/orchestration/commandInvariants.ts @@ -5,7 +5,7 @@ import type { OrchestrationThread, ProjectId, ThreadId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect } from "effect"; import { OrchestrationCommandInvariantError } from "./Errors.ts"; diff --git a/apps/server/src/orchestration/decider.projectScripts.test.ts b/apps/server/src/orchestration/decider.projectScripts.test.ts index 69a9117824..a53d69f937 100644 --- a/apps/server/src/orchestration/decider.projectScripts.test.ts +++ b/apps/server/src/orchestration/decider.projectScripts.test.ts @@ -5,7 +5,7 @@ import { MessageId, ProjectId, ThreadId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { describe, expect, it } from "vitest"; import { Effect } from "effect"; diff --git a/apps/server/src/orchestration/decider.ts b/apps/server/src/orchestration/decider.ts index 761ab56a7d..8918cd0d86 100644 --- a/apps/server/src/orchestration/decider.ts +++ b/apps/server/src/orchestration/decider.ts @@ -2,7 +2,7 @@ import type { OrchestrationCommand, OrchestrationEvent, OrchestrationReadModel, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect } from "effect"; import { OrchestrationCommandInvariantError } from "./Errors.ts"; diff --git a/apps/server/src/orchestration/projector.test.ts b/apps/server/src/orchestration/projector.test.ts index fd95d028d8..e3b93f6bbe 100644 --- a/apps/server/src/orchestration/projector.test.ts +++ b/apps/server/src/orchestration/projector.test.ts @@ -1,10 +1,4 @@ -import { - CommandId, - EventId, - ProjectId, - ThreadId, - type OrchestrationEvent, -} from "@t3tools/contracts"; +import { CommandId, EventId, ProjectId, ThreadId, type OrchestrationEvent } from "@tero/contracts"; import { Effect } from "effect"; import { describe, expect, it } from "vitest"; diff --git a/apps/server/src/orchestration/projector.ts b/apps/server/src/orchestration/projector.ts index 05a660f753..628d9e8689 100644 --- a/apps/server/src/orchestration/projector.ts +++ b/apps/server/src/orchestration/projector.ts @@ -1,10 +1,10 @@ -import type { OrchestrationEvent, OrchestrationReadModel, ThreadId } from "@t3tools/contracts"; +import type { OrchestrationEvent, OrchestrationReadModel, ThreadId } from "@tero/contracts"; import { OrchestrationCheckpointSummary, OrchestrationMessage, OrchestrationSession, OrchestrationThread, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect, Schema } from "effect"; import { toProjectorDecodeError, type OrchestrationProjectorDecodeError } from "./Errors.ts"; diff --git a/apps/server/src/os-jank.ts b/apps/server/src/os-jank.ts index 0721d0d9f8..d9ed3afccd 100644 --- a/apps/server/src/os-jank.ts +++ b/apps/server/src/os-jank.ts @@ -1,6 +1,6 @@ import * as OS from "node:os"; import { Effect, Path } from "effect"; -import { readPathFromLoginShell } from "@t3tools/shared/shell"; +import { readPathFromLoginShell } from "@tero/shared/shell"; export function fixPath(): void { if (process.platform !== "darwin") return; @@ -30,7 +30,7 @@ export const expandHomePath = Effect.fn(function* (input: string) { export const resolveBaseDir = Effect.fn(function* (raw: string | undefined) { const { join, resolve } = yield* Path.Path; if (!raw || raw.trim().length === 0) { - return join(OS.homedir(), ".t3"); + return join(OS.homedir(), ".tero"); } return resolve(yield* expandHomePath(raw.trim())); }); diff --git a/apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts b/apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts index 249e9d1e36..39b001996c 100644 --- a/apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts +++ b/apps/server/src/persistence/Layers/OrchestrationEventStore.test.ts @@ -1,4 +1,4 @@ -import { CommandId, EventId, ProjectId } from "@t3tools/contracts"; +import { CommandId, EventId, ProjectId } from "@tero/contracts"; import { assert, it } from "@effect/vitest"; import { Effect, Layer, Schema, Stream } from "effect"; import * as SqlClient from "effect/unstable/sql/SqlClient"; diff --git a/apps/server/src/persistence/Layers/OrchestrationEventStore.ts b/apps/server/src/persistence/Layers/OrchestrationEventStore.ts index 4d81cf5e8d..ea86593b5a 100644 --- a/apps/server/src/persistence/Layers/OrchestrationEventStore.ts +++ b/apps/server/src/persistence/Layers/OrchestrationEventStore.ts @@ -10,7 +10,7 @@ import { OrchestrationEventType, ProjectId, ThreadId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as SqlSchema from "effect/unstable/sql/SqlSchema"; import { Effect, Layer, Schema, Stream } from "effect"; diff --git a/apps/server/src/persistence/Layers/ProjectionCheckpoints.ts b/apps/server/src/persistence/Layers/ProjectionCheckpoints.ts index 26ef8fed1b..d095fbab11 100644 --- a/apps/server/src/persistence/Layers/ProjectionCheckpoints.ts +++ b/apps/server/src/persistence/Layers/ProjectionCheckpoints.ts @@ -1,4 +1,4 @@ -import { OrchestrationCheckpointFile } from "@t3tools/contracts"; +import { OrchestrationCheckpointFile } from "@tero/contracts"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as SqlSchema from "effect/unstable/sql/SqlSchema"; import { Effect, Layer, Option, Schema, Struct } from "effect"; diff --git a/apps/server/src/persistence/Layers/ProjectionProjects.ts b/apps/server/src/persistence/Layers/ProjectionProjects.ts index 7ff19f55ae..a224cc7237 100644 --- a/apps/server/src/persistence/Layers/ProjectionProjects.ts +++ b/apps/server/src/persistence/Layers/ProjectionProjects.ts @@ -2,7 +2,7 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as SqlSchema from "effect/unstable/sql/SqlSchema"; import { Effect, Layer, Schema, Struct } from "effect"; -import { ModelSelection, ProjectScript } from "@t3tools/contracts"; +import { ModelSelection, ProjectScript } from "@tero/contracts"; import { toPersistenceSqlError } from "../Errors.ts"; import { DeleteProjectionProjectInput, diff --git a/apps/server/src/persistence/Layers/ProjectionRepositories.test.ts b/apps/server/src/persistence/Layers/ProjectionRepositories.test.ts index 0ca13f2e97..6537d3d7d6 100644 --- a/apps/server/src/persistence/Layers/ProjectionRepositories.test.ts +++ b/apps/server/src/persistence/Layers/ProjectionRepositories.test.ts @@ -1,4 +1,4 @@ -import { ProjectId, ThreadId } from "@t3tools/contracts"; +import { ProjectId, ThreadId } from "@tero/contracts"; import { assert, it } from "@effect/vitest"; import { Effect, Layer, Option } from "effect"; import * as SqlClient from "effect/unstable/sql/SqlClient"; diff --git a/apps/server/src/persistence/Layers/ProjectionState.ts b/apps/server/src/persistence/Layers/ProjectionState.ts index 7c2b8110c7..33d72810d6 100644 --- a/apps/server/src/persistence/Layers/ProjectionState.ts +++ b/apps/server/src/persistence/Layers/ProjectionState.ts @@ -1,4 +1,4 @@ -import { NonNegativeInt } from "@t3tools/contracts"; +import { NonNegativeInt } from "@tero/contracts"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as SqlSchema from "effect/unstable/sql/SqlSchema"; import { Effect, Layer, Schema } from "effect"; diff --git a/apps/server/src/persistence/Layers/ProjectionThreadActivities.ts b/apps/server/src/persistence/Layers/ProjectionThreadActivities.ts index 8e88cfa785..8497d7521e 100644 --- a/apps/server/src/persistence/Layers/ProjectionThreadActivities.ts +++ b/apps/server/src/persistence/Layers/ProjectionThreadActivities.ts @@ -1,6 +1,6 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as SqlSchema from "effect/unstable/sql/SqlSchema"; -import { NonNegativeInt } from "@t3tools/contracts"; +import { NonNegativeInt } from "@tero/contracts"; import { Effect, Layer, Schema, Struct } from "effect"; import { toPersistenceDecodeError, toPersistenceSqlError } from "../Errors.ts"; diff --git a/apps/server/src/persistence/Layers/ProjectionThreadMessages.test.ts b/apps/server/src/persistence/Layers/ProjectionThreadMessages.test.ts index b761387d47..4ffd3efb46 100644 --- a/apps/server/src/persistence/Layers/ProjectionThreadMessages.test.ts +++ b/apps/server/src/persistence/Layers/ProjectionThreadMessages.test.ts @@ -1,4 +1,4 @@ -import { MessageId, ThreadId } from "@t3tools/contracts"; +import { MessageId, ThreadId } from "@tero/contracts"; import { assert, it } from "@effect/vitest"; import { Effect, Layer } from "effect"; diff --git a/apps/server/src/persistence/Layers/ProjectionThreadMessages.ts b/apps/server/src/persistence/Layers/ProjectionThreadMessages.ts index 6f0b25ddff..8068077b3a 100644 --- a/apps/server/src/persistence/Layers/ProjectionThreadMessages.ts +++ b/apps/server/src/persistence/Layers/ProjectionThreadMessages.ts @@ -1,7 +1,7 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as SqlSchema from "effect/unstable/sql/SqlSchema"; import { Effect, Layer, Schema, Struct } from "effect"; -import { ChatAttachment } from "@t3tools/contracts"; +import { ChatAttachment } from "@tero/contracts"; import { toPersistenceSqlError } from "../Errors.ts"; import { diff --git a/apps/server/src/persistence/Layers/ProjectionThreads.ts b/apps/server/src/persistence/Layers/ProjectionThreads.ts index 344f199092..44c6f2b8c4 100644 --- a/apps/server/src/persistence/Layers/ProjectionThreads.ts +++ b/apps/server/src/persistence/Layers/ProjectionThreads.ts @@ -11,7 +11,7 @@ import { ProjectionThreadRepository, type ProjectionThreadRepositoryShape, } from "../Services/ProjectionThreads.ts"; -import { ModelSelection } from "@t3tools/contracts"; +import { ModelSelection } from "@tero/contracts"; const ProjectionThreadDbRow = ProjectionThread.mapFields( Struct.assign({ diff --git a/apps/server/src/persistence/Layers/ProjectionTurns.ts b/apps/server/src/persistence/Layers/ProjectionTurns.ts index 9b6c9c5771..7058948bea 100644 --- a/apps/server/src/persistence/Layers/ProjectionTurns.ts +++ b/apps/server/src/persistence/Layers/ProjectionTurns.ts @@ -1,4 +1,4 @@ -import { OrchestrationCheckpointFile } from "@t3tools/contracts"; +import { OrchestrationCheckpointFile } from "@tero/contracts"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as SqlSchema from "effect/unstable/sql/SqlSchema"; import { Effect, Layer, Option, Schema, Struct } from "effect"; diff --git a/apps/server/src/persistence/Layers/ProviderSessionRuntime.ts b/apps/server/src/persistence/Layers/ProviderSessionRuntime.ts index da3e8bce90..4a27666783 100644 --- a/apps/server/src/persistence/Layers/ProviderSessionRuntime.ts +++ b/apps/server/src/persistence/Layers/ProviderSessionRuntime.ts @@ -1,4 +1,4 @@ -import { ThreadId } from "@t3tools/contracts"; +import { ThreadId } from "@tero/contracts"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as SqlSchema from "effect/unstable/sql/SqlSchema"; import { Effect, Layer, Option, Schema, Struct } from "effect"; diff --git a/apps/server/src/persistence/NodeSqliteClient.ts b/apps/server/src/persistence/NodeSqliteClient.ts index 1d6e22d9b0..8166ed0562 100644 --- a/apps/server/src/persistence/NodeSqliteClient.ts +++ b/apps/server/src/persistence/NodeSqliteClient.ts @@ -32,7 +32,9 @@ export type TypeId = "~local/sqlite-node/SqliteClient"; /** * SqliteClient - Effect service tag for the sqlite SQL client. */ -export const SqliteClient = ServiceMap.Service("t3/persistence/NodeSqliteClient"); +export const SqliteClient = ServiceMap.Service( + "tero/persistence/NodeSqliteClient", +); export interface SqliteClientConfig { readonly filename: string; diff --git a/apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts b/apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts index 0b8f8e9b52..e996eda967 100644 --- a/apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts +++ b/apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts @@ -14,7 +14,7 @@ import { OrchestrationCommandReceiptStatus, ProjectId, ThreadId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Option, Schema, ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -66,4 +66,6 @@ export interface OrchestrationCommandReceiptRepositoryShape { export class OrchestrationCommandReceiptRepository extends ServiceMap.Service< OrchestrationCommandReceiptRepository, OrchestrationCommandReceiptRepositoryShape ->()("t3/persistence/Services/OrchestrationCommandReceipts/OrchestrationCommandReceiptRepository") {} +>()( + "tero/persistence/Services/OrchestrationCommandReceipts/OrchestrationCommandReceiptRepository", +) {} diff --git a/apps/server/src/persistence/Services/OrchestrationEventStore.ts b/apps/server/src/persistence/Services/OrchestrationEventStore.ts index 24febd8fde..2083066699 100644 --- a/apps/server/src/persistence/Services/OrchestrationEventStore.ts +++ b/apps/server/src/persistence/Services/OrchestrationEventStore.ts @@ -9,7 +9,7 @@ * * @module OrchestrationEventStore */ -import { OrchestrationEvent } from "@t3tools/contracts"; +import { OrchestrationEvent } from "@tero/contracts"; import { ServiceMap } from "effect"; import type { Effect, Stream } from "effect"; @@ -67,4 +67,4 @@ export interface OrchestrationEventStoreShape { export class OrchestrationEventStore extends ServiceMap.Service< OrchestrationEventStore, OrchestrationEventStoreShape ->()("t3/persistence/Services/OrchestrationEventStore") {} +>()("tero/persistence/Services/OrchestrationEventStore") {} diff --git a/apps/server/src/persistence/Services/ProjectionCheckpoints.ts b/apps/server/src/persistence/Services/ProjectionCheckpoints.ts index 191d0af968..d68a103be3 100644 --- a/apps/server/src/persistence/Services/ProjectionCheckpoints.ts +++ b/apps/server/src/persistence/Services/ProjectionCheckpoints.ts @@ -15,7 +15,7 @@ import { OrchestrationCheckpointStatus, ThreadId, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Option, ServiceMap, Schema } from "effect"; import type { Effect } from "effect"; @@ -90,4 +90,4 @@ export interface ProjectionCheckpointRepositoryShape { export class ProjectionCheckpointRepository extends ServiceMap.Service< ProjectionCheckpointRepository, ProjectionCheckpointRepositoryShape ->()("t3/persistence/Services/ProjectionCheckpoints/ProjectionCheckpointRepository") {} +>()("tero/persistence/Services/ProjectionCheckpoints/ProjectionCheckpointRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionPendingApprovals.ts b/apps/server/src/persistence/Services/ProjectionPendingApprovals.ts index 8f4ec5eb4b..cf8337232b 100644 --- a/apps/server/src/persistence/Services/ProjectionPendingApprovals.ts +++ b/apps/server/src/persistence/Services/ProjectionPendingApprovals.ts @@ -13,7 +13,7 @@ import { ProjectionPendingApprovalStatus, ThreadId, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Option, Schema, ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -88,4 +88,4 @@ export interface ProjectionPendingApprovalRepositoryShape { export class ProjectionPendingApprovalRepository extends ServiceMap.Service< ProjectionPendingApprovalRepository, ProjectionPendingApprovalRepositoryShape ->()("t3/persistence/Services/ProjectionPendingApprovals/ProjectionPendingApprovalRepository") {} +>()("tero/persistence/Services/ProjectionPendingApprovals/ProjectionPendingApprovalRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionProjects.ts b/apps/server/src/persistence/Services/ProjectionProjects.ts index 996ffe6e7b..4e33e04c67 100644 --- a/apps/server/src/persistence/Services/ProjectionProjects.ts +++ b/apps/server/src/persistence/Services/ProjectionProjects.ts @@ -6,7 +6,7 @@ * * @module ProjectionProjectRepository */ -import { IsoDateTime, ModelSelection, ProjectId, ProjectScript } from "@t3tools/contracts"; +import { IsoDateTime, ModelSelection, ProjectId, ProjectScript } from "@tero/contracts"; import { Option, Schema, ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -76,4 +76,4 @@ export interface ProjectionProjectRepositoryShape { export class ProjectionProjectRepository extends ServiceMap.Service< ProjectionProjectRepository, ProjectionProjectRepositoryShape ->()("t3/persistence/Services/ProjectionProjects/ProjectionProjectRepository") {} +>()("tero/persistence/Services/ProjectionProjects/ProjectionProjectRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionState.ts b/apps/server/src/persistence/Services/ProjectionState.ts index e8ef9ceeb0..0904ef1132 100644 --- a/apps/server/src/persistence/Services/ProjectionState.ts +++ b/apps/server/src/persistence/Services/ProjectionState.ts @@ -6,7 +6,7 @@ * * @module ProjectionStateRepository */ -import { IsoDateTime, NonNegativeInt } from "@t3tools/contracts"; +import { IsoDateTime, NonNegativeInt } from "@tero/contracts"; import { Option, Schema, ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -61,4 +61,4 @@ export interface ProjectionStateRepositoryShape { export class ProjectionStateRepository extends ServiceMap.Service< ProjectionStateRepository, ProjectionStateRepositoryShape ->()("t3/persistence/Services/ProjectionState/ProjectionStateRepository") {} +>()("tero/persistence/Services/ProjectionState/ProjectionStateRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionThreadActivities.ts b/apps/server/src/persistence/Services/ProjectionThreadActivities.ts index 586ae3eb4a..1a8eae5a12 100644 --- a/apps/server/src/persistence/Services/ProjectionThreadActivities.ts +++ b/apps/server/src/persistence/Services/ProjectionThreadActivities.ts @@ -13,7 +13,7 @@ import { OrchestrationThreadActivityTone, ThreadId, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Schema, ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -80,4 +80,4 @@ export interface ProjectionThreadActivityRepositoryShape { export class ProjectionThreadActivityRepository extends ServiceMap.Service< ProjectionThreadActivityRepository, ProjectionThreadActivityRepositoryShape ->()("t3/persistence/Services/ProjectionThreadActivities/ProjectionThreadActivityRepository") {} +>()("tero/persistence/Services/ProjectionThreadActivities/ProjectionThreadActivityRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionThreadMessages.ts b/apps/server/src/persistence/Services/ProjectionThreadMessages.ts index 00b1d399c6..b00308d223 100644 --- a/apps/server/src/persistence/Services/ProjectionThreadMessages.ts +++ b/apps/server/src/persistence/Services/ProjectionThreadMessages.ts @@ -13,7 +13,7 @@ import { ThreadId, TurnId, IsoDateTime, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Schema, ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -78,4 +78,4 @@ export interface ProjectionThreadMessageRepositoryShape { export class ProjectionThreadMessageRepository extends ServiceMap.Service< ProjectionThreadMessageRepository, ProjectionThreadMessageRepositoryShape ->()("t3/persistence/Services/ProjectionThreadMessages/ProjectionThreadMessageRepository") {} +>()("tero/persistence/Services/ProjectionThreadMessages/ProjectionThreadMessageRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionThreadProposedPlans.ts b/apps/server/src/persistence/Services/ProjectionThreadProposedPlans.ts index d141a11bb9..904be2c03c 100644 --- a/apps/server/src/persistence/Services/ProjectionThreadProposedPlans.ts +++ b/apps/server/src/persistence/Services/ProjectionThreadProposedPlans.ts @@ -4,7 +4,7 @@ import { ThreadId, TrimmedNonEmptyString, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Schema, ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -50,5 +50,5 @@ export class ProjectionThreadProposedPlanRepository extends ServiceMap.Service< ProjectionThreadProposedPlanRepository, ProjectionThreadProposedPlanRepositoryShape >()( - "t3/persistence/Services/ProjectionThreadProposedPlans/ProjectionThreadProposedPlanRepository", + "tero/persistence/Services/ProjectionThreadProposedPlans/ProjectionThreadProposedPlanRepository", ) {} diff --git a/apps/server/src/persistence/Services/ProjectionThreadSessions.ts b/apps/server/src/persistence/Services/ProjectionThreadSessions.ts index 537ee10bee..98fffc3693 100644 --- a/apps/server/src/persistence/Services/ProjectionThreadSessions.ts +++ b/apps/server/src/persistence/Services/ProjectionThreadSessions.ts @@ -12,7 +12,7 @@ import { OrchestrationSessionStatus, ThreadId, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Option, Schema, ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -71,4 +71,4 @@ export interface ProjectionThreadSessionRepositoryShape { export class ProjectionThreadSessionRepository extends ServiceMap.Service< ProjectionThreadSessionRepository, ProjectionThreadSessionRepositoryShape ->()("t3/persistence/Services/ProjectionThreadSessions/ProjectionThreadSessionRepository") {} +>()("tero/persistence/Services/ProjectionThreadSessions/ProjectionThreadSessionRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionThreads.ts b/apps/server/src/persistence/Services/ProjectionThreads.ts index cf4bd55a81..a0ad473d6f 100644 --- a/apps/server/src/persistence/Services/ProjectionThreads.ts +++ b/apps/server/src/persistence/Services/ProjectionThreads.ts @@ -14,7 +14,7 @@ import { RuntimeMode, ThreadId, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Option, Schema, ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -92,4 +92,4 @@ export interface ProjectionThreadRepositoryShape { export class ProjectionThreadRepository extends ServiceMap.Service< ProjectionThreadRepository, ProjectionThreadRepositoryShape ->()("t3/persistence/Services/ProjectionThreads/ProjectionThreadRepository") {} +>()("tero/persistence/Services/ProjectionThreads/ProjectionThreadRepository") {} diff --git a/apps/server/src/persistence/Services/ProjectionTurns.ts b/apps/server/src/persistence/Services/ProjectionTurns.ts index 95dab450bf..92443f0b2a 100644 --- a/apps/server/src/persistence/Services/ProjectionTurns.ts +++ b/apps/server/src/persistence/Services/ProjectionTurns.ts @@ -16,7 +16,7 @@ import { OrchestrationCheckpointStatus, ThreadId, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Option, Schema, ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -165,4 +165,4 @@ export interface ProjectionTurnRepositoryShape { export class ProjectionTurnRepository extends ServiceMap.Service< ProjectionTurnRepository, ProjectionTurnRepositoryShape ->()("t3/persistence/Services/ProjectionTurns/ProjectionTurnRepository") {} +>()("tero/persistence/Services/ProjectionTurns/ProjectionTurnRepository") {} diff --git a/apps/server/src/persistence/Services/ProviderSessionRuntime.ts b/apps/server/src/persistence/Services/ProviderSessionRuntime.ts index 885a9dd5f1..8553093288 100644 --- a/apps/server/src/persistence/Services/ProviderSessionRuntime.ts +++ b/apps/server/src/persistence/Services/ProviderSessionRuntime.ts @@ -5,12 +5,7 @@ * * @module ProviderSessionRuntimeRepository */ -import { - IsoDateTime, - ProviderSessionRuntimeStatus, - RuntimeMode, - ThreadId, -} from "@t3tools/contracts"; +import { IsoDateTime, ProviderSessionRuntimeStatus, RuntimeMode, ThreadId } from "@tero/contracts"; import { Option, Schema, ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -78,4 +73,4 @@ export interface ProviderSessionRuntimeRepositoryShape { export class ProviderSessionRuntimeRepository extends ServiceMap.Service< ProviderSessionRuntimeRepository, ProviderSessionRuntimeRepositoryShape ->()("t3/persistence/Services/ProviderSessionRuntime/ProviderSessionRuntimeRepository") {} +>()("tero/persistence/Services/ProviderSessionRuntime/ProviderSessionRuntimeRepository") {} diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts index d4ed6fba19..39280b27e6 100644 --- a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts +++ b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts @@ -10,12 +10,7 @@ import type { SDKMessage, SDKUserMessage, } from "@anthropic-ai/claude-agent-sdk"; -import { - ApprovalRequestId, - ProviderItemId, - ProviderRuntimeEvent, - ThreadId, -} from "@t3tools/contracts"; +import { ApprovalRequestId, ProviderItemId, ProviderRuntimeEvent, ThreadId } from "@tero/contracts"; import { assert, describe, it } from "@effect/vitest"; import { Effect, Fiber, Layer, Random, Stream } from "effect"; diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.ts b/apps/server/src/provider/Layers/ClaudeAdapter.ts index af88fa634a..f7c612e186 100644 --- a/apps/server/src/provider/Layers/ClaudeAdapter.ts +++ b/apps/server/src/provider/Layers/ClaudeAdapter.ts @@ -39,13 +39,13 @@ import { TurnId, type UserInputQuestion, ClaudeCodeEffort, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { hasEffortLevel, applyClaudePromptEffortPrefix, getModelCapabilities, trimOrNull, -} from "@t3tools/shared/model"; +} from "@tero/shared/model"; import { Cause, DateTime, diff --git a/apps/server/src/provider/Layers/CodexAdapter.test.ts b/apps/server/src/provider/Layers/CodexAdapter.test.ts index 3017235f1e..9828e0627e 100644 --- a/apps/server/src/provider/Layers/CodexAdapter.test.ts +++ b/apps/server/src/provider/Layers/CodexAdapter.test.ts @@ -10,7 +10,7 @@ import { type ProviderUserInputAnswers, ThreadId, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { afterAll, it, vi } from "@effect/vitest"; diff --git a/apps/server/src/provider/Layers/CodexAdapter.ts b/apps/server/src/provider/Layers/CodexAdapter.ts index ca9c52cf8e..807179ecb7 100644 --- a/apps/server/src/provider/Layers/CodexAdapter.ts +++ b/apps/server/src/provider/Layers/CodexAdapter.ts @@ -20,7 +20,7 @@ import { ProviderItemId, ThreadId, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect, FileSystem, Layer, Queue, Schema, ServiceMap, Stream } from "effect"; import { diff --git a/apps/server/src/provider/Layers/EventNdjsonLogger.test.ts b/apps/server/src/provider/Layers/EventNdjsonLogger.test.ts index 258b50d6f2..7370d33bbd 100644 --- a/apps/server/src/provider/Layers/EventNdjsonLogger.test.ts +++ b/apps/server/src/provider/Layers/EventNdjsonLogger.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import { ThreadId } from "@t3tools/contracts"; +import { ThreadId } from "@tero/contracts"; import { assert, describe, it } from "@effect/vitest"; import { Effect } from "effect"; diff --git a/apps/server/src/provider/Layers/EventNdjsonLogger.ts b/apps/server/src/provider/Layers/EventNdjsonLogger.ts index 28b7ecf611..af9b589afc 100644 --- a/apps/server/src/provider/Layers/EventNdjsonLogger.ts +++ b/apps/server/src/provider/Layers/EventNdjsonLogger.ts @@ -8,8 +8,8 @@ import fs from "node:fs"; import path from "node:path"; -import type { ThreadId } from "@t3tools/contracts"; -import { RotatingFileSink } from "@t3tools/shared/logging"; +import type { ThreadId } from "@tero/contracts"; +import { RotatingFileSink } from "@tero/shared/logging"; import { Effect, Exit, Logger, Scope } from "effect"; import { toSafeThreadAttachmentSegment } from "../../attachmentStore.ts"; diff --git a/apps/server/src/provider/Layers/ProviderAdapterRegistry.test.ts b/apps/server/src/provider/Layers/ProviderAdapterRegistry.test.ts index db0293f0fe..f72d542b2d 100644 --- a/apps/server/src/provider/Layers/ProviderAdapterRegistry.test.ts +++ b/apps/server/src/provider/Layers/ProviderAdapterRegistry.test.ts @@ -1,4 +1,4 @@ -import type { ProviderKind } from "@t3tools/contracts"; +import type { ProviderKind } from "@tero/contracts"; import { it, assert, vi } from "@effect/vitest"; import { assertFailure } from "@effect/vitest/utils"; diff --git a/apps/server/src/provider/Layers/ProviderHealth.ts b/apps/server/src/provider/Layers/ProviderHealth.ts index cbb97a807e..f02ab891cd 100644 --- a/apps/server/src/provider/Layers/ProviderHealth.ts +++ b/apps/server/src/provider/Layers/ProviderHealth.ts @@ -13,7 +13,7 @@ import type { ServerProviderAuthStatus, ServerProviderStatus, ServerProviderStatusState, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Array, Effect, Fiber, FileSystem, Layer, Option, Path, Result, Stream } from "effect"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; diff --git a/apps/server/src/provider/Layers/ProviderService.test.ts b/apps/server/src/provider/Layers/ProviderService.test.ts index 7af85aafd2..c55570f284 100644 --- a/apps/server/src/provider/Layers/ProviderService.test.ts +++ b/apps/server/src/provider/Layers/ProviderService.test.ts @@ -8,7 +8,7 @@ import type { ProviderSendTurnInput, ProviderSession, ProviderTurnStartResult, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { ApprovalRequestId, EventId, @@ -16,7 +16,7 @@ import { ProviderSessionStartInput, ThreadId, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { it, assert, vi } from "@effect/vitest"; import { assertFailure } from "@effect/vitest/utils"; diff --git a/apps/server/src/provider/Layers/ProviderService.ts b/apps/server/src/provider/Layers/ProviderService.ts index 364e30fd0e..a7283f8528 100644 --- a/apps/server/src/provider/Layers/ProviderService.ts +++ b/apps/server/src/provider/Layers/ProviderService.ts @@ -21,7 +21,7 @@ import { ProviderStopSessionInput, type ProviderRuntimeEvent, type ProviderSession, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect, Layer, Option, PubSub, Queue, Schema, SchemaIssue, Stream } from "effect"; import { ProviderValidationError } from "../Errors.ts"; diff --git a/apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts b/apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts index d23b247f21..0d1e965490 100644 --- a/apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts +++ b/apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts @@ -3,7 +3,7 @@ import os from "node:os"; import path from "node:path"; import * as NodeServices from "@effect/platform-node/NodeServices"; -import { ThreadId } from "@t3tools/contracts"; +import { ThreadId } from "@tero/contracts"; import { it, assert } from "@effect/vitest"; import { assertFailure, assertSome } from "@effect/vitest/utils"; import { Effect, Layer, Option } from "effect"; diff --git a/apps/server/src/provider/Layers/ProviderSessionDirectory.ts b/apps/server/src/provider/Layers/ProviderSessionDirectory.ts index 961c63d696..30f5bc96db 100644 --- a/apps/server/src/provider/Layers/ProviderSessionDirectory.ts +++ b/apps/server/src/provider/Layers/ProviderSessionDirectory.ts @@ -1,4 +1,4 @@ -import { type ProviderKind, type ThreadId } from "@t3tools/contracts"; +import { type ProviderKind, type ThreadId } from "@tero/contracts"; import { Effect, Layer, Option } from "effect"; import { ProviderSessionRuntimeRepository } from "../../persistence/Services/ProviderSessionRuntime.ts"; diff --git a/apps/server/src/provider/Services/ClaudeAdapter.ts b/apps/server/src/provider/Services/ClaudeAdapter.ts index 3a3f616ea5..42e96479f5 100644 --- a/apps/server/src/provider/Services/ClaudeAdapter.ts +++ b/apps/server/src/provider/Services/ClaudeAdapter.ts @@ -26,5 +26,5 @@ export interface ClaudeAdapterShape extends ProviderAdapterShape()( - "t3/provider/Services/ClaudeAdapter", + "tero/provider/Services/ClaudeAdapter", ) {} diff --git a/apps/server/src/provider/Services/CodexAdapter.ts b/apps/server/src/provider/Services/CodexAdapter.ts index c9f944bb96..8282764b81 100644 --- a/apps/server/src/provider/Services/CodexAdapter.ts +++ b/apps/server/src/provider/Services/CodexAdapter.ts @@ -26,5 +26,5 @@ export interface CodexAdapterShape extends ProviderAdapterShape()( - "t3/provider/Services/CodexAdapter", + "tero/provider/Services/CodexAdapter", ) {} diff --git a/apps/server/src/provider/Services/ProviderAdapter.ts b/apps/server/src/provider/Services/ProviderAdapter.ts index 38a05f7574..6bcb1088ef 100644 --- a/apps/server/src/provider/Services/ProviderAdapter.ts +++ b/apps/server/src/provider/Services/ProviderAdapter.ts @@ -19,7 +19,7 @@ import type { ThreadId, ProviderTurnStartResult, TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import type { Effect } from "effect"; import type { Stream } from "effect"; diff --git a/apps/server/src/provider/Services/ProviderAdapterRegistry.ts b/apps/server/src/provider/Services/ProviderAdapterRegistry.ts index 490c4d3d14..84311af245 100644 --- a/apps/server/src/provider/Services/ProviderAdapterRegistry.ts +++ b/apps/server/src/provider/Services/ProviderAdapterRegistry.ts @@ -7,7 +7,7 @@ * * @module ProviderAdapterRegistry */ -import type { ProviderKind } from "@t3tools/contracts"; +import type { ProviderKind } from "@tero/contracts"; import { ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -37,6 +37,6 @@ export interface ProviderAdapterRegistryShape { export class ProviderAdapterRegistry extends ServiceMap.Service< ProviderAdapterRegistry, ProviderAdapterRegistryShape ->()("t3/provider/Services/ProviderAdapterRegistry") {} +>()("tero/provider/Services/ProviderAdapterRegistry") {} // Dummy comment for workflow testing. diff --git a/apps/server/src/provider/Services/ProviderHealth.ts b/apps/server/src/provider/Services/ProviderHealth.ts index ec3b2d318d..0455cee331 100644 --- a/apps/server/src/provider/Services/ProviderHealth.ts +++ b/apps/server/src/provider/Services/ProviderHealth.ts @@ -6,7 +6,7 @@ * * @module ProviderHealth */ -import type { ServerProviderStatus } from "@t3tools/contracts"; +import type { ServerProviderStatus } from "@tero/contracts"; import { ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -18,5 +18,5 @@ export interface ProviderHealthShape { } export class ProviderHealth extends ServiceMap.Service()( - "t3/provider/Services/ProviderHealth", + "tero/provider/Services/ProviderHealth", ) {} diff --git a/apps/server/src/provider/Services/ProviderService.ts b/apps/server/src/provider/Services/ProviderService.ts index ebfe8c8ab1..d1dc99f82c 100644 --- a/apps/server/src/provider/Services/ProviderService.ts +++ b/apps/server/src/provider/Services/ProviderService.ts @@ -23,7 +23,7 @@ import type { ProviderStopSessionInput, ThreadId, ProviderTurnStartResult, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { ServiceMap } from "effect"; import type { Effect, Stream } from "effect"; @@ -111,5 +111,5 @@ export interface ProviderServiceShape { * ProviderService - Service tag for provider orchestration. */ export class ProviderService extends ServiceMap.Service()( - "t3/provider/Services/ProviderService", + "tero/provider/Services/ProviderService", ) {} diff --git a/apps/server/src/provider/Services/ProviderSessionDirectory.ts b/apps/server/src/provider/Services/ProviderSessionDirectory.ts index 3a374976b0..8e4e69ee22 100644 --- a/apps/server/src/provider/Services/ProviderSessionDirectory.ts +++ b/apps/server/src/provider/Services/ProviderSessionDirectory.ts @@ -3,7 +3,7 @@ import type { ProviderSessionRuntimeStatus, RuntimeMode, ThreadId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Option, ServiceMap } from "effect"; import type { Effect } from "effect"; @@ -54,4 +54,4 @@ export interface ProviderSessionDirectoryShape { export class ProviderSessionDirectory extends ServiceMap.Service< ProviderSessionDirectory, ProviderSessionDirectoryShape ->()("t3/provider/Services/ProviderSessionDirectory") {} +>()("tero/provider/Services/ProviderSessionDirectory") {} diff --git a/apps/server/src/telemetry/Services/AnalyticsService.ts b/apps/server/src/telemetry/Services/AnalyticsService.ts index 64f8c8de55..df4dc828d0 100644 --- a/apps/server/src/telemetry/Services/AnalyticsService.ts +++ b/apps/server/src/telemetry/Services/AnalyticsService.ts @@ -24,7 +24,7 @@ export interface AnalyticsServiceShape { } export class AnalyticsService extends ServiceMap.Service()( - "t3/telemetry/Services/AnalyticsService", + "tero/telemetry/Services/AnalyticsService", ) { static readonly layerTest = Layer.succeed(AnalyticsService, { record: () => Effect.void, diff --git a/apps/server/src/terminal/Layers/Manager.test.ts b/apps/server/src/terminal/Layers/Manager.test.ts index 53ee7d74f1..9a6c41d7e5 100644 --- a/apps/server/src/terminal/Layers/Manager.test.ts +++ b/apps/server/src/terminal/Layers/Manager.test.ts @@ -7,7 +7,7 @@ import { type TerminalEvent, type TerminalOpenInput, type TerminalRestartInput, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { afterEach, describe, expect, it } from "vitest"; import { diff --git a/apps/server/src/terminal/Layers/Manager.ts b/apps/server/src/terminal/Layers/Manager.ts index b5085220c2..ef8ff73c34 100644 --- a/apps/server/src/terminal/Layers/Manager.ts +++ b/apps/server/src/terminal/Layers/Manager.ts @@ -12,7 +12,7 @@ import { TerminalWriteInput, type TerminalEvent, type TerminalSessionSnapshot, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Effect, Encoding, Layer, Schema } from "effect"; import { createLogger } from "../../logger"; @@ -446,6 +446,9 @@ function toSessionKey(threadId: string, terminalId: string): string { function shouldExcludeTerminalEnvKey(key: string): boolean { const normalizedKey = key.toUpperCase(); + if (normalizedKey.startsWith("TERO_")) { + return true; + } if (normalizedKey.startsWith("T3CODE_")) { return true; } diff --git a/apps/server/src/terminal/Services/Manager.ts b/apps/server/src/terminal/Services/Manager.ts index c2539da4b6..32f4941d1c 100644 --- a/apps/server/src/terminal/Services/Manager.ts +++ b/apps/server/src/terminal/Services/Manager.ts @@ -16,7 +16,7 @@ import { TerminalSessionSnapshot, TerminalSessionStatus, TerminalWriteInput, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { PtyProcess } from "./PTY"; import { Effect, Schema, ServiceMap } from "effect"; @@ -115,5 +115,5 @@ export interface TerminalManagerShape { * TerminalManager - Service tag for terminal session orchestration. */ export class TerminalManager extends ServiceMap.Service()( - "t3/terminal/Services/Manager/TerminalManager", + "tero/terminal/Services/Manager/TerminalManager", ) {} diff --git a/apps/server/src/terminal/Services/PTY.ts b/apps/server/src/terminal/Services/PTY.ts index dc164b2621..336a378d05 100644 --- a/apps/server/src/terminal/Services/PTY.ts +++ b/apps/server/src/terminal/Services/PTY.ts @@ -54,5 +54,5 @@ export interface PtyAdapterShape { * PtyAdapter - Service tag for PTY process integration. */ export class PtyAdapter extends ServiceMap.Service()( - "t3/terminal/Services/PTY/PtyAdapter", + "tero/terminal/Services/PTY/PtyAdapter", ) {} diff --git a/apps/server/src/workspaceEntries.ts b/apps/server/src/workspaceEntries.ts index 684b005e83..7f9d13cb75 100644 --- a/apps/server/src/workspaceEntries.ts +++ b/apps/server/src/workspaceEntries.ts @@ -7,7 +7,7 @@ import { ProjectEntry, ProjectSearchEntriesInput, ProjectSearchEntriesResult, -} from "@t3tools/contracts"; +} from "@tero/contracts"; const WORKSPACE_CACHE_TTL_MS = 15_000; const WORKSPACE_CACHE_MAX_KEYS = 4; diff --git a/apps/server/src/wsServer.test.ts b/apps/server/src/wsServer.test.ts index ff95b54112..9f29de629b 100644 --- a/apps/server/src/wsServer.test.ts +++ b/apps/server/src/wsServer.test.ts @@ -30,7 +30,7 @@ import { type WsPushChannel, type WsPushMessage, type WsPush, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { compileResolvedKeybindingRule, DEFAULT_KEYBINDINGS } from "./keybindings"; import type { TerminalClearInput, @@ -40,7 +40,7 @@ import type { TerminalResizeInput, TerminalSessionSnapshot, TerminalWriteInput, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { TerminalManager, type TerminalManagerShape } from "./terminal/Services/Manager"; import { makeSqlitePersistenceLive, SqlitePersistenceMemory } from "./persistence/Layers/Sqlite"; import { SqlClient, SqlError } from "effect/unstable/sql"; @@ -691,8 +691,8 @@ describe("WebSocket Server", () => { expect.objectContaining({ cwd: "/test/bootstrap-workspace", projectName: "bootstrap-workspace", - bootstrapProjectId: expect.any(String), - bootstrapThreadId: expect.any(String), + startupProjectId: expect.any(String), + startupThreadId: expect.any(String), }), ); @@ -720,15 +720,15 @@ describe("WebSocket Server", () => { worktreePath: string | null; }>; }; - const bootstrapProjectId = (welcome.data as { bootstrapProjectId?: string }).bootstrapProjectId; - const bootstrapThreadId = (welcome.data as { bootstrapThreadId?: string }).bootstrapThreadId; - expect(bootstrapProjectId).toBeDefined(); - expect(bootstrapThreadId).toBeDefined(); + const startupProjectId = (welcome.data as { startupProjectId?: string }).startupProjectId; + const startupThreadId = (welcome.data as { startupThreadId?: string }).startupThreadId; + expect(startupProjectId).toBeDefined(); + expect(startupThreadId).toBeDefined(); expect(snapshot.projects).toEqual( expect.arrayContaining([ expect.objectContaining({ - id: bootstrapProjectId, + id: startupProjectId, workspaceRoot: "/test/bootstrap-workspace", title: "bootstrap-workspace", defaultModelSelection: { @@ -741,8 +741,8 @@ describe("WebSocket Server", () => { expect(snapshot.threads).toEqual( expect.arrayContaining([ expect.objectContaining({ - id: bootstrapThreadId, - projectId: bootstrapProjectId, + id: startupThreadId, + projectId: startupProjectId, title: "New thread", modelSelection: { provider: "codex", @@ -775,12 +775,12 @@ describe("WebSocket Server", () => { const [firstWs, firstWelcome] = await connectAndAwaitWelcome(port); connections.push(firstWs); - const firstBootstrapProjectId = (firstWelcome.data as { bootstrapProjectId?: string }) - .bootstrapProjectId; - const firstBootstrapThreadId = (firstWelcome.data as { bootstrapThreadId?: string }) - .bootstrapThreadId; - expect(firstBootstrapProjectId).toBeDefined(); - expect(firstBootstrapThreadId).toBeDefined(); + const firstStartupProjectId = (firstWelcome.data as { startupProjectId?: string }) + .startupProjectId; + const firstStartupThreadId = (firstWelcome.data as { startupThreadId?: string }) + .startupThreadId; + expect(firstStartupProjectId).toBeDefined(); + expect(firstStartupThreadId).toBeDefined(); firstWs.close(); await closeTestServer(); @@ -802,8 +802,61 @@ describe("WebSocket Server", () => { expect.objectContaining({ cwd, projectName: "bootstrap-existing", - bootstrapProjectId: firstBootstrapProjectId, - bootstrapThreadId: firstBootstrapThreadId, + startupProjectId: firstStartupProjectId, + startupThreadId: firstStartupThreadId, + }), + ); + }); + + it("includes startup ids for the latest existing thread even without cwd auto-bootstrap", async () => { + const baseDir = makeTempDir("t3code-state-startup-existing-"); + const { dbPath } = deriveServerPathsSync(baseDir, undefined); + const persistenceLayer = makeSqlitePersistenceLive(dbPath).pipe( + Layer.provide(NodeServices.layer), + ); + const cwd = "/test/startup-existing"; + + server = await createTestServer({ + cwd, + baseDir, + persistenceLayer, + autoBootstrapProjectFromCwd: true, + }); + let addr = server.address(); + let port = typeof addr === "object" && addr !== null ? addr.port : 0; + expect(port).toBeGreaterThan(0); + + const [firstWs, firstWelcome] = await connectAndAwaitWelcome(port); + connections.push(firstWs); + const firstStartupProjectId = (firstWelcome.data as { startupProjectId?: string }) + .startupProjectId; + const firstStartupThreadId = (firstWelcome.data as { startupThreadId?: string }) + .startupThreadId; + expect(firstStartupProjectId).toBeDefined(); + expect(firstStartupThreadId).toBeDefined(); + + firstWs.close(); + await closeTestServer(); + server = null; + + server = await createTestServer({ + cwd: "/test/no-bootstrap-here", + baseDir, + persistenceLayer, + autoBootstrapProjectFromCwd: false, + }); + addr = server.address(); + port = typeof addr === "object" && addr !== null ? addr.port : 0; + expect(port).toBeGreaterThan(0); + + const [secondWs, secondWelcome] = await connectAndAwaitWelcome(port); + connections.push(secondWs); + expect(secondWelcome.data).toEqual( + expect.objectContaining({ + cwd: "/test/no-bootstrap-here", + projectName: "no-bootstrap-here", + startupProjectId: firstStartupProjectId, + startupThreadId: firstStartupThreadId, }), ); }); diff --git a/apps/server/src/wsServer.ts b/apps/server/src/wsServer.ts index bcb3850e7a..0838c6f69e 100644 --- a/apps/server/src/wsServer.ts +++ b/apps/server/src/wsServer.ts @@ -14,6 +14,7 @@ import { CommandId, DEFAULT_PROVIDER_INTERACTION_MODE, type ClientOrchestrationCommand, + type OrchestrationReadModel, type OrchestrationCommand, ORCHESTRATION_WS_CHANNELS, ORCHESTRATION_WS_METHODS, @@ -26,7 +27,7 @@ import { type WsResponse as WsResponseMessage, WsResponse, type WsPushEnvelopeBase, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer"; import { Cause, @@ -77,7 +78,7 @@ import { AnalyticsService } from "./telemetry/Services/AnalyticsService.ts"; import { expandHomePath } from "./os-jank.ts"; import { makeServerPushBus } from "./wsServer/pushBus.ts"; import { makeServerReadiness } from "./wsServer/readiness.ts"; -import { decodeJsonResult, formatSchemaError } from "@t3tools/shared/schemaJson"; +import { decodeJsonResult, formatSchemaError } from "@tero/shared/schemaJson"; /** * ServerShape - Service API for server lifecycle control. @@ -101,7 +102,7 @@ export interface ServerShape { /** * Server - Service tag for HTTP/WebSocket lifecycle management. */ -export class Server extends ServiceMap.Service()("t3/wsServer/Server") {} +export class Server extends ServiceMap.Service()("tero/wsServer/Server") {} const isServerNotRunningError = (error: Error): boolean => { const maybeCode = (error as NodeJS.ErrnoException).code; @@ -231,6 +232,53 @@ class RouteRequestError extends Schema.TaggedErrorClass()("Ro message: Schema.String, }) {} +function sortThreadsByUpdatedAtDesc< + T extends { + readonly updatedAt: string; + }, +>(threads: ReadonlyArray): Array { + return [...threads].toSorted( + (left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt), + ); +} + +function resolveWelcomeSelection(params: { + readonly snapshot: OrchestrationReadModel; + readonly cwd: string; + readonly autoBootstrapProjectFromCwd: boolean; +}): { + readonly startupProjectId?: ProjectId; + readonly startupThreadId?: ThreadId; +} { + const liveProjects = params.snapshot.projects.filter((project) => project.deletedAt === null); + const liveThreads = sortThreadsByUpdatedAtDesc( + params.snapshot.threads.filter((thread) => thread.deletedAt === null), + ); + + if (params.autoBootstrapProjectFromCwd) { + const cwdProject = liveProjects.find((project) => project.workspaceRoot === params.cwd); + if (cwdProject) { + const cwdThread = liveThreads.find((thread) => thread.projectId === cwdProject.id); + if (cwdThread) { + return { + startupProjectId: cwdProject.id, + startupThreadId: cwdThread.id, + }; + } + } + } + + const latestThread = liveThreads[0]; + if (!latestThread) { + return {}; + } + + return { + startupProjectId: latestThread.projectId, + startupThreadId: latestThread.id, + }; +} + export const createServer = Effect.fn(function* (): Effect.fn.Return< http.Server, ServerLifecycleError, @@ -621,9 +669,6 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return< yield* Scope.provide(orchestrationReactor.start, subscriptionsScope); yield* readiness.markOrchestrationSubscriptionsReady; - let welcomeBootstrapProjectId: ProjectId | undefined; - let welcomeBootstrapThreadId: ThreadId | undefined; - if (autoBootstrapProjectFromCwd) { yield* Effect.gen(function* () { const snapshot = yield* projectionReadModelQuery.getSnapshot(); @@ -677,11 +722,6 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return< worktreePath: null, createdAt, }); - welcomeBootstrapProjectId = bootstrapProjectId; - welcomeBootstrapThreadId = threadId; - } else { - welcomeBootstrapProjectId = bootstrapProjectId; - welcomeBootstrapThreadId = existingThread.id; } }).pipe( Effect.mapError( @@ -968,18 +1008,31 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return< wss.on("connection", (ws) => { const segments = cwd.split(/[/\\]/).filter(Boolean); const projectName = segments[segments.length - 1] ?? "project"; - - const welcomeData = { - cwd, - projectName, - ...(welcomeBootstrapProjectId ? { bootstrapProjectId: welcomeBootstrapProjectId } : {}), - ...(welcomeBootstrapThreadId ? { bootstrapThreadId: welcomeBootstrapThreadId } : {}), - }; // Send welcome before adding to broadcast set so publishAll calls // cannot reach this client before the welcome arrives. void runPromise( readiness.awaitServerReady.pipe( - Effect.flatMap(() => pushBus.publishClient(ws, WS_CHANNELS.serverWelcome, welcomeData)), + Effect.flatMap(() => projectionReadModelQuery.getSnapshot()), + Effect.map((snapshot) => { + const welcomeSelection = resolveWelcomeSelection({ + snapshot, + cwd, + autoBootstrapProjectFromCwd, + }); + return { + cwd, + projectName, + ...(welcomeSelection.startupProjectId + ? { startupProjectId: welcomeSelection.startupProjectId } + : {}), + ...(welcomeSelection.startupThreadId + ? { startupThreadId: welcomeSelection.startupThreadId } + : {}), + }; + }), + Effect.flatMap((welcomeData) => + pushBus.publishClient(ws, WS_CHANNELS.serverWelcome, welcomeData), + ), Effect.flatMap((delivered) => delivered ? Ref.update(clients, (clients) => clients.add(ws)) : Effect.void, ), diff --git a/apps/server/src/wsServer/pushBus.test.ts b/apps/server/src/wsServer/pushBus.test.ts index 172944607b..0620e93cae 100644 --- a/apps/server/src/wsServer/pushBus.test.ts +++ b/apps/server/src/wsServer/pushBus.test.ts @@ -2,7 +2,7 @@ import type { WebSocket } from "ws"; import { it } from "@effect/vitest"; import { describe, expect } from "vitest"; import { Effect, Ref } from "effect"; -import { WS_CHANNELS } from "@t3tools/contracts"; +import { WS_CHANNELS } from "@tero/contracts"; import { makeServerPushBus } from "./pushBus"; diff --git a/apps/server/src/wsServer/pushBus.ts b/apps/server/src/wsServer/pushBus.ts index c2cd302f23..3cb4c18453 100644 --- a/apps/server/src/wsServer/pushBus.ts +++ b/apps/server/src/wsServer/pushBus.ts @@ -3,7 +3,7 @@ import { type WsPushChannel, type WsPushData, type WsPushEnvelopeBase, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Deferred, Effect, Queue, Ref, Schema } from "effect"; import type { Scope } from "effect"; import type { WebSocket } from "ws"; diff --git a/apps/server/tsdown.config.ts b/apps/server/tsdown.config.ts index f89bc7d3d7..46528e9f4a 100644 --- a/apps/server/tsdown.config.ts +++ b/apps/server/tsdown.config.ts @@ -9,7 +9,7 @@ export default defineConfig({ outDir: "dist", sourcemap: true, clean: true, - noExternal: (id) => id.startsWith("@t3tools/"), + noExternal: (id) => id.startsWith("@tero/"), inlineOnly: false, banner: { js: "#!/usr/bin/env node\n", diff --git a/apps/web/package.json b/apps/web/package.json index 5127faf827..b4e6a322f5 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,5 +1,5 @@ { - "name": "@t3tools/web", + "name": "@tero/web", "version": "0.0.14", "private": true, "type": "module", @@ -22,12 +22,12 @@ "@formkit/auto-animate": "^0.9.0", "@lexical/react": "^0.41.0", "@pierre/diffs": "^1.1.0-beta.16", - "@t3tools/contracts": "workspace:*", - "@t3tools/shared": "workspace:*", "@tanstack/react-pacer": "^0.19.4", "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.160.2", "@tanstack/react-virtual": "^3.13.18", + "@tero/contracts": "workspace:*", + "@tero/shared": "workspace:*", "@xterm/addon-fit": "^0.11.0", "@xterm/xterm": "^6.0.0", "class-variance-authority": "^0.7.1", diff --git a/apps/web/src/appSettings.ts b/apps/web/src/appSettings.ts index be9c376989..f1d49f8374 100644 --- a/apps/web/src/appSettings.ts +++ b/apps/web/src/appSettings.ts @@ -4,17 +4,17 @@ import { TrimmedNonEmptyString, type ProviderKind, type ProviderStartOptions, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { getDefaultModel, getModelOptions, normalizeModelSlug, resolveSelectableModel, -} from "@t3tools/shared/model"; +} from "@tero/shared/model"; import { useLocalStorage } from "./hooks/useLocalStorage"; import { EnvMode } from "./components/BranchToolbar.logic"; -const APP_SETTINGS_STORAGE_KEY = "t3code:app-settings:v1"; +const APP_SETTINGS_STORAGE_KEY = "tero:app-settings:v1"; const MAX_CUSTOM_MODEL_COUNT = 32; export const MAX_CUSTOM_MODEL_LENGTH = 256; diff --git a/apps/web/src/components/BranchToolbar.logic.test.ts b/apps/web/src/components/BranchToolbar.logic.test.ts index 5aef38ccf6..e6e7723fd2 100644 --- a/apps/web/src/components/BranchToolbar.logic.test.ts +++ b/apps/web/src/components/BranchToolbar.logic.test.ts @@ -1,4 +1,4 @@ -import type { GitBranch } from "@t3tools/contracts"; +import type { GitBranch } from "@tero/contracts"; import { describe, expect, it } from "vitest"; import { dedupeRemoteBranchesWithLocalMatches, diff --git a/apps/web/src/components/BranchToolbar.logic.ts b/apps/web/src/components/BranchToolbar.logic.ts index 2215569c83..e6c66b8426 100644 --- a/apps/web/src/components/BranchToolbar.logic.ts +++ b/apps/web/src/components/BranchToolbar.logic.ts @@ -1,4 +1,4 @@ -import type { GitBranch } from "@t3tools/contracts"; +import type { GitBranch } from "@tero/contracts"; import { Schema } from "effect"; export const EnvMode = Schema.Literals(["local", "worktree"]); diff --git a/apps/web/src/components/BranchToolbar.tsx b/apps/web/src/components/BranchToolbar.tsx index 79c453c0f5..9efab2c3c8 100644 --- a/apps/web/src/components/BranchToolbar.tsx +++ b/apps/web/src/components/BranchToolbar.tsx @@ -1,4 +1,4 @@ -import type { ThreadId } from "@t3tools/contracts"; +import type { ThreadId } from "@tero/contracts"; import { FolderIcon, GitForkIcon } from "lucide-react"; import { useCallback } from "react"; diff --git a/apps/web/src/components/BranchToolbarBranchSelector.tsx b/apps/web/src/components/BranchToolbarBranchSelector.tsx index 9446279161..0620c77553 100644 --- a/apps/web/src/components/BranchToolbarBranchSelector.tsx +++ b/apps/web/src/components/BranchToolbarBranchSelector.tsx @@ -1,4 +1,4 @@ -import type { GitBranch } from "@t3tools/contracts"; +import type { GitBranch } from "@tero/contracts"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useVirtualizer } from "@tanstack/react-virtual"; import { ChevronDownIcon } from "lucide-react"; diff --git a/apps/web/src/components/ChatView.browser.tsx b/apps/web/src/components/ChatView.browser.tsx index 4e18092463..33a4a9af7a 100644 --- a/apps/web/src/components/ChatView.browser.tsx +++ b/apps/web/src/components/ChatView.browser.tsx @@ -12,7 +12,7 @@ import { WS_CHANNELS, WS_METHODS, OrchestrationSessionStatus, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { RouterProvider, createMemoryHistory } from "@tanstack/react-router"; import { HttpResponse, http, ws } from "msw"; import { setupWorker } from "msw/browser"; @@ -275,8 +275,8 @@ function buildFixture(snapshot: OrchestrationReadModel): TestFixture { welcome: { cwd: "/repo/project", projectName: "Project", - bootstrapProjectId: PROJECT_ID, - bootstrapThreadId: THREAD_ID, + startupProjectId: PROJECT_ID, + startupThreadId: THREAD_ID, }, }; } diff --git a/apps/web/src/components/ChatView.logic.test.ts b/apps/web/src/components/ChatView.logic.test.ts index bf72ec0b84..0255fed2fb 100644 --- a/apps/web/src/components/ChatView.logic.test.ts +++ b/apps/web/src/components/ChatView.logic.test.ts @@ -1,4 +1,4 @@ -import { ThreadId } from "@t3tools/contracts"; +import { ThreadId } from "@tero/contracts"; import { describe, expect, it } from "vitest"; import { buildExpiredTerminalContextToastCopy, deriveComposerSendState } from "./ChatView.logic"; diff --git a/apps/web/src/components/ChatView.logic.ts b/apps/web/src/components/ChatView.logic.ts index 0a9f242ed0..c04e4ff4dd 100644 --- a/apps/web/src/components/ChatView.logic.ts +++ b/apps/web/src/components/ChatView.logic.ts @@ -1,4 +1,4 @@ -import { ProjectId, type ModelSelection, type ThreadId } from "@t3tools/contracts"; +import { ProjectId, type ModelSelection, type ThreadId } from "@tero/contracts"; import { type ChatMessage, type Thread } from "../types"; import { randomUUID } from "~/lib/utils"; import { type ComposerImageAttachment, type DraftThreadState } from "../composerDraftStore"; @@ -9,8 +9,8 @@ import { type TerminalContextDraft, } from "../lib/terminalContext"; -export const LAST_INVOKED_SCRIPT_BY_PROJECT_KEY = "t3code:last-invoked-script-by-project"; -const WORKTREE_BRANCH_PREFIX = "t3code"; +export const LAST_INVOKED_SCRIPT_BY_PROJECT_KEY = "tero:last-invoked-script-by-project"; +const WORKTREE_BRANCH_PREFIX = "tero"; export const LastInvokedScriptByProjectSchema = Schema.Record(ProjectId, Schema.String); diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index fbc887bf62..261a6ee0f0 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -21,12 +21,12 @@ import { OrchestrationThreadActivity, ProviderInteractionMode, RuntimeMode, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { applyClaudePromptEffortPrefix, getModelCapabilities, normalizeModelSlug, -} from "@t3tools/shared/model"; +} from "@tero/shared/model"; import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useDebouncedValue } from "@tanstack/react-pacer"; diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx index 96e0219872..916744a079 100644 --- a/apps/web/src/components/DiffPanel.tsx +++ b/apps/web/src/components/DiffPanel.tsx @@ -2,7 +2,7 @@ import { parsePatchFiles } from "@pierre/diffs"; import { FileDiff, type FileDiffMetadata, Virtualizer } from "@pierre/diffs/react"; import { useQuery } from "@tanstack/react-query"; import { useNavigate, useParams, useSearch } from "@tanstack/react-router"; -import { ThreadId, type TurnId } from "@t3tools/contracts"; +import { ThreadId, type TurnId } from "@tero/contracts"; import { ChevronLeftIcon, ChevronRightIcon, diff --git a/apps/web/src/components/GitActionsControl.logic.test.ts b/apps/web/src/components/GitActionsControl.logic.test.ts index 44ad29efac..bb094874db 100644 --- a/apps/web/src/components/GitActionsControl.logic.test.ts +++ b/apps/web/src/components/GitActionsControl.logic.test.ts @@ -1,4 +1,4 @@ -import type { GitStatusResult } from "@t3tools/contracts"; +import type { GitStatusResult } from "@tero/contracts"; import { assert, describe, it } from "vitest"; import { buildGitActionProgressStages, diff --git a/apps/web/src/components/GitActionsControl.logic.ts b/apps/web/src/components/GitActionsControl.logic.ts index 8f7f023ef7..03cacdccea 100644 --- a/apps/web/src/components/GitActionsControl.logic.ts +++ b/apps/web/src/components/GitActionsControl.logic.ts @@ -1,8 +1,4 @@ -import type { - GitRunStackedActionResult, - GitStackedAction, - GitStatusResult, -} from "@t3tools/contracts"; +import type { GitRunStackedActionResult, GitStackedAction, GitStatusResult } from "@tero/contracts"; export type GitActionIconName = "commit" | "push" | "pr"; @@ -347,4 +343,4 @@ export function resolveDefaultBranchActionDialogCopy(input: { } // Re-export from shared for backwards compatibility in this module's exports -export { resolveAutoFeatureBranchName } from "@t3tools/shared/git"; +export { resolveAutoFeatureBranchName } from "@tero/shared/git"; diff --git a/apps/web/src/components/GitActionsControl.tsx b/apps/web/src/components/GitActionsControl.tsx index 4d563d77fd..67253094ef 100644 --- a/apps/web/src/components/GitActionsControl.tsx +++ b/apps/web/src/components/GitActionsControl.tsx @@ -3,7 +3,7 @@ import type { GitStackedAction, GitStatusResult, ThreadId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { useIsMutating, useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useCallback, useEffect, useEffectEvent, useMemo, useRef, useState } from "react"; import { ChevronDownIcon, CloudUploadIcon, GitCommitIcon, InfoIcon } from "lucide-react"; diff --git a/apps/web/src/components/KeybindingsToast.browser.tsx b/apps/web/src/components/KeybindingsToast.browser.tsx index 7cb55e795c..19f2e75c9a 100644 --- a/apps/web/src/components/KeybindingsToast.browser.tsx +++ b/apps/web/src/components/KeybindingsToast.browser.tsx @@ -10,7 +10,7 @@ import { type WsWelcomePayload, WS_CHANNELS, WS_METHODS, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { RouterProvider, createMemoryHistory } from "@tanstack/react-router"; import { ws, http, HttpResponse } from "msw"; import { setupWorker } from "msw/browser"; @@ -127,8 +127,8 @@ function buildFixture(): TestFixture { welcome: { cwd: "/repo/project", projectName: "Project", - bootstrapProjectId: PROJECT_ID, - bootstrapThreadId: THREAD_ID, + startupProjectId: PROJECT_ID, + startupThreadId: THREAD_ID, }, }; } diff --git a/apps/web/src/components/ProjectScriptsControl.tsx b/apps/web/src/components/ProjectScriptsControl.tsx index 4d6c5eef78..a63eaca880 100644 --- a/apps/web/src/components/ProjectScriptsControl.tsx +++ b/apps/web/src/components/ProjectScriptsControl.tsx @@ -1,8 +1,4 @@ -import type { - ProjectScript, - ProjectScriptIcon, - ResolvedKeybindingsConfig, -} from "@t3tools/contracts"; +import type { ProjectScript, ProjectScriptIcon, ResolvedKeybindingsConfig } from "@tero/contracts"; import { BugIcon, ChevronDownIcon, diff --git a/apps/web/src/components/PullRequestThreadDialog.tsx b/apps/web/src/components/PullRequestThreadDialog.tsx index 8b1ed51220..8ff3f210f9 100644 --- a/apps/web/src/components/PullRequestThreadDialog.tsx +++ b/apps/web/src/components/PullRequestThreadDialog.tsx @@ -1,4 +1,4 @@ -import type { GitResolvePullRequestResult } from "@t3tools/contracts"; +import type { GitResolvePullRequestResult } from "@tero/contracts"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useDebouncedValue } from "@tanstack/react-pacer"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; diff --git a/apps/web/src/components/Sidebar.logic.test.ts b/apps/web/src/components/Sidebar.logic.test.ts index 9eebee3666..35c5c94fd2 100644 --- a/apps/web/src/components/Sidebar.logic.test.ts +++ b/apps/web/src/components/Sidebar.logic.test.ts @@ -12,7 +12,7 @@ import { sortProjectsForSidebar, sortThreadsForSidebar, } from "./Sidebar.logic"; -import { ProjectId, ThreadId } from "@t3tools/contracts"; +import { ProjectId, ThreadId } from "@tero/contracts"; import { DEFAULT_INTERACTION_MODE, DEFAULT_RUNTIME_MODE, diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index 120b7c4759..1a4240d19b 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -35,7 +35,7 @@ import { ThreadId, type GitStatusResult, type ResolvedKeybindingsConfig, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { useMutation, useQueries, useQuery, useQueryClient } from "@tanstack/react-query"; import { useLocation, useNavigate, useParams } from "@tanstack/react-router"; import { diff --git a/apps/web/src/components/ThreadTerminalDrawer.tsx b/apps/web/src/components/ThreadTerminalDrawer.tsx index 1bdbfb6ad6..4a6048b05e 100644 --- a/apps/web/src/components/ThreadTerminalDrawer.tsx +++ b/apps/web/src/components/ThreadTerminalDrawer.tsx @@ -1,6 +1,6 @@ import { FitAddon } from "@xterm/addon-fit"; import { Plus, SquareSplitHorizontal, TerminalSquare, Trash2, XIcon } from "lucide-react"; -import { type ThreadId } from "@t3tools/contracts"; +import { type ThreadId } from "@tero/contracts"; import { Terminal, type ITheme } from "@xterm/xterm"; import { type PointerEvent as ReactPointerEvent, diff --git a/apps/web/src/components/chat/ChangedFilesTree.tsx b/apps/web/src/components/chat/ChangedFilesTree.tsx index 0174a77088..ee54e90620 100644 --- a/apps/web/src/components/chat/ChangedFilesTree.tsx +++ b/apps/web/src/components/chat/ChangedFilesTree.tsx @@ -1,4 +1,4 @@ -import { type TurnId } from "@t3tools/contracts"; +import { type TurnId } from "@tero/contracts"; import { memo, useCallback, useEffect, useMemo, useState } from "react"; import { type TurnDiffFileChange } from "../../types"; import { buildTurnDiffTree, type TurnDiffTreeNode } from "../../lib/turnDiffTree"; diff --git a/apps/web/src/components/chat/ChatHeader.tsx b/apps/web/src/components/chat/ChatHeader.tsx index 39a4f6eedc..4597c05c78 100644 --- a/apps/web/src/components/chat/ChatHeader.tsx +++ b/apps/web/src/components/chat/ChatHeader.tsx @@ -3,7 +3,7 @@ import { type ProjectScript, type ResolvedKeybindingsConfig, type ThreadId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { memo } from "react"; import GitActionsControl from "../GitActionsControl"; import { DiffIcon, TerminalSquareIcon } from "lucide-react"; diff --git a/apps/web/src/components/chat/CompactComposerControlsMenu.browser.tsx b/apps/web/src/components/chat/CompactComposerControlsMenu.browser.tsx index 01a5d32d64..777d257dd1 100644 --- a/apps/web/src/components/chat/CompactComposerControlsMenu.browser.tsx +++ b/apps/web/src/components/chat/CompactComposerControlsMenu.browser.tsx @@ -1,4 +1,4 @@ -import { DEFAULT_MODEL_BY_PROVIDER, ModelSelection, ThreadId } from "@t3tools/contracts"; +import { DEFAULT_MODEL_BY_PROVIDER, ModelSelection, ThreadId } from "@tero/contracts"; import "../../index.css"; import { page } from "vitest/browser"; diff --git a/apps/web/src/components/chat/CompactComposerControlsMenu.tsx b/apps/web/src/components/chat/CompactComposerControlsMenu.tsx index db38ed8c1e..e21b116fe1 100644 --- a/apps/web/src/components/chat/CompactComposerControlsMenu.tsx +++ b/apps/web/src/components/chat/CompactComposerControlsMenu.tsx @@ -1,4 +1,4 @@ -import { ProviderInteractionMode, RuntimeMode } from "@t3tools/contracts"; +import { ProviderInteractionMode, RuntimeMode } from "@tero/contracts"; import { memo, type ReactNode } from "react"; import { EllipsisIcon, ListTodoIcon } from "lucide-react"; import { Button } from "../ui/button"; diff --git a/apps/web/src/components/chat/ComposerCommandMenu.tsx b/apps/web/src/components/chat/ComposerCommandMenu.tsx index 818c3c20f8..0e67185443 100644 --- a/apps/web/src/components/chat/ComposerCommandMenu.tsx +++ b/apps/web/src/components/chat/ComposerCommandMenu.tsx @@ -1,4 +1,4 @@ -import { type ProjectEntry, type ModelSlug, type ProviderKind } from "@t3tools/contracts"; +import { type ProjectEntry, type ModelSlug, type ProviderKind } from "@tero/contracts"; import { memo } from "react"; import { type ComposerSlashCommand, type ComposerTriggerKind } from "../../composer-logic"; import { BotIcon } from "lucide-react"; diff --git a/apps/web/src/components/chat/ComposerPendingApprovalActions.tsx b/apps/web/src/components/chat/ComposerPendingApprovalActions.tsx index 5786bab478..016f28fb47 100644 --- a/apps/web/src/components/chat/ComposerPendingApprovalActions.tsx +++ b/apps/web/src/components/chat/ComposerPendingApprovalActions.tsx @@ -1,4 +1,4 @@ -import { type ApprovalRequestId, type ProviderApprovalDecision } from "@t3tools/contracts"; +import { type ApprovalRequestId, type ProviderApprovalDecision } from "@tero/contracts"; import { memo } from "react"; import { Button } from "../ui/button"; diff --git a/apps/web/src/components/chat/ComposerPendingTerminalContexts.test.tsx b/apps/web/src/components/chat/ComposerPendingTerminalContexts.test.tsx index 060f197e97..2f624d9814 100644 --- a/apps/web/src/components/chat/ComposerPendingTerminalContexts.test.tsx +++ b/apps/web/src/components/chat/ComposerPendingTerminalContexts.test.tsx @@ -1,4 +1,4 @@ -import { ThreadId } from "@t3tools/contracts"; +import { ThreadId } from "@tero/contracts"; import { renderToStaticMarkup } from "react-dom/server"; import { describe, expect, it } from "vitest"; diff --git a/apps/web/src/components/chat/ComposerPendingUserInputPanel.tsx b/apps/web/src/components/chat/ComposerPendingUserInputPanel.tsx index c8cad7bf36..1c9f420199 100644 --- a/apps/web/src/components/chat/ComposerPendingUserInputPanel.tsx +++ b/apps/web/src/components/chat/ComposerPendingUserInputPanel.tsx @@ -1,4 +1,4 @@ -import { type ApprovalRequestId } from "@t3tools/contracts"; +import { type ApprovalRequestId } from "@tero/contracts"; import { memo, useCallback, useEffect, useRef } from "react"; import { type PendingUserInput } from "../../session-logic"; import { diff --git a/apps/web/src/components/chat/MessagesTimeline.test.tsx b/apps/web/src/components/chat/MessagesTimeline.test.tsx index 692438c74a..48fb3a6e9a 100644 --- a/apps/web/src/components/chat/MessagesTimeline.test.tsx +++ b/apps/web/src/components/chat/MessagesTimeline.test.tsx @@ -1,4 +1,4 @@ -import { MessageId } from "@t3tools/contracts"; +import { MessageId } from "@tero/contracts"; import { renderToStaticMarkup } from "react-dom/server"; import { beforeAll, describe, expect, it, vi } from "vitest"; diff --git a/apps/web/src/components/chat/MessagesTimeline.tsx b/apps/web/src/components/chat/MessagesTimeline.tsx index f3e462f7fe..6332573cf6 100644 --- a/apps/web/src/components/chat/MessagesTimeline.tsx +++ b/apps/web/src/components/chat/MessagesTimeline.tsx @@ -1,4 +1,4 @@ -import { type MessageId, type TurnId } from "@t3tools/contracts"; +import { type MessageId, type TurnId } from "@tero/contracts"; import { memo, useCallback, diff --git a/apps/web/src/components/chat/OpenInPicker.tsx b/apps/web/src/components/chat/OpenInPicker.tsx index 9f62f7121e..444ce451cb 100644 --- a/apps/web/src/components/chat/OpenInPicker.tsx +++ b/apps/web/src/components/chat/OpenInPicker.tsx @@ -1,4 +1,4 @@ -import { EditorId, type ResolvedKeybindingsConfig } from "@t3tools/contracts"; +import { EditorId, type ResolvedKeybindingsConfig } from "@tero/contracts"; import { memo, useCallback, useEffect, useMemo } from "react"; import { isOpenFavoriteEditorShortcut, shortcutLabelForCommand } from "../../keybindings"; import { usePreferredEditor } from "../../editorPreferences"; diff --git a/apps/web/src/components/chat/ProviderHealthBanner.tsx b/apps/web/src/components/chat/ProviderHealthBanner.tsx index bfdefe58ec..4f9215a491 100644 --- a/apps/web/src/components/chat/ProviderHealthBanner.tsx +++ b/apps/web/src/components/chat/ProviderHealthBanner.tsx @@ -1,4 +1,4 @@ -import { PROVIDER_DISPLAY_NAMES, type ServerProviderStatus } from "@t3tools/contracts"; +import { PROVIDER_DISPLAY_NAMES, type ServerProviderStatus } from "@tero/contracts"; import { memo } from "react"; import { Alert, AlertDescription, AlertTitle } from "../ui/alert"; import { CircleAlertIcon } from "lucide-react"; diff --git a/apps/web/src/components/chat/ProviderModelPicker.browser.tsx b/apps/web/src/components/chat/ProviderModelPicker.browser.tsx index 1694b374c8..59c815f2f1 100644 --- a/apps/web/src/components/chat/ProviderModelPicker.browser.tsx +++ b/apps/web/src/components/chat/ProviderModelPicker.browser.tsx @@ -1,4 +1,4 @@ -import { type ModelSlug, type ProviderKind } from "@t3tools/contracts"; +import { type ModelSlug, type ProviderKind } from "@tero/contracts"; import { page } from "vitest/browser"; import { afterEach, describe, expect, it, vi } from "vitest"; import { render } from "vitest-browser-react"; diff --git a/apps/web/src/components/chat/ProviderModelPicker.tsx b/apps/web/src/components/chat/ProviderModelPicker.tsx index 95f27f39cd..169f18ed3c 100644 --- a/apps/web/src/components/chat/ProviderModelPicker.tsx +++ b/apps/web/src/components/chat/ProviderModelPicker.tsx @@ -1,5 +1,5 @@ -import { type ModelSlug, type ProviderKind } from "@t3tools/contracts"; -import { resolveSelectableModel } from "@t3tools/shared/model"; +import { type ModelSlug, type ProviderKind } from "@tero/contracts"; +import { resolveSelectableModel } from "@tero/shared/model"; import { memo, useState } from "react"; import { type ProviderPickerKind, PROVIDER_OPTIONS } from "../../session-logic"; import { ChevronDownIcon } from "lucide-react"; diff --git a/apps/web/src/components/chat/TraitsPicker.browser.tsx b/apps/web/src/components/chat/TraitsPicker.browser.tsx index 811ad5bb35..d838e70322 100644 --- a/apps/web/src/components/chat/TraitsPicker.browser.tsx +++ b/apps/web/src/components/chat/TraitsPicker.browser.tsx @@ -7,7 +7,7 @@ import { DEFAULT_MODEL_BY_PROVIDER, ProjectId, ThreadId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { page } from "vitest/browser"; import { useCallback } from "react"; import { afterEach, describe, expect, it, vi } from "vitest"; diff --git a/apps/web/src/components/chat/TraitsPicker.tsx b/apps/web/src/components/chat/TraitsPicker.tsx index e43c094283..45f5c9d0dd 100644 --- a/apps/web/src/components/chat/TraitsPicker.tsx +++ b/apps/web/src/components/chat/TraitsPicker.tsx @@ -4,7 +4,7 @@ import { type ProviderKind, type ProviderModelOptions, type ThreadId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { applyClaudePromptEffortPrefix, getModelCapabilities, @@ -12,7 +12,7 @@ import { trimOrNull, getDefaultEffort, hasEffortLevel, -} from "@t3tools/shared/model"; +} from "@tero/shared/model"; import { memo, useCallback, useState } from "react"; import { ChevronDownIcon } from "lucide-react"; import { Button } from "../ui/button"; diff --git a/apps/web/src/components/chat/composerProviderRegistry.tsx b/apps/web/src/components/chat/composerProviderRegistry.tsx index 088a2a47be..5dd2732f9b 100644 --- a/apps/web/src/components/chat/composerProviderRegistry.tsx +++ b/apps/web/src/components/chat/composerProviderRegistry.tsx @@ -3,7 +3,7 @@ import { type ProviderKind, type ProviderModelOptions, type ThreadId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { getModelCapabilities, isClaudeUltrathinkPrompt, @@ -12,7 +12,7 @@ import { trimOrNull, getDefaultEffort, hasEffortLevel, -} from "@t3tools/shared/model"; +} from "@tero/shared/model"; import type { ReactNode } from "react"; import { TraitsMenuContent, TraitsPicker } from "./TraitsPicker"; diff --git a/apps/web/src/components/desktopUpdate.logic.test.ts b/apps/web/src/components/desktopUpdate.logic.test.ts index 984eebd6b1..6129cfc079 100644 --- a/apps/web/src/components/desktopUpdate.logic.test.ts +++ b/apps/web/src/components/desktopUpdate.logic.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import type { DesktopUpdateActionResult, DesktopUpdateState } from "@t3tools/contracts"; +import type { DesktopUpdateActionResult, DesktopUpdateState } from "@tero/contracts"; import { getArm64IntelBuildWarningDescription, diff --git a/apps/web/src/components/desktopUpdate.logic.ts b/apps/web/src/components/desktopUpdate.logic.ts index faf30883cc..83d716191b 100644 --- a/apps/web/src/components/desktopUpdate.logic.ts +++ b/apps/web/src/components/desktopUpdate.logic.ts @@ -1,4 +1,4 @@ -import type { DesktopUpdateActionResult, DesktopUpdateState } from "@t3tools/contracts"; +import type { DesktopUpdateActionResult, DesktopUpdateState } from "@tero/contracts"; export type DesktopUpdateButtonAction = "download" | "install" | "none"; diff --git a/apps/web/src/components/ui/toast.tsx b/apps/web/src/components/ui/toast.tsx index 768a083e2e..5ae48df748 100644 --- a/apps/web/src/components/ui/toast.tsx +++ b/apps/web/src/components/ui/toast.tsx @@ -3,7 +3,7 @@ import { Toast } from "@base-ui/react/toast"; import { useEffect, type CSSProperties } from "react"; import { useParams } from "@tanstack/react-router"; -import { ThreadId } from "@t3tools/contracts"; +import { ThreadId } from "@tero/contracts"; import { CircleAlertIcon, CircleCheckIcon, diff --git a/apps/web/src/composerDraftStore.test.ts b/apps/web/src/composerDraftStore.test.ts index b68663a890..cc4d64b4ca 100644 --- a/apps/web/src/composerDraftStore.test.ts +++ b/apps/web/src/composerDraftStore.test.ts @@ -4,7 +4,7 @@ import { ThreadId, type ModelSelection, type ProviderModelOptions, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { diff --git a/apps/web/src/composerDraftStore.ts b/apps/web/src/composerDraftStore.ts index fb9c0d5150..c38f1eaa4f 100644 --- a/apps/web/src/composerDraftStore.ts +++ b/apps/web/src/composerDraftStore.ts @@ -10,7 +10,7 @@ import { ProviderModelOptions, RuntimeMode, ThreadId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import * as Schema from "effect/Schema"; import * as Equal from "effect/Equal"; import { DeepMutable } from "effect/Types"; @@ -18,7 +18,7 @@ import { getDefaultModel, normalizeModelSlug, resolveModelSlugForProvider, -} from "@t3tools/shared/model"; +} from "@tero/shared/model"; import { useMemo } from "react"; import { getLocalStorageItem } from "./hooks/useLocalStorage"; import { resolveAppModelSelection } from "./appSettings"; @@ -32,7 +32,7 @@ import { create } from "zustand"; import { createJSONStorage, persist } from "zustand/middleware"; import { createDebouncedStorage, createMemoryStorage } from "./lib/storage"; -export const COMPOSER_DRAFT_STORAGE_KEY = "t3code:composer-drafts:v1"; +export const COMPOSER_DRAFT_STORAGE_KEY = "tero:composer-drafts:v1"; const COMPOSER_DRAFT_STORAGE_VERSION = 3; const DraftThreadEnvModeSchema = Schema.Literals(["local", "worktree"]); export type DraftThreadEnvMode = typeof DraftThreadEnvModeSchema.Type; diff --git a/apps/web/src/contextMenuFallback.ts b/apps/web/src/contextMenuFallback.ts index 63cdef8481..cb64e46394 100644 --- a/apps/web/src/contextMenuFallback.ts +++ b/apps/web/src/contextMenuFallback.ts @@ -1,4 +1,4 @@ -import type { ContextMenuItem } from "@t3tools/contracts"; +import type { ContextMenuItem } from "@tero/contracts"; /** * Imperative DOM-based context menu for non-Electron environments. diff --git a/apps/web/src/diffRouteSearch.ts b/apps/web/src/diffRouteSearch.ts index d7de23e348..2adc7eacfc 100644 --- a/apps/web/src/diffRouteSearch.ts +++ b/apps/web/src/diffRouteSearch.ts @@ -1,4 +1,4 @@ -import { TurnId } from "@t3tools/contracts"; +import { TurnId } from "@tero/contracts"; export interface DiffRouteSearch { diff?: "1" | undefined; diff --git a/apps/web/src/editorPreferences.ts b/apps/web/src/editorPreferences.ts index ca43f3e5d8..7c45ab711d 100644 --- a/apps/web/src/editorPreferences.ts +++ b/apps/web/src/editorPreferences.ts @@ -1,8 +1,8 @@ -import { EDITORS, EditorId, NativeApi } from "@t3tools/contracts"; +import { EDITORS, EditorId, NativeApi } from "@tero/contracts"; import { getLocalStorageItem, setLocalStorageItem, useLocalStorage } from "./hooks/useLocalStorage"; import { useMemo } from "react"; -const LAST_EDITOR_KEY = "t3code:last-editor"; +const LAST_EDITOR_KEY = "tero:last-editor"; export function usePreferredEditor(availableEditors: ReadonlyArray) { const [lastEditor, setLastEditor] = useLocalStorage(LAST_EDITOR_KEY, null, EditorId); diff --git a/apps/web/src/historyBootstrap.test.ts b/apps/web/src/historyBootstrap.test.ts index eda176dab4..c2a57309f6 100644 --- a/apps/web/src/historyBootstrap.test.ts +++ b/apps/web/src/historyBootstrap.test.ts @@ -1,4 +1,4 @@ -import { MessageId } from "@t3tools/contracts"; +import { MessageId } from "@tero/contracts"; import { describe, expect, it } from "vitest"; import { buildBootstrapInput } from "./historyBootstrap"; diff --git a/apps/web/src/hooks/useHandleNewThread.ts b/apps/web/src/hooks/useHandleNewThread.ts index ffb4b5cf67..d2fb2322ed 100644 --- a/apps/web/src/hooks/useHandleNewThread.ts +++ b/apps/web/src/hooks/useHandleNewThread.ts @@ -1,4 +1,4 @@ -import { DEFAULT_RUNTIME_MODE, type ProjectId, ThreadId } from "@t3tools/contracts"; +import { DEFAULT_RUNTIME_MODE, type ProjectId, ThreadId } from "@tero/contracts"; import { useNavigate, useParams } from "@tanstack/react-router"; import { useCallback } from "react"; import { diff --git a/apps/web/src/hooks/useLocalStorage.ts b/apps/web/src/hooks/useLocalStorage.ts index da9aa4b889..a338ef2f63 100644 --- a/apps/web/src/hooks/useLocalStorage.ts +++ b/apps/web/src/hooks/useLocalStorage.ts @@ -39,7 +39,7 @@ export const removeLocalStorageItem = (key: string) => { isomorphicLocalStorage.removeItem(key); }; -const LOCAL_STORAGE_CHANGE_EVENT = "t3code:local_storage_change"; +const LOCAL_STORAGE_CHANGE_EVENT = "tero:local_storage_change"; interface LocalStorageChangeDetail { key: string; diff --git a/apps/web/src/hooks/useTheme.ts b/apps/web/src/hooks/useTheme.ts index 6afe83dfe3..b4dc49db95 100644 --- a/apps/web/src/hooks/useTheme.ts +++ b/apps/web/src/hooks/useTheme.ts @@ -6,7 +6,7 @@ type ThemeSnapshot = { systemDark: boolean; }; -const STORAGE_KEY = "t3code:theme"; +const STORAGE_KEY = "tero:theme"; const MEDIA_QUERY = "(prefers-color-scheme: dark)"; let listeners: Array<() => void> = []; diff --git a/apps/web/src/keybindings.test.ts b/apps/web/src/keybindings.test.ts index 0ecccf43f8..536813b5ee 100644 --- a/apps/web/src/keybindings.test.ts +++ b/apps/web/src/keybindings.test.ts @@ -5,7 +5,7 @@ import { type KeybindingShortcut, type KeybindingWhenNode, type ResolvedKeybindingsConfig, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { formatShortcutLabel, isChatNewShortcut, diff --git a/apps/web/src/keybindings.ts b/apps/web/src/keybindings.ts index 09d9308aad..d2b2792d8b 100644 --- a/apps/web/src/keybindings.ts +++ b/apps/web/src/keybindings.ts @@ -3,7 +3,7 @@ import { type KeybindingShortcut, type KeybindingWhenNode, type ResolvedKeybindingsConfig, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { isMacPlatform } from "./lib/utils"; export interface ShortcutEventLike { diff --git a/apps/web/src/lib/contextWindow.test.ts b/apps/web/src/lib/contextWindow.test.ts index 2173c18aa7..a30e070845 100644 --- a/apps/web/src/lib/contextWindow.test.ts +++ b/apps/web/src/lib/contextWindow.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { EventId, type OrchestrationThreadActivity, TurnId } from "@t3tools/contracts"; +import { EventId, type OrchestrationThreadActivity, TurnId } from "@tero/contracts"; import { deriveLatestContextWindowSnapshot, formatContextWindowTokens } from "./contextWindow"; diff --git a/apps/web/src/lib/contextWindow.ts b/apps/web/src/lib/contextWindow.ts index f668135a13..6b21f1c792 100644 --- a/apps/web/src/lib/contextWindow.ts +++ b/apps/web/src/lib/contextWindow.ts @@ -1,4 +1,4 @@ -import type { OrchestrationThreadActivity, ThreadTokenUsageSnapshot } from "@t3tools/contracts"; +import type { OrchestrationThreadActivity, ThreadTokenUsageSnapshot } from "@tero/contracts"; function asRecord(value: unknown): Record | null { return value && typeof value === "object" ? (value as Record) : null; diff --git a/apps/web/src/lib/gitReactQuery.ts b/apps/web/src/lib/gitReactQuery.ts index d6a72859f3..77ff294cc0 100644 --- a/apps/web/src/lib/gitReactQuery.ts +++ b/apps/web/src/lib/gitReactQuery.ts @@ -1,4 +1,4 @@ -import type { GitStackedAction } from "@t3tools/contracts"; +import type { GitStackedAction } from "@tero/contracts"; import { mutationOptions, queryOptions, type QueryClient } from "@tanstack/react-query"; import { ensureNativeApi } from "../nativeApi"; diff --git a/apps/web/src/lib/projectReactQuery.ts b/apps/web/src/lib/projectReactQuery.ts index 20aa265b87..ac116b2953 100644 --- a/apps/web/src/lib/projectReactQuery.ts +++ b/apps/web/src/lib/projectReactQuery.ts @@ -1,4 +1,4 @@ -import type { ProjectSearchEntriesResult } from "@t3tools/contracts"; +import type { ProjectSearchEntriesResult } from "@tero/contracts"; import { queryOptions } from "@tanstack/react-query"; import { ensureNativeApi } from "~/nativeApi"; diff --git a/apps/web/src/lib/projectScriptKeybindings.test.ts b/apps/web/src/lib/projectScriptKeybindings.test.ts index 9db0fd5290..1d3e809be8 100644 --- a/apps/web/src/lib/projectScriptKeybindings.test.ts +++ b/apps/web/src/lib/projectScriptKeybindings.test.ts @@ -1,4 +1,4 @@ -import { MAX_KEYBINDING_VALUE_LENGTH, type KeybindingCommand } from "@t3tools/contracts"; +import { MAX_KEYBINDING_VALUE_LENGTH, type KeybindingCommand } from "@tero/contracts"; import { describe, expect, it } from "vitest"; import { commandForProjectScript } from "../projectScripts"; diff --git a/apps/web/src/lib/projectScriptKeybindings.ts b/apps/web/src/lib/projectScriptKeybindings.ts index 4ea80cf824..2853a6d71b 100644 --- a/apps/web/src/lib/projectScriptKeybindings.ts +++ b/apps/web/src/lib/projectScriptKeybindings.ts @@ -3,7 +3,7 @@ import { type KeybindingCommand, type KeybindingRule, type ResolvedKeybindingsConfig, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Schema } from "effect"; export const PROJECT_SCRIPT_KEYBINDING_INVALID_MESSAGE = "Invalid keybinding."; diff --git a/apps/web/src/lib/providerReactQuery.test.ts b/apps/web/src/lib/providerReactQuery.test.ts index b7e770799a..065e223f3f 100644 --- a/apps/web/src/lib/providerReactQuery.test.ts +++ b/apps/web/src/lib/providerReactQuery.test.ts @@ -1,4 +1,4 @@ -import { ThreadId, type NativeApi } from "@t3tools/contracts"; +import { ThreadId, type NativeApi } from "@tero/contracts"; import { QueryClient } from "@tanstack/react-query"; import { afterEach, describe, expect, it, vi } from "vitest"; import { checkpointDiffQueryOptions, providerQueryKeys } from "./providerReactQuery"; diff --git a/apps/web/src/lib/providerReactQuery.ts b/apps/web/src/lib/providerReactQuery.ts index 0547aa9e5f..590fcd8d2d 100644 --- a/apps/web/src/lib/providerReactQuery.ts +++ b/apps/web/src/lib/providerReactQuery.ts @@ -2,7 +2,7 @@ import { OrchestrationGetFullThreadDiffInput, OrchestrationGetTurnDiffInput, ThreadId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { queryOptions } from "@tanstack/react-query"; import { Option, Schema } from "effect"; import { ensureNativeApi } from "../nativeApi"; diff --git a/apps/web/src/lib/terminalContext.test.ts b/apps/web/src/lib/terminalContext.test.ts index 2e53c43d31..ef12f31083 100644 --- a/apps/web/src/lib/terminalContext.test.ts +++ b/apps/web/src/lib/terminalContext.test.ts @@ -1,4 +1,4 @@ -import { ThreadId } from "@t3tools/contracts"; +import { ThreadId } from "@tero/contracts"; import { describe, expect, it } from "vitest"; import { diff --git a/apps/web/src/lib/terminalContext.ts b/apps/web/src/lib/terminalContext.ts index 562fe742a1..d6428512c9 100644 --- a/apps/web/src/lib/terminalContext.ts +++ b/apps/web/src/lib/terminalContext.ts @@ -1,4 +1,4 @@ -import { type ThreadId } from "@t3tools/contracts"; +import { type ThreadId } from "@tero/contracts"; export interface TerminalContextSelection { terminalId: string; diff --git a/apps/web/src/lib/terminalStateCleanup.test.ts b/apps/web/src/lib/terminalStateCleanup.test.ts index 8bc3c37300..823a3a22d5 100644 --- a/apps/web/src/lib/terminalStateCleanup.test.ts +++ b/apps/web/src/lib/terminalStateCleanup.test.ts @@ -1,4 +1,4 @@ -import { ThreadId } from "@t3tools/contracts"; +import { ThreadId } from "@tero/contracts"; import { describe, expect, it } from "vitest"; import { collectActiveTerminalThreadIds } from "./terminalStateCleanup"; diff --git a/apps/web/src/lib/terminalStateCleanup.ts b/apps/web/src/lib/terminalStateCleanup.ts index 5f2bfdafaa..c41498fe86 100644 --- a/apps/web/src/lib/terminalStateCleanup.ts +++ b/apps/web/src/lib/terminalStateCleanup.ts @@ -1,4 +1,4 @@ -import type { ThreadId } from "@t3tools/contracts"; +import type { ThreadId } from "@tero/contracts"; interface TerminalRetentionThread { id: ThreadId; diff --git a/apps/web/src/lib/utils.ts b/apps/web/src/lib/utils.ts index b5834606b1..1b612a3d59 100644 --- a/apps/web/src/lib/utils.ts +++ b/apps/web/src/lib/utils.ts @@ -1,4 +1,4 @@ -import { CommandId, MessageId, ProjectId, ThreadId } from "@t3tools/contracts"; +import { CommandId, MessageId, ProjectId, ThreadId } from "@tero/contracts"; import { type CxOptions, cx } from "class-variance-authority"; import { twMerge } from "tailwind-merge"; import * as Random from "effect/Random"; diff --git a/apps/web/src/nativeApi.ts b/apps/web/src/nativeApi.ts index 40443f67e0..17fc12e940 100644 --- a/apps/web/src/nativeApi.ts +++ b/apps/web/src/nativeApi.ts @@ -1,4 +1,4 @@ -import type { NativeApi } from "@t3tools/contracts"; +import type { NativeApi } from "@tero/contracts"; import { createWsNativeApi } from "./wsNativeApi"; diff --git a/apps/web/src/pendingUserInput.ts b/apps/web/src/pendingUserInput.ts index 86e41285ad..33fd0dc0ec 100644 --- a/apps/web/src/pendingUserInput.ts +++ b/apps/web/src/pendingUserInput.ts @@ -1,4 +1,4 @@ -import type { UserInputQuestion } from "@t3tools/contracts"; +import type { UserInputQuestion } from "@tero/contracts"; export interface PendingUserInputDraftAnswer { selectedOptionLabel?: string; diff --git a/apps/web/src/projectScripts.test.ts b/apps/web/src/projectScripts.test.ts index 08678f8730..51c2d2ec8c 100644 --- a/apps/web/src/projectScripts.test.ts +++ b/apps/web/src/projectScripts.test.ts @@ -53,8 +53,8 @@ describe("projectScripts helpers", () => { }); expect(env).toMatchObject({ - T3CODE_PROJECT_ROOT: "/repo", - T3CODE_WORKTREE_PATH: "/repo/worktree-a", + TERO_PROJECT_ROOT: "/repo", + TERO_WORKTREE_PATH: "/repo/worktree-a", }); }); @@ -62,14 +62,14 @@ describe("projectScripts helpers", () => { const env = projectScriptRuntimeEnv({ project: { cwd: "/repo" }, extraEnv: { - T3CODE_PROJECT_ROOT: "/custom-root", + TERO_PROJECT_ROOT: "/custom-root", CUSTOM_FLAG: "1", }, }); - expect(env.T3CODE_PROJECT_ROOT).toBe("/custom-root"); + expect(env.TERO_PROJECT_ROOT).toBe("/custom-root"); expect(env.CUSTOM_FLAG).toBe("1"); - expect(env.T3CODE_WORKTREE_PATH).toBeUndefined(); + expect(env.TERO_WORKTREE_PATH).toBeUndefined(); }); it("prefers the worktree path for script cwd resolution", () => { diff --git a/apps/web/src/projectScripts.ts b/apps/web/src/projectScripts.ts index c11c3923bc..77382e94ef 100644 --- a/apps/web/src/projectScripts.ts +++ b/apps/web/src/projectScripts.ts @@ -3,7 +3,7 @@ import { SCRIPT_RUN_COMMAND_PATTERN, type KeybindingCommand, type ProjectScript, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { Schema } from "effect"; function normalizeScriptId(value: string): string { @@ -76,10 +76,10 @@ export function projectScriptRuntimeEnv( input: ProjectScriptRuntimeEnvInput, ): Record { const env: Record = { - T3CODE_PROJECT_ROOT: input.project.cwd, + TERO_PROJECT_ROOT: input.project.cwd, }; if (input.worktreePath) { - env.T3CODE_WORKTREE_PATH = input.worktreePath; + env.TERO_WORKTREE_PATH = input.worktreePath; } if (input.extraEnv) { return { ...env, ...input.extraEnv }; diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx index 34f9c4b82f..d6407da445 100644 --- a/apps/web/src/routes/__root.tsx +++ b/apps/web/src/routes/__root.tsx @@ -1,4 +1,4 @@ -import { ThreadId } from "@t3tools/contracts"; +import { ThreadId } from "@tero/contracts"; import { Outlet, createRootRouteWithContext, @@ -130,6 +130,10 @@ function errorDetails(error: unknown): string { } } +function canNavigateToStartupThread(pathname: string): boolean { + return pathname === "/" || pathname === "/_chat" || pathname === "/_chat/"; +} + function EventRouter() { const syncServerReadModel = useStore((store) => store.syncServerReadModel); const setProjectExpanded = useStore((store) => store.setProjectExpanded); @@ -140,7 +144,7 @@ function EventRouter() { const navigate = useNavigate(); const pathname = useRouterState({ select: (state) => state.location.pathname }); const pathnameRef = useRef(pathname); - const handledBootstrapThreadIdRef = useRef(null); + const handledStartupThreadIdRef = useRef(null); pathnameRef.current = pathname; @@ -184,8 +188,9 @@ function EventRouter() { await flushSnapshotSync(); } catch { // Keep prior state and wait for next domain event to trigger a resync. + } finally { + syncing = false; } - syncing = false; }; const domainEventFlushThrottler = new Throttler( @@ -236,23 +241,23 @@ function EventRouter() { return; } - if (!payload.bootstrapProjectId || !payload.bootstrapThreadId) { + if (!canNavigateToStartupThread(pathnameRef.current)) { return; } - setProjectExpanded(payload.bootstrapProjectId, true); - if (pathnameRef.current !== "/") { + if (!payload.startupProjectId || !payload.startupThreadId) { return; } - if (handledBootstrapThreadIdRef.current === payload.bootstrapThreadId) { + setProjectExpanded(payload.startupProjectId, true); + if (handledStartupThreadIdRef.current === payload.startupThreadId) { return; } await navigate({ to: "/$threadId", - params: { threadId: payload.bootstrapThreadId }, + params: { threadId: payload.startupThreadId }, replace: true, }); - handledBootstrapThreadIdRef.current = payload.bootstrapThreadId; + handledStartupThreadIdRef.current = payload.startupThreadId; })().catch(() => undefined); }); // onServerConfigUpdated replays the latest cached value synchronously diff --git a/apps/web/src/routes/_chat.$threadId.tsx b/apps/web/src/routes/_chat.$threadId.tsx index 8e7a5d3ba8..93a2ee50b4 100644 --- a/apps/web/src/routes/_chat.$threadId.tsx +++ b/apps/web/src/routes/_chat.$threadId.tsx @@ -1,4 +1,4 @@ -import { ThreadId } from "@t3tools/contracts"; +import { ThreadId } from "@tero/contracts"; import { createFileRoute, retainSearchParams, useNavigate } from "@tanstack/react-router"; import { Suspense, lazy, type ReactNode, useCallback, useEffect, useState } from "react"; diff --git a/apps/web/src/routes/_chat.settings.tsx b/apps/web/src/routes/_chat.settings.tsx index 05fd640d0f..1f9b8d1e9b 100644 --- a/apps/web/src/routes/_chat.settings.tsx +++ b/apps/web/src/routes/_chat.settings.tsx @@ -2,8 +2,8 @@ import { createFileRoute } from "@tanstack/react-router"; import { useQuery } from "@tanstack/react-query"; import { ChevronDownIcon, PlusIcon, RotateCcwIcon, Undo2Icon, XIcon } from "lucide-react"; import { type ReactNode, useCallback, useState } from "react"; -import { type ProviderKind, DEFAULT_GIT_TEXT_GENERATION_MODEL } from "@t3tools/contracts"; -import { getModelOptions, normalizeModelSlug } from "@t3tools/shared/model"; +import { type ProviderKind, DEFAULT_GIT_TEXT_GENERATION_MODEL } from "@tero/contracts"; +import { getModelOptions, normalizeModelSlug } from "@tero/shared/model"; import { getAppModelOptions, getCustomModelsForProvider, diff --git a/apps/web/src/routes/_chat.tsx b/apps/web/src/routes/_chat.tsx index 7cb377056b..e2cfb79601 100644 --- a/apps/web/src/routes/_chat.tsx +++ b/apps/web/src/routes/_chat.tsx @@ -1,4 +1,4 @@ -import { type ResolvedKeybindingsConfig } from "@t3tools/contracts"; +import { type ResolvedKeybindingsConfig } from "@tero/contracts"; import { useQuery } from "@tanstack/react-query"; import { Outlet, createFileRoute, useNavigate } from "@tanstack/react-router"; import { useEffect } from "react"; diff --git a/apps/web/src/session-logic.test.ts b/apps/web/src/session-logic.test.ts index c786ffc72b..999f9afcce 100644 --- a/apps/web/src/session-logic.test.ts +++ b/apps/web/src/session-logic.test.ts @@ -4,7 +4,7 @@ import { ThreadId, TurnId, type OrchestrationThreadActivity, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { describe, expect, it } from "vitest"; import { diff --git a/apps/web/src/session-logic.ts b/apps/web/src/session-logic.ts index 83a95d6313..f6d73de809 100644 --- a/apps/web/src/session-logic.ts +++ b/apps/web/src/session-logic.ts @@ -9,7 +9,7 @@ import { type UserInputQuestion, type ThreadId, type TurnId, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import type { ChatMessage, diff --git a/apps/web/src/store.test.ts b/apps/web/src/store.test.ts index 4da4f23c8c..b0805b0b05 100644 --- a/apps/web/src/store.test.ts +++ b/apps/web/src/store.test.ts @@ -4,7 +4,7 @@ import { ThreadId, TurnId, type OrchestrationReadModel, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { describe, expect, it } from "vitest"; import { markThreadUnread, reorderProjects, syncServerReadModel, type AppState } from "./store"; diff --git a/apps/web/src/store.ts b/apps/web/src/store.ts index 4590b2886d..53f61af1e2 100644 --- a/apps/web/src/store.ts +++ b/apps/web/src/store.ts @@ -4,8 +4,8 @@ import { ThreadId, type OrchestrationReadModel, type OrchestrationSessionStatus, -} from "@t3tools/contracts"; -import { resolveModelSlugForProvider } from "@t3tools/shared/model"; +} from "@tero/contracts"; +import { resolveModelSlugForProvider } from "@tero/shared/model"; import { create } from "zustand"; import { type ChatMessage, type Project, type Thread } from "./types"; import { Debouncer } from "@tanstack/react-pacer"; @@ -18,8 +18,9 @@ export interface AppState { threadsHydrated: boolean; } -const PERSISTED_STATE_KEY = "t3code:renderer-state:v8"; +const PERSISTED_STATE_KEY = "tero:renderer-state:v8"; const LEGACY_PERSISTED_STATE_KEYS = [ + "t3code:renderer-state:v8", "t3code:renderer-state:v7", "t3code:renderer-state:v6", "t3code:renderer-state:v5", diff --git a/apps/web/src/terminalActivity.test.ts b/apps/web/src/terminalActivity.test.ts index 8fda326266..6cb7a980fd 100644 --- a/apps/web/src/terminalActivity.test.ts +++ b/apps/web/src/terminalActivity.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import type { TerminalEvent, TerminalSessionSnapshot } from "@t3tools/contracts"; +import type { TerminalEvent, TerminalSessionSnapshot } from "@tero/contracts"; import { terminalRunningSubprocessFromEvent } from "./terminalActivity"; const snapshot: TerminalSessionSnapshot = { diff --git a/apps/web/src/terminalActivity.ts b/apps/web/src/terminalActivity.ts index 24f4984ff5..085495aaa7 100644 --- a/apps/web/src/terminalActivity.ts +++ b/apps/web/src/terminalActivity.ts @@ -1,4 +1,4 @@ -import type { TerminalEvent } from "@t3tools/contracts"; +import type { TerminalEvent } from "@tero/contracts"; export function terminalRunningSubprocessFromEvent(event: TerminalEvent): boolean | null { switch (event.type) { diff --git a/apps/web/src/terminalStateStore.test.ts b/apps/web/src/terminalStateStore.test.ts index e7e240cf25..80440de3c5 100644 --- a/apps/web/src/terminalStateStore.test.ts +++ b/apps/web/src/terminalStateStore.test.ts @@ -1,4 +1,4 @@ -import { ThreadId } from "@t3tools/contracts"; +import { ThreadId } from "@tero/contracts"; import { beforeEach, describe, expect, it } from "vitest"; import { selectThreadTerminalState, useTerminalStateStore } from "./terminalStateStore"; diff --git a/apps/web/src/terminalStateStore.ts b/apps/web/src/terminalStateStore.ts index b2cea6d560..4e47e386f7 100644 --- a/apps/web/src/terminalStateStore.ts +++ b/apps/web/src/terminalStateStore.ts @@ -5,7 +5,7 @@ * API constrained to store actions/selectors. */ -import type { ThreadId } from "@t3tools/contracts"; +import type { ThreadId } from "@tero/contracts"; import { create } from "zustand"; import { createJSONStorage, persist } from "zustand/middleware"; import { @@ -25,7 +25,7 @@ interface ThreadTerminalState { activeTerminalGroupId: string; } -const TERMINAL_STATE_STORAGE_KEY = "t3code:terminal-state:v1"; +const TERMINAL_STATE_STORAGE_KEY = "tero:terminal-state:v1"; function normalizeTerminalIds(terminalIds: string[]): string[] { const ids = [...new Set(terminalIds.map((id) => id.trim()).filter((id) => id.length > 0))]; diff --git a/apps/web/src/threadSelectionStore.test.ts b/apps/web/src/threadSelectionStore.test.ts index b142c5c7d2..c51f354b54 100644 --- a/apps/web/src/threadSelectionStore.test.ts +++ b/apps/web/src/threadSelectionStore.test.ts @@ -1,4 +1,4 @@ -import { ThreadId } from "@t3tools/contracts"; +import { ThreadId } from "@tero/contracts"; import { beforeEach, describe, expect, it } from "vitest"; import { useThreadSelectionStore } from "./threadSelectionStore"; diff --git a/apps/web/src/threadSelectionStore.ts b/apps/web/src/threadSelectionStore.ts index 8360bc5c6f..38a3bc0f10 100644 --- a/apps/web/src/threadSelectionStore.ts +++ b/apps/web/src/threadSelectionStore.ts @@ -5,7 +5,7 @@ * and bulk actions on the selected set. */ -import type { ThreadId } from "@t3tools/contracts"; +import type { ThreadId } from "@tero/contracts"; import { create } from "zustand"; export interface ThreadSelectionState { diff --git a/apps/web/src/types.ts b/apps/web/src/types.ts index de06f95538..66c664a9c1 100644 --- a/apps/web/src/types.ts +++ b/apps/web/src/types.ts @@ -13,7 +13,7 @@ import type { CheckpointRef, ProviderInteractionMode, RuntimeMode, -} from "@t3tools/contracts"; +} from "@tero/contracts"; export type SessionPhase = "disconnected" | "connecting" | "ready" | "running"; export const DEFAULT_RUNTIME_MODE: RuntimeMode = "full-access"; diff --git a/apps/web/src/vite-env.d.ts b/apps/web/src/vite-env.d.ts index c3a23ad405..1478a84c14 100644 --- a/apps/web/src/vite-env.d.ts +++ b/apps/web/src/vite-env.d.ts @@ -1,6 +1,6 @@ /// -import type { NativeApi, DesktopBridge } from "@t3tools/contracts"; +import type { NativeApi, DesktopBridge } from "@tero/contracts"; interface ImportMetaEnv { readonly APP_VERSION: string; diff --git a/apps/web/src/worktreeCleanup.test.ts b/apps/web/src/worktreeCleanup.test.ts index 574675ae11..127740679c 100644 --- a/apps/web/src/worktreeCleanup.test.ts +++ b/apps/web/src/worktreeCleanup.test.ts @@ -1,4 +1,4 @@ -import { ProjectId, ThreadId } from "@t3tools/contracts"; +import { ProjectId, ThreadId } from "@tero/contracts"; import { describe, expect, it } from "vitest"; import { DEFAULT_INTERACTION_MODE, DEFAULT_RUNTIME_MODE, type Thread } from "./types"; diff --git a/apps/web/src/wsNativeApi.test.ts b/apps/web/src/wsNativeApi.test.ts index e500b57791..577f89fa3f 100644 --- a/apps/web/src/wsNativeApi.test.ts +++ b/apps/web/src/wsNativeApi.test.ts @@ -14,7 +14,7 @@ import { WS_METHODS, type WsPush, type ServerProviderStatus, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; const requestMock = vi.fn<(...args: Array) => Promise>(); @@ -138,7 +138,7 @@ describe("wsNativeApi", () => { expect(lateListener).toHaveBeenCalledWith(expect.objectContaining(payload)); }); - it("preserves bootstrap ids from server.welcome payloads", async () => { + it("preserves startup ids from server.welcome payloads", async () => { const { createWsNativeApi, onServerWelcome } = await import("./wsNativeApi"); createWsNativeApi(); @@ -148,8 +148,8 @@ describe("wsNativeApi", () => { emitPush(WS_CHANNELS.serverWelcome, { cwd: "/tmp/workspace", projectName: "t3-code", - bootstrapProjectId: ProjectId.makeUnsafe("project-1"), - bootstrapThreadId: ThreadId.makeUnsafe("thread-1"), + startupProjectId: ProjectId.makeUnsafe("project-1"), + startupThreadId: ThreadId.makeUnsafe("thread-1"), }); expect(listener).toHaveBeenCalledTimes(1); @@ -157,8 +157,8 @@ describe("wsNativeApi", () => { expect.objectContaining({ cwd: "/tmp/workspace", projectName: "t3-code", - bootstrapProjectId: "project-1", - bootstrapThreadId: "thread-1", + startupProjectId: "project-1", + startupThreadId: "thread-1", }), ); }); diff --git a/apps/web/src/wsNativeApi.ts b/apps/web/src/wsNativeApi.ts index 042875f6f7..6cd615ab50 100644 --- a/apps/web/src/wsNativeApi.ts +++ b/apps/web/src/wsNativeApi.ts @@ -8,7 +8,7 @@ import { WS_CHANNELS, WS_METHODS, type WsWelcomePayload, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { showContextMenuFallback } from "./contextMenuFallback"; import { WsTransport } from "./wsTransport"; diff --git a/apps/web/src/wsTransport.test.ts b/apps/web/src/wsTransport.test.ts index 55ff2556e4..e5902b20f7 100644 --- a/apps/web/src/wsTransport.test.ts +++ b/apps/web/src/wsTransport.test.ts @@ -1,4 +1,4 @@ -import { WS_CHANNELS } from "@t3tools/contracts"; +import { WS_CHANNELS } from "@tero/contracts"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { WsTransport } from "./wsTransport"; diff --git a/apps/web/src/wsTransport.ts b/apps/web/src/wsTransport.ts index 12c9a6d958..66741bdc2a 100644 --- a/apps/web/src/wsTransport.ts +++ b/apps/web/src/wsTransport.ts @@ -5,8 +5,8 @@ import { WebSocketResponse, type WsResponse as WsResponseMessage, WsResponse as WsResponseSchema, -} from "@t3tools/contracts"; -import { decodeUnknownJsonResult, formatSchemaError } from "@t3tools/shared/schemaJson"; +} from "@tero/contracts"; +import { decodeUnknownJsonResult, formatSchemaError } from "@tero/shared/schemaJson"; import { Result, Schema } from "effect"; type PushListener = (message: WsPushMessage) => void; diff --git a/bun.lock b/bun.lock index 857d3a83c7..9e5d5bdbbd 100644 --- a/bun.lock +++ b/bun.lock @@ -3,7 +3,7 @@ "configVersion": 1, "workspaces": { "": { - "name": "@t3tools/monorepo", + "name": "@tero/monorepo", "devDependencies": { "@types/node": "catalog:", "oxfmt": "^0.40.0", @@ -13,7 +13,7 @@ }, }, "apps/desktop": { - "name": "@t3tools/desktop", + "name": "@tero/desktop", "version": "0.0.14", "dependencies": { "effect": "catalog:", @@ -21,8 +21,8 @@ "electron-updater": "^6.6.2", }, "devDependencies": { - "@t3tools/contracts": "workspace:*", - "@t3tools/shared": "workspace:*", + "@tero/contracts": "workspace:*", + "@tero/shared": "workspace:*", "@types/node": "catalog:", "tsdown": "catalog:", "typescript": "catalog:", @@ -31,7 +31,7 @@ }, }, "apps/marketing": { - "name": "@t3tools/marketing", + "name": "@tero/marketing", "version": "0.0.0", "dependencies": { "astro": "^6.0.4", @@ -60,9 +60,9 @@ "devDependencies": { "@effect/language-service": "catalog:", "@effect/vitest": "catalog:", - "@t3tools/contracts": "workspace:*", - "@t3tools/shared": "workspace:*", - "@t3tools/web": "workspace:*", + "@tero/contracts": "workspace:*", + "@tero/shared": "workspace:*", + "@tero/web": "workspace:*", "@types/bun": "catalog:", "@types/node": "catalog:", "@types/ws": "^8.5.13", @@ -72,7 +72,7 @@ }, }, "apps/web": { - "name": "@t3tools/web", + "name": "@tero/web", "version": "0.0.14", "dependencies": { "@base-ui/react": "^1.2.0", @@ -83,8 +83,8 @@ "@formkit/auto-animate": "^0.9.0", "@lexical/react": "^0.41.0", "@pierre/diffs": "^1.1.0-beta.16", - "@t3tools/contracts": "workspace:*", - "@t3tools/shared": "workspace:*", + "@tero/contracts": "workspace:*", + "@tero/shared": "workspace:*", "@tanstack/react-pacer": "^0.19.4", "@tanstack/react-query": "^5.90.0", "@tanstack/react-router": "^1.160.2", @@ -123,7 +123,7 @@ }, }, "packages/contracts": { - "name": "@t3tools/contracts", + "name": "@tero/contracts", "version": "0.0.14", "dependencies": { "effect": "catalog:", @@ -137,10 +137,10 @@ }, }, "packages/shared": { - "name": "@t3tools/shared", + "name": "@tero/shared", "version": "0.0.0-alpha.1", "dependencies": { - "@t3tools/contracts": "workspace:*", + "@tero/contracts": "workspace:*", "effect": "catalog:", }, "devDependencies": { @@ -152,12 +152,12 @@ }, }, "scripts": { - "name": "@t3tools/scripts", + "name": "@tero/scripts", "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.77", "@effect/platform-node": "catalog:", - "@t3tools/contracts": "workspace:*", - "@t3tools/shared": "workspace:*", + "@tero/contracts": "workspace:*", + "@tero/shared": "workspace:*", "effect": "catalog:", }, "devDependencies": { @@ -663,17 +663,17 @@ "@szmarczak/http-timer": ["@szmarczak/http-timer@4.0.6", "", { "dependencies": { "defer-to-connect": "^2.0.0" } }, "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w=="], - "@t3tools/contracts": ["@t3tools/contracts@workspace:packages/contracts"], + "@tero/contracts": ["@tero/contracts@workspace:packages/contracts"], - "@t3tools/desktop": ["@t3tools/desktop@workspace:apps/desktop"], + "@tero/desktop": ["@tero/desktop@workspace:apps/desktop"], - "@t3tools/marketing": ["@t3tools/marketing@workspace:apps/marketing"], + "@tero/marketing": ["@tero/marketing@workspace:apps/marketing"], - "@t3tools/scripts": ["@t3tools/scripts@workspace:scripts"], + "@tero/scripts": ["@tero/scripts@workspace:scripts"], - "@t3tools/shared": ["@t3tools/shared@workspace:packages/shared"], + "@tero/shared": ["@tero/shared@workspace:packages/shared"], - "@t3tools/web": ["@t3tools/web@workspace:apps/web"], + "@tero/web": ["@tero/web@workspace:apps/web"], "@tailwindcss/node": ["@tailwindcss/node@4.2.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.1" } }, "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg=="], diff --git a/package.json b/package.json index 02e71cf097..79a7a96ea6 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "@t3tools/monorepo", + "name": "@tero/monorepo", "private": true, "workspaces": { "packages": [ @@ -22,24 +22,25 @@ }, "type": "module", "scripts": { + "postinstall": "node scripts/sync-workspace-links.mjs", "dev": "node scripts/dev-runner.ts dev", "dev:server": "node scripts/dev-runner.ts dev:server", "dev:web": "node scripts/dev-runner.ts dev:web", - "dev:marketing": "turbo run dev --filter=@t3tools/marketing", + "dev:marketing": "turbo run dev --filter=@tero/marketing", "dev:desktop": "node scripts/dev-runner.ts dev:desktop", - "start": "turbo run start --filter=t3", - "start:desktop": "turbo run start --filter=@t3tools/desktop", - "start:marketing": "turbo run preview --filter=@t3tools/marketing", + "start": "turbo run start --filter=tero", + "start:desktop": "turbo run start --filter=@tero/desktop", + "start:marketing": "turbo run preview --filter=@tero/marketing", "build": "turbo run build", - "build:marketing": "turbo run build --filter=@t3tools/marketing", - "build:desktop": "turbo run build --filter=@t3tools/desktop --filter=t3", + "build:marketing": "turbo run build --filter=@tero/marketing", + "build:desktop": "turbo run build --filter=@tero/desktop --filter=tero", "typecheck": "turbo run typecheck", "lint": "oxlint --report-unused-disable-directives", "test": "turbo run test", - "test:desktop-smoke": "turbo run smoke-test --filter=@t3tools/desktop", + "test:desktop-smoke": "turbo run smoke-test --filter=@tero/desktop", "fmt": "oxfmt", "fmt:check": "oxfmt --check", - "build:contracts": "turbo run build --filter=@t3tools/contracts", + "build:contracts": "turbo run build --filter=@tero/contracts", "dist:desktop:artifact": "node scripts/build-desktop-artifact.ts", "dist:desktop:dmg": "node scripts/build-desktop-artifact.ts --platform mac --target dmg", "dist:desktop:dmg:arm64": "node scripts/build-desktop-artifact.ts --platform mac --target dmg --arch arm64", diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 35f8b15498..ac4266e2b9 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -1,5 +1,5 @@ { - "name": "@t3tools/contracts", + "name": "@tero/contracts", "version": "0.0.14", "private": true, "files": [ diff --git a/packages/contracts/src/writing.ts b/packages/contracts/src/writing.ts new file mode 100644 index 0000000000..fdf2c1ed6f --- /dev/null +++ b/packages/contracts/src/writing.ts @@ -0,0 +1,6 @@ +import { Schema } from "effect"; + +export const AgentProfileId = Schema.Literals(["writer", "editor", "brainstormer", "continuity"]); +export type AgentProfileId = typeof AgentProfileId.Type; + +export const DEFAULT_AGENT_PROFILE_ID: AgentProfileId = "writer"; diff --git a/packages/contracts/src/ws.ts b/packages/contracts/src/ws.ts index 45ef0512da..e95aed8234 100644 --- a/packages/contracts/src/ws.ts +++ b/packages/contracts/src/ws.ts @@ -166,8 +166,8 @@ export type WsPushSequence = typeof WsPushSequence.Type; export const WsWelcomePayload = Schema.Struct({ cwd: TrimmedNonEmptyString, projectName: TrimmedNonEmptyString, - bootstrapProjectId: Schema.optional(ProjectId), - bootstrapThreadId: Schema.optional(ThreadId), + startupProjectId: Schema.optional(ProjectId), + startupThreadId: Schema.optional(ThreadId), }); export type WsWelcomePayload = typeof WsWelcomePayload.Type; diff --git a/packages/shared/package.json b/packages/shared/package.json index 02ae794d64..407a16838e 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,5 +1,5 @@ { - "name": "@t3tools/shared", + "name": "@tero/shared", "version": "0.0.0-alpha.1", "private": true, "type": "module", @@ -31,6 +31,10 @@ "./schemaJson": { "types": "./src/schemaJson.ts", "import": "./src/schemaJson.ts" + }, + "./runtime": { + "types": "./src/runtime.ts", + "import": "./src/runtime.ts" } }, "scripts": { @@ -39,7 +43,7 @@ "test": "vitest run" }, "dependencies": { - "@t3tools/contracts": "workspace:*", + "@tero/contracts": "workspace:*", "effect": "catalog:" }, "devDependencies": { diff --git a/packages/shared/src/Net.ts b/packages/shared/src/Net.ts index e5ac354f92..639e5017fd 100644 --- a/packages/shared/src/Net.ts +++ b/packages/shared/src/Net.ts @@ -86,7 +86,7 @@ export interface NetServiceShape { * NetService - Service tag for startup networking helpers. */ export class NetService extends ServiceMap.Service()( - "@t3tools/shared/Net/NetService", + "@tero/shared/Net/NetService", ) { static readonly layer = Layer.sync(NetService, () => { /** diff --git a/packages/shared/src/model.test.ts b/packages/shared/src/model.test.ts index d62a273c2c..fc2e9f4a9b 100644 --- a/packages/shared/src/model.test.ts +++ b/packages/shared/src/model.test.ts @@ -5,7 +5,7 @@ import { MODEL_OPTIONS, MODEL_OPTIONS_BY_PROVIDER, CODEX_REASONING_EFFORT_OPTIONS, -} from "@t3tools/contracts"; +} from "@tero/contracts"; import { applyClaudePromptEffortPrefix, diff --git a/packages/shared/src/model.ts b/packages/shared/src/model.ts index 53ebc856fd..3caf632375 100644 --- a/packages/shared/src/model.ts +++ b/packages/shared/src/model.ts @@ -10,7 +10,7 @@ import { type ModelSlug, type ProviderKind, CodexReasoningEffort, -} from "@t3tools/contracts"; +} from "@tero/contracts"; const MODEL_SLUG_SET_BY_PROVIDER: Record> = { claudeAgent: new Set(MODEL_OPTIONS_BY_PROVIDER.claudeAgent.map((option) => option.slug)), diff --git a/packages/shared/src/runtime.test.ts b/packages/shared/src/runtime.test.ts new file mode 100644 index 0000000000..f47668ffdc --- /dev/null +++ b/packages/shared/src/runtime.test.ts @@ -0,0 +1,74 @@ +import { describe, expect, it, vi } from "vitest"; + +import { + getDefaultTeroHomePath, + normalizeEnvAliases, + TERO_DEV_HOME_DIRNAME, + TERO_DEV_RUNNER_ENV_ALIASES, + TERO_HOME_DIRNAME, + TERO_RUNTIME_ENV_ALIASES, +} from "./runtime"; + +describe("getDefaultTeroHomePath", () => { + it("returns the production runtime home path", () => { + expect(getDefaultTeroHomePath("production", "/Users/tester")).toBe( + `/Users/tester/${TERO_HOME_DIRNAME}`, + ); + }); + + it("returns the development runtime home path", () => { + expect(getDefaultTeroHomePath("development", "/Users/tester")).toBe( + `/Users/tester/${TERO_DEV_HOME_DIRNAME}`, + ); + }); +}); + +describe("normalizeEnvAliases", () => { + it("copies legacy values into preferred keys when needed", () => { + const env = { + T3CODE_HOME: "/tmp/legacy", + } as NodeJS.ProcessEnv; + + normalizeEnvAliases(TERO_RUNTIME_ENV_ALIASES, { env }); + + expect(env.TERO_HOME).toBe("/tmp/legacy"); + }); + + it("preserves preferred values when both keys exist", () => { + const env = { + TERO_HOME: "/tmp/preferred", + T3CODE_HOME: "/tmp/legacy", + } as NodeJS.ProcessEnv; + + normalizeEnvAliases(TERO_RUNTIME_ENV_ALIASES, { env }); + + expect(env.TERO_HOME).toBe("/tmp/preferred"); + }); + + it("reports conflicting non-empty values", () => { + const env = { + TERO_HOME: "/tmp/preferred", + T3CODE_HOME: "/tmp/legacy", + } as NodeJS.ProcessEnv; + const onConflict = vi.fn(); + + normalizeEnvAliases(TERO_RUNTIME_ENV_ALIASES, { env, onConflict }); + + expect(onConflict).toHaveBeenCalledWith({ + preferred: "TERO_HOME", + legacy: "T3CODE_HOME", + }); + }); + + it("covers the extra dev-runner compatibility aliases", () => { + const env = { + T3CODE_DEV_INSTANCE: "branch-a", + T3CODE_DESKTOP_WS_URL: "ws://localhost:4000", + } as NodeJS.ProcessEnv; + + normalizeEnvAliases(TERO_DEV_RUNNER_ENV_ALIASES, { env }); + + expect(env.TERO_DEV_INSTANCE).toBe("branch-a"); + expect(env.TERO_DESKTOP_WS_URL).toBe("ws://localhost:4000"); + }); +}); diff --git a/packages/shared/src/runtime.ts b/packages/shared/src/runtime.ts new file mode 100644 index 0000000000..28246b7edf --- /dev/null +++ b/packages/shared/src/runtime.ts @@ -0,0 +1,62 @@ +import { homedir } from "node:os"; +import { join } from "node:path"; + +export type EnvAlias = readonly [preferred: string, legacy: string]; + +export const TERO_HOME_DIRNAME = ".tero"; +export const TERO_DEV_HOME_DIRNAME = ".tero-dev"; + +export const TERO_RUNTIME_ENV_ALIASES = [ + ["TERO_MODE", "T3CODE_MODE"], + ["TERO_PORT", "T3CODE_PORT"], + ["TERO_HOST", "T3CODE_HOST"], + ["TERO_HOME", "T3CODE_HOME"], + ["TERO_NO_BROWSER", "T3CODE_NO_BROWSER"], + ["TERO_AUTH_TOKEN", "T3CODE_AUTH_TOKEN"], + ["TERO_AUTO_BOOTSTRAP_PROJECT_FROM_CWD", "T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD"], + ["TERO_LOG_WS_EVENTS", "T3CODE_LOG_WS_EVENTS"], +] as const satisfies ReadonlyArray; + +export const TERO_DEV_RUNNER_ENV_ALIASES = [ + ["TERO_PORT_OFFSET", "T3CODE_PORT_OFFSET"], + ["TERO_DEV_INSTANCE", "T3CODE_DEV_INSTANCE"], + ...TERO_RUNTIME_ENV_ALIASES, + ["TERO_DESKTOP_WS_URL", "T3CODE_DESKTOP_WS_URL"], +] as const satisfies ReadonlyArray; + +export function getDefaultTeroHomePath( + runtime: "development" | "production", + homeDirectory = homedir(), +): string { + return join(homeDirectory, runtime === "development" ? TERO_DEV_HOME_DIRNAME : TERO_HOME_DIRNAME); +} + +export function normalizeEnvAliases( + aliases: ReadonlyArray, + options: { + readonly env?: NodeJS.ProcessEnv; + readonly onConflict?: (alias: { preferred: string; legacy: string }) => void; + } = {}, +): void { + const env = options.env ?? process.env; + + for (const [preferred, legacy] of aliases) { + const preferredValue = env[preferred]?.trim(); + const legacyValue = env[legacy]?.trim(); + if ( + preferredValue !== undefined && + legacyValue !== undefined && + preferredValue.length > 0 && + legacyValue.length > 0 && + preferredValue !== legacyValue + ) { + options.onConflict?.({ preferred, legacy }); + } + } + + for (const [preferred, legacy] of aliases) { + if (env[preferred] === undefined && env[legacy] !== undefined) { + env[preferred] = env[legacy]; + } + } +} diff --git a/packages/shared/src/shell.test.ts b/packages/shared/src/shell.test.ts index e2393eefff..d5b81172b2 100644 --- a/packages/shared/src/shell.test.ts +++ b/packages/shared/src/shell.test.ts @@ -10,7 +10,7 @@ describe("extractPathFromShellOutput", () => { it("extracts the path between capture markers", () => { expect( extractPathFromShellOutput( - "__T3CODE_PATH_START__\n/opt/homebrew/bin:/usr/bin\n__T3CODE_PATH_END__\n", + "__TERO_PATH_START__\n/opt/homebrew/bin:/usr/bin\n__TERO_PATH_END__\n", ), ).toBe("/opt/homebrew/bin:/usr/bin"); }); @@ -18,7 +18,7 @@ describe("extractPathFromShellOutput", () => { it("ignores shell startup noise around the capture markers", () => { expect( extractPathFromShellOutput( - "Welcome to fish\n__T3CODE_PATH_START__\n/opt/homebrew/bin:/usr/bin\n__T3CODE_PATH_END__\nBye\n", + "Welcome to fish\n__TERO_PATH_START__\n/opt/homebrew/bin:/usr/bin\n__TERO_PATH_END__\nBye\n", ), ).toBe("/opt/homebrew/bin:/usr/bin"); }); @@ -36,7 +36,7 @@ describe("readPathFromLoginShell", () => { args: ReadonlyArray, options: { encoding: "utf8"; timeout: number }, ) => string - >(() => "__T3CODE_ENV_PATH_START__\n/a:/b\n__T3CODE_ENV_PATH_END__\n"); + >(() => "__TERO_ENV_PATH_START__\n/a:/b\n__TERO_ENV_PATH_END__\n"); expect(readPathFromLoginShell("/opt/homebrew/bin/fish", execFile)).toBe("/a:/b"); expect(execFile).toHaveBeenCalledTimes(1); @@ -54,8 +54,8 @@ describe("readPathFromLoginShell", () => { expect(args).toHaveLength(2); expect(args?.[0]).toBe("-ilc"); expect(args?.[1]).toContain("printenv PATH || true"); - expect(args?.[1]).toContain("__T3CODE_ENV_PATH_START__"); - expect(args?.[1]).toContain("__T3CODE_ENV_PATH_END__"); + expect(args?.[1]).toContain("__TERO_ENV_PATH_START__"); + expect(args?.[1]).toContain("__TERO_ENV_PATH_END__"); expect(options).toEqual({ encoding: "utf8", timeout: 5000 }); }); }); @@ -70,12 +70,12 @@ describe("readEnvironmentFromLoginShell", () => { ) => string >(() => [ - "__T3CODE_ENV_PATH_START__", + "__TERO_ENV_PATH_START__", "/a:/b", - "__T3CODE_ENV_PATH_END__", - "__T3CODE_ENV_SSH_AUTH_SOCK_START__", + "__TERO_ENV_PATH_END__", + "__TERO_ENV_SSH_AUTH_SOCK_START__", "/tmp/secretive.sock", - "__T3CODE_ENV_SSH_AUTH_SOCK_END__", + "__TERO_ENV_SSH_AUTH_SOCK_END__", ].join("\n"), ); @@ -95,11 +95,11 @@ describe("readEnvironmentFromLoginShell", () => { ) => string >(() => [ - "__T3CODE_ENV_PATH_START__", + "__TERO_ENV_PATH_START__", "/a:/b", - "__T3CODE_ENV_PATH_END__", - "__T3CODE_ENV_SSH_AUTH_SOCK_START__", - "__T3CODE_ENV_SSH_AUTH_SOCK_END__", + "__TERO_ENV_PATH_END__", + "__TERO_ENV_SSH_AUTH_SOCK_START__", + "__TERO_ENV_SSH_AUTH_SOCK_END__", ].join("\n"), ); @@ -116,7 +116,7 @@ describe("readEnvironmentFromLoginShell", () => { options: { encoding: "utf8"; timeout: number }, ) => string >(() => - ["__T3CODE_ENV_CUSTOM_VAR_START__", " padded value ", "__T3CODE_ENV_CUSTOM_VAR_END__"].join( + ["__TERO_ENV_CUSTOM_VAR_START__", " padded value ", "__TERO_ENV_CUSTOM_VAR_END__"].join( "\n", ), ); diff --git a/packages/shared/src/shell.ts b/packages/shared/src/shell.ts index f1d60bf334..448e1763e7 100644 --- a/packages/shared/src/shell.ts +++ b/packages/shared/src/shell.ts @@ -1,7 +1,7 @@ import { execFileSync } from "node:child_process"; -const PATH_CAPTURE_START = "__T3CODE_PATH_START__"; -const PATH_CAPTURE_END = "__T3CODE_PATH_END__"; +const PATH_CAPTURE_START = "__TERO_PATH_START__"; +const PATH_CAPTURE_END = "__TERO_PATH_END__"; const SHELL_ENV_NAME_PATTERN = /^[A-Z0-9_]+$/; type ExecFileSyncLike = ( @@ -30,11 +30,11 @@ export function readPathFromLoginShell( } function envCaptureStart(name: string): string { - return `__T3CODE_ENV_${name}_START__`; + return `__TERO_ENV_${name}_START__`; } function envCaptureEnd(name: string): string { - return `__T3CODE_ENV_${name}_END__`; + return `__TERO_ENV_${name}_END__`; } function buildEnvironmentCaptureCommand(names: ReadonlyArray): string { diff --git a/postmortem.md b/postmortem.md new file mode 100644 index 0000000000..b5c6d3f66d --- /dev/null +++ b/postmortem.md @@ -0,0 +1,313 @@ +# Postmortem + +## Summary + +This investigation started as a user-facing app-breakage incident and turned into a forensic/debug task across the installed `T3 Code (Alpha)` app and the local `t3code-ero` repo. + +At the start, the visible symptom was that the installed app in `/Applications/T3 Code (Alpha).app` repeatedly became unusable. Recovery worked by deleting local support/state while keeping the app bundle: + +- `~/.t3` +- `~/Library/Application Support/t3code` +- `~/Library/Preferences/com.t3tools.t3code.plist` + +That was the first major signal that the problem was persisted-state corruption or incompatibility, not damage to the installed app bundle itself. + +The investigation then proved a stronger version of that theory: + +- launching the repo desktop app could mutate the same persisted database used by the installed app +- the repo snapshot and the installed app were not schema-compatible +- once the repo app touched the installed app's state, the installed app could break immediately on next launch + +The rest of the work focused on: + +1. proving the exact poisoning mechanism +2. isolating repo runtime state from installed-app state +3. cleaning up the local dev/runtime path so the fix was not just a pile of one-off hacks + +## User Impact + +Observed user-facing failures included: + +- installed app landing on a broken or blank screen +- add-project flow appearing to freeze or silently fail +- file picker closing without the selected project being added +- later clicks on `Add project` doing nothing +- SQL-related failures in the installed app +- backend decode/migration failures from persisted state +- repo dev launches opening unwanted browser tabs +- repo desktop and repo web/dev behavior colliding with installed-app data + +## What Happened + +### Phase 1: Initial app cleanup was incomplete + +The first cleanup removed: + +- `/Applications/T3 Code (Alpha).app` +- `~/Library/Application Support/t3code` +- `~/Library/Preferences/com.t3tools.t3code.plist` + +That turned out not to be enough, because the critical state also lived under: + +- `~/.t3` + +Once `~/.t3` was removed, the installed app recovered more reliably. + +### Phase 2: The first clear persisted-state failures appeared + +Two important failure signatures showed up: + +1. Orchestration decode failure from persisted event state + +The packaged app logs showed: + +- `Decode error in OrchestrationEventStore.readFromSequence:rowToEvent: Missing key at ["payload"]["defaultModel"]` + +That established that persisted local data could be unreadable under the current app version. + +2. SQLite schema mismatch in `projection_threads` + +Later, the installed app failed with: + +- `ProjectionThreadRepository.getById:query` +- underlying SQLite error: `no such column: model` + +This was the decisive clue that the installed app was hitting a schema shape it no longer expected. + +### Phase 3: The repo app was proven to poison the installed app + +The most important forensic finding of the whole investigation was this: + +- a repo-launched desktop app touched `~/.t3` +- it ran migration `16_CanonicalizeModelSelections` +- that migration removed `projection_threads.model` +- the installed app binary still queried `projection_threads.model` +- the installed app then broke immediately with `no such column: model` + +This proved: + +- the installed app and repo snapshot were using the same persisted DB +- they were schema-incompatible +- repo runtime work alone could poison the installed app without any large rename pass + +This turned the investigation from vague “something is broken” debugging into a concrete cross-version state-isolation problem. + +### Phase 4: The repo desktop launch path was not actually isolated + +The desktop side had several issues layered on top of each other: + +1. The repo desktop app could come up on the installed app's state root instead of a dev-only root. +2. The backend child process could be started in the wrong way. +3. That wrong backend path could behave like a web/server run and open browser tabs. +4. Old Bun resolution made the launcher pick the wrong Bun binary. + +Examples of what we observed during that stage: + +- repo desktop launch logging `baseDir: /Users/raulrodrigues/.t3` +- unexpected browser tabs opening while trying to launch the repo desktop app +- Electron being used to run the backend entrypoint +- an older Homebrew Bun install being picked up instead of the user's real `~/.bun/bin/bun` + +### Phase 5: Desktop isolation was enforced + +To stop further poisoning while the investigation continued, the repo desktop runtime was hardened: + +- local/unpackaged desktop runs default to `~/.tero-dev` +- local desktop runs use a separate Electron `userData` surface +- backend spawn is forced into explicit desktop mode via CLI flags +- backend spawn is forced to a dedicated home-dir instead of inheriting ambiguous defaults +- backend spawn disables browser-open behavior explicitly + +This was intentionally defensive. It was more forceful than the ideal final architecture, but it stopped the repo app from mutating the installed app's `~/.t3` state. + +Once that was in place, the repo desktop app finally launched in a stable, isolated way: + +- mode: `desktop` +- base dir: `~/.tero-dev` +- `noBrowser: true` + +### Phase 6: Web/dev isolation was tightened too + +After desktop was contained, attention shifted to the web/dev path. + +The root problem there was: + +- local repo web/dev defaulted to non-dev state unless explicitly overridden + +That was changed so local dev also defaults to: + +- `~/.tero-dev` + +This brought desktop and web/dev into the same isolation model. + +### Phase 7: A second hidden problem surfaced: workspace package resolution was broken + +While isolating the runtime, another independent issue became clear: + +- runtime workspace imports like `@tero/shared/*` and `@tero/contracts` were not resolving correctly in this checkout + +This affected: + +- desktop runtime +- `scripts/dev-runner.ts` +- Vite/web runtime +- typechecking in some packages + +Initial short-term workarounds used: + +- repo-relative imports in desktop runtime and dev-runner +- Vite aliases in `apps/web/vite.config.ts` + +Those were necessary to keep the investigation moving, but they were not the right final fix. + +### Phase 8: The runtime hack debt was replaced with a workspace-link fix + +The underlying issue turned out to be that the root workspace package links were simply missing from `node_modules`. + +There was no functioning: + +- `node_modules/@tero/contracts` +- `node_modules/@tero/shared` +- or even `node_modules/@tero/*` scope directory + +That explained why so many runtime entrypoints were failing. + +The final fix for that was: + +- add `scripts/sync-workspace-links.mjs` +- wire it into root `package.json` `postinstall` +- recreate proper root `node_modules/@tero/*` symlinks for workspace packages + +After that: + +- normal package imports worked again +- repo-relative runtime import hacks were removed +- temporary Vite aliases were removed +- the cleanup could be completed on top of a proper shared fix instead of app-specific workarounds + +## Root Causes + +### Primary root cause + +The installed app and the local repo runtime were sharing persisted state that was not schema-compatible. + +Most importantly: + +- the installed app used `~/.t3` +- repo runtime could also end up using `~/.t3` +- both sides expected different DB/event shapes + +### Secondary root cause + +The desktop launch/runtime contract was too implicit and too easy to drift into the wrong mode. + +Examples: + +- desktop backend could behave like a generic web server +- browser-open behavior could happen unexpectedly +- base-dir selection depended on fragile runtime assumptions + +### Tertiary root cause + +Workspace package linking in the checkout was broken, which caused local runtime entrypoints to fail to resolve internal packages. + +That issue did not directly cause the installed-app poisoning, but it made the investigation and the local dev story much more fragile. + +## Evidence Collected + +Key evidence gathered during the investigation included: + +- deleting support/state restored the installed app while keeping the bundle +- logs showing decode failure on persisted orchestration state +- logs showing SQL error `no such column: model` +- proof that repo desktop launch had touched `~/.t3` +- proof that repo desktop launch had run migration `16_CanonicalizeModelSelections` +- proof that local repo desktop later ran safely on `~/.tero-dev` +- proof that local repo web/dev later ran safely on `~/.tero-dev` +- proof that browser auto-open disappeared once the backend path was corrected +- proof that `node_modules/@tero/*` links were missing and then restored + +## What Was Changed + +### Isolation and runtime safety + +- local desktop runs now isolate to `~/.tero-dev` +- local web/dev runs now isolate to `~/.tero-dev` +- local desktop backend launch explicitly forces: + - `--mode desktop` + - `--no-browser` + - `--home-dir ` + - `--auth-token ` +- desktop runtime now uses the correct Bun binary from `~/.bun/bin/bun` + +### Startup / restore handling + +- web startup restore flow was improved so the UI is less likely to strand on `No active thread` + +### Workspace runtime fix + +- added `scripts/sync-workspace-links.mjs` +- added root `postinstall` hook to sync workspace package links +- restored normal package imports in: + - desktop runtime + - desktop shell env helper + - dev-runner + - web/Vite path + +### Investigation artifacts + +- `CURRENT-TASK.md` was maintained as the running incident log +- `TODO.md` was added to track the broader workspace-link follow-up +- temporary forensic logging was added when needed and removed once the system stabilized + +## Recovery Procedure That Worked + +When the installed app was poisoned, the reliable recovery was: + +1. keep `/Applications/T3 Code (Alpha).app` +2. delete: + - `~/.t3` + - `~/Library/Application Support/t3code` + - `~/Library/Preferences/com.t3tools.t3code.plist` +3. relaunch the installed app + +This consistently restored the installed app without needing to replace the bundle itself. + +## What Was Misleading During The Investigation + +Several early signals were real but incomplete: + +- reinstalling the app bundle sometimes seemed to help, but the real issue was persisted local state +- the add-project bug looked like a pure file-picker failure, but at least part of it was a stalled or poisoned downstream flow +- browser tabs opening during repo runs looked like “maybe this is expected web dev behavior,” but for this repo it was actually evidence that the wrong runtime path was being used +- some failures looked like product bugs when they were actually the result of cross-version state collision + +## Final State + +At the end of the cleanup: + +- repo desktop app runs in isolated desktop mode on `~/.tero-dev` +- repo web/dev runs on `~/.tero-dev` +- the unwanted browser-open path is no longer part of the validated repo dev flow +- workspace package links under `node_modules/@tero/*` exist again +- temporary runtime import hacks were removed +- temporary Vite alias hacks were removed +- `bun fmt` passes +- `bun lint` passes +- `bun typecheck` passes + +## Lessons + +1. State isolation between installed apps and local repo/dev runs must be explicit, not inferred. +2. Cross-version SQLite/event-schema compatibility is a hard boundary. If two runtimes can point at the same DB, that boundary must be enforced. +3. Temporary forensic hardening is acceptable during containment, but it should be cleaned up after the root cause is understood. +4. Broken workspace linking creates wide, misleading failure surfaces. Fixing it centrally is much better than app-by-app import hacks. +5. When repeated recovery is “delete local state, keep the app bundle,” treat persisted-state poisoning as the default theory until disproven. + +## Follow-up + +Remaining follow-up is mostly structural, not incident-response: + +- keep `CURRENT-TASK.md` as the detailed incident log for this task history +- keep `TODO.md` for broader workspace/runtime cleanup tracking +- if desired later, simplify the desktop launch path further now that the isolation and workspace-link layers are stable diff --git a/prompt.md b/prompt.md new file mode 100644 index 0000000000..dca57b305a --- /dev/null +++ b/prompt.md @@ -0,0 +1,113 @@ +You are working in this repo: + +/Users/raulrodrigues/workspace/personal/t3code-ero + +This is an active forensic/debug task around a repeatable app-breakage issue. + +Critical context: + +- The installed app in `/Applications/T3 Code (Alpha).app` has repeatedly broken while work was being done in this repo. +- Recovery has worked by deleting persisted local state while keeping the app bundle: + - ~/.t3 + - ~/Library/Application Support/t3code + - ~/Library/Preferences/com.t3tools.t3code.plist +- That strongly suggests persisted state corruption/incompatibility, not app bundle corruption. +- I will use the installed app from `/Applications` as the probe: + - you make repo changes + - you run the repo commands + - I launch/test the installed app + - if it breaks, that is evidence +- You are allowed to make code changes in the repo. +- You are expected to run the relevant commands in the repo as part of the investigation. + +Primary goal: +Figure out what kinds of repo changes or runtime behavior poison the installed app, and preserve the evidence in CURRENT-TASK.md so the investigation survives future breakage. + +Required workflow: + +1. Inspect the relevant startup/state code paths. +2. Make careful changes that improve investigation quality, safety, logging, isolation, or path handling. +3. Run the required repo commands after meaningful code changes: + - `bun fmt` + - `bun lint` + - `bun typecheck` +4. If tests are needed, never run `bun test`; use `bun run test`. +5. After each meaningful change set and command run, stop and tell me exactly what to test in the installed app. +6. Update CURRENT-TASK.md continuously with findings, theories, changes, and outcomes. + +Important: + +- Do not skip the command runs. We need the full real workflow because part of the point is to figure out what behavior or change sequence is triggering the installed app breakage. +- Work in very small, intentional batches. Do not accumulate a large set of changes before stopping. +- Prefer one narrowly scoped change set at a time so any installed-app breakage can be correlated to a small, useful diff. +- After each small batch, run the required commands, summarize the batch, and wait for the installed-app test result before broadening scope. +- Do not use destructive git commands. +- Do not assume the worktree is clean. +- Avoid unrelated cleanup. +- Do not do a blind broad rename pass yet. This is forensic/debug work first. + +Focus files first: + +- apps/desktop/src/main.ts +- apps/server/src/main.ts +- apps/server/src/config.ts +- apps/server/src/os-jank.ts +- apps/web/src/store.ts +- apps/server/src/codexAppServerManager.ts +- apps/server/src/orchestration/Layers/ProviderCommandReactor.ts +- CURRENT-TASK.md + +What I want from you: + +- A concise state-path / restore-path matrix covering: + - packaged app behavior + - dev behavior + - legacy t3code paths + - new tero paths + - env var fallbacks + - collision risk +- A ranked list of likely “poison on startup” mechanisms, with code references. +- Code or documentation changes that help the investigation. +- Command results from: + - `bun fmt` + - `bun lint` + - `bun typecheck` +- Updates to CURRENT-TASK.md after each meaningful finding or change. + +Testing workflow: + +- After each meaningful small change set and command run, report: + - what changed + - why it may or may not affect the installed app + - whether `bun fmt`, `bun lint`, and `bun typecheck` passed + - exactly what I should test in `/Applications/T3 Code (Alpha).app` +- I will test the installed app manually. +- If it breaks, treat that exact small change set plus command run sequence as evidence. +- If it does not break, treat that as evidence too and write it down in CURRENT-TASK.md. + +When the installed app breaks: + +- Update CURRENT-TASK.md with: + - the exact most recent changes + - the exact commands that were run + - what I observed in the installed app + - whether the app bundle was kept + - what local state was removed to recover + - what this implies for the theory +- Narrow the theory based on the latest evidence. + +Constraints: + +- Follow AGENTS.md. +- Before considering any coding task complete, `bun fmt`, `bun lint`, and `bun typecheck` must pass. +- Never run `bun test`; use `bun run test` if tests are needed. +- Do not use destructive git commands. +- Do not assume the worktree is clean. +- Avoid unrelated cleanup. + +Success means: + +- CURRENT-TASK.md becomes a reliable incident log. +- We narrow which changes or command sequences correlate with app breakage. +- We identify which persisted-state or startup-restore paths are responsible. +- We get to a concrete mitigation plan before resuming the rename. diff --git a/scripts/build-desktop-artifact.ts b/scripts/build-desktop-artifact.ts index 0b875721fd..73568fbb7b 100644 --- a/scripts/build-desktop-artifact.ts +++ b/scripts/build-desktop-artifact.ts @@ -168,7 +168,7 @@ interface StagePackageJson { readonly name: string; readonly version: string; readonly buildVersion: string; - readonly t3codeCommitHash: string; + readonly teroCommitHash: string; readonly private: true; readonly description: string; readonly author: string; @@ -195,15 +195,15 @@ const AzureTrustedSigningOptionsConfig = Config.all({ }); const BuildEnvConfig = Config.all({ - platform: Config.schema(BuildPlatform, "T3CODE_DESKTOP_PLATFORM").pipe(Config.option), - target: Config.string("T3CODE_DESKTOP_TARGET").pipe(Config.option), - arch: Config.schema(BuildArch, "T3CODE_DESKTOP_ARCH").pipe(Config.option), - version: Config.string("T3CODE_DESKTOP_VERSION").pipe(Config.option), - outputDir: Config.string("T3CODE_DESKTOP_OUTPUT_DIR").pipe(Config.option), - skipBuild: Config.boolean("T3CODE_DESKTOP_SKIP_BUILD").pipe(Config.withDefault(false)), - keepStage: Config.boolean("T3CODE_DESKTOP_KEEP_STAGE").pipe(Config.withDefault(false)), - signed: Config.boolean("T3CODE_DESKTOP_SIGNED").pipe(Config.withDefault(false)), - verbose: Config.boolean("T3CODE_DESKTOP_VERBOSE").pipe(Config.withDefault(false)), + platform: Config.schema(BuildPlatform, "TERO_DESKTOP_PLATFORM").pipe(Config.option), + target: Config.string("TERO_DESKTOP_TARGET").pipe(Config.option), + arch: Config.schema(BuildArch, "TERO_DESKTOP_ARCH").pipe(Config.option), + version: Config.string("TERO_DESKTOP_VERSION").pipe(Config.option), + outputDir: Config.string("TERO_DESKTOP_OUTPUT_DIR").pipe(Config.option), + skipBuild: Config.boolean("TERO_DESKTOP_SKIP_BUILD").pipe(Config.withDefault(false)), + keepStage: Config.boolean("TERO_DESKTOP_KEEP_STAGE").pipe(Config.withDefault(false)), + signed: Config.boolean("TERO_DESKTOP_SIGNED").pipe(Config.withDefault(false)), + verbose: Config.boolean("TERO_DESKTOP_VERBOSE").pipe(Config.withDefault(false)), }); const resolveBooleanFlag = (flag: Option.Option, envValue: boolean) => @@ -317,7 +317,7 @@ function stageMacIcons(stageResourcesDir: string, verbose: boolean) { } const tmpRoot = yield* fs.makeTempDirectoryScoped({ - prefix: "t3code-icon-build-", + prefix: "tero-icon-build-", }); const iconPngPath = path.join(stageResourcesDir, "icon.png"); @@ -426,6 +426,7 @@ function resolveGitHubPublishConfig(): } | undefined { const rawRepo = + process.env.TERO_DESKTOP_UPDATE_REPOSITORY?.trim() || process.env.T3CODE_DESKTOP_UPDATE_REPOSITORY?.trim() || process.env.GITHUB_REPOSITORY?.trim() || ""; @@ -449,9 +450,9 @@ const createBuildConfig = Effect.fn("createBuildConfig")(function* ( signed: boolean, ) { const buildConfig: Record = { - appId: "com.t3tools.t3code", + appId: "com.tero.desktop", productName, - artifactName: "T3-Code-${version}-${arch}.${ext}", + artifactName: "Tero-${version}-${arch}.${ext}", directories: { buildResources: "apps/desktop/resources", }, @@ -564,7 +565,7 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( const commitHash = resolveGitCommitHash(repoRoot); const mkdir = options.keepStage ? fs.makeTempDirectory : fs.makeTempDirectoryScoped; const stageRoot = yield* mkdir({ - prefix: `t3code-desktop-${options.platform}-stage-`, + prefix: `tero-desktop-${options.platform}-stage-`, }); const stageAppDir = path.join(stageRoot, "app"); @@ -618,18 +619,18 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( yield* fs.copy(stageResourcesDir, path.join(stageAppDir, "apps/desktop/prod-resources")); const stagePackageJson: StagePackageJson = { - name: "t3-code-desktop", + name: "tero-desktop", version: appVersion, buildVersion: appVersion, - t3codeCommitHash: commitHash, + teroCommitHash: commitHash, private: true, - description: "T3 Code desktop build", - author: "T3 Tools", + description: "Tero desktop build", + author: "Tero", main: "apps/desktop/dist-electron/main.js", build: yield* createBuildConfig( options.platform, options.target, - desktopPackageJson.productName ?? "T3 Code", + desktopPackageJson.productName ?? "Tero", options.signed, ), dependencies: { @@ -728,49 +729,49 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( const buildDesktopArtifactCli = Command.make("build-desktop-artifact", { platform: Flag.choice("platform", BuildPlatform.literals).pipe( - Flag.withDescription("Build platform (env: T3CODE_DESKTOP_PLATFORM)."), + Flag.withDescription("Build platform (env: TERO_DESKTOP_PLATFORM)."), Flag.optional, ), target: Flag.string("target").pipe( Flag.withDescription( - "Artifact target, for example dmg/AppImage/nsis (env: T3CODE_DESKTOP_TARGET).", + "Artifact target, for example dmg/AppImage/nsis (env: TERO_DESKTOP_TARGET).", ), Flag.optional, ), arch: Flag.choice("arch", BuildArch.literals).pipe( - Flag.withDescription("Build arch, for example arm64/x64/universal (env: T3CODE_DESKTOP_ARCH)."), + Flag.withDescription("Build arch, for example arm64/x64/universal (env: TERO_DESKTOP_ARCH)."), Flag.optional, ), buildVersion: Flag.string("build-version").pipe( - Flag.withDescription("Artifact version metadata (env: T3CODE_DESKTOP_VERSION)."), + Flag.withDescription("Artifact version metadata (env: TERO_DESKTOP_VERSION)."), Flag.optional, ), outputDir: Flag.string("output-dir").pipe( - Flag.withDescription("Output directory for artifacts (env: T3CODE_DESKTOP_OUTPUT_DIR)."), + Flag.withDescription("Output directory for artifacts (env: TERO_DESKTOP_OUTPUT_DIR)."), Flag.optional, ), skipBuild: Flag.boolean("skip-build").pipe( Flag.withDescription( - "Skip `bun run build:desktop` and use existing dist artifacts (env: T3CODE_DESKTOP_SKIP_BUILD).", + "Skip `bun run build:desktop` and use existing dist artifacts (env: TERO_DESKTOP_SKIP_BUILD).", ), Flag.optional, ), keepStage: Flag.boolean("keep-stage").pipe( - Flag.withDescription("Keep temporary staging files (env: T3CODE_DESKTOP_KEEP_STAGE)."), + Flag.withDescription("Keep temporary staging files (env: TERO_DESKTOP_KEEP_STAGE)."), Flag.optional, ), signed: Flag.boolean("signed").pipe( Flag.withDescription( - "Enable signing/notarization discovery; Windows uses Azure Trusted Signing (env: T3CODE_DESKTOP_SIGNED).", + "Enable signing/notarization discovery; Windows uses Azure Trusted Signing (env: TERO_DESKTOP_SIGNED).", ), Flag.optional, ), verbose: Flag.boolean("verbose").pipe( - Flag.withDescription("Stream subprocess stdout (env: T3CODE_DESKTOP_VERBOSE)."), + Flag.withDescription("Stream subprocess stdout (env: TERO_DESKTOP_VERBOSE)."), Flag.optional, ), }).pipe( - Command.withDescription("Build a desktop artifact for T3 Code."), + Command.withDescription("Build a desktop artifact for Tero."), Command.withHandler((input) => Effect.flatMap(resolveBuildOptions(input), buildDesktopArtifact)), ); diff --git a/scripts/dev-runner.test.ts b/scripts/dev-runner.test.ts index e76d187188..ddc6b2c06f 100644 --- a/scripts/dev-runner.test.ts +++ b/scripts/dev-runner.test.ts @@ -13,12 +13,12 @@ import { it.layer(NodeServices.layer)("dev-runner", (it) => { describe("resolveOffset", () => { - it.effect("uses explicit T3CODE_PORT_OFFSET when provided", () => + it.effect("uses explicit TERO_PORT_OFFSET when provided", () => Effect.sync(() => { const result = resolveOffset({ portOffset: 12, devInstance: undefined }); assert.deepStrictEqual(result, { offset: 12, - source: "T3CODE_PORT_OFFSET=12", + source: "TERO_PORT_OFFSET=12", }); }), ); @@ -40,20 +40,20 @@ it.layer(NodeServices.layer)("dev-runner", (it) => { }), ); - assert.ok(error.includes("Invalid T3CODE_PORT_OFFSET")); + assert.ok(error.includes("Invalid TERO_PORT_OFFSET")); }), ); }); describe("createDevRunnerEnv", () => { - it.effect("defaults T3CODE_HOME to ~/.t3 when not provided", () => + it.effect("defaults TERO_HOME to ~/.tero-dev when not provided", () => Effect.gen(function* () { const env = yield* createDevRunnerEnv({ mode: "dev", baseEnv: {}, serverOffset: 0, webOffset: 0, - t3Home: undefined, + teroHome: undefined, authToken: undefined, noBrowser: undefined, autoBootstrapProjectFromCwd: undefined, @@ -63,7 +63,7 @@ it.layer(NodeServices.layer)("dev-runner", (it) => { devUrl: undefined, }); - assert.equal(env.T3CODE_HOME, resolve(homedir(), ".t3")); + assert.equal(env.TERO_HOME, resolve(homedir(), ".tero-dev")); }), ); @@ -74,7 +74,7 @@ it.layer(NodeServices.layer)("dev-runner", (it) => { baseEnv: {}, serverOffset: 0, webOffset: 0, - t3Home: "/tmp/custom-t3", + teroHome: "/tmp/custom-tero", authToken: "secret", noBrowser: true, autoBootstrapProjectFromCwd: false, @@ -84,13 +84,13 @@ it.layer(NodeServices.layer)("dev-runner", (it) => { devUrl: new URL("http://localhost:7331"), }); - assert.equal(env.T3CODE_HOME, resolve("/tmp/custom-t3")); - assert.equal(env.T3CODE_PORT, "4222"); + assert.equal(env.TERO_HOME, resolve("/tmp/custom-tero")); + assert.equal(env.TERO_PORT, "4222"); assert.equal(env.VITE_WS_URL, "ws://localhost:4222"); - assert.equal(env.T3CODE_NO_BROWSER, "1"); - assert.equal(env.T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD, "0"); - assert.equal(env.T3CODE_LOG_WS_EVENTS, "1"); - assert.equal(env.T3CODE_HOST, "0.0.0.0"); + assert.equal(env.TERO_NO_BROWSER, "1"); + assert.equal(env.TERO_AUTO_BOOTSTRAP_PROJECT_FROM_CWD, "0"); + assert.equal(env.TERO_LOG_WS_EVENTS, "1"); + assert.equal(env.TERO_HOST, "0.0.0.0"); assert.equal(env.VITE_DEV_SERVER_URL, "http://localhost:7331/"); }), ); @@ -100,11 +100,11 @@ it.layer(NodeServices.layer)("dev-runner", (it) => { const env = yield* createDevRunnerEnv({ mode: "dev", baseEnv: { - T3CODE_LOG_WS_EVENTS: "keep-me-out", + TERO_LOG_WS_EVENTS: "keep-me-out", }, serverOffset: 0, webOffset: 0, - t3Home: undefined, + teroHome: undefined, authToken: undefined, noBrowser: undefined, autoBootstrapProjectFromCwd: undefined, @@ -114,8 +114,8 @@ it.layer(NodeServices.layer)("dev-runner", (it) => { devUrl: undefined, }); - assert.equal(env.T3CODE_MODE, "web"); - assert.equal(env.T3CODE_LOG_WS_EVENTS, undefined); + assert.equal(env.TERO_MODE, "web"); + assert.equal(env.TERO_LOG_WS_EVENTS, undefined); }), ); @@ -126,7 +126,7 @@ it.layer(NodeServices.layer)("dev-runner", (it) => { baseEnv: {}, serverOffset: 0, webOffset: 0, - t3Home: undefined, + teroHome: undefined, authToken: undefined, noBrowser: undefined, autoBootstrapProjectFromCwd: undefined, @@ -136,18 +136,18 @@ it.layer(NodeServices.layer)("dev-runner", (it) => { devUrl: undefined, }); - assert.equal(env.T3CODE_LOG_WS_EVENTS, "0"); + assert.equal(env.TERO_LOG_WS_EVENTS, "0"); }), ); - it.effect("uses custom t3Home when provided", () => + it.effect("uses custom teroHome when provided", () => Effect.gen(function* () { const env = yield* createDevRunnerEnv({ mode: "dev", baseEnv: {}, serverOffset: 0, webOffset: 0, - t3Home: "/tmp/my-t3", + teroHome: "/tmp/my-tero", authToken: undefined, noBrowser: undefined, autoBootstrapProjectFromCwd: undefined, @@ -157,7 +157,7 @@ it.layer(NodeServices.layer)("dev-runner", (it) => { devUrl: undefined, }); - assert.equal(env.T3CODE_HOME, resolve("/tmp/my-t3")); + assert.equal(env.TERO_HOME, resolve("/tmp/my-tero")); }), ); }); diff --git a/scripts/dev-runner.ts b/scripts/dev-runner.ts index da2b3ebe54..13a476caf9 100644 --- a/scripts/dev-runner.ts +++ b/scripts/dev-runner.ts @@ -1,10 +1,13 @@ #!/usr/bin/env node -import { homedir } from "node:os"; - import * as NodeRuntime from "@effect/platform-node/NodeRuntime"; import * as NodeServices from "@effect/platform-node/NodeServices"; -import { NetService } from "@t3tools/shared/Net"; +import { NetService } from "@tero/shared/Net"; +import { + getDefaultTeroHomePath, + normalizeEnvAliases, + TERO_DEV_RUNNER_ENV_ALIASES, +} from "@tero/shared/runtime"; import { Config, Data, Effect, Hash, Layer, Logger, Option, Path, Schema } from "effect"; import { Argument, Command, Flag } from "effect/unstable/cli"; import { ChildProcess } from "effect/unstable/process"; @@ -14,23 +17,21 @@ const BASE_WEB_PORT = 5733; const MAX_HASH_OFFSET = 3000; const MAX_PORT = 65535; -export const DEFAULT_T3_HOME = Effect.map(Effect.service(Path.Path), (path) => - path.join(homedir(), ".t3"), -); +normalizeEnvAliases(TERO_DEV_RUNNER_ENV_ALIASES); const MODE_ARGS = { dev: [ "run", "dev", "--ui=tui", - "--filter=@t3tools/contracts", - "--filter=@t3tools/web", - "--filter=t3", + "--filter=@tero/contracts", + "--filter=@tero/web", + "--filter=tero", "--parallel", ], - "dev:server": ["run", "dev", "--filter=t3"], - "dev:web": ["run", "dev", "--filter=@t3tools/web"], - "dev:desktop": ["run", "dev", "--filter=@t3tools/desktop", "--filter=@t3tools/web", "--parallel"], + "dev:server": ["run", "dev", "--filter=tero"], + "dev:web": ["run", "dev", "--filter=@tero/web"], + "dev:desktop": ["run", "dev", "--filter=@tero/desktop", "--filter=@tero/web", "--parallel"], } as const satisfies Record>; type DevMode = keyof typeof MODE_ARGS; @@ -70,8 +71,8 @@ const optionalUrlConfig = (name: string): Config.Config => ); const OffsetConfig = Config.all({ - portOffset: optionalIntegerConfig("T3CODE_PORT_OFFSET"), - devInstance: optionalStringConfig("T3CODE_DEV_INSTANCE"), + portOffset: optionalIntegerConfig("TERO_PORT_OFFSET"), + devInstance: optionalStringConfig("TERO_DEV_INSTANCE"), }); export function resolveOffset(config: { @@ -80,11 +81,11 @@ export function resolveOffset(config: { }): { readonly offset: number; readonly source: string } { if (config.portOffset !== undefined) { if (config.portOffset < 0) { - throw new Error(`Invalid T3CODE_PORT_OFFSET: ${config.portOffset}`); + throw new Error(`Invalid TERO_PORT_OFFSET: ${config.portOffset}`); } return { offset: config.portOffset, - source: `T3CODE_PORT_OFFSET=${config.portOffset}`, + source: `TERO_PORT_OFFSET=${config.portOffset}`, }; } @@ -94,11 +95,11 @@ export function resolveOffset(config: { } if (/^\d+$/.test(seed)) { - return { offset: Number(seed), source: `numeric T3CODE_DEV_INSTANCE=${seed}` }; + return { offset: Number(seed), source: `numeric TERO_DEV_INSTANCE=${seed}` }; } const offset = ((Hash.string(seed) >>> 0) % MAX_HASH_OFFSET) + 1; - return { offset, source: `hashed T3CODE_DEV_INSTANCE=${seed}` }; + return { offset, source: `hashed TERO_DEV_INSTANCE=${seed}` }; } function resolveBaseDir(baseDir: string | undefined): Effect.Effect { @@ -110,7 +111,7 @@ function resolveBaseDir(baseDir: string | undefined): Effect.Effect({ interface DevRunnerCliInput { readonly mode: DevMode; - readonly t3Home: string | undefined; + readonly teroHome: string | undefined; readonly authToken: string | undefined; readonly noBrowser: boolean | undefined; readonly autoBootstrapProjectFromCwd: boolean | undefined; @@ -381,7 +382,7 @@ export function runDevRunnerWithInput(input: DevRunnerCliInput) { Effect.mapError( (cause) => new DevRunnerError({ - message: "Failed to read T3CODE_PORT_OFFSET/T3CODE_DEV_INSTANCE configuration.", + message: "Failed to read TERO_PORT_OFFSET/TERO_DEV_INSTANCE configuration.", cause, }), ), @@ -397,9 +398,9 @@ export function runDevRunnerWithInput(input: DevRunnerCliInput) { }); const envOverrides = { - noBrowser: readOptionalBooleanEnv("T3CODE_NO_BROWSER"), - autoBootstrapProjectFromCwd: readOptionalBooleanEnv("T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD"), - logWebSocketEvents: readOptionalBooleanEnv("T3CODE_LOG_WS_EVENTS"), + noBrowser: readOptionalBooleanEnv("TERO_NO_BROWSER"), + autoBootstrapProjectFromCwd: readOptionalBooleanEnv("TERO_AUTO_BOOTSTRAP_PROJECT_FROM_CWD"), + logWebSocketEvents: readOptionalBooleanEnv("TERO_LOG_WS_EVENTS"), }; const { serverOffset, webOffset } = yield* resolveModePortOffsets({ @@ -414,7 +415,7 @@ export function runDevRunnerWithInput(input: DevRunnerCliInput) { baseEnv: process.env, serverOffset, webOffset, - t3Home: input.t3Home, + teroHome: input.teroHome, authToken: input.authToken, noBrowser: resolveOptionalBooleanOverride(input.noBrowser, envOverrides.noBrowser), autoBootstrapProjectFromCwd: resolveOptionalBooleanOverride( @@ -436,7 +437,7 @@ export function runDevRunnerWithInput(input: DevRunnerCliInput) { : ""; yield* Effect.logInfo( - `[dev-runner] mode=${input.mode} source=${source}${selectionSuffix} serverPort=${String(env.T3CODE_PORT)} webPort=${String(env.PORT)} baseDir=${String(env.T3CODE_HOME)}`, + `[dev-runner] mode=${input.mode} source=${source}${selectionSuffix} serverPort=${String(env.TERO_PORT)} webPort=${String(env.PORT)} baseDir=${String(env.TERO_HOME)}`, ); if (input.dryRun) { @@ -484,38 +485,38 @@ const devRunnerCli = Command.make("dev-runner", { mode: Argument.choice("mode", DEV_RUNNER_MODES).pipe( Argument.withDescription("Development mode to run."), ), - t3Home: Flag.string("home-dir").pipe( - Flag.withDescription("Base directory for all T3 Code data (equivalent to T3CODE_HOME)."), - Flag.withFallbackConfig(optionalStringConfig("T3CODE_HOME")), + teroHome: Flag.string("home-dir").pipe( + Flag.withDescription("Base directory for all Tero data (equivalent to TERO_HOME)."), + Flag.withFallbackConfig(optionalStringConfig("TERO_HOME")), ), authToken: Flag.string("auth-token").pipe( - Flag.withDescription("Auth token (forwards to T3CODE_AUTH_TOKEN)."), + Flag.withDescription("Auth token (forwards to TERO_AUTH_TOKEN)."), Flag.withAlias("token"), - Flag.withFallbackConfig(optionalStringConfig("T3CODE_AUTH_TOKEN")), + Flag.withFallbackConfig(optionalStringConfig("TERO_AUTH_TOKEN")), ), noBrowser: Flag.boolean("no-browser").pipe( - Flag.withDescription("Browser auto-open toggle (equivalent to T3CODE_NO_BROWSER)."), - Flag.withFallbackConfig(optionalBooleanConfig("T3CODE_NO_BROWSER")), + Flag.withDescription("Browser auto-open toggle (equivalent to TERO_NO_BROWSER)."), + Flag.withFallbackConfig(optionalBooleanConfig("TERO_NO_BROWSER")), ), autoBootstrapProjectFromCwd: Flag.boolean("auto-bootstrap-project-from-cwd").pipe( Flag.withDescription( - "Auto-bootstrap toggle (equivalent to T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD).", + "Auto-bootstrap toggle (equivalent to TERO_AUTO_BOOTSTRAP_PROJECT_FROM_CWD).", ), - Flag.withFallbackConfig(optionalBooleanConfig("T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD")), + Flag.withFallbackConfig(optionalBooleanConfig("TERO_AUTO_BOOTSTRAP_PROJECT_FROM_CWD")), ), logWebSocketEvents: Flag.boolean("log-websocket-events").pipe( - Flag.withDescription("WebSocket event logging toggle (equivalent to T3CODE_LOG_WS_EVENTS)."), + Flag.withDescription("WebSocket event logging toggle (equivalent to TERO_LOG_WS_EVENTS)."), Flag.withAlias("log-ws-events"), - Flag.withFallbackConfig(optionalBooleanConfig("T3CODE_LOG_WS_EVENTS")), + Flag.withFallbackConfig(optionalBooleanConfig("TERO_LOG_WS_EVENTS")), ), host: Flag.string("host").pipe( - Flag.withDescription("Server host/interface override (forwards to T3CODE_HOST)."), - Flag.withFallbackConfig(optionalStringConfig("T3CODE_HOST")), + Flag.withDescription("Server host/interface override (forwards to TERO_HOST)."), + Flag.withFallbackConfig(optionalStringConfig("TERO_HOST")), ), port: Flag.integer("port").pipe( Flag.withSchema(Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 65535 }))), - Flag.withDescription("Server port override (forwards to T3CODE_PORT)."), - Flag.withFallbackConfig(optionalPortConfig("T3CODE_PORT")), + Flag.withDescription("Server port override (forwards to TERO_PORT)."), + Flag.withFallbackConfig(optionalPortConfig("TERO_PORT")), ), devUrl: Flag.string("dev-url").pipe( Flag.withSchema(Schema.URLFromString), diff --git a/scripts/lib/workspace-links.d.mts b/scripts/lib/workspace-links.d.mts new file mode 100644 index 0000000000..979bccf63e --- /dev/null +++ b/scripts/lib/workspace-links.d.mts @@ -0,0 +1,16 @@ +export function expandWorkspacePattern(repoRoot: string, pattern: string): Promise; + +export function collectWorkspacePackages(repoRoot: string): Promise< + Array<{ + name: string; + directory: string; + }> +>; + +export function ensureWorkspaceLink(input: { + rootNodeModulesDir: string; + name: string; + directory: string; +}): Promise; + +export function syncWorkspaceLinks(repoRoot: string): Promise; diff --git a/scripts/lib/workspace-links.d.ts b/scripts/lib/workspace-links.d.ts new file mode 100644 index 0000000000..979bccf63e --- /dev/null +++ b/scripts/lib/workspace-links.d.ts @@ -0,0 +1,16 @@ +export function expandWorkspacePattern(repoRoot: string, pattern: string): Promise; + +export function collectWorkspacePackages(repoRoot: string): Promise< + Array<{ + name: string; + directory: string; + }> +>; + +export function ensureWorkspaceLink(input: { + rootNodeModulesDir: string; + name: string; + directory: string; +}): Promise; + +export function syncWorkspaceLinks(repoRoot: string): Promise; diff --git a/scripts/lib/workspace-links.mjs b/scripts/lib/workspace-links.mjs new file mode 100644 index 0000000000..2410654fc7 --- /dev/null +++ b/scripts/lib/workspace-links.mjs @@ -0,0 +1,152 @@ +import { lstat, mkdir, readFile, readdir, readlink, rm, symlink } from "node:fs/promises"; +import { dirname, join, relative, resolve } from "node:path"; + +async function pathStat(path) { + try { + return await lstat(path); + } catch { + return null; + } +} + +async function listChildDirectories(path) { + try { + const entries = await readdir(path, { withFileTypes: true }); + return entries.filter((entry) => entry.isDirectory()).map((entry) => join(path, entry.name)); + } catch { + return []; + } +} + +function dedupeAndSort(paths) { + return [...new Set(paths)].toSorted((left, right) => left.localeCompare(right)); +} + +async function expandWorkspacePatternFrom(baseDir, segments) { + if (segments.length === 0) { + return [baseDir]; + } + + const [segment, ...rest] = segments; + if (segment === "**") { + const directMatches = await expandWorkspacePatternFrom(baseDir, rest); + const childDirectories = await listChildDirectories(baseDir); + const recursiveMatches = await Promise.all( + childDirectories.map((childDirectory) => + expandWorkspacePatternFrom(childDirectory, segments), + ), + ); + return dedupeAndSort([...directMatches, ...recursiveMatches.flat()]); + } + + if (segment === "*") { + const childDirectories = await listChildDirectories(baseDir); + const matches = await Promise.all( + childDirectories.map((childDirectory) => expandWorkspacePatternFrom(childDirectory, rest)), + ); + return dedupeAndSort(matches.flat()); + } + + const nextDirectory = join(baseDir, segment); + const stat = await pathStat(nextDirectory); + if (!stat?.isDirectory()) { + return []; + } + + return expandWorkspacePatternFrom(nextDirectory, rest); +} + +async function readJson(path) { + return JSON.parse(await readFile(path, "utf8")); +} + +export async function expandWorkspacePattern(repoRoot, pattern) { + const segments = pattern.split(/[\\/]/).filter((segment) => segment.length > 0); + return dedupeAndSort(await expandWorkspacePatternFrom(repoRoot, segments)); +} + +export async function collectWorkspacePackages(repoRoot) { + const rootPackageJsonPath = join(repoRoot, "package.json"); + const rootPackageJson = await readJson(rootPackageJsonPath); + const packagePatterns = rootPackageJson.workspaces?.packages ?? []; + const directories = dedupeAndSort( + ( + await Promise.all(packagePatterns.map((pattern) => expandWorkspacePattern(repoRoot, pattern))) + ).flat(), + ); + + const packages = []; + const seenByName = new Map(); + + for (const directory of directories) { + const packageJsonPath = join(directory, "package.json"); + try { + const manifest = await readJson(packageJsonPath); + if (typeof manifest.name !== "string" || manifest.name.length === 0) { + continue; + } + + const existingDirectory = seenByName.get(manifest.name); + if (existingDirectory && existingDirectory !== directory) { + throw new Error( + `Duplicate workspace package name "${manifest.name}" in ${existingDirectory} and ${directory}.`, + ); + } + + seenByName.set(manifest.name, directory); + packages.push({ + name: manifest.name, + directory, + }); + } catch (error) { + if (error instanceof Error && error.message.startsWith("Duplicate workspace package name")) { + throw error; + } + if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") { + continue; + } + throw error; + } + } + + return packages.toSorted((left, right) => left.name.localeCompare(right.name)); +} + +export async function ensureWorkspaceLink({ rootNodeModulesDir, name, directory }) { + const destination = join(rootNodeModulesDir, name); + const target = relative(dirname(destination), directory); + + await mkdir(dirname(destination), { recursive: true }); + + try { + const stat = await lstat(destination); + if (stat.isSymbolicLink()) { + const existingTarget = await readlink(destination); + if (existingTarget === target) { + return; + } + } + await rm(destination, { recursive: true, force: true }); + } catch { + // Missing destination is fine. + } + + await symlink(target, destination, process.platform === "win32" ? "junction" : "dir"); +} + +export async function syncWorkspaceLinks(repoRoot) { + const resolvedRepoRoot = resolve(repoRoot); + const rootNodeModulesDir = join(resolvedRepoRoot, "node_modules"); + const workspacePackages = await collectWorkspacePackages(resolvedRepoRoot); + + await mkdir(rootNodeModulesDir, { recursive: true }); + await Promise.all( + workspacePackages.map(({ name, directory }) => + ensureWorkspaceLink({ + rootNodeModulesDir, + name, + directory, + }), + ), + ); +} diff --git a/scripts/package.json b/scripts/package.json index d9db897ea2..6f4a4bbb5a 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -1,5 +1,5 @@ { - "name": "@t3tools/scripts", + "name": "@tero/scripts", "private": true, "type": "module", "scripts": { @@ -12,8 +12,8 @@ "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.77", "@effect/platform-node": "catalog:", - "@t3tools/contracts": "workspace:*", - "@t3tools/shared": "workspace:*", + "@tero/contracts": "workspace:*", + "@tero/shared": "workspace:*", "effect": "catalog:" }, "devDependencies": { diff --git a/scripts/sync-workspace-links.mjs b/scripts/sync-workspace-links.mjs new file mode 100644 index 0000000000..551458c7fc --- /dev/null +++ b/scripts/sync-workspace-links.mjs @@ -0,0 +1,5 @@ +import { resolve } from "node:path"; + +import { syncWorkspaceLinks } from "./lib/workspace-links.mjs"; + +await syncWorkspaceLinks(resolve(import.meta.dirname, "..")); diff --git a/scripts/workspace-links.test.ts b/scripts/workspace-links.test.ts new file mode 100644 index 0000000000..db12d3c1cd --- /dev/null +++ b/scripts/workspace-links.test.ts @@ -0,0 +1,111 @@ +import { mkdtemp, mkdir, realpath, rm, symlink, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { dirname, join, relative } from "node:path"; +import { afterEach, describe, expect, it } from "vitest"; + +import { + collectWorkspacePackages, + ensureWorkspaceLink, + expandWorkspacePattern, +} from "./lib/workspace-links.mjs"; + +const tempDirectories: string[] = []; + +async function makeRepoFixture() { + const root = await mkdtemp(join(tmpdir(), "tero-workspace-links-")); + tempDirectories.push(root); + return root; +} + +describe("workspace link helpers", () => { + afterEach(async () => { + await Promise.all( + tempDirectories + .splice(0, tempDirectories.length) + .map((directory) => rm(directory, { recursive: true, force: true })), + ); + }); + + it("expands wildcard workspace patterns deterministically", async () => { + const repoRoot = await makeRepoFixture(); + await mkdir(join(repoRoot, "apps", "desktop"), { recursive: true }); + await mkdir(join(repoRoot, "apps", "web"), { recursive: true }); + + expect(await expandWorkspacePattern(repoRoot, "apps/*")).toEqual([ + join(repoRoot, "apps", "desktop"), + join(repoRoot, "apps", "web"), + ]); + }); + + it("collects workspace packages from configured patterns", async () => { + const repoRoot = await makeRepoFixture(); + await mkdir(join(repoRoot, "apps", "server"), { recursive: true }); + await mkdir(join(repoRoot, "packages", "shared"), { recursive: true }); + await writeFile( + join(repoRoot, "package.json"), + JSON.stringify({ + workspaces: { + packages: ["apps/*", "packages/*"], + }, + }), + ); + await writeFile( + join(repoRoot, "apps", "server", "package.json"), + JSON.stringify({ name: "tero" }), + ); + await writeFile( + join(repoRoot, "packages", "shared", "package.json"), + JSON.stringify({ name: "@tero/shared" }), + ); + + expect(await collectWorkspacePackages(repoRoot)).toEqual([ + { name: "@tero/shared", directory: join(repoRoot, "packages", "shared") }, + { name: "tero", directory: join(repoRoot, "apps", "server") }, + ]); + }); + + it("rejects duplicate workspace package names", async () => { + const repoRoot = await makeRepoFixture(); + await mkdir(join(repoRoot, "apps", "server"), { recursive: true }); + await mkdir(join(repoRoot, "packages", "server"), { recursive: true }); + await writeFile( + join(repoRoot, "package.json"), + JSON.stringify({ + workspaces: { + packages: ["apps/*", "packages/*"], + }, + }), + ); + await writeFile( + join(repoRoot, "apps", "server", "package.json"), + JSON.stringify({ name: "tero" }), + ); + await writeFile( + join(repoRoot, "packages", "server", "package.json"), + JSON.stringify({ name: "tero" }), + ); + + await expect(collectWorkspacePackages(repoRoot)).rejects.toThrow( + 'Duplicate workspace package name "tero"', + ); + }); + + it("creates or repairs workspace symlinks under node_modules", async () => { + const repoRoot = await makeRepoFixture(); + const packageDirectory = join(repoRoot, "packages", "shared"); + const rootNodeModulesDir = join(repoRoot, "node_modules"); + const destination = join(rootNodeModulesDir, "@tero", "shared"); + + await mkdir(packageDirectory, { recursive: true }); + await mkdir(dirname(destination), { recursive: true }); + await symlink(relative(dirname(destination), join(repoRoot, "wrong-target")), destination); + + await ensureWorkspaceLink({ + rootNodeModulesDir, + name: "@tero/shared", + directory: packageDirectory, + }); + + expect(await realpath(destination)).toBe(await realpath(packageDirectory)); + }); +}); diff --git a/turbo.json b/turbo.json index 9359376f59..7956b2214e 100644 --- a/turbo.json +++ b/turbo.json @@ -5,13 +5,13 @@ "VITE_WS_URL", "VITE_DEV_SERVER_URL", "ELECTRON_RENDERER_PORT", - "T3CODE_LOG_WS_EVENTS", - "T3CODE_MODE", - "T3CODE_PORT", - "T3CODE_NO_BROWSER", - "T3CODE_HOME", - "T3CODE_AUTH_TOKEN", - "T3CODE_DESKTOP_WS_URL" + "TERO_LOG_WS_EVENTS", + "TERO_MODE", + "TERO_PORT", + "TERO_NO_BROWSER", + "TERO_HOME", + "TERO_AUTH_TOKEN", + "TERO_DESKTOP_WS_URL" ], "tasks": { "build": { @@ -19,7 +19,7 @@ "outputs": ["dist/**", "dist-electron/**"] }, "dev": { - "dependsOn": ["@t3tools/contracts#build"], + "dependsOn": ["@tero/contracts#build"], "cache": false, "persistent": true },