Skip to content
JacobZyyPublic

About

Personal CLI by Jacob and Taotao, built with Rust

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

jt

Personal Rust CLI. Name joins Jacob and Taotao.

Create a Codex local environment from the current project directory:

jt codex init

Writes .codex/environments/environment.toml and updates .worktreeinclude without running setup or installing project dependencies. Commit both files so new worktrees receive the setup and local configuration copy rules. Existing environment files, including custom configuration, are automatically backed up and overwritten; identical content is left unchanged. Symlinked configuration paths are rejected. No AGENTS.md files are written. jt must be available on PATH when the generated setup runs.

Initialization also extends the project's existing Lefthook or simple-git-hooks configuration with a post-checkout task and runs that manager's installer from the primary worktree. Existing business hooks and local overrides are preserved. If no configuration exists, a declared manager dependency selects the manager. JT does not add dependencies. Conflicting managers, unavailable executables, foreign installed hooks, or failed installation are reported; automatic native Git inheritance is enabled only after installation is verified. Commit the updated manager configuration so later manager reinstalls retain the task. Nonempty Lefthook YAML flow mappings and ESM default re-exports from another file require a supported local configuration form first; JT reports the unsupported form without rewriting it.

.worktreeinclude remains the only copy list. Defaults select .env.local, .env.*.local, .nlab/*.local.json, .codex/config.toml, .codex/hooks.json, .claude/settings.local.json, CLAUDE.local.md, AGENTS.override.md, and the selected manager's local override files. Custom entries remain intact. JT does not select entire configuration directories, locks, logs, reports, or indexes. Codex uses its native copy support; generated setup fills missing configuration before dependency preparation. Ordinary git worktree add uses the manager's post-checkout task, which only handles initial linked-worktree checkout and never installs dependencies or indexes.

Missing files are copied once from the same repository's primary worktree. Existing target configuration takes precedence; each worktree can then change independently. JT's copy logic also preserves valid legacy NLab settings and existing Lefthook override aliases. Codex's native copy list only protects exact existing paths; it cannot express priority between alternate filenames. JT never deletes existing configuration or guesses which files Codex copied. For an old worktree, git worktree add --no-checkout, or creation with hooks disabled, fill missing configuration explicitly:

jt codex inherit

Optionally preview an update without replacing the existing configuration:

jt codex init --check

--check writes a separate candidate when the generated content differs and reports whether the existing file is an unchanged managed template. Rerun jt codex init to apply the update; --update is also accepted and behaves the same as the default. Before replacement, the original bytes are saved in the reported environment.toml.backup-*/environment.toml path; copy that file back to restore. Neither command runs setup. --check does not update the copy list or manager configuration and does not install hooks. Candidate and recovery files should stay out of commits.

Node projects support npm and pnpm. packageManager selects the manager when declared; otherwise the lockfile selects it (npm when neither exists). Unsupported or ambiguous lockfiles fail before writing. Setup uses npm ci or pnpm install --frozen-lockfile --prefer-offline when the matching lockfile exists, otherwise a normal install. Setup first checks the exact dependency inputs, manager version, Node runtime/ABI, OS/architecture, install options, configuration, and local dependency layout. A successful installation records a runtime stamp in node_modules. An exact declared packageManager version must match the available manager; a mismatch stops setup. Compatible worktrees clone these stamped dependencies using filesystem copy-on-write, preserve relative symlinks, and adjust package-manager launchers to the new root. Dependency writes then remain local to the worktree. Repeated setup preserves a compatible stamped environment. Concurrent setup is serialized by an owner-marked lock; after an interrupted process, verify that its owner has stopped before removing the reported lock.

Missing installation evidence, changed lockfiles/configuration, external/absolute dependency links, install lifecycle scripts, incompatible local packages, or unavailable copy-on-write trigger normal installation. Workspace and internal file:/link: dependencies require matching project contents and links confined to the new root; workspace source changes trigger installation. Root and local package installation lifecycle scripts always run normal installation, including repeated setup, because arbitrary ignored generated outputs cannot be proven valid. Ignored local package directories whose full layout is unknown also use normal installation without a reusable stamp. Existing unverified dependencies are not deleted to make cloning possible. Run the generated setup once in the primary environment to establish an installation baseline; jt codex init only installs the selected Git hook manager's hooks. Actions use only nonempty scripts present in package.json: Dev (dev, then start), Unit tests (test:unit:run, test:unit, then test), and Type check (type-check, typecheck, then type:check). Test scripts and arguments are preserved.

Projects with Cargo.toml receive Cargo Build and Rust tests actions. These actual commands set CARGO_TARGET_DIR to a common Git cache outside the primary checkout, reusing Cargo's locked build cache across worktrees; an existing environment variable takes precedence. Cargo.lock enables --locked. jt codex init --lightweight additionally sets debug output and incremental compilation defaults to zero in those action commands. This option applies only to Cargo projects and does not change test selection or features. Setup exports are not relied on, and ordinary terminal commands outside these actions keep their original environment. Projects without a supported manifest get no inferred build/test actions.

CodeGraph setup is included only when the current or primary worktree already has .codegraph/codegraph.db. SQLite online backup produces a consistent snapshot including committed WAL pages; setup caches one snapshot in the common Git directory and tries to clone that stable file with copy-on-write. Unsupported filesystems use an independent ordinary copy. It never copies the live DB main file directly or copies daemon PID/socket/lock files. Existing worktree indexes are retained and incrementally synced. Shared DB symlinks/hard links are rejected. Projects that have not enabled CodeGraph are not indexed. If the optional source snapshot cannot be backed up, setup reports the SQLite error and rebuilds the local index without modifying the source database. CodeGraph must be on PATH; index initialization, sync, and other setup failures still stop setup. Logical directory sizes from du do not measure physical copy-on-write savings.

Inspect registered worktrees without deleting historical directories:

jt codex worktrees
jt codex worktrees --check /path/to/evidence.json

The check requires explicit, fresh lifecycle facts from the current Codex task/attachment inventory:

{
  "version": 1,
  "root": "/exact/worktree",
  "head": "WORKTREE_HEAD",
  "primary_head": "CURRENT_PRIMARY_HEAD",
  "attachment_identity_key": "/exact/worktree",
  "all_tasks_known": true,
  "managed": true,
  "pinned": false,
  "permanent": false,
  "shared": false,
  "tasks": [{ "id": "CHAT_ID", "complete": true, "running": false, "retain": false }],
  "validation": { "head": "CURRENT_PRIMARY_HEAD", "command": "cargo test --locked", "exit_code": 0 },
  "ignored_files_reviewed": true,
  "ignored_backups": [{ "source": ".env", "backup": "/outside/worktree/saved.env" }]
}

Task completion, running state, ownership, and validation are caller assertions, not inferred from directory age or private Codex databases. The CLI verifies current Git HEADs, integration into a clean primary branch, and listed ignored-file backups. Use an empty backup list only after reviewing that no necessary ignored files need preserving. Necessary directory contents must be listed as individual files. Primary, locked, pinned, permanent, shared, active, retained, or unknown worktrees remain protected. Passing checks produces the exact supported Codex archive_worktree invocation for review; refresh native task/attachment state before executing it. Native archival preserves uncommitted and unpushed work; ignored files need those external backups.

The CLI does not automatically archive worktrees: Codex exposes no supported completion hook that also proves integration, successful validation, and complete task ownership. SessionEnd can fire on exit or idle and cannot establish these facts. Codex's existing native archive/keep-count lifecycle remains responsible for automatic snapshots and reclamation; existing user settings are preserved. Historical directories with unknown attachment ownership are inventory-only. git worktree prune cleans stale registration and does not reclaim real directories. See the official Codex worktree lifecycle and hook events.

Validate the generated dependency runtime with node --test apps/jt/src/codex/dependencies.test.mjs alongside the Rust checks. The suite exercises real local npm/pnpm installs, clone isolation, incompatible inputs, repeated setup, lifecycle scripts, and concurrent setup protection.

Bootstrap terminal tooling:

jt cli bootstrap

Interactive bootstrap supports macOS, Debian/Ubuntu, and WSL on x86_64 and ARM64. It installs:

  • Fish or Zsh and Starship with Catppuccin Mocha.
  • bat, eza, fd, ripgrep, fzf, btop, zoxide, jq, tldr, git-delta, and lazygit.
  • Fish Git abbreviations/functions plus proxy-on and proxy-off.
  • Zellij when selected.

The command asks before mutation, changes the default shell, writes only jt-managed shell files, backs up replaced files, and configures git-delta globally. Fish users can explicitly enable proxy-on at shell startup; this option defaults to No and validates GitHub through http://127.0.0.1:7890 before exporting proxy variables. Ghostty, fonts, Node.js, fnm, and pnpm stay outside this bootstrap.

Bootstrap automatically loads jt completions in interactive Fish and Zsh shells when jt is on PATH. Load them manually when needed:

jt completions fish | source
source <(jt completions zsh)

Install Ghostty on macOS:

jt ghostty install

This separate interactive command installs Ghostty and Maple Mono NF CN through Homebrew, then writes a jt-managed Ghostty configuration. It backs up replaced files and asks before mutation. Linux and WSL servers are rejected without installation.

Write project Zed settings from the live repository template:

jt zed-conf

The command finds the current Git repository root and writes .zed/settings.json. Existing different content is backed up beside the file before an atomic update. The template is fetched over HTTPS from apps/jt/templates/zed/settings.json on main, so merging a template-only change updates future command runs without releasing a new jt version.

Find statically unused functions, variables, and files:

jt code unused
jt code unused /path/to/project --kind function,variable,file
jt code unused /path/to/project --mode library --json
# compatibility alias
jt unused

PATH can be a project root or a nested source directory/file. jt walks upward to find the nearest JavaScript/TypeScript project (package.json, tsconfig.json, or jsconfig.json), builds one reference graph, and reports findings inside the requested scope. The scan is read-only: it does not run codegraph init, install packages, or write an index.

Projects can commit .nlab/unused.config.json to control the graph:

{
  "version": 2,
  "roots": ["src"],
  "entrypoints": ["src/main.ts"],
  "exclude": [
    "src/types/service-type/**",
    "src/types/service-enums/**",
    "src/mock/**"
  ]
}

roots contains project-relative files or directories; an omitted or empty list means the project root. Version 1 remains readable. Version 2 adds entrypoints, used when HTML/package scripts cannot prove the complete runtime roots. exclude accepts project-relative gitignore-style patterns without negation. Excluded files do not produce findings, but still provide consumer evidence so generated bootstrap code cannot make a live hand-written target look unused. Test files remain a hard boundary. An explicit PATH only narrows output inside configured roots. Absolute paths, .., unknown fields, unsupported versions, and symlinked roots fail before scanning. JSON reports effective scanRoots and exclude values.

The scanner uses Oxc for JavaScript/TypeScript syntax and the project's TypeScript/Volar installation for cross-file and Vue template references. It reports only function/method, variable binding, and file candidates; parameters, catch/import bindings, class fields, abstract methods, and overload signatures are excluded. Owner-aware reachability prevents dead function/file cycles from protecting themselves. --mode app is the default: an export declaration alone and a barrel re-export alone are not symbol usage. --mode library protects only package public-entry closure, not every internal export. Static and bounded dynamic imports, import.meta.glob, CommonJS require/export, type usage, initializer side effects, and unresolved runtime boundaries retain exact/potential/unknown distinctions. Missing TypeScript/Volar dependencies are not installed; affected semantic coverage appears in diagnostics/unknown.

The semantic phase loads the trusted project's installed typescript/vue-tsc packages in a bounded Node.js helper (120-second timeout). Run it only in workspaces whose dependencies you trust.

Entrypoints come from version 2 config, literal HTML module scripts, supported package scripts, and package entry fields; a filename named main is not enough. Entrypoint files are excluded only at file level—their internal declarations remain candidates. Declaration files (*.d.ts) provide type-consumer evidence without becoming findings. Test files (tests/, __tests__/, *.test.*, *.spec.*) are fully filtered. Results are sorted by path and source position. --json writes pure JSON to stdout; errors retain the normal error: stderr format. Unsupported framework auto-imports, reflection, string-based registries, virtual modules, and unbounded runtime loading become diagnostics/unknown rather than false unused findings.

Regression coverage includes projects with known answers under apps/jt/tests/fixtures. unused-golden locks the Oxc fallback; unused-semantic-golden locks TypeScript/Volar, Vue template, and namespace-import behavior when workspace Node.js dependencies are installed.

Write a self-contained interactive call graph:

jt call-graph
jt call-graph src/views --focus queryQcTemplate --depth 3
jt call-graph --output .nlab/call-graph.html --max-nodes 800
jt call-graph --database .nlab/unused-graph.db
jt call-graph --open

The command reuses the same .nlab/unused.config.json roots and exclusions, Oxc graph, and TypeScript/Volar semantic pass as jt unused. It merges repeated call sites into directed edges, keeps import/type-import/re-export/dynamic-import edges distinct, and writes one offline HTML file with no CDN, server, telemetry, or source-code upload. The Canvas view supports pan, zoom, node dragging, search, kind filters, caller/callee focus, configurable depth, and call-site details. Output defaults to .nlab/call-graph.html; jt-generated files can be regenerated, while replacing another HTML file requires --force. The HTML embeds the complete graph and initially displays at most 400 nodes; search and focus can reveal the rest. The same run atomically writes .nlab/unused-graph.db, a read-only SQLite input for @jacob-z/unused-graph-inspector. A 20,000-node/80,000-edge safety ceiling requires narrowing .nlab/unused.config.json for exceptionally large repositories.

Run the Nuxt SSR inspector from this workspace:

NUXT_GRAPH_DATABASE=/path/to/project/.nlab/unused-graph.db \
  pnpm --filter @jacob-z/unused-graph-inspector dev

Its SSR routes query SQLite on the server. The client renders an aggregated project-area overview, then uses Cytoscape.js and ELK for focused directed call flows. The npm executable and jt graph launcher remain deferred.

The graph follows Graphy's split between language analysis, a renderer-independent node/edge model, and an interactive focused view. It does not invent edges for unresolved reflection or string-based dispatch; those gaps remain diagnostics rather than false exact calls.

AI edit hook capabilities are now maintained by nlab. See the jt 4 migration guide for removed commands and retained project data.

The repository is both a Cargo workspace and a pnpm/Turborepo monorepo:

apps/jt                    jt Rust binary and owned assets/templates
apps/unused-graph-inspector Nuxt graph inspection app

Turborepo's experimental Cargo workspace support discovers jt through cargo metadata. pnpm build, pnpm check, pnpm lint, and pnpm test therefore orchestrate both Node packages and Rust crates from one task graph. Cargo and pnpm keep their own manifests and lockfiles as ecosystem dependency sources.

API contract generation is now provided by nlab api. Existing .nlab configuration and generated artifacts remain in use.

Configure Node.js and Rust package release automation:

jt repo cicd

Command operates on current project root. It discovers and validates publishable Node.js packages and Cargo workspace members before making changes. With an existing GitHub origin, behavior stays non-interactive. When Git or origin is missing, an interactive terminal can create one public GitHub repository through an installed, authenticated GitHub CLI (gh). Run gh auth login first when needed.

Repository creation asks twice, defaults both confirmations to No, and validates selected OWNER/REPO against package metadata. It runs git init only after final confirmation and only when needed, then calls gh repo create without committing or pushing. A failed creation or later initialization keeps any local Git repository, GitHub repository, and origin already created so re-running command can continue safely.

After origin validation, command creates:

.github/workflows/npm-release.yml
release-please-config.json
.release-please-manifest.json

The generated caller invokes the versioned reusable workflow in this repository. release-please owns versions, changelogs, Git tags, and GitHub Releases. npm packages publish through npm Trusted Publishing; Rust crates publish to crates.io through Trusted Publishing. jt never versions, tags, or publishes locally and never stores npm or crates.io tokens.

Existing release-please-config.json and .release-please-manifest.json files are preserved so a repository can own custom release configuration. An existing different caller workflow is never overwritten.

Initialize global Node/pnpm environment through Vite+:

jt node init
# Use the official npm registry instead of the default npmmirror:
jt node init --npmregister https://registry.npmjs.org

This interactive command supports macOS/Linux on x86_64 and ARM64. Every Node version is installed through vp env install. On macOS Apple Silicon, a missing arm64 archive triggers installation through a matching x64 Vite+ CLI and requires Rosetta 2. It:

  1. Installs or reuses Vite+, then prepares Node 14.21.3, 16.19.0, 20.11.0, 22.21.0, and 24.15.0.
  2. Installs default pnpm, prewarms pnpm 7.4.1, 9.7.0, and 10.12.4, then installs nrm.
  3. Configures npm registry, nrm sources, and Vite+ shell loaders. --npmregister URL defaults to https://registry.npmmirror.com/. Existing nrm sources are reused by URL; new URLs use jt. Built-in source overrides are preserved, and the zz registry remains configured. The URL must be HTTP(S), without credentials, query, fragment, or whitespace. This controls package installation, migration, and pnpm prewarming. Vite+ itself is always downloaded from npmmirror; Node binaries use their independent download mirror.
  4. Migrates globals whose npm inventory proves an npmjs/npmmirror registry source to Vite+.
  5. Freshly scans old nvm/fnm/Homebrew/pnpm/Bun state. It shows every cleanup action and asks again before deleting anything.

Both confirmations default to No. Canceling first confirmation makes no changes. Incomplete inventory blocks cleanup. Existing Vite+ installation and additive changes remain when a later stage fails or cleanup is declined.

PNPM/Bun package manifests do not prove install provenance. jt reports those globals as unreconstructable and retains PNPM_HOME/Bun targets instead of guessing a registry package or Bun global directory. After cleanup confirmation, jt fresh-checks both legacy inventory and every target Vite+ global before deleting old providers.

Recursive deletion is limited to exact known dedicated nvm/fnm roots. Custom manager roots and unverified fnm wrappers are reported and retained. Shell cleanup follows actual removable providers; a failed shell write stops all later provider deletion.

Write one JT icon into ./public:

jt icon 64
jt icon svg
jt icon animated

Pass a directory as the fourth shell argument to override the destination:

jt icon 64 ./assets
jt icon svg ./assets
jt icon animated ./assets

PNG sizes are 16, 24, 32, 48, 64, 128, 256, 512, and 1024. A PNG selector writes the matching original jt-<size>.png; svg creates jt.svg; animated creates jt-animated.svg. Commands use embedded sources, work offline, create the destination directory, and refuse to overwrite an existing file.

Install

./install.sh

The script builds the current source with cargo build --release --locked, installs jt to ~/.local/bin, and verifies the installed binary. Override the directory when needed:

JT_INSTALL_DIR=/path/to/bin ./install.sh

Install a published release, replacing vX.Y.Z with a tag from GitHub Releases:

cargo install --git https://github.com/JacobZyy/jt-cli --tag vX.Y.Z --locked --root "$HOME/.local" jt

Upgrade an existing user-owned installation:

jt upgrade --check
jt upgrade
jt upgrade --dry-run --force

jt upgrade resolves an exact published GitHub Release and pins its Git commit, asks Cargo to build it in a temporary directory, verifies the staged binary, then atomically replaces ~/.local/bin/jt. A failed post-install version check restores the previous binary. --force reinstalls the current version. The command never runs a shell or sudo. Other paths and package-manager shims must use their original installer or manager. Cargo and Git remain required for jt; persistent rollback is not implemented.

Supported repositories

  • GitHub repository containing Node.js packages, Rust crates, or both
  • Standalone package, Turborepo, npm/pnpm workspace, or Cargo workspace
  • Turborepo validation runs turbo run test build; Cargo workspace validation also runs directly
  • npm or pnpm; pnpm projects declare an exact packageManager, such as pnpm@10.15.0
  • Lockfile optional for npm; an explicit packageManager resolves repositories with stale or multiple lockfiles
  • Publishable Node.js packages use the npm registry, with publishConfig.registry absent or https://registry.npmjs.org
  • Publishable Rust packages use crates.io, with Cargo publish absent or including only crates-io
  • Every publishable package declares same GitHub repository, matching existing origin or selected OWNER/REPO

Private Turborepo roots, private Node.js workspace packages, and Cargo packages with publish = false are allowed and skipped. Yarn, Bun, Deno, GitLab, non-npm Node registries, other Cargo registries, and ecosystems other than Node.js/Rust report an explicit unsupported error.

Turborepo is the monorepo build system supported here. Turbopack is a Next.js bundler, not a monorepo manager.

One-time GitHub, npm, and crates.io setup

  1. Run jt repo cicd, commit generated workflow on a feature branch, then merge it through a PR.
  2. In GitHub Actions settings, grant workflows read/write permission and allow GitHub Actions to create pull requests.
  3. For every npm package, add a GitHub Actions Trusted Publisher in npm package settings:
    • organization or user: package repository owner
    • repository: package repository name
    • workflow filename: npm-release.yml
    • allowed action: npm publish
  4. Publish every new crate manually once, then configure its crates.io Trusted Publisher for the same repository and .github/workflows/npm-release.yml. crates.io cannot use Trusted Publishing for a crate's first release.
  5. Use Conventional Commits on the repository default branch, for example feat: add export command.

npm and crates.io authenticate through OIDC on GitHub-hosted runners. npm validates the caller workflow in the consuming repository, not the central reusable workflow. The workflow uses Node.js 24 and no persistent publish token.

Release flow:

  1. Merge a Conventional Commit PR into the default branch.
  2. Install, test, and build every detected ecosystem. Turborepo orchestrates Node packages and the Cargo workspace through one task graph.
  3. release-please creates or updates one manifest Release PR for all changed packages.
  4. Merge Release PR.
  5. Same workflow validates again; release-please creates versions, CHANGELOG.md files, tags, and GitHub Releases.
  6. Released npm packages publish to npm. Released Cargo workspace packages publish to crates.io in dependency order through release-plz.

Validation produces npm tarballs before release. Only isolated publish jobs receive OIDC permission; npm package lifecycle scripts are disabled during packing and publishing. If registry publishing fails after GitHub Release creation, use Re-run failed jobs.

Generated workflow pins reusable workflow to @v1.4.0; consuming repositories never execute mutable main with write and OIDC permissions.

Releasing jt

Push features through a Conventional Commit PR. .github/workflows/release.yml runs release-please on main; its Release PR owns Cargo.toml, Cargo.lock, CHANGELOG.md, the Git tag, and the GitHub Release. Never version or tag jt locally.

Development

pnpm check
pnpm lint
pnpm test
pnpm build

cargo fmt --all --check
cargo clippy --locked --workspace --all-targets --all-features -- -D warnings

The pnpm commands are the normal repository entrypoints. Direct Cargo formatting and strict Clippy remain explicit release gates while Turbo's Cargo integration is experimental.

Git hook

Activate repository pre-commit hook once per clone:

git config --local core.hooksPath .githooks

Every commit runs cargo test --locked --quiet. Failed tests abort commit. GitHub CI remains final required check because local hooks can be bypassed.

Commit policy

Use Conventional Commits because release-please derives versions and changelog entries from commits on main. Git does not activate repository hooks automatically, and --no-verify can bypass them. For squash merges, enforce Conventional Commit PR titles with GitHub rules or a server-side PR-title check.

release-please currently uses built-in GITHUB_TOKEN. GitHub suppresses workflows triggered by its Release PR. Repositories requiring PR checks need a bot bypass until optional GitHub App token support is added.

License

MIT

About

Personal CLI by Jacob and Taotao, built with Rust

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages