Imported from aadilmallick/global-scripts-manager (
AGENTS.md). Install upstream withnpx skills add aadilmallick/global-scripts-manager. Copyright stays with the author.
AGENTS.md
Guidance for AI agents and developers working on Global Scripts Manager (GSM) — an interactive, cross-platform CLI for managing personal scripts and making them globally callable from anywhere on your system.
Full user + developer documentation lives in
DOCS/. This file is the fast, codebase-oriented orientation. When in doubt, read the code — this file is a map, not the territory.
1. Project overview
- What it does: An interactive terminal UI (
main.ts) that lets a user create, delete, edit, list, copy, and open scripts, and optionally add them to$PATHso they can be run from any directory. - Stack: Deno 2 + TypeScript. Node built-ins via
node:imports. UI deps pulled from npm (ora,inquirer,@inquirer/prompts,gradient-string,figlet). Colors viajsr:@std/internal/styles. CI pins Denov2.9.3for Windows ARM64 compilation. - State/persistence: No database. Script metadata is stored as JSON at
~/.global_scripts_manager/scripts.json(or$ROOT_FOLDER/scripts.json); script bodies live in~/.global_scripts_manager/scripts/<name>-<id>/<name>. - Target platforms: macOS, Linux, Windows (shell-profile-based PATH editing, per-OS clipboard/terminal handling).
- Deployment: GitHub Actions tests and cross-compiles Linux x86_64/ARM64, Windows x86_64/ARM64, and macOS x86_64/ARM64 binaries. It uploads immutable workflow artifacts on
mainpushes and PRs, publishes binaries plus checksums as GitHub Release assets onv*tag pushes, and maintains a generated Homebrew formula.install.shprovides the macOS/Linux curl installer. Docker image also provided.
2. Quick commands
Run everything from the repo root. Deno 2 is required (deno --version).
| Command | Purpose | Status |
|---|---|---|
deno task start |
Run the interactive CLI (deno run -A main.ts) |
✅ works |
deno test -A main_test.ts |
Run tests | ✅ works — -A is required (npm deps read env vars; without it you get NotCapable: Requires env access) |
deno check main.ts main_test.ts |
Type-check | ✅ works |
deno compile -A -o gsm main.ts |
Build standalone binary | ✅ works |
deno fmt --check / deno lint |
Format / lint (no custom config; Deno defaults) | ❌ currently fail (17 files unformatted; 34 lint problems — see Known issues) |
⚠️ Validation gotcha:
deno runexecutes without strict type-checking in Deno 2, so a change can run even when it wouldn't passdeno check. Rundeno checkto be sure.
3. Project structure
main.ts Entry point + ALL action handlers (create/delete/edit/list/copy/open/quit flows)
main_test.ts Tests (currently one test: getTextChunks)
globals.ts Path config: root folder, scripts dir, JSON dir (env-overridable)
deno.json Task definitions + import map
api/
CLI.ts Platform detection, shell detection, clipboard, subprocess helpers, open-in-terminal
FileManager.ts Filesystem helpers + PATH profile editing (addToPath/removeFromPath) + script scaffolding
ScriptModel.ts Script type, ScriptsModel (reactive), PathsHandler, ScriptsJSONHandler (persistence)
patterns.ts Reactive primitives: createSignal/createEffect/createMemo, StateManager, proxy helper
tags.ts Tag parsing/collection helpers
cliUtilities.ts Prompt/spinner/streaming UI helpers (inquirer, ora, figlet)
.github/workflows/
create_executable.yaml CI: test, cross-compile, upload artifacts, publish releases, update formula
scripts/
generate_homebrew_formula.sh Release checksum + Homebrew formula generator
install.sh macOS/Linux curl installer
Dockerfile / docker-compose.yml Containerized runs (IN_DOCKER=true)
4. How the app works (data flow)
main.tsbootstraps:PathsHandler→ScriptsJSONHandler→loadScriptsFromFile()→ScriptsModel.chooseAction()shows the main menu (showQuickPickvia inquirer) and dispatches to a handler.- Every mutation goes through
ScriptsModel(in-memory, signal-backed) and is persisted byjsonHandler.writeScriptsToFile(scriptModel.scripts). - "Global" scripts get their containing folder appended to the user's shell profile (
~/.zshrc,~/.bashrc,~/.profile, or PowerShell$PROFILE) — the user mustsourceor restart their shell. IN_DOCKER=truebypasses the character-by-character text streaming (uses plainconsole.log).
File identity rule (important): a script's filepath is derived from its name + id:
<root>/scripts/<name>-<id>/<name>. The id (crypto.randomUUID()) makes names unique on disk even if a user reuses a display name. The folder is what gets added to $PATH.
5. Conventions & patterns to follow
- Reactive state:
ScriptsModelusescreateSignal/createEffectfromapi/patterns.ts(SolidJS-style). The#scriptsprivate field is mirrored into a signal; every setter notifies effects. UsescriptModel.editScript(...)rather than mutating arrays directly. - Persistence discipline: any change to scripts/tags must end in
jsonHandler.writeScriptsToFile(scriptModel.scripts)(andjsonHandler.addTags(...)for new tags). The globaltagsset inScriptsJSONHandleris the source of truth for the tag registry inscripts.jsonmetadata. - UI helpers: prefer
api/cliUtilities.ts(showQuickPick,getConfirm,getInput,showLoader). Note thatmain.tsinconsistently uses Deno's rawprompt()for the script-name and tag prompts — prefer the helpers for new code. - FS access: use
FileManagerstatic methods (they throw descriptive errors and guard existence). Don't callnode:fsdirectly in new code without reason. - Platform branching: use
CLI.isLinux()/isWindows()/isMacOS()andCLI.getShell(); never hardcode a shell or clipboard command. - Colors: import from
jsr:@std/internal@^1.0.5/styles(e.g.green,red,bgGreen) as done inmain.ts. - New top-level action? Add the option string to the
actionsarray inchooseAction(), write a handler, and add acasein the switch. Keep handlers inmain.ts(current convention). - Tests: keep using
@std/assert(deno.test blocks). Remember tests must be run with-A.
6. Environment variables
| Var | Effect |
|---|---|
ROOT_FOLDER |
Overrides the data root (default ~/.global_scripts_manager). scripts subdir and scripts.json live under it. |
IN_DOCKER |
Any truthy value disables the streaming text effect (plain console.log). |
SHELL / PSModulePath |
Used by CLI.getShell() to detect zsh/bash/powershell/cmd for PATH edits & script templates. |
TF_BUILD, AGENT_NAME (CI) |
Read by chalk (npm dep) — the reason tests need --allow-env. |
GSM_REPOSITORY |
Overrides the GitHub owner/repository used by install.sh and formula generation. |
GSM_VERSION |
Pins install.sh to a release version instead of the latest release. |
INSTALL_DIR |
Overrides the default ~/.local/bin install location used by install.sh. |
7. Known issues & pitfalls (verified)
- Type errors in
copyToClipboard(fixed).api/CLI.tspreviously passedstdio: ["piped", "ignore", "pipe"]— the invalid IOType"piped"(should be"pipe") collapsedchildto typenever, causing 9 TS errors underdeno check/deno compileand blocking the CI compile workflow. Fixed:"piped"→"pipe". - Duplicate-name check (fixed).
createScript()previously calledisScriptNameUnique(finalScriptName)— the previous (initially empty) name — so duplicate display names were allowed. Fixed: it now checksisScriptNameUnique(scriptName), the name the user just entered. - Tests must run with
-A.deno test main_test.tsfails withNotCapable: Requires env access to "TF_BUILD"(chalk does env probing). - First-run ordering dependency.
ScriptsJSONHandler.upsertFolder()callsfs.statwith no try/catch, so a write fails if the root folder is missing. In practicemain.tscreates the script folder first (which recursively creates the root), so first-run works — keep that ordering if you refactor. - Renaming only renames the file.
editScript → edit namerenames<folder>/<name>to<folder>/<newName>but leaves the folder named<oldName>-<id>. For global scripts the folder is what's on$PATH, so the new name may not be invocable until the folder is renamed too. - Release architecture scope. The workflow publishes Linux x86_64/ARM64, Windows x86_64/ARM64, and macOS x86_64/ARM64.
deno fmt --checkanddeno lintfail. 17 of 22 files are notdeno fmt-formatted, anddeno lintreports 34 problems (18no-unused-vars, 6no-import-prefix, 5no-unversioned-import, 3no-explicit-any, 2require-await). Don't reformat the whole repo without asking — but do keep new code formatted and lint-clean.- Minor: unused imports (
bgRed,yellow) inmain.ts;getAddRunScriptInfo(),onScriptsChange(),StateManager/createMemo/withImmutableare currently unused by the main flow;isPinned/schedulefields exist in the model but are not surfaced in the UI.
See DOCS/development.md for the full list.
8. Do / Don't
- Do keep the reactive model + explicit
writeScriptsToFilepattern. - Do preserve cross-platform behavior (PATH profiles, clipboard, terminal opening).
- Do run
deno test -A main_test.tsanddeno check ...before finishing — both should pass cleanly now. - Don't change storage layout (
scripts/<name>-<id>/<name>,scripts.jsonschema) without updatingScriptModel.ts,globals.ts, and.gitignore. - Don't add new npm/jsr deps casually — verify with
deno add/lockfile updates and keep the dependency set small.