trantor 0.18.28 → 0.18.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/bin/cli.mjs +2 -0
- package/bin/crew.sh +6 -3
- package/bin/new.mjs +142 -0
- package/package.json +2 -2
- package/skills/crew/SKILL.md +3 -0
package/README.md
CHANGED
|
@@ -393,10 +393,16 @@ rate, not work rate.
|
|
|
393
393
|
|
|
394
394
|
```
|
|
395
395
|
trantor setup | doctor | connect | profile | provider | models
|
|
396
|
-
| up <agents…> | swap <old> <new> | down | seat-why <agent> | ui | advise | hub | watch
|
|
396
|
+
| new <name> | up <agents…> | swap <old> <new> | down | seat-why <agent> | ui | advise | hub | watch
|
|
397
397
|
| adopt <project> | reconcile | duty | orchestrate | patrol | app | backfill | init-hooks
|
|
398
398
|
```
|
|
399
399
|
|
|
400
|
+
- **`trantor new <name>`** — project genesis in one command: makes the dir under your dev root
|
|
401
|
+
(`TRANTOR_DEV_ROOT` or `~/development`), git on main (or `--from <git-url>`, or `--adopt` an
|
|
402
|
+
existing folder), seeds CLAUDE.md from `--brief <file>`, installs the auto-card hook, posts
|
|
403
|
+
the brief to the hub, and opens the first card "genesis: <name>". `--json` for machines.
|
|
404
|
+
It never spawns a session — firing the crew stays your call.
|
|
405
|
+
|
|
400
406
|
- **`trantor provider`** — `list` every crew seat (built-in + brought) with availability + tier ·
|
|
401
407
|
`add <name> --key … [--plan api] [--base-url <url> --models a,b]` to bring any provider (custom
|
|
402
408
|
endpoints are wired into OpenCode for you) · `remove <name>`.
|
package/bin/cli.mjs
CHANGED
|
@@ -69,6 +69,7 @@ switch (cmd) {
|
|
|
69
69
|
case "sweep": run("bin/sweep.mjs"); break;
|
|
70
70
|
case "reconcile": run("bin/reconcile.mjs"); break;
|
|
71
71
|
case "init-hooks": run("bin/init-hooks.mjs"); break;
|
|
72
|
+
case "new": run("bin/new.mjs"); break;
|
|
72
73
|
case "balances": case "balance": case "credits": run("bin/balances.mjs"); break;
|
|
73
74
|
case "recost": run("bin/recost.mjs"); break;
|
|
74
75
|
case "handoff": run("bin/baton.mjs"); break;
|
|
@@ -208,6 +209,7 @@ switch (cmd) {
|
|
|
208
209
|
trantor gates verification gates: "must verify before shipping" claims that survive handoffs — [--all] [--json]
|
|
209
210
|
trantor backfill card past GIT work onto the board (solo commits that were never carded) — [--since "14 days ago"] [--dry-run]
|
|
210
211
|
trantor init-hooks install a git post-commit hook so EVERY commit auto-cards on the board (reliable solo-work backstop) — [--uninstall]
|
|
212
|
+
trantor new project genesis: new <name> [--from <git-url>] [--brief <file>] [--dir <path>] [--adopt] [--json] — makes the dir, git main, CLAUDE.md from the brief, hooks, hub brief + first card (never spawns a session)
|
|
211
213
|
trantor balances how much credit is left on each CONFIGURED provider (from your profile) — refill before you stall — [--json]
|
|
212
214
|
trantor recost recompute sub-agent notional cost from on-disk transcripts + reseed the board (repair after upgrade) — [--dry-run]
|
|
213
215
|
trantor handoff finish this session NOW: write a handoff, open a fresh session that takes over, and close this one (manual baton)
|
package/bin/crew.sh
CHANGED
|
@@ -613,13 +613,16 @@ usage: trantor open [<project>]
|
|
|
613
613
|
EOF
|
|
614
614
|
}
|
|
615
615
|
open_orchestrator() {
|
|
616
|
-
local a
|
|
616
|
+
local a PROJ_ARG=""
|
|
617
617
|
for a in "$@"; do case "$a" in
|
|
618
618
|
--help|-h) usage_open; return 0 ;;
|
|
619
619
|
--*) echo "trantor open: unknown flag '$a'"; usage_open; return 1 ;;
|
|
620
|
-
*) PROJ="$a" ;;
|
|
620
|
+
*) PROJ="$a"; PROJ_ARG="$a" ;;
|
|
621
621
|
esac; done
|
|
622
|
-
|
|
622
|
+
# only an EXPLICIT name is resolved to its checkout; a project declared by RELAY_PROJECT from
|
|
623
|
+
# inside a differently named dir (the test harness, RELAY_PROJECT=<name> sessions) means "this
|
|
624
|
+
# cwd IS the project" — refusing it made `trantor open` impossible for exactly those sessions
|
|
625
|
+
DIR="$(_orch_resolve_dir "$DIR" "${PROJ_ARG:-}")" || exit 1
|
|
623
626
|
command -v herdr >/dev/null 2>&1 || { echo "trantor open needs herdr (the pane host) — install: curl -fsSL https://herdr.dev/install.sh | sh"; exit 1; }
|
|
624
627
|
local wsid="" orch="" live_ids="" live_names="" pair="" line fresh=0
|
|
625
628
|
local sid; sid="$(_orch_sid "$PROJ")" || { echo "trantor open: could not mint a session id (uuidgen missing?)" >&2; exit 1; }
|
package/bin/new.mjs
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// trantor new — project genesis, the CLI half (#5862). One command stands a project up:
|
|
3
|
+
//
|
|
4
|
+
// trantor new <name> [--from <git-url>] [--brief <file>] [--dir <path>] [--adopt] [--json]
|
|
5
|
+
//
|
|
6
|
+
// It makes the directory under the dev root (TRANTOR_DEV_ROOT or ~/development), starts git on
|
|
7
|
+
// main (or clones --from, or adopts an existing folder with --adopt), seeds CLAUDE.md from the
|
|
8
|
+
// brief (verbatim brief + the trantor conventions block), installs the same auto-card hook as
|
|
9
|
+
// `trantor init-hooks`, posts the brief as the hub project brief (POST /project — the same call
|
|
10
|
+
// relay_project_brief makes), and opens the first card "genesis: <name>" on the new board.
|
|
11
|
+
//
|
|
12
|
+
// It NEVER spawns a session: the wake is genesis-2, the app half. A hub that is down downgrades
|
|
13
|
+
// the genesis to a warning (card: null) — the directory is real either way, and a dead hub must
|
|
14
|
+
// not make a made project look unmade.
|
|
15
|
+
import { spawnSync } from "node:child_process";
|
|
16
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, appendFileSync } from "node:fs";
|
|
17
|
+
import { homedir } from "node:os";
|
|
18
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
import { ensureEnrolled, loadIdentity, signedPost } from "../hooks/lib/api.mjs";
|
|
21
|
+
import { resolveHub } from "../lib/project.mjs";
|
|
22
|
+
|
|
23
|
+
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
24
|
+
|
|
25
|
+
// The trantor conventions block — what every trantor-wired project's CLAUDE.md carries so the
|
|
26
|
+
// first session knows the board, the crew, and the gates exist. Kept SHORT and factual; the
|
|
27
|
+
// brief above it is the project's own voice.
|
|
28
|
+
const CONVENTIONS = [
|
|
29
|
+
"",
|
|
30
|
+
"## Trantor conventions",
|
|
31
|
+
"",
|
|
32
|
+
"This project is wired into Trantor (the board, the bus, the crew):",
|
|
33
|
+
"",
|
|
34
|
+
"- **Board** — work lives on the board, not in anyone's head. `trantor catchup` answers",
|
|
35
|
+
" \"where are we?\"; the desktop app (`trantor app install`) shows the live board.",
|
|
36
|
+
"- **Crew** — `trantor up codex kimi glm` fires seats into their own worktrees; contracts go",
|
|
37
|
+
" over the bus (`relay_send`), and every seat reports what became of its card. See the",
|
|
38
|
+
" `/trantor:crew` skill for the full doctrine before firing anything.",
|
|
39
|
+
"- **Gates** — a card reaches done only with real evidence: the test command, the counts,",
|
|
40
|
+
" and the observed behavior. \"It should work\" is not a state a card can be in.",
|
|
41
|
+
"- **Commits card themselves** — the post-commit hook (trantor auto-card) backfills the board",
|
|
42
|
+
" from git; keep commit messages in the imperative and reference card ids when one exists.",
|
|
43
|
+
"",
|
|
44
|
+
].join("\n");
|
|
45
|
+
|
|
46
|
+
const die = (msg) => { console.error(`trantor new: ${msg}`); process.exit(1); };
|
|
47
|
+
|
|
48
|
+
// ── args ────────────────────────────────────────────────────────────────────────────────────────
|
|
49
|
+
const args = process.argv.slice(2);
|
|
50
|
+
const flag = (n) => { const i = args.indexOf("--" + n); return i >= 0 ? args[i + 1] : null; };
|
|
51
|
+
const has = (n) => args.includes("--" + n);
|
|
52
|
+
const json = has("json");
|
|
53
|
+
const name = args.find(a => !a.startsWith("--"));
|
|
54
|
+
if (!name) die("usage: trantor new <name> [--from <git-url>] [--brief <file>] [--dir <path>] [--adopt] [--json]");
|
|
55
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name)) die(`invalid project name "${name}" — letters, digits, dot, dash, underscore`);
|
|
56
|
+
const from = flag("from");
|
|
57
|
+
const briefFile = flag("brief");
|
|
58
|
+
const adopt = has("adopt");
|
|
59
|
+
// The brief is read BEFORE anything is created: a refused genesis must leave no directory behind.
|
|
60
|
+
let brief = "";
|
|
61
|
+
if (briefFile) {
|
|
62
|
+
if (!existsSync(briefFile)) die(`brief file not found: ${briefFile}`);
|
|
63
|
+
brief = readFileSync(briefFile, "utf8").trimEnd();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ── the directory ───────────────────────────────────────────────────────────────────────────────
|
|
67
|
+
const dirArg = flag("dir");
|
|
68
|
+
const devRoot = dirArg
|
|
69
|
+
? resolve(dirArg)
|
|
70
|
+
: (process.env.TRANTOR_DEV_ROOT ? resolve(process.env.TRANTOR_DEV_ROOT) : join(homedir(), "development"));
|
|
71
|
+
if (dirArg && !isAbsolute(dirArg)) { /* relative --dir is allowed; resolved above */ }
|
|
72
|
+
const dir = isAbsolute(name) ? name : join(devRoot, name);
|
|
73
|
+
|
|
74
|
+
const existed = existsSync(dir);
|
|
75
|
+
const occupied = existed && readdirSync(dir).length > 0;
|
|
76
|
+
if (occupied && !adopt) die(`"${dir}" already exists and is not empty — pass --adopt to adopt it`);
|
|
77
|
+
if (adopt && !existed) die(`--adopt: "${dir}" does not exist — nothing to adopt`);
|
|
78
|
+
if (!existed) mkdirSync(dir, { recursive: true });
|
|
79
|
+
|
|
80
|
+
// ── git ─────────────────────────────────────────────────────────────────────────────────────────
|
|
81
|
+
const git = (gitArgs) => spawnSync("git", gitArgs, { cwd: dir, encoding: "utf8" });
|
|
82
|
+
const gitOk = (r, what) => { if (r.status !== 0) die(`${what} failed: ${(r.stderr || r.stdout || "").trim()}`); };
|
|
83
|
+
|
|
84
|
+
let branch;
|
|
85
|
+
if (from) {
|
|
86
|
+
gitOk(git(["clone", from, "."]), "git clone");
|
|
87
|
+
branch = git(["branch", "--show-current"]).stdout.trim() || "main";
|
|
88
|
+
} else {
|
|
89
|
+
if (!existsSync(join(dir, ".git"))) gitOk(git(["init", "-b", "main"]), "git init");
|
|
90
|
+
branch = git(["branch", "--show-current"]).stdout.trim() || "main";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ── CLAUDE.md — verbatim brief + the conventions block ──────────────────────────────────────────
|
|
94
|
+
const claude = join(dir, "CLAUDE.md");
|
|
95
|
+
if (existsSync(claude)) {
|
|
96
|
+
const current = readFileSync(claude, "utf8");
|
|
97
|
+
if (!current.includes("## Trantor conventions")) appendFileSync(claude, `\n${CONVENTIONS}\n`);
|
|
98
|
+
} else {
|
|
99
|
+
const head = brief ? `${brief}\n` : `# ${name}\n\n(Genesis — no brief was given. Add this project's what/why/goal here.)\n`;
|
|
100
|
+
writeFileSync(claude, `${head}${CONVENTIONS}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ── hooks — the SAME install trantor init-hooks performs, in the new repo ───────────────────────
|
|
104
|
+
const hook = spawnSync(process.execPath, [join(ROOT, "bin", "init-hooks.mjs")], { cwd: dir, encoding: "utf8" });
|
|
105
|
+
if (hook.status !== 0) die(`hook install failed: ${(hook.stderr || "").trim()}`);
|
|
106
|
+
|
|
107
|
+
// ── the hub: brief + first card, signed like every other client ─────────────────────────────────
|
|
108
|
+
const hub = resolveHub(name);
|
|
109
|
+
const session = process.env.RELAY_SESSION || `genesis:${name}`;
|
|
110
|
+
const identity = loadIdentity(session);
|
|
111
|
+
let card = null;
|
|
112
|
+
let hubError = null;
|
|
113
|
+
try {
|
|
114
|
+
await ensureEnrolled(session, identity, name);
|
|
115
|
+
const briefForHub = (brief || `Genesis of ${name} — created by trantor new.`).slice(0, 600);
|
|
116
|
+
const r1 = await signedPost("/project", { project: name, brief: briefForHub, by: session }, { session, project: name, timeoutMs: 8000 });
|
|
117
|
+
if (!r1.ok) throw new Error(`hub ${r1.status} on /project`);
|
|
118
|
+
const r2 = await signedPost("/task", {
|
|
119
|
+
project: name,
|
|
120
|
+
title: `genesis: ${name}`,
|
|
121
|
+
status: "todo",
|
|
122
|
+
by: session,
|
|
123
|
+
note: "project genesis — created by trantor new",
|
|
124
|
+
}, { session, project: name, timeoutMs: 8000 });
|
|
125
|
+
if (!r2.ok) throw new Error(`hub ${r2.status} on /task`);
|
|
126
|
+
card = r2.json?.task?.id ?? null;
|
|
127
|
+
} catch (e) {
|
|
128
|
+
hubError = e instanceof Error ? e.message : String(e);
|
|
129
|
+
console.error(`trantor new: hub unreachable or refusing (${hubError}) — the project exists locally; the brief and first card were NOT posted.`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ── report ──────────────────────────────────────────────────────────────────────────────────────
|
|
133
|
+
if (json) {
|
|
134
|
+
console.log(JSON.stringify({ name, dir, branch, hub, card }));
|
|
135
|
+
} else {
|
|
136
|
+
console.log(`✓ ${dir} (${branch}${from ? ", cloned" : adopt ? ", adopted" : ""})`);
|
|
137
|
+
console.log(`✓ CLAUDE.md seeded${brief ? " from the brief" : " (no brief — add the project's what/why/goal)"}`);
|
|
138
|
+
console.log(`✓ auto-card hook installed (trantor init-hooks)`);
|
|
139
|
+
if (card !== null) console.log(`✓ hub ${hub}: brief posted, card #${card} ("genesis: ${name}")`);
|
|
140
|
+
else if (hubError) console.log(`! hub ${hub}: brief/card not posted (${hubError})`);
|
|
141
|
+
console.log(`\nNext: cd ${dir} && claude — or fire the crew with \`trantor up\`. No session was spawned.`);
|
|
142
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trantor",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.29",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"trantor": "bin/cli.mjs"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"zod": "^4.4.3"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"test": "node bin/slop-gate.mjs --surface desktop/src && node test.mjs && node test-scenarios.mjs && node test-failure.mjs && node test-redelivery.mjs && node test-crew-completion.mjs && node test-contracts.mjs && node test-autonomy.mjs && node test-integrate.mjs && node test-handoff.mjs && node test-handoff-summarizer.mjs && node test-baton-turn-boundary.mjs && node test-agents.mjs && node test-update.mjs && node test-handoff-guard.mjs && node test-baton-latest.mjs && node test-balances.mjs && node test-usage-live.mjs && node test-subagent-cost.mjs && node test-inbox.mjs && node test-inflight.mjs && node test-notify.mjs && node test-focus.mjs && node test-cardlog.mjs && node test-checklist.mjs && node test-relay-note.mjs && node test-reaper.mjs && node test-events.mjs && node test-proposals.mjs && node test-scrub.mjs && node test-store-delta.mjs && node test-message-re.mjs && node test-claims.mjs && node test-adopt.mjs && node test-summarize.mjs && node test-identity-core.mjs && node test-identity.mjs && node test-identity-instances.mjs && node test-duty.mjs && node test-duty-dark.mjs && node test-duty-seat.mjs && node test-discovery.mjs && node test-doctor.mjs && node test-crew-env.mjs && node test-desktop-transport.mjs && node test-crew-worktree.mjs && bash test-crew-herdr.sh && npm --prefix desktop run test --silent && node test-splitbrain.mjs && node test-overseer.mjs && node test-overseer-lib.mjs && node test-overseer-warn.mjs && node test-provider-keys.mjs && node test-inbox-delivery.mjs && node test-identity-drift.mjs && node test-inbox-staleness.mjs && node test-seats.mjs && node test-seat-identity.mjs && node test-hub-routing.mjs && node test-hook-routing.mjs && node test-relay-wait.mjs && node test-dsh-seat.mjs && node test-kimi-bridge.mjs && node test-kimi-events.mjs && python3 engine/test-routing.py && node test-reconcile.mjs && node test-resources.mjs && node test-patrol.mjs && node test-bridge.mjs && bash test-crew.sh"
|
|
14
|
+
"test": "node bin/slop-gate.mjs --surface desktop/src && node test.mjs && node test-scenarios.mjs && node test-failure.mjs && node test-redelivery.mjs && node test-crew-completion.mjs && node test-contracts.mjs && node test-autonomy.mjs && node test-integrate.mjs && node test-handoff.mjs && node test-handoff-summarizer.mjs && node test-baton-turn-boundary.mjs && node test-agents.mjs && node test-update.mjs && node test-handoff-guard.mjs && node test-baton-latest.mjs && node test-balances.mjs && node test-usage-live.mjs && node test-subagent-cost.mjs && node test-inbox.mjs && node test-inflight.mjs && node test-notify.mjs && node test-focus.mjs && node test-cardlog.mjs && node test-checklist.mjs && node test-relay-note.mjs && node test-reaper.mjs && node test-events.mjs && node test-proposals.mjs && node test-scrub.mjs && node test-store-delta.mjs && node test-message-re.mjs && node test-claims.mjs && node test-adopt.mjs && node test-summarize.mjs && node test-identity-core.mjs && node test-identity.mjs && node test-identity-instances.mjs && node test-duty.mjs && node test-duty-dark.mjs && node test-duty-seat.mjs && node test-discovery.mjs && node test-doctor.mjs && node test-crew-env.mjs && node test-desktop-transport.mjs && node test-crew-worktree.mjs && bash test-crew-herdr.sh && npm --prefix desktop run test --silent && node test-splitbrain.mjs && node test-overseer.mjs && node test-overseer-lib.mjs && node test-overseer-warn.mjs && node test-provider-keys.mjs && node test-inbox-delivery.mjs && node test-identity-drift.mjs && node test-inbox-staleness.mjs && node test-seats.mjs && node test-seat-identity.mjs && node test-hub-routing.mjs && node test-hook-routing.mjs && node test-relay-wait.mjs && node test-dsh-seat.mjs && node test-kimi-bridge.mjs && node test-kimi-events.mjs && python3 engine/test-routing.py && node test-new.mjs && node test-reconcile.mjs && node test-resources.mjs && node test-patrol.mjs && node test-bridge.mjs && bash test-crew.sh"
|
|
15
15
|
},
|
|
16
16
|
"description": "The hub-world for AI agent crews — orchestrate Claude Code, Codex, GLM, Kimi, DeepSeek & any OpenRouter model as live crews with a plan-aware Advisor, a Kanban/flow command center, a testing gate, and an economics brain (Scrooge).",
|
|
17
17
|
"files": [
|
package/skills/crew/SKILL.md
CHANGED
|
@@ -32,6 +32,9 @@ PRD.md + TDD.md. The TDD MUST define one file-set per agent (no merge conflicts)
|
|
|
32
32
|
explicit EVENT/INTERFACE CONTRACT — cross-agent bugs come from contract drift.
|
|
33
33
|
|
|
34
34
|
## Phase 1 — board setup
|
|
35
|
+
0. No project yet? `trantor new <name> --brief <file>` stands one up (dir, git main, CLAUDE.md
|
|
36
|
+
from the brief, hooks, hub brief + first card) — it never spawns a session; firing the crew
|
|
37
|
+
is this phase's job.
|
|
35
38
|
1. `relay_project_brief("<what + why + goal>")`
|
|
36
39
|
2. One card per package: `relay_task_add(title, assignee, difficulty, model)` — set `model`
|
|
37
40
|
to the advisor-routed model (or the CLI's default name); difficulty + model show as badges
|