@agentic-patterns/cli 0.1.3 → 0.1.5
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 +14 -4
- package/assets/dashboard/assets/{index-C2JvJdBt.js → index-x4ivBrgq.js} +20 -20
- package/assets/dashboard/index.html +1 -1
- package/assets/plugin-template/.claude-plugin/plugin.json +13 -0
- package/assets/plugin-template/hooks/emit.mjs +42 -0
- package/assets/plugin-template/hooks/hooks.json +342 -0
- package/dist/cli.js +131 -21
- package/dist/cli.js.map +1 -1
- package/package.json +10 -7
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Agentic Patterns Dashboard</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-x4ivBrgq.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/assets/index-whvaenaU.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agentic-patterns",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Streams all 26 Claude Code lifecycle events (tool calls, permissions, subagents, compaction, sessions) into the @agentic-patterns/server dashboard for live observability. Pair with `ap playground`. See https://github.com/pattern-stack/agentic-patterns-ts/blob/main/docs/CLAUDE-CODE-PLUGIN-ACTIVATION.md for activation + troubleshooting.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "pattern-stack",
|
|
7
|
+
"url": "https://github.com/pattern-stack"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/pattern-stack/agentic-patterns-ts",
|
|
10
|
+
"repository": "https://github.com/pattern-stack/agentic-patterns-ts",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": ["agents", "observability", "llm", "ai"]
|
|
13
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Zero-dependency hook shim. Keep this file tiny and stable — it's copied
|
|
3
|
+
// verbatim into every user project via `ap init --with-plugin` and will not
|
|
4
|
+
// retroactively update. All evolving behavior belongs server-side at
|
|
5
|
+
// /hooks/:eventType. See docs/CLAUDE-CODE-PLUGIN-ACTIVATION.md.
|
|
6
|
+
//
|
|
7
|
+
// Fallback port 3456 mirrors DEFAULT_DASHBOARD_PORT in
|
|
8
|
+
// packages/agent-cli/src/constants.ts. Keep in sync.
|
|
9
|
+
import { stdin } from "node:process";
|
|
10
|
+
|
|
11
|
+
const eventName = process.argv[2] ?? "Unknown";
|
|
12
|
+
const base = process.env.AP_DASHBOARD_URL ?? "http://localhost:3456";
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const chunks = [];
|
|
16
|
+
for await (const c of stdin) chunks.push(c);
|
|
17
|
+
const body = Buffer.concat(chunks).toString("utf8") || "{}";
|
|
18
|
+
|
|
19
|
+
const controller = new AbortController();
|
|
20
|
+
const timer = setTimeout(() => controller.abort(), 500);
|
|
21
|
+
|
|
22
|
+
const headers = { "content-type": "application/json" };
|
|
23
|
+
const correlationId = process.env.AP_RUNNER_CORRELATION_ID;
|
|
24
|
+
if (correlationId) {
|
|
25
|
+
headers["x-ap-runner-correlation-id"] = correlationId;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
await fetch(`${base}/hooks/${eventName}`, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers,
|
|
31
|
+
body,
|
|
32
|
+
signal: controller.signal,
|
|
33
|
+
}).catch((err) => {
|
|
34
|
+
process.stderr.write(`[ap-hook] ${eventName}: ${err.message ?? err}\n`);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
process.stderr.write(`[ap-hook] ${eventName}: ${err?.message ?? err}\n`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
process.exit(0);
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs SessionStart",
|
|
10
|
+
"async": true,
|
|
11
|
+
"timeout": 10
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"InstructionsLoaded": [
|
|
17
|
+
{
|
|
18
|
+
"matcher": "",
|
|
19
|
+
"hooks": [
|
|
20
|
+
{
|
|
21
|
+
"type": "command",
|
|
22
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs InstructionsLoaded",
|
|
23
|
+
"async": true,
|
|
24
|
+
"timeout": 10
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"UserPromptSubmit": [
|
|
30
|
+
{
|
|
31
|
+
"matcher": "",
|
|
32
|
+
"hooks": [
|
|
33
|
+
{
|
|
34
|
+
"type": "command",
|
|
35
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs UserPromptSubmit",
|
|
36
|
+
"async": true,
|
|
37
|
+
"timeout": 10
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"PreToolUse": [
|
|
43
|
+
{
|
|
44
|
+
"matcher": "",
|
|
45
|
+
"hooks": [
|
|
46
|
+
{
|
|
47
|
+
"type": "command",
|
|
48
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs PreToolUse",
|
|
49
|
+
"async": true,
|
|
50
|
+
"timeout": 10
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"PermissionRequest": [
|
|
56
|
+
{
|
|
57
|
+
"matcher": "",
|
|
58
|
+
"hooks": [
|
|
59
|
+
{
|
|
60
|
+
"type": "command",
|
|
61
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs PermissionRequest",
|
|
62
|
+
"async": true,
|
|
63
|
+
"timeout": 10
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
"PermissionDenied": [
|
|
69
|
+
{
|
|
70
|
+
"matcher": "",
|
|
71
|
+
"hooks": [
|
|
72
|
+
{
|
|
73
|
+
"type": "command",
|
|
74
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs PermissionDenied",
|
|
75
|
+
"async": true,
|
|
76
|
+
"timeout": 10
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
"PostToolUse": [
|
|
82
|
+
{
|
|
83
|
+
"matcher": "",
|
|
84
|
+
"hooks": [
|
|
85
|
+
{
|
|
86
|
+
"type": "command",
|
|
87
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs PostToolUse",
|
|
88
|
+
"async": true,
|
|
89
|
+
"timeout": 10
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
"PostToolUseFailure": [
|
|
95
|
+
{
|
|
96
|
+
"matcher": "",
|
|
97
|
+
"hooks": [
|
|
98
|
+
{
|
|
99
|
+
"type": "command",
|
|
100
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs PostToolUseFailure",
|
|
101
|
+
"async": true,
|
|
102
|
+
"timeout": 10
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"Notification": [
|
|
108
|
+
{
|
|
109
|
+
"matcher": "",
|
|
110
|
+
"hooks": [
|
|
111
|
+
{
|
|
112
|
+
"type": "command",
|
|
113
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs Notification",
|
|
114
|
+
"async": true,
|
|
115
|
+
"timeout": 10
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
"SubagentStart": [
|
|
121
|
+
{
|
|
122
|
+
"matcher": "",
|
|
123
|
+
"hooks": [
|
|
124
|
+
{
|
|
125
|
+
"type": "command",
|
|
126
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs SubagentStart",
|
|
127
|
+
"async": true,
|
|
128
|
+
"timeout": 10
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
],
|
|
133
|
+
"SubagentStop": [
|
|
134
|
+
{
|
|
135
|
+
"matcher": "",
|
|
136
|
+
"hooks": [
|
|
137
|
+
{
|
|
138
|
+
"type": "command",
|
|
139
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs SubagentStop",
|
|
140
|
+
"async": true,
|
|
141
|
+
"timeout": 10
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
"TaskCreated": [
|
|
147
|
+
{
|
|
148
|
+
"matcher": "",
|
|
149
|
+
"hooks": [
|
|
150
|
+
{
|
|
151
|
+
"type": "command",
|
|
152
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs TaskCreated",
|
|
153
|
+
"async": true,
|
|
154
|
+
"timeout": 10
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
"TaskCompleted": [
|
|
160
|
+
{
|
|
161
|
+
"matcher": "",
|
|
162
|
+
"hooks": [
|
|
163
|
+
{
|
|
164
|
+
"type": "command",
|
|
165
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs TaskCompleted",
|
|
166
|
+
"async": true,
|
|
167
|
+
"timeout": 10
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
"Stop": [
|
|
173
|
+
{
|
|
174
|
+
"matcher": "",
|
|
175
|
+
"hooks": [
|
|
176
|
+
{
|
|
177
|
+
"type": "command",
|
|
178
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs Stop",
|
|
179
|
+
"async": true,
|
|
180
|
+
"timeout": 10
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
],
|
|
185
|
+
"StopFailure": [
|
|
186
|
+
{
|
|
187
|
+
"matcher": "",
|
|
188
|
+
"hooks": [
|
|
189
|
+
{
|
|
190
|
+
"type": "command",
|
|
191
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs StopFailure",
|
|
192
|
+
"async": true,
|
|
193
|
+
"timeout": 10
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
],
|
|
198
|
+
"TeammateIdle": [
|
|
199
|
+
{
|
|
200
|
+
"matcher": "",
|
|
201
|
+
"hooks": [
|
|
202
|
+
{
|
|
203
|
+
"type": "command",
|
|
204
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs TeammateIdle",
|
|
205
|
+
"async": true,
|
|
206
|
+
"timeout": 10
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
],
|
|
211
|
+
"ConfigChange": [
|
|
212
|
+
{
|
|
213
|
+
"matcher": "",
|
|
214
|
+
"hooks": [
|
|
215
|
+
{
|
|
216
|
+
"type": "command",
|
|
217
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs ConfigChange",
|
|
218
|
+
"async": true,
|
|
219
|
+
"timeout": 10
|
|
220
|
+
}
|
|
221
|
+
]
|
|
222
|
+
}
|
|
223
|
+
],
|
|
224
|
+
"CwdChanged": [
|
|
225
|
+
{
|
|
226
|
+
"matcher": "",
|
|
227
|
+
"hooks": [
|
|
228
|
+
{
|
|
229
|
+
"type": "command",
|
|
230
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs CwdChanged",
|
|
231
|
+
"async": true,
|
|
232
|
+
"timeout": 10
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
}
|
|
236
|
+
],
|
|
237
|
+
"FileChanged": [
|
|
238
|
+
{
|
|
239
|
+
"matcher": "",
|
|
240
|
+
"hooks": [
|
|
241
|
+
{
|
|
242
|
+
"type": "command",
|
|
243
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs FileChanged",
|
|
244
|
+
"async": true,
|
|
245
|
+
"timeout": 10
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
],
|
|
250
|
+
"WorktreeCreate": [
|
|
251
|
+
{
|
|
252
|
+
"matcher": "",
|
|
253
|
+
"hooks": [
|
|
254
|
+
{
|
|
255
|
+
"type": "command",
|
|
256
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs WorktreeCreate",
|
|
257
|
+
"async": true,
|
|
258
|
+
"timeout": 10
|
|
259
|
+
}
|
|
260
|
+
]
|
|
261
|
+
}
|
|
262
|
+
],
|
|
263
|
+
"WorktreeRemove": [
|
|
264
|
+
{
|
|
265
|
+
"matcher": "",
|
|
266
|
+
"hooks": [
|
|
267
|
+
{
|
|
268
|
+
"type": "command",
|
|
269
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs WorktreeRemove",
|
|
270
|
+
"async": true,
|
|
271
|
+
"timeout": 10
|
|
272
|
+
}
|
|
273
|
+
]
|
|
274
|
+
}
|
|
275
|
+
],
|
|
276
|
+
"PreCompact": [
|
|
277
|
+
{
|
|
278
|
+
"matcher": "",
|
|
279
|
+
"hooks": [
|
|
280
|
+
{
|
|
281
|
+
"type": "command",
|
|
282
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs PreCompact",
|
|
283
|
+
"async": true,
|
|
284
|
+
"timeout": 10
|
|
285
|
+
}
|
|
286
|
+
]
|
|
287
|
+
}
|
|
288
|
+
],
|
|
289
|
+
"PostCompact": [
|
|
290
|
+
{
|
|
291
|
+
"matcher": "",
|
|
292
|
+
"hooks": [
|
|
293
|
+
{
|
|
294
|
+
"type": "command",
|
|
295
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs PostCompact",
|
|
296
|
+
"async": true,
|
|
297
|
+
"timeout": 10
|
|
298
|
+
}
|
|
299
|
+
]
|
|
300
|
+
}
|
|
301
|
+
],
|
|
302
|
+
"Elicitation": [
|
|
303
|
+
{
|
|
304
|
+
"matcher": "",
|
|
305
|
+
"hooks": [
|
|
306
|
+
{
|
|
307
|
+
"type": "command",
|
|
308
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs Elicitation",
|
|
309
|
+
"async": true,
|
|
310
|
+
"timeout": 10
|
|
311
|
+
}
|
|
312
|
+
]
|
|
313
|
+
}
|
|
314
|
+
],
|
|
315
|
+
"ElicitationResult": [
|
|
316
|
+
{
|
|
317
|
+
"matcher": "",
|
|
318
|
+
"hooks": [
|
|
319
|
+
{
|
|
320
|
+
"type": "command",
|
|
321
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs ElicitationResult",
|
|
322
|
+
"async": true,
|
|
323
|
+
"timeout": 10
|
|
324
|
+
}
|
|
325
|
+
]
|
|
326
|
+
}
|
|
327
|
+
],
|
|
328
|
+
"SessionEnd": [
|
|
329
|
+
{
|
|
330
|
+
"matcher": "",
|
|
331
|
+
"hooks": [
|
|
332
|
+
{
|
|
333
|
+
"type": "command",
|
|
334
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/emit.mjs SessionEnd",
|
|
335
|
+
"async": true,
|
|
336
|
+
"timeout": 10
|
|
337
|
+
}
|
|
338
|
+
]
|
|
339
|
+
}
|
|
340
|
+
]
|
|
341
|
+
}
|
|
342
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -65,7 +65,8 @@ var TRACKED_ENV = [
|
|
|
65
65
|
{ key: "DEEPSEEK_API_KEY", label: "DeepSeek API key", secret: true },
|
|
66
66
|
{ key: "OPENROUTER_API_KEY", label: "OpenRouter API key", secret: true },
|
|
67
67
|
{ key: "OLLAMA_HOST", label: "Ollama host URL", secret: false },
|
|
68
|
-
{ key: "AGENT_TIER", label: "Default tier (opus | sonnet | haiku)", secret: false }
|
|
68
|
+
{ key: "AGENT_TIER", label: "Default tier (opus | sonnet | haiku)", secret: false },
|
|
69
|
+
{ key: "AGENT_MODEL", label: "Pinned model id (overrides tier)", secret: false }
|
|
69
70
|
];
|
|
70
71
|
function runConfigStatusCommand(input) {
|
|
71
72
|
const { config } = input;
|
|
@@ -172,6 +173,12 @@ import fs2 from "fs";
|
|
|
172
173
|
import path3 from "path";
|
|
173
174
|
import { fileURLToPath } from "url";
|
|
174
175
|
import { isCancel as isCancel2, select as select2, text as text2 } from "@clack/prompts";
|
|
176
|
+
|
|
177
|
+
// src/constants.ts
|
|
178
|
+
var DEFAULT_DASHBOARD_PORT = 3456;
|
|
179
|
+
var DEFAULT_DASHBOARD_URL = `http://localhost:${DEFAULT_DASHBOARD_PORT}`;
|
|
180
|
+
|
|
181
|
+
// src/commands/init.ts
|
|
175
182
|
var DIM3 = "\x1B[2m";
|
|
176
183
|
var BOLD3 = "\x1B[1m";
|
|
177
184
|
var GREEN2 = "\x1B[32m";
|
|
@@ -186,8 +193,7 @@ async function runInitCommand(opts) {
|
|
|
186
193
|
monorepoRoot = resolveMonorepoRoot();
|
|
187
194
|
if (!monorepoRoot) {
|
|
188
195
|
process.stderr.write(
|
|
189
|
-
|
|
190
|
-
`
|
|
196
|
+
"error: --link requires the CLI to be run from the agentic-patterns-ts source tree\n"
|
|
191
197
|
);
|
|
192
198
|
process.exit(1);
|
|
193
199
|
}
|
|
@@ -279,6 +285,25 @@ async function runInitCommand(opts) {
|
|
|
279
285
|
copyDir(pluginSrc.pluginDir, path3.join(targetDir, ".claude-plugin"));
|
|
280
286
|
copyDir(pluginSrc.hooksDir, path3.join(targetDir, "hooks"));
|
|
281
287
|
created.push(".claude-plugin/", "hooks/");
|
|
288
|
+
const settingsDir = path3.join(targetDir, ".claude");
|
|
289
|
+
const settingsPath = path3.join(settingsDir, "settings.json");
|
|
290
|
+
const hooksSource = fs2.readFileSync(path3.join(pluginSrc.hooksDir, "hooks.json"), "utf8");
|
|
291
|
+
const ourHooks = JSON.parse(
|
|
292
|
+
hooksSource.replaceAll("${CLAUDE_PLUGIN_ROOT}", "${CLAUDE_PROJECT_DIR}")
|
|
293
|
+
);
|
|
294
|
+
const mergeOutcome = mergeHookSettings(settingsPath, ourHooks);
|
|
295
|
+
fs2.mkdirSync(settingsDir, { recursive: true });
|
|
296
|
+
fs2.writeFileSync(settingsPath, `${JSON.stringify(mergeOutcome.merged, null, 2)}
|
|
297
|
+
`);
|
|
298
|
+
if (mergeOutcome.kind === "created") {
|
|
299
|
+
created.push(".claude/settings.json");
|
|
300
|
+
} else if (mergeOutcome.kind === "merged") {
|
|
301
|
+
created.push(
|
|
302
|
+
`.claude/settings.json ${DIM3}(merged ${mergeOutcome.added} hook entries)${RESET3}`
|
|
303
|
+
);
|
|
304
|
+
} else {
|
|
305
|
+
created.push(`.claude/settings.json ${DIM3}(already up to date)${RESET3}`);
|
|
306
|
+
}
|
|
282
307
|
} else {
|
|
283
308
|
pluginNote = `${YELLOW2}warning${RESET3}: --with-plugin requested but plugin source not found.
|
|
284
309
|
${DIM3}Run from the agentic-patterns-ts source tree, or wait for plugin packaging (Phase 2).${RESET3}`;
|
|
@@ -308,15 +333,17 @@ async function runInitCommand(opts) {
|
|
|
308
333
|
process.stdout.write(` cd ${rootRel}
|
|
309
334
|
`);
|
|
310
335
|
}
|
|
311
|
-
process.stdout.write(
|
|
312
|
-
`
|
|
336
|
+
process.stdout.write(
|
|
337
|
+
` bun install ${DIM3}# picks up the new example${RESET3}
|
|
338
|
+
`
|
|
339
|
+
);
|
|
313
340
|
process.stdout.write(` cd ${projRel}
|
|
314
341
|
`);
|
|
315
342
|
process.stdout.write(
|
|
316
343
|
` cp .env.example .env ${DIM3}# fill in your ${envKeyFor(provider)}${RESET3}
|
|
317
344
|
`
|
|
318
345
|
);
|
|
319
|
-
process.stdout.write(`
|
|
346
|
+
process.stdout.write(` bun run dev ${DIM3}# launch playground${RESET3}
|
|
320
347
|
|
|
321
348
|
`);
|
|
322
349
|
} else {
|
|
@@ -328,9 +355,8 @@ async function runInitCommand(opts) {
|
|
|
328
355
|
` cp .env.example .env ${DIM3}# fill in your ${envKeyFor(provider)}${RESET3}
|
|
329
356
|
`
|
|
330
357
|
);
|
|
331
|
-
process.stdout.write(
|
|
332
|
-
`
|
|
333
|
-
process.stdout.write(` pnpm dev ${DIM3}# launch playground${RESET3}
|
|
358
|
+
process.stdout.write(" bun install\n");
|
|
359
|
+
process.stdout.write(` bun run dev ${DIM3}# launch playground${RESET3}
|
|
334
360
|
|
|
335
361
|
`);
|
|
336
362
|
}
|
|
@@ -376,10 +402,15 @@ function renderPackageJson(name, provider, link) {
|
|
|
376
402
|
function renderEnvExample(provider) {
|
|
377
403
|
const lines = [
|
|
378
404
|
"# Dashboard URL \u2014 used by the Claude Code plugin to ship lifecycle events",
|
|
379
|
-
|
|
405
|
+
`AP_DASHBOARD_URL=${DEFAULT_DASHBOARD_URL}`,
|
|
380
406
|
"",
|
|
381
407
|
"# Default model tier \u2014 opus | sonnet | haiku (used by the agent runner)",
|
|
382
408
|
"AGENT_TIER=sonnet",
|
|
409
|
+
"",
|
|
410
|
+
"# Optional: pin an exact model id; wins over AGENT_TIER. Useful when",
|
|
411
|
+
"# your provider has a model the framework's tier map doesn't list",
|
|
412
|
+
"# (e.g. AGENT_MODEL=qwen3.6:27b for an Ollama box).",
|
|
413
|
+
"# AGENT_MODEL=",
|
|
383
414
|
""
|
|
384
415
|
];
|
|
385
416
|
if (provider === "anthropic") {
|
|
@@ -428,7 +459,7 @@ function renderAgent(provider) {
|
|
|
428
459
|
* file (via \`agents/**\\/agent.ts\`), builds a runner from your environment
|
|
429
460
|
* (using ${provider}), and wires it into the playground dashboard.
|
|
430
461
|
*
|
|
431
|
-
*
|
|
462
|
+
* bun run dev # launch the dashboard at http://localhost:3456
|
|
432
463
|
* ap run demo # chat with this agent in the terminal
|
|
433
464
|
*/
|
|
434
465
|
|
|
@@ -548,18 +579,83 @@ function writeFile(root, rel, contents, log) {
|
|
|
548
579
|
function copyDir(src, dest) {
|
|
549
580
|
fs2.cpSync(src, dest, { recursive: true });
|
|
550
581
|
}
|
|
582
|
+
function mergeHookSettings(settingsPath, ours) {
|
|
583
|
+
if (!fs2.existsSync(settingsPath)) {
|
|
584
|
+
return { kind: "created", merged: ours };
|
|
585
|
+
}
|
|
586
|
+
let existing;
|
|
587
|
+
try {
|
|
588
|
+
existing = JSON.parse(fs2.readFileSync(settingsPath, "utf8"));
|
|
589
|
+
} catch {
|
|
590
|
+
const backup = `${settingsPath}.ap-backup-${Date.now()}`;
|
|
591
|
+
fs2.renameSync(settingsPath, backup);
|
|
592
|
+
process.stdout.write(
|
|
593
|
+
`${YELLOW2}warning${RESET3}: existing .claude/settings.json was malformed; moved to ${path3.basename(backup)}
|
|
594
|
+
`
|
|
595
|
+
);
|
|
596
|
+
return { kind: "created", merged: ours };
|
|
597
|
+
}
|
|
598
|
+
const merged = { ...existing, hooks: { ...existing.hooks ?? {} } };
|
|
599
|
+
let added = 0;
|
|
600
|
+
for (const event of Object.keys(ours.hooks)) {
|
|
601
|
+
const ourMatchers = ours.hooks[event] ?? [];
|
|
602
|
+
const theirMatchers = merged.hooks[event] ?? [];
|
|
603
|
+
const result = [...theirMatchers];
|
|
604
|
+
for (const ourMatcher of ourMatchers) {
|
|
605
|
+
const theirMatcher = result.find((m) => m.matcher === ourMatcher.matcher);
|
|
606
|
+
if (!theirMatcher) {
|
|
607
|
+
result.push(ourMatcher);
|
|
608
|
+
added += ourMatcher.hooks.length;
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
for (const ourHook of ourMatcher.hooks) {
|
|
612
|
+
const duplicate = theirMatcher.hooks.some(
|
|
613
|
+
(h) => h.type === ourHook.type && h.command === ourHook.command
|
|
614
|
+
);
|
|
615
|
+
if (!duplicate) {
|
|
616
|
+
theirMatcher.hooks.push(ourHook);
|
|
617
|
+
added += 1;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
merged.hooks[event] = result;
|
|
622
|
+
}
|
|
623
|
+
if (added === 0) return { kind: "unchanged", merged };
|
|
624
|
+
return { kind: "merged", merged, added };
|
|
625
|
+
}
|
|
551
626
|
function resolvePluginSource() {
|
|
627
|
+
const here = path3.dirname(fileURLToPath(import.meta.url));
|
|
628
|
+
const bundledCandidates = [
|
|
629
|
+
path3.resolve(here, "../assets/plugin-template"),
|
|
630
|
+
path3.resolve(here, "../../assets/plugin-template")
|
|
631
|
+
];
|
|
632
|
+
for (const base of bundledCandidates) {
|
|
633
|
+
const pluginDir = path3.join(base, ".claude-plugin");
|
|
634
|
+
const hooksDir = path3.join(base, "hooks");
|
|
635
|
+
if (fs2.existsSync(pluginDir) && fs2.existsSync(hooksDir)) {
|
|
636
|
+
return { pluginDir, hooksDir };
|
|
637
|
+
}
|
|
638
|
+
}
|
|
552
639
|
const root = resolveMonorepoRoot();
|
|
553
|
-
if (
|
|
554
|
-
|
|
640
|
+
if (root) {
|
|
641
|
+
return { pluginDir: path3.join(root, ".claude-plugin"), hooksDir: path3.join(root, "hooks") };
|
|
642
|
+
}
|
|
643
|
+
return null;
|
|
555
644
|
}
|
|
556
645
|
function resolveMonorepoRoot() {
|
|
557
646
|
try {
|
|
558
647
|
const here = path3.dirname(fileURLToPath(import.meta.url));
|
|
559
648
|
let cur = here;
|
|
560
649
|
for (let i = 0; i < 8; i++) {
|
|
561
|
-
|
|
562
|
-
|
|
650
|
+
const rootPkgPath = path3.join(cur, "package.json");
|
|
651
|
+
if (fs2.existsSync(rootPkgPath) && fs2.existsSync(path3.join(cur, "packages", "agent-core"))) {
|
|
652
|
+
try {
|
|
653
|
+
const rootPkg = JSON.parse(fs2.readFileSync(rootPkgPath, "utf8"));
|
|
654
|
+
if (rootPkg.workspaces && rootPkg.workspaces.length > 0) {
|
|
655
|
+
return cur;
|
|
656
|
+
}
|
|
657
|
+
} catch {
|
|
658
|
+
}
|
|
563
659
|
}
|
|
564
660
|
const parent = path3.dirname(cur);
|
|
565
661
|
if (parent === cur) break;
|
|
@@ -587,7 +683,7 @@ import {
|
|
|
587
683
|
import { createServer } from "@agentic-patterns/server";
|
|
588
684
|
import { serve } from "@hono/node-server";
|
|
589
685
|
async function runPlaygroundCommand(opts) {
|
|
590
|
-
const port = opts.port ??
|
|
686
|
+
const port = opts.port ?? DEFAULT_DASHBOARD_PORT;
|
|
591
687
|
const shouldOpen = opts.open !== false;
|
|
592
688
|
const serveDashboard = opts.noDashboard !== true;
|
|
593
689
|
const eventBus = new AgentEventBus();
|
|
@@ -986,16 +1082,29 @@ function formatConfigRow(config) {
|
|
|
986
1082
|
return parts.join(" \xB7 ");
|
|
987
1083
|
}
|
|
988
1084
|
function detectRunnerFromEnv() {
|
|
1085
|
+
const pinned = process.env.AGENT_MODEL;
|
|
989
1086
|
if (process.env.ANTHROPIC_API_KEY) {
|
|
990
|
-
return {
|
|
1087
|
+
return {
|
|
1088
|
+
provider: "anthropic",
|
|
1089
|
+
detail: `env ANTHROPIC_API_KEY \u2192 ${pinned ?? "claude-sonnet-4-5"}${pinned ? " (AGENT_MODEL)" : ""}`
|
|
1090
|
+
};
|
|
991
1091
|
}
|
|
992
1092
|
if (process.env.OPENAI_API_KEY) {
|
|
993
|
-
return {
|
|
1093
|
+
return {
|
|
1094
|
+
provider: "openai",
|
|
1095
|
+
detail: `env OPENAI_API_KEY \u2192 ${pinned ?? "gpt-4o"}${pinned ? " (AGENT_MODEL)" : ""}`
|
|
1096
|
+
};
|
|
994
1097
|
}
|
|
995
1098
|
if (process.env.GOOGLE_GENERATIVE_AI_API_KEY || process.env.GOOGLE_API_KEY) {
|
|
996
|
-
return {
|
|
1099
|
+
return {
|
|
1100
|
+
provider: "google",
|
|
1101
|
+
detail: `env GOOGLE_*_API_KEY \u2192 ${pinned ?? "gemini-2.5-flash"}${pinned ? " (AGENT_MODEL)" : ""}`
|
|
1102
|
+
};
|
|
997
1103
|
}
|
|
998
1104
|
if (process.env.OLLAMA_HOST) {
|
|
1105
|
+
if (pinned) {
|
|
1106
|
+
return { provider: "ollama", detail: `env OLLAMA_HOST \u2192 ${pinned} (AGENT_MODEL)` };
|
|
1107
|
+
}
|
|
999
1108
|
const tier = process.env.AGENT_TIER ?? "sonnet";
|
|
1000
1109
|
const model = tier === "opus" ? "qwen3:30b-a3b" : tier === "haiku" ? "qwen3:4b" : "qwen3:14b";
|
|
1001
1110
|
return {
|
|
@@ -1051,7 +1160,8 @@ function resolveProjectConfig(from = process.cwd()) {
|
|
|
1051
1160
|
}
|
|
1052
1161
|
}
|
|
1053
1162
|
const agents = normalizeGlobs(manifest.agentic?.agents) ?? DEFAULT_AGENT_GLOBS;
|
|
1054
|
-
const
|
|
1163
|
+
const envPort = process.env.PORT;
|
|
1164
|
+
const port = manifest.agentic?.port ?? (envPort !== void 0 ? Number.parseInt(envPort, 10) : DEFAULT_DASHBOARD_PORT);
|
|
1055
1165
|
return { root, agents, port, hasManifest };
|
|
1056
1166
|
}
|
|
1057
1167
|
function normalizeGlobs(value) {
|
|
@@ -1159,7 +1269,7 @@ Commands:
|
|
|
1159
1269
|
|
|
1160
1270
|
Options:
|
|
1161
1271
|
-h, --help show this help
|
|
1162
|
-
--port <port> server port for playground (default
|
|
1272
|
+
--port <port> server port for playground (default 3456)
|
|
1163
1273
|
--no-dashboard playground without dashboard (API only)
|
|
1164
1274
|
--no-open don't auto-open the browser
|
|
1165
1275
|
--agents <glob> override agent discovery glob
|