@phnx-labs/agents-cli 1.20.57 → 1.20.58
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 +20 -0
- package/README.md +34 -3
- package/dist/bin/agents +0 -0
- package/dist/commands/defaults.js +24 -0
- package/dist/commands/exec.js +28 -4
- package/dist/commands/secrets.js +28 -19
- package/dist/commands/versions.js +11 -3
- package/dist/commands/view.js +19 -4
- package/dist/lib/agents.d.ts +21 -0
- package/dist/lib/agents.js +28 -4
- package/dist/lib/daemon.d.ts +5 -5
- package/dist/lib/daemon.js +65 -17
- package/dist/lib/git.d.ts +9 -0
- package/dist/lib/git.js +12 -0
- package/dist/lib/hosts/dispatch.d.ts +21 -0
- package/dist/lib/hosts/dispatch.js +88 -5
- package/dist/lib/permissions.d.ts +19 -1
- package/dist/lib/permissions.js +137 -0
- package/dist/lib/project-root.d.ts +65 -0
- package/dist/lib/project-root.js +133 -0
- package/dist/lib/resources/permissions.js +2 -0
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/secrets/agent.d.ts +26 -23
- package/dist/lib/secrets/agent.js +196 -216
- package/dist/lib/secrets/remote.js +1 -0
- package/dist/lib/session/active.d.ts +3 -0
- package/dist/lib/session/active.js +1 -0
- package/dist/lib/session/parse.js +38 -15
- package/dist/lib/session/state.d.ts +4 -1
- package/dist/lib/session/state.js +18 -1
- package/dist/lib/session/types.d.ts +8 -0
- package/dist/lib/staleness/detectors/permissions.js +42 -0
- package/dist/lib/staleness/detectors/subagents.js +30 -0
- package/dist/lib/staleness/writers/subagents.js +13 -1
- package/dist/lib/subagents.d.ts +22 -0
- package/dist/lib/subagents.js +146 -0
- package/dist/lib/teams/agents.d.ts +14 -0
- package/dist/lib/teams/agents.js +158 -22
- package/dist/lib/types.d.ts +13 -0
- package/dist/lib/versions.d.ts +39 -0
- package/dist/lib/versions.js +199 -12
- package/package.json +1 -1
- package/scripts/postinstall.js +26 -11
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* markers. Codex has no such tools, so it falls back to last-role + question
|
|
15
15
|
* shape + mtime — same function, driven off the normalized events.
|
|
16
16
|
*/
|
|
17
|
-
import type { SessionEvent } from './types.js';
|
|
17
|
+
import type { SessionAttachment, SessionEvent } from './types.js';
|
|
18
18
|
export type SessionActivity = 'working' | 'waiting_input' | 'idle';
|
|
19
19
|
export type AwaitingReason = 'question' | 'plan_review' | 'permission';
|
|
20
20
|
/** One discrete choice the agent offered the user. */
|
|
@@ -98,6 +98,8 @@ export interface SessionState {
|
|
|
98
98
|
createdTickets?: string[];
|
|
99
99
|
/** Team name this session SPAWNED via `agents teams create/add`. */
|
|
100
100
|
spawnedTeam?: string;
|
|
101
|
+
/** Displayable files/screenshots attached to the session prompt. */
|
|
102
|
+
attachments?: SessionAttachment[];
|
|
101
103
|
}
|
|
102
104
|
export interface StateContext {
|
|
103
105
|
/** Session file mtime; drives running-vs-stale. */
|
|
@@ -169,6 +171,7 @@ export declare function detectDurableSignals(events: SessionEvent[]): {
|
|
|
169
171
|
ticket?: DetectedTicket;
|
|
170
172
|
createdTickets?: string[];
|
|
171
173
|
spawnedTeam?: string;
|
|
174
|
+
attachments?: SessionAttachment[];
|
|
172
175
|
};
|
|
173
176
|
/** Full inference: activity + preview + durable signals + worktree/ticket from ctx. */
|
|
174
177
|
export declare function inferSessionState(events: SessionEvent[], ctx?: StateContext): SessionState;
|
|
@@ -352,6 +352,8 @@ export function detectDurableSignals(events) {
|
|
|
352
352
|
let sawTicketCreate = false;
|
|
353
353
|
let spawnedTeam;
|
|
354
354
|
const createdTickets = new Set();
|
|
355
|
+
const attachments = [];
|
|
356
|
+
const seenAttachments = new Set();
|
|
355
357
|
for (const e of events) {
|
|
356
358
|
// Structural PR signal: a real `gh pr create` tool call, then the pull URL
|
|
357
359
|
// from a following tool_result — never a bare URL mentioned in prose.
|
|
@@ -384,18 +386,32 @@ export function detectDurableSignals(events) {
|
|
|
384
386
|
if (!ticket && e.type === 'message' && e.role === 'user') {
|
|
385
387
|
ticket = detectTicket(e.content);
|
|
386
388
|
}
|
|
389
|
+
if (e.type === 'attachment') {
|
|
390
|
+
const mediaType = e.mediaType || 'application/octet-stream';
|
|
391
|
+
const key = e.path || e.name || `${mediaType}:${e.sizeBytes ?? 0}:${e.timestamp}`;
|
|
392
|
+
if (key && !seenAttachments.has(key)) {
|
|
393
|
+
seenAttachments.add(key);
|
|
394
|
+
attachments.push({
|
|
395
|
+
path: e.path,
|
|
396
|
+
name: e.name,
|
|
397
|
+
mediaType,
|
|
398
|
+
sizeBytes: e.sizeBytes,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
387
402
|
}
|
|
388
403
|
return {
|
|
389
404
|
pr,
|
|
390
405
|
ticket,
|
|
391
406
|
createdTickets: createdTickets.size > 0 ? [...createdTickets] : undefined,
|
|
392
407
|
spawnedTeam,
|
|
408
|
+
attachments: attachments.length > 0 ? attachments : undefined,
|
|
393
409
|
};
|
|
394
410
|
}
|
|
395
411
|
/** Full inference: activity + preview + durable signals + worktree/ticket from ctx. */
|
|
396
412
|
export function inferSessionState(events, ctx = {}) {
|
|
397
413
|
const state = inferActivity(events, ctx);
|
|
398
|
-
const { pr, ticket, createdTickets, spawnedTeam } = detectDurableSignals(events);
|
|
414
|
+
const { pr, ticket, createdTickets, spawnedTeam, attachments } = detectDurableSignals(events);
|
|
399
415
|
const worktree = detectWorktree(ctx.cwd, ctx.gitBranch);
|
|
400
416
|
// Rate-limit: scan the most recent assistant messages + tool errors (tail-first).
|
|
401
417
|
let rateLimited = false;
|
|
@@ -421,6 +437,7 @@ export function inferSessionState(events, ctx = {}) {
|
|
|
421
437
|
ticket: ticket ?? detectTicket(undefined, ctx.gitBranch) ?? state.ticket,
|
|
422
438
|
createdTickets,
|
|
423
439
|
spawnedTeam,
|
|
440
|
+
attachments,
|
|
424
441
|
rateLimited: rateLimited || undefined,
|
|
425
442
|
};
|
|
426
443
|
}
|
|
@@ -30,9 +30,17 @@ export interface SessionEvent {
|
|
|
30
30
|
outputTokens?: number;
|
|
31
31
|
cacheReadTokens?: number;
|
|
32
32
|
cacheCreationTokens?: number;
|
|
33
|
+
name?: string;
|
|
33
34
|
mediaType?: string;
|
|
34
35
|
sizeBytes?: number;
|
|
35
36
|
}
|
|
37
|
+
/** A displayable file attachment discovered in a session transcript. */
|
|
38
|
+
export interface SessionAttachment {
|
|
39
|
+
path?: string;
|
|
40
|
+
name?: string;
|
|
41
|
+
mediaType: string;
|
|
42
|
+
sizeBytes?: number;
|
|
43
|
+
}
|
|
36
44
|
/** Metadata attached when a session was spawned by `agents teams`. */
|
|
37
45
|
export interface TeamOrigin {
|
|
38
46
|
/** Teammate name if set, otherwise first 8 chars of the agent UUID. */
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import * as fs from 'fs';
|
|
14
14
|
import * as path from 'path';
|
|
15
15
|
import * as TOML from 'smol-toml';
|
|
16
|
+
import * as yaml from 'yaml';
|
|
16
17
|
import { capableAgents } from '../../capabilities.js';
|
|
17
18
|
import { discoverPermissionGroups, buildPermissionsFromGroups, CODEX_RULES_FILENAME, } from '../../permissions.js';
|
|
18
19
|
import { lazyAgentMap } from '../writers/lazy-map.js';
|
|
@@ -181,6 +182,45 @@ function buildKimiDetector() {
|
|
|
181
182
|
},
|
|
182
183
|
};
|
|
183
184
|
}
|
|
185
|
+
function buildCursorDetector() {
|
|
186
|
+
return {
|
|
187
|
+
kind: 'permissions',
|
|
188
|
+
agent: 'cursor',
|
|
189
|
+
list({ versionHome }) {
|
|
190
|
+
const configPath = path.join(versionHome, '.cursor', 'cli-config.json');
|
|
191
|
+
if (!fs.existsSync(configPath))
|
|
192
|
+
return [];
|
|
193
|
+
try {
|
|
194
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
195
|
+
const allow = config.permissions?.allow?.length ?? 0;
|
|
196
|
+
const deny = config.permissions?.deny?.length ?? 0;
|
|
197
|
+
if (allow + deny > 0)
|
|
198
|
+
return discoverPermissionGroups().map(g => g.name);
|
|
199
|
+
}
|
|
200
|
+
catch { /* parse fail */ }
|
|
201
|
+
return [];
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function buildKiroDetector() {
|
|
206
|
+
return {
|
|
207
|
+
kind: 'permissions',
|
|
208
|
+
agent: 'kiro',
|
|
209
|
+
list({ versionHome }) {
|
|
210
|
+
const permissionsPath = path.join(versionHome, '.kiro', 'settings', 'permissions.yaml');
|
|
211
|
+
if (!fs.existsSync(permissionsPath))
|
|
212
|
+
return [];
|
|
213
|
+
try {
|
|
214
|
+
const config = yaml.parse(fs.readFileSync(permissionsPath, 'utf-8'));
|
|
215
|
+
if (config && Array.isArray(config.rules) && config.rules.length > 0) {
|
|
216
|
+
return discoverPermissionGroups().map(g => g.name);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch { /* parse fail */ }
|
|
220
|
+
return [];
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
}
|
|
184
224
|
const handlers = {
|
|
185
225
|
claude: buildClaudeDetector,
|
|
186
226
|
codex: buildCodexDetector,
|
|
@@ -189,6 +229,8 @@ const handlers = {
|
|
|
189
229
|
antigravity: buildAntigravityDetector,
|
|
190
230
|
grok: buildGrokDetector,
|
|
191
231
|
kimi: buildKimiDetector,
|
|
232
|
+
cursor: buildCursorDetector,
|
|
233
|
+
kiro: buildKiroDetector,
|
|
192
234
|
};
|
|
193
235
|
export const permissionsDetectors = lazyAgentMap(() => {
|
|
194
236
|
const m = {};
|
|
@@ -57,6 +57,20 @@ function buildDroidDetector() {
|
|
|
57
57
|
},
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
+
function buildCopilotDetector() {
|
|
61
|
+
return {
|
|
62
|
+
kind: 'subagents',
|
|
63
|
+
agent: 'copilot',
|
|
64
|
+
list({ versionHome }) {
|
|
65
|
+
const agentsDir = path.join(versionHome, '.copilot', 'agents');
|
|
66
|
+
if (!fs.existsSync(agentsDir))
|
|
67
|
+
return [];
|
|
68
|
+
return fs.readdirSync(agentsDir)
|
|
69
|
+
.filter(f => f.endsWith('.agent.md'))
|
|
70
|
+
.map(f => f.replace('.agent.md', ''));
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
60
74
|
function buildOpenclawDetector() {
|
|
61
75
|
return {
|
|
62
76
|
kind: 'subagents',
|
|
@@ -71,6 +85,20 @@ function buildOpenclawDetector() {
|
|
|
71
85
|
},
|
|
72
86
|
};
|
|
73
87
|
}
|
|
88
|
+
function buildKiroDetector() {
|
|
89
|
+
return {
|
|
90
|
+
kind: 'subagents',
|
|
91
|
+
agent: 'kiro',
|
|
92
|
+
list({ versionHome }) {
|
|
93
|
+
const agentsDir = path.join(versionHome, '.kiro', 'agents');
|
|
94
|
+
if (!fs.existsSync(agentsDir))
|
|
95
|
+
return [];
|
|
96
|
+
return fs.readdirSync(agentsDir)
|
|
97
|
+
.filter(f => f.endsWith('.json'))
|
|
98
|
+
.map(f => f.replace('.json', ''));
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
74
102
|
function buildKimiDetector() {
|
|
75
103
|
return {
|
|
76
104
|
kind: 'subagents',
|
|
@@ -102,12 +130,14 @@ function buildOpenCodeDetector() {
|
|
|
102
130
|
}
|
|
103
131
|
const handlers = {
|
|
104
132
|
claude: buildClaudeDetector,
|
|
133
|
+
copilot: buildCopilotDetector,
|
|
105
134
|
grok: buildGrokDetector,
|
|
106
135
|
codex: buildCodexDetector,
|
|
107
136
|
kimi: buildKimiDetector,
|
|
108
137
|
opencode: buildOpenCodeDetector,
|
|
109
138
|
droid: buildDroidDetector,
|
|
110
139
|
openclaw: buildOpenclawDetector,
|
|
140
|
+
kiro: buildKiroDetector,
|
|
111
141
|
};
|
|
112
142
|
export const subagentsDetectors = lazyAgentMap(() => {
|
|
113
143
|
const m = {};
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import * as fs from 'fs';
|
|
14
14
|
import * as path from 'path';
|
|
15
15
|
import { capableAgents } from '../../capabilities.js';
|
|
16
|
-
import { listInstalledSubagents, transformSubagentForClaude, transformSubagentForCodex, writeKimiSubagentFiles, buildKimiSubagentsParentYaml, KIMI_SUBAGENTS_PARENT_FILE, transformSubagentForOpenCode, transformSubagentForDroid, syncSubagentToOpenclaw, parseSubagentFrontmatter, } from '../../subagents.js';
|
|
16
|
+
import { listInstalledSubagents, transformSubagentForClaude, transformSubagentForCodex, transformSubagentForCopilot, writeKimiSubagentFiles, buildKimiSubagentsParentYaml, KIMI_SUBAGENTS_PARENT_FILE, transformSubagentForOpenCode, transformSubagentForDroid, transformSubagentForKiro, syncSubagentToOpenclaw, parseSubagentFrontmatter, } from '../../subagents.js';
|
|
17
17
|
import { safeJoin } from '../../paths.js';
|
|
18
18
|
import { lazyAgentMap } from './lazy-map.js';
|
|
19
19
|
function buildSubagentsWriter(agent) {
|
|
@@ -57,12 +57,24 @@ function buildSubagentsWriter(agent) {
|
|
|
57
57
|
fs.writeFileSync(safeJoin(droidsDir, `${sub.name}.md`), transformSubagentForDroid(sub.path));
|
|
58
58
|
synced.push(sub.name);
|
|
59
59
|
}
|
|
60
|
+
else if (agent === 'copilot') {
|
|
61
|
+
const agentsDir = path.join(versionHome, '.copilot', 'agents');
|
|
62
|
+
fs.mkdirSync(agentsDir, { recursive: true });
|
|
63
|
+
fs.writeFileSync(safeJoin(agentsDir, `${sub.name}.agent.md`), transformSubagentForCopilot(sub.path));
|
|
64
|
+
synced.push(sub.name);
|
|
65
|
+
}
|
|
60
66
|
else if (agent === 'openclaw') {
|
|
61
67
|
const target = safeJoin(path.join(versionHome, '.openclaw'), sub.name);
|
|
62
68
|
const r = syncSubagentToOpenclaw(sub.path, target);
|
|
63
69
|
if (r.success)
|
|
64
70
|
synced.push(sub.name);
|
|
65
71
|
}
|
|
72
|
+
else if (agent === 'kiro') {
|
|
73
|
+
const agentsDir = path.join(versionHome, '.kiro', 'agents');
|
|
74
|
+
fs.mkdirSync(agentsDir, { recursive: true });
|
|
75
|
+
fs.writeFileSync(safeJoin(agentsDir, `${sub.name}.json`), transformSubagentForKiro(sub.path));
|
|
76
|
+
synced.push(sub.name);
|
|
77
|
+
}
|
|
66
78
|
}
|
|
67
79
|
catch { /* per-item sync failure: skip */ }
|
|
68
80
|
}
|
package/dist/lib/subagents.d.ts
CHANGED
|
@@ -56,6 +56,18 @@ export declare function transformSubagentForClaude(subagentDir: string): string;
|
|
|
56
56
|
* See https://docs.factory.ai/cli/configuration/custom-droids.
|
|
57
57
|
*/
|
|
58
58
|
export declare function transformSubagentForDroid(subagentDir: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* Transform a subagent into a GitHub Copilot CLI custom agent `.agent.md` file.
|
|
61
|
+
*
|
|
62
|
+
* Copilot custom agents are Markdown profiles with YAML frontmatter stored in
|
|
63
|
+
* `~/.copilot/agents/` (user) or `.github/agents/` (project). The file name
|
|
64
|
+
* ends in `.agent.md` and the frontmatter carries `name`, `description`, and
|
|
65
|
+
* optionally `model` and `tools`. The emitted body is identical to Factory
|
|
66
|
+
* Droid's custom-droid format (flatten frontmatter + body + appended .md
|
|
67
|
+
* sections, `color` dropped), so this is an alias of transformSubagentForDroid.
|
|
68
|
+
* See GitHub docs for custom agents.
|
|
69
|
+
*/
|
|
70
|
+
export declare const transformSubagentForCopilot: typeof transformSubagentForDroid;
|
|
59
71
|
/**
|
|
60
72
|
* Transform a subagent into an OpenCode agent markdown file.
|
|
61
73
|
*
|
|
@@ -107,6 +119,16 @@ export declare function writeKimiSubagentFiles(agentsDir: string, subagentDir: s
|
|
|
107
119
|
* https://developers.openai.com/codex/subagents (custom agents section)
|
|
108
120
|
*/
|
|
109
121
|
export declare function transformSubagentForCodex(subagentDir: string): string;
|
|
122
|
+
/**
|
|
123
|
+
* Transform a subagent into a Kiro CLI custom-agent JSON file.
|
|
124
|
+
*
|
|
125
|
+
* Kiro custom agents live in `~/.kiro/agents/<name>.json` (or `.kiro/agents/`
|
|
126
|
+
* workspace-local) and declare name, description, prompt, tools, and optional
|
|
127
|
+
* model. We flatten the AGENT.md frontmatter + body plus any sibling .md files
|
|
128
|
+
* as sections into a single `prompt`, and expose the standard built-in tool
|
|
129
|
+
* set so the subagent can actually run.
|
|
130
|
+
*/
|
|
131
|
+
export declare function transformSubagentForKiro(subagentDir: string): string;
|
|
110
132
|
/**
|
|
111
133
|
* Sync a subagent to an OpenClaw workspace
|
|
112
134
|
* Copies full directory, renames AGENT.md to AGENTS.md
|
package/dist/lib/subagents.js
CHANGED
|
@@ -262,6 +262,18 @@ export function transformSubagentForDroid(subagentDir) {
|
|
|
262
262
|
}
|
|
263
263
|
return result;
|
|
264
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* Transform a subagent into a GitHub Copilot CLI custom agent `.agent.md` file.
|
|
267
|
+
*
|
|
268
|
+
* Copilot custom agents are Markdown profiles with YAML frontmatter stored in
|
|
269
|
+
* `~/.copilot/agents/` (user) or `.github/agents/` (project). The file name
|
|
270
|
+
* ends in `.agent.md` and the frontmatter carries `name`, `description`, and
|
|
271
|
+
* optionally `model` and `tools`. The emitted body is identical to Factory
|
|
272
|
+
* Droid's custom-droid format (flatten frontmatter + body + appended .md
|
|
273
|
+
* sections, `color` dropped), so this is an alias of transformSubagentForDroid.
|
|
274
|
+
* See GitHub docs for custom agents.
|
|
275
|
+
*/
|
|
276
|
+
export const transformSubagentForCopilot = transformSubagentForDroid;
|
|
265
277
|
/**
|
|
266
278
|
* Transform a subagent into an OpenCode agent markdown file.
|
|
267
279
|
*
|
|
@@ -409,6 +421,43 @@ export function transformSubagentForCodex(subagentDir) {
|
|
|
409
421
|
toml += `developer_instructions = """\n${safeInstructions}\n"""\n`;
|
|
410
422
|
return toml;
|
|
411
423
|
}
|
|
424
|
+
/**
|
|
425
|
+
* Transform a subagent into a Kiro CLI custom-agent JSON file.
|
|
426
|
+
*
|
|
427
|
+
* Kiro custom agents live in `~/.kiro/agents/<name>.json` (or `.kiro/agents/`
|
|
428
|
+
* workspace-local) and declare name, description, prompt, tools, and optional
|
|
429
|
+
* model. We flatten the AGENT.md frontmatter + body plus any sibling .md files
|
|
430
|
+
* as sections into a single `prompt`, and expose the standard built-in tool
|
|
431
|
+
* set so the subagent can actually run.
|
|
432
|
+
*/
|
|
433
|
+
export function transformSubagentForKiro(subagentDir) {
|
|
434
|
+
const agentMd = path.join(subagentDir, 'AGENT.md');
|
|
435
|
+
const frontmatter = parseSubagentFrontmatter(agentMd);
|
|
436
|
+
const body = getSubagentBody(agentMd);
|
|
437
|
+
if (!frontmatter) {
|
|
438
|
+
throw new Error(`Invalid AGENT.md in ${subagentDir}`);
|
|
439
|
+
}
|
|
440
|
+
const files = fs.readdirSync(subagentDir)
|
|
441
|
+
.filter(f => f.endsWith('.md') && f !== 'AGENT.md')
|
|
442
|
+
.sort();
|
|
443
|
+
let prompt = body;
|
|
444
|
+
for (const file of files) {
|
|
445
|
+
const content = fs.readFileSync(path.join(subagentDir, file), 'utf-8').trim();
|
|
446
|
+
const sectionName = file.replace('.md', '');
|
|
447
|
+
const title = sectionName.charAt(0).toUpperCase() + sectionName.slice(1).toLowerCase();
|
|
448
|
+
prompt += `\n\n## ${title}\n\n${content}`;
|
|
449
|
+
}
|
|
450
|
+
const config = {
|
|
451
|
+
name: frontmatter.name,
|
|
452
|
+
description: frontmatter.description,
|
|
453
|
+
prompt,
|
|
454
|
+
tools: ['read', 'write', 'shell', 'web_search', 'web_fetch'],
|
|
455
|
+
};
|
|
456
|
+
if (frontmatter.model) {
|
|
457
|
+
config.model = frontmatter.model;
|
|
458
|
+
}
|
|
459
|
+
return JSON.stringify(config, null, 2);
|
|
460
|
+
}
|
|
412
461
|
/**
|
|
413
462
|
* Sync a subagent to an OpenClaw workspace
|
|
414
463
|
* Copies full directory, renames AGENT.md to AGENTS.md
|
|
@@ -497,6 +546,21 @@ export function installSubagentToAgent(subagentDir, subagentName, agent, agentHo
|
|
|
497
546
|
const targetDir = safeJoin(path.join(agentHome, '.openclaw'), subagentName);
|
|
498
547
|
return syncSubagentToOpenclaw(subagentDir, targetDir);
|
|
499
548
|
}
|
|
549
|
+
else if (agent === 'kiro') {
|
|
550
|
+
// Kiro: JSON custom-agent file under ~/.kiro/agents/
|
|
551
|
+
const agentsDir = path.join(agentHome, '.kiro', 'agents');
|
|
552
|
+
if (!fs.existsSync(agentsDir)) {
|
|
553
|
+
fs.mkdirSync(agentsDir, { recursive: true });
|
|
554
|
+
}
|
|
555
|
+
try {
|
|
556
|
+
const transformed = transformSubagentForKiro(subagentDir);
|
|
557
|
+
fs.writeFileSync(safeJoin(agentsDir, `${subagentName}.json`), transformed);
|
|
558
|
+
return { success: true };
|
|
559
|
+
}
|
|
560
|
+
catch (err) {
|
|
561
|
+
return { success: false, error: String(err) };
|
|
562
|
+
}
|
|
563
|
+
}
|
|
500
564
|
else {
|
|
501
565
|
// Other agents don't support subagents yet
|
|
502
566
|
return { success: false, error: `Agent '${agent}' does not support subagents` };
|
|
@@ -545,6 +609,13 @@ export function removeSubagentFromAgent(subagentName, agent, agentHome) {
|
|
|
545
609
|
}
|
|
546
610
|
return { success: true };
|
|
547
611
|
}
|
|
612
|
+
else if (agent === 'kiro') {
|
|
613
|
+
const targetPath = safeJoin(path.join(agentHome, '.kiro', 'agents'), `${subagentName}.json`);
|
|
614
|
+
if (fs.existsSync(targetPath)) {
|
|
615
|
+
fs.unlinkSync(targetPath);
|
|
616
|
+
}
|
|
617
|
+
return { success: true };
|
|
618
|
+
}
|
|
548
619
|
else {
|
|
549
620
|
return { success: true }; // No-op for unsupported agents
|
|
550
621
|
}
|
|
@@ -656,6 +727,22 @@ export function listSubagentsForAgent(agentId, home) {
|
|
|
656
727
|
subagents.push({ name, path: filePath, files: [file], frontmatter });
|
|
657
728
|
}
|
|
658
729
|
}
|
|
730
|
+
else if (agentId === 'copilot') {
|
|
731
|
+
// Copilot: flat `<name>.agent.md` files under ~/.copilot/agents/
|
|
732
|
+
const agentsDir = path.join(home, '.copilot', 'agents');
|
|
733
|
+
if (!fs.existsSync(agentsDir))
|
|
734
|
+
return subagents;
|
|
735
|
+
for (const file of fs.readdirSync(agentsDir)) {
|
|
736
|
+
if (!file.endsWith('.agent.md'))
|
|
737
|
+
continue;
|
|
738
|
+
const filePath = path.join(agentsDir, file);
|
|
739
|
+
if (!fs.statSync(filePath).isFile())
|
|
740
|
+
continue;
|
|
741
|
+
const name = file.replace(/\.agent\.md$/, '');
|
|
742
|
+
const frontmatter = parseSubagentFrontmatter(filePath) ?? { name, description: '' };
|
|
743
|
+
subagents.push({ name, path: filePath, files: [file], frontmatter });
|
|
744
|
+
}
|
|
745
|
+
}
|
|
659
746
|
else if (agentId === 'openclaw') {
|
|
660
747
|
// OpenClaw: directories with AGENTS.md
|
|
661
748
|
const openclawDir = path.join(home, '.openclaw');
|
|
@@ -697,6 +784,38 @@ export function listSubagentsForAgent(agentId, home) {
|
|
|
697
784
|
});
|
|
698
785
|
}
|
|
699
786
|
}
|
|
787
|
+
else if (agentId === 'kiro') {
|
|
788
|
+
// Kiro: JSON files under ~/.kiro/agents/
|
|
789
|
+
const agentsDir = path.join(home, '.kiro', 'agents');
|
|
790
|
+
if (!fs.existsSync(agentsDir))
|
|
791
|
+
return subagents;
|
|
792
|
+
for (const file of fs.readdirSync(agentsDir)) {
|
|
793
|
+
if (!file.endsWith('.json'))
|
|
794
|
+
continue;
|
|
795
|
+
const filePath = path.join(agentsDir, file);
|
|
796
|
+
if (!fs.statSync(filePath).isFile())
|
|
797
|
+
continue;
|
|
798
|
+
let frontmatter;
|
|
799
|
+
try {
|
|
800
|
+
const config = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
801
|
+
frontmatter = {
|
|
802
|
+
name: config.name || file.replace('.json', ''),
|
|
803
|
+
description: config.description || '',
|
|
804
|
+
model: config.model,
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
catch {
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
const name = file.replace('.json', '');
|
|
811
|
+
subagents.push({
|
|
812
|
+
name,
|
|
813
|
+
path: filePath,
|
|
814
|
+
files: [file],
|
|
815
|
+
frontmatter,
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
}
|
|
700
819
|
return subagents;
|
|
701
820
|
}
|
|
702
821
|
/**
|
|
@@ -768,6 +887,19 @@ export function diffVersionSubagents(agent, version) {
|
|
|
768
887
|
}
|
|
769
888
|
}
|
|
770
889
|
}
|
|
890
|
+
else if (agent === 'kiro') {
|
|
891
|
+
const agentsDir = path.join(versionHome, '.kiro', 'agents');
|
|
892
|
+
if (fs.existsSync(agentsDir)) {
|
|
893
|
+
for (const file of fs.readdirSync(agentsDir)) {
|
|
894
|
+
if (!file.endsWith('.json'))
|
|
895
|
+
continue;
|
|
896
|
+
const name = path.basename(file, '.json');
|
|
897
|
+
if (!discovered.has(name)) {
|
|
898
|
+
orphans.push(name);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
771
903
|
return { agent, version, orphans: orphans.sort() };
|
|
772
904
|
}
|
|
773
905
|
/**
|
|
@@ -826,6 +958,13 @@ export function removeSubagentFromVersion(agent, version, subagentName) {
|
|
|
826
958
|
fs.renameSync(targetPath, path.join(trashDir, `${subagentName}.md.${stamp}`));
|
|
827
959
|
}
|
|
828
960
|
}
|
|
961
|
+
else if (agent === 'copilot') {
|
|
962
|
+
const targetPath = path.join(versionHome, '.copilot', 'agents', `${subagentName}.agent.md`);
|
|
963
|
+
if (fs.existsSync(targetPath)) {
|
|
964
|
+
fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
|
|
965
|
+
fs.renameSync(targetPath, path.join(trashDir, `${subagentName}.agent.md.${stamp}`));
|
|
966
|
+
}
|
|
967
|
+
}
|
|
829
968
|
else if (agent === 'openclaw') {
|
|
830
969
|
const targetDir = path.join(versionHome, '.openclaw', subagentName);
|
|
831
970
|
if (fs.existsSync(targetDir)) {
|
|
@@ -834,6 +973,13 @@ export function removeSubagentFromVersion(agent, version, subagentName) {
|
|
|
834
973
|
fs.renameSync(targetDir, trashDest);
|
|
835
974
|
}
|
|
836
975
|
}
|
|
976
|
+
else if (agent === 'kiro') {
|
|
977
|
+
const targetPath = path.join(versionHome, '.kiro', 'agents', `${subagentName}.json`);
|
|
978
|
+
if (fs.existsSync(targetPath)) {
|
|
979
|
+
fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
|
|
980
|
+
fs.renameSync(targetPath, path.join(trashDir, `${subagentName}.json.${stamp}`));
|
|
981
|
+
}
|
|
982
|
+
}
|
|
837
983
|
return { success: true };
|
|
838
984
|
}
|
|
839
985
|
catch (err) {
|
|
@@ -213,6 +213,10 @@ export declare class AgentProcess {
|
|
|
213
213
|
* round-trips so a bare `teams status`/`teams logs` is still correct.
|
|
214
214
|
*/
|
|
215
215
|
private syncRemoteMirror;
|
|
216
|
+
/** Reset the local stdout cursor for a newly truncated resume log. */
|
|
217
|
+
resetLogReadPosition(): number;
|
|
218
|
+
/** Restore the cursor when a resume transaction puts the prior log back. */
|
|
219
|
+
restoreLogReadPosition(position: number): void;
|
|
216
220
|
readNewEvents(): Promise<void>;
|
|
217
221
|
/**
|
|
218
222
|
* Truncate the local mirror to its trailing REMOTE_MIRROR_MAX_BYTES and reset
|
|
@@ -268,6 +272,16 @@ export declare class AgentProcess {
|
|
|
268
272
|
export type CloudDispatchFn = (agent: AgentProcess) => Promise<{
|
|
269
273
|
cloudSessionId: string;
|
|
270
274
|
}>;
|
|
275
|
+
interface ResumeLogTransaction {
|
|
276
|
+
agent: AgentProcess;
|
|
277
|
+
stdoutPath: string;
|
|
278
|
+
backupPath: string;
|
|
279
|
+
hadOriginal: boolean;
|
|
280
|
+
previousReadPos: number;
|
|
281
|
+
}
|
|
282
|
+
export declare function beginResumeLogTransaction(agent: AgentProcess): Promise<ResumeLogTransaction>;
|
|
283
|
+
export declare function commitResumeLogTransaction(transaction: ResumeLogTransaction): Promise<void>;
|
|
284
|
+
export declare function terminateSpawnedProcess(pid: number): Promise<void>;
|
|
271
285
|
export declare class AgentManager {
|
|
272
286
|
private agents;
|
|
273
287
|
private maxAgents;
|