@cjhyy/code-shell 0.2.0 → 0.3.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.
Files changed (118) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/README.md +35 -2
  3. package/dist/anthropic-DJXVKFMJ.js +1 -0
  4. package/dist/arena/index.js +1 -1
  5. package/dist/arena-MRBATZFD.js +3 -0
  6. package/dist/{chunk-EU3J7TBP.js → chunk-5QIVXFCT.js} +1 -1
  7. package/dist/{chunk-XXCZE2U2.js → chunk-AFMP73YP.js} +1 -1
  8. package/dist/{chunk-L7B2ORID.js → chunk-D3TVASD2.js} +2 -2
  9. package/dist/chunk-DI26OUIU.js +2 -0
  10. package/dist/chunk-FZXY554D.js +2 -0
  11. package/dist/chunk-GGVAOPI7.js +1 -0
  12. package/dist/chunk-GKAQH2DG.js +2 -0
  13. package/dist/chunk-GYNO4R7R.js +2 -0
  14. package/dist/{chunk-RERBX75D.js → chunk-K4HILJ6G.js} +3 -3
  15. package/dist/{chunk-2EUIMKJA.js → chunk-OWBYLMDD.js} +1 -1
  16. package/dist/chunk-SKIX2M5Z.js +4 -0
  17. package/dist/chunk-SV7H4FKT.js +3 -0
  18. package/dist/{chunk-2GJ7H5NC.js → chunk-WONQHE4P.js} +40 -40
  19. package/dist/{chunk-DEYFK6FP.js → chunk-X2S7ASQ2.js} +1 -1
  20. package/dist/chunk-Z5HKHAO7.js +336 -0
  21. package/dist/cli/commands/builtin/plugin-commands-registration.d.ts +12 -0
  22. package/dist/cli/commands/builtin/plugin-handler.d.ts +6 -0
  23. package/dist/cli/commands/registry.d.ts +9 -2
  24. package/dist/cli/main.js +138 -122
  25. package/dist/client-base-GSFXPP2Y.js +1 -0
  26. package/dist/devtools-FBX4HVVY.js +1 -0
  27. package/dist/engine/engine.d.ts +29 -0
  28. package/dist/engine/turn-loop.d.ts +25 -0
  29. package/dist/hooks/events.d.ts +84 -5
  30. package/dist/hooks/inject.d.ts +19 -0
  31. package/dist/hooks/shell-runner.d.ts +45 -0
  32. package/dist/index.d.ts +3 -2
  33. package/dist/index.js +1 -1
  34. package/dist/llm/api-key-sanitize.d.ts +22 -0
  35. package/dist/llm/providers/openai.d.ts +23 -0
  36. package/dist/llm/retry.d.ts +12 -0
  37. package/dist/llm/stream-watchdog.d.ts +42 -0
  38. package/dist/logging/logger.d.ts +14 -0
  39. package/dist/manager-4XKLZHKE.js +1 -0
  40. package/dist/mcp-manager-3JGHEJO3.js +1 -0
  41. package/dist/openai-OQ6LLN5J.js +1 -0
  42. package/dist/{openrouter-models-GZVCRWD2.js → openrouter-models-ZT4R2AE3.js} +1 -1
  43. package/dist/openrouter-sync-ARMVWY7S.js +1 -0
  44. package/dist/plugin-handler-GJISMH5Q.js +11 -0
  45. package/dist/plugins/gitOps.d.ts +23 -0
  46. package/dist/plugins/installedPlugins.d.ts +15 -0
  47. package/dist/plugins/knownMarketplaces.d.ts +10 -0
  48. package/dist/plugins/loadPluginHooks.d.ts +52 -0
  49. package/dist/plugins/marketplaceManager.d.ts +49 -0
  50. package/dist/plugins/parseMarketplaceInput.d.ts +16 -0
  51. package/dist/plugins/pluginCommandHook.d.ts +54 -0
  52. package/dist/plugins/pluginCommandsLoader.d.ts +25 -0
  53. package/dist/plugins/pluginInstaller.d.ts +36 -0
  54. package/dist/plugins/schemas.d.ts +9 -0
  55. package/dist/plugins/types.d.ts +73 -2
  56. package/dist/plugins/varRewrite.d.ts +42 -0
  57. package/dist/product/index.js +1 -1
  58. package/dist/protocol/helpers.d.ts +42 -0
  59. package/dist/render/clearTerminal.d.ts +9 -3
  60. package/dist/render/components/App.d.ts +1 -0
  61. package/dist/render/components/Static.d.ts +23 -0
  62. package/dist/render/devtools.d.ts +8 -4
  63. package/dist/render/dom.d.ts +2 -1
  64. package/dist/render/frame.d.ts +1 -0
  65. package/dist/render/index.d.ts +10 -0
  66. package/dist/render/ink.d.ts +24 -2
  67. package/dist/render/log-update.d.ts +12 -1
  68. package/dist/render/selection.d.ts +1 -1
  69. package/dist/run/index.js +1 -1
  70. package/dist/services/index.d.ts +1 -0
  71. package/dist/services/memory-orchestrator.d.ts +39 -0
  72. package/dist/settings/schema.d.ts +93 -6
  73. package/dist/skills/frontmatter.d.ts +13 -0
  74. package/dist/skills/index.d.ts +4 -4
  75. package/dist/skills/scanner.d.ts +11 -21
  76. package/dist/tool-summary-HJX6RHG4.js +2 -0
  77. package/dist/tool-system/builtin/agent-notifications.d.ts +55 -0
  78. package/dist/tool-system/builtin/agent-registry.d.ts +35 -4
  79. package/dist/tool-system/builtin/agent-transcript-translator.d.ts +22 -0
  80. package/dist/tool-system/builtin/skill-prompt.d.ts +7 -0
  81. package/dist/tool-system/builtin/skill.d.ts +3 -1
  82. package/dist/tool-system/context.d.ts +23 -0
  83. package/dist/tool-system/executor.d.ts +1 -1
  84. package/dist/tool-system/permission.d.ts +1 -1
  85. package/dist/types.d.ts +35 -0
  86. package/dist/ui/components/AgentBlock.d.ts +5 -2
  87. package/dist/ui/components/AgentDock.d.ts +36 -0
  88. package/dist/ui/components/CommandInput.d.ts +12 -1
  89. package/dist/ui/components/MessageRow.d.ts +54 -0
  90. package/dist/ui/components/SpinnerWithVerb.d.ts +2 -1
  91. package/dist/ui/components/TextInput.d.ts +9 -1
  92. package/dist/ui/components/VirtualMessageList.d.ts +30 -11
  93. package/dist/ui/dev-seed.d.ts +1 -0
  94. package/dist/ui/fullscreen-mode.d.ts +9 -0
  95. package/dist/ui/hooks/useVirtualScroll.d.ts +101 -0
  96. package/dist/ui/perf-probes.d.ts +22 -0
  97. package/dist/ui/query-guard.d.ts +35 -0
  98. package/dist/ui/slice-anchor.d.ts +44 -0
  99. package/dist/ui/store.d.ts +26 -1
  100. package/package.json +5 -3
  101. package/dist/anthropic-OVRWAZRD.js +0 -1
  102. package/dist/arena-64T2EQD4.js +0 -3
  103. package/dist/chunk-A4M2Y42L.js +0 -2
  104. package/dist/chunk-DTPABBSV.js +0 -2
  105. package/dist/chunk-MGP35GKD.js +0 -254
  106. package/dist/chunk-Q3RSX66I.js +0 -1
  107. package/dist/chunk-VKKU4LOM.js +0 -3
  108. package/dist/chunk-ZSAONDT6.js +0 -2
  109. package/dist/client-base-F5FCRSB3.js +0 -1
  110. package/dist/devtools-SGDUOOM4.js +0 -1
  111. package/dist/manager-J6FXEUEK.js +0 -1
  112. package/dist/mcp-manager-27EVBKRT.js +0 -1
  113. package/dist/openai-3QEADSYL.js +0 -1
  114. package/dist/openrouter-sync-WG4AG5NC.js +0 -1
  115. package/dist/plugins/loader.d.ts +0 -72
  116. package/dist/skills/matcher.d.ts +0 -21
  117. package/dist/tool-summary-V2B3MD5N.js +0 -2
  118. package/skills-builtin/codeshell-help.md +0 -135
@@ -186,21 +186,21 @@ export declare const SettingsSchema: z.ZodObject<{
186
186
  argsPattern?: Record<string, string> | undefined;
187
187
  }>, "many">>;
188
188
  }, "strip", z.ZodTypeAny, {
189
+ defaultMode: "default" | "acceptEdits" | "dontAsk" | "bypassPermissions" | "auto" | "plan";
189
190
  rules: {
190
191
  tool: string;
191
192
  decision: "allow" | "deny" | "ask";
192
193
  reason?: string | undefined;
193
194
  argsPattern?: Record<string, string> | undefined;
194
195
  }[];
195
- defaultMode: "default" | "acceptEdits" | "dontAsk" | "bypassPermissions" | "auto" | "plan";
196
196
  }, {
197
+ defaultMode?: "default" | "acceptEdits" | "dontAsk" | "bypassPermissions" | "auto" | "plan" | undefined;
197
198
  rules?: {
198
199
  tool: string;
199
200
  decision: "allow" | "deny" | "ask";
200
201
  reason?: string | undefined;
201
202
  argsPattern?: Record<string, string> | undefined;
202
203
  }[] | undefined;
203
- defaultMode?: "default" | "acceptEdits" | "dontAsk" | "bypassPermissions" | "auto" | "plan" | undefined;
204
204
  }>>;
205
205
  context: z.ZodDefault<z.ZodObject<{
206
206
  maxTokens: z.ZodDefault<z.ZodNumber>;
@@ -345,6 +345,35 @@ export declare const SettingsSchema: z.ZodObject<{
345
345
  deniedReads?: string[] | undefined;
346
346
  network?: "allow" | "deny" | undefined;
347
347
  }>>;
348
+ /**
349
+ * Shell-hook configuration. Each entry binds a HookEventName to a
350
+ * shell command; the command receives ctx JSON on stdin and returns
351
+ * a HookResult JSON on stdout (or exit 2 = deny with stderr as
352
+ * reason). See src/hooks/shell-runner.ts for the wire protocol.
353
+ *
354
+ * The event field is loose-typed here (z.string()) because adding
355
+ * new lifecycle events shouldn't force a schema bump; the runner
356
+ * silently ignores entries whose event isn't registered.
357
+ */
358
+ hooks: z.ZodOptional<z.ZodArray<z.ZodObject<{
359
+ event: z.ZodString;
360
+ command: z.ZodString;
361
+ matcher: z.ZodOptional<z.ZodString>;
362
+ timeout_ms: z.ZodOptional<z.ZodNumber>;
363
+ cwd: z.ZodOptional<z.ZodString>;
364
+ }, "strip", z.ZodTypeAny, {
365
+ command: string;
366
+ event: string;
367
+ cwd?: string | undefined;
368
+ matcher?: string | undefined;
369
+ timeout_ms?: number | undefined;
370
+ }, {
371
+ command: string;
372
+ event: string;
373
+ cwd?: string | undefined;
374
+ matcher?: string | undefined;
375
+ timeout_ms?: number | undefined;
376
+ }>, "many">>;
348
377
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
349
378
  agent: z.ZodDefault<z.ZodObject<{
350
379
  preset: z.ZodDefault<z.ZodString>;
@@ -529,21 +558,21 @@ export declare const SettingsSchema: z.ZodObject<{
529
558
  argsPattern?: Record<string, string> | undefined;
530
559
  }>, "many">>;
531
560
  }, "strip", z.ZodTypeAny, {
561
+ defaultMode: "default" | "acceptEdits" | "dontAsk" | "bypassPermissions" | "auto" | "plan";
532
562
  rules: {
533
563
  tool: string;
534
564
  decision: "allow" | "deny" | "ask";
535
565
  reason?: string | undefined;
536
566
  argsPattern?: Record<string, string> | undefined;
537
567
  }[];
538
- defaultMode: "default" | "acceptEdits" | "dontAsk" | "bypassPermissions" | "auto" | "plan";
539
568
  }, {
569
+ defaultMode?: "default" | "acceptEdits" | "dontAsk" | "bypassPermissions" | "auto" | "plan" | undefined;
540
570
  rules?: {
541
571
  tool: string;
542
572
  decision: "allow" | "deny" | "ask";
543
573
  reason?: string | undefined;
544
574
  argsPattern?: Record<string, string> | undefined;
545
575
  }[] | undefined;
546
- defaultMode?: "default" | "acceptEdits" | "dontAsk" | "bypassPermissions" | "auto" | "plan" | undefined;
547
576
  }>>;
548
577
  context: z.ZodDefault<z.ZodObject<{
549
578
  maxTokens: z.ZodDefault<z.ZodNumber>;
@@ -688,6 +717,35 @@ export declare const SettingsSchema: z.ZodObject<{
688
717
  deniedReads?: string[] | undefined;
689
718
  network?: "allow" | "deny" | undefined;
690
719
  }>>;
720
+ /**
721
+ * Shell-hook configuration. Each entry binds a HookEventName to a
722
+ * shell command; the command receives ctx JSON on stdin and returns
723
+ * a HookResult JSON on stdout (or exit 2 = deny with stderr as
724
+ * reason). See src/hooks/shell-runner.ts for the wire protocol.
725
+ *
726
+ * The event field is loose-typed here (z.string()) because adding
727
+ * new lifecycle events shouldn't force a schema bump; the runner
728
+ * silently ignores entries whose event isn't registered.
729
+ */
730
+ hooks: z.ZodOptional<z.ZodArray<z.ZodObject<{
731
+ event: z.ZodString;
732
+ command: z.ZodString;
733
+ matcher: z.ZodOptional<z.ZodString>;
734
+ timeout_ms: z.ZodOptional<z.ZodNumber>;
735
+ cwd: z.ZodOptional<z.ZodString>;
736
+ }, "strip", z.ZodTypeAny, {
737
+ command: string;
738
+ event: string;
739
+ cwd?: string | undefined;
740
+ matcher?: string | undefined;
741
+ timeout_ms?: number | undefined;
742
+ }, {
743
+ command: string;
744
+ event: string;
745
+ cwd?: string | undefined;
746
+ matcher?: string | undefined;
747
+ timeout_ms?: number | undefined;
748
+ }>, "many">>;
691
749
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
692
750
  agent: z.ZodDefault<z.ZodObject<{
693
751
  preset: z.ZodDefault<z.ZodString>;
@@ -872,21 +930,21 @@ export declare const SettingsSchema: z.ZodObject<{
872
930
  argsPattern?: Record<string, string> | undefined;
873
931
  }>, "many">>;
874
932
  }, "strip", z.ZodTypeAny, {
933
+ defaultMode: "default" | "acceptEdits" | "dontAsk" | "bypassPermissions" | "auto" | "plan";
875
934
  rules: {
876
935
  tool: string;
877
936
  decision: "allow" | "deny" | "ask";
878
937
  reason?: string | undefined;
879
938
  argsPattern?: Record<string, string> | undefined;
880
939
  }[];
881
- defaultMode: "default" | "acceptEdits" | "dontAsk" | "bypassPermissions" | "auto" | "plan";
882
940
  }, {
941
+ defaultMode?: "default" | "acceptEdits" | "dontAsk" | "bypassPermissions" | "auto" | "plan" | undefined;
883
942
  rules?: {
884
943
  tool: string;
885
944
  decision: "allow" | "deny" | "ask";
886
945
  reason?: string | undefined;
887
946
  argsPattern?: Record<string, string> | undefined;
888
947
  }[] | undefined;
889
- defaultMode?: "default" | "acceptEdits" | "dontAsk" | "bypassPermissions" | "auto" | "plan" | undefined;
890
948
  }>>;
891
949
  context: z.ZodDefault<z.ZodObject<{
892
950
  maxTokens: z.ZodDefault<z.ZodNumber>;
@@ -1031,6 +1089,35 @@ export declare const SettingsSchema: z.ZodObject<{
1031
1089
  deniedReads?: string[] | undefined;
1032
1090
  network?: "allow" | "deny" | undefined;
1033
1091
  }>>;
1092
+ /**
1093
+ * Shell-hook configuration. Each entry binds a HookEventName to a
1094
+ * shell command; the command receives ctx JSON on stdin and returns
1095
+ * a HookResult JSON on stdout (or exit 2 = deny with stderr as
1096
+ * reason). See src/hooks/shell-runner.ts for the wire protocol.
1097
+ *
1098
+ * The event field is loose-typed here (z.string()) because adding
1099
+ * new lifecycle events shouldn't force a schema bump; the runner
1100
+ * silently ignores entries whose event isn't registered.
1101
+ */
1102
+ hooks: z.ZodOptional<z.ZodArray<z.ZodObject<{
1103
+ event: z.ZodString;
1104
+ command: z.ZodString;
1105
+ matcher: z.ZodOptional<z.ZodString>;
1106
+ timeout_ms: z.ZodOptional<z.ZodNumber>;
1107
+ cwd: z.ZodOptional<z.ZodString>;
1108
+ }, "strip", z.ZodTypeAny, {
1109
+ command: string;
1110
+ event: string;
1111
+ cwd?: string | undefined;
1112
+ matcher?: string | undefined;
1113
+ timeout_ms?: number | undefined;
1114
+ }, {
1115
+ command: string;
1116
+ event: string;
1117
+ cwd?: string | undefined;
1118
+ matcher?: string | undefined;
1119
+ timeout_ms?: number | undefined;
1120
+ }>, "many">>;
1034
1121
  }, z.ZodTypeAny, "passthrough">>;
1035
1122
  export type ValidatedSettings = z.infer<typeof SettingsSchema>;
1036
1123
  export declare function validateSettings(raw: unknown): ValidatedSettings;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Frontmatter parser for SKILL.md files. Byte-compatible with Claude Code's
3
+ * `utils/frontmatterParser.ts` so community skill repositories can be reused
4
+ * without modification.
5
+ */
6
+ export declare const FRONTMATTER_REGEX: RegExp;
7
+ export interface ParsedFrontmatter {
8
+ frontmatter: Record<string, unknown>;
9
+ body: string;
10
+ }
11
+ export declare function parseFrontmatter(raw: string): ParsedFrontmatter;
12
+ export declare function quoteProblematicValues(frontmatterText: string): string;
13
+ export declare function coerceDescription(value: unknown): string;
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Skills system scan, match, and inject skills into prompts.
2
+ * Skills barrel. Phase A keeps the scanner only. Listing rendering lives in
3
+ * src/tool-system/builtin/skill-prompt.ts so the prompt-formatting layer can
4
+ * grow token-budget logic later without churn here.
3
5
  */
4
- export { scanSkills } from "./scanner.js";
6
+ export { scanSkills, invalidateSkillCache } from "./scanner.js";
5
7
  export type { SkillDefinition } from "./scanner.js";
6
- export { matchSkillsByInput, matchSkillsByTool, buildSkillListing } from "./matcher.js";
7
- export type { MatchResult } from "./matcher.js";
@@ -1,30 +1,20 @@
1
1
  /**
2
- * Skills scanner — discovers SKILL.md files and parses their metadata.
3
- *
4
- * Skills are markdown files with YAML frontmatter:
5
- * ---
6
- * name: skill-name
7
- * description: what it does
8
- * triggers:
9
- * keywords: [word1, word2]
10
- * tools: [ToolName]
11
- * when_to_use: description of when to invoke
12
- * ---
13
- * <skill content>
2
+ * Skill scanner — discovers <base>/<name>/SKILL.md files from project + user
3
+ * directories AND from installed plugins. Mirrors Claude Code's
4
+ * `loadSkillsFromSkillsDir` (skills/loadSkillsDir.ts:407) plus plugin
5
+ * integration (utils/plugins/pluginLoader.ts).
14
6
  */
15
7
  export interface SkillDefinition {
8
+ /** Directory name; authoritative regardless of frontmatter.name. */
16
9
  name: string;
10
+ /** From frontmatter.description, coerced. Empty string if absent or invalid. */
17
11
  description: string;
18
- triggers: {
19
- keywords?: string[];
20
- tools?: string[];
21
- intents?: string[];
22
- };
23
- whenToUse: string;
12
+ /** SKILL.md body with frontmatter stripped. */
24
13
  content: string;
14
+ /** Absolute path to the SKILL.md file. */
25
15
  filePath: string;
16
+ /** Where the skill was loaded from. */
17
+ source: "project" | "user" | "plugin";
26
18
  }
27
- /**
28
- * Scan for SKILL.md files in standard locations.
29
- */
30
19
  export declare function scanSkills(cwd: string): SkillDefinition[];
20
+ export declare function invalidateSkillCache(): void;
@@ -0,0 +1,2 @@
1
+ import {d as d$1}from'./chunk-DI26OUIU.js';import {h,a}from'./chunk-3QP5BQ3L.js';h();var p='Generate a very brief (under 40 chars) summary of what these tools did. Use past tense, commit-subject style. Examples: "Read 3 config files", "Fixed import in auth.ts", "Searched for API endpoints". Respond with ONLY the summary text, nothing else.';async function d(r,a,l){if(r.length===0)return null;try{let e=[];for(let t=0;t<r.length;t++){let s=r[t],o=a[t],u=JSON.stringify(s.args).slice(0,200),c=(o?.result??o?.error??"").slice(0,300);e.push(`Tool: ${s.toolName}(${u}) \u2192 ${c}`);}return (await l(p,e.join(`
2
+ `)))?.trim()||null}catch(e){return d$1.warn("tool_summary.failed",{error:e.message}),null}}a(d,"generateToolUseSummary");export{d as generateToolUseSummary};
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Background-agent completion notification queue.
3
+ *
4
+ * Mirrors Claude Code's `commandQueue` with `mode: 'task-notification'`. A
5
+ * background sub-agent that finishes (completed | failed) enqueues an item
6
+ * here; the UI layer subscribes and, when the main agent is idle, drains
7
+ * the queue and submits the formatted XML as a new user turn so the LLM
8
+ * sees the result. Cancellation does NOT enqueue (user explicitly stopped
9
+ * the agent; no follow-up needed).
10
+ *
11
+ * The result text lives only in this queue + the eventual user message —
12
+ * not in `asyncAgentRegistry`. Registry stays metadata-only.
13
+ *
14
+ * Process-local singleton; same lifetime contract as `asyncAgentRegistry`.
15
+ */
16
+ export type NotificationItem = {
17
+ agentId: string;
18
+ name?: string;
19
+ description: string;
20
+ status: "completed" | "failed";
21
+ /** Final assistant text (completed only). */
22
+ finalText?: string;
23
+ /** Error message (failed only). */
24
+ error?: string;
25
+ enqueuedAt: number;
26
+ };
27
+ type Listener = () => void;
28
+ declare class NotificationQueue {
29
+ private items;
30
+ private listeners;
31
+ enqueue(item: NotificationItem): void;
32
+ subscribe: (cb: Listener) => (() => void);
33
+ getSnapshot: () => NotificationItem[];
34
+ /** Atomic: returns all items and clears in one shot. */
35
+ drainAll(): NotificationItem[];
36
+ reset(): void;
37
+ private notify;
38
+ }
39
+ export declare const notificationQueue: NotificationQueue;
40
+ /**
41
+ * Render a batch of completion notifications as the XML user-message
42
+ * body that the main agent's LLM will see. Format is stable — main
43
+ * agent prompts can rely on the `<background-agents-completed>` root
44
+ * tag as a signal that this turn is a system-injected notification,
45
+ * not a real user message.
46
+ */
47
+ export declare function buildNotificationMessage(items: NotificationItem[]): string;
48
+ /**
49
+ * One-line-per-agent human summary for the chat feed. The full result
50
+ * body goes to the LLM via buildNotificationMessage; the user sees only
51
+ * this terse marker plus an optional inline error preview, and can
52
+ * switch to the sub-agent's dock view if they want details.
53
+ */
54
+ export declare function buildNotificationSummary(items: NotificationItem[]): string;
55
+ export {};
@@ -12,23 +12,54 @@
12
12
  * RunManager, not here.
13
13
  */
14
14
  export type AsyncAgentStatus = "running" | "completed" | "failed" | "cancelled";
15
+ /**
16
+ * Minimal structural shape for an entry in an agent's transcript. We avoid
17
+ * importing the UI's `ChatEntry` here to prevent a tool-system → ui import
18
+ * direction. Consumers (the UI dock) widen this type via `as ChatEntry[]`
19
+ * at their boundary.
20
+ */
21
+ export interface AgentTranscriptEntry {
22
+ id: string;
23
+ type: string;
24
+ [key: string]: unknown;
25
+ }
15
26
  export interface AsyncAgentEntry {
16
27
  agentId: string;
28
+ /** Short kind label shown in the dock (e.g. "Explore", "Plan"). */
29
+ name?: string;
17
30
  description: string;
18
31
  status: AsyncAgentStatus;
19
32
  startedAt: number;
20
33
  finishedAt?: number;
21
- result?: string;
22
- error?: string;
34
+ /** finishedAt + 30_000. Dock filters rows past this. */
35
+ finishedFadeAt?: number;
23
36
  abort: () => void;
37
+ /** Stream events recorded by run_in_background agents for UI view-switching. */
38
+ transcript?: AgentTranscriptEntry[];
24
39
  }
25
40
  declare class AsyncAgentRegistry {
26
41
  private agents;
42
+ private listeners;
43
+ private snapshot;
44
+ subscribe: (cb: () => void) => (() => void);
45
+ getSnapshot: () => AsyncAgentEntry[];
46
+ hasRunning: () => boolean;
47
+ private notify;
27
48
  register(entry: AsyncAgentEntry): void;
49
+ appendToTranscript(agentId: string, entry: AgentTranscriptEntry): void;
50
+ /**
51
+ * Re-emit the registry change signal after callers mutate an agent's
52
+ * transcript array in place (patching or filtering entries). Required
53
+ * because `notify` rebuilds the snapshot and tells React subscribers to
54
+ * re-read.
55
+ */
56
+ touchTranscript(agentId: string): void;
28
57
  get(agentId: string): AsyncAgentEntry | undefined;
29
58
  list(): AsyncAgentEntry[];
30
- markCompleted(agentId: string, result: string): void;
31
- markFailed(agentId: string, error: string): void;
59
+ private markFinished;
60
+ markCompleted(agentId: string): void;
61
+ markFailed(agentId: string): void;
62
+ markCancelled(agentId: string): void;
32
63
  cancel(agentId: string): boolean;
33
64
  reset(): void;
34
65
  }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Translate child Engine StreamEvents into ChatEntry-shaped objects so the
3
+ * AgentDock detail view (which reuses App.renderEntry) can show a sub-agent's
4
+ * conversation in the same visual language as the main feed.
5
+ *
6
+ * The transcript is the canonical record for one background sub-agent. State
7
+ * (current thinking entry, currently-streaming assistant_text, in-flight
8
+ * tool_start map) lives in a per-agent translator. Each translator instance
9
+ * appends ChatEntry-shaped objects via `append` and mutates them via `update`,
10
+ * mirroring how App.tsx's handleStreamEvent talks to chatStore but isolated to
11
+ * a single agent's transcript.
12
+ *
13
+ * The output shapes intentionally match `src/ui/store.ts#ChatEntryData` so the
14
+ * existing renderEntry switch picks them up without code changes.
15
+ */
16
+ import type { StreamEvent } from "../../types.js";
17
+ /**
18
+ * Create a transcript translator scoped to one background sub-agent. Returned
19
+ * function is a StreamCallback drop-in: feed it the child Engine's events and
20
+ * it maintains the agent's transcript in `asyncAgentRegistry`.
21
+ */
22
+ export declare function createTranscriptTranslator(agentId: string): (event: StreamEvent) => void;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Skill listing renderer for the system prompt. Mirrors Claude Code's split
3
+ * between scan/data layer (skills/scanner.ts) and render layer (this file),
4
+ * matching tools/SkillTool/prompt.ts in CC.
5
+ */
6
+ import type { SkillDefinition } from "../../skills/scanner.js";
7
+ export declare function buildSkillListing(skills: SkillDefinition[]): string;
@@ -1,5 +1,7 @@
1
1
  /**
2
- * SkillTool — invoke custom skills/plugins by name.
2
+ * SkillTool — load a skill's SKILL.md body and return it as the tool result.
3
+ * The scanner is the single source of truth; this tool never reads the disk
4
+ * directly. Matches Claude Code's tools/SkillTool/SkillTool.ts pattern.
3
5
  */
4
6
  import type { ToolDefinition } from "../../types.js";
5
7
  export declare const skillToolDef: ToolDefinition;
@@ -17,6 +17,7 @@ import type { ModelPool } from "../llm/model-pool.js";
17
17
  import type { ToolRegistry } from "./registry.js";
18
18
  import type { AgentPresetName } from "../preset/index.js";
19
19
  import type { SandboxBackend } from "./sandbox/index.js";
20
+ import type { HookRegistry } from "../hooks/registry.js";
20
21
  /** One choice in a multiple-choice AskUserQuestion. */
21
22
  export interface AskUserChoice {
22
23
  label: string;
@@ -55,6 +56,14 @@ export interface SubAgentSpawnRequest {
55
56
  prompt: string;
56
57
  maxTurns: number;
57
58
  signal: AbortSignal;
59
+ /**
60
+ * Per-call override for where the spawned child Engine's stream events go.
61
+ * Defaults to `spawner.parentStream` (the parent UI). Background sub-agents
62
+ * pass a transcriptSink here so per-event detail is captured in the agent's
63
+ * transcript instead of leaking into the main feed; the main feed still
64
+ * gets `agent_start` / `agent_end` markers via `spawner.parentStream`.
65
+ */
66
+ streamOverride?: StreamCallback;
58
67
  }
59
68
  export interface SubAgentSpawner {
60
69
  /** Run a sub-agent synchronously and return its text output. */
@@ -87,6 +96,13 @@ export interface ToolContext {
87
96
  askUser?: AskUserFn;
88
97
  /** Sub-agent spawner (Agent tool). Undefined → Agent tool unavailable. */
89
98
  subAgentSpawner?: SubAgentSpawner;
99
+ /**
100
+ * True when this Engine is itself a sub-agent. Set from EngineConfig.
101
+ * The Agent tool refuses to spawn when this is true — runtime check
102
+ * layered on top of the tool-list strip in Engine.spawn so a registry
103
+ * regression can't leak nested spawns.
104
+ */
105
+ isSubAgent?: boolean;
90
106
  /**
91
107
  * Active sandbox backend for the Bash tool. Undefined falls back to "off"
92
108
  * (plain spawn). Headless runs default to an OS-level sandbox; the REPL
@@ -95,6 +111,13 @@ export interface ToolContext {
95
111
  sandbox?: SandboxBackend;
96
112
  /** Optional cancellation signal for the whole turn. */
97
113
  signal?: AbortSignal;
114
+ /**
115
+ * Engine-owned HookRegistry. Tools that emit lifecycle hooks
116
+ * (notification, file_changed for non-Write/Edit paths, custom
117
+ * tool-defined events) use this. Undefined for legacy callers /
118
+ * standalone tests; tools must tolerate absence.
119
+ */
120
+ hooks?: HookRegistry;
98
121
  }
99
122
  /**
100
123
  * Per-Engine container that produces a fresh ToolContext on demand.
@@ -38,7 +38,7 @@ export declare class ToolExecutor {
38
38
  setLogger(log: Logger): void;
39
39
  /** Check if a tool is safe for concurrent execution (read-only). */
40
40
  isConcurrencySafe(toolName: string): boolean;
41
- executeSingle(call: ToolCall): Promise<ToolResult>;
41
+ executeSingle(callIn: ToolCall): Promise<ToolResult>;
42
42
  private isReadOnlyBashCommand;
43
43
  executeAll(calls: ToolCall[]): Promise<ToolResult[]>;
44
44
  /**
@@ -88,7 +88,7 @@ export declare class PermissionClassifier {
88
88
  reconfigure(mode: PermissionMode, approvalBackend: ApprovalBackend, rules?: PermissionRule[]): void;
89
89
  getMode(): PermissionMode;
90
90
  classify(toolName: string, args: Record<string, unknown>): PermissionDecision;
91
- handleAsk(toolName: string, args: Record<string, unknown>): Promise<boolean>;
91
+ handleAsk(toolName: string, args: Record<string, unknown>, reason?: string): Promise<boolean>;
92
92
  /** Get denial warning message if the model keeps getting denied. */
93
93
  getDenialWarning(toolName: string): string | undefined;
94
94
  private matchesRule;
package/dist/types.d.ts CHANGED
@@ -217,11 +217,14 @@ export type StreamEvent = {
217
217
  } | {
218
218
  type: "agent_start";
219
219
  agentId: string;
220
+ name?: string;
220
221
  description: string;
221
222
  } | {
222
223
  type: "agent_end";
223
224
  agentId: string;
225
+ name?: string;
224
226
  description: string;
227
+ text?: string;
225
228
  error?: string;
226
229
  } | {
227
230
  type: "tool_summary";
@@ -288,6 +291,32 @@ export interface MCPServerConfig {
288
291
  transport?: MCPTransport;
289
292
  headers?: Record<string, string>;
290
293
  }
294
+ /**
295
+ * Shell-hook configuration entry from settings.json. Each entry binds
296
+ * a hook event to a shell command. The command runs as a child process
297
+ * on every emit (filtered by `matcher` when present), receives ctx
298
+ * JSON on stdin, and returns a HookResult JSON on stdout. See
299
+ * src/hooks/shell-runner.ts for the protocol spec.
300
+ */
301
+ export interface SettingsHookConfig {
302
+ event: string;
303
+ command: string;
304
+ /**
305
+ * Optional regex (matched with new RegExp(matcher).test(toolName)) to
306
+ * filter which tools fire this hook. Currently honored for
307
+ * pre_tool_use / post_tool_use / on_tool_start / on_tool_end /
308
+ * on_permission_check / file_changed. Ignored for events without a
309
+ * meaningful toolName (session/turn/prompt/notification/compact).
310
+ */
311
+ matcher?: string;
312
+ /** Per-hook timeout in milliseconds. Defaults to 60_000. */
313
+ timeout_ms?: number;
314
+ /**
315
+ * Optional working directory for the spawned command. Defaults to
316
+ * Engine.cwd. Useful for hooks that need to run in a sibling repo.
317
+ */
318
+ cwd?: string;
319
+ }
291
320
  export interface Settings {
292
321
  model: {
293
322
  provider: string;
@@ -318,4 +347,10 @@ export interface Settings {
318
347
  output: {
319
348
  format: "text" | "json" | "jsonl" | "stream-json";
320
349
  };
350
+ /**
351
+ * Optional shell-hook entries. Engine reads on construction and
352
+ * registers a wrapper handler per entry that spawns the command and
353
+ * parses the HookResult. Empty / missing = no shell hooks.
354
+ */
355
+ hooks?: SettingsHookConfig[];
321
356
  }
@@ -11,17 +11,20 @@
11
11
  */
12
12
  import React from "react";
13
13
  interface AgentBlockStartProps {
14
+ name?: string;
14
15
  description: string;
15
16
  running?: boolean;
16
17
  isLast?: boolean;
17
18
  }
18
- export declare function AgentBlockStart({ description, running, isLast }: AgentBlockStartProps): import("react/jsx-runtime").JSX.Element;
19
+ export declare function AgentBlockStart({ name, description, running, isLast }: AgentBlockStartProps): import("react/jsx-runtime").JSX.Element;
19
20
  interface AgentBlockEndProps {
21
+ name?: string;
20
22
  description: string;
23
+ text?: string;
21
24
  error?: string;
22
25
  isLast?: boolean;
23
26
  }
24
- export declare function AgentBlockEnd({ description, error, isLast }: AgentBlockEndProps): import("react/jsx-runtime").JSX.Element;
27
+ export declare function AgentBlockEnd({ name, description, text, error, isLast }: AgentBlockEndProps): import("react/jsx-runtime").JSX.Element;
25
28
  /**
26
29
  * Tree line prefix for nested content under an agent.
27
30
  */
@@ -0,0 +1,36 @@
1
+ import React from "react";
2
+ import { type AsyncAgentEntry } from "../../tool-system/builtin/agent-registry.js";
3
+ export declare const MAX_VISIBLE = 5;
4
+ export type DockViewMode = {
5
+ kind: "main";
6
+ } | {
7
+ kind: "agent";
8
+ agentId: string;
9
+ };
10
+ export interface AgentDockProps {
11
+ viewMode: DockViewMode;
12
+ /** null = dock is not the keyboard target; integer = focused row index. */
13
+ focusedIndex: number | null;
14
+ }
15
+ /**
16
+ * AgentDock — vertical list of running and recently-finished sub-agents,
17
+ * pinned at the very bottom of the UI. Updates elapsed text once per
18
+ * second; redraws are local to this subtree (no app-wide re-render).
19
+ *
20
+ * See docs/superpowers/specs/2026-05-18-subagent-dock-revisions-design.md.
21
+ */
22
+ export declare function AgentDock({ viewMode, focusedIndex, }: AgentDockProps): React.ReactElement | null;
23
+ /**
24
+ * Filter the registry snapshot down to rows the dock should show:
25
+ * running agents + recently-finished agents still inside the fade window.
26
+ * Successful completions are dropped immediately — their final text already
27
+ * surfaced via agent_end in the main feed, so the dock row is redundant.
28
+ * Failures and cancellations linger so the user can investigate.
29
+ *
30
+ * Exported so App.tsx's keyboard handler shares the same predicate.
31
+ */
32
+ export declare function getVisibleAgents(all: AsyncAgentEntry[], now: number): AsyncAgentEntry[];
33
+ /**
34
+ * Format an elapsed-millisecond duration as "23s" / "4m 23s" / "1h 4m 23s".
35
+ */
36
+ export declare function formatElapsed(ms: number): string;
@@ -1,6 +1,10 @@
1
1
  interface CommandDef {
2
2
  name: string;
3
3
  description: string;
4
+ /** Optional usage string like "/fullscreen [on|off|toggle]" — the
5
+ * bracketed portion is rendered dim after the command name once the
6
+ * user has finished typing it. */
7
+ usage?: string;
4
8
  }
5
9
  interface CommandInputProps {
6
10
  value: string;
@@ -8,6 +12,13 @@ interface CommandInputProps {
8
12
  onSubmit: (value: string) => void;
9
13
  commands: CommandDef[];
10
14
  placeholder?: string;
15
+ /** Fired when ↓ is pressed and input is empty and history is exhausted.
16
+ * Used by App.tsx to move keyboard focus into the AgentDock. */
17
+ onArrowOut?: (direction: "down") => void;
18
+ /** When true, skip ALL key handling. Used while another part of the UI
19
+ * (e.g. the AgentDock) owns the keyboard. The text input still renders;
20
+ * only the useInput body short-circuits. */
21
+ disabled?: boolean;
11
22
  }
12
- export declare function CommandInput({ value, onChange, onSubmit, commands, placeholder, }: CommandInputProps): import("react/jsx-runtime").JSX.Element;
23
+ export declare function CommandInput({ value, onChange, onSubmit, commands, placeholder, onArrowOut, disabled, }: CommandInputProps): import("react/jsx-runtime").JSX.Element;
13
24
  export {};