@phnx-labs/agents-cli 1.20.35 → 1.20.36
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 +91 -0
- package/LICENSE +185 -21
- package/README.md +8 -4
- package/dist/commands/audit.d.ts +14 -0
- package/dist/commands/audit.js +68 -0
- package/dist/commands/browser.js +82 -8
- package/dist/commands/check.d.ts +15 -0
- package/dist/commands/check.js +84 -0
- package/dist/commands/cloud.js +143 -3
- package/dist/commands/computer.js +81 -0
- package/dist/commands/daemon.js +4 -1
- package/dist/commands/doctor.js +1 -89
- package/dist/commands/events.js +3 -3
- package/dist/commands/exec.d.ts +19 -0
- package/dist/commands/exec.js +277 -11
- package/dist/commands/hosts.js +10 -6
- package/dist/commands/inspect.js +8 -9
- package/dist/commands/lock.d.ts +12 -0
- package/dist/commands/lock.js +70 -0
- package/dist/commands/message.d.ts +15 -0
- package/dist/commands/message.js +56 -0
- package/dist/commands/routines.js +101 -5
- package/dist/commands/secrets-migrate.js +106 -57
- package/dist/commands/secrets.d.ts +31 -18
- package/dist/commands/secrets.js +156 -75
- package/dist/commands/serve.d.ts +10 -0
- package/dist/commands/serve.js +37 -0
- package/dist/commands/sessions-inject.d.ts +14 -0
- package/dist/commands/sessions-inject.js +111 -0
- package/dist/commands/sessions-picker.d.ts +2 -0
- package/dist/commands/sessions-picker.js +24 -3
- package/dist/commands/sessions-resume.js +20 -8
- package/dist/commands/sessions.d.ts +71 -1
- package/dist/commands/sessions.js +365 -37
- package/dist/commands/setup.js +4 -2
- package/dist/commands/sync.d.ts +3 -1
- package/dist/commands/sync.js +156 -4
- package/dist/commands/teams.js +217 -0
- package/dist/commands/versions.js +2 -4
- package/dist/commands/watchdog.d.ts +18 -0
- package/dist/commands/watchdog.js +238 -0
- package/dist/index.js +25 -2
- package/dist/lib/audit/log.d.ts +92 -0
- package/dist/lib/audit/log.js +177 -0
- package/dist/lib/auto-pull.js +2 -1
- package/dist/lib/browser/chrome.d.ts +10 -0
- package/dist/lib/browser/chrome.js +18 -7
- package/dist/lib/browser/drivers/ssh.js +2 -1
- package/dist/lib/browser/har.d.ts +84 -0
- package/dist/lib/browser/har.js +77 -0
- package/dist/lib/browser/ipc.js +24 -3
- package/dist/lib/browser/profiles.d.ts +1 -1
- package/dist/lib/browser/profiles.js +8 -10
- package/dist/lib/browser/refs.d.ts +65 -0
- package/dist/lib/browser/refs.js +73 -1
- package/dist/lib/browser/runtime-state.js +1 -0
- package/dist/lib/browser/service.d.ts +38 -2
- package/dist/lib/browser/service.js +112 -8
- package/dist/lib/browser/types.d.ts +14 -1
- package/dist/lib/budget/live-cloud.d.ts +42 -0
- package/dist/lib/budget/live-cloud.js +79 -0
- package/dist/lib/budget/live-team.d.ts +31 -0
- package/dist/lib/budget/live-team.js +115 -0
- package/dist/lib/cloud/codex.js +4 -0
- package/dist/lib/cloud/rush.d.ts +12 -1
- package/dist/lib/cloud/rush.js +13 -3
- package/dist/lib/cloud/types.d.ts +9 -0
- package/dist/lib/computer/dispatch.d.ts +8 -0
- package/dist/lib/computer/dispatch.js +125 -0
- package/dist/lib/computer/loop.d.ts +62 -0
- package/dist/lib/computer/loop.js +98 -0
- package/dist/lib/computer/model.d.ts +44 -0
- package/dist/lib/computer/model.js +157 -0
- package/dist/lib/concurrency.d.ts +19 -0
- package/dist/lib/concurrency.js +33 -0
- package/dist/lib/daemon.d.ts +57 -0
- package/dist/lib/daemon.js +192 -16
- package/dist/lib/devices/registry.d.ts +7 -0
- package/dist/lib/devices/registry.js +24 -0
- package/dist/lib/devices/tailscale.js +1 -1
- package/dist/lib/drift.d.ts +52 -0
- package/dist/lib/drift.js +112 -0
- package/dist/lib/events.d.ts +1 -1
- package/dist/lib/events.js +31 -13
- package/dist/lib/exec.d.ts +17 -0
- package/dist/lib/exec.js +79 -13
- package/dist/lib/git.d.ts +27 -0
- package/dist/lib/git.js +56 -1
- package/dist/lib/hooks/cache.d.ts +6 -0
- package/dist/lib/hooks/cache.js +54 -12
- package/dist/lib/hooks.d.ts +27 -0
- package/dist/lib/hooks.js +127 -8
- package/dist/lib/hosts/dispatch.d.ts +15 -0
- package/dist/lib/hosts/dispatch.js +39 -6
- package/dist/lib/hosts/logs.js +30 -1
- package/dist/lib/hosts/option.js +1 -1
- package/dist/lib/hosts/passthrough.js +3 -1
- package/dist/lib/hosts/ready.d.ts +29 -6
- package/dist/lib/hosts/ready.js +66 -15
- package/dist/lib/hosts/registry.d.ts +19 -2
- package/dist/lib/hosts/registry.js +58 -2
- package/dist/lib/hosts/remote-cmd.d.ts +62 -1
- package/dist/lib/hosts/remote-cmd.js +70 -1
- package/dist/lib/hosts/remote-os.d.ts +17 -0
- package/dist/lib/hosts/remote-os.js +30 -0
- package/dist/lib/hosts/session-index.d.ts +34 -0
- package/dist/lib/hosts/session-index.js +56 -0
- package/dist/lib/hosts/tasks.d.ts +14 -0
- package/dist/lib/hosts/tasks.js +15 -0
- package/dist/lib/lock.d.ts +93 -0
- package/dist/lib/lock.js +207 -0
- package/dist/lib/loop.js +16 -1
- package/dist/lib/machine-id.d.ts +21 -0
- package/dist/lib/machine-id.js +26 -0
- package/dist/lib/mailbox-target.d.ts +36 -0
- package/dist/lib/mailbox-target.js +45 -0
- package/dist/lib/mailbox.d.ts +47 -0
- package/dist/lib/mailbox.js +194 -0
- package/dist/lib/mcp.d.ts +5 -0
- package/dist/lib/mcp.js +24 -8
- package/dist/lib/migrate.d.ts +19 -0
- package/dist/lib/migrate.js +134 -26
- package/dist/lib/overdue.js +3 -0
- package/dist/lib/picker.d.ts +2 -0
- package/dist/lib/picker.js +4 -1
- package/dist/lib/platform/exec.d.ts +46 -0
- package/dist/lib/platform/exec.js +74 -0
- package/dist/lib/platform/process.d.ts +31 -0
- package/dist/lib/platform/process.js +34 -1
- package/dist/lib/platform/winpath.js +2 -0
- package/dist/lib/plugins.js +16 -6
- package/dist/lib/profiles.d.ts +25 -0
- package/dist/lib/profiles.js +22 -6
- package/dist/lib/pty-client.js +2 -1
- package/dist/lib/rotate.d.ts +61 -0
- package/dist/lib/rotate.js +52 -0
- package/dist/lib/routines.d.ts +40 -2
- package/dist/lib/routines.js +66 -8
- package/dist/lib/runner.d.ts +11 -2
- package/dist/lib/runner.js +49 -7
- package/dist/lib/scheduler.js +6 -1
- package/dist/lib/secrets/bundles.d.ts +60 -4
- package/dist/lib/secrets/bundles.js +131 -12
- package/dist/lib/secrets/filestore.d.ts +3 -0
- package/dist/lib/secrets/filestore.js +42 -16
- package/dist/lib/secrets/index.d.ts +43 -2
- package/dist/lib/secrets/index.js +102 -3
- package/dist/lib/secrets/mcp.d.ts +93 -0
- package/dist/lib/secrets/mcp.js +205 -0
- package/dist/lib/secrets/remote.js +12 -5
- package/dist/lib/secrets/sync.js +83 -4
- package/dist/lib/secrets/windows.js +14 -3
- package/dist/lib/serve/data.d.ts +81 -0
- package/dist/lib/serve/data.js +91 -0
- package/dist/lib/serve/page.d.ts +7 -0
- package/dist/lib/serve/page.js +140 -0
- package/dist/lib/serve/server.d.ts +46 -0
- package/dist/lib/serve/server.js +115 -0
- package/dist/lib/session/active.d.ts +54 -0
- package/dist/lib/session/active.js +190 -19
- package/dist/lib/session/discover.d.ts +37 -0
- package/dist/lib/session/discover.js +111 -28
- package/dist/lib/session/inject.d.ts +18 -0
- package/dist/lib/session/inject.js +21 -0
- package/dist/lib/session/parse.js +23 -20
- package/dist/lib/session/pid-registry.d.ts +1 -0
- package/dist/lib/session/pid-registry.js +24 -0
- package/dist/lib/session/provenance.d.ts +14 -2
- package/dist/lib/session/provenance.js +39 -8
- package/dist/lib/session/remote-active.js +19 -7
- package/dist/lib/session/remote-list.d.ts +51 -0
- package/dist/lib/session/remote-list.js +213 -0
- package/dist/lib/session/remote.d.ts +7 -1
- package/dist/lib/session/remote.js +16 -2
- package/dist/lib/session/sync/config.d.ts +1 -15
- package/dist/lib/session/sync/config.js +4 -20
- package/dist/lib/session/types.d.ts +17 -0
- package/dist/lib/shims.d.ts +36 -6
- package/dist/lib/shims.js +91 -29
- package/dist/lib/ssh-exec.js +2 -0
- package/dist/lib/ssh-tunnel.js +2 -1
- package/dist/lib/startup/command-registry.d.ts +6 -0
- package/dist/lib/startup/command-registry.js +13 -1
- package/dist/lib/state.d.ts +13 -0
- package/dist/lib/state.js +103 -9
- package/dist/lib/sync-umbrella.d.ts +14 -7
- package/dist/lib/sync-umbrella.js +17 -9
- package/dist/lib/teams/forEach.d.ts +110 -0
- package/dist/lib/teams/forEach.js +186 -0
- package/dist/lib/teams/index.d.ts +1 -0
- package/dist/lib/teams/index.js +1 -0
- package/dist/lib/teams/pr-watch.d.ts +226 -0
- package/dist/lib/teams/pr-watch.js +371 -0
- package/dist/lib/teams/supervisor.d.ts +14 -1
- package/dist/lib/teams/supervisor.js +19 -0
- package/dist/lib/teams/worktree.d.ts +9 -0
- package/dist/lib/teams/worktree.js +32 -0
- package/dist/lib/terminal/backends/index.d.ts +2 -1
- package/dist/lib/terminal/backends/index.js +3 -1
- package/dist/lib/terminal/backends/vscodium-agent.d.ts +31 -0
- package/dist/lib/terminal/backends/vscodium-agent.js +72 -0
- package/dist/lib/terminal/index.d.ts +4 -1
- package/dist/lib/terminal/index.js +4 -1
- package/dist/lib/terminal/inject.d.ts +204 -0
- package/dist/lib/terminal/inject.js +247 -0
- package/dist/lib/terminal/resolve.d.ts +64 -0
- package/dist/lib/terminal/resolve.js +90 -0
- package/dist/lib/terminal/types.d.ts +1 -1
- package/dist/lib/triggers/webhook.d.ts +85 -0
- package/dist/lib/triggers/webhook.js +141 -0
- package/dist/lib/versions.d.ts +23 -0
- package/dist/lib/versions.js +119 -13
- package/dist/lib/watchdog/index.d.ts +3 -0
- package/dist/lib/watchdog/index.js +5 -0
- package/dist/lib/watchdog/read.d.ts +35 -0
- package/dist/lib/watchdog/read.js +149 -0
- package/dist/lib/watchdog/runner.d.ts +127 -0
- package/dist/lib/watchdog/runner.js +322 -0
- package/dist/lib/watchdog/watchdog.d.ts +40 -0
- package/dist/lib/watchdog/watchdog.js +166 -0
- package/dist/lib/watchdog/watchdogTail.d.ts +5 -0
- package/dist/lib/watchdog/watchdogTail.js +154 -0
- package/dist/lib/workflows.d.ts +166 -0
- package/dist/lib/workflows.js +193 -0
- package/package.json +5 -4
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Watchdog tail summarization: pull the most recent user / assistant message
|
|
2
|
+
// out of the session JSONL window the watchdog read this tick. Pure functions —
|
|
3
|
+
// the watchdog runtime hands us tailLines + agentType and we read no files.
|
|
4
|
+
// Ported from Swarmify (extension/src/core/watchdogTail.ts) — behavior verbatim.
|
|
5
|
+
const MAX_MESSAGE_CHARS = 600;
|
|
6
|
+
export function summarizeWatchdogTail(tailLines, agentType) {
|
|
7
|
+
let lastUser;
|
|
8
|
+
let lastAssistant;
|
|
9
|
+
for (let i = tailLines.length - 1; i >= 0; i--) {
|
|
10
|
+
if (lastUser && lastAssistant)
|
|
11
|
+
break;
|
|
12
|
+
let raw;
|
|
13
|
+
try {
|
|
14
|
+
raw = JSON.parse(tailLines[i]);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (!raw || typeof raw !== 'object')
|
|
20
|
+
continue;
|
|
21
|
+
if (agentType === 'claude') {
|
|
22
|
+
const claim = readClaude(raw);
|
|
23
|
+
if (claim?.role === 'user' && !lastUser)
|
|
24
|
+
lastUser = claim.text;
|
|
25
|
+
else if (claim?.role === 'assistant' && !lastAssistant)
|
|
26
|
+
lastAssistant = claim.text;
|
|
27
|
+
}
|
|
28
|
+
else if (agentType === 'codex') {
|
|
29
|
+
const claim = readCodex(raw);
|
|
30
|
+
if (claim?.role === 'user' && !lastUser)
|
|
31
|
+
lastUser = claim.text;
|
|
32
|
+
else if (claim?.role === 'assistant' && !lastAssistant)
|
|
33
|
+
lastAssistant = claim.text;
|
|
34
|
+
}
|
|
35
|
+
else if (agentType === 'gemini') {
|
|
36
|
+
const claim = readGemini(raw);
|
|
37
|
+
if (claim?.role === 'user' && !lastUser)
|
|
38
|
+
lastUser = claim.text;
|
|
39
|
+
else if (claim?.role === 'assistant' && !lastAssistant)
|
|
40
|
+
lastAssistant = claim.text;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
lastUserMessage: clip(lastUser),
|
|
45
|
+
lastAssistantMessage: clip(lastAssistant),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function readClaude(raw) {
|
|
49
|
+
const t = raw.type;
|
|
50
|
+
if (t !== 'user' && t !== 'assistant')
|
|
51
|
+
return null;
|
|
52
|
+
const msg = raw.message;
|
|
53
|
+
if (!msg)
|
|
54
|
+
return null;
|
|
55
|
+
const text = pickContentText(msg.content);
|
|
56
|
+
if (!text)
|
|
57
|
+
return null;
|
|
58
|
+
return { role: t, text };
|
|
59
|
+
}
|
|
60
|
+
function readCodex(raw) {
|
|
61
|
+
if (raw.type !== 'response_item')
|
|
62
|
+
return null;
|
|
63
|
+
const payload = raw.payload;
|
|
64
|
+
if (!payload || payload.type !== 'message')
|
|
65
|
+
return null;
|
|
66
|
+
const role = payload.role;
|
|
67
|
+
if (role !== 'user' && role !== 'assistant')
|
|
68
|
+
return null;
|
|
69
|
+
const text = pickContentText(payload.content);
|
|
70
|
+
if (!text)
|
|
71
|
+
return null;
|
|
72
|
+
return { role, text };
|
|
73
|
+
}
|
|
74
|
+
// Gemini stores chats as a single pretty-printed JSON document — readTailLines
|
|
75
|
+
// returns partial fragments that JSON.parse rejects. So in practice this
|
|
76
|
+
// branch only fires for the JSONL-style events that some Gemini wrappers emit
|
|
77
|
+
// during a live session. Acceptable: the result is an empty summary, which
|
|
78
|
+
// the UI handles gracefully.
|
|
79
|
+
function readGemini(raw) {
|
|
80
|
+
const t = raw.type;
|
|
81
|
+
if (t === 'user_message') {
|
|
82
|
+
const text = typeof raw.text === 'string' ? raw.text : null;
|
|
83
|
+
return text ? { role: 'user', text } : null;
|
|
84
|
+
}
|
|
85
|
+
if (t === 'agent_message' || t === 'model_message') {
|
|
86
|
+
const text = typeof raw.text === 'string' ? raw.text : null;
|
|
87
|
+
return text ? { role: 'assistant', text } : null;
|
|
88
|
+
}
|
|
89
|
+
if (t === 'message') {
|
|
90
|
+
const role = raw.role === 'user' ? 'user' : raw.role === 'assistant' ? 'assistant' : null;
|
|
91
|
+
if (!role)
|
|
92
|
+
return null;
|
|
93
|
+
const text = typeof raw.text === 'string' ? raw.text : pickContentText(raw.content);
|
|
94
|
+
return text ? { role, text } : null;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
function pickContentText(content) {
|
|
99
|
+
if (typeof content === 'string')
|
|
100
|
+
return isSyntheticTagged(content) ? null : content;
|
|
101
|
+
if (!Array.isArray(content))
|
|
102
|
+
return null;
|
|
103
|
+
const parts = [];
|
|
104
|
+
for (const part of content) {
|
|
105
|
+
if (!part || typeof part !== 'object')
|
|
106
|
+
continue;
|
|
107
|
+
const p = part;
|
|
108
|
+
if ((p.type === 'text' || p.type === 'input_text' || p.type === 'output_text') && typeof p.text === 'string') {
|
|
109
|
+
if (isSyntheticTagged(p.text))
|
|
110
|
+
continue;
|
|
111
|
+
parts.push(p.text);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return parts.length ? parts.join('\n') : null;
|
|
115
|
+
}
|
|
116
|
+
// Mirrors the synthetic-tag filter in the session readers so a "User: …"
|
|
117
|
+
// surfaced in the watchdog UI never shows a <local-command-stdout>,
|
|
118
|
+
// <system-reminder>, or similar harness chunk that Claude wraps tool output in.
|
|
119
|
+
const SYNTHETIC_TAG_PREFIXES = [
|
|
120
|
+
'<local-command-caveat',
|
|
121
|
+
'<local-command-stdout',
|
|
122
|
+
'<local-command-stderr',
|
|
123
|
+
'<command-name',
|
|
124
|
+
'<command-message',
|
|
125
|
+
'<command-args',
|
|
126
|
+
'<bash-input',
|
|
127
|
+
'<bash-stdout',
|
|
128
|
+
'<bash-stderr',
|
|
129
|
+
'<system-reminder',
|
|
130
|
+
'<user-prompt-submit-hook',
|
|
131
|
+
'<task-notification',
|
|
132
|
+
'<persisted-output',
|
|
133
|
+
];
|
|
134
|
+
function isSyntheticTagged(text) {
|
|
135
|
+
const trimmed = text.trim();
|
|
136
|
+
if (!trimmed)
|
|
137
|
+
return true;
|
|
138
|
+
const lower = trimmed.toLowerCase();
|
|
139
|
+
for (const prefix of SYNTHETIC_TAG_PREFIXES) {
|
|
140
|
+
if (lower.startsWith(prefix))
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
function clip(text) {
|
|
146
|
+
if (!text)
|
|
147
|
+
return undefined;
|
|
148
|
+
const cleaned = text.replace(/\s+/g, ' ').trim();
|
|
149
|
+
if (!cleaned)
|
|
150
|
+
return undefined;
|
|
151
|
+
return cleaned.length > MAX_MESSAGE_CHARS
|
|
152
|
+
? cleaned.slice(0, MAX_MESSAGE_CHARS - 1) + '…'
|
|
153
|
+
: cleaned;
|
|
154
|
+
}
|
package/dist/lib/workflows.d.ts
CHANGED
|
@@ -21,6 +21,70 @@ export interface LoopConfigRaw {
|
|
|
21
21
|
/** Delay between iterations ("0" back-to-back, "30m" paces). */
|
|
22
22
|
interval?: string;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* The `verify:` sub-block of a `for_each:` construct (issue #343).
|
|
26
|
+
*
|
|
27
|
+
* Each produced item's stage teammate can be gated by a panel of independent
|
|
28
|
+
* skeptics: `votes` of them run (as teammates that depend on the stage), and
|
|
29
|
+
* `keep_if` records how their verdicts converge (`majority` / `all` / `any`).
|
|
30
|
+
* The vote-counting itself is a downstream concern — the declarative layer's
|
|
31
|
+
* job is to expand the panel; see `expandForEach`.
|
|
32
|
+
*/
|
|
33
|
+
export interface ForEachVerifySpec {
|
|
34
|
+
/** Subagent / agent id that plays skeptic for each item. */
|
|
35
|
+
agent: string;
|
|
36
|
+
/** Prompt template for each skeptic (`{{item}}`, `{{index}}` substituted). */
|
|
37
|
+
prompt?: string;
|
|
38
|
+
/** How many independent skeptics run per item (>= 1). */
|
|
39
|
+
votes: number;
|
|
40
|
+
/** Convergence rule for keeping a finding. */
|
|
41
|
+
keep_if: 'majority' | 'all' | 'any';
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The `for_each:` block as it appears in WORKFLOW.md frontmatter (issue #343).
|
|
45
|
+
*
|
|
46
|
+
* Declarative dynamic fan-out: a producer emits a list at runtime, one stage
|
|
47
|
+
* teammate runs per produced item (runtime-computed N), optionally followed by
|
|
48
|
+
* a `verify` panel. This is a thin declarative layer over the existing teams
|
|
49
|
+
* substrate — each expanded teammate is staged into the supervisor's
|
|
50
|
+
* mid-flight-add path (`AgentManager.spawn`), NOT a new engine. See
|
|
51
|
+
* `expandForEach` and `src/lib/teams/forEach.ts`.
|
|
52
|
+
*
|
|
53
|
+
* Parsed defensively (mirrors `parseLoopBlock`): a malformed block drops to
|
|
54
|
+
* undefined rather than passing a bad shape downstream.
|
|
55
|
+
*/
|
|
56
|
+
export interface ForEachSpec {
|
|
57
|
+
/**
|
|
58
|
+
* The producer: a shell command or subagent whose stdout is a JSON array (or
|
|
59
|
+
* newline-delimited list) of items. Alternatively `itemsRef` names a prior
|
|
60
|
+
* step's output. At least one of the two is expected for a runnable spec.
|
|
61
|
+
*/
|
|
62
|
+
produce?: string;
|
|
63
|
+
/** `${step}`-style reference to a prior step's produced list. */
|
|
64
|
+
itemsRef?: string;
|
|
65
|
+
/** Subagent / agent id for the per-item stage. */
|
|
66
|
+
agent: string;
|
|
67
|
+
/** Base name for the expanded teammates (default `item`). */
|
|
68
|
+
name?: string;
|
|
69
|
+
/** Per-item prompt template (`{{item}}`, `{{index}}` substituted). */
|
|
70
|
+
prompt: string;
|
|
71
|
+
/** In-flight cap — maps to the supervisor's wave size (>= 1). */
|
|
72
|
+
concurrency?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Hard runaway guard: the producer can emit at most this many items before
|
|
75
|
+
* the fan-out is truncated. Defaults to `DEFAULT_FOR_EACH_CAP`.
|
|
76
|
+
*/
|
|
77
|
+
max_items?: number;
|
|
78
|
+
/** Optional convergence gate run after each item's stage. */
|
|
79
|
+
verify?: ForEachVerifySpec;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Hard upper bound on items a single `for_each` expands, absent an explicit
|
|
83
|
+
* `max_items`. A guard against a runaway producer spawning unbounded teammates
|
|
84
|
+
* (acceptance criterion in issue #343). Anthropic's Dynamic Workflows cap at
|
|
85
|
+
* 1000; we default lower and let authors raise it deliberately.
|
|
86
|
+
*/
|
|
87
|
+
export declare const DEFAULT_FOR_EACH_CAP = 256;
|
|
24
88
|
/** Parsed WORKFLOW.md frontmatter. */
|
|
25
89
|
export interface WorkflowFrontmatter {
|
|
26
90
|
name: string;
|
|
@@ -43,6 +107,12 @@ export interface WorkflowFrontmatter {
|
|
|
43
107
|
* `--loop` flag. Validated/coerced in parseWorkflowFrontmatter.
|
|
44
108
|
*/
|
|
45
109
|
loop?: LoopConfigRaw;
|
|
110
|
+
/**
|
|
111
|
+
* Optional declarative dynamic fan-out (issue #343): a producer emits a list
|
|
112
|
+
* and one stage teammate runs per item, with an optional verify panel.
|
|
113
|
+
* Validated/coerced in parseWorkflowFrontmatter via `parseForEachBlock`.
|
|
114
|
+
*/
|
|
115
|
+
forEach?: ForEachSpec;
|
|
46
116
|
}
|
|
47
117
|
/** A workflow found during repo discovery. */
|
|
48
118
|
export interface DiscoveredWorkflow {
|
|
@@ -76,6 +146,81 @@ export declare function parseWorkflowFrontmatter(workflowDir: string): WorkflowF
|
|
|
76
146
|
* - interval: a string (e.g. "0", "30m"); non-strings dropped.
|
|
77
147
|
*/
|
|
78
148
|
export declare function parseLoopBlock(v: unknown): LoopConfigRaw | undefined;
|
|
149
|
+
/**
|
|
150
|
+
* Defensively coerce a frontmatter `verify:` sub-block into a ForEachVerifySpec.
|
|
151
|
+
*
|
|
152
|
+
* Requires an `agent`; drops the whole block otherwise (a verify panel with no
|
|
153
|
+
* skeptic is meaningless). `votes` defaults to 1 (a single confirmation) and
|
|
154
|
+
* `keep_if` to `majority`; both are validated against their allowed shapes.
|
|
155
|
+
*/
|
|
156
|
+
export declare function parseVerifyBlock(v: unknown): ForEachVerifySpec | undefined;
|
|
157
|
+
/**
|
|
158
|
+
* Defensively coerce a frontmatter `for_each:` block into a ForEachSpec (issue
|
|
159
|
+
* #343). Mirrors `parseLoopBlock`'s discipline: a block missing the two
|
|
160
|
+
* load-bearing fields (`agent` + `prompt`) drops to undefined rather than
|
|
161
|
+
* passing a half-formed spec to the expander. Optional numeric/verify fields
|
|
162
|
+
* are individually validated and dropped when malformed.
|
|
163
|
+
*/
|
|
164
|
+
export declare function parseForEachBlock(v: unknown): ForEachSpec | undefined;
|
|
165
|
+
/** One teammate produced by expanding a `for_each` spec against a produced list. */
|
|
166
|
+
export interface ForEachTeammate {
|
|
167
|
+
/** `stage` runs the per-item work; `verify` is a skeptic in the panel. */
|
|
168
|
+
role: 'stage' | 'verify';
|
|
169
|
+
/** Unique teammate name within the team (used for `--after` linkage). */
|
|
170
|
+
name: string;
|
|
171
|
+
/** Subagent / agent id this teammate runs as. */
|
|
172
|
+
agentType: string;
|
|
173
|
+
/** Fully-resolved prompt (template variables already substituted). */
|
|
174
|
+
prompt: string;
|
|
175
|
+
/** Names of sibling teammates this one waits on (`--after` semantics). */
|
|
176
|
+
after: string[];
|
|
177
|
+
/** The produced item this teammate handles. */
|
|
178
|
+
item: string;
|
|
179
|
+
/** Zero-based index of the item in the (capped) produced list. */
|
|
180
|
+
itemIndex: number;
|
|
181
|
+
/** For `verify` teammates: 1-based vote index and the panel's gate config. */
|
|
182
|
+
vote?: number;
|
|
183
|
+
votes?: number;
|
|
184
|
+
keep_if?: 'majority' | 'all' | 'any';
|
|
185
|
+
}
|
|
186
|
+
/** Result of expanding a `for_each` spec: the teammates plus cap accounting. */
|
|
187
|
+
export interface ForEachExpansion {
|
|
188
|
+
teammates: ForEachTeammate[];
|
|
189
|
+
/** How many items the producer emitted (pre-cap). */
|
|
190
|
+
producedCount: number;
|
|
191
|
+
/** How many items were actually expanded (post-cap). */
|
|
192
|
+
usedCount: number;
|
|
193
|
+
/** producedCount - usedCount; > 0 means the runaway guard truncated. */
|
|
194
|
+
truncated: number;
|
|
195
|
+
/** The effective per-`for_each` item cap that was applied. */
|
|
196
|
+
cap: number;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Substitute `{{item}}` / `{{index}}` (and 1-based `{{n}}`) in a prompt
|
|
200
|
+
* template. Unknown `{{...}}` tokens are left intact so a template can carry
|
|
201
|
+
* placeholders the caller resolves elsewhere.
|
|
202
|
+
*/
|
|
203
|
+
export declare function renderForEachTemplate(template: string, item: string, index: number): string;
|
|
204
|
+
/**
|
|
205
|
+
* Expand a `for_each` spec against a producer's output into concrete teammate
|
|
206
|
+
* descriptors (issue #343) — the heart of the declarative fan-out.
|
|
207
|
+
*
|
|
208
|
+
* Pure and deterministic: no I/O, no spawning. `src/lib/teams/forEach.ts`
|
|
209
|
+
* feeds the result to `AgentManager.spawn`, staging each descriptor into the
|
|
210
|
+
* supervisor's existing mid-flight-add path — so this reuses the dynamic-DAG
|
|
211
|
+
* substrate rather than introducing a new engine.
|
|
212
|
+
*
|
|
213
|
+
* For N produced items (capped at `spec.max_items` / `DEFAULT_FOR_EACH_CAP`):
|
|
214
|
+
* - one `stage` teammate per item, depending on `producerName` if given;
|
|
215
|
+
* - when `verify` is set, `votes` `verify` teammates per item, each
|
|
216
|
+
* depending on that item's stage teammate.
|
|
217
|
+
*
|
|
218
|
+
* Names are unique (`<base>-<n>` and `<base>-<n>-verify-<v>`) so `--after`
|
|
219
|
+
* linkage and the teams cycle check carry over unchanged.
|
|
220
|
+
*/
|
|
221
|
+
export declare function expandForEach(spec: ForEachSpec, items: string[], opts?: {
|
|
222
|
+
producerName?: string;
|
|
223
|
+
}): ForEachExpansion;
|
|
79
224
|
/**
|
|
80
225
|
* Decide which subagent .md stems a workflow may use, given the discovered
|
|
81
226
|
* subagent files and the parsed `allowedAgents` frontmatter. This is the
|
|
@@ -95,6 +240,27 @@ export declare function resolveAllowedSubagents(available: string[], allowedAgen
|
|
|
95
240
|
allowedStems: string[];
|
|
96
241
|
missing: string[];
|
|
97
242
|
};
|
|
243
|
+
/**
|
|
244
|
+
* Prune stale workflow-managed subagent files from the shared per-agent agents
|
|
245
|
+
* dir before a scoped run writes the permitted set (issue #401, follow-up to
|
|
246
|
+
* #324). A prior *unrestricted* run of a workflow copies every subagent
|
|
247
|
+
* definition into the shared `~/.claude/agents/` dir; a later run that declares
|
|
248
|
+
* `allowedAgents:` copies only the permitted ones but never removes the
|
|
249
|
+
* leftovers — so an unlisted subagent stays on disk and remains dispatchable,
|
|
250
|
+
* silently defeating the fail-closed scope.
|
|
251
|
+
*
|
|
252
|
+
* Fail-closed fix (mirrors how `cleanupWorkflowMcpConfig` only tears down what
|
|
253
|
+
* the workflow itself created): remove any file that (a) belongs to THIS
|
|
254
|
+
* workflow's subagents/ — matched by filename, i.e. the workflow-managed
|
|
255
|
+
* universe — and (b) is NOT in the permitted set. A user's own hand-placed
|
|
256
|
+
* subagent shares no name with a workflow subagent file, so it is never
|
|
257
|
+
* touched. Permitted files are left in place; the caller (re)copies them.
|
|
258
|
+
*
|
|
259
|
+
* `workflowSubagentFiles` are the .md filenames in the workflow's subagents/
|
|
260
|
+
* dir (e.g. `security.md`); `allowedStems` are the permitted stems from
|
|
261
|
+
* `resolveAllowedSubagents`. Returns the filenames actually removed.
|
|
262
|
+
*/
|
|
263
|
+
export declare function pruneStaleWorkflowSubagents(sharedAgentsDir: string, workflowSubagentFiles: string[], allowedStems: string[]): string[];
|
|
98
264
|
/** Count subagent .md files in a workflow's subagents/ directory. */
|
|
99
265
|
export declare function countWorkflowSubagents(workflowDir: string): number;
|
|
100
266
|
/**
|
package/dist/lib/workflows.js
CHANGED
|
@@ -11,6 +11,13 @@ import * as yaml from 'yaml';
|
|
|
11
11
|
import { capableAgents } from './capabilities.js';
|
|
12
12
|
import { getProjectAgentsDir, getSystemWorkflowsDir, getUserWorkflowsDir, getTrashWorkflowsDir, getEnabledExtraRepos, } from './state.js';
|
|
13
13
|
import { listInstalledVersions, getVersionHomePath } from './versions.js';
|
|
14
|
+
/**
|
|
15
|
+
* Hard upper bound on items a single `for_each` expands, absent an explicit
|
|
16
|
+
* `max_items`. A guard against a runaway producer spawning unbounded teammates
|
|
17
|
+
* (acceptance criterion in issue #343). Anthropic's Dynamic Workflows cap at
|
|
18
|
+
* 1000; we default lower and let authors raise it deliberately.
|
|
19
|
+
*/
|
|
20
|
+
export const DEFAULT_FOR_EACH_CAP = 256;
|
|
14
21
|
/** Parse WORKFLOW.md frontmatter from a workflow directory. Returns null if invalid. */
|
|
15
22
|
export function parseWorkflowFrontmatter(workflowDir) {
|
|
16
23
|
const workflowMdPath = path.join(workflowDir, 'WORKFLOW.md');
|
|
@@ -42,6 +49,7 @@ export function parseWorkflowFrontmatter(workflowDir) {
|
|
|
42
49
|
allowedAgents: asStringArray(parsed.allowedAgents),
|
|
43
50
|
secrets: asStringArray(parsed.secrets),
|
|
44
51
|
loop: parseLoopBlock(parsed.loop),
|
|
52
|
+
forEach: parseForEachBlock(parsed.for_each),
|
|
45
53
|
};
|
|
46
54
|
}
|
|
47
55
|
catch {
|
|
@@ -83,6 +91,152 @@ export function parseLoopBlock(v) {
|
|
|
83
91
|
out.interval = raw.interval;
|
|
84
92
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
85
93
|
}
|
|
94
|
+
/** A finite positive integer, or undefined. Shared guard for count-like fields. */
|
|
95
|
+
function asPosInt(v) {
|
|
96
|
+
return typeof v === 'number' && Number.isFinite(v) && Number.isInteger(v) && v > 0
|
|
97
|
+
? v
|
|
98
|
+
: undefined;
|
|
99
|
+
}
|
|
100
|
+
/** A non-empty trimmed string, or undefined. */
|
|
101
|
+
function asNonEmptyString(v) {
|
|
102
|
+
return typeof v === 'string' && v.trim().length > 0 ? v : undefined;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Defensively coerce a frontmatter `verify:` sub-block into a ForEachVerifySpec.
|
|
106
|
+
*
|
|
107
|
+
* Requires an `agent`; drops the whole block otherwise (a verify panel with no
|
|
108
|
+
* skeptic is meaningless). `votes` defaults to 1 (a single confirmation) and
|
|
109
|
+
* `keep_if` to `majority`; both are validated against their allowed shapes.
|
|
110
|
+
*/
|
|
111
|
+
export function parseVerifyBlock(v) {
|
|
112
|
+
if (!v || typeof v !== 'object' || Array.isArray(v))
|
|
113
|
+
return undefined;
|
|
114
|
+
const raw = v;
|
|
115
|
+
const agent = asNonEmptyString(raw.agent);
|
|
116
|
+
if (!agent)
|
|
117
|
+
return undefined;
|
|
118
|
+
const keepIf = raw.keep_if;
|
|
119
|
+
const out = {
|
|
120
|
+
agent,
|
|
121
|
+
votes: asPosInt(raw.votes) ?? 1,
|
|
122
|
+
keep_if: keepIf === 'all' || keepIf === 'any' || keepIf === 'majority'
|
|
123
|
+
? keepIf
|
|
124
|
+
: 'majority',
|
|
125
|
+
};
|
|
126
|
+
const prompt = asNonEmptyString(raw.prompt);
|
|
127
|
+
if (prompt)
|
|
128
|
+
out.prompt = prompt;
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Defensively coerce a frontmatter `for_each:` block into a ForEachSpec (issue
|
|
133
|
+
* #343). Mirrors `parseLoopBlock`'s discipline: a block missing the two
|
|
134
|
+
* load-bearing fields (`agent` + `prompt`) drops to undefined rather than
|
|
135
|
+
* passing a half-formed spec to the expander. Optional numeric/verify fields
|
|
136
|
+
* are individually validated and dropped when malformed.
|
|
137
|
+
*/
|
|
138
|
+
export function parseForEachBlock(v) {
|
|
139
|
+
if (!v || typeof v !== 'object' || Array.isArray(v))
|
|
140
|
+
return undefined;
|
|
141
|
+
const raw = v;
|
|
142
|
+
const agent = asNonEmptyString(raw.agent);
|
|
143
|
+
const prompt = asNonEmptyString(raw.prompt);
|
|
144
|
+
if (!agent || !prompt)
|
|
145
|
+
return undefined;
|
|
146
|
+
const out = { agent, prompt };
|
|
147
|
+
const produce = asNonEmptyString(raw.produce);
|
|
148
|
+
if (produce)
|
|
149
|
+
out.produce = produce;
|
|
150
|
+
// `for_each: ${step}` references a prior step's list. Accept either the
|
|
151
|
+
// snake-case `for_each` key or an explicit `items_ref`.
|
|
152
|
+
const itemsRef = asNonEmptyString(raw.for_each) ?? asNonEmptyString(raw.items_ref);
|
|
153
|
+
if (itemsRef)
|
|
154
|
+
out.itemsRef = itemsRef;
|
|
155
|
+
const name = asNonEmptyString(raw.name);
|
|
156
|
+
if (name)
|
|
157
|
+
out.name = name;
|
|
158
|
+
const concurrency = asPosInt(raw.concurrency);
|
|
159
|
+
if (concurrency)
|
|
160
|
+
out.concurrency = concurrency;
|
|
161
|
+
const maxItems = asPosInt(raw.max_items);
|
|
162
|
+
if (maxItems)
|
|
163
|
+
out.max_items = maxItems;
|
|
164
|
+
const verify = parseVerifyBlock(raw.verify);
|
|
165
|
+
if (verify)
|
|
166
|
+
out.verify = verify;
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Substitute `{{item}}` / `{{index}}` (and 1-based `{{n}}`) in a prompt
|
|
171
|
+
* template. Unknown `{{...}}` tokens are left intact so a template can carry
|
|
172
|
+
* placeholders the caller resolves elsewhere.
|
|
173
|
+
*/
|
|
174
|
+
export function renderForEachTemplate(template, item, index) {
|
|
175
|
+
return template
|
|
176
|
+
.replace(/\{\{\s*item\s*\}\}/g, item)
|
|
177
|
+
.replace(/\{\{\s*index\s*\}\}/g, String(index))
|
|
178
|
+
.replace(/\{\{\s*n\s*\}\}/g, String(index + 1));
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Expand a `for_each` spec against a producer's output into concrete teammate
|
|
182
|
+
* descriptors (issue #343) — the heart of the declarative fan-out.
|
|
183
|
+
*
|
|
184
|
+
* Pure and deterministic: no I/O, no spawning. `src/lib/teams/forEach.ts`
|
|
185
|
+
* feeds the result to `AgentManager.spawn`, staging each descriptor into the
|
|
186
|
+
* supervisor's existing mid-flight-add path — so this reuses the dynamic-DAG
|
|
187
|
+
* substrate rather than introducing a new engine.
|
|
188
|
+
*
|
|
189
|
+
* For N produced items (capped at `spec.max_items` / `DEFAULT_FOR_EACH_CAP`):
|
|
190
|
+
* - one `stage` teammate per item, depending on `producerName` if given;
|
|
191
|
+
* - when `verify` is set, `votes` `verify` teammates per item, each
|
|
192
|
+
* depending on that item's stage teammate.
|
|
193
|
+
*
|
|
194
|
+
* Names are unique (`<base>-<n>` and `<base>-<n>-verify-<v>`) so `--after`
|
|
195
|
+
* linkage and the teams cycle check carry over unchanged.
|
|
196
|
+
*/
|
|
197
|
+
export function expandForEach(spec, items, opts = {}) {
|
|
198
|
+
const cap = spec.max_items ?? DEFAULT_FOR_EACH_CAP;
|
|
199
|
+
const producedCount = items.length;
|
|
200
|
+
const used = items.slice(0, cap);
|
|
201
|
+
const base = spec.name ?? 'item';
|
|
202
|
+
const teammates = [];
|
|
203
|
+
used.forEach((item, itemIndex) => {
|
|
204
|
+
const stageName = `${base}-${itemIndex + 1}`;
|
|
205
|
+
teammates.push({
|
|
206
|
+
role: 'stage',
|
|
207
|
+
name: stageName,
|
|
208
|
+
agentType: spec.agent,
|
|
209
|
+
prompt: renderForEachTemplate(spec.prompt, item, itemIndex),
|
|
210
|
+
after: opts.producerName ? [opts.producerName] : [],
|
|
211
|
+
item,
|
|
212
|
+
itemIndex,
|
|
213
|
+
});
|
|
214
|
+
if (spec.verify) {
|
|
215
|
+
const verifyPrompt = spec.verify.prompt ?? spec.prompt;
|
|
216
|
+
for (let vote = 1; vote <= spec.verify.votes; vote++) {
|
|
217
|
+
teammates.push({
|
|
218
|
+
role: 'verify',
|
|
219
|
+
name: `${stageName}-verify-${vote}`,
|
|
220
|
+
agentType: spec.verify.agent,
|
|
221
|
+
prompt: renderForEachTemplate(verifyPrompt, item, itemIndex),
|
|
222
|
+
after: [stageName],
|
|
223
|
+
item,
|
|
224
|
+
itemIndex,
|
|
225
|
+
vote,
|
|
226
|
+
votes: spec.verify.votes,
|
|
227
|
+
keep_if: spec.verify.keep_if,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
return {
|
|
233
|
+
teammates,
|
|
234
|
+
producedCount,
|
|
235
|
+
usedCount: used.length,
|
|
236
|
+
truncated: producedCount - used.length,
|
|
237
|
+
cap,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
86
240
|
/**
|
|
87
241
|
* Decide which subagent .md stems a workflow may use, given the discovered
|
|
88
242
|
* subagent files and the parsed `allowedAgents` frontmatter. This is the
|
|
@@ -110,6 +264,45 @@ export function resolveAllowedSubagents(available, allowedAgents) {
|
|
|
110
264
|
missing: allowedAgents.filter(a => !present.has(a)),
|
|
111
265
|
};
|
|
112
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Prune stale workflow-managed subagent files from the shared per-agent agents
|
|
269
|
+
* dir before a scoped run writes the permitted set (issue #401, follow-up to
|
|
270
|
+
* #324). A prior *unrestricted* run of a workflow copies every subagent
|
|
271
|
+
* definition into the shared `~/.claude/agents/` dir; a later run that declares
|
|
272
|
+
* `allowedAgents:` copies only the permitted ones but never removes the
|
|
273
|
+
* leftovers — so an unlisted subagent stays on disk and remains dispatchable,
|
|
274
|
+
* silently defeating the fail-closed scope.
|
|
275
|
+
*
|
|
276
|
+
* Fail-closed fix (mirrors how `cleanupWorkflowMcpConfig` only tears down what
|
|
277
|
+
* the workflow itself created): remove any file that (a) belongs to THIS
|
|
278
|
+
* workflow's subagents/ — matched by filename, i.e. the workflow-managed
|
|
279
|
+
* universe — and (b) is NOT in the permitted set. A user's own hand-placed
|
|
280
|
+
* subagent shares no name with a workflow subagent file, so it is never
|
|
281
|
+
* touched. Permitted files are left in place; the caller (re)copies them.
|
|
282
|
+
*
|
|
283
|
+
* `workflowSubagentFiles` are the .md filenames in the workflow's subagents/
|
|
284
|
+
* dir (e.g. `security.md`); `allowedStems` are the permitted stems from
|
|
285
|
+
* `resolveAllowedSubagents`. Returns the filenames actually removed.
|
|
286
|
+
*/
|
|
287
|
+
export function pruneStaleWorkflowSubagents(sharedAgentsDir, workflowSubagentFiles, allowedStems) {
|
|
288
|
+
if (!fs.existsSync(sharedAgentsDir))
|
|
289
|
+
return [];
|
|
290
|
+
const allow = new Set(allowedStems);
|
|
291
|
+
const pruned = [];
|
|
292
|
+
for (const file of workflowSubagentFiles) {
|
|
293
|
+
if (!file.endsWith('.md'))
|
|
294
|
+
continue;
|
|
295
|
+
const stem = file.replace(/\.md$/, '');
|
|
296
|
+
if (allow.has(stem))
|
|
297
|
+
continue; // permitted → the copy step will (re)write it
|
|
298
|
+
const target = path.join(sharedAgentsDir, file);
|
|
299
|
+
if (fs.existsSync(target)) {
|
|
300
|
+
fs.rmSync(target, { force: true });
|
|
301
|
+
pruned.push(file);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return pruned;
|
|
305
|
+
}
|
|
113
306
|
/** Count subagent .md files in a workflow's subagents/ directory. */
|
|
114
307
|
export function countWorkflowSubagents(workflowDir) {
|
|
115
308
|
const subagentsDir = path.join(workflowDir, 'subagents');
|
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.36",
|
|
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",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsc && rm -rf 'dist/lib/secrets/AgentsKeychain.app' 'dist/lib/secrets/Agents CLI.app' 'dist/lib/menubar/MenubarHelper.app' && ([ \"$(uname)\" = \"Darwin\" ] && cp -R 'bin/Agents CLI.app' 'dist/lib/secrets/Agents CLI.app' || true) && ([ \"$(uname)\" = \"Darwin\" ] && [ -d 'bin/MenubarHelper.app' ] && mkdir -p 'dist/lib/menubar' && cp -R 'bin/MenubarHelper.app' 'dist/lib/menubar/MenubarHelper.app' || true)",
|
|
48
48
|
"build:bin": "scripts/build-bin.sh",
|
|
49
|
+
"prepare": "npm run build",
|
|
49
50
|
"prepack": "scripts/verify-keychain-helper.sh && scripts/verify-menubar-helper.sh",
|
|
50
51
|
"postinstall": "node scripts/postinstall.js",
|
|
51
52
|
"dev": "tsx src/index.ts",
|
|
@@ -72,7 +73,7 @@
|
|
|
72
73
|
"multi-agent"
|
|
73
74
|
],
|
|
74
75
|
"author": "Phoenix Labs",
|
|
75
|
-
"license": "
|
|
76
|
+
"license": "Apache-2.0",
|
|
76
77
|
"engines": {
|
|
77
78
|
"node": ">=22.5.0",
|
|
78
79
|
"npm": ">=9"
|
|
@@ -102,8 +103,8 @@
|
|
|
102
103
|
"devDependencies": {
|
|
103
104
|
"@types/diff": "8.0.0",
|
|
104
105
|
"@types/marked-terminal": "6.1.1",
|
|
105
|
-
"@types/node": "
|
|
106
|
-
"tsx": "4.
|
|
106
|
+
"@types/node": "26.1.0",
|
|
107
|
+
"tsx": "4.23.0",
|
|
107
108
|
"typescript": "6.0.3",
|
|
108
109
|
"vitest": "4.1.9"
|
|
109
110
|
}
|