@ashulab/agent-forge 0.2.0 → 0.4.0
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 +29 -11
- package/dist/launcher.cjs +142 -52
- package/package.json +1 -1
- package/schema/agents.schema.json +1 -5
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ On each run the launcher:
|
|
|
9
9
|
- detects installed provider CLIs on your PATH
|
|
10
10
|
- asks which provider to use (or takes it from a flag)
|
|
11
11
|
- starts the provider with the GitHub token and a per-agent git identity injected into the environment
|
|
12
|
-
-
|
|
12
|
+
- injects the agent's `systemPrompt` per process (`claude --append-system-prompt`, `codex -c developer_instructions`; antigravity falls back to an `AGENTS.md` block)
|
|
13
13
|
|
|
14
14
|
Provider auth stays in the provider CLI — the launcher never handles provider API keys.
|
|
15
15
|
|
|
@@ -60,9 +60,10 @@ The shape, for reference — the `"$schema"` line points at the published schema
|
|
|
60
60
|
}
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
- The **installation id is not stored** — it is resolved on each run, in order: the owner of the repo you launch in, then
|
|
64
|
-
-
|
|
65
|
-
- `installationId` is
|
|
63
|
+
- The **installation id is not stored** — it is resolved on each run, in order: the owner of the repo you launch in, then the App's sole installation. So the same agent works across every org the App is installed on, and it survives an uninstall/reinstall (which rotates the id).
|
|
64
|
+
- An App installed on more than one account only resolves when you launch from inside a repo it covers — that repo picks the installation.
|
|
65
|
+
- `installationId` is accepted as an explicit override — set it to pin a specific id or skip the lookup (offline, or in a git credential helper).
|
|
66
|
+
- One agent, many accounts: a **private** GitHub App only installs on the account that owns it. To run the agent on both a personal account and an org, make the App public or use one App per account — see [docs/github-app.md](docs/github-app.md).
|
|
66
67
|
- `botId` is optional. When omitted it is resolved from the GitHub API using `botName` so the git author email links commits to the bot. Set it explicitly only to skip that lookup (e.g. offline).
|
|
67
68
|
- Do not put provider-specific config here — providers are auto-detected from PATH.
|
|
68
69
|
|
|
@@ -87,23 +88,25 @@ By default the installation token carries the full installation scope. Narrow it
|
|
|
87
88
|
|
|
88
89
|
Omitting the `permissions` key entirely is the safe default — the token then carries whatever the App grants. `repositories` works the same way: list it and the token can only touch those repos.
|
|
89
90
|
|
|
90
|
-
On launch, `agent-forge` prints the scope GitHub actually granted (`
|
|
91
|
+
On launch, `agent-forge` prints the scope GitHub actually granted (`scope` row in the summary, alongside the `account` the token is for); `agent-forge token --agent <name>` prints it to stderr. If a call fails with `403 Resource not accessible by integration`, check that row first.
|
|
91
92
|
|
|
92
93
|
## Add an agent
|
|
93
94
|
|
|
94
95
|
You need a GitHub App first — its **App ID**, a **private key**, and an **installation**. If you don't have one yet, follow [docs/github-app.md](docs/github-app.md).
|
|
95
96
|
|
|
97
|
+
Running `agent-forge` with no agents configured offers to run the wizard for you.
|
|
98
|
+
|
|
96
99
|
### Guided (recommended)
|
|
97
100
|
|
|
98
101
|
```bash
|
|
99
102
|
agent-forge add
|
|
100
103
|
```
|
|
101
104
|
|
|
102
|
-
You provide the **GitHub App ID** and the **path to its private key**. The wizard derives the rest from the GitHub API — slug (`botName`), bot user id
|
|
105
|
+
You provide the **GitHub App ID** and the **path to its private key**. The wizard derives the rest from the GitHub API — slug (`botName`), bot user id — asks for a label and an optional system prompt, mints a test token to confirm it works, and writes the entry. It writes to the resolved config path — the global registry (`~/.config/agent-forge/agents.json`) unless `$AGENT_FORGE_CONFIG` or an existing `./agents.json` redirects it.
|
|
103
106
|
|
|
104
107
|
### Manual
|
|
105
108
|
|
|
106
|
-
Copy one agent object inside the `agents` array and set unique values for `name`, `label`, `appId`, `botName`, `privateKeyPath
|
|
109
|
+
Copy one agent object inside the `agents` array and set unique values for `name`, `label`, `appId`, `botName`, `privateKeyPath`. Make sure the private key file exists and is readable.
|
|
107
110
|
|
|
108
111
|
## Launch
|
|
109
112
|
|
|
@@ -113,26 +116,41 @@ Interactive:
|
|
|
113
116
|
agent-forge
|
|
114
117
|
```
|
|
115
118
|
|
|
116
|
-
Direct flags (passing both skips
|
|
119
|
+
Direct flags (passing both skips the menus; passing one pre-fills it, the other is prompted — or auto-picked when only one agent or provider is available):
|
|
117
120
|
|
|
118
121
|
```bash
|
|
119
122
|
agent-forge --agent ops-agent --provider claude
|
|
120
123
|
agent-forge --agent review-agent --provider codex
|
|
121
124
|
```
|
|
122
125
|
|
|
126
|
+
Before launching, it prints a summary:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
│ provider claude
|
|
130
|
+
│ identity ops-agent[bot]
|
|
131
|
+
│ account AshuLab ← which account the token is for
|
|
132
|
+
│ repo AshuLab/app ← the repo you're in (drives account)
|
|
133
|
+
│ scope write: contents, issues, pull_requests · +4 read
|
|
134
|
+
│ expires ~59m ← installation tokens last ~1h
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
(The `antigravity` provider adds a `memory` row pointing at the `AGENTS.md` it wrote.)
|
|
138
|
+
|
|
123
139
|
Other commands:
|
|
124
140
|
|
|
125
141
|
```bash
|
|
126
142
|
agent-forge --list
|
|
143
|
+
agent-forge --version
|
|
127
144
|
agent-forge --help
|
|
128
145
|
```
|
|
129
146
|
|
|
130
147
|
### Provider-specific prompt behavior
|
|
131
148
|
|
|
132
|
-
|
|
133
|
-
- `codex` / `antigravity` (`agy`): no CLI flag — identity comes from `AGENTS.md` only (`agy --prompt` is headless `--print`; a codex positional arg is a fake first user turn)
|
|
149
|
+
`systemPrompt` is injected per process — no repo file is touched:
|
|
134
150
|
|
|
135
|
-
|
|
151
|
+
- `claude`: `--append-system-prompt` (lands in the real system prompt)
|
|
152
|
+
- `codex`: `-c developer_instructions=…` (appends a developer message; unlike `model_instructions_file` it does not replace codex's base prompt)
|
|
153
|
+
- `antigravity` (`agy`): no such flag — the launcher writes `systemPrompt` into `AGENTS.md` in the working directory, inside a managed `agent-forge:identity` block. Do not commit it. Run one agent per worktree.
|
|
136
154
|
|
|
137
155
|
## Token refresh
|
|
138
156
|
|
package/dist/launcher.cjs
CHANGED
|
@@ -4017,7 +4017,7 @@ var require_jsonwebtoken = __commonJS({
|
|
|
4017
4017
|
// src/launcher.js
|
|
4018
4018
|
var import_node_child_process3 = require("node:child_process");
|
|
4019
4019
|
var import_node_path4 = require("node:path");
|
|
4020
|
-
var
|
|
4020
|
+
var import_node_util4 = require("node:util");
|
|
4021
4021
|
|
|
4022
4022
|
// node_modules/.pnpm/@clack+core@1.4.3/node_modules/@clack/core/dist/index.mjs
|
|
4023
4023
|
var import_node_util = require("node:util");
|
|
@@ -5377,19 +5377,9 @@ function pickInstallation(installations, agent) {
|
|
|
5377
5377
|
if (installations.length === 0) {
|
|
5378
5378
|
throw new Error(`GitHub App ${agent.appId} has no installations. Install it on an account or org first.`);
|
|
5379
5379
|
}
|
|
5380
|
-
if (agent.account) {
|
|
5381
|
-
const want = String(agent.account).toLowerCase();
|
|
5382
|
-
const match = installations.find((item) => item.account.toLowerCase() === want);
|
|
5383
|
-
if (!match) {
|
|
5384
|
-
throw new Error(
|
|
5385
|
-
`GitHub App ${agent.appId} is not installed on "${agent.account}". Installed on: ${installations.map((item) => item.account).join(", ")}.`
|
|
5386
|
-
);
|
|
5387
|
-
}
|
|
5388
|
-
return match.id;
|
|
5389
|
-
}
|
|
5390
5380
|
if (installations.length === 1) return installations[0].id;
|
|
5391
5381
|
throw new Error(
|
|
5392
|
-
`GitHub App ${agent.appId} is installed on ${installations.length} accounts (${installations.map((item) => item.account).join(", ")}).
|
|
5382
|
+
`GitHub App ${agent.appId} is installed on ${installations.length} accounts (${installations.map((item) => item.account).join(", ")}). Launch from inside a repo under one of them.`
|
|
5393
5383
|
);
|
|
5394
5384
|
}
|
|
5395
5385
|
function parseGithubRemote(url) {
|
|
@@ -5409,19 +5399,21 @@ function currentRepoSlug() {
|
|
|
5409
5399
|
async function installationForRepo(slug, jwtToken) {
|
|
5410
5400
|
try {
|
|
5411
5401
|
const data = await githubApi(`/repos/${slug.owner}/${slug.repo}/installation`, { jwtToken });
|
|
5412
|
-
return String(data.id);
|
|
5402
|
+
return { id: String(data.id), account: data.account?.login || data.account?.slug || null };
|
|
5413
5403
|
} catch {
|
|
5414
5404
|
return null;
|
|
5415
5405
|
}
|
|
5416
5406
|
}
|
|
5417
|
-
async function
|
|
5418
|
-
if (agent.installationId) return String(agent.installationId);
|
|
5407
|
+
async function resolveInstallation(agent, jwtToken) {
|
|
5408
|
+
if (agent.installationId) return { id: String(agent.installationId), account: null };
|
|
5419
5409
|
const slug = currentRepoSlug();
|
|
5420
5410
|
if (slug) {
|
|
5421
|
-
const
|
|
5422
|
-
if (
|
|
5411
|
+
const hit = await installationForRepo(slug, jwtToken);
|
|
5412
|
+
if (hit) return hit;
|
|
5423
5413
|
}
|
|
5424
|
-
|
|
5414
|
+
const installations = await listInstallations(jwtToken);
|
|
5415
|
+
const id = pickInstallation(installations, agent);
|
|
5416
|
+
return { id, account: installations.find((item) => item.id === id)?.account ?? null };
|
|
5425
5417
|
}
|
|
5426
5418
|
function buildTokenScope(agent) {
|
|
5427
5419
|
const scope = {};
|
|
@@ -5432,13 +5424,15 @@ function buildTokenScope(agent) {
|
|
|
5432
5424
|
async function generateGithubAppToken(agent) {
|
|
5433
5425
|
const privateKey = readPrivateKey(agent.privateKeyPath, agent.name || agent.appId);
|
|
5434
5426
|
const jwtToken = signAppJwt(agent.appId, privateKey);
|
|
5435
|
-
const installationId = await
|
|
5427
|
+
const { id: installationId, account } = await resolveInstallation(agent, jwtToken);
|
|
5436
5428
|
const data = await githubApi(`/app/installations/${installationId}/access_tokens`, {
|
|
5437
5429
|
jwtToken,
|
|
5438
5430
|
body: buildTokenScope(agent)
|
|
5439
5431
|
});
|
|
5440
5432
|
return {
|
|
5441
5433
|
token: data.token,
|
|
5434
|
+
account,
|
|
5435
|
+
expiresAt: data.expires_at || null,
|
|
5442
5436
|
// What GitHub actually granted, after both the App's config and our scoping.
|
|
5443
5437
|
permissions: data.permissions || {},
|
|
5444
5438
|
repositorySelection: data.repository_selection || "all"
|
|
@@ -5480,7 +5474,9 @@ function getProviderPromptArgs(providerName, agent) {
|
|
|
5480
5474
|
if (!prompt) {
|
|
5481
5475
|
return [];
|
|
5482
5476
|
}
|
|
5483
|
-
|
|
5477
|
+
if (providerName === "claude") return ["--append-system-prompt", prompt];
|
|
5478
|
+
if (providerName === "codex") return ["-c", `developer_instructions=${JSON.stringify(prompt)}`];
|
|
5479
|
+
return [];
|
|
5484
5480
|
}
|
|
5485
5481
|
|
|
5486
5482
|
// src/identity.js
|
|
@@ -5488,7 +5484,7 @@ var import_node_fs3 = require("node:fs");
|
|
|
5488
5484
|
var import_node_path3 = require("node:path");
|
|
5489
5485
|
var START = "<!-- agent-forge:identity:start -->";
|
|
5490
5486
|
var END = "<!-- agent-forge:identity:end -->";
|
|
5491
|
-
var MEMORY_FILE = {
|
|
5487
|
+
var MEMORY_FILE = { antigravity: "AGENTS.md" };
|
|
5492
5488
|
function upsertIdentityBlock(content, prompt) {
|
|
5493
5489
|
const block2 = `${START}
|
|
5494
5490
|
${prompt}
|
|
@@ -5515,10 +5511,97 @@ function syncIdentityFile(providerName, agent, cwd = process.cwd()) {
|
|
|
5515
5511
|
return file;
|
|
5516
5512
|
}
|
|
5517
5513
|
|
|
5514
|
+
// src/format.js
|
|
5515
|
+
function formatScope(permissions, full = false) {
|
|
5516
|
+
const entries = Object.entries(permissions);
|
|
5517
|
+
if (entries.length === 0) return "full installation scope";
|
|
5518
|
+
if (full) return entries.map(([name, level]) => `${name}:${level}`).join(" ");
|
|
5519
|
+
const writes = entries.filter(([, level]) => level !== "read").map(([name]) => name);
|
|
5520
|
+
if (writes.length === 0) return `read-only (${entries.length})`;
|
|
5521
|
+
const reads = entries.length - writes.length;
|
|
5522
|
+
return `write: ${writes.join(", ")}${reads ? ` \xB7 +${reads} read` : ""}`;
|
|
5523
|
+
}
|
|
5524
|
+
function formatExpiry(iso) {
|
|
5525
|
+
const ms = new Date(iso).getTime() - Date.now();
|
|
5526
|
+
if (!Number.isFinite(ms)) return String(iso);
|
|
5527
|
+
const minutes = Math.round(ms / 6e4);
|
|
5528
|
+
return minutes > 0 ? `~${minutes}m` : "expired";
|
|
5529
|
+
}
|
|
5530
|
+
|
|
5531
|
+
// src/cli.js
|
|
5532
|
+
var import_node_util3 = require("node:util");
|
|
5533
|
+
|
|
5534
|
+
// package.json
|
|
5535
|
+
var package_default = {
|
|
5536
|
+
name: "@ashulab/agent-forge",
|
|
5537
|
+
version: "0.4.0",
|
|
5538
|
+
description: "Give claude/codex/antigravity their own GitHub identity: a bot account, a scoped token minted per run, git attribution to match",
|
|
5539
|
+
license: "MIT",
|
|
5540
|
+
author: "Diego Ghersi <ghersidl@gmail.com>",
|
|
5541
|
+
type: "module",
|
|
5542
|
+
bin: {
|
|
5543
|
+
"agent-forge": "dist/launcher.cjs"
|
|
5544
|
+
},
|
|
5545
|
+
main: "dist/launcher.cjs",
|
|
5546
|
+
files: [
|
|
5547
|
+
"dist",
|
|
5548
|
+
"schema",
|
|
5549
|
+
"LICENSE",
|
|
5550
|
+
"README.md"
|
|
5551
|
+
],
|
|
5552
|
+
engines: {
|
|
5553
|
+
node: ">=24"
|
|
5554
|
+
},
|
|
5555
|
+
packageManager: "pnpm@11.20.0",
|
|
5556
|
+
repository: {
|
|
5557
|
+
type: "git",
|
|
5558
|
+
url: "git+https://github.com/AshuLab/agent-forge.git"
|
|
5559
|
+
},
|
|
5560
|
+
bugs: {
|
|
5561
|
+
url: "https://github.com/AshuLab/agent-forge/issues"
|
|
5562
|
+
},
|
|
5563
|
+
homepage: "https://github.com/AshuLab/agent-forge#readme",
|
|
5564
|
+
keywords: [
|
|
5565
|
+
"github-app",
|
|
5566
|
+
"cli",
|
|
5567
|
+
"coding-agent",
|
|
5568
|
+
"claude",
|
|
5569
|
+
"codex",
|
|
5570
|
+
"antigravity",
|
|
5571
|
+
"installation-token",
|
|
5572
|
+
"git-identity"
|
|
5573
|
+
],
|
|
5574
|
+
publishConfig: {
|
|
5575
|
+
access: "public"
|
|
5576
|
+
},
|
|
5577
|
+
scripts: {
|
|
5578
|
+
dev: "node src/launcher.js",
|
|
5579
|
+
test: "node --test",
|
|
5580
|
+
start: "node dist/launcher.cjs",
|
|
5581
|
+
build: "esbuild src/launcher.js --bundle --platform=node --format=cjs --target=node24 --outfile=dist/launcher.cjs --banner:js='#!/usr/bin/env node' && chmod +x dist/launcher.cjs",
|
|
5582
|
+
prepublishOnly: "pnpm build && pnpm test"
|
|
5583
|
+
},
|
|
5584
|
+
dependencies: {
|
|
5585
|
+
"@clack/prompts": "^1.7.0",
|
|
5586
|
+
jsonwebtoken: "^9.0.3"
|
|
5587
|
+
},
|
|
5588
|
+
devDependencies: {
|
|
5589
|
+
esbuild: "^0.28.2"
|
|
5590
|
+
}
|
|
5591
|
+
};
|
|
5592
|
+
|
|
5518
5593
|
// src/cli.js
|
|
5594
|
+
var LOGO = `
|
|
5595
|
+
\u2584\u2584 \u2584\u2584\u2584 \u2584\u2584\u2584 \u2584\u2584 \u2584\u2584\u2584 \u2584\u2584\u2584 \u2584\u2584 \u2584\u2584 \u2584\u2584\u2584 \u2584\u2584\u2584
|
|
5596
|
+
\u2588\u2584\u2588 \u2588 \u2584 \u2588\u2584 \u2588 \u2588 \u2588 \u2588\u2584 \u2588 \u2588 \u2588\u2584\u2580 \u2588 \u2584 \u2588\u2584
|
|
5597
|
+
\u2588 \u2588 \u2580\u2584\u2588 \u2588\u2584\u2584 \u2588 \u2588 \u2588 \u2588 \u2580\u2584\u2580 \u2588 \u2588 \u2580\u2584\u2588 \u2588\u2584\u2584
|
|
5598
|
+
`;
|
|
5599
|
+
function printVersion() {
|
|
5600
|
+
console.log(package_default.version);
|
|
5601
|
+
}
|
|
5519
5602
|
function printHelp() {
|
|
5520
5603
|
console.log(`
|
|
5521
|
-
Agent Forge
|
|
5604
|
+
Agent Forge v${package_default.version}
|
|
5522
5605
|
|
|
5523
5606
|
Usage:
|
|
5524
5607
|
agent-forge
|
|
@@ -5526,6 +5609,7 @@ Usage:
|
|
|
5526
5609
|
agent-forge add guided setup for a new agent
|
|
5527
5610
|
agent-forge token --agent <name> print a fresh GitHub App token (for refresh)
|
|
5528
5611
|
agent-forge --list
|
|
5612
|
+
agent-forge --version
|
|
5529
5613
|
agent-forge --help
|
|
5530
5614
|
|
|
5531
5615
|
Requirements:
|
|
@@ -5539,10 +5623,13 @@ Config resolution (first match wins):
|
|
|
5539
5623
|
- ./agents.json (only if it already exists)
|
|
5540
5624
|
`);
|
|
5541
5625
|
}
|
|
5626
|
+
function bail(message = "Cancelled") {
|
|
5627
|
+
cancel(`\u2716 ${message}`);
|
|
5628
|
+
process.exit(0);
|
|
5629
|
+
}
|
|
5542
5630
|
function keep(value) {
|
|
5543
5631
|
if (isCancel(value)) {
|
|
5544
|
-
|
|
5545
|
-
process.exit(0);
|
|
5632
|
+
bail();
|
|
5546
5633
|
}
|
|
5547
5634
|
return value;
|
|
5548
5635
|
}
|
|
@@ -5569,17 +5656,8 @@ async function addAgentWizard({ embedded = false } = {}) {
|
|
|
5569
5656
|
if (meta.installations.length === 0) {
|
|
5570
5657
|
throw new Error("This App has no installations. Install it on an account or org first.");
|
|
5571
5658
|
}
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
note(`${meta.installations[0].account} \u2014 resolved automatically each run`, "Installation");
|
|
5575
|
-
} else {
|
|
5576
|
-
account = keep(
|
|
5577
|
-
await select({
|
|
5578
|
-
message: "Which account is this agent for?",
|
|
5579
|
-
options: meta.installations.map((i2) => ({ value: i2.account, label: `${i2.account} (${i2.id})` }))
|
|
5580
|
-
})
|
|
5581
|
-
);
|
|
5582
|
-
}
|
|
5659
|
+
const where = meta.installations.length === 1 ? `${meta.installations[0].account} \u2014 resolved automatically each run` : `${meta.installations.map((i2) => i2.account).join(", ")} \u2014 resolved from the repo you launch in`;
|
|
5660
|
+
note(where, "Installation");
|
|
5583
5661
|
const name = (keep(await text({ message: "Agent name", placeholder: meta.slug, defaultValue: meta.slug })) || meta.slug).trim();
|
|
5584
5662
|
const label = (keep(await text({ message: "Label", placeholder: meta.name, defaultValue: meta.name })) || meta.name).trim();
|
|
5585
5663
|
const systemPrompt = keep(await text({ message: "System prompt (optional)", placeholder: "You are ..." })).trim();
|
|
@@ -5589,7 +5667,6 @@ async function addAgentWizard({ embedded = false } = {}) {
|
|
|
5589
5667
|
appId,
|
|
5590
5668
|
botName: meta.slug,
|
|
5591
5669
|
privateKeyPath,
|
|
5592
|
-
...account ? { account } : {},
|
|
5593
5670
|
...systemPrompt ? { systemPrompt } : {}
|
|
5594
5671
|
};
|
|
5595
5672
|
const check = spinner();
|
|
@@ -5614,7 +5691,7 @@ async function interactiveSelection(defaults = {}) {
|
|
|
5614
5691
|
if (agents.length === 0 && !defaults.agent) {
|
|
5615
5692
|
note("No agents configured yet. Each agent is a GitHub App identity the launcher runs under.", "First run");
|
|
5616
5693
|
if (!keep(await confirm({ message: "Add one now?" }))) {
|
|
5617
|
-
|
|
5694
|
+
outro(`Run ${(0, import_node_util3.styleText)("cyan", "agent-forge add")} when you're ready.`);
|
|
5618
5695
|
process.exit(0);
|
|
5619
5696
|
}
|
|
5620
5697
|
await addAgentWizard({ embedded: true });
|
|
@@ -5644,9 +5721,9 @@ async function interactiveSelection(defaults = {}) {
|
|
|
5644
5721
|
})
|
|
5645
5722
|
);
|
|
5646
5723
|
}
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5724
|
+
const launchPrompt = `Launch ${(0, import_node_util3.styleText)("green", agentChoice)} with ${(0, import_node_util3.styleText)(["cyan", "bold"], providerChoice)}?`;
|
|
5725
|
+
if (!keep(await confirm({ message: launchPrompt }))) {
|
|
5726
|
+
bail("Cancelled \u2014 nothing launched");
|
|
5650
5727
|
}
|
|
5651
5728
|
return { agentName: agentChoice, providerName: providerChoice };
|
|
5652
5729
|
}
|
|
@@ -5655,6 +5732,7 @@ function parseArgs() {
|
|
|
5655
5732
|
const result = {
|
|
5656
5733
|
command: args[0] && !args[0].startsWith("-") ? args[0] : void 0,
|
|
5657
5734
|
help: false,
|
|
5735
|
+
version: false,
|
|
5658
5736
|
list: false,
|
|
5659
5737
|
agent: void 0,
|
|
5660
5738
|
provider: void 0
|
|
@@ -5664,6 +5742,7 @@ function parseArgs() {
|
|
|
5664
5742
|
const arg = args[i2];
|
|
5665
5743
|
const next = args[i2 + 1];
|
|
5666
5744
|
if (arg === "--help" || arg === "-h") result.help = true;
|
|
5745
|
+
if (arg === "--version" || arg === "-v") result.version = true;
|
|
5667
5746
|
if (arg === "--list" || arg === "-l") result.list = true;
|
|
5668
5747
|
if (arg === "--agent" || arg === "-a") {
|
|
5669
5748
|
result.agent = value(next);
|
|
@@ -5677,7 +5756,8 @@ function parseArgs() {
|
|
|
5677
5756
|
return result;
|
|
5678
5757
|
}
|
|
5679
5758
|
function showIntro() {
|
|
5680
|
-
|
|
5759
|
+
console.log((0, import_node_util3.styleText)("cyan", LOGO));
|
|
5760
|
+
intro(`Agent Forge ${(0, import_node_util3.styleText)("dim", `v${package_default.version}`)}`);
|
|
5681
5761
|
}
|
|
5682
5762
|
|
|
5683
5763
|
// src/launcher.js
|
|
@@ -5703,10 +5783,10 @@ async function launchAgent(agentName, providerName) {
|
|
|
5703
5783
|
const providerInfo = getProviderInfo(providerName, agent);
|
|
5704
5784
|
const s = spinner();
|
|
5705
5785
|
s.start("Minting installation token");
|
|
5706
|
-
const { token: githubToken, permissions, repositorySelection } = await generateGithubAppToken(agent);
|
|
5786
|
+
const { token: githubToken, account, expiresAt, permissions, repositorySelection } = await generateGithubAppToken(agent);
|
|
5707
5787
|
const botId = await resolveBotId(agent);
|
|
5708
5788
|
const gitIdentity = buildGitIdentity(agent, botId);
|
|
5709
|
-
s.stop(`Token ready for ${(0,
|
|
5789
|
+
s.stop(`Token ready for ${(0, import_node_util4.styleText)("green", gitIdentity.GIT_AUTHOR_NAME)}`);
|
|
5710
5790
|
const identityFile = syncIdentityFile(providerName, agent);
|
|
5711
5791
|
const runtimeEnv = {
|
|
5712
5792
|
...process.env,
|
|
@@ -5714,18 +5794,24 @@ async function launchAgent(agentName, providerName) {
|
|
|
5714
5794
|
GH_TOKEN: githubToken,
|
|
5715
5795
|
...gitIdentity
|
|
5716
5796
|
};
|
|
5717
|
-
const
|
|
5718
|
-
const scopeText =
|
|
5719
|
-
const
|
|
5797
|
+
const narrowed = Boolean(agent.permissions) || Boolean(agent.repositories);
|
|
5798
|
+
const scopeText = formatScope(permissions, narrowed);
|
|
5799
|
+
const repoSlug = currentRepoSlug();
|
|
5800
|
+
const row = (label, value) => `${(0, import_node_util4.styleText)("dim", label.padEnd(10))}${value}`;
|
|
5720
5801
|
const summary = [
|
|
5721
|
-
row("provider", (0,
|
|
5722
|
-
row("identity", (0,
|
|
5723
|
-
row("token", (0, import_node_util3.styleText)("dim", repositorySelection === "selected" ? `${scopeText} \xB7 selected repos` : scopeText))
|
|
5802
|
+
row("provider", (0, import_node_util4.styleText)(["cyan", "bold"], providerName)),
|
|
5803
|
+
row("identity", (0, import_node_util4.styleText)("green", gitIdentity.GIT_AUTHOR_NAME))
|
|
5724
5804
|
];
|
|
5805
|
+
if (account) summary.push(row("account", (0, import_node_util4.styleText)("yellow", account)));
|
|
5806
|
+
if (repoSlug) summary.push(row("repo", (0, import_node_util4.styleText)("dim", `${repoSlug.owner}/${repoSlug.repo}`)));
|
|
5807
|
+
summary.push(
|
|
5808
|
+
row("scope", (0, import_node_util4.styleText)("dim", repositorySelection === "selected" ? `${scopeText} \xB7 selected repos` : scopeText))
|
|
5809
|
+
);
|
|
5810
|
+
if (expiresAt) summary.push(row("expires", (0, import_node_util4.styleText)("dim", formatExpiry(expiresAt))));
|
|
5725
5811
|
if (identityFile) {
|
|
5726
|
-
summary.push(row("memory", (0,
|
|
5812
|
+
summary.push(row("memory", (0, import_node_util4.styleText)("dim", (0, import_node_path4.relative)(process.cwd(), identityFile) || identityFile)));
|
|
5727
5813
|
}
|
|
5728
|
-
note(summary.join("\n"), (0,
|
|
5814
|
+
note(summary.join("\n"), (0, import_node_util4.styleText)("bold", agent.label || agent.name));
|
|
5729
5815
|
const providerArgs = getProviderPromptArgs(providerName, agent);
|
|
5730
5816
|
const child = (0, import_node_child_process3.spawn)(providerInfo.command, providerArgs, {
|
|
5731
5817
|
stdio: "inherit",
|
|
@@ -5739,7 +5825,7 @@ async function launchAgent(agentName, providerName) {
|
|
|
5739
5825
|
cancel(`Could not execute ${providerInfo.command}: ${error.message}`);
|
|
5740
5826
|
process.exit(1);
|
|
5741
5827
|
});
|
|
5742
|
-
outro(`Running ${(0,
|
|
5828
|
+
outro(`Running ${(0, import_node_util4.styleText)("cyan", providerName)}\u2026`);
|
|
5743
5829
|
}
|
|
5744
5830
|
async function main() {
|
|
5745
5831
|
const flags = parseArgs();
|
|
@@ -5747,6 +5833,10 @@ async function main() {
|
|
|
5747
5833
|
printHelp();
|
|
5748
5834
|
return;
|
|
5749
5835
|
}
|
|
5836
|
+
if (flags.version) {
|
|
5837
|
+
printVersion();
|
|
5838
|
+
return;
|
|
5839
|
+
}
|
|
5750
5840
|
if (flags.list) {
|
|
5751
5841
|
listAgents();
|
|
5752
5842
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ashulab/agent-forge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Give claude/codex/antigravity their own GitHub identity: a bot account, a scoped token minted per run, git attribution to match",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Diego Ghersi <ghersidl@gmail.com>",
|
|
@@ -25,13 +25,9 @@
|
|
|
25
25
|
"type": "string",
|
|
26
26
|
"description": "GitHub App ID (numeric, stored as a string)"
|
|
27
27
|
},
|
|
28
|
-
"account": {
|
|
29
|
-
"type": "string",
|
|
30
|
-
"description": "Account/org login where the App is installed. Fallback for installation-id resolution when you run outside a matching repo. Needed only when the App has more than one installation. Resolution order: repo owner, then account, then the sole installation."
|
|
31
|
-
},
|
|
32
28
|
"installationId": {
|
|
33
29
|
"type": "string",
|
|
34
|
-
"description": "Optional override. Normally the installation id is resolved (repo owner, then
|
|
30
|
+
"description": "Optional override. Normally the installation id is resolved (repo owner, then the App's sole installation). Set this to pin a specific id or skip the lookup (offline, or in a git credential helper)."
|
|
35
31
|
},
|
|
36
32
|
"botName": {
|
|
37
33
|
"type": "string",
|