dsh-loop-engine 1.0.0-rc3 → 1.0.0-rc4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -103
- package/README.zh.md +9 -51
- package/lib/client.js +212 -3
- package/lib/index.js +309 -125
- package/lib/invariant.js +2 -1
- package/lib/types/client/LoopEngineComposerSelect.d.ts +40 -0
- package/lib/types/client/index.d.ts +1 -0
- package/lib/types/client/locales.d.ts +2 -0
- package/lib/types/client/store.d.ts +13 -1
- package/lib/types/commands.d.ts +43 -6
- package/lib/types/driver-core/context-files.d.ts +62 -0
- package/lib/types/engine-codex/skills.d.ts +13 -10
- package/lib/types/engine-pi/skills.d.ts +40 -11
- package/lib/types/settings.d.ts +2 -0
- package/lib/types/skills.d.ts +16 -0
- package/package.json +1 -1
|
@@ -9,12 +9,14 @@ import type { LoopEngineId } from '../settings.ts';
|
|
|
9
9
|
export interface LoopEngineState {
|
|
10
10
|
status: 'loading' | 'ready' | 'unavailable' | 'saving';
|
|
11
11
|
engine: LoopEngineId;
|
|
12
|
+
showInComposer: boolean;
|
|
12
13
|
writable: boolean;
|
|
13
14
|
error: string | null;
|
|
14
15
|
}
|
|
15
|
-
/** Narrow a wire section to the stored engine id; an invalid one reads default. */
|
|
16
|
+
/** Narrow a wire section to the stored engine id and display toggle; an invalid one reads default. */
|
|
16
17
|
export declare function decodeLoopEngine(section: unknown): {
|
|
17
18
|
engine: LoopEngineId;
|
|
19
|
+
showInComposer: boolean;
|
|
18
20
|
} | undefined;
|
|
19
21
|
/** Coordinates the settings-backed loop engine selection. */
|
|
20
22
|
export declare class LoopEngineStore {
|
|
@@ -28,6 +30,7 @@ export declare class LoopEngineStore {
|
|
|
28
30
|
*/
|
|
29
31
|
constructor(scope: SettingsScope<{
|
|
30
32
|
engine: LoopEngineId;
|
|
33
|
+
showInComposer: boolean;
|
|
31
34
|
}>);
|
|
32
35
|
/** Begin following the bound scope and publish its current answer. */
|
|
33
36
|
load(): void;
|
|
@@ -38,6 +41,15 @@ export declare class LoopEngineStore {
|
|
|
38
41
|
* @returns whether the write landed.
|
|
39
42
|
*/
|
|
40
43
|
setEngine(engine: LoopEngineId): Promise<boolean>;
|
|
44
|
+
/**
|
|
45
|
+
* Persist whether the composer shows the engine picker. Success is judged
|
|
46
|
+
* against the snapshot the write left behind, so a refused write reports
|
|
47
|
+
* error after its recovery. Unlike {@link setEngine}, landing does not reload
|
|
48
|
+
* the page — the toggle only changes composer visibility.
|
|
49
|
+
* @param show - whether the chat page composer reveals the engine picker.
|
|
50
|
+
* @returns whether the write landed.
|
|
51
|
+
*/
|
|
52
|
+
setShowInComposer(show: boolean): Promise<boolean>;
|
|
41
53
|
/** Stop following the scope. */
|
|
42
54
|
dispose(): void;
|
|
43
55
|
private derive;
|
package/lib/types/commands.d.ts
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Claude Code
|
|
2
|
+
* Claude Code slash-command bridge.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The dsh `commands` runtime executes a registered command locally — the line
|
|
5
|
+
* is consumed and never reaches the model — so a command whose real processing
|
|
6
|
+
* lives inside the Claude Code CLI must forward the raw line back to the
|
|
7
|
+
* engine. The definitions here do exactly that: the handler delivers
|
|
8
|
+
* `/<name> [args]` to the receiving agent as a plain user message, and the CLI
|
|
9
|
+
* then expands it natively (built-ins and custom `.claude/commands/*.md`).
|
|
10
|
+
* Registering the built-ins keeps them visible in the dsh web slash menu;
|
|
11
|
+
* unregistered `/lines` pass through as user text, but the menu would hide the
|
|
12
|
+
* engine's command surface.
|
|
13
|
+
*
|
|
14
|
+
* User-level custom slash commands (`~/.claude/commands/*.md`) are discovered
|
|
15
|
+
* and registered the same way, so they appear in the menu AND reach the CLI.
|
|
16
|
+
* Project-level `.claude/commands/` files are left to the CLI entirely: they
|
|
17
|
+
* are cwd-dependent, and a global dsh registration would collide across
|
|
18
|
+
* projects.
|
|
8
19
|
*
|
|
9
20
|
* @module dsh-loop-engine/commands
|
|
10
21
|
*/
|
|
22
|
+
import type { UserMessage } from '@deepseek-ai/dsh-session';
|
|
11
23
|
/** Minimal shape of a DSH command definition (avoiding a direct peer dep on @deepseek-ai/dsh-commands). */
|
|
12
24
|
export interface CommandDefinition {
|
|
13
25
|
readonly name: string;
|
|
@@ -18,15 +30,40 @@ export interface CommandDefinition {
|
|
|
18
30
|
};
|
|
19
31
|
readonly handler: (invocation: CommandInvocation) => CommandResult | Promise<CommandResult>;
|
|
20
32
|
}
|
|
33
|
+
/** Invocation delivered to one registered command handler. */
|
|
21
34
|
export interface CommandInvocation {
|
|
22
35
|
readonly commandId: string;
|
|
36
|
+
/** The receiving agent; forwarding handlers deliver the raw line back to it. */
|
|
37
|
+
readonly agent: {
|
|
38
|
+
readonly followup: (input: UserMessage) => void;
|
|
39
|
+
};
|
|
40
|
+
/** Exact text following the command name, including separator whitespace. */
|
|
23
41
|
readonly rawInput: string;
|
|
24
42
|
readonly signal: AbortSignal;
|
|
25
43
|
}
|
|
44
|
+
/** Settled result of one command handler. */
|
|
26
45
|
export interface CommandResult {
|
|
27
46
|
readonly kind: 'success' | 'error';
|
|
28
47
|
readonly text?: string;
|
|
29
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Build the forwarding handler for one Claude Code slash command: it
|
|
51
|
+
* re-delivers the full `/<name> [args]` line to the receiving agent as a
|
|
52
|
+
* plain user message, where the CLI expands it. `rawInput` already carries the
|
|
53
|
+
* separator whitespace and any arguments.
|
|
54
|
+
* @param name - the command name without the leading slash.
|
|
55
|
+
* @returns the command handler.
|
|
56
|
+
*/
|
|
57
|
+
export declare function forwardClaudeCodeCommand(name: string): (invocation: CommandInvocation) => CommandResult;
|
|
30
58
|
/** Claude Code's built-in slash commands. */
|
|
31
|
-
export declare const CLAUDE_CODE_COMMANDS: CommandDefinition[];
|
|
59
|
+
export declare const CLAUDE_CODE_COMMANDS: readonly CommandDefinition[];
|
|
60
|
+
/**
|
|
61
|
+
* Discover the user-level custom slash commands from `~/.claude/commands/*.md`
|
|
62
|
+
* and build forwarding definitions for them. The scan is synchronous so the
|
|
63
|
+
* mount path can register the commands before the engine-selection commit
|
|
64
|
+
* returns; files without a usable name or description, and names already taken
|
|
65
|
+
* by the built-ins, are skipped.
|
|
66
|
+
* @returns forwarding definitions, sorted by file name.
|
|
67
|
+
*/
|
|
68
|
+
export declare function discoverUserSlashCommands(): CommandDefinition[];
|
|
32
69
|
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context-file collection and body loading shared by the hosted engine
|
|
3
|
+
* drivers.
|
|
4
|
+
*
|
|
5
|
+
* Codex and Pi read per-directory instruction files (`AGENTS.md`; pi also
|
|
6
|
+
* accepts `CLAUDE.md` and prefers `AGENTS.override.md` inside any directory
|
|
7
|
+
* that has one) while walking from the session cwd up to the git root. The
|
|
8
|
+
* skill providers surface each collected set as one merged skill candidate so
|
|
9
|
+
* the dsh skill-injection seam (`/name` gestures) can carry it into the
|
|
10
|
+
* prompt; the body-loading helpers below feed both providers' list/get paths.
|
|
11
|
+
*
|
|
12
|
+
* @module dsh-loop-engine/driver-core/context-files
|
|
13
|
+
*/
|
|
14
|
+
/** Per-directory context-file resolution policy for one engine. */
|
|
15
|
+
export interface ContextFilePolicy {
|
|
16
|
+
/** Per-directory override file that replaces the primary files when present. */
|
|
17
|
+
readonly override?: string;
|
|
18
|
+
/** Per-directory primary files, tried in order until one exists. */
|
|
19
|
+
readonly primary: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The directory chain from `cwd` up to the git root, nearest first. Without a
|
|
23
|
+
* repository the chain is just the resolved `cwd` itself, matching
|
|
24
|
+
* {@link findProjectRoot}'s fallback so the walk stays bounded.
|
|
25
|
+
* @param cwd - the session working directory.
|
|
26
|
+
* @returns the chain of directories to inspect.
|
|
27
|
+
*/
|
|
28
|
+
export declare function projectAncestors(cwd: string): Promise<string[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Collect every directory's context file per the policy, from the session cwd
|
|
31
|
+
* up to the git root.
|
|
32
|
+
* @param cwd - the session working directory.
|
|
33
|
+
* @param policy - per-directory resolution policy.
|
|
34
|
+
* @returns existing context files, nearest directory first.
|
|
35
|
+
*/
|
|
36
|
+
export declare function collectProjectContextFiles(cwd: string, policy: ContextFilePolicy): Promise<string[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Read one file, or `undefined` when it is unreadable.
|
|
39
|
+
* @param path - the file to read.
|
|
40
|
+
* @returns the file body, or `undefined` on any failure.
|
|
41
|
+
*/
|
|
42
|
+
export declare function readOptionalFile(path: string): Promise<string | undefined>;
|
|
43
|
+
/**
|
|
44
|
+
* Whether any of the given sources carries non-whitespace content.
|
|
45
|
+
* @param paths - candidate file paths.
|
|
46
|
+
* @returns whether at least one readable source is non-empty.
|
|
47
|
+
*/
|
|
48
|
+
export declare function anySourceNonEmpty(paths: readonly string[]): Promise<boolean>;
|
|
49
|
+
/**
|
|
50
|
+
* Whether one file exists and carries non-whitespace content.
|
|
51
|
+
* @param path - the file to inspect.
|
|
52
|
+
* @returns whether the file is readable and non-empty.
|
|
53
|
+
*/
|
|
54
|
+
export declare function fileNonEmpty(path: string): Promise<boolean>;
|
|
55
|
+
/**
|
|
56
|
+
* Concatenate every non-empty readable source body in order, or `undefined`
|
|
57
|
+
* when none are readable.
|
|
58
|
+
* @param paths - candidate file paths, nearest directory first.
|
|
59
|
+
* @returns the joined bodies, or `undefined` when nothing could be read.
|
|
60
|
+
*/
|
|
61
|
+
export declare function readSources(paths: readonly string[]): Promise<string | undefined>;
|
|
62
|
+
//# sourceMappingURL=context-files.d.ts.map
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Codex skill provider: exposes the codex CLI's instruction files as DSH
|
|
3
|
-
* skills.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* skills.
|
|
4
|
+
*
|
|
5
|
+
* Codex has no per-skill catalog like the agents-skill standard; its
|
|
6
|
+
* instructions are `AGENTS.md` files read from the session cwd up to the git
|
|
7
|
+
* root, plus the global `~/.codex/AGENTS.md`. Each file set is surfaced as one
|
|
8
|
+
* user-invocable `agents-md` skill whose body is the concatenated file
|
|
9
|
+
* contents, so the dsh skill-injection seam (`/name` gestures) can carry it
|
|
10
|
+
* into the prompt.
|
|
8
11
|
*
|
|
9
12
|
* @module dsh-loop-engine/engine-codex/skills
|
|
10
13
|
*/
|
|
11
|
-
import {
|
|
14
|
+
import type { SkillCandidate, SkillDefinition, SkillLookupOptions, SkillProvider, SkillProviderControl } from '../skills.ts';
|
|
12
15
|
/**
|
|
13
|
-
* Skill provider that discovers `AGENTS.md` from
|
|
14
|
-
*
|
|
16
|
+
* Skill provider that discovers `AGENTS.md` from every directory between the
|
|
17
|
+
* project cwd and the git root, plus the user home `~/.codex/AGENTS.md`.
|
|
15
18
|
*/
|
|
16
19
|
export declare class CodexSkillProvider implements SkillProvider {
|
|
17
20
|
private readonly control;
|
|
@@ -19,8 +22,8 @@ export declare class CodexSkillProvider implements SkillProvider {
|
|
|
19
22
|
constructor(control: SkillProviderControl);
|
|
20
23
|
list(options: SkillLookupOptions): Promise<readonly SkillCandidate[]>;
|
|
21
24
|
get(candidate: SkillCandidate, _options: SkillLookupOptions): Promise<SkillDefinition | undefined>;
|
|
22
|
-
/**
|
|
23
|
-
private
|
|
25
|
+
/** One merged `agents-md` candidate for a ranked file set. */
|
|
26
|
+
private agentsCandidate;
|
|
24
27
|
}
|
|
25
28
|
export default CodexSkillProvider;
|
|
26
29
|
//# sourceMappingURL=skills.d.ts.map
|
|
@@ -1,17 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pi skill provider: exposes the Pi CLI's instruction files as DSH
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
2
|
+
* Pi skill provider: exposes the Pi CLI's instruction files and skills as DSH
|
|
3
|
+
* skills.
|
|
4
|
+
*
|
|
5
|
+
* Pi reads per-directory context files (`AGENTS.md`, or `CLAUDE.md`,
|
|
6
|
+
* preferring `AGENTS.override.md` where one exists) from the session cwd up to
|
|
7
|
+
* the git root, plus a global `AGENTS.md` under the pi config directory
|
|
8
|
+
* (`PI_CODING_AGENT_DIR` or `~/.pi/agent`), and installs skills from
|
|
9
|
+
* `skills/` directories (`~/.pi/agent/skills/` and project `.pi/skills/`
|
|
10
|
+
* walking up). Each context-file set is surfaced as one user-invocable
|
|
11
|
+
* `agents-md` skill whose body is the concatenated file contents; every found
|
|
12
|
+
* `SKILL.md` catalog entry is surfaced under its own name, so the dsh
|
|
13
|
+
* skill-injection seam (`/name` gestures) can carry them into the prompt.
|
|
14
|
+
*
|
|
15
|
+
* `.agents/skills` roots are deliberately not scanned here: dsh's own
|
|
16
|
+
* `skill-filesystem` provider already exposes them through the same registry
|
|
17
|
+
* in the web profile. Pi settings/CLI/package skills are only discoverable
|
|
18
|
+
* through a running `pi --mode rpc` probe, which the engine does not perform
|
|
19
|
+
* at composition time — the filesystem subset above is authoritative for the
|
|
20
|
+
* web menu.
|
|
8
21
|
*
|
|
9
22
|
* @module dsh-loop-engine/engine-pi/skills
|
|
10
23
|
*/
|
|
11
|
-
import {
|
|
24
|
+
import type { SkillCandidate, SkillDefinition, SkillLookupOptions, SkillProvider, SkillProviderControl } from '../skills.ts';
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the pi config directory, honoring the `PI_CODING_AGENT_DIR`
|
|
27
|
+
* environment override and falling back to `~/.pi/agent`.
|
|
28
|
+
* @returns the absolute pi config directory.
|
|
29
|
+
*/
|
|
30
|
+
export declare function piAgentDir(): string;
|
|
12
31
|
/**
|
|
13
|
-
* Skill provider that discovers
|
|
14
|
-
*
|
|
32
|
+
* Skill provider that discovers context files and skills from pi's standard
|
|
33
|
+
* locations:
|
|
34
|
+
* - project context files between the cwd and the git root (plus
|
|
35
|
+
* `~/.pi/agent/AGENTS.md`) — surfaced as one `agents-md` skill;
|
|
36
|
+
* - project `.pi/skills/` and user `~/.pi/agent/skills/` — each `SKILL.md`
|
|
37
|
+
* entry surfaced under its own name.
|
|
15
38
|
*/
|
|
16
39
|
export declare class PiSkillProvider implements SkillProvider {
|
|
17
40
|
private readonly control;
|
|
@@ -19,8 +42,14 @@ export declare class PiSkillProvider implements SkillProvider {
|
|
|
19
42
|
constructor(control: SkillProviderControl);
|
|
20
43
|
list(options: SkillLookupOptions): Promise<readonly SkillCandidate[]>;
|
|
21
44
|
get(candidate: SkillCandidate, _options: SkillLookupOptions): Promise<SkillDefinition | undefined>;
|
|
22
|
-
/**
|
|
23
|
-
private
|
|
45
|
+
/** One merged `agents-md` candidate for a ranked file set. */
|
|
46
|
+
private agentsCandidate;
|
|
47
|
+
/** Collect every skill in one skills directory, both pi layouts. */
|
|
48
|
+
private collectSkillsDir;
|
|
49
|
+
/** One parsed skill as a ranked candidate. */
|
|
50
|
+
private skillCandidate;
|
|
51
|
+
/** Parse one SKILL.md file, or `undefined` when it is unreadable or invalid. */
|
|
52
|
+
private tryParse;
|
|
24
53
|
}
|
|
25
54
|
export default PiSkillProvider;
|
|
26
55
|
//# sourceMappingURL=skills.d.ts.map
|
package/lib/types/settings.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export type LoopEngineId = (typeof LOOP_ENGINE_IDS)[number];
|
|
|
21
21
|
export interface LoopEngineSettings {
|
|
22
22
|
/** The engine future Agents are created on. */
|
|
23
23
|
engine: LoopEngineId;
|
|
24
|
+
/** Whether the composer's loop engine picker is shown on the chat page. */
|
|
25
|
+
showInComposer: boolean;
|
|
24
26
|
}
|
|
25
27
|
/** Schema of the loop engine settings section. */
|
|
26
28
|
export declare const LOOP_ENGINE_SETTINGS_SCHEMA: z<LoopEngineSettings>;
|
package/lib/types/skills.d.ts
CHANGED
|
@@ -56,6 +56,22 @@ export interface SkillProviderControl {
|
|
|
56
56
|
readonly signal: AbortSignal;
|
|
57
57
|
invalidate(): void;
|
|
58
58
|
}
|
|
59
|
+
/** One parsed agents-skill standard skill: frontmatter metadata plus the body. */
|
|
60
|
+
export interface ParsedSkill {
|
|
61
|
+
name: string;
|
|
62
|
+
description: string;
|
|
63
|
+
whenToUse?: string;
|
|
64
|
+
invocation: SkillInvocationPolicy;
|
|
65
|
+
content: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Parse one agents-skill standard file: YAML frontmatter (`name`,
|
|
69
|
+
* `description`, optional `whenToUse`/`disable-model-invocation`/
|
|
70
|
+
* `user-invocable`) plus the markdown body.
|
|
71
|
+
* @param raw - the raw file content.
|
|
72
|
+
* @returns the parsed skill, or `undefined` when the file is not a skill.
|
|
73
|
+
*/
|
|
74
|
+
export declare function parseSkillFile(raw: string): ParsedSkill | undefined;
|
|
59
75
|
/**
|
|
60
76
|
* Skill provider that discovers skills from Claude Code's standard locations:
|
|
61
77
|
* - `<project>/.claude/skills/` — project skills
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-loop-engine",
|
|
3
3
|
"description": "Web-switchable agent loop engine selection for the DeepSeek Harness - out-of-tree plugin (Claude Code / Codex drivers) maintained by @kuun993, zero main-repo changes",
|
|
4
|
-
"version": "1.0.0-
|
|
4
|
+
"version": "1.0.0-rc4",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|