@phnx-labs/agents-cli 1.20.58 → 1.20.59
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/CHANGELOG.md +10 -0
- package/README.md +7 -2
- package/dist/bin/agents +0 -0
- package/dist/commands/exec.js +1 -1
- package/dist/commands/output.d.ts +19 -0
- package/dist/commands/output.js +333 -0
- package/dist/commands/secrets.js +6 -6
- package/dist/index.js +2 -1
- package/dist/lib/agents.js +14 -10
- package/dist/lib/hosts/passthrough.js +1 -0
- package/dist/lib/mcp.js +1 -1
- package/dist/lib/output/git-output.d.ts +74 -0
- package/dist/lib/output/git-output.js +213 -0
- package/dist/lib/permissions.d.ts +12 -0
- package/dist/lib/permissions.js +73 -9
- package/dist/lib/project-root.js +2 -1
- package/dist/lib/resources/mcp.js +1 -1
- package/dist/lib/resources/permissions.js +3 -1
- package/dist/lib/resources/skills.js +6 -1
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/secrets/remote.d.ts +7 -2
- package/dist/lib/secrets/remote.js +11 -10
- package/dist/lib/session/db.d.ts +3 -0
- package/dist/lib/session/db.js +20 -4
- package/dist/lib/session/discover.d.ts +2 -0
- package/dist/lib/session/discover.js +40 -4
- package/dist/lib/session/types.d.ts +2 -0
- package/dist/lib/staleness/detectors/permissions.js +22 -1
- package/dist/lib/staleness/detectors/subagents.js +11 -11
- package/dist/lib/staleness/writers/commands.js +3 -3
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +2 -0
- package/dist/lib/subagents.d.ts +1 -0
- package/dist/lib/subagents.js +15 -12
- package/package.json +1 -1
|
@@ -508,6 +508,7 @@ async function readClaudeMeta(filePath, sessionId, account, label) {
|
|
|
508
508
|
label,
|
|
509
509
|
messageCount: scan.messageCount,
|
|
510
510
|
tokenCount: scan.tokenCount,
|
|
511
|
+
outputTokens: scan.outputTokens,
|
|
511
512
|
costUsd: scan.costUsd,
|
|
512
513
|
durationMs: scan.durationMs,
|
|
513
514
|
isTeamOrigin,
|
|
@@ -533,6 +534,7 @@ async function readClaudeMeta(filePath, sessionId, account, label) {
|
|
|
533
534
|
label,
|
|
534
535
|
messageCount: scan.messageCount,
|
|
535
536
|
tokenCount: scan.tokenCount,
|
|
537
|
+
outputTokens: scan.outputTokens,
|
|
536
538
|
costUsd: scan.costUsd,
|
|
537
539
|
durationMs: scan.durationMs,
|
|
538
540
|
topic: scan.topic,
|
|
@@ -748,6 +750,7 @@ export async function readCodexMeta(filePath, resolveAccount, currentVersion) {
|
|
|
748
750
|
topic: scan.topic,
|
|
749
751
|
messageCount: scan.messageCount,
|
|
750
752
|
tokenCount: scan.tokenCount,
|
|
753
|
+
outputTokens: scan.outputTokens,
|
|
751
754
|
costUsd: scan.costUsd,
|
|
752
755
|
durationMs: scan.durationMs,
|
|
753
756
|
account: resolveAccount?.(),
|
|
@@ -873,6 +876,7 @@ function readGeminiMeta(filePath, hashDir, projectMap, currentVersion) {
|
|
|
873
876
|
let topic;
|
|
874
877
|
let messageCount = 0;
|
|
875
878
|
let tokenCount = 0;
|
|
879
|
+
let outputTokens = 0;
|
|
876
880
|
let sawTokenCount = false;
|
|
877
881
|
let costUsd = 0;
|
|
878
882
|
let sawCost = false;
|
|
@@ -910,6 +914,16 @@ function readGeminiMeta(filePath, hashDir, projectMap, currentVersion) {
|
|
|
910
914
|
tokenCount += total;
|
|
911
915
|
sawTokenCount = true;
|
|
912
916
|
}
|
|
917
|
+
// Output tokens: sum directional generation fields per message (output +
|
|
918
|
+
// thoughts + tool), mirroring the cost path — never `tokens.total`, which
|
|
919
|
+
// may be cumulative and would double-count when summed.
|
|
920
|
+
const gtk = message.tokens;
|
|
921
|
+
if (gtk && typeof gtk === 'object') {
|
|
922
|
+
outputTokens +=
|
|
923
|
+
(typeof gtk.output === 'number' ? gtk.output : 0) +
|
|
924
|
+
(typeof gtk.thoughts === 'number' ? gtk.thoughts : 0) +
|
|
925
|
+
(typeof gtk.tool === 'number' ? gtk.tool : 0);
|
|
926
|
+
}
|
|
913
927
|
// Per-message cost: directional tokens × this message's model price.
|
|
914
928
|
const msgModel = (typeof message.model === 'string' ? message.model : undefined) || sessionModel;
|
|
915
929
|
const tk = message.tokens;
|
|
@@ -944,6 +958,7 @@ function readGeminiMeta(filePath, hashDir, projectMap, currentVersion) {
|
|
|
944
958
|
topic,
|
|
945
959
|
messageCount,
|
|
946
960
|
tokenCount: sawTokenCount ? tokenCount : undefined,
|
|
961
|
+
outputTokens: sawTokenCount ? outputTokens : undefined,
|
|
947
962
|
costUsd: sawCost ? costUsd : undefined,
|
|
948
963
|
durationMs,
|
|
949
964
|
};
|
|
@@ -1158,6 +1173,7 @@ async function scanOpenCodeIncremental() {
|
|
|
1158
1173
|
s.time_updated AS time_updated,
|
|
1159
1174
|
COALESCE(stats.message_count, 0) AS message_count,
|
|
1160
1175
|
stats.token_count AS token_count,
|
|
1176
|
+
stats.output_tokens AS output_tokens,
|
|
1161
1177
|
COALESCE(stats.has_token_data, 0) AS has_token_data
|
|
1162
1178
|
FROM session s
|
|
1163
1179
|
LEFT JOIN (
|
|
@@ -1171,6 +1187,7 @@ async function scanOpenCodeIncremental() {
|
|
|
1171
1187
|
COALESCE(json_extract(data, '$.tokens.cache.read'), 0) +
|
|
1172
1188
|
COALESCE(json_extract(data, '$.tokens.cache.write'), 0)
|
|
1173
1189
|
) AS token_count,
|
|
1190
|
+
SUM(COALESCE(json_extract(data, '$.tokens.output'), 0)) AS output_tokens,
|
|
1174
1191
|
MAX(CASE WHEN json_type(data, '$.tokens') IS NOT NULL THEN 1 ELSE 0 END) AS has_token_data
|
|
1175
1192
|
FROM message
|
|
1176
1193
|
GROUP BY session_id
|
|
@@ -1194,6 +1211,7 @@ async function scanOpenCodeIncremental() {
|
|
|
1194
1211
|
const timeUpdated = asInt(row.time_updated);
|
|
1195
1212
|
const messageCount = asInt(row.message_count);
|
|
1196
1213
|
const tokenCount = asInt(row.token_count);
|
|
1214
|
+
const outputTokens = asInt(row.output_tokens);
|
|
1197
1215
|
const hasTokenData = asInt(row.has_token_data) === 1;
|
|
1198
1216
|
const timestamp = isNaN(timeCreated) ? new Date().toISOString() : new Date(timeCreated).toISOString();
|
|
1199
1217
|
// OpenCode is one shared DB, not one file per session — its row carries a
|
|
@@ -1215,6 +1233,7 @@ async function scanOpenCodeIncremental() {
|
|
|
1215
1233
|
topic,
|
|
1216
1234
|
messageCount: Number.isNaN(messageCount) ? undefined : messageCount,
|
|
1217
1235
|
tokenCount: hasTokenData && !Number.isNaN(tokenCount) ? tokenCount : undefined,
|
|
1236
|
+
outputTokens: hasTokenData && !Number.isNaN(outputTokens) ? outputTokens : undefined,
|
|
1218
1237
|
};
|
|
1219
1238
|
entries.push({ meta, content: topic || '', scan: currentScan });
|
|
1220
1239
|
}
|
|
@@ -1653,6 +1672,7 @@ async function readDroidMeta(filePath, currentVersion) {
|
|
|
1653
1672
|
topic: scan.topic,
|
|
1654
1673
|
messageCount: scan.messageCount,
|
|
1655
1674
|
tokenCount,
|
|
1675
|
+
outputTokens: settings.usage?.outputTokens,
|
|
1656
1676
|
costUsd: costUsd > 0 ? costUsd : undefined,
|
|
1657
1677
|
durationMs: scan.durationMs,
|
|
1658
1678
|
};
|
|
@@ -1801,6 +1821,7 @@ export async function scanClaudeSession(filePath) {
|
|
|
1801
1821
|
let entrypoint;
|
|
1802
1822
|
let messageCount = 0;
|
|
1803
1823
|
let tokenCount = 0;
|
|
1824
|
+
let outputTokens = 0;
|
|
1804
1825
|
let sawTokenCount = false;
|
|
1805
1826
|
let costUsd = 0;
|
|
1806
1827
|
let sawCost = false;
|
|
@@ -1961,6 +1982,8 @@ export async function scanClaudeSession(filePath) {
|
|
|
1961
1982
|
tokenCount += usage;
|
|
1962
1983
|
sawTokenCount = true;
|
|
1963
1984
|
}
|
|
1985
|
+
if (typeof usageObj?.output_tokens === 'number')
|
|
1986
|
+
outputTokens += usageObj.output_tokens;
|
|
1964
1987
|
// Per-assistant-message cost: each event carries its own model, so we
|
|
1965
1988
|
// multiply that event's raw token directions by that model's price.
|
|
1966
1989
|
const model = parsed.message?.model;
|
|
@@ -2000,6 +2023,7 @@ export async function scanClaudeSession(filePath) {
|
|
|
2000
2023
|
entrypoint,
|
|
2001
2024
|
messageCount,
|
|
2002
2025
|
tokenCount: sawTokenCount ? tokenCount : undefined,
|
|
2026
|
+
outputTokens: sawTokenCount ? outputTokens : undefined,
|
|
2003
2027
|
costUsd: sawCost ? costUsd : undefined,
|
|
2004
2028
|
durationMs,
|
|
2005
2029
|
lastActivity: lastTsMs !== undefined ? new Date(lastTsMs).toISOString() : undefined,
|
|
@@ -2167,6 +2191,9 @@ async function scanCodexSession(filePath) {
|
|
|
2167
2191
|
topic,
|
|
2168
2192
|
messageCount,
|
|
2169
2193
|
tokenCount,
|
|
2194
|
+
outputTokens: lastTotalTokenUsage
|
|
2195
|
+
? (lastTotalTokenUsage.output_tokens ?? 0) + (lastTotalTokenUsage.reasoning_output_tokens ?? 0)
|
|
2196
|
+
: undefined,
|
|
2170
2197
|
costUsd,
|
|
2171
2198
|
durationMs,
|
|
2172
2199
|
lastActivity: lastTsMs !== undefined ? new Date(lastTsMs).toISOString() : undefined,
|
|
@@ -2500,7 +2527,7 @@ export function readKimiMeta(filePath) {
|
|
|
2500
2527
|
}
|
|
2501
2528
|
}
|
|
2502
2529
|
// Parse wire.jsonl to extract message count and token usage
|
|
2503
|
-
const { messageCount, tokenCount } = parseKimiWireMetrics(sessionDir);
|
|
2530
|
+
const { messageCount, tokenCount, outputTokens } = parseKimiWireMetrics(sessionDir);
|
|
2504
2531
|
const meta = {
|
|
2505
2532
|
id: sessionId,
|
|
2506
2533
|
shortId,
|
|
@@ -2511,6 +2538,7 @@ export function readKimiMeta(filePath) {
|
|
|
2511
2538
|
topic,
|
|
2512
2539
|
messageCount,
|
|
2513
2540
|
tokenCount: tokenCount > 0 ? tokenCount : undefined,
|
|
2541
|
+
outputTokens: outputTokens > 0 ? outputTokens : undefined,
|
|
2514
2542
|
};
|
|
2515
2543
|
return { meta, content: lastPrompt || '' };
|
|
2516
2544
|
}
|
|
@@ -2522,8 +2550,9 @@ function parseKimiWireMetrics(sessionDir) {
|
|
|
2522
2550
|
const wirePath = path.join(sessionDir, 'agents', 'main', 'wire.jsonl');
|
|
2523
2551
|
let messageCount = 0;
|
|
2524
2552
|
let tokenCount = 0;
|
|
2553
|
+
let outputTokens = 0;
|
|
2525
2554
|
if (!fs.existsSync(wirePath)) {
|
|
2526
|
-
return { messageCount: 0, tokenCount: 0 };
|
|
2555
|
+
return { messageCount: 0, tokenCount: 0, outputTokens: 0 };
|
|
2527
2556
|
}
|
|
2528
2557
|
try {
|
|
2529
2558
|
const lines = fs.readFileSync(wirePath, 'utf-8').split('\n');
|
|
@@ -2539,6 +2568,7 @@ function parseKimiWireMetrics(sessionDir) {
|
|
|
2539
2568
|
// Kimi usage structure: inputOther + output + inputCacheRead + inputCacheCreation
|
|
2540
2569
|
const u = event.usage;
|
|
2541
2570
|
tokenCount += (u.inputOther || 0) + (u.output || 0) + (u.inputCacheRead || 0) + (u.inputCacheCreation || 0);
|
|
2571
|
+
outputTokens += (u.output || 0);
|
|
2542
2572
|
}
|
|
2543
2573
|
}
|
|
2544
2574
|
catch {
|
|
@@ -2549,11 +2579,13 @@ function parseKimiWireMetrics(sessionDir) {
|
|
|
2549
2579
|
catch {
|
|
2550
2580
|
// If wire.jsonl can't be read, return 0s (graceful degradation)
|
|
2551
2581
|
}
|
|
2552
|
-
return { messageCount, tokenCount };
|
|
2582
|
+
return { messageCount, tokenCount, outputTokens };
|
|
2553
2583
|
}
|
|
2554
2584
|
/** Parse a time filter string (relative like '7d' or ISO timestamp) into epoch milliseconds. */
|
|
2555
2585
|
export function parseTimeFilter(input) {
|
|
2556
|
-
|
|
2586
|
+
// Units: m=minute, h=hour, d=day, w=week, mo=month(30d), y=year(365d). `mo`
|
|
2587
|
+
// must precede the single-letter alternatives so "1mo" isn't read as "1m"+"o".
|
|
2588
|
+
const relativeMatch = input.match(/^(\d+)(mo|[mhdwy])$/i);
|
|
2557
2589
|
if (relativeMatch) {
|
|
2558
2590
|
const value = parseInt(relativeMatch[1], 10);
|
|
2559
2591
|
const unit = relativeMatch[2].toLowerCase();
|
|
@@ -2565,6 +2597,10 @@ export function parseTimeFilter(input) {
|
|
|
2565
2597
|
return Date.now() - value * 86_400_000;
|
|
2566
2598
|
if (unit === 'w')
|
|
2567
2599
|
return Date.now() - value * 7 * 86_400_000;
|
|
2600
|
+
if (unit === 'mo')
|
|
2601
|
+
return Date.now() - value * 30 * 86_400_000;
|
|
2602
|
+
if (unit === 'y')
|
|
2603
|
+
return Date.now() - value * 365 * 86_400_000;
|
|
2568
2604
|
}
|
|
2569
2605
|
const ts = new Date(input).getTime();
|
|
2570
2606
|
return Number.isNaN(ts) ? 0 : ts;
|
|
@@ -66,6 +66,8 @@ export interface SessionMeta {
|
|
|
66
66
|
gitBranch?: string;
|
|
67
67
|
messageCount?: number;
|
|
68
68
|
tokenCount?: number;
|
|
69
|
+
/** Real generated (output) tokens — excludes cache-read/-write context (issue: `agents output`). */
|
|
70
|
+
outputTokens?: number;
|
|
69
71
|
/** Total USD cost, computed at scan time from per-model token usage (issue #323). */
|
|
70
72
|
costUsd?: number;
|
|
71
73
|
/** Wall-clock duration in ms (lastTs − firstTs), persisted at scan time. */
|
|
@@ -84,7 +84,7 @@ function buildOpenCodeDetector() {
|
|
|
84
84
|
kind: 'permissions',
|
|
85
85
|
agent: 'opencode',
|
|
86
86
|
list({ versionHome }) {
|
|
87
|
-
const opencodeConfigPath = path.join(versionHome, '.opencode', 'opencode.jsonc');
|
|
87
|
+
const opencodeConfigPath = path.join(versionHome, '.config', 'opencode', 'opencode.jsonc');
|
|
88
88
|
if (!fs.existsSync(opencodeConfigPath))
|
|
89
89
|
return [];
|
|
90
90
|
try {
|
|
@@ -202,6 +202,26 @@ function buildCursorDetector() {
|
|
|
202
202
|
},
|
|
203
203
|
};
|
|
204
204
|
}
|
|
205
|
+
function buildDroidDetector() {
|
|
206
|
+
return {
|
|
207
|
+
kind: 'permissions',
|
|
208
|
+
agent: 'droid',
|
|
209
|
+
list({ versionHome }) {
|
|
210
|
+
const settingsPath = path.join(versionHome, '.factory', 'settings.json');
|
|
211
|
+
if (!fs.existsSync(settingsPath))
|
|
212
|
+
return [];
|
|
213
|
+
try {
|
|
214
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
215
|
+
const hasAllow = Array.isArray(settings.commandAllowlist) && settings.commandAllowlist.length > 0;
|
|
216
|
+
const hasDeny = Array.isArray(settings.commandDenylist) && settings.commandDenylist.length > 0;
|
|
217
|
+
if (hasAllow || hasDeny)
|
|
218
|
+
return discoverPermissionGroups().map(g => g.name);
|
|
219
|
+
}
|
|
220
|
+
catch { /* parse fail */ }
|
|
221
|
+
return [];
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
}
|
|
205
225
|
function buildKiroDetector() {
|
|
206
226
|
return {
|
|
207
227
|
kind: 'permissions',
|
|
@@ -230,6 +250,7 @@ const handlers = {
|
|
|
230
250
|
grok: buildGrokDetector,
|
|
231
251
|
kimi: buildKimiDetector,
|
|
232
252
|
cursor: buildCursorDetector,
|
|
253
|
+
droid: buildDroidDetector,
|
|
233
254
|
kiro: buildKiroDetector,
|
|
234
255
|
};
|
|
235
256
|
export const permissionsDetectors = lazyAgentMap(() => {
|
|
@@ -85,32 +85,32 @@ function buildOpenclawDetector() {
|
|
|
85
85
|
},
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
|
-
function
|
|
88
|
+
function buildKimiDetector() {
|
|
89
89
|
return {
|
|
90
90
|
kind: 'subagents',
|
|
91
|
-
agent: '
|
|
91
|
+
agent: 'kimi',
|
|
92
92
|
list({ versionHome }) {
|
|
93
|
-
const agentsDir = path.join(versionHome, '.
|
|
93
|
+
const agentsDir = path.join(versionHome, '.kimi-code', 'agents');
|
|
94
94
|
if (!fs.existsSync(agentsDir))
|
|
95
95
|
return [];
|
|
96
|
+
// Parent is `_agents-cli.yaml` (underscore-prefixed reserved name).
|
|
96
97
|
return fs.readdirSync(agentsDir)
|
|
97
|
-
.filter(f => f.endsWith('.
|
|
98
|
-
.map(f => f.replace(
|
|
98
|
+
.filter(f => f.endsWith('.yaml') && !f.startsWith('_'))
|
|
99
|
+
.map(f => f.replace(/\.yaml$/, ''));
|
|
99
100
|
},
|
|
100
101
|
};
|
|
101
102
|
}
|
|
102
|
-
function
|
|
103
|
+
function buildKiroDetector() {
|
|
103
104
|
return {
|
|
104
105
|
kind: 'subagents',
|
|
105
|
-
agent: '
|
|
106
|
+
agent: 'kiro',
|
|
106
107
|
list({ versionHome }) {
|
|
107
|
-
const agentsDir = path.join(versionHome, '.
|
|
108
|
+
const agentsDir = path.join(versionHome, '.kiro', 'agents');
|
|
108
109
|
if (!fs.existsSync(agentsDir))
|
|
109
110
|
return [];
|
|
110
|
-
// Parent is `_agents-cli.yaml` (underscore-prefixed reserved name).
|
|
111
111
|
return fs.readdirSync(agentsDir)
|
|
112
|
-
.filter(f => f.endsWith('.
|
|
113
|
-
.map(f => f.replace(/\.
|
|
112
|
+
.filter(f => f.endsWith('.json'))
|
|
113
|
+
.map(f => f.replace(/\.json$/, ''));
|
|
114
114
|
},
|
|
115
115
|
};
|
|
116
116
|
}
|
|
@@ -36,10 +36,10 @@ function buildCommandsWriter(agent) {
|
|
|
36
36
|
const agentDir = path.join(versionHome, agentConfigDirName(agent));
|
|
37
37
|
const commandsAsSkills = shouldInstallCommandAsSkill(agent, version);
|
|
38
38
|
const supportsCommands = supports(agent, 'commands', version).ok;
|
|
39
|
-
//
|
|
40
|
-
//
|
|
39
|
+
// Version-gated agents (e.g. goose skills >= 1.25.0) are registered but
|
|
40
|
+
// may be called at a version too old for both paths — skip gracefully.
|
|
41
41
|
if (!commandsAsSkills && !supportsCommands) {
|
|
42
|
-
|
|
42
|
+
return { synced: [] };
|
|
43
43
|
}
|
|
44
44
|
const skillRoots = trustedSkillRoots();
|
|
45
45
|
const commandsTarget = path.join(agentDir, agentConfig.commandsSubdir);
|
|
@@ -65,6 +65,7 @@ export declare const loadDrive: ModuleLoader;
|
|
|
65
65
|
export declare const loadFactory: ModuleLoader;
|
|
66
66
|
export declare const loadUsage: ModuleLoader;
|
|
67
67
|
export declare const loadCost: ModuleLoader;
|
|
68
|
+
export declare const loadOutput: ModuleLoader;
|
|
68
69
|
export declare const loadBudget: ModuleLoader;
|
|
69
70
|
export declare const loadAlias: ModuleLoader;
|
|
70
71
|
export declare const loadPty: ModuleLoader;
|
|
@@ -43,6 +43,7 @@ export const loadDrive = async () => (await import('../../commands/drive.js')).r
|
|
|
43
43
|
export const loadFactory = async () => (await import('../../commands/factory.js')).registerFactoryCommands;
|
|
44
44
|
export const loadUsage = async () => (await import('../../commands/usage.js')).registerUsageCommand;
|
|
45
45
|
export const loadCost = async () => (await import('../../commands/cost.js')).registerCostCommand;
|
|
46
|
+
export const loadOutput = async () => (await import('../../commands/output.js')).registerOutputCommand;
|
|
46
47
|
export const loadBudget = async () => (await import('../../commands/budget.js')).registerBudgetCommand;
|
|
47
48
|
export const loadAlias = async () => (await import('../../commands/alias.js')).registerAliasCommand;
|
|
48
49
|
export const loadPty = async () => (await import('../../commands/pty.js')).registerPtyCommands;
|
|
@@ -137,6 +138,7 @@ export const COMMAND_LOADERS = {
|
|
|
137
138
|
factory: [loadFactory],
|
|
138
139
|
usage: [loadUsage],
|
|
139
140
|
cost: [loadCost],
|
|
141
|
+
output: [loadOutput],
|
|
140
142
|
budget: [loadBudget],
|
|
141
143
|
alias: [loadAlias],
|
|
142
144
|
pty: [loadPty],
|
package/dist/lib/subagents.d.ts
CHANGED
|
@@ -159,6 +159,7 @@ export declare function subagentContentMatches(installedDir: string, sourceDir:
|
|
|
159
159
|
* List subagents installed to a specific agent's home
|
|
160
160
|
* Claude: scans ~/.claude/agents/{name}.md
|
|
161
161
|
* Kimi: scans ~/.kimi-code/agents/{name}.yaml (+ sibling .system.md)
|
|
162
|
+
* Kiro: scans ~/.kiro/agents/{name}.json
|
|
162
163
|
* OpenClaw: scans ~/.openclaw/{name}/AGENTS.md
|
|
163
164
|
*/
|
|
164
165
|
export declare function listSubagentsForAgent(agentId: AgentId, home: string): InstalledSubagent[];
|
package/dist/lib/subagents.js
CHANGED
|
@@ -393,21 +393,10 @@ export function writeKimiSubagentFiles(agentsDir, subagentDir, name) {
|
|
|
393
393
|
export function transformSubagentForCodex(subagentDir) {
|
|
394
394
|
const agentMd = path.join(subagentDir, 'AGENT.md');
|
|
395
395
|
const frontmatter = parseSubagentFrontmatter(agentMd);
|
|
396
|
-
const body = getSubagentBody(agentMd);
|
|
397
396
|
if (!frontmatter) {
|
|
398
397
|
throw new Error(`Invalid AGENT.md in ${subagentDir}`);
|
|
399
398
|
}
|
|
400
|
-
|
|
401
|
-
let instructions = body.trim();
|
|
402
|
-
const files = fs.readdirSync(subagentDir)
|
|
403
|
-
.filter(f => f.endsWith('.md') && f !== 'AGENT.md')
|
|
404
|
-
.sort();
|
|
405
|
-
for (const file of files) {
|
|
406
|
-
const content = fs.readFileSync(path.join(subagentDir, file), 'utf-8').trim();
|
|
407
|
-
const sectionName = file.replace('.md', '');
|
|
408
|
-
const title = sectionName.charAt(0).toUpperCase() + sectionName.slice(1).toLowerCase();
|
|
409
|
-
instructions += `\n\n## ${title}\n\n${content}`;
|
|
410
|
-
}
|
|
399
|
+
const instructions = flattenSubagentInstructions(subagentDir);
|
|
411
400
|
// Escape TOML multi-line string (""") content — only """ needs escaping.
|
|
412
401
|
const safeInstructions = instructions.replace(/"""/g, '\\"""');
|
|
413
402
|
const safeName = frontmatter.name.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
@@ -421,6 +410,19 @@ export function transformSubagentForCodex(subagentDir) {
|
|
|
421
410
|
toml += `developer_instructions = """\n${safeInstructions}\n"""\n`;
|
|
422
411
|
return toml;
|
|
423
412
|
}
|
|
413
|
+
function flattenSubagentInstructions(subagentDir) {
|
|
414
|
+
let instructions = getSubagentBody(path.join(subagentDir, 'AGENT.md')).trim();
|
|
415
|
+
const files = fs.readdirSync(subagentDir)
|
|
416
|
+
.filter(f => f.endsWith('.md') && f !== 'AGENT.md')
|
|
417
|
+
.sort();
|
|
418
|
+
for (const file of files) {
|
|
419
|
+
const content = fs.readFileSync(path.join(subagentDir, file), 'utf-8').trim();
|
|
420
|
+
const sectionName = file.replace('.md', '');
|
|
421
|
+
const title = sectionName.charAt(0).toUpperCase() + sectionName.slice(1).toLowerCase();
|
|
422
|
+
instructions += `\n\n## ${title}\n\n${content}`;
|
|
423
|
+
}
|
|
424
|
+
return instructions;
|
|
425
|
+
}
|
|
424
426
|
/**
|
|
425
427
|
* Transform a subagent into a Kiro CLI custom-agent JSON file.
|
|
426
428
|
*
|
|
@@ -655,6 +657,7 @@ export function subagentContentMatches(installedDir, sourceDir) {
|
|
|
655
657
|
* List subagents installed to a specific agent's home
|
|
656
658
|
* Claude: scans ~/.claude/agents/{name}.md
|
|
657
659
|
* Kimi: scans ~/.kimi-code/agents/{name}.yaml (+ sibling .system.md)
|
|
660
|
+
* Kiro: scans ~/.kiro/agents/{name}.json
|
|
658
661
|
* OpenClaw: scans ~/.openclaw/{name}/AGENTS.md
|
|
659
662
|
*/
|
|
660
663
|
export function listSubagentsForAgent(agentId, home) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phnx-labs/agents-cli",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.59",
|
|
4
4
|
"description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|