@wellgrow/cli 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -50,9 +50,6 @@ var wellGrowConfigSchema = z.object({
50
50
  mcp: z.object({
51
51
  paths: z.array(z.string())
52
52
  }),
53
- hooks: z.object({
54
- paths: z.array(z.string())
55
- }),
56
53
  user: z.object({
57
54
  name: z.string().optional()
58
55
  }).optional(),
@@ -104,9 +101,6 @@ var DEFAULT_CONFIG = {
104
101
  mcp: {
105
102
  paths: []
106
103
  },
107
- hooks: {
108
- paths: []
109
- },
110
104
  logging: {
111
105
  verbose: false,
112
106
  log_dir: "~/.wellgrow/logs"
@@ -505,15 +499,9 @@ function updateToolPart(parts, toolCallId, state, output, errorText) {
505
499
  parts[idx] = { ...part, state, output, errorText };
506
500
  }
507
501
  }
508
- function approvalDecisionToToolDecision(ad) {
509
- if (ad.action === "allow") {
510
- return { decision: "allow" };
511
- }
512
- return { decision: "deny", reason: "\u30E6\u30FC\u30B6\u30FC\u304C\u30C4\u30FC\u30EB\u306E\u5B9F\u884C\u3092\u62D2\u5426\u3057\u307E\u3057\u305F" };
513
- }
514
502
  async function requestApproval(tc, meta, pipeline, callbacks) {
515
503
  if (!callbacks.onApprovalRequest) {
516
- return { decision: "allow" };
504
+ return true;
517
505
  }
518
506
  const decision = await callbacks.onApprovalRequest({
519
507
  toolCallId: tc.toolCallId,
@@ -530,7 +518,7 @@ async function requestApproval(tc, meta, pipeline, callbacks) {
530
518
  });
531
519
  }
532
520
  }
533
- return approvalDecisionToToolDecision(decision);
521
+ return decision.action === "allow";
534
522
  }
535
523
  function toToolOutput(value) {
536
524
  if (typeof value === "string") {
@@ -539,7 +527,7 @@ function toToolOutput(value) {
539
527
  return { type: "json", value };
540
528
  }
541
529
  async function executeSingleTool(tc, parts, config, callbacks, needsApproval) {
542
- const { registry, pipeline, executionHooks } = config;
530
+ const { registry, pipeline } = config;
543
531
  const handler = registry.handlers[tc.toolName];
544
532
  if (!handler) {
545
533
  const errorMsg = `\u4E0D\u660E\u306A\u30C4\u30FC\u30EB: ${tc.toolName}`;
@@ -552,72 +540,18 @@ async function executeSingleTool(tc, parts, config, callbacks, needsApproval) {
552
540
  output: { type: "error-text", value: `Error: ${errorMsg}` }
553
541
  };
554
542
  }
555
- const meta = registry.getMeta(tc.toolName);
556
- const metaInfo = {
557
- category: meta?.category ?? "execute",
558
- source: meta?.source ?? "builtin"
559
- };
560
- if (executionHooks?.beforeExecute) {
561
- const hookResult = await executionHooks.beforeExecute(tc, metaInfo);
562
- if (hookResult.decision === "deny") {
563
- updateToolPart(
564
- parts,
565
- tc.toolCallId,
566
- "output-denied",
567
- void 0,
568
- hookResult.reason
569
- );
570
- callbacks.onMessageUpdate([...parts]);
571
- return pipeline.createDeniedResult(
572
- tc.toolCallId,
573
- tc.toolName,
574
- hookResult.reason
575
- );
576
- }
577
- if (hookResult.decision === "ask") {
578
- needsApproval = true;
579
- }
580
- if (hookResult.decision === "allow" && hookResult.updatedInput) {
581
- tc = { ...tc, args: { ...tc.args, ...hookResult.updatedInput } };
582
- }
583
- }
584
543
  if (needsApproval) {
585
- let approved = false;
586
- if (executionHooks?.onPermissionRequest) {
587
- const hookDecision = await executionHooks.onPermissionRequest(
588
- tc,
589
- metaInfo
590
- );
591
- if (hookDecision) {
592
- if (hookDecision.behavior === "deny") {
593
- const reason = "\u30D5\u30C3\u30AF\u306B\u3088\u308A\u62D2\u5426\u3055\u308C\u307E\u3057\u305F";
594
- updateToolPart(parts, tc.toolCallId, "output-denied", void 0, reason);
595
- callbacks.onMessageUpdate([...parts]);
596
- return pipeline.createDeniedResult(tc.toolCallId, tc.toolName, reason);
597
- }
598
- if (hookDecision.updatedInput) {
599
- tc = { ...tc, args: { ...tc.args, ...hookDecision.updatedInput } };
600
- }
601
- approved = true;
602
- }
603
- }
544
+ const meta = registry.getMeta(tc.toolName);
545
+ const metaInfo = {
546
+ category: meta?.category ?? "execute",
547
+ source: meta?.source ?? "builtin"
548
+ };
549
+ const approved = await requestApproval(tc, metaInfo, pipeline, callbacks);
604
550
  if (!approved) {
605
- const approval = await requestApproval(tc, metaInfo, pipeline, callbacks);
606
- if (approval.decision === "deny") {
607
- updateToolPart(
608
- parts,
609
- tc.toolCallId,
610
- "output-denied",
611
- void 0,
612
- approval.reason
613
- );
614
- callbacks.onMessageUpdate([...parts]);
615
- return pipeline.createDeniedResult(
616
- tc.toolCallId,
617
- tc.toolName,
618
- approval.reason
619
- );
620
- }
551
+ const reason = "\u30E6\u30FC\u30B6\u30FC\u304C\u30C4\u30FC\u30EB\u306E\u5B9F\u884C\u3092\u62D2\u5426\u3057\u307E\u3057\u305F";
552
+ updateToolPart(parts, tc.toolCallId, "output-denied", void 0, reason);
553
+ callbacks.onMessageUpdate([...parts]);
554
+ return pipeline.createDeniedResult(tc.toolCallId, tc.toolName, reason);
621
555
  }
622
556
  }
623
557
  try {
@@ -631,6 +565,7 @@ async function executeSingleTool(tc, parts, config, callbacks, needsApproval) {
631
565
  abortSignal: config.abortSignal
632
566
  };
633
567
  const resultPromise = handler(tc.args, handlerCtx);
568
+ const meta = registry.getMeta(tc.toolName);
634
569
  const uiHooks = meta?.uiHooks;
635
570
  const startEvent = uiHooks?.onStart?.(tc.args, tc.toolCallId);
636
571
  if (startEvent && callbacks.onToolUIEvent) {
@@ -646,46 +581,21 @@ async function executeSingleTool(tc, parts, config, callbacks, needsApproval) {
646
581
  if (completeEvent && callbacks.onToolUIEvent) {
647
582
  callbacks.onToolUIEvent(completeEvent);
648
583
  }
649
- const afterResult = await executionHooks?.afterExecute?.(tc, {
650
- success: true,
651
- output: toolResult
652
- });
653
- const output = toToolOutput(toolResult);
654
- if (afterResult?.feedback) {
655
- const feedbackOutput = toToolOutput(
656
- `${typeof toolResult === "string" ? toolResult : JSON.stringify(toolResult)}
657
-
658
- [Hook feedback]: ${afterResult.feedback}`
659
- );
660
- return {
661
- type: "tool-result",
662
- toolCallId: tc.toolCallId,
663
- toolName: tc.toolName,
664
- output: feedbackOutput
665
- };
666
- }
667
584
  return {
668
585
  type: "tool-result",
669
586
  toolCallId: tc.toolCallId,
670
587
  toolName: tc.toolName,
671
- output
588
+ output: toToolOutput(toolResult)
672
589
  };
673
590
  } catch (error) {
674
591
  const errorMsg = formatToolError(error);
675
592
  updateToolPart(parts, tc.toolCallId, "output-error", void 0, errorMsg);
676
593
  callbacks.onMessageUpdate([...parts]);
677
- const afterResult = await executionHooks?.afterExecute?.(tc, {
678
- success: false,
679
- error: errorMsg
680
- });
681
- const errorOutput = afterResult?.feedback ? `${errorMsg}
682
-
683
- [Hook feedback]: ${afterResult.feedback}` : errorMsg;
684
594
  return {
685
595
  type: "tool-result",
686
596
  toolCallId: tc.toolCallId,
687
597
  toolName: tc.toolName,
688
- output: { type: "error-text", value: errorOutput }
598
+ output: { type: "error-text", value: errorMsg }
689
599
  };
690
600
  }
691
601
  }
@@ -749,17 +659,6 @@ async function runAgentLoop(messages, config, callbacks) {
749
659
  const turnResult = await executeTurn(messages, config, callbacks, parts);
750
660
  fullText += turnResult.text;
751
661
  if (turnResult.finishReason !== "tool-calls") {
752
- if (config.onStop) {
753
- const stopResult = await config.onStop(turnResult.text);
754
- if (stopResult.blocked && stopResult.reason) {
755
- messages.push({
756
- role: "user",
757
- content: stopResult.reason
758
- });
759
- turnsUsed++;
760
- continue;
761
- }
762
- }
763
662
  break;
764
663
  }
765
664
  const toolResults = await executeToolCalls(
@@ -1089,7 +988,7 @@ function createSessionContext() {
1089
988
  }
1090
989
 
1091
990
  // src/agents/resolver.ts
1092
- import { join as join10 } from "path";
991
+ import { join as join9 } from "path";
1093
992
 
1094
993
  // src/agents/loader.ts
1095
994
  import { readFile as readFile2, readdir } from "fs/promises";
@@ -1116,9 +1015,6 @@ var agentConfigSchema = z3.object({
1116
1015
  }).optional(),
1117
1016
  skills: z3.object({
1118
1017
  paths: z3.array(z3.string()).optional()
1119
- }).optional(),
1120
- hooks: z3.object({
1121
- paths: z3.array(z3.string()).optional()
1122
1018
  }).optional()
1123
1019
  });
1124
1020
 
@@ -1621,10 +1517,8 @@ var backgroundProcesses = /* @__PURE__ */ new Set();
1621
1517
  var isShuttingDown = false;
1622
1518
  var lastSigintTime = 0;
1623
1519
  var activeCtx = null;
1624
- var activeHookEngine = null;
1625
- function setActiveSession(ctx, hookEngine) {
1520
+ function setActiveSession(ctx) {
1626
1521
  activeCtx = ctx;
1627
- activeHookEngine = hookEngine;
1628
1522
  }
1629
1523
  function registerShutdownHandler(handler) {
1630
1524
  shutdownHandlers.push(handler);
@@ -1683,23 +1577,11 @@ async function cleanupMcpClients() {
1683
1577
  } catch {
1684
1578
  }
1685
1579
  }
1686
- async function fireSessionEndHook(reason) {
1687
- if (!activeHookEngine) return;
1688
- try {
1689
- await Promise.race([
1690
- activeHookEngine.fire("SessionEnd", { reason }),
1691
- new Promise((r) => setTimeout(r, 5e3))
1692
- ]);
1693
- } catch {
1694
- }
1695
- }
1696
1580
  async function gracefulShutdown(_reason) {
1697
1581
  if (isShuttingDown) {
1698
1582
  process.exit(1);
1699
1583
  }
1700
1584
  isShuttingDown = true;
1701
- const hookReason = _reason === "sigint" || _reason === "sigint_double" ? "prompt_input_exit" : "other";
1702
- await fireSessionEndHook(hookReason);
1703
1585
  for (const handler of shutdownHandlers) {
1704
1586
  try {
1705
1587
  await Promise.race([handler(), new Promise((r) => setTimeout(r, 5e3))]);
@@ -3099,455 +2981,6 @@ async function discoverSkills(paths) {
3099
2981
  return skills;
3100
2982
  }
3101
2983
 
3102
- // src/extensions/hook-engine.ts
3103
- import { readFile as readFile9, readdir as readdir4, stat as stat3 } from "fs/promises";
3104
- import { join as join9 } from "path";
3105
- import { homedir as homedir9 } from "os";
3106
- import { spawn as spawn2 } from "child_process";
3107
- import { z as z11 } from "zod";
3108
- import { generateText } from "ai";
3109
- var hookDefinitionSchema = z11.discriminatedUnion("type", [
3110
- z11.object({
3111
- type: z11.literal("command"),
3112
- command: z11.string(),
3113
- timeout: z11.number().positive().optional(),
3114
- async: z11.boolean().optional(),
3115
- statusMessage: z11.string().optional()
3116
- }),
3117
- z11.object({
3118
- type: z11.literal("prompt"),
3119
- prompt: z11.string(),
3120
- model: z11.string().optional(),
3121
- timeout: z11.number().positive().optional()
3122
- })
3123
- ]);
3124
- var hookGroupSchema = z11.object({
3125
- matcher: z11.string().optional(),
3126
- hooks: z11.array(hookDefinitionSchema)
3127
- });
3128
- var hooksFileConfigSchema = z11.object({
3129
- hooks: z11.record(z11.array(hookGroupSchema)).default({})
3130
- });
3131
- var DEFAULT_HOOK_MODEL = "claude-haiku-4-5-20251001";
3132
- var DEFAULT_COMMAND_TIMEOUT_SEC = 30;
3133
- var MAX_HOOK_OUTPUT_BYTES = 1024 * 1024;
3134
- function expandTilde2(p) {
3135
- return p.startsWith("~") ? join9(homedir9(), p.slice(1)) : p;
3136
- }
3137
- function hookKey(hook) {
3138
- switch (hook.type) {
3139
- case "command":
3140
- return `command:${hook.command}:async=${hook.async ?? false}`;
3141
- case "prompt":
3142
- return `prompt:${hook.prompt}:${hook.model ?? "default"}`;
3143
- }
3144
- }
3145
- function warnHook(message) {
3146
- process.stderr.write(`[hooks] ${message}
3147
- `);
3148
- }
3149
- async function loadHooksFile(filePath) {
3150
- let content;
3151
- try {
3152
- content = await readFile9(expandTilde2(filePath), "utf-8");
3153
- } catch {
3154
- return { hooks: {} };
3155
- }
3156
- let raw;
3157
- try {
3158
- raw = JSON.parse(content);
3159
- } catch (error) {
3160
- warnHook(
3161
- `${filePath} \u306EJSON\u89E3\u6790\u306B\u5931\u6557: ${error instanceof Error ? error.message : String(error)}`
3162
- );
3163
- return { hooks: {} };
3164
- }
3165
- const result = hooksFileConfigSchema.safeParse(raw);
3166
- if (!result.success) {
3167
- const issues = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
3168
- warnHook(`${filePath} \u306E\u30B9\u30AD\u30FC\u30DE\u304C\u4E0D\u6B63: ${issues}`);
3169
- return { hooks: {} };
3170
- }
3171
- return { hooks: result.data.hooks };
3172
- }
3173
- async function resolveHookFiles(pathInput) {
3174
- const resolvedPath = expandTilde2(pathInput);
3175
- let stats;
3176
- try {
3177
- stats = await stat3(resolvedPath);
3178
- } catch {
3179
- return [];
3180
- }
3181
- if (stats.isFile()) {
3182
- return [resolvedPath];
3183
- }
3184
- if (!stats.isDirectory()) {
3185
- return [];
3186
- }
3187
- let entries;
3188
- try {
3189
- entries = await readdir4(resolvedPath, { withFileTypes: true });
3190
- } catch {
3191
- return [];
3192
- }
3193
- return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => join9(resolvedPath, entry.name)).sort((a, b) => a.localeCompare(b));
3194
- }
3195
- function mergeHooksConfig(target, source) {
3196
- const events = Object.keys(source.hooks);
3197
- for (const event of events) {
3198
- const sourceGroups = source.hooks[event] ?? [];
3199
- const currentGroups = target.hooks[event] ?? [];
3200
- target.hooks[event] = [...currentGroups, ...sourceGroups];
3201
- }
3202
- }
3203
- async function loadHooksConfig(pathsOrGlobalPath, agentPath) {
3204
- const pathList = Array.isArray(pathsOrGlobalPath) ? pathsOrGlobalPath : [pathsOrGlobalPath, ...agentPath ? [agentPath] : []];
3205
- const merged = { hooks: {} };
3206
- for (const pathEntry of pathList) {
3207
- const files = await resolveHookFiles(pathEntry);
3208
- for (const filePath of files) {
3209
- const config = await loadHooksFile(filePath);
3210
- mergeHooksConfig(merged, config);
3211
- }
3212
- }
3213
- return merged;
3214
- }
3215
- var regexCache = /* @__PURE__ */ new Map();
3216
- function getCachedRegex(pattern) {
3217
- const cached = regexCache.get(pattern);
3218
- if (cached !== void 0) return cached;
3219
- try {
3220
- const regex = new RegExp(`^(?:${pattern})$`);
3221
- regexCache.set(pattern, regex);
3222
- return regex;
3223
- } catch {
3224
- regexCache.set(pattern, null);
3225
- return null;
3226
- }
3227
- }
3228
- function matchesTarget(matcher, target) {
3229
- if (!matcher || matcher === "" || matcher === "*") return true;
3230
- if (target === null) return true;
3231
- const regex = getCachedRegex(matcher);
3232
- if (regex) return regex.test(target);
3233
- return matcher === target;
3234
- }
3235
- function getMatchTarget(event, input) {
3236
- switch (event) {
3237
- case "PreToolUse":
3238
- case "PostToolUse":
3239
- case "PostToolUseFailure":
3240
- case "PermissionRequest":
3241
- return input.tool_name ?? null;
3242
- case "SessionStart":
3243
- return input.source ?? null;
3244
- case "SessionEnd":
3245
- return input.reason ?? null;
3246
- case "UserPromptSubmit":
3247
- case "Stop":
3248
- return null;
3249
- }
3250
- }
3251
- function executeCommandHook(hook, input) {
3252
- return new Promise((resolve5) => {
3253
- const timeoutMs = (hook.timeout ?? DEFAULT_COMMAND_TIMEOUT_SEC) * 1e3;
3254
- const command = expandTilde2(hook.command);
3255
- const child = spawn2("sh", ["-c", command], {
3256
- env: { ...process.env },
3257
- timeout: timeoutMs,
3258
- stdio: ["pipe", "pipe", "pipe"]
3259
- });
3260
- let stdout = "";
3261
- let stderr = "";
3262
- child.stdout.on("data", (data) => {
3263
- if (stdout.length < MAX_HOOK_OUTPUT_BYTES) {
3264
- stdout += data.toString();
3265
- }
3266
- });
3267
- child.stderr.on("data", (data) => {
3268
- if (stderr.length < MAX_HOOK_OUTPUT_BYTES) {
3269
- stderr += data.toString();
3270
- }
3271
- });
3272
- child.stdin.on("error", () => {
3273
- });
3274
- child.stdin.write(JSON.stringify(input));
3275
- child.stdin.end();
3276
- child.on("close", (code) => {
3277
- resolve5({ stdout, stderr, exitCode: code ?? 1 });
3278
- });
3279
- child.on("error", (err) => {
3280
- warnHook(`\u30B3\u30DE\u30F3\u30C9hook\u5B9F\u884C\u30A8\u30E9\u30FC (${hook.command}): ${err.message}`);
3281
- resolve5({ stdout, stderr, exitCode: 1 });
3282
- });
3283
- });
3284
- }
3285
- async function executeLLMHook(hook, input, resolveModel) {
3286
- const prompt = hook.prompt.replace(/\$ARGUMENTS/g, JSON.stringify(input));
3287
- const model = resolveModel(hook.model);
3288
- try {
3289
- const result = await generateText({
3290
- model,
3291
- prompt,
3292
- maxOutputTokens: 500,
3293
- ...hook.timeout ? { abortSignal: AbortSignal.timeout(hook.timeout * 1e3) } : {}
3294
- });
3295
- const text = result.text.trim();
3296
- try {
3297
- const parsed = JSON.parse(text);
3298
- return { ok: parsed.ok !== false, reason: parsed.reason };
3299
- } catch {
3300
- warnHook(
3301
- `Prompt hook\u306E\u30EC\u30B9\u30DD\u30F3\u30B9\u304CJSON\u5F62\u5F0F\u3067\u306F\u3042\u308A\u307E\u305B\u3093: ${text.slice(0, 200)}`
3302
- );
3303
- return { ok: true };
3304
- }
3305
- } catch (error) {
3306
- warnHook(
3307
- `Prompt hook\u5B9F\u884C\u30A8\u30E9\u30FC: ${error instanceof Error ? error.message : String(error)}`
3308
- );
3309
- return { ok: true };
3310
- }
3311
- }
3312
- function normalizeCommandOutput(event, stdout, stderr, exitCode) {
3313
- if (exitCode === 2) {
3314
- return {
3315
- blocked: true,
3316
- reason: stderr.trim() || "\u30D5\u30C3\u30AF\u306B\u3088\u308A\u30D6\u30ED\u30C3\u30AF\u3055\u308C\u307E\u3057\u305F",
3317
- feedback: stderr.trim() || void 0
3318
- };
3319
- }
3320
- if (exitCode !== 0) {
3321
- return { blocked: false };
3322
- }
3323
- const trimmed = stdout.trim();
3324
- let parsed;
3325
- try {
3326
- parsed = JSON.parse(trimmed);
3327
- } catch {
3328
- if (event === "SessionStart" && trimmed) {
3329
- return { blocked: false, additionalContext: trimmed };
3330
- }
3331
- return { blocked: false };
3332
- }
3333
- const hso = parsed.hookSpecificOutput;
3334
- switch (event) {
3335
- case "PreToolUse": {
3336
- const decision = hso?.permissionDecision;
3337
- if (decision === "deny") {
3338
- return {
3339
- blocked: true,
3340
- reason: hso?.permissionDecisionReason ?? "\u30D5\u30C3\u30AF\u306B\u3088\u308A\u30D6\u30ED\u30C3\u30AF\u3055\u308C\u307E\u3057\u305F"
3341
- };
3342
- }
3343
- if (decision === "ask") {
3344
- return { blocked: false, askUser: true };
3345
- }
3346
- return {
3347
- blocked: false,
3348
- updatedInput: hso?.updatedInput,
3349
- additionalContext: hso?.additionalContext
3350
- };
3351
- }
3352
- case "PermissionRequest": {
3353
- const decision = hso?.decision;
3354
- if (decision?.behavior === "deny") {
3355
- return { blocked: true, reason: "\u30D5\u30C3\u30AF\u306B\u3088\u308A\u62D2\u5426\u3055\u308C\u307E\u3057\u305F" };
3356
- }
3357
- if (decision?.behavior === "allow") {
3358
- return { blocked: false, updatedInput: decision.updatedInput };
3359
- }
3360
- return { blocked: false };
3361
- }
3362
- case "Stop": {
3363
- if (parsed.decision === "block") {
3364
- return {
3365
- blocked: true,
3366
- stopDecision: "block",
3367
- reason: parsed.reason ?? "\u30D5\u30C3\u30AF\u306B\u3088\u308A\u7D9A\u884C\u304C\u8981\u6C42\u3055\u308C\u307E\u3057\u305F"
3368
- };
3369
- }
3370
- return { blocked: false, stopDecision: "approve" };
3371
- }
3372
- case "SessionStart": {
3373
- const ctx = hso?.additionalContext ?? trimmed;
3374
- return { blocked: false, additionalContext: ctx || void 0 };
3375
- }
3376
- default:
3377
- return { blocked: false };
3378
- }
3379
- }
3380
- function normalizePromptOutput(ok, reason) {
3381
- if (!ok) {
3382
- return {
3383
- blocked: true,
3384
- reason: reason ?? "\u30D5\u30C3\u30AF\u306B\u3088\u308A\u30D6\u30ED\u30C3\u30AF\u3055\u308C\u307E\u3057\u305F"
3385
- };
3386
- }
3387
- return { blocked: false };
3388
- }
3389
- function mergeResults(results) {
3390
- const merged = { blocked: false };
3391
- for (const r of results) {
3392
- if (r.blocked) {
3393
- merged.blocked = true;
3394
- merged.reason = r.reason ?? merged.reason;
3395
- }
3396
- if (r.stopDecision) merged.stopDecision = r.stopDecision;
3397
- if (r.askUser) merged.askUser = true;
3398
- if (r.feedback) {
3399
- merged.feedback = merged.feedback ? `${merged.feedback}
3400
- ${r.feedback}` : r.feedback;
3401
- }
3402
- if (r.additionalContext) {
3403
- merged.additionalContext = merged.additionalContext ? `${merged.additionalContext}
3404
- ${r.additionalContext}` : r.additionalContext;
3405
- }
3406
- if (r.updatedInput) {
3407
- merged.updatedInput = { ...merged.updatedInput, ...r.updatedInput };
3408
- }
3409
- }
3410
- return merged;
3411
- }
3412
- var HookEngine = class {
3413
- hooksConfig;
3414
- options;
3415
- _hasHooks;
3416
- constructor(hooksConfig, options) {
3417
- this.hooksConfig = hooksConfig;
3418
- this.options = options;
3419
- this._hasHooks = Object.keys(hooksConfig.hooks).length > 0;
3420
- }
3421
- get hasHooks() {
3422
- return this._hasHooks;
3423
- }
3424
- async fire(event, eventInput = {}) {
3425
- const groups = this.hooksConfig.hooks[event];
3426
- if (!groups || groups.length === 0) {
3427
- return { blocked: false };
3428
- }
3429
- const input = {
3430
- session_id: this.options.sessionId,
3431
- cwd: this.options.cwd,
3432
- hook_event_name: event,
3433
- ...eventInput
3434
- };
3435
- const matchTarget = getMatchTarget(event, input);
3436
- const matchingGroups = groups.filter(
3437
- (g) => matchesTarget(g.matcher, matchTarget)
3438
- );
3439
- if (matchingGroups.length === 0) {
3440
- return { blocked: false };
3441
- }
3442
- const seen = /* @__PURE__ */ new Set();
3443
- const syncHooks = [];
3444
- const asyncHooks = [];
3445
- for (const group of matchingGroups) {
3446
- for (const hook of group.hooks) {
3447
- const key = hookKey(hook);
3448
- if (seen.has(key)) continue;
3449
- seen.add(key);
3450
- if (hook.type === "command" && hook.async) {
3451
- asyncHooks.push(hook);
3452
- } else {
3453
- syncHooks.push(hook);
3454
- }
3455
- }
3456
- }
3457
- for (const hook of asyncHooks) {
3458
- this.executeOne(hook, event, input).catch((error) => {
3459
- warnHook(
3460
- `\u975E\u540C\u671Fhook\u5B9F\u884C\u30A8\u30E9\u30FC (${hook.command}): ${error instanceof Error ? error.message : String(error)}`
3461
- );
3462
- });
3463
- }
3464
- if (syncHooks.length === 0) {
3465
- return { blocked: false };
3466
- }
3467
- const results = await Promise.all(
3468
- syncHooks.map((hook) => this.executeOne(hook, event, input))
3469
- );
3470
- return mergeResults(results);
3471
- }
3472
- // ----- Bridge to ToolExecutionHooks (agent-loop.ts) -----
3473
- createToolExecutionHooks() {
3474
- return {
3475
- beforeExecute: async (toolCall, _meta) => {
3476
- const result = await this.fire("PreToolUse", {
3477
- tool_name: toolCall.toolName,
3478
- tool_input: toolCall.args,
3479
- tool_use_id: toolCall.toolCallId
3480
- });
3481
- if (result.blocked) {
3482
- return {
3483
- decision: "deny",
3484
- reason: result.reason ?? "\u30D5\u30C3\u30AF\u306B\u3088\u308A\u30D6\u30ED\u30C3\u30AF\u3055\u308C\u307E\u3057\u305F"
3485
- };
3486
- }
3487
- if (result.askUser) {
3488
- return { decision: "ask" };
3489
- }
3490
- return {
3491
- decision: "allow",
3492
- updatedInput: result.updatedInput,
3493
- additionalContext: result.additionalContext
3494
- };
3495
- },
3496
- afterExecute: async (toolCall, result) => {
3497
- const event = result.success ? "PostToolUse" : "PostToolUseFailure";
3498
- const hookResult = await this.fire(event, {
3499
- tool_name: toolCall.toolName,
3500
- tool_input: toolCall.args,
3501
- tool_use_id: toolCall.toolCallId,
3502
- ...result.success ? { tool_response: result.output } : { error: result.error }
3503
- });
3504
- if (hookResult.feedback) {
3505
- return { feedback: hookResult.feedback };
3506
- }
3507
- },
3508
- onPermissionRequest: async (toolCall, _meta) => {
3509
- const result = await this.fire("PermissionRequest", {
3510
- tool_name: toolCall.toolName,
3511
- tool_input: toolCall.args
3512
- });
3513
- if (result.blocked) {
3514
- return { behavior: "deny" };
3515
- }
3516
- if (result.updatedInput !== void 0) {
3517
- return {
3518
- behavior: "allow",
3519
- updatedInput: result.updatedInput
3520
- };
3521
- }
3522
- return null;
3523
- }
3524
- };
3525
- }
3526
- // ----- Private helpers -----
3527
- async executeOne(hook, event, input) {
3528
- switch (hook.type) {
3529
- case "command": {
3530
- const { stdout, stderr, exitCode } = await executeCommandHook(
3531
- hook,
3532
- input
3533
- );
3534
- return normalizeCommandOutput(event, stdout, stderr, exitCode);
3535
- }
3536
- case "prompt": {
3537
- const { ok, reason } = await executeLLMHook(
3538
- hook,
3539
- input,
3540
- (id) => this.resolveModel(id)
3541
- );
3542
- return normalizePromptOutput(ok, reason);
3543
- }
3544
- }
3545
- }
3546
- resolveModel(modelId) {
3547
- return getModel(modelId ?? DEFAULT_HOOK_MODEL, this.options.config);
3548
- }
3549
- };
3550
-
3551
2984
  // src/agents/resolver.ts
3552
2985
  async function resolveAgent(options, sessionCtx) {
3553
2986
  const config = options.config ?? await loadConfig();
@@ -3576,7 +3009,7 @@ async function resolveAgent(options, sessionCtx) {
3576
3009
  environment
3577
3010
  });
3578
3011
  const skillPaths = [
3579
- join10(agentDir, "skills"),
3012
+ join9(agentDir, "skills"),
3580
3013
  ...config.skills.paths,
3581
3014
  ...agentConfig.skills?.paths ?? []
3582
3015
  ];
@@ -3620,19 +3053,6 @@ async function resolveAgent(options, sessionCtx) {
3620
3053
  mode,
3621
3054
  allowedMcps: config.permissions.allowed_mcps
3622
3055
  });
3623
- const hookPaths = [
3624
- ...config.hooks.paths,
3625
- ...agentConfig.hooks?.paths ?? []
3626
- ];
3627
- const hooksConfig = await loadHooksConfig(hookPaths);
3628
- let hookEngine = null;
3629
- if (Object.keys(hooksConfig.hooks).length > 0) {
3630
- hookEngine = new HookEngine(hooksConfig, {
3631
- sessionId: sessionCtx.sessionId,
3632
- cwd: sessionCtx.cwd,
3633
- config
3634
- });
3635
- }
3636
3056
  let finalSystemPrompt = systemPrompt;
3637
3057
  if (mcpFailures.length > 0) {
3638
3058
  finalSystemPrompt += `
@@ -3652,7 +3072,6 @@ ${mcpFailures.map((f) => `- ${f}`).join("\n")}
3652
3072
  registry,
3653
3073
  pipeline,
3654
3074
  mcpManager,
3655
- hookEngine,
3656
3075
  maxTurns
3657
3076
  };
3658
3077
  }
@@ -3672,19 +3091,9 @@ async function createSession(options) {
3672
3091
  },
3673
3092
  ctx
3674
3093
  );
3675
- if (agent.hookEngine) {
3676
- await agent.hookEngine.fire("SessionStart", {
3677
- source: options.sessionStartSource ?? "startup"
3678
- });
3679
- }
3680
3094
  return { ctx, messages: [], agent };
3681
3095
  }
3682
3096
  async function switchAgent(session, newAgentName, options) {
3683
- if (session.agent.hookEngine) {
3684
- await session.agent.hookEngine.fire("SessionEnd", {
3685
- reason: "agent_switch"
3686
- });
3687
- }
3688
3097
  if (session.agent.mcpManager) {
3689
3098
  await session.agent.mcpManager.disconnectAll();
3690
3099
  session.ctx.mcpManager = null;
@@ -3702,30 +3111,7 @@ async function switchAgent(session, newAgentName, options) {
3702
3111
  );
3703
3112
  }
3704
3113
  async function sendMessage(session, userMessage, callbacks, options) {
3705
- const hookEngine = session.agent.hookEngine;
3706
- if (hookEngine) {
3707
- const submitResult = await hookEngine.fire("UserPromptSubmit", {
3708
- prompt: userMessage
3709
- });
3710
- if (submitResult.blocked) {
3711
- return {
3712
- fullText: submitResult.reason ?? "",
3713
- parts: []
3714
- };
3715
- }
3716
- }
3717
3114
  session.messages.push({ role: "user", content: userMessage });
3718
- const executionHooks = hookEngine?.createToolExecutionHooks();
3719
- const onStop = hookEngine ? async (lastMessage) => {
3720
- const result = await hookEngine.fire("Stop", {
3721
- stop_hook_active: true,
3722
- last_assistant_message: lastMessage
3723
- });
3724
- return {
3725
- blocked: result.blocked,
3726
- reason: result.reason
3727
- };
3728
- } : void 0;
3729
3115
  return runAgentLoop(
3730
3116
  session.messages,
3731
3117
  {
@@ -3733,8 +3119,6 @@ async function sendMessage(session, userMessage, callbacks, options) {
3733
3119
  system: session.agent.systemPrompt,
3734
3120
  registry: session.agent.registry,
3735
3121
  pipeline: session.agent.pipeline,
3736
- executionHooks,
3737
- onStop,
3738
3122
  abortSignal: options?.abortSignal,
3739
3123
  maxTurns: options?.maxTurns ?? session.agent.maxTurns,
3740
3124
  maxRetries: options?.maxRetries,
@@ -3747,16 +3131,16 @@ async function sendMessage(session, userMessage, callbacks, options) {
3747
3131
  }
3748
3132
 
3749
3133
  // src/core/history.ts
3750
- import { appendFile as appendFile3, readFile as readFile10, mkdir as mkdir6, readdir as readdir5 } from "fs/promises";
3751
- import { join as join11 } from "path";
3752
- import { homedir as homedir10 } from "os";
3134
+ import { appendFile as appendFile3, readFile as readFile9, mkdir as mkdir6, readdir as readdir4 } from "fs/promises";
3135
+ import { join as join10 } from "path";
3136
+ import { homedir as homedir9 } from "os";
3753
3137
  import { randomUUID as randomUUID3 } from "crypto";
3754
- var WELLGROW_DIR = join11(homedir10(), ".wellgrow");
3755
- var HISTORY_FILE = join11(WELLGROW_DIR, "history.jsonl");
3138
+ var WELLGROW_DIR = join10(homedir9(), ".wellgrow");
3139
+ var HISTORY_FILE = join10(WELLGROW_DIR, "history.jsonl");
3756
3140
  async function createSessionRecorder(model, agent) {
3757
3141
  const sessionId = randomUUID3();
3758
3142
  const now = /* @__PURE__ */ new Date();
3759
- const dateDir = join11(
3143
+ const dateDir = join10(
3760
3144
  WELLGROW_DIR,
3761
3145
  "sessions",
3762
3146
  String(now.getFullYear()),
@@ -3764,7 +3148,7 @@ async function createSessionRecorder(model, agent) {
3764
3148
  String(now.getDate()).padStart(2, "0")
3765
3149
  );
3766
3150
  await mkdir6(dateDir, { recursive: true });
3767
- const sessionFile = join11(dateDir, `${sessionId}.jsonl`);
3151
+ const sessionFile = join10(dateDir, `${sessionId}.jsonl`);
3768
3152
  let firstUserMessage = "";
3769
3153
  const meta = JSON.stringify({
3770
3154
  type: "meta",
@@ -3810,7 +3194,7 @@ async function createSessionRecorder(model, agent) {
3810
3194
  }
3811
3195
  async function listHistory(limit = 20) {
3812
3196
  try {
3813
- const content = await readFile10(HISTORY_FILE, "utf-8");
3197
+ const content = await readFile9(HISTORY_FILE, "utf-8");
3814
3198
  const lines = content.trim().split("\n").filter(Boolean);
3815
3199
  return lines.map((line) => JSON.parse(line)).reverse().slice(0, limit);
3816
3200
  } catch {
@@ -3818,18 +3202,18 @@ async function listHistory(limit = 20) {
3818
3202
  }
3819
3203
  }
3820
3204
  async function getSessionContent(sessionId) {
3821
- const sessionsDir = join11(WELLGROW_DIR, "sessions");
3205
+ const sessionsDir = join10(WELLGROW_DIR, "sessions");
3822
3206
  try {
3823
- const years = await readdir5(sessionsDir);
3207
+ const years = await readdir4(sessionsDir);
3824
3208
  for (const year of years) {
3825
- const months = await readdir5(join11(sessionsDir, year));
3209
+ const months = await readdir4(join10(sessionsDir, year));
3826
3210
  for (const month of months) {
3827
- const days = await readdir5(join11(sessionsDir, year, month));
3211
+ const days = await readdir4(join10(sessionsDir, year, month));
3828
3212
  for (const day of days) {
3829
- const files = await readdir5(join11(sessionsDir, year, month, day));
3213
+ const files = await readdir4(join10(sessionsDir, year, month, day));
3830
3214
  const match = files.find((f) => f.startsWith(sessionId));
3831
3215
  if (match) {
3832
- return readFile10(join11(sessionsDir, year, month, day, match), "utf-8");
3216
+ return readFile9(join10(sessionsDir, year, month, day, match), "utf-8");
3833
3217
  }
3834
3218
  }
3835
3219
  }
@@ -4522,7 +3906,7 @@ function useChatSession({
4522
3906
  setCurrentAgentName(s.agent.name);
4523
3907
  setCurrentAgentIcon(s.agent.icon);
4524
3908
  setAgents(agentList);
4525
- setActiveSession(s.ctx, s.agent.hookEngine);
3909
+ setActiveSession(s.ctx);
4526
3910
  registerShutdownHandler(async () => {
4527
3911
  if (recorderRef.current) {
4528
3912
  await recorderRef.current.finalize(messageCountRef.current);
@@ -4580,7 +3964,7 @@ function useChatSession({
4580
3964
  setCurrentModelName(getModelDisplayName(session.agent.modelId));
4581
3965
  setCurrentAgentName(session.agent.name);
4582
3966
  setCurrentAgentIcon(session.agent.icon);
4583
- setActiveSession(session.ctx, session.agent.hookEngine);
3967
+ setActiveSession(session.ctx);
4584
3968
  recorderRef.current = await createSessionRecorder(
4585
3969
  getModelDisplayName(session.agent.modelId),
4586
3970
  newAgentName
@@ -4602,9 +3986,6 @@ function useChatSession({
4602
3986
  const resetSession = useCallback2(async () => {
4603
3987
  const session = sessionRef.current;
4604
3988
  if (!session) return;
4605
- if (session.agent.hookEngine) {
4606
- await session.agent.hookEngine.fire("SessionEnd", { reason: "clear" });
4607
- }
4608
3989
  if (recorderRef.current) {
4609
3990
  await recorderRef.current.finalize(messageCountRef.current);
4610
3991
  }
@@ -4612,8 +3993,7 @@ function useChatSession({
4612
3993
  const newSession = await createSession({
4613
3994
  agentName: currentAgentId,
4614
3995
  modelOverride,
4615
- modeOverride: mode,
4616
- sessionStartSource: "clear"
3996
+ modeOverride: mode
4617
3997
  });
4618
3998
  if (verbose) {
4619
3999
  newSession.ctx.logFile = await initLogger(
@@ -4626,7 +4006,7 @@ function useChatSession({
4626
4006
  setCurrentModelName(getModelDisplayName(newSession.agent.modelId));
4627
4007
  setCurrentAgentName(newSession.agent.name);
4628
4008
  setCurrentAgentIcon(newSession.agent.icon);
4629
- setActiveSession(newSession.ctx, newSession.agent.hookEngine);
4009
+ setActiveSession(newSession.ctx);
4630
4010
  recorderRef.current = await createSessionRecorder(
4631
4011
  getModelDisplayName(newSession.agent.modelId),
4632
4012
  currentAgentId
@@ -4815,10 +4195,6 @@ function useAskUserQueue(getAskUserState) {
4815
4195
  async function handleSlashCommand(text, ctx) {
4816
4196
  if (text === "exit" || text === "quit") {
4817
4197
  ctx.cancelAllAskUser();
4818
- if (ctx.session.agent.hookEngine) {
4819
- await ctx.session.agent.hookEngine.fire("SessionEnd", { reason: "prompt_input_exit" }).catch(() => {
4820
- });
4821
- }
4822
4198
  if (ctx.recorder) {
4823
4199
  await ctx.recorder.finalize(ctx.messageCountRef.current);
4824
4200
  }
@@ -5413,22 +4789,22 @@ function registerDoctorCommand(program2) {
5413
4789
 
5414
4790
  // src/commands/skills.ts
5415
4791
  import {
5416
- readFile as readFile11,
5417
- readdir as readdir6,
4792
+ readFile as readFile10,
4793
+ readdir as readdir5,
5418
4794
  mkdir as mkdir7,
5419
4795
  cp,
5420
4796
  rm as rm2,
5421
4797
  mkdtemp,
5422
- stat as stat4
4798
+ stat as stat3
5423
4799
  } from "fs/promises";
5424
- import { join as join12, resolve as resolve4 } from "path";
5425
- import { homedir as homedir11, tmpdir } from "os";
4800
+ import { join as join11, resolve as resolve4 } from "path";
4801
+ import { homedir as homedir10, tmpdir } from "os";
5426
4802
  import { execFile as execFile4 } from "child_process";
5427
4803
  import { promisify as promisify4 } from "util";
5428
4804
  import matter2 from "gray-matter";
5429
4805
  var execFileAsync4 = promisify4(execFile4);
5430
- var WELLGROW_HOME2 = join12(homedir11(), ".wellgrow");
5431
- var SKILLS_DIR = join12(WELLGROW_HOME2, "skills");
4806
+ var WELLGROW_HOME2 = join11(homedir10(), ".wellgrow");
4807
+ var SKILLS_DIR = join11(WELLGROW_HOME2, "skills");
5432
4808
  var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".github", ".husky"]);
5433
4809
  function parseSource(source) {
5434
4810
  if (source.startsWith("./") || source.startsWith("../")) {
@@ -5457,7 +4833,7 @@ async function discoverSkillsInRepo(dir) {
5457
4833
  async function scan(currentDir, depth) {
5458
4834
  if (depth > 3) return;
5459
4835
  try {
5460
- const content = await readFile11(join12(currentDir, "SKILL.md"), "utf-8");
4836
+ const content = await readFile10(join11(currentDir, "SKILL.md"), "utf-8");
5461
4837
  const { data } = matter2(content);
5462
4838
  if (data.name && data.description && !seen.has(data.name)) {
5463
4839
  seen.add(data.name);
@@ -5471,10 +4847,10 @@ async function discoverSkillsInRepo(dir) {
5471
4847
  } catch {
5472
4848
  }
5473
4849
  try {
5474
- const entries = await readdir6(currentDir, { withFileTypes: true });
4850
+ const entries = await readdir5(currentDir, { withFileTypes: true });
5475
4851
  for (const entry of entries) {
5476
4852
  if (!entry.isDirectory() || SKIP_DIRS.has(entry.name)) continue;
5477
- await scan(join12(currentDir, entry.name), depth + 1);
4853
+ await scan(join11(currentDir, entry.name), depth + 1);
5478
4854
  }
5479
4855
  } catch {
5480
4856
  }
@@ -5487,7 +4863,7 @@ async function fetchSource(source) {
5487
4863
  if (!parsed.url.startsWith("https://") && !parsed.url.startsWith("git@")) {
5488
4864
  return { dir: parsed.url, isTemp: false };
5489
4865
  }
5490
- const tmpDir = await mkdtemp(join12(tmpdir(), "wellgrow-skills-"));
4866
+ const tmpDir = await mkdtemp(join11(tmpdir(), "wellgrow-skills-"));
5491
4867
  const args = ["clone", "--depth", "1"];
5492
4868
  if (parsed.ref) {
5493
4869
  args.push("--branch", parsed.ref);
@@ -5559,7 +4935,7 @@ ${toInstall.length} \u500B\u306E\u30B9\u30AD\u30EB\u3092\u30A4\u30F3\u30B9\u30C8
5559
4935
  `
5560
4936
  );
5561
4937
  for (const skill of toInstall) {
5562
- const dest = join12(SKILLS_DIR, skill.name);
4938
+ const dest = join11(SKILLS_DIR, skill.name);
5563
4939
  await cp(skill.sourceDir, dest, { recursive: true, force: true });
5564
4940
  process.stdout.write(` \u2713 ${skill.name}
5565
4941
  `);
@@ -5587,7 +4963,7 @@ ${toInstall.length} \u500B\u306E\u30B9\u30AD\u30EB\u3092\u30A4\u30F3\u30B9\u30C8
5587
4963
  skills.command("list").alias("ls").description("\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u6E08\u307F\u30B9\u30AD\u30EB\u3092\u4E00\u89A7\u8868\u793A").action(async () => {
5588
4964
  let dirs;
5589
4965
  try {
5590
- const entries = await readdir6(SKILLS_DIR, { withFileTypes: true });
4966
+ const entries = await readdir5(SKILLS_DIR, { withFileTypes: true });
5591
4967
  dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
5592
4968
  } catch {
5593
4969
  dirs = [];
@@ -5602,8 +4978,8 @@ ${dirs.length} \u500B\u306E\u30B9\u30AD\u30EB (${SKILLS_DIR}):
5602
4978
  `);
5603
4979
  for (const name of dirs) {
5604
4980
  try {
5605
- const content = await readFile11(
5606
- join12(SKILLS_DIR, name, "SKILL.md"),
4981
+ const content = await readFile10(
4982
+ join11(SKILLS_DIR, name, "SKILL.md"),
5607
4983
  "utf-8"
5608
4984
  );
5609
4985
  const { data } = matter2(content);
@@ -5622,9 +4998,9 @@ ${dirs.length} \u500B\u306E\u30B9\u30AD\u30EB (${SKILLS_DIR}):
5622
4998
  }
5623
4999
  });
5624
5000
  skills.command("remove <name>").alias("rm").description("\u30B9\u30AD\u30EB\u3092\u524A\u9664").action(async (name) => {
5625
- const skillDir = join12(SKILLS_DIR, name);
5001
+ const skillDir = join11(SKILLS_DIR, name);
5626
5002
  try {
5627
- await stat4(skillDir);
5003
+ await stat3(skillDir);
5628
5004
  } catch {
5629
5005
  process.stderr.write(`\u30B9\u30AD\u30EB "${name}" \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002
5630
5006
  `);
@@ -5969,7 +5345,7 @@ Error: ${formatErrorMessage(error)}
5969
5345
  }
5970
5346
  }
5971
5347
  var program = new Command();
5972
- program.name("wellgrow").description("WellGrow CLI \u2014 AI chat assistant").version("0.1.3").argument("[prompt]", "\u30EF\u30F3\u30B7\u30E7\u30C3\u30C8\u8CEA\u554F\uFF08\u7701\u7565\u3067\u30A4\u30F3\u30BF\u30E9\u30AF\u30C6\u30A3\u30D6\u30E2\u30FC\u30C9\uFF09").option("--model <model>", "\u4F7F\u7528\u3059\u308B\u30E2\u30C7\u30EB").option("-a, --agent <agent>", "\u4F7F\u7528\u3059\u308B\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8").option(
5348
+ program.name("wellgrow").description("WellGrow CLI \u2014 AI chat assistant").version("0.2.0").argument("[prompt]", "\u30EF\u30F3\u30B7\u30E7\u30C3\u30C8\u8CEA\u554F\uFF08\u7701\u7565\u3067\u30A4\u30F3\u30BF\u30E9\u30AF\u30C6\u30A3\u30D6\u30E2\u30FC\u30C9\uFF09").option("--model <model>", "\u4F7F\u7528\u3059\u308B\u30E2\u30C7\u30EB").option("-a, --agent <agent>", "\u4F7F\u7528\u3059\u308B\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8").option(
5973
5349
  "--mode <mode>",
5974
5350
  "\u30E2\u30FC\u30C9 (plan, auto)"
5975
5351
  ).option("--verbose", "\u8A73\u7D30\u30ED\u30B0\u3092\u51FA\u529B").option("-p, --pipe", "\u30D1\u30A4\u30D7\u5165\u529B\u30E2\u30FC\u30C9\uFF08stdin\u304B\u3089\u306E\u5165\u529B\u3092\u53D7\u3051\u4ED8\u3051\u308B\uFF09").action(async (prompt, opts) => {