token-never-sleeps
v1.0.3
Published
Pure TypeScript distribution of the TNS external orchestration loop
Maintainers
Readme
Token Never Sleeps
token-never-sleeps is a clean TypeScript distribution of the TNS agent external orchestration loop.
It keeps the core local runner behavior:
tns initcreates a runnable workspacetns runexecutes one direct local looptns startchooses tmux or direct modetns statusshows tracked section statetns planconverts natural language or a rough draft into a runnabletask.mdtns plan-importconverts a markdown plan into tracked sectionstns compileemits a deterministic orchestration program, including bounded parallel planstns skillmanages configured skill sources and installed skill bindingstns skillsinspects local skillbases and resolves stage-local skill imports without modifying configtns gatewayruns a local protocol bus for multi-terminal coordinationtns gateway webserves a local dashboard backed by real workspace state
What this package intentionally leaves out:
- Python entrypoints
- bundled website content
- heavyweight generated outputs and media artifacts
- remote notification glue that is not part of the local runner core
Install
npm install -g token-never-sleepsRequirements
- Node.js 22+
claudeCLI available inPATH- optional:
tmuxif you want managed runner mode
Config resolution
Most commands do not need --config after you enter a workspace. TNS resolves
configuration in this order:
- an explicit
--config /path/to/tns_config.json - the nearest
tns_config.jsonfound from the current directory upward - the global config at
~/.tns/config.json
Global config is for shared system defaults such as permissions, monitor
settings, execution classes, command bridges, validators, skill sources, and
injection profiles. After tns init --workspace ..., the workspace-local
tns_config.json takes priority for runtime identity and task-local settings
such as workspace, product_doc, thread / threads, program, tmux, and
task-specific overrides. Set TNS_GLOBAL_CONFIG=/path/to/config.json to test or
use a different global config file.
Quickstart
Create a blank workspace:
tns init --workspace ./my-taskCreate a workspace and bind it to the split dashboard:
tns init --workspace ./my-task --dashboardCreate a template workspace:
tns init --workspace ./novel-project --template novel-writingThen run the real orchestration loop:
cd ./my-task
tns plan --text "create a short project checklist in checklist.md and verify the file exists" --apply --compile
tns status
tns doctor
tns run --onceFor long-running work, use the managed runner and inspect it without mutating state:
tns start
tns btw
tns traceGateway Coordination (beta)
Gateway is a long-running local protocol bus for multiple TNS terminals inside one workspace. It prepares the collaboration layer for resource waits, task dispatch, and protocol messages between clients.
Start the gateway in one terminal:
tns gateway serveUse other terminals as clients:
tns gateway register --client client-a
tns gateway register --client client-b
tns gateway heartbeat --client client-a
tns gateway send --from client-a --to client-b --type ping --payload '{"hello":true}'
tns gateway recv --client client-bDispatch and complete work:
tns gateway dispatch --from client-a --to client-b --task "Review sec-003" --task-type review --payload '{"section":"sec-003"}'
tns gateway claim --client client-b --task-type review
tns gateway complete --client client-b --task-id TASK_ID --payload '{"status":"done"}'Wait for a named TNS resource lock to become free:
tns gateway wait-resource --client client-b --resource state --timeout-ms 30000Gateway writes protocol state under .tns/gateway/:
inbox.jsonl: client requestsevents.jsonl: protocol event streamresponses/: per-request responsesclients.json: registered clients and heartbeatstasks.json: dispatch/claim/complete queue
Every gateway event is also mirrored to .tns/hook-events.jsonl as a
gateway_hook record. Hook payloads include protocol version, workspace,
gateway pid, request id, client ids, task id, resource, event name, timestamp,
and payload/result data when present. Use tns gateway events, tns gateway
status, tns status, tns btw, or tns trace to inspect the bus.
Start a read-only dashboard for the same workspace:
tns gateway web --port 48731Open the printed URL in a browser. The web frontend is separated from the
backend state API: it renders only data from /api/snapshot, /api/events, and
the /api/stream SSE feed. The backend reads the real .tns state files and
gateway files, including task details, sections, runtime agents, bounded
parallel thread lanes, skill injection events, configured skill sources,
gateway clients/tasks, resource locks and waiters, hooks, diagnostics,
approvals, artifacts, reviews, exploration state, and compiled program data. It
does not emit mock agent or test data.
Dashboard API access is project-key authenticated. Run tns init --dashboard
to create .tns/dashboard.json; it contains a generated xxxx-xxxx key and
prebuilt frontend, snapshot, and stream URLs. The key can be supplied as
?key=xxxx-xxxx, Authorization: Bearer xxxx-xxxx, or
x-tns-dashboard-key.
The backend defaults to the workspace from its config. Authenticated dashboard
workspace switching and POST /api/workspaces/init are limited to that
workspace's parent directory. A browser-created workspace runs the same init
path as tns init; it creates a real task.md, tns_config.json, .tns/
state directory, and a fresh dashboard key for that new project.
CLI-created workspaces can opt into the same dashboard binding:
tns init --workspace /tmp/my-task --dashboard --dashboard-url http://127.0.0.1:48731/This writes .tns/dashboard.json with the frontend URL, snapshot API URL, SSE
stream URL, and init API URL. The dashboard remains frontend/backend separated:
the web window consumes the local tns gateway web API/SSE backend, while that
backend reads real .tns state for task, section, agent/thread, skill, gateway,
lock, hook, artifact, and diagnostic monitoring. If users want a separate
static/proxy setup, they should configure that locally around the same HTTP API;
TNS does not require a specific reverse proxy.
Local dashboard flow:
tns init --workspace /tmp/my-task --runner direct --dashboard
cd /tmp/my-task
tns gateway web --port 48731In another terminal, read the generated URL:
node -e 'const fs=require("fs"); const d=JSON.parse(fs.readFileSync(".tns/dashboard.json","utf8")); console.log(d.frontend_url)'Open that frontend_url. It contains the workspace path and project key. API
calls without the matching key return 401, and a key from one dashboard-bound
workspace does not authorize another workspace. The Workspaces tab can create a
sibling test workspace; that request is authenticated with the current
workspace key and the created workspace receives its own fresh key.
Task Planning
When task.md is still a natural-language request or a rough draft, run a
planner pass before compiling:
tns plan --check
tns plan --polish --apply --compile
tns plan --text "add a CLI command, update docs, and verify with tests" --apply
tns plan --input rough-task.md --output planned-task.md
tns plan --apply --compiletns plan --check scores task quality locally without calling Claude.
tns plan --polish uses that score as a gate and only calls the dedicated task
planner agent when the task is below --min-score (default 75). Planner
results are recorded in .tns/compiled/task-plan-review.json. With --apply,
TNS backs up the current task document under .tns/compiled/, replaces
task.md, and syncs .tns/sections.json. The generated task document uses
concrete ## sections with objectives, inputs, deliverables, acceptance
criteria, and verification steps. It also preserves explicit dependency lines
so the compiler can build a stronger program contract. If the planner suggests
skills, it writes non-binding Recommended skills: notes; it does not emit
executable import <skill> lines.
Deterministic compilation
TNS can compile task.md and tns_config.json into a deterministic orchestration program:
tns compile
tns compile --synthesize
tns compile --synthesize --applyThis writes:
.tns/compiled/program.json.tns/compiled/compiler-review.jsonwhen synthesis is enabled
The compiled program makes these contracts explicit:
- task sections and required inputs
- bridge files and state files
- lifecycle and watchdog settings
- thread count and bounded parallel planning hints
- permission profiles and approval tags
- validators and runner-side command hooks
- declared external tools, skills, and MCP requirements
- state-level skill imports such as
import pdf
For task documents without an explicit config.program, compile also derives a
conservative section dependency graph. Use Depends on: <section title or id>
inside a section, or reference a backticked artifact path created by an upstream
section, to materialize parallel.depends_on in the generated program and
bounded parallel_plan.
At run time, TNS also checks whether task.md and .tns/sections.json have
drifted from the compiled contract. It can refresh derived compiled programs and
section state automatically, and records the latest retry/recompile/block
decision in .tns/diagnostics.json.
The runner reads the compiled program when it exists, so orchestration details stop living only in prompt inference. For example, a section body containing:
import pdfis compiled into the matching state as:
{
"parallel": {
"skills": ["pdf"]
}
}At runtime, the executor resolves and injects that skill for the section. Synthesis mode runs the dedicated compiler agent and produces a structured patch for:
- preflight
- permissions
- validators
- runner-side command hooks
- policy
- external tools / skills / MCP declarations
--apply merges that patch back into tns_config.json and recompiles.
Orchestration Programs
TNS can carry an explicit orchestration program in config.program.
It supports:
task,decision,loop,terminalstates- deterministic transitions with conditions
- instruction ops:
set,inc,dec,append,emit,if,while - thread-control ops:
thread_suspend,thread_resume,thread_interrupt,thread_wait - state-level parallel hints under
parallel
Compile materializes that program and writes the runner-visible contract to
.tns/compiled/program.json:
tns compile
tns status
tns run --onceBounded Parallel Planning
Set thread or threads to request bounded parallel orchestration:
{
"thread": 2,
"program": {
"threads": 2,
"parallel": {
"mode": "auto",
"max_threads": 2
}
}
}The planning layer follows the configured thread budget instead of applying a fixed local machine cap. The compiler emits a parallel_plan with batches, resources, dependencies, and thread controls. The standard runner executes the next ready parallel batch with Promise.allSettled; singleton batches continue through the conservative one-section path. State-level hints include:
parallel.threadparallel.resourceparallel.depends_onparallel.exclusiveparallel.starts_suspendedparallel.executor_classparallel.skillsparallel.verifier_skillsparallel.workspaceparallel.merge_policy
Use tns trace to inspect parallel_batch_start,
agent_start, agent_end, and parallel_batch_end events from the real run.
Skillbases
TNS supports user-provided skill libraries for executor and verifier injection. A source can be an extracted skillbase, a plugin library, or a direct directory of skill folders:
{
"skillbases": {
"use_default_sources": false,
"sources": [
{
"id": "local-skillbase",
"path": "/abs/path/to/skillbase",
"kind": "skillbase",
"priority": 0
}
]
}
}Inspect and resolve skills:
tns skills --action doctor --source /abs/path/to/skillbase
tns skills --action list --source /abs/path/to/skillbase
tns skills --action resolve --name pdf --source /abs/path/to/skillbase
tns skills --action match --text "extract tables from a PDF report" --source /abs/path/to/skillbasePersist a skill source and install a skill into the runner config:
tns skill source-add --source /abs/path/to/skillbase
tns skill source-list
tns skill install pdf
tns skill sync-checkInstall can bind a source and skill in one command:
tns skill install pdf --source /abs/path/to/skillbaseBy default, install writes the resolved skill into
injections.profiles.executor_task.skills and records source path plus
content_hash in externals.skills. tns skill sync-check compares that
recorded hash with the currently resolved local skill so existing installs can
be checked for drift.
Use --mode verifier to install into verifier_audit, or --profile NAME to
target a custom injection profile.
Install from skills.sh and bind the installed skill into the current TNS profile:
tns skill registry-install vercel-labs/agent-skills --skill web-design-guidelines --agent claude-code
tns skill registry-update --project
tns skill registry-sync --agent claude-codeDefault local sources include workspace .claude/skills, ~/.agents/skills,
~/.claude/skills, and ~/.claude/plugins. You can add multiple explicit local
sources for different agent ecosystems, for example:
tns skills --action doctor --source ~/.claude/plugins --source /abs/path/to/skillbaseSkill injection is stage-local:
- compiler-stage TNS internal skills are resolved from the package-local
skills/directory - executor and verifier imports resolve from configured user skillbases or explicit external skill paths
- verifier skills do not inherit executor skills unless you explicitly configure them
This keeps TNS internal skills separate from the user’s external skillbase while still allowing natural-language imports such as import pdf.
Skill selection is controlled by the skill management layer, not by the compiler agent:
skillbases.selection.mode: "explicit"uses only manually planned skills from config, program state, ortask.mdimport linesskillbases.selection.mode: "auto"matches each section against the configured skillbase- a section can override the config with
skills: auto,skills: explicit, orskills: off
Example automatic matching:
{
"skillbases": {
"use_default_sources": false,
"sources": [
{ "id": "local-skillbase", "path": "/abs/path/to/skillbase", "kind": "skillbase", "priority": 0 }
],
"selection": {
"mode": "auto",
"max_matches_per_section": 2,
"min_score": 0.22,
"verifier_mode": "none"
}
}
}With mode: "explicit", SkillsBench-style preset skills map naturally to explicit import pdf lines or program.states[].parallel.skills. With mode: "auto", TNS searches the configured base and records auto_skills plus match scores in .tns/injection-events.jsonl.
Stage permissions
token-never-sleeps can auto-approve safe command families for Claude inside a workspace,
and stop for explicit user approval when a section needs stronger permissions.
Typical flow:
tns run --once
tns btw
tns approve --tag restricted-step --note "approved by operator"
tns run --onceKey points:
- each section step resolves a permission profile
- safe command families are passed through Claude
--allowedTools - escalated profiles can require an approval tag such as
restricted-step - missing approvals freeze the workspace and create
.tns/approvals.json - path scope can be
workspace,workspace_whitelist, orglobal workspace_whitelistcombines the workspace with explicitallowed_paths- executor
files_touchedare audited against the resolved path scope; protected.tnsrunner state remains blocked
See also:
tns help permissionsExploration xmode (beta)
token-never-sleeps can optionally run bounded post-completion exploration passes after
all currently tracked sections are done.
Use it when you want:
- one extra detail and robustness review
- small concrete refinements after the main task is already complete
- optional follow-up work captured into a taskx exploration branch
- planner-assisted polishing of low-quality or rough taskx drafts before import
- continuous xmode cycles such as "find earliest missing branch cycle, import it, execute it, then review again"
Example config:
{
"exploration": {
"enabled": true,
"allow_taskx": true,
"taskx_filename": "taskx.md",
"max_rounds_per_window": 3,
"agent": "tns-executor",
"plan_taskx": true,
"taskx_min_score": 75,
"taskx_branch_dir": ".tns/taskx",
"require_taskx_deliverables": true
}
}If the exploration pass finds explicit new requirements, it can create taskx.md,
TNS archives the source and planned branch under .tns/taskx/round-XXX/, imports
the planned sections, removes the transient root taskx.md, and continues running.
Imported taskx sections are preserved in .tns/sections.json without rewriting the
primary task.md.
When a workspace uses task-delivery/xmode-control.json with ordered cycles and
deliverable paths, xmode treats it as a branch backlog. Each round targets the
earliest cycle with missing deliverables only, so a multi-thread runner can execute the
cycle's independent taskx sections before the next exploration round imports more.
Long-running Claude sessions
token-never-sleeps keeps a runtime heartbeat while one Claude agent call is still in flight. If a single Claude follow session runs too long, the watchdog terminates that one call, marks the section as retryable, and lets the next loop continue from saved state instead of blocking the whole runner indefinitely.
Default monitor settings:
{
"monitor": {
"heartbeat_seconds": 30,
"max_agent_runtime_seconds": 1800,
"kill_grace_seconds": 15
}
}Use tns btw to inspect:
- current runner heartbeat
- active section and step
- active Claude agent and child pid
- current agent deadline
- next wake time after freezes or sleeps
status and btw also expose named resource locks under .tns/locks/, so compile/run/control flows can coordinate without corrupting shared state.
Templates
Available templates:
blanknovel-writing
Templates live under templates/ inside the package and are copied into the target workspace by tns init.
Commands
tns help
tns help init
tns help compile
tns help skills
tns help run
tns help config
tns help permissions
tns help exploration
tns help tmuxPackage Layout
agents/ Claude agent definitions used by the runner
.claude-plugin/ minimal plugin metadata so claude can load the local agents
dist/ compiled CLI output
skills/ reusable skills, including task-to-program compilation guidance
templates/ workspace templates copied by tns initScope
This distribution is meant to be a clean npm runner. It does not try to preserve every historical feature from the mixed plugin repository. If a feature is not implemented in TypeScript here, it is intentionally out of scope for this package.
