dsh-prompt-star 0.1.2 → 0.1.3

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/lib/client.js CHANGED
@@ -10,29 +10,20 @@ window.__ModuleLoader__.load({
10
10
  /**
11
11
  * The ⭐ button rendered beside the conversation input.
12
12
  *
13
- * Pure-client plugin: on click it reads the current draft (from the session
14
- * input snapshot), reads a few project doc files through the already-mounted
15
- * `workspaceFiles` remote, and assembles a fuller, more efficient prompt on the
16
- * client, then writes it back with `inputActions.setDraft`. The user presses
17
- * Ctrl/Cmd+Z if they want the original draft back.
13
+ * On click it reads the current draft (session input snapshot), asks the host
14
+ * (over the `/dsh-prompt-star` Connection RPC channel) to read a few project
15
+ * doc files as context, assembles a fuller, more efficient prompt on the client,
16
+ * then writes it back with `inputActions.setDraft`. Press Ctrl/Cmd+Z to restore
17
+ * the original draft.
18
18
  *
19
19
  * The slot framework composes this component's props: the session standard seat
20
- * (`useInput`, `inputActions`, `sessionId`) plus the register-time `workspaceFiles`
21
- * face the client plugin injects. The interfaces below are the minimal structural
22
- * shapes this component needs; they match the mounted `workspaceFiles` remote and
23
- * the composed slot props without importing the harness client types.
20
+ * (`useInput`, `inputActions`) plus the register-time `context` face this client
21
+ * plugin injects. The interfaces below are the minimal structural shapes this
22
+ * component needs; they match the composed slot props without importing the
23
+ * harness client types.
24
24
  */
25
- /** Common project-doc filenames probed as prompt context. */
26
- const DOC_CANDIDATES = [
27
- "README.md",
28
- "README",
29
- "AGENTS.md",
30
- "CLAUDE.md",
31
- "CONTRIBUTING.md",
32
- "docs/README.md",
33
- "docs/index.md",
34
- ".cursorrules"
35
- ];
25
+ /** Common project-doc filenames are probed on the HOST; the client never
26
+ * enumerates them (it just asks the host for the read context). */
36
27
  const buttonStyle = {
37
28
  all: "unset",
38
29
  display: "inline-flex",
@@ -47,35 +38,7 @@ window.__ModuleLoader__.load({
47
38
  opacity: .9,
48
39
  userSelect: "none"
49
40
  };
50
- /** Best-effort read of project doc files; every read is guarded so a missing
51
- * or unreadable file is skipped rather than breaking the button. */
52
- async function gatherContext(workspaceFiles, sessionId) {
53
- const docs = [];
54
- const seen = /* @__PURE__ */ new Set();
55
- let filesRead = 0;
56
- for (const path of DOC_CANDIDATES) try {
57
- const result = await workspaceFiles.read(sessionId, path, {
58
- offset: 1,
59
- limit: 120
60
- });
61
- if (result.ok && result.value?.text) {
62
- const text = result.value.text.trim();
63
- if (text.length > 0 && !seen.has(path)) {
64
- seen.add(path);
65
- docs.push({
66
- name: path,
67
- text
68
- });
69
- filesRead += 1;
70
- }
71
- }
72
- } catch {}
73
- return {
74
- docs,
75
- filesRead
76
- };
77
- }
78
- /** Assemble a fuller, structured prompt from the draft + any project context. */
41
+ /** Build a fuller, structured prompt from the draft + any project context. */
79
42
  function assemblePrompt(draft, context) {
80
43
  return [
81
44
  "请把下面的【我的草稿】整理成一段完整、清晰、高效的提示词,直接输出整理后的提示词本身。",
@@ -87,13 +50,13 @@ window.__ModuleLoader__.load({
87
50
  "4. 长度以覆盖草稿要点为准,不要过度扩写。",
88
51
  "",
89
52
  "# 项目文档",
90
- context.filesRead === 0 ? "(未读取到项目文档)" : context.docs.map((doc) => `## ${doc.name}\n${doc.text}`).join("\n\n"),
53
+ context.docs.length === 0 ? "(未读取到项目文档)" : context.docs.map((doc) => `## ${doc.name}\n${doc.text}`).join("\n\n"),
91
54
  "",
92
55
  "# 我的草稿",
93
56
  draft
94
57
  ].join("\n");
95
58
  }
96
- function StarButton({ workspaceFiles, useInput, inputActions, sessionId }) {
59
+ function StarButton({ context, useInput, inputActions }) {
97
60
  const input = useInput();
98
61
  const [busy, setBusy] = (0, react.useState)(false);
99
62
  const [error, setError] = (0, react.useState)(void 0);
@@ -106,11 +69,16 @@ window.__ModuleLoader__.load({
106
69
  setBusy(true);
107
70
  setError(void 0);
108
71
  try {
109
- const context = await gatherContext(workspaceFiles, sessionId);
110
- const full = assemblePrompt(draft, context);
72
+ const res = await context();
73
+ if (!res.ok) {
74
+ setError(`读取项目文档失败: ${res.error?.message ?? "未知错误"}`);
75
+ return;
76
+ }
77
+ const value = res.value;
78
+ const full = assemblePrompt(draft, value);
111
79
  if (full && full !== draft) {
112
80
  inputActions.setDraft(full);
113
- setError(context.filesRead === 0 ? "已按草稿整理(未读取到项目文档)" : `已整理并读取 ${context.filesRead} 个项目文档`);
81
+ setError(value.docs.length === 0 ? "已按草稿整理(未读取到项目文档)" : `已整理并读取 ${value.docs.length} 个项目文档`);
114
82
  } else setError("未生成新内容");
115
83
  } catch (cause) {
116
84
  setError(`生成失败: ${cause instanceof Error ? cause.message : String(cause)}`);
@@ -128,43 +96,53 @@ window.__ModuleLoader__.load({
128
96
  });
129
97
  }
130
98
  //#endregion
99
+ //#region lib/types/types.js
100
+ /**
101
+ * dsh-prompt-star — host↔client RPC protocol (shared vocabulary).
102
+ *
103
+ * Transport: the generic Connection RPC channel `/dsh-prompt-star`
104
+ * (the host registers it with `ctx.connection.rpc.handle`, the browser calls
105
+ * `ctx.connection.rpc.call('/dsh-prompt-star', endpoint, payload)`).
106
+ *
107
+ * The host sells exactly one capability: reading a few project documentation
108
+ * files for the current workspace (it CAN read file contents natively; the
109
+ * browser client cannot). Everything else — assembling the fuller prompt from
110
+ * the draft + that context — stays on the client.
111
+ */
112
+ /** Absolute logical Connection RPC channel owned by this plugin. */
113
+ const RPC_CHANNEL = "/dsh-prompt-star";
114
+ /** Channel-relative endpoint that reads project doc files on the host. */
115
+ const EP_CONTEXT = "context";
116
+ //#endregion
131
117
  //#region lib/types/client/index.js
132
118
  /**
133
119
  * Client plugin: mounts the ⭐ button into the composer input tool row
134
120
  * (`conversation.input.right`, a session-scoped list slot) and wires it to the
135
- * already-mounted `workspaceFiles` remote.
121
+ * host's `/dsh-prompt-star` Connection RPC channel.
136
122
  *
137
- * This is a pure-client plugin: it reads the draft via the session input
138
- * snapshot, reads project doc files through `ctx.remote.workspaceFiles`, and
139
- * assembles a fuller prompt on the client — no custom host↔client RPC is needed,
140
- * so it works in any stock `dsh web` build.
123
+ * This client half only needs the slot registry and the `connection` service.
124
+ * On click the button asks the host for project doc context (the browser cannot
125
+ * read file contents itself), then assembles the fuller prompt on the client and
126
+ * writes it back with `inputActions.setDraft`.
141
127
  */
142
- /** Cordis services this client plugin needs: the slot registry and the mounted
143
- * `workspaceFiles` remote (both provided by the stock web-app composition). */
144
- const inject = [
145
- "slots",
146
- "remote",
147
- "remote.workspaceFiles"
148
- ];
128
+ /** Cordis services this client plugin needs: the slot registry and the
129
+ * Connection service (both provided by the stock web-app composition). */
130
+ const inject = ["slots", "connection"];
149
131
  /**
150
132
  * Client plugin body: register the ⭐ button once the slot registry and the
151
- * `workspaceFiles` remote are up.
133
+ * Connection service are up.
152
134
  * @param ctx - client root context.
153
135
  */
154
136
  function apply(ctx) {
155
- ctx.inject([
156
- "slots",
157
- "remote",
158
- "remote.workspaceFiles"
159
- ], (scope) => {
137
+ ctx.inject(["slots", "connection"], (scope) => {
160
138
  const root = scope;
161
- const { workspaceFiles } = root.remote;
139
+ const rpc = root.connection.rpc;
162
140
  root.slots.inject("conversation.input.right", () => {
163
141
  root.slots.register({
164
142
  name: "conversation.input.right",
165
143
  id: "prompt-star",
166
144
  order: 0,
167
- inject: () => ({ workspaceFiles })
145
+ inject: () => ({ context: () => rpc.call(RPC_CHANNEL, EP_CONTEXT, {}) })
168
146
  }, StarButton);
169
147
  });
170
148
  });
package/lib/index.js CHANGED
@@ -1,21 +1,99 @@
1
+ //#region lib/types/types.js
2
+ /**
3
+ * dsh-prompt-star — host↔client RPC protocol (shared vocabulary).
4
+ *
5
+ * Transport: the generic Connection RPC channel `/dsh-prompt-star`
6
+ * (the host registers it with `ctx.connection.rpc.handle`, the browser calls
7
+ * `ctx.connection.rpc.call('/dsh-prompt-star', endpoint, payload)`).
8
+ *
9
+ * The host sells exactly one capability: reading a few project documentation
10
+ * files for the current workspace (it CAN read file contents natively; the
11
+ * browser client cannot). Everything else — assembling the fuller prompt from
12
+ * the draft + that context — stays on the client.
13
+ */
14
+ /** Absolute logical Connection RPC channel owned by this plugin. */
15
+ const RPC_CHANNEL = "/dsh-prompt-star";
16
+ //#endregion
1
17
  //#region lib/types/index.js
2
18
  /**
3
19
  * Host entry for dsh-prompt-star.
4
20
  *
5
- * The ⭐ button is a pure client plugin (see `src/client/`): it reads the
6
- * conversation draft via the session input snapshot, reads project doc files
7
- * through the already-mounted `workspaceFiles` remote, and assembles a fuller,
8
- * more efficient prompt entirely on the client. It needs no custom host↔client
9
- * RPC, so this host half only provides a minimal Cordis plugin that keeps the
10
- * bundle mounted as a Loader entry — which is what makes `dsh-client-modules`
11
- * discover and serve its client bundle.
21
+ * The ⭐ button lives in the client half; this host half registers ONE generic
22
+ * Connection RPC channel (`/dsh-prompt-star`) that reads a few project
23
+ * documentation files for the current workspace. The browser client CANNOT read
24
+ * file contents (the stock web app exposes no content-reading remote), so the
25
+ * read happens here and is shipped to the browser over the Connection channel.
12
26
  *
13
27
  * This entry follows the standard DSH plugin export contract `apply(ctx)`.
28
+ *
29
+ * The host-side services (`ctx.connection.rpc`, `ctx.fs`) are consumed through
30
+ * small local structural types instead of importing the harness packages, so the
31
+ * plugin's `tsc` stays inside its own `rootDir` and never drags harness source
32
+ * files into its program.
14
33
  */
34
+ /** Project doc files probed, in priority order. Reads are best-effort. */
35
+ const DOC_CANDIDATES = [
36
+ "README.md",
37
+ "README",
38
+ "AGENTS.md",
39
+ "CLAUDE.md",
40
+ "CONTRIBUTING.md",
41
+ "docs/README.md",
42
+ "docs/index.md",
43
+ ".cursorrules"
44
+ ];
45
+ /** Per-doc content cap so a huge README cannot flood the input. */
46
+ const MAX_DOC_CHARS = 4e3;
15
47
  /**
16
- * Minimal host plugin body. The button logic lives entirely in the client
17
- * bundle; mounting this row is what lets the web app serve `lib/client.js`.
48
+ * Minimal host plugin body: mount the `/dsh-prompt-star` RPC channel.
49
+ * @param ctx - host root context.
18
50
  */
19
- function apply(_ctx) {}
51
+ function apply(ctx) {
52
+ const host = ctx;
53
+ host.connection.rpc.handle(RPC_CHANNEL, async (endpoint, payload) => {
54
+ if (endpoint !== "context") return {
55
+ ok: false,
56
+ error: {
57
+ code: "unknown_endpoint",
58
+ message: `unknown endpoint '${endpoint}'`,
59
+ details: {}
60
+ }
61
+ };
62
+ try {
63
+ const root = (payload ?? {}).cwd?.trim() || process.cwd();
64
+ const docs = [];
65
+ const seen = /* @__PURE__ */ new Set();
66
+ let filesRead = 0;
67
+ for (const name of DOC_CANDIDATES) try {
68
+ const target = host.fs.resolve(name, { cwd: root });
69
+ const trimmed = (await host.fs.readText(target))?.trim();
70
+ if (trimmed && !seen.has(name)) {
71
+ seen.add(name);
72
+ docs.push({
73
+ name,
74
+ text: trimmed.slice(0, MAX_DOC_CHARS)
75
+ });
76
+ filesRead += 1;
77
+ }
78
+ } catch {}
79
+ return {
80
+ ok: true,
81
+ value: {
82
+ docs,
83
+ filesRead
84
+ }
85
+ };
86
+ } catch (err) {
87
+ return {
88
+ ok: false,
89
+ error: {
90
+ code: "internal",
91
+ message: err instanceof Error ? err.message : String(err),
92
+ details: {}
93
+ }
94
+ };
95
+ }
96
+ });
97
+ }
20
98
  //#endregion
21
- export { apply, apply as default };
99
+ export { apply };
@@ -1,44 +1,23 @@
1
1
  /**
2
2
  * The ⭐ button rendered beside the conversation input.
3
3
  *
4
- * Pure-client plugin: on click it reads the current draft (from the session
5
- * input snapshot), reads a few project doc files through the already-mounted
6
- * `workspaceFiles` remote, and assembles a fuller, more efficient prompt on the
7
- * client, then writes it back with `inputActions.setDraft`. The user presses
8
- * Ctrl/Cmd+Z if they want the original draft back.
4
+ * On click it reads the current draft (session input snapshot), asks the host
5
+ * (over the `/dsh-prompt-star` Connection RPC channel) to read a few project
6
+ * doc files as context, assembles a fuller, more efficient prompt on the client,
7
+ * then writes it back with `inputActions.setDraft`. Press Ctrl/Cmd+Z to restore
8
+ * the original draft.
9
9
  *
10
10
  * The slot framework composes this component's props: the session standard seat
11
- * (`useInput`, `inputActions`, `sessionId`) plus the register-time `workspaceFiles`
12
- * face the client plugin injects. The interfaces below are the minimal structural
13
- * shapes this component needs; they match the mounted `workspaceFiles` remote and
14
- * the composed slot props without importing the harness client types.
11
+ * (`useInput`, `inputActions`) plus the register-time `context` face this client
12
+ * plugin injects. The interfaces below are the minimal structural shapes this
13
+ * component needs; they match the composed slot props without importing the
14
+ * harness client types.
15
15
  */
16
16
  import { type ReactElement } from 'react';
17
- /** Minimal structural face of the mounted `workspaceFiles` remote. */
18
- export interface WorkspaceFilesRemote {
19
- read(sessionId: string, path: string, range: {
20
- offset?: number;
21
- limit?: number;
22
- }, signal?: AbortSignal): Promise<{
23
- ok: boolean;
24
- value?: {
25
- text: string;
26
- eof?: boolean;
27
- };
28
- }>;
29
- list(sessionId: string, path: string, signal?: AbortSignal): Promise<{
30
- ok: boolean;
31
- value?: {
32
- entries: Array<{
33
- name: string;
34
- type: string;
35
- }>;
36
- };
37
- }>;
38
- }
17
+ import type { ContextRpcResult } from '../types';
39
18
  export interface StarButtonProps {
40
- /** Mounted `workspaceFiles` remote (reads project docs). */
41
- workspaceFiles: WorkspaceFilesRemote;
19
+ /** Host RPC caller that returns the project doc context. */
20
+ context(): Promise<ContextRpcResult>;
42
21
  /** Session input snapshot hook (returns the current draft). */
43
22
  useInput(): {
44
23
  draft: string;
@@ -47,8 +26,6 @@ export interface StarButtonProps {
47
26
  inputActions: {
48
27
  setDraft(text: string): void;
49
28
  };
50
- /** Current session identity, used as the wire identity for file reads. */
51
- sessionId: string;
52
29
  }
53
- export declare function StarButton({ workspaceFiles, useInput, inputActions, sessionId, }: StarButtonProps): ReactElement;
30
+ export declare function StarButton({ context, useInput, inputActions }: StarButtonProps): ReactElement;
54
31
  //# sourceMappingURL=StarButton.d.ts.map
@@ -2,30 +2,21 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  /**
3
3
  * The ⭐ button rendered beside the conversation input.
4
4
  *
5
- * Pure-client plugin: on click it reads the current draft (from the session
6
- * input snapshot), reads a few project doc files through the already-mounted
7
- * `workspaceFiles` remote, and assembles a fuller, more efficient prompt on the
8
- * client, then writes it back with `inputActions.setDraft`. The user presses
9
- * Ctrl/Cmd+Z if they want the original draft back.
5
+ * On click it reads the current draft (session input snapshot), asks the host
6
+ * (over the `/dsh-prompt-star` Connection RPC channel) to read a few project
7
+ * doc files as context, assembles a fuller, more efficient prompt on the client,
8
+ * then writes it back with `inputActions.setDraft`. Press Ctrl/Cmd+Z to restore
9
+ * the original draft.
10
10
  *
11
11
  * The slot framework composes this component's props: the session standard seat
12
- * (`useInput`, `inputActions`, `sessionId`) plus the register-time `workspaceFiles`
13
- * face the client plugin injects. The interfaces below are the minimal structural
14
- * shapes this component needs; they match the mounted `workspaceFiles` remote and
15
- * the composed slot props without importing the harness client types.
12
+ * (`useInput`, `inputActions`) plus the register-time `context` face this client
13
+ * plugin injects. The interfaces below are the minimal structural shapes this
14
+ * component needs; they match the composed slot props without importing the
15
+ * harness client types.
16
16
  */
17
17
  import { useState } from 'react';
18
- /** Common project-doc filenames probed as prompt context. */
19
- const DOC_CANDIDATES = [
20
- 'README.md',
21
- 'README',
22
- 'AGENTS.md',
23
- 'CLAUDE.md',
24
- 'CONTRIBUTING.md',
25
- 'docs/README.md',
26
- 'docs/index.md',
27
- '.cursorrules',
28
- ];
18
+ /** Common project-doc filenames are probed on the HOST; the client never
19
+ * enumerates them (it just asks the host for the read context). */
29
20
  const buttonStyle = {
30
21
  all: 'unset',
31
22
  display: 'inline-flex',
@@ -40,33 +31,9 @@ const buttonStyle = {
40
31
  opacity: 0.9,
41
32
  userSelect: 'none',
42
33
  };
43
- /** Best-effort read of project doc files; every read is guarded so a missing
44
- * or unreadable file is skipped rather than breaking the button. */
45
- async function gatherContext(workspaceFiles, sessionId) {
46
- const docs = [];
47
- const seen = new Set();
48
- let filesRead = 0;
49
- for (const path of DOC_CANDIDATES) {
50
- try {
51
- const result = await workspaceFiles.read(sessionId, path, { offset: 1, limit: 120 });
52
- if (result.ok && result.value?.text) {
53
- const text = result.value.text.trim();
54
- if (text.length > 0 && !seen.has(path)) {
55
- seen.add(path);
56
- docs.push({ name: path, text });
57
- filesRead += 1;
58
- }
59
- }
60
- }
61
- catch {
62
- // Not present / not readable: leave it out.
63
- }
64
- }
65
- return { docs, filesRead };
66
- }
67
- /** Assemble a fuller, structured prompt from the draft + any project context. */
34
+ /** Build a fuller, structured prompt from the draft + any project context. */
68
35
  function assemblePrompt(draft, context) {
69
- const projectContext = context.filesRead === 0
36
+ const projectContext = context.docs.length === 0
70
37
  ? '(未读取到项目文档)'
71
38
  : context.docs.map((doc) => `## ${doc.name}\n${doc.text}`).join('\n\n');
72
39
  return [
@@ -85,7 +52,7 @@ function assemblePrompt(draft, context) {
85
52
  draft,
86
53
  ].join('\n');
87
54
  }
88
- export function StarButton({ workspaceFiles, useInput, inputActions, sessionId, }) {
55
+ export function StarButton({ context, useInput, inputActions }) {
89
56
  const input = useInput();
90
57
  const [busy, setBusy] = useState(false);
91
58
  const [error, setError] = useState(undefined);
@@ -98,13 +65,18 @@ export function StarButton({ workspaceFiles, useInput, inputActions, sessionId,
98
65
  setBusy(true);
99
66
  setError(undefined);
100
67
  try {
101
- const context = await gatherContext(workspaceFiles, sessionId);
102
- const full = assemblePrompt(draft, context);
68
+ const res = await context();
69
+ if (!res.ok) {
70
+ setError(`读取项目文档失败: ${res.error?.message ?? '未知错误'}`);
71
+ return;
72
+ }
73
+ const value = res.value;
74
+ const full = assemblePrompt(draft, value);
103
75
  if (full && full !== draft) {
104
76
  inputActions.setDraft(full);
105
- setError(context.filesRead === 0
77
+ setError(value.docs.length === 0
106
78
  ? '已按草稿整理(未读取到项目文档)'
107
- : `已整理并读取 ${context.filesRead} 个项目文档`);
79
+ : `已整理并读取 ${value.docs.length} 个项目文档`);
108
80
  }
109
81
  else {
110
82
  setError('未生成新内容');
@@ -1,20 +1,20 @@
1
1
  /**
2
2
  * Client plugin: mounts the ⭐ button into the composer input tool row
3
3
  * (`conversation.input.right`, a session-scoped list slot) and wires it to the
4
- * already-mounted `workspaceFiles` remote.
4
+ * host's `/dsh-prompt-star` Connection RPC channel.
5
5
  *
6
- * This is a pure-client plugin: it reads the draft via the session input
7
- * snapshot, reads project doc files through `ctx.remote.workspaceFiles`, and
8
- * assembles a fuller prompt on the client — no custom host↔client RPC is needed,
9
- * so it works in any stock `dsh web` build.
6
+ * This client half only needs the slot registry and the `connection` service.
7
+ * On click the button asks the host for project doc context (the browser cannot
8
+ * read file contents itself), then assembles the fuller prompt on the client and
9
+ * writes it back with `inputActions.setDraft`.
10
10
  */
11
11
  import type { Context } from '@deepseek-ai/cordis';
12
- /** Cordis services this client plugin needs: the slot registry and the mounted
13
- * `workspaceFiles` remote (both provided by the stock web-app composition). */
12
+ /** Cordis services this client plugin needs: the slot registry and the
13
+ * Connection service (both provided by the stock web-app composition). */
14
14
  export declare const inject: string[];
15
15
  /**
16
16
  * Client plugin body: register the ⭐ button once the slot registry and the
17
- * `workspaceFiles` remote are up.
17
+ * Connection service are up.
18
18
  * @param ctx - client root context.
19
19
  */
20
20
  export declare function apply(ctx: Context): void;
@@ -1,32 +1,35 @@
1
1
  /**
2
2
  * Client plugin: mounts the ⭐ button into the composer input tool row
3
3
  * (`conversation.input.right`, a session-scoped list slot) and wires it to the
4
- * already-mounted `workspaceFiles` remote.
4
+ * host's `/dsh-prompt-star` Connection RPC channel.
5
5
  *
6
- * This is a pure-client plugin: it reads the draft via the session input
7
- * snapshot, reads project doc files through `ctx.remote.workspaceFiles`, and
8
- * assembles a fuller prompt on the client — no custom host↔client RPC is needed,
9
- * so it works in any stock `dsh web` build.
6
+ * This client half only needs the slot registry and the `connection` service.
7
+ * On click the button asks the host for project doc context (the browser cannot
8
+ * read file contents itself), then assembles the fuller prompt on the client and
9
+ * writes it back with `inputActions.setDraft`.
10
10
  */
11
11
  import { StarButton } from "./StarButton.js";
12
- /** Cordis services this client plugin needs: the slot registry and the mounted
13
- * `workspaceFiles` remote (both provided by the stock web-app composition). */
14
- export const inject = ['slots', 'remote', 'remote.workspaceFiles'];
12
+ import { RPC_CHANNEL, EP_CONTEXT } from '../types';
13
+ /** Cordis services this client plugin needs: the slot registry and the
14
+ * Connection service (both provided by the stock web-app composition). */
15
+ export const inject = ['slots', 'connection'];
15
16
  /**
16
17
  * Client plugin body: register the ⭐ button once the slot registry and the
17
- * `workspaceFiles` remote are up.
18
+ * Connection service are up.
18
19
  * @param ctx - client root context.
19
20
  */
20
21
  export function apply(ctx) {
21
- ctx.inject(['slots', 'remote', 'remote.workspaceFiles'], (scope) => {
22
+ ctx.inject(['slots', 'connection'], (scope) => {
22
23
  const root = scope;
23
- const { workspaceFiles } = root.remote;
24
+ const rpc = root.connection.rpc;
24
25
  root.slots.inject('conversation.input.right', () => {
25
26
  root.slots.register({
26
27
  name: 'conversation.input.right',
27
28
  id: 'prompt-star',
28
29
  order: 0,
29
- inject: () => ({ workspaceFiles }),
30
+ inject: () => ({
31
+ context: () => rpc.call(RPC_CHANNEL, EP_CONTEXT, {}),
32
+ }),
30
33
  }, StarButton);
31
34
  });
32
35
  });
@@ -1,21 +1,23 @@
1
1
  /**
2
2
  * Host entry for dsh-prompt-star.
3
3
  *
4
- * The ⭐ button is a pure client plugin (see `src/client/`): it reads the
5
- * conversation draft via the session input snapshot, reads project doc files
6
- * through the already-mounted `workspaceFiles` remote, and assembles a fuller,
7
- * more efficient prompt entirely on the client. It needs no custom host↔client
8
- * RPC, so this host half only provides a minimal Cordis plugin that keeps the
9
- * bundle mounted as a Loader entry — which is what makes `dsh-client-modules`
10
- * discover and serve its client bundle.
4
+ * The ⭐ button lives in the client half; this host half registers ONE generic
5
+ * Connection RPC channel (`/dsh-prompt-star`) that reads a few project
6
+ * documentation files for the current workspace. The browser client CANNOT read
7
+ * file contents (the stock web app exposes no content-reading remote), so the
8
+ * read happens here and is shipped to the browser over the Connection channel.
11
9
  *
12
10
  * This entry follows the standard DSH plugin export contract `apply(ctx)`.
11
+ *
12
+ * The host-side services (`ctx.connection.rpc`, `ctx.fs`) are consumed through
13
+ * small local structural types instead of importing the harness packages, so the
14
+ * plugin's `tsc` stays inside its own `rootDir` and never drags harness source
15
+ * files into its program.
13
16
  */
14
17
  import type { Context } from '@deepseek-ai/cordis';
15
18
  /**
16
- * Minimal host plugin body. The button logic lives entirely in the client
17
- * bundle; mounting this row is what lets the web app serve `lib/client.js`.
19
+ * Minimal host plugin body: mount the `/dsh-prompt-star` RPC channel.
20
+ * @param ctx - host root context.
18
21
  */
19
- export declare function apply(_ctx: Context): void;
20
- export default apply;
22
+ export declare function apply(ctx: Context): void;
21
23
  //# sourceMappingURL=index.d.ts.map
@@ -1,22 +1,81 @@
1
1
  /**
2
2
  * Host entry for dsh-prompt-star.
3
3
  *
4
- * The ⭐ button is a pure client plugin (see `src/client/`): it reads the
5
- * conversation draft via the session input snapshot, reads project doc files
6
- * through the already-mounted `workspaceFiles` remote, and assembles a fuller,
7
- * more efficient prompt entirely on the client. It needs no custom host↔client
8
- * RPC, so this host half only provides a minimal Cordis plugin that keeps the
9
- * bundle mounted as a Loader entry — which is what makes `dsh-client-modules`
10
- * discover and serve its client bundle.
4
+ * The ⭐ button lives in the client half; this host half registers ONE generic
5
+ * Connection RPC channel (`/dsh-prompt-star`) that reads a few project
6
+ * documentation files for the current workspace. The browser client CANNOT read
7
+ * file contents (the stock web app exposes no content-reading remote), so the
8
+ * read happens here and is shipped to the browser over the Connection channel.
11
9
  *
12
10
  * This entry follows the standard DSH plugin export contract `apply(ctx)`.
11
+ *
12
+ * The host-side services (`ctx.connection.rpc`, `ctx.fs`) are consumed through
13
+ * small local structural types instead of importing the harness packages, so the
14
+ * plugin's `tsc` stays inside its own `rootDir` and never drags harness source
15
+ * files into its program.
13
16
  */
17
+ import { RPC_CHANNEL, EP_CONTEXT } from './types';
18
+ /** Project doc files probed, in priority order. Reads are best-effort. */
19
+ const DOC_CANDIDATES = [
20
+ 'README.md',
21
+ 'README',
22
+ 'AGENTS.md',
23
+ 'CLAUDE.md',
24
+ 'CONTRIBUTING.md',
25
+ 'docs/README.md',
26
+ 'docs/index.md',
27
+ '.cursorrules',
28
+ ];
29
+ /** Per-doc content cap so a huge README cannot flood the input. */
30
+ const MAX_DOC_CHARS = 4000;
14
31
  /**
15
- * Minimal host plugin body. The button logic lives entirely in the client
16
- * bundle; mounting this row is what lets the web app serve `lib/client.js`.
32
+ * Minimal host plugin body: mount the `/dsh-prompt-star` RPC channel.
33
+ * @param ctx - host root context.
17
34
  */
18
- export function apply(_ctx) {
19
- // no-op by design: the client plugin does the real work.
35
+ export function apply(ctx) {
36
+ const host = ctx;
37
+ // Registrations on `ctx.connection.rpc` belong to this plugin's Cordis fiber,
38
+ // so they are removed automatically when the plugin is torn down.
39
+ host.connection.rpc.handle(RPC_CHANNEL, async (endpoint, payload) => {
40
+ if (endpoint !== EP_CONTEXT) {
41
+ return {
42
+ ok: false,
43
+ error: { code: 'unknown_endpoint', message: `unknown endpoint '${endpoint}'`, details: {} },
44
+ };
45
+ }
46
+ try {
47
+ const req = (payload ?? {});
48
+ const root = req.cwd?.trim() || process.cwd();
49
+ const docs = [];
50
+ const seen = new Set();
51
+ let filesRead = 0;
52
+ for (const name of DOC_CANDIDATES) {
53
+ try {
54
+ const target = host.fs.resolve(name, { cwd: root });
55
+ const text = await host.fs.readText(target);
56
+ const trimmed = text?.trim();
57
+ if (trimmed && !seen.has(name)) {
58
+ seen.add(name);
59
+ docs.push({ name, text: trimmed.slice(0, MAX_DOC_CHARS) });
60
+ filesRead += 1;
61
+ }
62
+ }
63
+ catch {
64
+ // Absent / unreadable / outside the workspace: leave it out.
65
+ }
66
+ }
67
+ return { ok: true, value: { docs, filesRead } };
68
+ }
69
+ catch (err) {
70
+ return {
71
+ ok: false,
72
+ error: {
73
+ code: 'internal',
74
+ message: err instanceof Error ? err.message : String(err),
75
+ details: {},
76
+ },
77
+ };
78
+ }
79
+ });
20
80
  }
21
- export default apply;
22
81
  //# sourceMappingURL=index.js.map
@@ -1,46 +1,45 @@
1
1
  /**
2
- * Shared request/result types for the prompt-star host Remote namespace.
3
- * Kept free of Cordis/Node so the client bundle can import them.
2
+ * dsh-prompt-star — host↔client RPC protocol (shared vocabulary).
3
+ *
4
+ * Transport: the generic Connection RPC channel `/dsh-prompt-star`
5
+ * (the host registers it with `ctx.connection.rpc.handle`, the browser calls
6
+ * `ctx.connection.rpc.call('/dsh-prompt-star', endpoint, payload)`).
7
+ *
8
+ * The host sells exactly one capability: reading a few project documentation
9
+ * files for the current workspace (it CAN read file contents natively; the
10
+ * browser client cannot). Everything else — assembling the fuller prompt from
11
+ * the draft + that context — stays on the client.
4
12
  */
5
- /** One project doc file the host probes and may read as context. */
6
- export interface DocFileSource {
7
- /** Absolute path as resolved on the host. */
8
- readonly path: string;
9
- /** Basename (CLAUDE.md / AGENTS.md / .cursorrules / README.md). */
10
- readonly name: string;
11
- /** Whether the file existed and was read. */
12
- readonly read: boolean;
13
- /** Bytes read, capped; 0 when `read` is false. */
14
- readonly bytes: number;
15
- }
16
- /** Client → Host: a click of ⭐ with the current draft. */
17
- export interface PromptStarRequest {
18
- /** Current draft text (the editor's clipboard-text projection). */
19
- readonly draft: string;
13
+ /** Absolute logical Connection RPC channel owned by this plugin. */
14
+ export declare const RPC_CHANNEL = "/dsh-prompt-star";
15
+ /** Channel-relative endpoint that reads project doc files on the host. */
16
+ export declare const EP_CONTEXT = "context";
17
+ export interface GenerateContextRequest {
20
18
  /**
21
- * Session workspace root when the client knows it, else undefined and the
22
- * host falls back to its own resolution / process.cwd().
19
+ * Optional workspace directory to probe for doc files. When absent the host
20
+ * falls back to the DSH process working directory.
23
21
  */
24
- readonly workspaceRoot?: string;
25
- /** Pick a template intent explicitly; empty lets the model infer it. */
26
- readonly intent?: string;
27
- /** Optional caller cancellation; the host falls back to an internal deadline. */
28
- readonly signal?: AbortSignal;
22
+ cwd?: string;
29
23
  }
30
- /** Host → Client: the generated prompt ready to fill the input. */
31
- export interface PromptStarResult {
32
- /** Generated full prompt text. */
33
- readonly prompt: string;
34
- /** Intent/template the model chose (or the supplied `intent`). */
35
- readonly intent: string;
36
- /** Doc files the host probed for context. */
37
- readonly sources: readonly DocFileSource[];
38
- /** Model route actually used (provider/model when available). */
39
- readonly model?: {
40
- readonly provider: string;
41
- readonly model: string;
42
- };
43
- /** True when a transient/LLM failure was swallowed and `prompt` is a best-effort copy. */
44
- readonly degraded: boolean;
24
+ export interface DocContext {
25
+ /** One entry per successfully read project-doc file. */
26
+ docs: Array<{
27
+ name: string;
28
+ text: string;
29
+ }>;
30
+ /** Number of doc files read. */
31
+ filesRead: number;
45
32
  }
33
+ /** The `context` endpoint's success/error envelope (Connection RPC result). */
34
+ export type ContextRpcResult = {
35
+ ok: true;
36
+ value: DocContext;
37
+ } | {
38
+ ok: false;
39
+ error: {
40
+ code: string;
41
+ message: string;
42
+ details: object;
43
+ };
44
+ };
46
45
  //# sourceMappingURL=types.d.ts.map
@@ -1,6 +1,17 @@
1
1
  /**
2
- * Shared request/result types for the prompt-star host Remote namespace.
3
- * Kept free of Cordis/Node so the client bundle can import them.
2
+ * dsh-prompt-star — host↔client RPC protocol (shared vocabulary).
3
+ *
4
+ * Transport: the generic Connection RPC channel `/dsh-prompt-star`
5
+ * (the host registers it with `ctx.connection.rpc.handle`, the browser calls
6
+ * `ctx.connection.rpc.call('/dsh-prompt-star', endpoint, payload)`).
7
+ *
8
+ * The host sells exactly one capability: reading a few project documentation
9
+ * files for the current workspace (it CAN read file contents natively; the
10
+ * browser client cannot). Everything else — assembling the fuller prompt from
11
+ * the draft + that context — stays on the client.
4
12
  */
5
- export {};
13
+ /** Absolute logical Connection RPC channel owned by this plugin. */
14
+ export const RPC_CHANNEL = '/dsh-prompt-star';
15
+ /** Channel-relative endpoint that reads project doc files on the host. */
16
+ export const EP_CONTEXT = 'context';
6
17
  //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-prompt-star",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "DSH plugin: a ⭐ button beside the conversation input that reads your draft + the project's doc files and turns them into a fuller, more efficient prompt, filling the input directly (Ctrl/Cmd+Z to undo).",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -29,13 +29,8 @@
29
29
  "patch": "./cordis.patch.yml"
30
30
  },
31
31
  "client": {
32
- "external": [
33
- "@deepseek-ai/dsh-api-gateway/client"
34
- ],
35
32
  "inject": [
36
- "@deepseek-ai/dsh-api-gateway",
37
- "@deepseek-ai/dsh-client-connection",
38
- "@deepseek-ai/dsh-client-locale"
33
+ "@deepseek-ai/dsh-client-connection"
39
34
  ],
40
35
  "platform": "web"
41
36
  }
@@ -49,11 +44,9 @@
49
44
  ],
50
45
  "peerDependencies": {
51
46
  "@deepseek-ai/cordis": "^4.0.2",
52
- "@deepseek-ai/dsh-typert-protocol": "^0.1.2-rc.1",
53
- "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
54
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-rc.1",
47
+ "@deepseek-ai/dsh-client-connection": "^0.1.2-rc.1",
48
+ "@deepseek-ai/dsh-fs": "^0.1.2-rc.1",
55
49
  "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-rc.1",
56
- "@deepseek-ai/dsh-client-store": "^0.1.2-rc.1",
57
50
  "react": "^18.3.1"
58
51
  },
59
52
  "devDependencies": {