@ashulab/agent-forge 0.4.0 → 0.5.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 +4 -4
- package/dist/launcher.cjs +100 -39
- package/package.json +1 -1
- package/schema/agents.schema.json +5 -4
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
|
-
- injects the agent's `systemPrompt`
|
|
12
|
+
- injects an identity prompt per process — a fixed block naming the git author identity and the `GH_TOKEN` / `GITHUB_TOKEN` in the environment, with the agent's optional `systemPrompt` appended (`claude --append-system-prompt`, `codex -c developer_instructions`; antigravity via a global custom agent selected with `--agent`) — no repo file is touched
|
|
13
13
|
|
|
14
14
|
Provider auth stays in the provider CLI — the launcher never handles provider API keys.
|
|
15
15
|
|
|
@@ -134,7 +134,7 @@ Before launching, it prints a summary:
|
|
|
134
134
|
│ expires ~59m ← installation tokens last ~1h
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
-
(The `antigravity` provider adds
|
|
137
|
+
(The `antigravity` provider adds an `agent` row showing the `--agent` id and the custom-agent file it wrote.)
|
|
138
138
|
|
|
139
139
|
Other commands:
|
|
140
140
|
|
|
@@ -146,11 +146,11 @@ agent-forge --help
|
|
|
146
146
|
|
|
147
147
|
### Provider-specific prompt behavior
|
|
148
148
|
|
|
149
|
-
`systemPrompt` is injected per process — no repo file is touched:
|
|
149
|
+
The identity prompt (identity block + the agent's optional `systemPrompt`) is injected per process — no repo file is touched:
|
|
150
150
|
|
|
151
151
|
- `claude`: `--append-system-prompt` (lands in the real system prompt)
|
|
152
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
|
|
153
|
+
- `antigravity` (`agy`): no prompt flag — the launcher writes the identity prompt as a global antigravity custom agent at `~/.gemini/config/agents/<name>/agent.md` (frontmatter `name`, `mainAgent: true`, `agentForge: true`; prompt under an `# Identity` heading) and passes `--agent <name>`. The file is keyed by the agent's registry `name`, upserted on each run, and never deleted. If a file with that name exists without our `agentForge` marker, the launch fails rather than overwrite it — rename the agent or remove that file.
|
|
154
154
|
|
|
155
155
|
## Token refresh
|
|
156
156
|
|
package/dist/launcher.cjs
CHANGED
|
@@ -4016,7 +4016,6 @@ var require_jsonwebtoken = __commonJS({
|
|
|
4016
4016
|
|
|
4017
4017
|
// src/launcher.js
|
|
4018
4018
|
var import_node_child_process3 = require("node:child_process");
|
|
4019
|
-
var import_node_path4 = require("node:path");
|
|
4020
4019
|
var import_node_util4 = require("node:util");
|
|
4021
4020
|
|
|
4022
4021
|
// node_modules/.pnpm/@clack+core@1.4.3/node_modules/@clack/core/dist/index.mjs
|
|
@@ -5220,6 +5219,7 @@ var import_node_fs = require("node:fs");
|
|
|
5220
5219
|
var import_node_os = require("node:os");
|
|
5221
5220
|
var import_node_path = require("node:path");
|
|
5222
5221
|
var REQUIRED_FIELDS = ["name", "appId", "privateKeyPath"];
|
|
5222
|
+
var VALID_NAME = /^[a-z0-9][a-z0-9-]*$/;
|
|
5223
5223
|
var SCHEMA_URL = "https://raw.githubusercontent.com/AshuLab/agent-forge/main/schema/agents.schema.json";
|
|
5224
5224
|
var CONFIG_HOME = process.env.XDG_CONFIG_HOME || (0, import_node_path.join)((0, import_node_os.homedir)(), ".config");
|
|
5225
5225
|
var GLOBAL_CONFIG = (0, import_node_path.join)(CONFIG_HOME, "agent-forge", "agents.json");
|
|
@@ -5264,6 +5264,11 @@ function validateAgents(agents) {
|
|
|
5264
5264
|
throw new Error(`agents.json: agent ${ref} is missing "${field}"`);
|
|
5265
5265
|
}
|
|
5266
5266
|
}
|
|
5267
|
+
if (!VALID_NAME.test(agent.name)) {
|
|
5268
|
+
throw new Error(
|
|
5269
|
+
`agents.json: agent ${ref} has an invalid name "${agent.name}" (lowercase letters, digits, hyphens)`
|
|
5270
|
+
);
|
|
5271
|
+
}
|
|
5267
5272
|
if (seen.has(agent.name)) {
|
|
5268
5273
|
throw new Error(`agents.json: duplicate agent name "${agent.name}"`);
|
|
5269
5274
|
}
|
|
@@ -5469,46 +5474,83 @@ function getProviderInfo(providerName, agent) {
|
|
|
5469
5474
|
const resolvedCommand = isCommandInstalled(command) ? command : (provider.aliases || []).find((alias) => isCommandInstalled(alias)) || command;
|
|
5470
5475
|
return { provider, command: resolvedCommand };
|
|
5471
5476
|
}
|
|
5472
|
-
function getProviderPromptArgs(providerName,
|
|
5473
|
-
const prompt = agent.systemPrompt || agent.instructions || agent.identityPrompt;
|
|
5474
|
-
if (!prompt) {
|
|
5475
|
-
return [];
|
|
5476
|
-
}
|
|
5477
|
+
function getProviderPromptArgs(providerName, prompt) {
|
|
5477
5478
|
if (providerName === "claude") return ["--append-system-prompt", prompt];
|
|
5478
5479
|
if (providerName === "codex") return ["-c", `developer_instructions=${JSON.stringify(prompt)}`];
|
|
5479
5480
|
return [];
|
|
5480
5481
|
}
|
|
5482
|
+
function buildProviderArgs(providerName, prompt, antigravityAgent) {
|
|
5483
|
+
return [
|
|
5484
|
+
...getProviderPromptArgs(providerName, prompt),
|
|
5485
|
+
...antigravityAgent ? ["--agent", antigravityAgent.name] : []
|
|
5486
|
+
];
|
|
5487
|
+
}
|
|
5481
5488
|
|
|
5482
|
-
// src/
|
|
5489
|
+
// src/agent-file.js
|
|
5483
5490
|
var import_node_fs3 = require("node:fs");
|
|
5491
|
+
var import_node_os3 = require("node:os");
|
|
5484
5492
|
var import_node_path3 = require("node:path");
|
|
5485
|
-
var
|
|
5486
|
-
var
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5493
|
+
var MARKER = "agentForge: true";
|
|
5494
|
+
var MARKER_LINE = /^agentForge:\s*true\s*$/m;
|
|
5495
|
+
function isManagedByAgentForge(text2) {
|
|
5496
|
+
const end = text2.startsWith("---\n") ? text2.indexOf("\n---", 4) : -1;
|
|
5497
|
+
return end !== -1 && MARKER_LINE.test(text2.slice(0, end));
|
|
5498
|
+
}
|
|
5499
|
+
var AGENTS_HOME = (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".gemini", "config", "agents");
|
|
5500
|
+
var VALID_NAME2 = /^[a-z0-9][a-z0-9-]*$/;
|
|
5501
|
+
function renderAgentFile(name, description, prompt) {
|
|
5502
|
+
return [
|
|
5503
|
+
"---",
|
|
5504
|
+
`name: ${name}`,
|
|
5505
|
+
`description: ${JSON.stringify(description)}`,
|
|
5506
|
+
"mainAgent: true",
|
|
5507
|
+
MARKER,
|
|
5508
|
+
"---",
|
|
5509
|
+
"",
|
|
5510
|
+
"# Identity",
|
|
5511
|
+
"",
|
|
5512
|
+
prompt.trimEnd(),
|
|
5513
|
+
""
|
|
5514
|
+
].join("\n");
|
|
5515
|
+
}
|
|
5516
|
+
function syncAntigravityAgent(agent, prompt, agentsHome = AGENTS_HOME) {
|
|
5517
|
+
const name = agent.name;
|
|
5518
|
+
if (!VALID_NAME2.test(name)) {
|
|
5519
|
+
throw new Error(
|
|
5520
|
+
`Agent name "${name}" is not a valid antigravity agent id (lowercase letters, digits, hyphens).`
|
|
5521
|
+
);
|
|
5522
|
+
}
|
|
5523
|
+
const dir = (0, import_node_path3.join)(agentsHome, name);
|
|
5524
|
+
const file = (0, import_node_path3.join)(dir, "agent.md");
|
|
5525
|
+
const next = renderAgentFile(name, agent.label || "GitHub App identity managed by agent-forge", prompt);
|
|
5526
|
+
if ((0, import_node_fs3.existsSync)(file)) {
|
|
5527
|
+
const current = (0, import_node_fs3.readFileSync)(file, "utf8");
|
|
5528
|
+
if (!isManagedByAgentForge(current)) {
|
|
5529
|
+
throw new Error(
|
|
5530
|
+
`${file} already exists and is not managed by agent-forge.
|
|
5531
|
+
Rename the agent in your registry or remove that file.`
|
|
5532
|
+
);
|
|
5533
|
+
}
|
|
5534
|
+
if (next !== current) (0, import_node_fs3.writeFileSync)(file, next);
|
|
5535
|
+
return { file, name };
|
|
5536
|
+
}
|
|
5537
|
+
(0, import_node_fs3.mkdirSync)(dir, { recursive: true });
|
|
5538
|
+
(0, import_node_fs3.writeFileSync)(file, next);
|
|
5539
|
+
return { file, name };
|
|
5502
5540
|
}
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5541
|
+
|
|
5542
|
+
// src/identity-prompt.js
|
|
5543
|
+
function buildIdentityPrompt({ name, email }, agentPrompt) {
|
|
5544
|
+
const base = [
|
|
5545
|
+
"You are operating under a GitHub App identity that agent-forge set up for this session; a human is driving it interactively.",
|
|
5546
|
+
"",
|
|
5547
|
+
`Your commits are authored as \`${name}\` <${email}> \u2014 already configured via GIT_AUTHOR_*/GIT_COMMITTER_*, do not change it.`,
|
|
5548
|
+
"A scoped, short-lived GitHub App installation token is available in the environment as GH_TOKEN and GITHUB_TOKEN; use it for `gh` and authenticated `git` operations."
|
|
5549
|
+
].join("\n");
|
|
5550
|
+
const own = agentPrompt?.trim();
|
|
5551
|
+
return own ? `${base}
|
|
5552
|
+
|
|
5553
|
+
${own}` : base;
|
|
5512
5554
|
}
|
|
5513
5555
|
|
|
5514
5556
|
// src/format.js
|
|
@@ -5534,7 +5576,7 @@ var import_node_util3 = require("node:util");
|
|
|
5534
5576
|
// package.json
|
|
5535
5577
|
var package_default = {
|
|
5536
5578
|
name: "@ashulab/agent-forge",
|
|
5537
|
-
version: "0.
|
|
5579
|
+
version: "0.5.0",
|
|
5538
5580
|
description: "Give claude/codex/antigravity their own GitHub identity: a bot account, a scoped token minted per run, git attribution to match",
|
|
5539
5581
|
license: "MIT",
|
|
5540
5582
|
author: "Diego Ghersi <ghersidl@gmail.com>",
|
|
@@ -5658,8 +5700,23 @@ async function addAgentWizard({ embedded = false } = {}) {
|
|
|
5658
5700
|
}
|
|
5659
5701
|
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
5702
|
note(where, "Installation");
|
|
5661
|
-
const name = (keep(await text({
|
|
5703
|
+
const name = (keep(await text({
|
|
5704
|
+
message: "Agent name",
|
|
5705
|
+
placeholder: meta.slug,
|
|
5706
|
+
defaultValue: meta.slug,
|
|
5707
|
+
validate: (v) => /^[a-z0-9][a-z0-9-]*$/.test(v.trim()) ? void 0 : "Lowercase letters, digits, hyphens"
|
|
5708
|
+
})) || meta.slug).trim();
|
|
5662
5709
|
const label = (keep(await text({ message: "Label", placeholder: meta.name, defaultValue: meta.name })) || meta.name).trim();
|
|
5710
|
+
note(
|
|
5711
|
+
[
|
|
5712
|
+
"agent-forge already tells every launched agent:",
|
|
5713
|
+
" \xB7 its bot git identity (GIT_AUTHOR_*/GIT_COMMITTER_*)",
|
|
5714
|
+
" \xB7 that a scoped GH_TOKEN / GITHUB_TOKEN is in the environment",
|
|
5715
|
+
"",
|
|
5716
|
+
"Add only what is specific to this agent \u2014 role, style, guardrails."
|
|
5717
|
+
].join("\n"),
|
|
5718
|
+
"Identity is handled for you"
|
|
5719
|
+
);
|
|
5663
5720
|
const systemPrompt = keep(await text({ message: "System prompt (optional)", placeholder: "You are ..." })).trim();
|
|
5664
5721
|
const agent = {
|
|
5665
5722
|
name,
|
|
@@ -5787,7 +5844,11 @@ async function launchAgent(agentName, providerName) {
|
|
|
5787
5844
|
const botId = await resolveBotId(agent);
|
|
5788
5845
|
const gitIdentity = buildGitIdentity(agent, botId);
|
|
5789
5846
|
s.stop(`Token ready for ${(0, import_node_util4.styleText)("green", gitIdentity.GIT_AUTHOR_NAME)}`);
|
|
5790
|
-
const
|
|
5847
|
+
const identityPrompt = buildIdentityPrompt(
|
|
5848
|
+
{ name: gitIdentity.GIT_AUTHOR_NAME, email: gitIdentity.GIT_AUTHOR_EMAIL },
|
|
5849
|
+
agent.systemPrompt || agent.instructions || agent.identityPrompt
|
|
5850
|
+
);
|
|
5851
|
+
const antigravityAgent = providerName === "antigravity" ? syncAntigravityAgent(agent, identityPrompt) : null;
|
|
5791
5852
|
const runtimeEnv = {
|
|
5792
5853
|
...process.env,
|
|
5793
5854
|
GITHUB_TOKEN: githubToken,
|
|
@@ -5808,11 +5869,11 @@ async function launchAgent(agentName, providerName) {
|
|
|
5808
5869
|
row("scope", (0, import_node_util4.styleText)("dim", repositorySelection === "selected" ? `${scopeText} \xB7 selected repos` : scopeText))
|
|
5809
5870
|
);
|
|
5810
5871
|
if (expiresAt) summary.push(row("expires", (0, import_node_util4.styleText)("dim", formatExpiry(expiresAt))));
|
|
5811
|
-
if (
|
|
5812
|
-
summary.push(row("
|
|
5872
|
+
if (antigravityAgent) {
|
|
5873
|
+
summary.push(row("agent", (0, import_node_util4.styleText)("dim", `--agent ${antigravityAgent.name} \xB7 ${antigravityAgent.file}`)));
|
|
5813
5874
|
}
|
|
5814
5875
|
note(summary.join("\n"), (0, import_node_util4.styleText)("bold", agent.label || agent.name));
|
|
5815
|
-
const providerArgs =
|
|
5876
|
+
const providerArgs = buildProviderArgs(providerName, identityPrompt, antigravityAgent);
|
|
5816
5877
|
const child = (0, import_node_child_process3.spawn)(providerInfo.command, providerArgs, {
|
|
5817
5878
|
stdio: "inherit",
|
|
5818
5879
|
env: runtimeEnv,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ashulab/agent-forge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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>",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"properties": {
|
|
16
16
|
"name": {
|
|
17
17
|
"type": "string",
|
|
18
|
-
"
|
|
18
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$",
|
|
19
|
+
"description": "Unique agent id, used with --agent. Lowercase letters, digits, hyphens (also the antigravity custom-agent id)."
|
|
19
20
|
},
|
|
20
21
|
"label": {
|
|
21
22
|
"type": "string",
|
|
@@ -43,15 +44,15 @@
|
|
|
43
44
|
},
|
|
44
45
|
"systemPrompt": {
|
|
45
46
|
"type": "string",
|
|
46
|
-
"description": "
|
|
47
|
+
"description": "Optional agent-specific system prompt, appended to the identity block agent-forge always injects. Per process: claude --append-system-prompt, codex developer_instructions, antigravity a global custom agent selected with --agent"
|
|
47
48
|
},
|
|
48
49
|
"instructions": {
|
|
49
50
|
"type": "string",
|
|
50
|
-
"description": "Fallback
|
|
51
|
+
"description": "Fallback for systemPrompt when it is absent; same identity-block + append behavior"
|
|
51
52
|
},
|
|
52
53
|
"identityPrompt": {
|
|
53
54
|
"type": "string",
|
|
54
|
-
"description": "Fallback
|
|
55
|
+
"description": "Fallback for systemPrompt and instructions when both are absent; same identity-block + append behavior"
|
|
55
56
|
},
|
|
56
57
|
"repositories": {
|
|
57
58
|
"type": "array",
|