@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.
- package/CHANGELOG.md +108 -0
- package/README.md +35 -2
- package/dist/anthropic-DJXVKFMJ.js +1 -0
- package/dist/arena/index.js +1 -1
- package/dist/arena-MRBATZFD.js +3 -0
- package/dist/{chunk-EU3J7TBP.js → chunk-5QIVXFCT.js} +1 -1
- package/dist/{chunk-XXCZE2U2.js → chunk-AFMP73YP.js} +1 -1
- package/dist/{chunk-L7B2ORID.js → chunk-D3TVASD2.js} +2 -2
- package/dist/chunk-DI26OUIU.js +2 -0
- package/dist/chunk-FZXY554D.js +2 -0
- package/dist/chunk-GGVAOPI7.js +1 -0
- package/dist/chunk-GKAQH2DG.js +2 -0
- package/dist/chunk-GYNO4R7R.js +2 -0
- package/dist/{chunk-RERBX75D.js → chunk-K4HILJ6G.js} +3 -3
- package/dist/{chunk-2EUIMKJA.js → chunk-OWBYLMDD.js} +1 -1
- package/dist/chunk-SKIX2M5Z.js +4 -0
- package/dist/chunk-SV7H4FKT.js +3 -0
- package/dist/{chunk-2GJ7H5NC.js → chunk-WONQHE4P.js} +40 -40
- package/dist/{chunk-DEYFK6FP.js → chunk-X2S7ASQ2.js} +1 -1
- package/dist/chunk-Z5HKHAO7.js +336 -0
- package/dist/cli/commands/builtin/plugin-commands-registration.d.ts +12 -0
- package/dist/cli/commands/builtin/plugin-handler.d.ts +6 -0
- package/dist/cli/commands/registry.d.ts +9 -2
- package/dist/cli/main.js +138 -122
- package/dist/client-base-GSFXPP2Y.js +1 -0
- package/dist/devtools-FBX4HVVY.js +1 -0
- package/dist/engine/engine.d.ts +29 -0
- package/dist/engine/turn-loop.d.ts +25 -0
- package/dist/hooks/events.d.ts +84 -5
- package/dist/hooks/inject.d.ts +19 -0
- package/dist/hooks/shell-runner.d.ts +45 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/llm/api-key-sanitize.d.ts +22 -0
- package/dist/llm/providers/openai.d.ts +23 -0
- package/dist/llm/retry.d.ts +12 -0
- package/dist/llm/stream-watchdog.d.ts +42 -0
- package/dist/logging/logger.d.ts +14 -0
- package/dist/manager-4XKLZHKE.js +1 -0
- package/dist/mcp-manager-3JGHEJO3.js +1 -0
- package/dist/openai-OQ6LLN5J.js +1 -0
- package/dist/{openrouter-models-GZVCRWD2.js → openrouter-models-ZT4R2AE3.js} +1 -1
- package/dist/openrouter-sync-ARMVWY7S.js +1 -0
- package/dist/plugin-handler-GJISMH5Q.js +11 -0
- package/dist/plugins/gitOps.d.ts +23 -0
- package/dist/plugins/installedPlugins.d.ts +15 -0
- package/dist/plugins/knownMarketplaces.d.ts +10 -0
- package/dist/plugins/loadPluginHooks.d.ts +52 -0
- package/dist/plugins/marketplaceManager.d.ts +49 -0
- package/dist/plugins/parseMarketplaceInput.d.ts +16 -0
- package/dist/plugins/pluginCommandHook.d.ts +54 -0
- package/dist/plugins/pluginCommandsLoader.d.ts +25 -0
- package/dist/plugins/pluginInstaller.d.ts +36 -0
- package/dist/plugins/schemas.d.ts +9 -0
- package/dist/plugins/types.d.ts +73 -2
- package/dist/plugins/varRewrite.d.ts +42 -0
- package/dist/product/index.js +1 -1
- package/dist/protocol/helpers.d.ts +42 -0
- package/dist/render/clearTerminal.d.ts +9 -3
- package/dist/render/components/App.d.ts +1 -0
- package/dist/render/components/Static.d.ts +23 -0
- package/dist/render/devtools.d.ts +8 -4
- package/dist/render/dom.d.ts +2 -1
- package/dist/render/frame.d.ts +1 -0
- package/dist/render/index.d.ts +10 -0
- package/dist/render/ink.d.ts +24 -2
- package/dist/render/log-update.d.ts +12 -1
- package/dist/render/selection.d.ts +1 -1
- package/dist/run/index.js +1 -1
- package/dist/services/index.d.ts +1 -0
- package/dist/services/memory-orchestrator.d.ts +39 -0
- package/dist/settings/schema.d.ts +93 -6
- package/dist/skills/frontmatter.d.ts +13 -0
- package/dist/skills/index.d.ts +4 -4
- package/dist/skills/scanner.d.ts +11 -21
- package/dist/tool-summary-HJX6RHG4.js +2 -0
- package/dist/tool-system/builtin/agent-notifications.d.ts +55 -0
- package/dist/tool-system/builtin/agent-registry.d.ts +35 -4
- package/dist/tool-system/builtin/agent-transcript-translator.d.ts +22 -0
- package/dist/tool-system/builtin/skill-prompt.d.ts +7 -0
- package/dist/tool-system/builtin/skill.d.ts +3 -1
- package/dist/tool-system/context.d.ts +23 -0
- package/dist/tool-system/executor.d.ts +1 -1
- package/dist/tool-system/permission.d.ts +1 -1
- package/dist/types.d.ts +35 -0
- package/dist/ui/components/AgentBlock.d.ts +5 -2
- package/dist/ui/components/AgentDock.d.ts +36 -0
- package/dist/ui/components/CommandInput.d.ts +12 -1
- package/dist/ui/components/MessageRow.d.ts +54 -0
- package/dist/ui/components/SpinnerWithVerb.d.ts +2 -1
- package/dist/ui/components/TextInput.d.ts +9 -1
- package/dist/ui/components/VirtualMessageList.d.ts +30 -11
- package/dist/ui/dev-seed.d.ts +1 -0
- package/dist/ui/fullscreen-mode.d.ts +9 -0
- package/dist/ui/hooks/useVirtualScroll.d.ts +101 -0
- package/dist/ui/perf-probes.d.ts +22 -0
- package/dist/ui/query-guard.d.ts +35 -0
- package/dist/ui/slice-anchor.d.ts +44 -0
- package/dist/ui/store.d.ts +26 -1
- package/package.json +5 -3
- package/dist/anthropic-OVRWAZRD.js +0 -1
- package/dist/arena-64T2EQD4.js +0 -3
- package/dist/chunk-A4M2Y42L.js +0 -2
- package/dist/chunk-DTPABBSV.js +0 -2
- package/dist/chunk-MGP35GKD.js +0 -254
- package/dist/chunk-Q3RSX66I.js +0 -1
- package/dist/chunk-VKKU4LOM.js +0 -3
- package/dist/chunk-ZSAONDT6.js +0 -2
- package/dist/client-base-F5FCRSB3.js +0 -1
- package/dist/devtools-SGDUOOM4.js +0 -1
- package/dist/manager-J6FXEUEK.js +0 -1
- package/dist/mcp-manager-27EVBKRT.js +0 -1
- package/dist/openai-3QEADSYL.js +0 -1
- package/dist/openrouter-sync-WG4AG5NC.js +0 -1
- package/dist/plugins/loader.d.ts +0 -72
- package/dist/skills/matcher.d.ts +0 -21
- package/dist/tool-summary-V2B3MD5N.js +0 -2
- package/skills-builtin/codeshell-help.md +0 -135
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{a as LLMClientBase}from'./chunk-OWBYLMDD.js';import'./chunk-FU7AHZNN.js';import'./chunk-DI26OUIU.js';import'./chunk-3QP5BQ3L.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{a as recordFrame,b as renderDevtools}from'./chunk-GYNO4R7R.js';import'./chunk-3QP5BQ3L.js';
|
package/dist/engine/engine.d.ts
CHANGED
|
@@ -51,6 +51,14 @@ export interface EngineConfig {
|
|
|
51
51
|
* the loop to approve commands.
|
|
52
52
|
*/
|
|
53
53
|
sandbox?: SandboxConfig;
|
|
54
|
+
/**
|
|
55
|
+
* True when this Engine is itself a sub-agent (spawned by another
|
|
56
|
+
* Engine's Agent tool). Threaded into ToolContext so the Agent tool can
|
|
57
|
+
* refuse re-entry — defense in depth against the tool-list strip in
|
|
58
|
+
* spawn(): if a tool registry regression ever leaks Agent into a child's
|
|
59
|
+
* pool, the runtime check still blocks the call.
|
|
60
|
+
*/
|
|
61
|
+
isSubAgent?: boolean;
|
|
54
62
|
}
|
|
55
63
|
export interface EngineHookConfig {
|
|
56
64
|
event: HookEventName;
|
|
@@ -98,6 +106,21 @@ export declare class Engine {
|
|
|
98
106
|
/** Public accessor so UI/clients can read the resolved per-model window. */
|
|
99
107
|
get maxContextTokens(): number;
|
|
100
108
|
private resolveMaxContextTokens;
|
|
109
|
+
/**
|
|
110
|
+
* Emit a lifecycle hook with isSubAgent auto-merged into data so handlers
|
|
111
|
+
* can skip noisy injections for spawned children. All Engine-side hook
|
|
112
|
+
* emits should go through this wrapper to keep the context envelope
|
|
113
|
+
* uniform with TurnLoop.emitHook.
|
|
114
|
+
*/
|
|
115
|
+
private emitHook;
|
|
116
|
+
/**
|
|
117
|
+
* Read settings.hooks and register a shell-runner wrapper handler per
|
|
118
|
+
* entry. Sub-agents skip shell hooks entirely — spawning child processes
|
|
119
|
+
* per emit for every sub-agent run would multiply token-side overhead
|
|
120
|
+
* for marginal value; explicit users who want sub-agent observability
|
|
121
|
+
* should register SDK-side handlers.
|
|
122
|
+
*/
|
|
123
|
+
private registerSettingsHooks;
|
|
101
124
|
constructor(config: EngineConfig);
|
|
102
125
|
/**
|
|
103
126
|
* Load models[] / providers[] from settings into the active ModelPool and
|
|
@@ -137,6 +160,12 @@ export declare class Engine {
|
|
|
137
160
|
signal?: AbortSignal;
|
|
138
161
|
sessionId?: string;
|
|
139
162
|
}): Promise<EngineResult>;
|
|
163
|
+
/**
|
|
164
|
+
* Run the end-of-session memory pipeline as a fire-and-forget background
|
|
165
|
+
* task. Extracts durable memories from the transcript, saves a session
|
|
166
|
+
* summary, and conditionally triggers auto-dream consolidation.
|
|
167
|
+
*/
|
|
168
|
+
private runMemoryPipeline;
|
|
140
169
|
getToolRegistry(): ToolRegistry;
|
|
141
170
|
/**
|
|
142
171
|
* Switch the active model by pool key. Takes effect on the next run() call.
|
|
@@ -40,6 +40,24 @@ export interface TurnLoopDeps {
|
|
|
40
40
|
ctxOverheadStore: CtxOverheadStore;
|
|
41
41
|
/** Current session id, used to key the overhead store. */
|
|
42
42
|
sessionId: string;
|
|
43
|
+
/**
|
|
44
|
+
* Carried into every hook emit's `ctx.data.isSubAgent` so handlers can
|
|
45
|
+
* skip noisy injections for spawned children. Set by Engine from
|
|
46
|
+
* EngineConfig.isSubAgent (engine.ts:119).
|
|
47
|
+
*/
|
|
48
|
+
isSubAgent?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Reads/clears the most recent compaction event emitted by
|
|
51
|
+
* ContextManager since the last call. Returns `null` if no compaction
|
|
52
|
+
* fired since the previous check. The Engine wires this up so the
|
|
53
|
+
* turn loop can `post_compact` emit + inject hook messages without
|
|
54
|
+
* the ContextManager itself depending on HookRegistry.
|
|
55
|
+
*/
|
|
56
|
+
consumePendingCompactInfo?: () => {
|
|
57
|
+
strategy: string;
|
|
58
|
+
before: number;
|
|
59
|
+
after: number;
|
|
60
|
+
} | null;
|
|
43
61
|
}
|
|
44
62
|
export interface TurnLoopResult {
|
|
45
63
|
text: string;
|
|
@@ -62,6 +80,13 @@ export declare class TurnLoop {
|
|
|
62
80
|
/** Last emitted ctx token estimate; used to skip no-op usage_update events. */
|
|
63
81
|
private lastCtxEmit;
|
|
64
82
|
constructor(deps: TurnLoopDeps, config: TurnLoopConfig);
|
|
83
|
+
/**
|
|
84
|
+
* Emit a lifecycle hook with isSubAgent + sessionId auto-merged into data.
|
|
85
|
+
* Returns the aggregated HookResult so callers can consume `messages` /
|
|
86
|
+
* `decision` / `stop`. Use this instead of `deps.hooks.emit` directly so
|
|
87
|
+
* every emit carries the same context envelope.
|
|
88
|
+
*/
|
|
89
|
+
private emitHook;
|
|
65
90
|
/**
|
|
66
91
|
* Emit a usage_update so the UI ctx bar reflects current message-array
|
|
67
92
|
* size. Called at every point where messages mutate: after a tool_result
|
package/dist/hooks/events.d.ts
CHANGED
|
@@ -5,16 +5,72 @@
|
|
|
5
5
|
* Lifecycle hooks the engine emits.
|
|
6
6
|
*
|
|
7
7
|
* **Currently emitted** (handlers registered here will actually fire):
|
|
8
|
-
* -
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* - on_session_start / on_session_end (engine.ts) — fires once per Engine.run().
|
|
9
|
+
* on_session_start handlers may return
|
|
10
|
+
* `messages` to inject a <system-reminder>
|
|
11
|
+
* at the head of the conversation, before
|
|
12
|
+
* the current user prompt.
|
|
13
|
+
* - on_agent_start / on_agent_end (engine.ts) — notify-only; returned
|
|
14
|
+
* messages are NOT consumed (the loop
|
|
15
|
+
* is already armed by the time these
|
|
16
|
+
* fire). Use on_session_start instead.
|
|
17
|
+
* - user_prompt_submit (engine.ts) — fires once per run() for
|
|
18
|
+
* every new user prompt (cold-start and
|
|
19
|
+
* resume both qualify). Returned messages
|
|
20
|
+
* are merged into the same lifecycle
|
|
21
|
+
* <system-reminder> as on_session_start.
|
|
22
|
+
* - on_turn_start / on_turn_end (turn-loop.ts) — on_turn_start handlers
|
|
23
|
+
* may return `messages` to inject a
|
|
24
|
+
* per-turn reminder appended to the
|
|
25
|
+
* conversation right before the model call.
|
|
26
|
+
* - pre_tool_use / post_tool_use (executor.ts) — pre_tool_use honors
|
|
27
|
+
* `decision: "deny"` to short-circuit
|
|
28
|
+
* the call (executor.ts:131).
|
|
11
29
|
* - on_tool_start / on_tool_end (executor.ts)
|
|
12
30
|
* - file_changed (executor.ts, Write/Edit only)
|
|
31
|
+
* - on_permission_check (executor.ts) — fires after the
|
|
32
|
+
* classifier runs and before its
|
|
33
|
+
* decision is acted on. Handler can
|
|
34
|
+
* override via `decision`; the
|
|
35
|
+
* override is logged as
|
|
36
|
+
* `permission.hook_override`. ctx.data
|
|
37
|
+
* carries `classifierDecision` so
|
|
38
|
+
* handlers can branch on the rule
|
|
39
|
+
* set's verdict.
|
|
40
|
+
* - post_compact (turn-loop.ts) — fires after
|
|
41
|
+
* ContextManager.manageAsync() runs
|
|
42
|
+
* a non-micro compaction. Handlers
|
|
43
|
+
* may return `messages` to inject a
|
|
44
|
+
* <system-reminder> into the same
|
|
45
|
+
* turn before the model call. ctx.data
|
|
46
|
+
* carries `strategy` (summary/snip/
|
|
47
|
+
* window/emergency), `beforeTokens`,
|
|
48
|
+
* `afterTokens`. Microcompact is
|
|
49
|
+
* intentionally suppressed.
|
|
50
|
+
* - notification (agent.ts) — fired when a background
|
|
51
|
+
* sub-agent transitions to a terminal
|
|
52
|
+
* state. ctx.data carries `kind`
|
|
53
|
+
* ("agent_completed" / "agent_failed" /
|
|
54
|
+
* "agent_cancelled"), `agentId`,
|
|
55
|
+
* `name`, `description`, plus
|
|
56
|
+
* `finalText` (completed) or `error`
|
|
57
|
+
* (failed). Fired void — handler
|
|
58
|
+
* latency does not block the main
|
|
59
|
+
* loop. Not consumed by the engine
|
|
60
|
+
* (bg-agent feed renders the same
|
|
61
|
+
* info via notificationQueue);
|
|
62
|
+
* intended for shell hooks (osascript
|
|
63
|
+
* / desktop notifications).
|
|
64
|
+
*
|
|
65
|
+
* All Engine-side emits run through Engine.emitHook / TurnLoop.emitHook, which
|
|
66
|
+
* auto-merge `isSubAgent` (and sessionId, for turn-loop) into ctx.data so
|
|
67
|
+
* handlers can skip noisy injections for spawned children.
|
|
13
68
|
*
|
|
14
69
|
* **Reserved / not-yet-emitted** (defined so downstream can register handlers
|
|
15
70
|
* in anticipation; wire the emitter before relying on them):
|
|
16
|
-
* -
|
|
17
|
-
*
|
|
71
|
+
* - pre_compact — would require pre-flight prediction inside
|
|
72
|
+
* ContextManager; current implementation only knows
|
|
73
|
+
* after-the-fact (use post_compact instead).
|
|
18
74
|
*/
|
|
19
75
|
export type HookEventName = "on_agent_start" | "on_agent_end" | "on_turn_start" | "on_turn_end" | "on_tool_start" | "on_tool_end" | "on_permission_check" | "on_session_start" | "on_session_end" | "pre_tool_use" | "post_tool_use" | "user_prompt_submit" | "pre_compact" | "post_compact" | "file_changed" | "notification";
|
|
20
76
|
export interface HookContext {
|
|
@@ -32,4 +88,27 @@ export interface HookResult {
|
|
|
32
88
|
messages?: string[];
|
|
33
89
|
/** Allow/deny/ask override for permission hooks */
|
|
34
90
|
decision?: "allow" | "deny" | "ask";
|
|
91
|
+
/**
|
|
92
|
+
* For pre_tool_use: replace the tool's args before execution. Used by
|
|
93
|
+
* "sanitizer" handlers (e.g. redact secrets in Bash commands, normalize
|
|
94
|
+
* file paths, inject a default flag). Last handler in the chain wins.
|
|
95
|
+
* Args are re-validated against the tool's input schema before the
|
|
96
|
+
* tool runs, so a malformed updatedInput still surfaces as an
|
|
97
|
+
* "Invalid input" error rather than silently passing through.
|
|
98
|
+
*/
|
|
99
|
+
updatedInput?: Record<string, unknown>;
|
|
100
|
+
/**
|
|
101
|
+
* For post_tool_use: text appended to the tool's content (visible to
|
|
102
|
+
* the model on the next LLM call). Used by linter/typecheck handlers
|
|
103
|
+
* to surface results without re-running the tool. Multiple handlers'
|
|
104
|
+
* additionalContext entries are joined with two newlines.
|
|
105
|
+
*/
|
|
106
|
+
additionalContext?: string;
|
|
107
|
+
/**
|
|
108
|
+
* For user_prompt_submit: replace the most recent user message text
|
|
109
|
+
* with this string. Last handler wins. Used to auto-prepend project
|
|
110
|
+
* context, mask secrets, or rewrite shorthand prompts. The original
|
|
111
|
+
* prompt is logged at info level for audit purposes.
|
|
112
|
+
*/
|
|
113
|
+
updatedPrompt?: string;
|
|
35
114
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook message injection helper.
|
|
3
|
+
*
|
|
4
|
+
* Handlers return raw markdown strings via HookResult.messages. The emit
|
|
5
|
+
* site uses wrapHookMessages() to package them into a single user-role
|
|
6
|
+
* <system-reminder> Message so the model sees them as one block instead
|
|
7
|
+
* of a noisy sequence of separate user turns.
|
|
8
|
+
*
|
|
9
|
+
* Contract:
|
|
10
|
+
* - Handlers do NOT wrap their own <system-reminder>; emit-site owns the
|
|
11
|
+
* wrapper. Keeps formatting consistent and lets us evolve it (e.g. add
|
|
12
|
+
* a trailing close-tag, tag attributes) without touching every handler.
|
|
13
|
+
* - Empty / whitespace-only messages are dropped before wrapping; if
|
|
14
|
+
* nothing remains, returns null (caller skips the injection).
|
|
15
|
+
* - Multiple handlers' messages are joined with a blank line between
|
|
16
|
+
* blocks so each is visually distinct inside the reminder.
|
|
17
|
+
*/
|
|
18
|
+
import type { Message } from "../types.js";
|
|
19
|
+
export declare function wrapHookMessages(messages: string[] | undefined): Message | null;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shell-runner — execute a user-configured shell command as a hook handler.
|
|
3
|
+
*
|
|
4
|
+
* Protocol (matches Claude Code's shell-hook contract):
|
|
5
|
+
*
|
|
6
|
+
* stdin JSON.stringify({ eventName, data }) — the full HookContext
|
|
7
|
+
* envelope, including ctx.data fields like toolName / args /
|
|
8
|
+
* sessionId / isSubAgent.
|
|
9
|
+
*
|
|
10
|
+
* stdout Either ignored (when exit 0 with no/blank output) or parsed
|
|
11
|
+
* as a HookResult JSON document. Unparseable stdout on exit 0
|
|
12
|
+
* is logged at warn level and dropped — we do NOT crash the
|
|
13
|
+
* turn loop on malformed handler output.
|
|
14
|
+
*
|
|
15
|
+
* exit 0 Normal return. Stdout (if any) becomes the HookResult.
|
|
16
|
+
* exit 2 Deny / block. Stderr becomes the human-readable reason and
|
|
17
|
+
* is surfaced to the model via HookResult.messages so the LLM
|
|
18
|
+
* sees why the action was rejected.
|
|
19
|
+
* exit * Any other non-zero code = handler error. Logged at error
|
|
20
|
+
* level; we return {} (no effect) so a buggy hook never wedges
|
|
21
|
+
* the engine.
|
|
22
|
+
*
|
|
23
|
+
* ENV (passed to child):
|
|
24
|
+
* CODESHELL_HOOK_EVENT — event name, also in stdin
|
|
25
|
+
* CODESHELL_HOOK_CWD — repo root for context-aware hooks
|
|
26
|
+
* (callers may add more by reading process.env directly)
|
|
27
|
+
*
|
|
28
|
+
* timeout Defaults to 60_000 ms; settings.timeout_ms overrides.
|
|
29
|
+
* On timeout we SIGTERM then SIGKILL, log, and return {}.
|
|
30
|
+
*/
|
|
31
|
+
import type { HookContext, HookResult } from "./events.js";
|
|
32
|
+
import type { SettingsHookConfig } from "../types.js";
|
|
33
|
+
/**
|
|
34
|
+
* Run one shell-hook command and return the parsed HookResult. Catches
|
|
35
|
+
* every failure mode (spawn error, timeout, malformed JSON) and
|
|
36
|
+
* returns an empty result so the registry's chain keeps going.
|
|
37
|
+
*/
|
|
38
|
+
export declare function runShellHook(config: SettingsHookConfig, ctx: HookContext): Promise<HookResult>;
|
|
39
|
+
/**
|
|
40
|
+
* Check whether the hook should fire for the current ctx, respecting
|
|
41
|
+
* the optional `matcher` regex. Returns false for non-tool events
|
|
42
|
+
* when matcher is set (matcher only makes sense when ctx.data has a
|
|
43
|
+
* toolName field).
|
|
44
|
+
*/
|
|
45
|
+
export declare function shellHookMatches(config: SettingsHookConfig, ctx: HookContext): boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export { taskManager } from "./tool-system/builtin/task.js";
|
|
|
25
25
|
export type { Task, TaskStatus } from "./tool-system/builtin/task.js";
|
|
26
26
|
export { HookRegistry } from "./hooks/registry.js";
|
|
27
27
|
export type { HookEventName, HookContext, HookResult } from "./hooks/events.js";
|
|
28
|
+
export { wrapHookMessages } from "./hooks/inject.js";
|
|
28
29
|
export { Transcript } from "./session/transcript.js";
|
|
29
30
|
export { SessionManager } from "./session/session-manager.js";
|
|
30
31
|
export { FileHistory } from "./session/file-history.js";
|
|
@@ -43,8 +44,8 @@ export type { MicrocompactOptions } from "./context/compaction.js";
|
|
|
43
44
|
export type { SummarizeFn, CompactStrategy, OnCompactFn, ContextManagerConfig, } from "./context/manager.js";
|
|
44
45
|
export { applyToolResultPersistence, createContentReplacementState, reconstructContentReplacementState, resolveToolResultsDir, isPersistedReplacement, DEFAULT_PERSIST_THRESHOLD, PER_MESSAGE_AGGREGATE_CAP, PREVIEW_SIZE, } from "./context/tool-result-storage.js";
|
|
45
46
|
export type { ContentReplacementState } from "./context/tool-result-storage.js";
|
|
46
|
-
export { scanSkills,
|
|
47
|
-
export type { SkillDefinition
|
|
47
|
+
export { scanSkills, invalidateSkillCache } from "./skills/index.js";
|
|
48
|
+
export type { SkillDefinition } from "./skills/index.js";
|
|
48
49
|
export { Arena } from "./arena/arena.js";
|
|
49
50
|
export { MODEL_PRESETS, getMaxOutputTokens } from "./arena/model-presets.js";
|
|
50
51
|
export type { ModelPreset } from "./arena/model-presets.js";
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{a as defineProduct}from'./chunk-
|
|
1
|
+
export{a as defineProduct}from'./chunk-GGVAOPI7.js';export{a as createRunManager}from'./chunk-5QIVXFCT.js';export{Y as ArtifactTracker,k as AutoApprovalBackend,F as BUILTIN_AGENT_PRESETS,f as BUILTIN_TOOLS,X as CheckpointWriter,aa as CompositeEvaluator,x as ContextManager,G as DEFAULT_AGENT_PRESET,H as DEFAULT_CLI_PRESET,p as DEFAULT_PERSIST_THRESHOLD,R as Engine,fa as EngineRunner,T as FileRunStore,j as HeadlessApprovalBackend,_ as Heartbeat,n as HookRegistry,$ as NoopEvaluator,q as PER_MESSAGE_AGGREGATE_CAP,r as PREVIEW_SIZE,m as PermissionClassifier,N as PromptComposer,V as RunApprovalBackend,Z as RunLock,ga as RunManager,U as RunQueue,y as SectionCache,i as ToolExecutor,g as ToolRegistry,S as VALID_TRANSITIONS,v as applyToolResultPersistence,E as availableSections,L as buildPresetSystemPrompt,A as combineInstructions,s as createContentReplacementState,W as createRunAskUserFn,w as isPersistedReplacement,J as listPresetNames,C as loadSection,D as loadSections,t as reconstructContentReplacementState,I as registerPreset,B as registerSection,K as resolveAgentPreset,M as resolveBuiltinToolNames,u as resolveToolResultsDir,z as scanInstructions,e as taskManager,o as wrapHookMessages}from'./chunk-Z5HKHAO7.js';export{i as invalidateSkillCache,h as scanSkills}from'./chunk-SKIX2M5Z.js';export{a as ModelPool}from'./chunk-MLCTC53I.js';export{a as FileHistory}from'./chunk-WI6FSWGU.js';import'./chunk-P6JSTIFM.js';import'./chunk-SNFAPSLA.js';export{a as MCPManager}from'./chunk-D3TVASD2.js';export{c as SettingsManager,a as SettingsSchema,b as validateSettings}from'./chunk-FZXY554D.js';import {p}from'./chunk-M7VU4YVY.js';export{e as COMPACTABLE_TOOL_NAMES,k as applySummaryCompaction,i as buildSummarizationPrompt,a as estimateTokens,f as microcompact,h as truncateToolResult,d as windowCompact}from'./chunk-M7VU4YVY.js';export{b as MemoryManager}from'./chunk-T222K6IV.js';export{b as SessionManager,a as Transcript}from'./chunk-QTWU7UNA.js';export{j as IterativeArena,f as defaultIterateConvergence,i as getIterateFormat,g as iterateCodeFormat,e as iterateDiffRatio,h as iterateDocumentFormat}from'./chunk-K4HILJ6G.js';export{J as Arena,I as ArenaLedger,e as DiscussionStrategy,S as MODEL_PRESETS,f as PlanningStrategy,d as ReviewStrategy,z as applyReviewResult,u as buildDigest,n as collectEvidence,b as createLLMClient,v as formatDigest,T as getMaxOutputTokens,k as getStrategy,l as getStrategyForPlan,p as hasTools,B as isTerminal,y as markUnderReview,A as markUnresolved,m as planArena,s as registerClaims,a as registerProvider,x as resolveClaimStatus,t as selectClaimsForReview,o as selectTools,w as transitionClaim,C as validTransitions}from'./chunk-WONQHE4P.js';import'./chunk-GKAQH2DG.js';export{a as AnthropicClient}from'./chunk-X2S7ASQ2.js';export{b as OpenAIClient}from'./chunk-SV7H4FKT.js';import'./chunk-O5HFCH2U.js';export{a as LLMClientBase}from'./chunk-OWBYLMDD.js';export{l as ConfigError,d as ContextLimitError,a as FrameworkError,b as LLMError,c as LLMRateLimitError,i as PermissionDeniedError,j as SessionError,e as ToolError,g as ToolExecutionError,f as ToolNotFoundError,h as ToolTimeoutError,k as TranscriptError}from'./chunk-FU7AHZNN.js';export{d as logger}from'./chunk-DI26OUIU.js';import {h,a}from'./chunk-3QP5BQ3L.js';h();p();h();var vt={review:[{patterns:[/\breview\b/i,/\baudit\b/i,/\binspect\b/i,/\bcheck\b/i],weight:2},{patterns:[/\bcode\s*quality\b/i,/\bbug\b/i,/\bvulnerab/i,/\bsecur/i]},{patterns:[/\bpr\b/i,/\bpull\s*request\b/i,/\bdiff\b/i,/\bchanges?\b/i]},{patterns:[/\brefactor/i,/\bclean\s*up\b/i,/\bimprove\b/i]}],discussion:[{patterns:[/\bdiscuss\b/i,/\bdebate\b/i,/\bcompare\b/i],weight:2},{patterns:[/\bpros?\s*(and|&|\/)\s*cons?\b/i,/\btrade\s*-?\s*offs?\b/i]},{patterns:[/\bshould\s+we\b/i,/\bwhich\s+(is|approach|way)\b/i,/\bvs\.?\b/i]},{patterns:[/\bopinion\b/i,/\bthoughts?\b/i,/\badvice\b/i]}],planning:[{patterns:[/\bplan\b/i,/\bplanning\b/i,/\broadmap\b/i,/\bstrategy\b/i],weight:2},{patterns:[/\barchitect/i,/\bdesign\b/i,/\bproposal\b/i]},{patterns:[/\bimplement(ation)?\s+(plan|strategy|approach)\b/i],weight:2},{patterns:[/\bphase\b/i,/\bmilestone\b/i,/\btimeline\b/i,/\bprioritiz/i]},{patterns:[/\bbuild\b/i,/\bcreate\b/i,/\bsetup\b/i,/\bbootstrap\b/i]}]},ht=3,Mt=2;function Pt(m){let a={review:0,discussion:0,planning:0};for(let[n,i]of Object.entries(vt))for(let p of i){let u=p.weight??1;for(let d of p.patterns)d.test(m)&&(a[n]+=u);}let s=Object.entries(a).sort((n,i)=>i[1]-n[1]),[t,e]=s[0],[,l]=s[1],r=e-l;return e>=ht&&r>=Mt?{mode:t,confidence:"high",reason:`Topic strongly matches "${t}" (score: ${e}, lead: +${r})`}:e>0&&r>0?{mode:t,confidence:"low",reason:`Topic weakly matches "${t}" (score: ${e}, lead: +${r})`}:{mode:"review",confidence:"low",reason:"No strong mode signal detected, defaulting to review"}}a(Pt,"detectArenaMode");var Dt="0.2.0";export{Dt as VERSION,Pt as detectArenaMode};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sanitizeApiKey — defensive cleanup for API keys pasted via the wizard.
|
|
3
|
+
*
|
|
4
|
+
* Windows terminals (ConPTY, legacy cmd.exe, IME-active states) are the main
|
|
5
|
+
* source of breakage: bracketed-paste residue, CR endings, zero-width chars
|
|
6
|
+
* pulled in from rich-text sources (Notion / OneNote / web pages), full-width
|
|
7
|
+
* spaces from CJK IME, smart quotes from word processors. None of these are
|
|
8
|
+
* valid in any real provider's API key, so we strip them and warn.
|
|
9
|
+
*/
|
|
10
|
+
export type SanitizeResult = {
|
|
11
|
+
value: string;
|
|
12
|
+
changed: boolean;
|
|
13
|
+
warnings: string[];
|
|
14
|
+
};
|
|
15
|
+
export declare function sanitizeApiKey(raw: string): SanitizeResult;
|
|
16
|
+
/**
|
|
17
|
+
* Returns true when the sanitized key contains any non-ASCII-printable
|
|
18
|
+
* character. Real provider keys are uniformly ASCII (`[A-Za-z0-9_\-.]`-ish),
|
|
19
|
+
* so a stray non-ASCII char almost certainly means the user pasted the wrong
|
|
20
|
+
* thing (e.g. the surrounding sentence in Chinese).
|
|
21
|
+
*/
|
|
22
|
+
export declare function hasNonAsciiPrintable(s: string): boolean;
|
|
@@ -10,6 +10,28 @@
|
|
|
10
10
|
import type { LLMConfig, LLMResponse } from "../../types.js";
|
|
11
11
|
import type { CreateMessageOptions } from "../types.js";
|
|
12
12
|
import { LLMClientBase } from "../client-base.js";
|
|
13
|
+
interface RunStreamOpts {
|
|
14
|
+
idleTimeoutMs?: number;
|
|
15
|
+
requestId?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Per-chunk handler — invoked synchronously with the raw chunk for the
|
|
18
|
+
* provider to do its own parsing. Returns the text delta (or "") for
|
|
19
|
+
* the watchdog-side text accumulator used in tests.
|
|
20
|
+
*/
|
|
21
|
+
onChunk?: (chunk: any) => string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Consume an async iterable of stream chunks with an idle watchdog.
|
|
25
|
+
* Returns the accumulated text — either from onChunk return values, or
|
|
26
|
+
* by extracting choices[0].delta.content from raw chunks (useful in tests
|
|
27
|
+
* that call runStreamWithWatchdog directly without an onChunk handler).
|
|
28
|
+
*
|
|
29
|
+
* The watchdog is active whenever opts.idleTimeoutMs is explicitly provided,
|
|
30
|
+
* or when STREAM_WATCHDOG_CONFIG.enabled is true. When active, each call to
|
|
31
|
+
* iterator.next() races against an idle deadline; if the deadline fires first
|
|
32
|
+
* the function rejects with StreamIdleTimeoutError.
|
|
33
|
+
*/
|
|
34
|
+
export declare function runStreamWithWatchdog<T = any>(stream: AsyncIterable<T>, opts?: RunStreamOpts): Promise<string>;
|
|
13
35
|
export declare class OpenAIClient extends LLMClientBase {
|
|
14
36
|
private _client;
|
|
15
37
|
private _forceMaxCompletionTokens;
|
|
@@ -39,3 +61,4 @@ export declare class OpenAIClient extends LLMClientBase {
|
|
|
39
61
|
private convertTools;
|
|
40
62
|
private handleApiError;
|
|
41
63
|
}
|
|
64
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry classification — which errors trigger an automatic retry of the
|
|
3
|
+
* streaming LLM call?
|
|
4
|
+
*
|
|
5
|
+
* Stream-idle timeouts are retried — the upstream may have just recovered.
|
|
6
|
+
* User-initiated aborts (AbortError) are never retried — the user explicitly
|
|
7
|
+
* asked for the work to stop.
|
|
8
|
+
*
|
|
9
|
+
* Other transient errors (5xx, 429, ECONNRESET) are out of scope for this
|
|
10
|
+
* iteration; add them here as they become observed.
|
|
11
|
+
*/
|
|
12
|
+
export declare function isRetryable(err: unknown): boolean;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stream Idle Watchdog — guarantees an upper bound on the time a streaming
|
|
3
|
+
* LLM call can spend with no bytes arriving. Without this, a wedged HTTP/2
|
|
4
|
+
* connection can hang the session indefinitely; the SDK's request timeout
|
|
5
|
+
* only covers the initial fetch, not the streaming body.
|
|
6
|
+
*
|
|
7
|
+
* Disabled by default. Enabled via env CODESHELL_ENABLE_STREAM_WATCHDOG=1.
|
|
8
|
+
*/
|
|
9
|
+
export interface StreamWatchdogOptions {
|
|
10
|
+
/** Abort the stream when no chunks arrive within this window. */
|
|
11
|
+
idleTimeoutMs: number;
|
|
12
|
+
/** Called when the watchdog decides to abort. */
|
|
13
|
+
onTimeout: () => void;
|
|
14
|
+
/**
|
|
15
|
+
* Called at idleTimeoutMs/2 (or warningMs if provided). Logging only —
|
|
16
|
+
* the watchdog does not abort on warning. The argument is the configured
|
|
17
|
+
* warning threshold (NOT real-time elapsed) so callers can format the log
|
|
18
|
+
* without re-deriving it.
|
|
19
|
+
*/
|
|
20
|
+
onWarning?: (warnThresholdMs: number) => void;
|
|
21
|
+
/** Override the warning trigger; defaults to idleTimeoutMs / 2. */
|
|
22
|
+
warningMs?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface StreamWatchdog {
|
|
25
|
+
/** Re-arm both timers — call after every chunk. */
|
|
26
|
+
reset(): void;
|
|
27
|
+
/** Clear all timers permanently — call in finally. */
|
|
28
|
+
dispose(): void;
|
|
29
|
+
}
|
|
30
|
+
export declare class StreamIdleTimeoutError extends Error {
|
|
31
|
+
idleMs: number;
|
|
32
|
+
requestId?: string | undefined;
|
|
33
|
+
readonly kind = "stream-idle-timeout";
|
|
34
|
+
constructor(idleMs: number, requestId?: string | undefined);
|
|
35
|
+
}
|
|
36
|
+
export declare function createStreamWatchdog(opts: StreamWatchdogOptions): StreamWatchdog;
|
|
37
|
+
/** Environment-driven defaults. Read once at import time is fine — these never change inside a process. */
|
|
38
|
+
export declare const STREAM_WATCHDOG_CONFIG: {
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
idleTimeoutMs: number;
|
|
41
|
+
retries: number;
|
|
42
|
+
};
|
package/dist/logging/logger.d.ts
CHANGED
|
@@ -24,6 +24,20 @@
|
|
|
24
24
|
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
25
25
|
export declare function setCurrentSid(sid: string): void;
|
|
26
26
|
export declare function getCurrentSid(): string;
|
|
27
|
+
/**
|
|
28
|
+
* Run `fn` inside an ALS scope tagged with `sid`. All log lines emitted
|
|
29
|
+
* synchronously or via `await` inside `fn` resolve `getCurrentSid()` to
|
|
30
|
+
* this `sid` — concurrent Engine.run invocations no longer race on a
|
|
31
|
+
* module global.
|
|
32
|
+
*/
|
|
33
|
+
export declare function runWithSid<T>(sid: string, fn: () => T | Promise<T>): T | Promise<T>;
|
|
34
|
+
/**
|
|
35
|
+
* Bind `sid` to the *current* async context without needing a callback.
|
|
36
|
+
* The binding lasts until the current async stack unwinds; concurrent
|
|
37
|
+
* sibling async chains keep their own bindings. Engine.run uses this so
|
|
38
|
+
* we don't have to indent the entire (long) method body into a callback.
|
|
39
|
+
*/
|
|
40
|
+
export declare function enterSid(sid: string): void;
|
|
27
41
|
export declare function getInMemoryErrors(): ReadonlyArray<{
|
|
28
42
|
msg: string;
|
|
29
43
|
t: string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{c as SettingsManager}from'./chunk-FZXY554D.js';import'./chunk-3QP5BQ3L.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{a as MCPManager}from'./chunk-D3TVASD2.js';import'./chunk-DI26OUIU.js';import'./chunk-3QP5BQ3L.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{b as OpenAIClient,a as runStreamWithWatchdog}from'./chunk-SV7H4FKT.js';import'./chunk-O5HFCH2U.js';import'./chunk-OWBYLMDD.js';import'./chunk-FU7AHZNN.js';import'./chunk-DI26OUIU.js';import'./chunk-3QP5BQ3L.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{d as findOpenRouterModel,c as getOpenRouterModels,b as getOpenRouterSnapshot,e as listOpenRouterModelsByVendor,a as setOpenRouterSnapshot}from'./chunk-
|
|
1
|
+
export{d as findOpenRouterModel,c as getOpenRouterModels,b as getOpenRouterSnapshot,e as listOpenRouterModelsByVendor,a as setOpenRouterSnapshot}from'./chunk-GKAQH2DG.js';import'./chunk-3QP5BQ3L.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{a as syncOpenRouterCatalog}from'./chunk-AFMP73YP.js';export{b as getOpenRouterSnapshot}from'./chunk-GKAQH2DG.js';import'./chunk-3QP5BQ3L.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {g,f,e,d,i}from'./chunk-SKIX2M5Z.js';import {h,a,b}from'./chunk-3QP5BQ3L.js';import {existsSync,readFileSync,mkdirSync,writeFileSync,rmSync,readdirSync,statSync,cpSync,mkdtempSync}from'fs';import {join,dirname,isAbsolute,resolve}from'path';import {homedir,tmpdir}from'os';import {spawn}from'child_process';h();h();function H(e){let r=e.trim();if(r==="")return null;if((r.startsWith("/")||/^[a-zA-Z]:[\\/]/.test(r))&&r.endsWith(".git"))return {source:"git",url:r};if(r.match(/^[a-zA-Z0-9._-]+@[^:\s]+:[^\s]+$/))return {source:"git",url:r};if(r.startsWith("https://")||r.startsWith("http://")){let i;try{i=new URL(r);}catch{return null}return i.hostname==="github.com"||i.hostname==="www.github.com"?i.pathname.match(/^\/([^/]+\/[^/]+?)(\/|\.git|$)/)?{source:"git",url:r.endsWith(".git")?r:`${r}.git`}:null:r.endsWith(".git")?{source:"git",url:r}:null}let n=r.match(/^([A-Za-z0-9][A-Za-z0-9._-]*)\/([A-Za-z0-9._-]+?)(\.git)?$/);if(n){let i=n[1],s=n[2];return {source:"github",repo:`${i}/${s}`}}return null}a(H,"parseMarketplaceInput");function q(e){if(e.source==="github"){let i=e.repo.split("/");return i[i.length-1].toLowerCase()}let t=e.url.replace(/\.git$/,"").split(/[/:]/).filter(Boolean);return (t[t.length-1]??"marketplace").toLowerCase()}a(q,"deriveMarketplaceName");h();h();async function x(e,r,t=6e4){return new Promise(n=>{let i=spawn("git",e,{cwd:r,stdio:["ignore","pipe","pipe"]}),s="",o="";i.stdout.on("data",l=>s+=l.toString()),i.stderr.on("data",l=>o+=l.toString());let a=setTimeout(()=>{i.kill("SIGKILL"),n({ok:false,error:`git ${e.join(" ")} timed out after ${t}ms`});},t);i.on("close",l=>{clearTimeout(a),n(l===0?{ok:true,stdout:s.trim()}:{ok:false,error:`git ${e.join(" ")} exited ${l}: ${o.trim()||"unknown error"}`});}),i.on("error",l=>{clearTimeout(a),n({ok:false,error:`git ${e.join(" ")} failed: ${l.message}`});});})}a(x,"runGit");async function $(e,r,t){let n=["clone","--depth","1"];return t?.ref&&n.push("--branch",t.ref),n.push("--",e,r),x(n)}a($,"gitClone");async function C(e){return x(["rev-parse","HEAD"],e)}a(C,"gitRevParseHead");async function z(e,r){let t=["fetch","--depth","1","origin"];r&&t.push(r);let n=await x(t,e);return n.ok?x(["reset","--hard",r?`origin/${r}`:"FETCH_HEAD"],e):n}a(z,"gitFetchAndReset");function j(e){return `https://github.com/${e}.git`}a(j,"githubRepoToCloneUrl");h();function Se(){return process.env.HOME??homedir()}a(Se,"userHome");function V(){return join(Se(),".code-shell","plugins","known_marketplaces.json")}a(V,"knownMarketplacesPath");function m(){let e=V();if(!existsSync(e))return {};try{let r=JSON.parse(readFileSync(e,"utf-8"));if(r&&typeof r=="object"&&!Array.isArray(r))return r}catch{}return {}}a(m,"readKnownMarketplaces");function J(e){let r=V();mkdirSync(dirname(r),{recursive:true}),writeFileSync(r,JSON.stringify(e,null,2)+`
|
|
2
|
+
`,"utf-8");}a(J,"writeKnownMarketplaces");function B(e,r){let t=m();t[e]=r,J(t);}a(B,"upsertKnownMarketplace");function N(e){let r=m();return e in r?(delete r[e],J(r),true):false}a(N,"removeKnownMarketplace");h();function S(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}a(S,"isObject");function Me(e,r){if(typeof e=="string")return e.length===0?{ok:false,error:`${r}: source string is empty`}:{ok:true,value:e};if(!S(e))return {ok:false,error:`${r}: source must be a string or object`};let t=e.source;return t==="git"?typeof e.url!="string"||!e.url?{ok:false,error:`${r}: git source requires url`}:{ok:true,value:{source:"git",url:e.url,ref:typeof e.ref=="string"?e.ref:void 0,sha:typeof e.sha=="string"?e.sha:void 0}}:t==="github"?typeof e.repo!="string"||!e.repo.includes("/")?{ok:false,error:`${r}: github source requires repo "owner/name"`}:{ok:true,value:{source:"github",repo:e.repo,ref:typeof e.ref=="string"?e.ref:void 0,sha:typeof e.sha=="string"?e.sha:void 0}}:t==="git-subdir"?typeof e.url!="string"||!e.url?{ok:false,error:`${r}: git-subdir source requires url`}:typeof e.path!="string"||!e.path?{ok:false,error:`${r}: git-subdir source requires path`}:{ok:true,value:{source:"git-subdir",url:e.url,path:e.path,ref:typeof e.ref=="string"?e.ref:void 0,sha:typeof e.sha=="string"?e.sha:void 0}}:{ok:false,error:`${r}: unsupported source type "${String(t)}" (MVP supports git, github, git-subdir, or a string path)`}}a(Me,"validatePluginEntrySource");function be(e,r){let t=`plugins[${r}]`;if(!S(e))return {ok:false,error:`${t}: must be an object`};if(typeof e.name!="string"||!e.name)return {ok:false,error:`${t}: name is required`};if(e.source===void 0)return {ok:false,error:`${t}: source is required`};let n=Me(e.source,`${t}.source`);if(!n.ok)return n;let i;if(e.author!==void 0){if(!S(e.author)||typeof e.author.name!="string")return {ok:false,error:`${t}.author: must be an object with name`};i={name:e.author.name,email:typeof e.author.email=="string"?e.author.email:void 0};}return {ok:true,value:{name:e.name,description:typeof e.description=="string"?e.description:void 0,author:i,category:typeof e.category=="string"?e.category:void 0,source:n.value,homepage:typeof e.homepage=="string"?e.homepage:void 0}}}a(be,"validatePluginEntry");function L(e){if(!S(e))return {ok:false,error:"marketplace.json: not an object"};if(typeof e.name!="string"||!e.name)return {ok:false,error:"marketplace.json: name is required"};if(!S(e.owner)||typeof e.owner.name!="string")return {ok:false,error:"marketplace.json: owner.name is required"};if(!Array.isArray(e.plugins))return {ok:false,error:"marketplace.json: plugins must be an array"};let r=[];for(let t=0;t<e.plugins.length;t++){let n=be(e.plugins[t],t);if(!n.ok)return n;r.push(n.value);}return {ok:true,value:{name:e.name,description:typeof e.description=="string"?e.description:void 0,owner:{name:e.owner.name,email:typeof e.owner.email=="string"?e.owner.email:void 0},plugins:r}}}a(L,"validateMarketplace");function Re(){return process.env.HOME??homedir()}a(Re,"userHome");function W(){return join(Re(),".code-shell","plugins","marketplaces")}a(W,"marketplacesRoot");function D(e){return join(W(),e)}a(D,"marketplaceDir");function Z(e){return join(D(e),".claude-plugin","marketplace.json")}a(Z,"marketplaceJsonPath");function xe(e){return e.source==="github"?j(e.repo):e.url}a(xe,"sourceToCloneUrl");async function Y(e,r){let t=D(e),i=m()[e];if(i&&JSON.stringify(i.source)===JSON.stringify(r)&&existsSync(t)){let g=(r.source==="git",void 0);await z(t,g);}else {existsSync(t)&&rmSync(t,{recursive:true,force:true}),mkdirSync(W(),{recursive:true});let g=await $(xe(r),t);if(!g.ok)return {ok:false,error:g.error}}let o=Z(e);if(!existsSync(o))return rmSync(t,{recursive:true,force:true}),{ok:false,error:`Marketplace ${e}: .claude-plugin/marketplace.json not found in the cloned repository.`};let a;try{a=JSON.parse(readFileSync(o,"utf-8"));}catch(g){return rmSync(t,{recursive:true,force:true}),{ok:false,error:`Marketplace ${e}: failed to parse marketplace.json: ${g.message}`}}let l=L(a);if(!l.ok)return rmSync(t,{recursive:true,force:true}),{ok:false,error:`Marketplace ${e}: ${l.error}`};let d={source:r,installLocation:t,lastUpdated:new Date().toISOString()};return B(e,d),{ok:true,name:e,marketplace:l.value,replaced:!!i}}a(Y,"addMarketplace");function Q(e){let r=N(e),t=D(e);return existsSync(t)&&rmSync(t,{recursive:true,force:true}),r}a(Q,"removeMarketplace");function F(e){if(!m()[e])return null;let t=Z(e);if(!existsSync(t))return null;let n;try{n=JSON.parse(readFileSync(t,"utf-8"));}catch{return null}let i=L(n);return i.ok?i.value:null}a(F,"loadMarketplace");function X(){let e=m(),r=[];for(let[t,n]of Object.entries(e)){let i=F(t);r.push({name:t,source:n.source,installLocation:n.installLocation,lastUpdated:n.lastUpdated,pluginCount:i?i.plugins.length:-1});}return r.sort((t,n)=>t.name.localeCompare(n.name))}a(X,"listMarketplaces");h();h();var K="CLAUDE_PLUGIN_ROOT",te="CODESHELL_PLUGIN_ROOT",Ie=8192;function Oe(e){let r=Math.min(e.length,Ie);for(let t=0;t<r;t++)if(e[t]===0)return true;return false}a(Oe,"isLikelyBinary");function ne(e,r){let t;try{t=readdirSync(e,{withFileTypes:!0});}catch{return}for(let n of t){let i=join(e,n.name);if(n.isSymbolicLink())continue;if(n.isDirectory()){if(n.name===".git")continue;ne(i,r);continue}if(!n.isFile())continue;let s;try{s=readFileSync(i);}catch{continue}if(r.filesScanned+=1,Oe(s))continue;let o=s.toString("utf8");if(!o.includes(K))continue;let a=o.split(K).join(te);try{writeFileSync(i,a,"utf8"),r.filesRewritten+=1,r.rewrittenPaths.push(i);}catch{}}}a(ne,"walkAndRewrite");function ie(e){let r={filesScanned:0,filesRewritten:0,rewrittenPaths:[]},t=false;try{t=statSync(e).isDirectory();}catch{t=false;}if(!t)return r;ne(e,r);let n={rewrittenAt:new Date().toISOString(),from:K,to:te,filesScanned:r.filesScanned,filesRewritten:r.filesRewritten,note:"codeshell rewrote plugin files at install time so ${CLAUDE_PLUGIN_ROOT} placeholders use codeshell's native env var. Upstream plugin sources are unchanged; only this local copy was modified."};try{writeFileSync(join(e,".code-shell-installed.json"),JSON.stringify(n,null,2)+`
|
|
3
|
+
`,"utf8");}catch{}return r}a(ie,"rewritePluginVars");function Ke(){return process.env.HOME??homedir()}a(Ke,"userHome");function se(){return join(Ke(),".code-shell","plugins","cache")}a(se,"pluginCacheRoot");function E(e,r,t){return join(se(),e,r,t)}a(E,"pluginCacheDir");async function _e(e,r,t){let n=isAbsolute(r)?r:resolve(e,r);return existsSync(n)?(mkdirSync(dirname(t),{recursive:true}),existsSync(t)&&rmSync(t,{recursive:true,force:true}),cpSync(n,t,{recursive:true}),{ok:true,sha:void 0}):{ok:false,error:`plugin source path "${r}" not found in marketplace`}}a(_e,"materializePath");async function Ge(e,r,t){mkdirSync(dirname(t),{recursive:true}),existsSync(t)&&rmSync(t,{recursive:true,force:true});let n=await $(e,t,r?{ref:r}:void 0);if(!n.ok)return {ok:false,error:n.error};let i=await C(t);return i.ok?{ok:true,sha:i.stdout}:{ok:false,error:i.error}}a(Ge,"materializeGit");async function He(e,r,t,n){let i=mkdtempSync(join(tmpdir(),"plugin-clone-"));try{let s=await $(e,i+"/repo",t?{ref:t}:void 0);if(!s.ok)return {ok:!1,error:s.error};let o=await C(i+"/repo");if(!o.ok)return {ok:!1,error:o.error};let a=join(i,"repo",r);return existsSync(a)?(mkdirSync(dirname(n),{recursive:!0}),existsSync(n)&&rmSync(n,{recursive:!0,force:!0}),cpSync(a,n,{recursive:!0}),{ok:!0,sha:o.stdout}):{ok:!1,error:`git-subdir path "${r}" not found in cloned repository`}}finally{rmSync(i,{recursive:true,force:true});}}a(He,"materializeGitSubdir");function oe(e){return e?e.slice(0,12):"unknown"}a(oe,"shortSha");async function qe(e,r,t,n){let i=E(t,n,"_pending_");if(typeof e=="string"){let s=await _e(r,e,i);if(!s.ok)return s;let o=E(t,n,"local");return existsSync(o)&&rmSync(o,{recursive:true,force:true}),mkdirSync(dirname(o),{recursive:true}),cpSync(i,o,{recursive:true}),rmSync(i,{recursive:true,force:true}),{ok:true,cacheDir:o,version:"local",sha:void 0}}if(e.source==="git"||e.source==="github"){let s=e.source==="github"?j(e.repo):e.url,o=await Ge(s,e.ref,i);if(!o.ok)return existsSync(i)&&rmSync(i,{recursive:true,force:true}),o;let a=oe(o.sha),l=E(t,n,a);return existsSync(l)&&rmSync(l,{recursive:true,force:true}),mkdirSync(dirname(l),{recursive:true}),cpSync(i,l,{recursive:true}),rmSync(i,{recursive:true,force:true}),{ok:true,cacheDir:l,version:a,sha:o.sha}}if(e.source==="git-subdir"){let s=await He(e.url,e.path,e.ref,i);if(!s.ok)return existsSync(i)&&rmSync(i,{recursive:true,force:true}),s;let o=oe(s.sha),a=E(t,n,o);return existsSync(a)&&rmSync(a,{recursive:true,force:true}),mkdirSync(dirname(a),{recursive:true}),cpSync(i,a,{recursive:true}),rmSync(i,{recursive:true,force:true}),{ok:true,cacheDir:a,version:o,sha:s.sha}}return {ok:false,error:`unsupported source type "${e.source}"`}}a(qe,"materialize");async function ue(e$1,r){let n=m()[r];if(!n)return {ok:false,error:`marketplace "${r}" not found. Run /plugin marketplace add first.`};let i=F(r);if(!i)return {ok:false,error:`marketplace "${r}" is registered but its manifest could not be loaded.`};let s=i.plugins.find(fe=>fe.name===e$1);if(!s)return {ok:false,error:`plugin "${e$1}" not found in marketplace "${r}".`};let o=await qe(s.source,n.installLocation,r,e$1);if(!o.ok)return o;let a=ie(o.cacheDir),l=g(e$1,r);f(l);let d=new Date().toISOString(),g$1={scope:"user",installPath:o.cacheDir,version:o.version,installedAt:d,lastUpdated:d,...o.sha?{gitCommitSha:o.sha}:{}};return e(l,g$1),{ok:true,entry:g$1,freshlyCloned:true,varRewrite:{filesScanned:a.filesScanned,filesRewritten:a.filesRewritten}}}a(ue,"installPlugin");function ae(e,r){let t=g(e,r),i=d().plugins[t],s=false;if(i){for(let l of i)l.installPath&&existsSync(l.installPath)&&(rmSync(l.installPath,{recursive:true,force:true}),s=true);let a=join(se(),r,e);try{b("fs").rmdirSync(a);}catch{}}let o=f(t);return {ok:o||s,removedFromManifest:o,removedFromDisk:s}}a(ae,"uninstallPlugin");function le(){let e=d(),r=[];for(let[t,n]of Object.entries(e.plugins))for(let i of n)r.push({key:t,entry:i});return r.sort((t,n)=>t.key.localeCompare(n.key)),r}a(le,"listInstalled");var _=["Usage:"," /plugin marketplace add <git-url-or-owner/repo>"," /plugin marketplace remove <name>"," /plugin marketplace list"," /plugin install <plugin>@<marketplace>"," /plugin uninstall <plugin>@<marketplace>"," /plugin list"].join(`
|
|
4
|
+
`);function ce(e){let r=e.lastIndexOf("@");return r<=0||r===e.length-1?null:{plugin:e.slice(0,r),marketplace:e.slice(r+1)}}a(ce,"splitInstallKey");async function Ar(e){let r=e.trim().split(/\s+/).filter(Boolean);if(r.length===0)return _;let t=r[0];if(t==="marketplace"){let n=r[1];if(n==="add"){let i=r.slice(2).join(" ").trim();if(!i)return "Usage: /plugin marketplace add <git-url-or-owner/repo>";let s=H(i);if(!s)return `Cannot parse "${i}" as a marketplace source.
|
|
5
|
+
Supported forms: owner/repo, https://...git, git@host:owner/repo[.git], /abs/path/to/repo.git`;let o=q(s),a=await Y(o,s);if(!a.ok)return `Failed to add marketplace "${o}": ${a.error}`;let l=[a.replaced?`Refreshed marketplace "${a.name}"`:`Added marketplace "${a.name}"`,` plugins available: ${a.marketplace.plugins.length}`];for(let d of a.marketplace.plugins.slice(0,10))l.push(` - ${d.name}${d.description?`: ${d.description}`:""}`);return a.marketplace.plugins.length>10&&l.push(` ... (${a.marketplace.plugins.length-10} more)`),l.join(`
|
|
6
|
+
`)}if(n==="remove"){let i$1=r[2];if(!i$1)return "Usage: /plugin marketplace remove <name>";let s=Q(i$1);return i(),s?`Removed marketplace "${i$1}"`:`Marketplace "${i$1}" not found`}if(n==="list"||n===void 0){let i=X();if(i.length===0)return "No marketplaces. Use /plugin marketplace add <url>.";let s=[`Marketplaces (${i.length}):`];for(let o of i){let a=o.source.source==="github"?`github:${o.source.repo}`:o.source.url,l=o.pluginCount>=0?`${o.pluginCount} plugins`:"manifest unreadable";s.push(` ${o.name} (${a}) \u2014 ${l}`);}return s.join(`
|
|
7
|
+
`)}return `Unknown subcommand "marketplace ${n}".
|
|
8
|
+
${_}`}if(t==="install"){let n=r[1];if(!n)return "Usage: /plugin install <plugin>@<marketplace>";let i$1=ce(n);if(!i$1)return `Expected <plugin>@<marketplace>, got "${n}"`;let s=await ue(i$1.plugin,i$1.marketplace);if(!s.ok)return `Failed to install ${n}: ${s.error}`;i();let o=[`Installed ${n}`,` version: ${s.entry.version}`,` path: ${s.entry.installPath}`];return s.varRewrite.filesRewritten>0&&o.push(` rewrote: ${s.varRewrite.filesRewritten} file(s) \u2014 \${CLAUDE_PLUGIN_ROOT} \u2192 \${CODESHELL_PLUGIN_ROOT}`),o.join(`
|
|
9
|
+
`)}if(t==="uninstall"){let n=r[1];if(!n)return "Usage: /plugin uninstall <plugin>@<marketplace>";let i$1=ce(n);if(!i$1)return `Expected <plugin>@<marketplace>, got "${n}"`;let s=ae(i$1.plugin,i$1.marketplace);if(i(),!s.removedFromManifest&&!s.removedFromDisk)return `${n} was not installed`;let o=[];return s.removedFromManifest&&o.push("manifest entry"),s.removedFromDisk&&o.push("cache dir"),`Uninstalled ${n} (removed ${o.join(" + ")})`}if(t==="list"){let n=le();if(n.length===0)return "No plugins installed.";let i=[`Installed plugins (${n.length}):`];for(let{key:s,entry:o}of n)i.push(` ${s} v${o.version} ${o.installPath}`);return i.join(`
|
|
10
|
+
`)}return `Unknown subcommand "${t}".
|
|
11
|
+
${_}`}a(Ar,"runPluginCommand");export{Ar as runPluginCommand};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal `git` subprocess wrappers used by the plugin marketplace. Each
|
|
3
|
+
* function returns a discriminated union so callers can produce useful
|
|
4
|
+
* error messages without try/catching. Mirrors Claude Code's
|
|
5
|
+
* utils/plugins/marketplaceManager.ts:loadAndCacheMarketplace at the
|
|
6
|
+
* subset we need for the MVP.
|
|
7
|
+
*/
|
|
8
|
+
export type GitResult = {
|
|
9
|
+
ok: true;
|
|
10
|
+
stdout: string;
|
|
11
|
+
} | {
|
|
12
|
+
ok: false;
|
|
13
|
+
error: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function gitClone(url: string, destDir: string, options?: {
|
|
16
|
+
ref?: string;
|
|
17
|
+
}): Promise<GitResult>;
|
|
18
|
+
export declare function gitRevParseHead(repoDir: string): Promise<GitResult>;
|
|
19
|
+
export declare function gitFetchAndReset(repoDir: string, ref?: string): Promise<GitResult>;
|
|
20
|
+
/**
|
|
21
|
+
* Github source helper: convert "owner/repo" to an https clone URL.
|
|
22
|
+
*/
|
|
23
|
+
export declare function githubRepoToCloneUrl(repo: string): string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read/write ~/.code-shell/plugins/installed_plugins.json (V2 format).
|
|
3
|
+
* Byte-compatible with Claude Code's analogous file.
|
|
4
|
+
*/
|
|
5
|
+
import type { InstalledPluginsV2, PluginInstallEntry } from "./types.js";
|
|
6
|
+
export declare function installedPluginsPath(): string;
|
|
7
|
+
export declare function readInstalledPlugins(): InstalledPluginsV2;
|
|
8
|
+
export declare function writeInstalledPlugins(data: InstalledPluginsV2): void;
|
|
9
|
+
/**
|
|
10
|
+
* Append an install entry for `<plugin>@<marketplace>`. Multiple entries
|
|
11
|
+
* for the same key are allowed (different scopes); MVP only writes scope:"user".
|
|
12
|
+
*/
|
|
13
|
+
export declare function appendInstallEntry(key: string, entry: PluginInstallEntry): void;
|
|
14
|
+
export declare function removeInstallEntries(key: string): boolean;
|
|
15
|
+
export declare function pluginInstallKey(plugin: string, marketplace: string): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read/write ~/.code-shell/plugins/known_marketplaces.json.
|
|
3
|
+
* File format is byte-compatible with Claude Code's analogous file.
|
|
4
|
+
*/
|
|
5
|
+
import type { KnownMarketplaces, KnownMarketplace } from "./types.js";
|
|
6
|
+
export declare function knownMarketplacesPath(): string;
|
|
7
|
+
export declare function readKnownMarketplaces(): KnownMarketplaces;
|
|
8
|
+
export declare function writeKnownMarketplaces(data: KnownMarketplaces): void;
|
|
9
|
+
export declare function upsertKnownMarketplace(name: string, entry: KnownMarketplace): void;
|
|
10
|
+
export declare function removeKnownMarketplace(name: string): boolean;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* loadPluginHooks — scan every installed plugin's hooks/hooks.json and
|
|
3
|
+
* register the command-type hooks with the engine's HookRegistry.
|
|
4
|
+
*
|
|
5
|
+
* Plugin hooks.json schema (Claude Code-compatible):
|
|
6
|
+
*
|
|
7
|
+
* {
|
|
8
|
+
* "hooks": {
|
|
9
|
+
* "<EventName>": [
|
|
10
|
+
* {
|
|
11
|
+
* "matcher": "<regex>", // optional; semantics depend on event
|
|
12
|
+
* "hooks": [
|
|
13
|
+
* {
|
|
14
|
+
* "type": "command",
|
|
15
|
+
* "command": "...", // ${CODESHELL_PLUGIN_ROOT} placeholder
|
|
16
|
+
* "async": false, // currently ignored (always sync await)
|
|
17
|
+
* "timeout_ms": 60000 // optional override
|
|
18
|
+
* }
|
|
19
|
+
* ]
|
|
20
|
+
* }
|
|
21
|
+
* ]
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* Event-name mapping (CC PascalCase → codeshell snake_case):
|
|
26
|
+
* SessionStart → on_session_start
|
|
27
|
+
* UserPromptSubmit → user_prompt_submit
|
|
28
|
+
* PreToolUse → pre_tool_use
|
|
29
|
+
* PostToolUse → post_tool_use
|
|
30
|
+
* PreCompact → pre_compact
|
|
31
|
+
* Notification → notification
|
|
32
|
+
* Stop → on_session_end
|
|
33
|
+
* SubagentStop → (skipped — codeshell sub-agents don't surface this event)
|
|
34
|
+
*
|
|
35
|
+
* matcher semantics:
|
|
36
|
+
* SessionStart — matches against ctx.data.source ("startup"|"resume"|"clear"|"compact")
|
|
37
|
+
* PreToolUse /
|
|
38
|
+
* PostToolUse — matches against ctx.data.toolName
|
|
39
|
+
* other events — matcher is currently ignored (the CC spec doesn't
|
|
40
|
+
* define matcher semantics for them either)
|
|
41
|
+
*
|
|
42
|
+
* Errors are swallowed per-plugin: a malformed hooks.json from one plugin
|
|
43
|
+
* must not block other plugins from loading.
|
|
44
|
+
*/
|
|
45
|
+
import type { HookRegistry } from "../hooks/registry.js";
|
|
46
|
+
/**
|
|
47
|
+
* Walk installed plugins and register their command hooks. Safe to call
|
|
48
|
+
* multiple times — the caller is responsible for not double-registering
|
|
49
|
+
* (HookRegistry has no de-dup; the engine constructor calls this exactly
|
|
50
|
+
* once after creating the registry).
|
|
51
|
+
*/
|
|
52
|
+
export declare function loadPluginHooks(registry: HookRegistry): void;
|