@agimon-ai/doompi-prompt 0.0.1-alpha.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/LICENSE +21 -0
- package/README.md +118 -0
- package/dist/_virtual/_rolldown/runtime.cjs +1 -0
- package/dist/adapters/hubApi.cjs +2 -0
- package/dist/adapters/hubApi.cjs.map +1 -0
- package/dist/adapters/hubApi.d.cts +13 -0
- package/dist/adapters/hubApi.d.cts.map +1 -0
- package/dist/adapters/hubApi.d.mts +13 -0
- package/dist/adapters/hubApi.d.mts.map +1 -0
- package/dist/adapters/hubApi.mjs +2 -0
- package/dist/adapters/hubApi.mjs.map +1 -0
- package/dist/adapters/node/promptStore.cjs +2 -0
- package/dist/adapters/node/promptStore.cjs.map +1 -0
- package/dist/adapters/node/promptStore.d.cts +12 -0
- package/dist/adapters/node/promptStore.d.cts.map +1 -0
- package/dist/adapters/node/promptStore.d.mts +12 -0
- package/dist/adapters/node/promptStore.d.mts.map +1 -0
- package/dist/adapters/node/promptStore.mjs +2 -0
- package/dist/adapters/node/promptStore.mjs.map +1 -0
- package/dist/adapters/pi/extension.cjs +2 -0
- package/dist/adapters/pi/extension.cjs.map +1 -0
- package/dist/adapters/pi/extension.d.cts +19 -0
- package/dist/adapters/pi/extension.d.cts.map +1 -0
- package/dist/adapters/pi/extension.d.mts +19 -0
- package/dist/adapters/pi/extension.d.mts.map +1 -0
- package/dist/adapters/pi/extension.mjs +2 -0
- package/dist/adapters/pi/extension.mjs.map +1 -0
- package/dist/adapters/pi/inputCapture.cjs +2 -0
- package/dist/adapters/pi/inputCapture.cjs.map +1 -0
- package/dist/adapters/pi/inputCapture.mjs +2 -0
- package/dist/adapters/pi/inputCapture.mjs.map +1 -0
- package/dist/adapters/pi/leader.cjs +2 -0
- package/dist/adapters/pi/leader.cjs.map +1 -0
- package/dist/adapters/pi/leader.mjs +2 -0
- package/dist/adapters/pi/leader.mjs.map +1 -0
- package/dist/adapters/pi/promptConstants.cjs +2 -0
- package/dist/adapters/pi/promptConstants.cjs.map +1 -0
- package/dist/adapters/pi/promptConstants.mjs +2 -0
- package/dist/adapters/pi/promptConstants.mjs.map +1 -0
- package/dist/commands/promptSaveCommand.cjs +2 -0
- package/dist/commands/promptSaveCommand.cjs.map +1 -0
- package/dist/commands/promptSaveCommand.mjs +2 -0
- package/dist/commands/promptSaveCommand.mjs.map +1 -0
- package/dist/commands/promptsCommand.cjs +2 -0
- package/dist/commands/promptsCommand.cjs.map +1 -0
- package/dist/commands/promptsCommand.mjs +2 -0
- package/dist/commands/promptsCommand.mjs.map +1 -0
- package/dist/container/index.cjs +2 -0
- package/dist/container/index.cjs.map +1 -0
- package/dist/container/index.d.cts +7 -0
- package/dist/container/index.d.cts.map +1 -0
- package/dist/container/index.d.mts +7 -0
- package/dist/container/index.d.mts.map +1 -0
- package/dist/container/index.mjs +2 -0
- package/dist/container/index.mjs.map +1 -0
- package/dist/extensions/pi.cjs +1 -0
- package/dist/extensions/pi.d.cts +2 -0
- package/dist/extensions/pi.d.mts +2 -0
- package/dist/extensions/pi.mjs +1 -0
- package/dist/hubApi.cjs +1 -0
- package/dist/hubApi.d.cts +2 -0
- package/dist/hubApi.d.mts +2 -0
- package/dist/hubApi.mjs +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.mjs +1 -0
- package/dist/services/promptItems.cjs +2 -0
- package/dist/services/promptItems.cjs.map +1 -0
- package/dist/services/promptItems.mjs +2 -0
- package/dist/services/promptItems.mjs.map +1 -0
- package/dist/services/recentPrompts.cjs +2 -0
- package/dist/services/recentPrompts.cjs.map +1 -0
- package/dist/services/recentPrompts.d.cts +20 -0
- package/dist/services/recentPrompts.d.cts.map +1 -0
- package/dist/services/recentPrompts.d.mts +20 -0
- package/dist/services/recentPrompts.d.mts.map +1 -0
- package/dist/services/recentPrompts.mjs +2 -0
- package/dist/services/recentPrompts.mjs.map +1 -0
- package/dist/services/savedPromptDocument.cjs +11 -0
- package/dist/services/savedPromptDocument.cjs.map +1 -0
- package/dist/services/savedPromptDocument.d.cts +21 -0
- package/dist/services/savedPromptDocument.d.cts.map +1 -0
- package/dist/services/savedPromptDocument.d.mts +21 -0
- package/dist/services/savedPromptDocument.d.mts.map +1 -0
- package/dist/services/savedPromptDocument.mjs +11 -0
- package/dist/services/savedPromptDocument.mjs.map +1 -0
- package/dist/types/prompt.d.cts +54 -0
- package/dist/types/prompt.d.cts.map +1 -0
- package/dist/types/prompt.d.mts +54 -0
- package/dist/types/prompt.d.mts.map +1 -0
- package/dist/types/webPrompts.cjs +2 -0
- package/dist/types/webPrompts.cjs.map +1 -0
- package/dist/types/webPrompts.mjs +2 -0
- package/dist/types/webPrompts.mjs.map +1 -0
- package/llms.txt +11 -0
- package/package.json +124 -0
- package/src/exports/webClient.ts +2 -0
- package/src/prompts/doompi-use-prompt/SKILL.md +17 -0
- package/src/types/webPrompts.ts +61 -0
- package/src/web/PromptEditor.tsx +57 -0
- package/src/web/PromptPickerList.tsx +108 -0
- package/src/web/PromptsActivitySection.tsx +69 -0
- package/src/web/PromptsDialog.tsx +111 -0
- package/src/web/index.ts +32 -0
- package/src/web/promptsActions.ts +72 -0
- package/src/web/promptsApi.ts +88 -0
- package/src/web/tsconfig.json +22 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vuong Ngo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# @agimon-ai/doompi-prompt
|
|
2
|
+
|
|
3
|
+
Staged recent prompts and saved prompt templates for DoomPi
|
|
4
|
+
|
|
5
|
+
This package is a composable [DoomPi](https://www.npmjs.com/package/@agimon-ai/doompi) subsystem. Use it with the distribution or install it independently in [Pi](https://www.npmjs.com/package/@earendil-works/pi-coding-agent).
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Node.js 22.19.0 or newer
|
|
10
|
+
- `@earendil-works/pi-coding-agent` 0.84.4
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pi install npm:@agimon-ai/doompi-prompt
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The package declares its Pi extension entry, so Pi loads it after installation. DoomPi users can include the package through their normal profile and domain composition instead.
|
|
19
|
+
|
|
20
|
+
## Use
|
|
21
|
+
|
|
22
|
+
Run the registered command in Pi:
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
/prompts
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Browse the last three prompts of this session and every saved prompt, then stage one in the editor.
|
|
29
|
+
|
|
30
|
+
## Package behavior
|
|
31
|
+
|
|
32
|
+
Two commands, and no history file:
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
/prompts Pick a staged or saved prompt and stage it in the editor (also SPC e p)
|
|
36
|
+
/prompt-save review Save the current draft as ~/.pi/agent/prompts/review.md
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The last three prompts you submit are held in memory for the session only, and nothing is written
|
|
40
|
+
to disk unless you run `/prompt-save`. Saving writes an ordinary Pi prompt template:
|
|
41
|
+
|
|
42
|
+
```markdown
|
|
43
|
+
---
|
|
44
|
+
description: 'Review the staged diff'
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
Review the staged diff and report bugs, security issues and error handling gaps.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Because it is a plain template, Pi also exposes it as `/review` from the next start, with the usual
|
|
51
|
+
`$1` and `$@` argument substitution. The picker reads the directory directly, so a prompt saved a
|
|
52
|
+
minute ago is already listed in the current session.
|
|
53
|
+
|
|
54
|
+
Arrow up and down are untouched: they remain Pi's own in-session history. To recall regardless of
|
|
55
|
+
cursor position, bind `tui.editor.historyPrevious` and `tui.editor.historyNext` in
|
|
56
|
+
`~/.pi/agent/keybindings.json`.
|
|
57
|
+
|
|
58
|
+
## Cockpit
|
|
59
|
+
|
|
60
|
+
The package contributes a `prompts` group to the cockpit's activity dock, next to agents, runners
|
|
61
|
+
and workflows, backed by a hub-scoped API at `/api/plugin/prompts`. The group reports how many
|
|
62
|
+
prompts are saved and offers `send a prompt`, which opens a dialog over the conversation. Picking
|
|
63
|
+
one sends it to the focused session; the same dialog creates, edits, renames and deletes entries.
|
|
64
|
+
|
|
65
|
+
| Route | Purpose |
|
|
66
|
+
| ----------------------- | ------------------------------------------------------------------- |
|
|
67
|
+
| `GET /prompts` | Every saved prompt, sorted by name |
|
|
68
|
+
| `PUT /prompts/:name` | Create or replace one, deriving its description from the first line |
|
|
69
|
+
| `DELETE /prompts/:name` | Remove one |
|
|
70
|
+
|
|
71
|
+
The API is hub-scoped because saved prompts belong to the agent directory rather than to a session.
|
|
72
|
+
Staged prompts are not exposed there: they live in the memory of the session that received them.
|
|
73
|
+
|
|
74
|
+
In RPC hosts such as the cockpit, Pi reports an empty editor draft, so `/prompt-save` run from the
|
|
75
|
+
cockpit composer saves the newest staged prompt rather than the text being typed. Save from the
|
|
76
|
+
dialog instead.
|
|
77
|
+
|
|
78
|
+
The cockpit hub mounts package APIs from the route table `doompi sync` generates. After installing
|
|
79
|
+
or changing this package, run a build and `doompi sync`, then restart the hub, or its routes answer 404.
|
|
80
|
+
|
|
81
|
+
## Help
|
|
82
|
+
|
|
83
|
+
The published package includes `llms.txt` and the package-owned `src/prompts/doompi-use-prompt/SKILL.md`. When the DoomPi Help minor mode is active, the extension contributes `doompi-use-prompt` to its live Help catalog. The contribution follows Help provider replacement and is withdrawn when the extension shuts down.
|
|
84
|
+
|
|
85
|
+
Help remains optional. Loading the standalone Pi extension does not require DoomPi Help or any other DoomPi runtime service.
|
|
86
|
+
|
|
87
|
+
To add another package-owned Help prompt, use the `scaffold-doom-prompt` feature with a `doompi-author-*` or `doompi-use-*` name and a concise description. Then link the generated `SKILL.md` from `llms.txt` and register a matching descriptor through the optional `DOOM_HELP_SERVICE` injection in the Pi adapter. Keep prompts as published resources under `src/prompts`; do not export them or copy them into `dist`.
|
|
88
|
+
|
|
89
|
+
## Public API
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { activatePromptExtension, createNodeSavedPromptStore, createRecentPrompts } from '@agimon-ai/doompi-prompt';
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The Pi host entry is also available at:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
@agimon-ai/doompi-prompt/extensions/pi
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The service layer is host-neutral. The Pi entrypoint owns only command registration and its runtime-scoped installation guard.
|
|
102
|
+
|
|
103
|
+
## Development
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pnpm build
|
|
107
|
+
pnpm typecheck
|
|
108
|
+
pnpm test
|
|
109
|
+
pnpm lint
|
|
110
|
+
pnpm exec vibe-lint check .
|
|
111
|
+
npm pack --dry-run
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Maintained by [Agimon](https://agimon.ai/about).
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));exports.__toESM=s;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../services/savedPromptDocument.cjs"),t=require("../types/webPrompts.cjs"),n=require("./node/promptStore.cjs");let r=require("hono");const i=`name`,a={badRequest:400,notFound:404,tooLarge:413,failed:500};function o(e){return{name:e.name,description:e.description,text:e.text}}function s(e){return e instanceof Error?e.message:`The prompt library could not complete that request.`}async function c(e){let t;try{t=await e.json()}catch{return{error:`The request body must be JSON.`}}let n=t?.text;return typeof n!=`string`||n.trim().length===0?{error:`A non-empty text is required.`}:Buffer.byteLength(n,`utf8`)>65536?{error:`That prompt is too large to save.`}:{text:n}}function l(l={}){let u=l.store??n.createNodeSavedPromptStore(),d=new r.Hono;return d.get(t.PROMPTS_PATH,async e=>{try{let t=await u.list();return e.json({prompts:t.map(o)})}catch(t){return e.json({error:s(t)},a.failed)}}),d.put(`${t.PROMPTS_PATH}/:${i}`,async t=>{let n=t.req.param(i);if(!e.isValidPromptName(n))return t.json({error:`"${n}" is not a usable prompt name. Use ${e.PROMPT_NAME_RULE}.`},a.badRequest);let r=await c(t.req.raw);if(`error`in r){let e=r.error.includes(`too large`)?a.tooLarge:a.badRequest;return t.json({error:r.error},e)}try{let i=await u.has(n),a={name:n,description:e.describePrompt(r.text),text:r.text};return await u.save(a),t.json({prompt:o(a),replaced:i})}catch(e){return t.json({error:s(e)},a.failed)}}),d.delete(`${t.PROMPTS_PATH}/:${i}`,async t=>{let n=t.req.param(i);if(!e.isValidPromptName(n))return t.json({error:`"${n}" is not a usable prompt name.`},a.badRequest);try{return await u.remove(n)?t.json({name:n}):t.json({error:`No saved prompt is named "${n}".`},a.notFound)}catch(e){return t.json({error:s(e)},a.failed)}}),d}const u={basePath:t.API_BASE_PATH,start(e){let t=l();return{fetch:e=>t.fetch(e),close:()=>void 0}}};exports.api=u,exports.createPromptHubApi=l;
|
|
2
|
+
//# sourceMappingURL=hubApi.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hubApi.cjs","names":["createNodeSavedPromptStore","Hono","PROMPTS_PATH","isValidPromptName","PROMPT_NAME_RULE","describePrompt","API_BASE_PATH"],"sources":["../../src/adapters/hubApi.ts"],"sourcesContent":["import type { DoomApi, DoomApiContext, DoomApiHandler } from '@agimon-ai/doompi-extension-contracts/package-api';\nimport { Hono } from 'hono';\nimport { describePrompt, isValidPromptName, PROMPT_NAME_RULE } from '../services/savedPromptDocument.ts';\nimport type { SavedPrompt, SavedPromptStore } from '../types/prompt.ts';\nimport {\n API_BASE_PATH,\n PROMPTS_PATH,\n type SavedPromptListResponse,\n type SavedPromptView,\n type SavedPromptWriteResponse,\n} from '../types/webPrompts.ts';\nimport { createNodeSavedPromptStore } from './node/promptStore.ts';\n\n/**\n * This package's HTTP surface, served by the cockpit hub.\n *\n * DESIGN PATTERNS:\n * - Hub scope, not session scope: saved prompts live in the agent directory and\n * belong to the machine, so no route names a session.\n * - The store is injected. The same port the Pi commands write through answers\n * the browser, so the two surfaces cannot disagree about the format on disk.\n * - Routes are declared relative to the mount the host strips.\n *\n * AVOID:\n * - Validating names here by hand. The rule lives in src/services with the\n * document format it protects.\n */\n\nconst NAME_PARAM = 'name';\nconst MAX_PROMPT_BYTES = 64 * 1024;\nconst STATUS = { badRequest: 400, notFound: 404, tooLarge: 413, failed: 500 } as const;\n\nexport interface HubApiOptions {\n store?: SavedPromptStore;\n}\n\nfunction toView(prompt: SavedPrompt): SavedPromptView {\n return { name: prompt.name, description: prompt.description, text: prompt.text };\n}\n\nfunction failureMessage(error: unknown): string {\n return error instanceof Error ? error.message : 'The prompt library could not complete that request.';\n}\n\n/** Reads `{ text }` out of a request body, or the reason it is unusable. */\nasync function readText(request: Request): Promise<{ text: string } | { error: string }> {\n let payload: unknown;\n try {\n payload = await request.json();\n } catch {\n // A body that is not JSON is a client mistake, reported as one.\n return { error: 'The request body must be JSON.' };\n }\n const text = (payload as { text?: unknown } | null)?.text;\n if (typeof text !== 'string' || text.trim().length === 0) return { error: 'A non-empty text is required.' };\n if (Buffer.byteLength(text, 'utf8') > MAX_PROMPT_BYTES) return { error: 'That prompt is too large to save.' };\n return { text };\n}\n\nexport function createPromptHubApi(options: HubApiOptions = {}): Hono {\n const store = options.store ?? createNodeSavedPromptStore();\n const app = new Hono();\n\n app.get(PROMPTS_PATH, async (context) => {\n try {\n const prompts = await store.list();\n return context.json<SavedPromptListResponse>({ prompts: prompts.map(toView) });\n } catch (error) {\n return context.json({ error: failureMessage(error) }, STATUS.failed);\n }\n });\n\n app.put(`${PROMPTS_PATH}/:${NAME_PARAM}`, async (context) => {\n const name = context.req.param(NAME_PARAM);\n if (!isValidPromptName(name)) {\n return context.json(\n { error: `\"${name}\" is not a usable prompt name. Use ${PROMPT_NAME_RULE}.` },\n STATUS.badRequest,\n );\n }\n\n const body = await readText(context.req.raw);\n if ('error' in body) {\n const status = body.error.includes('too large') ? STATUS.tooLarge : STATUS.badRequest;\n return context.json({ error: body.error }, status);\n }\n\n try {\n const replaced = await store.has(name);\n const prompt: SavedPrompt = { name, description: describePrompt(body.text), text: body.text };\n await store.save(prompt);\n return context.json<SavedPromptWriteResponse>({ prompt: toView(prompt), replaced });\n } catch (error) {\n return context.json({ error: failureMessage(error) }, STATUS.failed);\n }\n });\n\n app.delete(`${PROMPTS_PATH}/:${NAME_PARAM}`, async (context) => {\n const name = context.req.param(NAME_PARAM);\n if (!isValidPromptName(name)) {\n return context.json({ error: `\"${name}\" is not a usable prompt name.` }, STATUS.badRequest);\n }\n\n try {\n const removed = await store.remove(name);\n if (!removed) return context.json({ error: `No saved prompt is named \"${name}\".` }, STATUS.notFound);\n return context.json({ name });\n } catch (error) {\n return context.json({ error: failureMessage(error) }, STATUS.failed);\n }\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built entry. */\nexport const api: DoomApi = {\n basePath: API_BASE_PATH,\n start(_context: DoomApiContext): DoomApiHandler {\n const app = createPromptHubApi();\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request here; the store holds no handles.\n close: () => undefined,\n };\n },\n};\n"],"mappings":"sJA4BA,MAAM,EAAa,OAEb,EAAS,CAAE,WAAY,IAAK,SAAU,IAAK,SAAU,IAAK,OAAQ,GAAI,EAM5E,SAAS,EAAO,EAAsC,CACpD,MAAO,CAAE,KAAM,EAAO,KAAM,YAAa,EAAO,YAAa,KAAM,EAAO,IAAK,CACjF,CAEA,SAAS,EAAe,EAAwB,CAC9C,OAAO,aAAiB,MAAQ,EAAM,QAAU,qDAClD,CAGA,eAAe,EAAS,EAAiE,CACvF,IAAI,EACJ,GAAI,CACF,EAAU,MAAM,EAAQ,KAAK,CAC/B,MAAQ,CAEN,MAAO,CAAE,MAAO,gCAAiC,CACnD,CACA,IAAM,EAAQ,GAAuC,KAGrD,OAFI,OAAO,GAAS,UAAY,EAAK,KAAK,CAAC,CAAC,SAAW,EAAU,CAAE,MAAO,+BAAgC,EACtG,OAAO,WAAW,EAAM,MAAM,EAAI,MAAyB,CAAE,MAAO,mCAAoC,EACrG,CAAE,MAAK,CAChB,CAEA,SAAgB,EAAmB,EAAyB,CAAC,EAAS,CACpE,IAAM,EAAQ,EAAQ,OAASA,EAAAA,2BAA2B,EACpD,EAAM,IAAIC,EAAAA,KAmDhB,OAjDA,EAAI,IAAIC,EAAAA,aAAc,KAAO,IAAY,CACvC,GAAI,CACF,IAAM,EAAU,MAAM,EAAM,KAAK,EACjC,OAAO,EAAQ,KAA8B,CAAE,QAAS,EAAQ,IAAI,CAAM,CAAE,CAAC,CAC/E,OAAS,EAAO,CACd,OAAO,EAAQ,KAAK,CAAE,MAAO,EAAe,CAAK,CAAE,EAAG,EAAO,MAAM,CACrE,CACF,CAAC,EAED,EAAI,IAAI,GAAGA,EAAAA,aAAa,IAAI,IAAc,KAAO,IAAY,CAC3D,IAAM,EAAO,EAAQ,IAAI,MAAM,CAAU,EACzC,GAAI,CAACC,EAAAA,kBAAkB,CAAI,EACzB,OAAO,EAAQ,KACb,CAAE,MAAO,IAAI,EAAK,qCAAqCC,EAAAA,iBAAiB,EAAG,EAC3E,EAAO,UACT,EAGF,IAAM,EAAO,MAAM,EAAS,EAAQ,IAAI,GAAG,EAC3C,GAAI,UAAW,EAAM,CACnB,IAAM,EAAS,EAAK,MAAM,SAAS,WAAW,EAAI,EAAO,SAAW,EAAO,WAC3E,OAAO,EAAQ,KAAK,CAAE,MAAO,EAAK,KAAM,EAAG,CAAM,CACnD,CAEA,GAAI,CACF,IAAM,EAAW,MAAM,EAAM,IAAI,CAAI,EAC/B,EAAsB,CAAE,OAAM,YAAaC,EAAAA,eAAe,EAAK,IAAI,EAAG,KAAM,EAAK,IAAK,EAE5F,OADA,MAAM,EAAM,KAAK,CAAM,EAChB,EAAQ,KAA+B,CAAE,OAAQ,EAAO,CAAM,EAAG,UAAS,CAAC,CACpF,OAAS,EAAO,CACd,OAAO,EAAQ,KAAK,CAAE,MAAO,EAAe,CAAK,CAAE,EAAG,EAAO,MAAM,CACrE,CACF,CAAC,EAED,EAAI,OAAO,GAAGH,EAAAA,aAAa,IAAI,IAAc,KAAO,IAAY,CAC9D,IAAM,EAAO,EAAQ,IAAI,MAAM,CAAU,EACzC,GAAI,CAACC,EAAAA,kBAAkB,CAAI,EACzB,OAAO,EAAQ,KAAK,CAAE,MAAO,IAAI,EAAK,+BAAgC,EAAG,EAAO,UAAU,EAG5F,GAAI,CAGF,OADK,MADiB,EAAM,OAAO,CAAI,EAEhC,EAAQ,KAAK,CAAE,MAAK,CAAC,EADP,EAAQ,KAAK,CAAE,MAAO,6BAA6B,EAAK,GAAI,EAAG,EAAO,QAAQ,CAErG,OAAS,EAAO,CACd,OAAO,EAAQ,KAAK,CAAE,MAAO,EAAe,CAAK,CAAE,EAAG,EAAO,MAAM,CACrE,CACF,CAAC,EAEM,CACT,CAGA,MAAa,EAAe,CAC1B,SAAUG,EAAAA,cACV,MAAM,EAA0C,CAC9C,IAAM,EAAM,EAAmB,EAC/B,MAAO,CACL,MAAQ,GAAY,EAAI,MAAM,CAAO,EAErC,UAAa,IAAA,EACf,CACF,CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SavedPromptStore } from "../types/prompt.cjs";
|
|
2
|
+
import { DoomApi } from "@agimon-ai/doompi-extension-contracts/package-api";
|
|
3
|
+
import { Hono } from "hono";
|
|
4
|
+
//#region src/adapters/hubApi.d.ts
|
|
5
|
+
interface HubApiOptions {
|
|
6
|
+
store?: SavedPromptStore;
|
|
7
|
+
}
|
|
8
|
+
declare function createPromptHubApi(options?: HubApiOptions): Hono;
|
|
9
|
+
/** The named export a host imports from this package's built entry. */
|
|
10
|
+
declare const api: DoomApi;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { HubApiOptions, api, createPromptHubApi };
|
|
13
|
+
//# sourceMappingURL=hubApi.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hubApi.d.cts","names":[],"sources":["../../src/adapters/hubApi.ts"],"mappings":";;;;UAgCiB;EACf,QAAQ;;iBA0BM,mBAAmB,UAAS,gBAAqB;;cAyDpD,KAAK"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SavedPromptStore } from "../types/prompt.mjs";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { DoomApi } from "@agimon-ai/doompi-extension-contracts/package-api";
|
|
4
|
+
//#region src/adapters/hubApi.d.ts
|
|
5
|
+
interface HubApiOptions {
|
|
6
|
+
store?: SavedPromptStore;
|
|
7
|
+
}
|
|
8
|
+
declare function createPromptHubApi(options?: HubApiOptions): Hono;
|
|
9
|
+
/** The named export a host imports from this package's built entry. */
|
|
10
|
+
declare const api: DoomApi;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { HubApiOptions, api, createPromptHubApi };
|
|
13
|
+
//# sourceMappingURL=hubApi.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hubApi.d.mts","names":[],"sources":["../../src/adapters/hubApi.ts"],"mappings":";;;;UAgCiB;EACf,QAAQ;;iBA0BM,mBAAmB,UAAS,gBAAqB;;cAyDpD,KAAK"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{PROMPT_NAME_RULE as e,describePrompt as t,isValidPromptName as n}from"../services/savedPromptDocument.mjs";import{API_BASE_PATH as r,PROMPTS_PATH as i}from"../types/webPrompts.mjs";import{createNodeSavedPromptStore as a}from"./node/promptStore.mjs";import{Hono as o}from"hono";const s=`name`,c={badRequest:400,notFound:404,tooLarge:413,failed:500};function l(e){return{name:e.name,description:e.description,text:e.text}}function u(e){return e instanceof Error?e.message:`The prompt library could not complete that request.`}async function d(e){let t;try{t=await e.json()}catch{return{error:`The request body must be JSON.`}}let n=t?.text;return typeof n!=`string`||n.trim().length===0?{error:`A non-empty text is required.`}:Buffer.byteLength(n,`utf8`)>65536?{error:`That prompt is too large to save.`}:{text:n}}function f(r={}){let f=r.store??a(),p=new o;return p.get(i,async e=>{try{let t=await f.list();return e.json({prompts:t.map(l)})}catch(t){return e.json({error:u(t)},c.failed)}}),p.put(`${i}/:${s}`,async r=>{let i=r.req.param(s);if(!n(i))return r.json({error:`"${i}" is not a usable prompt name. Use ${e}.`},c.badRequest);let a=await d(r.req.raw);if(`error`in a){let e=a.error.includes(`too large`)?c.tooLarge:c.badRequest;return r.json({error:a.error},e)}try{let e=await f.has(i),n={name:i,description:t(a.text),text:a.text};return await f.save(n),r.json({prompt:l(n),replaced:e})}catch(e){return r.json({error:u(e)},c.failed)}}),p.delete(`${i}/:${s}`,async e=>{let t=e.req.param(s);if(!n(t))return e.json({error:`"${t}" is not a usable prompt name.`},c.badRequest);try{return await f.remove(t)?e.json({name:t}):e.json({error:`No saved prompt is named "${t}".`},c.notFound)}catch(t){return e.json({error:u(t)},c.failed)}}),p}const p={basePath:r,start(e){let t=f();return{fetch:e=>t.fetch(e),close:()=>void 0}}};export{p as api,f as createPromptHubApi};
|
|
2
|
+
//# sourceMappingURL=hubApi.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hubApi.mjs","names":[],"sources":["../../src/adapters/hubApi.ts"],"sourcesContent":["import type { DoomApi, DoomApiContext, DoomApiHandler } from '@agimon-ai/doompi-extension-contracts/package-api';\nimport { Hono } from 'hono';\nimport { describePrompt, isValidPromptName, PROMPT_NAME_RULE } from '../services/savedPromptDocument.ts';\nimport type { SavedPrompt, SavedPromptStore } from '../types/prompt.ts';\nimport {\n API_BASE_PATH,\n PROMPTS_PATH,\n type SavedPromptListResponse,\n type SavedPromptView,\n type SavedPromptWriteResponse,\n} from '../types/webPrompts.ts';\nimport { createNodeSavedPromptStore } from './node/promptStore.ts';\n\n/**\n * This package's HTTP surface, served by the cockpit hub.\n *\n * DESIGN PATTERNS:\n * - Hub scope, not session scope: saved prompts live in the agent directory and\n * belong to the machine, so no route names a session.\n * - The store is injected. The same port the Pi commands write through answers\n * the browser, so the two surfaces cannot disagree about the format on disk.\n * - Routes are declared relative to the mount the host strips.\n *\n * AVOID:\n * - Validating names here by hand. The rule lives in src/services with the\n * document format it protects.\n */\n\nconst NAME_PARAM = 'name';\nconst MAX_PROMPT_BYTES = 64 * 1024;\nconst STATUS = { badRequest: 400, notFound: 404, tooLarge: 413, failed: 500 } as const;\n\nexport interface HubApiOptions {\n store?: SavedPromptStore;\n}\n\nfunction toView(prompt: SavedPrompt): SavedPromptView {\n return { name: prompt.name, description: prompt.description, text: prompt.text };\n}\n\nfunction failureMessage(error: unknown): string {\n return error instanceof Error ? error.message : 'The prompt library could not complete that request.';\n}\n\n/** Reads `{ text }` out of a request body, or the reason it is unusable. */\nasync function readText(request: Request): Promise<{ text: string } | { error: string }> {\n let payload: unknown;\n try {\n payload = await request.json();\n } catch {\n // A body that is not JSON is a client mistake, reported as one.\n return { error: 'The request body must be JSON.' };\n }\n const text = (payload as { text?: unknown } | null)?.text;\n if (typeof text !== 'string' || text.trim().length === 0) return { error: 'A non-empty text is required.' };\n if (Buffer.byteLength(text, 'utf8') > MAX_PROMPT_BYTES) return { error: 'That prompt is too large to save.' };\n return { text };\n}\n\nexport function createPromptHubApi(options: HubApiOptions = {}): Hono {\n const store = options.store ?? createNodeSavedPromptStore();\n const app = new Hono();\n\n app.get(PROMPTS_PATH, async (context) => {\n try {\n const prompts = await store.list();\n return context.json<SavedPromptListResponse>({ prompts: prompts.map(toView) });\n } catch (error) {\n return context.json({ error: failureMessage(error) }, STATUS.failed);\n }\n });\n\n app.put(`${PROMPTS_PATH}/:${NAME_PARAM}`, async (context) => {\n const name = context.req.param(NAME_PARAM);\n if (!isValidPromptName(name)) {\n return context.json(\n { error: `\"${name}\" is not a usable prompt name. Use ${PROMPT_NAME_RULE}.` },\n STATUS.badRequest,\n );\n }\n\n const body = await readText(context.req.raw);\n if ('error' in body) {\n const status = body.error.includes('too large') ? STATUS.tooLarge : STATUS.badRequest;\n return context.json({ error: body.error }, status);\n }\n\n try {\n const replaced = await store.has(name);\n const prompt: SavedPrompt = { name, description: describePrompt(body.text), text: body.text };\n await store.save(prompt);\n return context.json<SavedPromptWriteResponse>({ prompt: toView(prompt), replaced });\n } catch (error) {\n return context.json({ error: failureMessage(error) }, STATUS.failed);\n }\n });\n\n app.delete(`${PROMPTS_PATH}/:${NAME_PARAM}`, async (context) => {\n const name = context.req.param(NAME_PARAM);\n if (!isValidPromptName(name)) {\n return context.json({ error: `\"${name}\" is not a usable prompt name.` }, STATUS.badRequest);\n }\n\n try {\n const removed = await store.remove(name);\n if (!removed) return context.json({ error: `No saved prompt is named \"${name}\".` }, STATUS.notFound);\n return context.json({ name });\n } catch (error) {\n return context.json({ error: failureMessage(error) }, STATUS.failed);\n }\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built entry. */\nexport const api: DoomApi = {\n basePath: API_BASE_PATH,\n start(_context: DoomApiContext): DoomApiHandler {\n const app = createPromptHubApi();\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request here; the store holds no handles.\n close: () => undefined,\n };\n },\n};\n"],"mappings":"4RA4BA,MAAM,EAAa,OAEb,EAAS,CAAE,WAAY,IAAK,SAAU,IAAK,SAAU,IAAK,OAAQ,GAAI,EAM5E,SAAS,EAAO,EAAsC,CACpD,MAAO,CAAE,KAAM,EAAO,KAAM,YAAa,EAAO,YAAa,KAAM,EAAO,IAAK,CACjF,CAEA,SAAS,EAAe,EAAwB,CAC9C,OAAO,aAAiB,MAAQ,EAAM,QAAU,qDAClD,CAGA,eAAe,EAAS,EAAiE,CACvF,IAAI,EACJ,GAAI,CACF,EAAU,MAAM,EAAQ,KAAK,CAC/B,MAAQ,CAEN,MAAO,CAAE,MAAO,gCAAiC,CACnD,CACA,IAAM,EAAQ,GAAuC,KAGrD,OAFI,OAAO,GAAS,UAAY,EAAK,KAAK,CAAC,CAAC,SAAW,EAAU,CAAE,MAAO,+BAAgC,EACtG,OAAO,WAAW,EAAM,MAAM,EAAI,MAAyB,CAAE,MAAO,mCAAoC,EACrG,CAAE,MAAK,CAChB,CAEA,SAAgB,EAAmB,EAAyB,CAAC,EAAS,CACpE,IAAM,EAAQ,EAAQ,OAAS,EAA2B,EACpD,EAAM,IAAI,EAmDhB,OAjDA,EAAI,IAAI,EAAc,KAAO,IAAY,CACvC,GAAI,CACF,IAAM,EAAU,MAAM,EAAM,KAAK,EACjC,OAAO,EAAQ,KAA8B,CAAE,QAAS,EAAQ,IAAI,CAAM,CAAE,CAAC,CAC/E,OAAS,EAAO,CACd,OAAO,EAAQ,KAAK,CAAE,MAAO,EAAe,CAAK,CAAE,EAAG,EAAO,MAAM,CACrE,CACF,CAAC,EAED,EAAI,IAAI,GAAG,EAAa,IAAI,IAAc,KAAO,IAAY,CAC3D,IAAM,EAAO,EAAQ,IAAI,MAAM,CAAU,EACzC,GAAI,CAAC,EAAkB,CAAI,EACzB,OAAO,EAAQ,KACb,CAAE,MAAO,IAAI,EAAK,qCAAqC,EAAiB,EAAG,EAC3E,EAAO,UACT,EAGF,IAAM,EAAO,MAAM,EAAS,EAAQ,IAAI,GAAG,EAC3C,GAAI,UAAW,EAAM,CACnB,IAAM,EAAS,EAAK,MAAM,SAAS,WAAW,EAAI,EAAO,SAAW,EAAO,WAC3E,OAAO,EAAQ,KAAK,CAAE,MAAO,EAAK,KAAM,EAAG,CAAM,CACnD,CAEA,GAAI,CACF,IAAM,EAAW,MAAM,EAAM,IAAI,CAAI,EAC/B,EAAsB,CAAE,OAAM,YAAa,EAAe,EAAK,IAAI,EAAG,KAAM,EAAK,IAAK,EAE5F,OADA,MAAM,EAAM,KAAK,CAAM,EAChB,EAAQ,KAA+B,CAAE,OAAQ,EAAO,CAAM,EAAG,UAAS,CAAC,CACpF,OAAS,EAAO,CACd,OAAO,EAAQ,KAAK,CAAE,MAAO,EAAe,CAAK,CAAE,EAAG,EAAO,MAAM,CACrE,CACF,CAAC,EAED,EAAI,OAAO,GAAG,EAAa,IAAI,IAAc,KAAO,IAAY,CAC9D,IAAM,EAAO,EAAQ,IAAI,MAAM,CAAU,EACzC,GAAI,CAAC,EAAkB,CAAI,EACzB,OAAO,EAAQ,KAAK,CAAE,MAAO,IAAI,EAAK,+BAAgC,EAAG,EAAO,UAAU,EAG5F,GAAI,CAGF,OADK,MADiB,EAAM,OAAO,CAAI,EAEhC,EAAQ,KAAK,CAAE,MAAK,CAAC,EADP,EAAQ,KAAK,CAAE,MAAO,6BAA6B,EAAK,GAAI,EAAG,EAAO,QAAQ,CAErG,OAAS,EAAO,CACd,OAAO,EAAQ,KAAK,CAAE,MAAO,EAAe,CAAK,CAAE,EAAG,EAAO,MAAM,CACrE,CACF,CAAC,EAEM,CACT,CAGA,MAAa,EAAe,CAC1B,SAAU,EACV,MAAM,EAA0C,CAC9C,IAAM,EAAM,EAAmB,EAC/B,MAAO,CACL,MAAQ,GAAY,EAAI,MAAM,CAAO,EAErC,UAAa,IAAA,EACf,CACF,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../services/savedPromptDocument.cjs");let n=require("node:fs/promises"),r=require("node:os"),i=require("node:path");i=e.__toESM(i,1);const a=i.default.join(`.pi`,`agent`);function o(e){if(typeof e!=`object`||!e||!(`code`in e))return;let{code:t}=e;return typeof t==`string`?t:void 0}function s(e){return o(e)===`ENOENT`}function c(e={}){let t=(e.env??process.env).PI_CODING_AGENT_DIR?.trim()||i.default.join(e.home??(0,r.homedir)(),a);return i.default.join(t,`prompts`)}function l(e={}){let r=c(e),a=e=>i.default.join(r,`${e}.md`);return{async list(){let e;try{e=await(0,n.readdir)(r)}catch(e){if(s(e))return[];throw e}let a=[];for(let c of e.filter(e=>e.endsWith(`.md`)).sort()){let e=c.slice(0,-3);try{a.push(t.parsePromptDocument(e,await(0,n.readFile)(i.default.join(r,c),`utf8`)))}catch(e){if(!s(e)&&o(e)!==`EISDIR`)throw e}}return a},async has(e){try{return(await(0,n.stat)(a(e))).isFile()}catch(e){if(s(e))return!1;throw e}},async save(e){await(0,n.mkdir)(r,{recursive:!0,mode:448});let i=a(e.name),o=`${i}.tmp`;return await(0,n.writeFile)(o,t.buildPromptDocument(e),{encoding:`utf8`,mode:384}),await(0,n.rename)(o,i),{name:e.name,path:i}},async remove(e){try{return await(0,n.rm)(a(e)),!0}catch(e){if(s(e))return!1;throw e}}}}exports.createNodeSavedPromptStore=l,exports.resolvePromptsDirectory=c;
|
|
2
|
+
//# sourceMappingURL=promptStore.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptStore.cjs","names":["path","homedir","readdir","parsePromptDocument","readFile","stat","mkdir","writeFile","buildPromptDocument","rename","rm"],"sources":["../../../src/adapters/node/promptStore.ts"],"sourcesContent":["import { mkdir, readdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport path from 'node:path';\nimport { buildPromptDocument, parsePromptDocument } from '../../services/savedPromptDocument.ts';\nimport type { SavedPrompt, SavedPromptStore, SavedPromptWrite } from '../../types/prompt.ts';\n\n/**\n * Saved prompts on disk, as Pi prompt templates.\n *\n * DESIGN PATTERNS:\n * - Writes into Pi's own global prompts directory, so `/<name>` resolves on the\n * next start with no resource contribution from this package.\n * - Private modes and temp-file-then-rename, matching how the rest of the\n * distribution writes user state under the agent directory.\n * - The environment and the home directory are injected, so tests get a real\n * directory without touching the developer's own prompts.\n *\n * AVOID:\n * - Inventing a parallel store. The template file is the only source of truth.\n */\n\nconst AGENT_DIR_ENV = 'PI_CODING_AGENT_DIR';\nconst DEFAULT_AGENT_DIR = path.join('.pi', 'agent');\nconst PROMPTS_DIR = 'prompts';\nconst TEMPLATE_SUFFIX = '.md';\nconst DIRECTORY_MODE = 0o700;\nconst FILE_MODE = 0o600;\nconst MISSING_FILE_CODE = 'ENOENT';\n/** A directory named like a template is not one, and reading it fails this way. */\nconst DIRECTORY_READ_CODE = 'EISDIR';\n\nexport interface NodeSavedPromptStoreOptions {\n env?: NodeJS.ProcessEnv;\n home?: string;\n}\n\nfunction errorCode(error: unknown): string | undefined {\n if (typeof error !== 'object' || error === null || !('code' in error)) return undefined;\n const { code } = error;\n return typeof code === 'string' ? code : undefined;\n}\n\nfunction isMissing(error: unknown): boolean {\n return errorCode(error) === MISSING_FILE_CODE;\n}\n\n/** `<agentDir>/prompts`, honouring the same override Pi itself reads. */\nexport function resolvePromptsDirectory(options: NodeSavedPromptStoreOptions = {}): string {\n const env = options.env ?? process.env;\n const configured = env[AGENT_DIR_ENV]?.trim();\n const agentDir = configured ? configured : path.join(options.home ?? homedir(), DEFAULT_AGENT_DIR);\n return path.join(agentDir, PROMPTS_DIR);\n}\n\nexport function createNodeSavedPromptStore(options: NodeSavedPromptStoreOptions = {}): SavedPromptStore {\n const directory = resolvePromptsDirectory(options);\n const filePath = (name: string): string => path.join(directory, `${name}${TEMPLATE_SUFFIX}`);\n\n return {\n async list(): Promise<readonly SavedPrompt[]> {\n let names: string[];\n try {\n names = await readdir(directory);\n } catch (error) {\n // No prompts directory yet is the normal first-run state, not a fault.\n if (isMissing(error)) return [];\n throw error;\n }\n\n const prompts: SavedPrompt[] = [];\n for (const entry of names.filter((name) => name.endsWith(TEMPLATE_SUFFIX)).sort()) {\n const name = entry.slice(0, -TEMPLATE_SUFFIX.length);\n try {\n prompts.push(parsePromptDocument(name, await readFile(path.join(directory, entry), 'utf8')));\n } catch (error) {\n // A template that vanished or turned into a directory between the\n // listing and the read is skipped; anything else is a real fault.\n if (!isMissing(error) && errorCode(error) !== DIRECTORY_READ_CODE) throw error;\n }\n }\n return prompts;\n },\n\n async has(name: string): Promise<boolean> {\n try {\n const entry = await stat(filePath(name));\n return entry.isFile();\n } catch (error) {\n if (isMissing(error)) return false;\n throw error;\n }\n },\n\n async save(prompt: SavedPrompt): Promise<SavedPromptWrite> {\n await mkdir(directory, { recursive: true, mode: DIRECTORY_MODE });\n const target = filePath(prompt.name);\n const temporary = `${target}.tmp`;\n await writeFile(temporary, buildPromptDocument(prompt), { encoding: 'utf8', mode: FILE_MODE });\n await rename(temporary, target);\n return { name: prompt.name, path: target };\n },\n\n async remove(name: string): Promise<boolean> {\n try {\n await rm(filePath(name));\n return true;\n } catch (error) {\n // Deleting a template that is already gone is the caller's answer, not a fault.\n if (isMissing(error)) return false;\n throw error;\n }\n },\n };\n}\n"],"mappings":"2MAqBA,MACM,EAAoBA,EAAAA,QAAK,KAAK,MAAO,OAAO,EAclD,SAAS,EAAU,EAAoC,CACrD,GAAI,OAAO,GAAU,WAAY,GAAkB,EAAE,SAAU,GAAQ,OACvE,GAAM,CAAE,QAAS,EACjB,OAAO,OAAO,GAAS,SAAW,EAAO,IAAA,EAC3C,CAEA,SAAS,EAAU,EAAyB,CAC1C,OAAO,EAAU,CAAK,IAAM,QAC9B,CAGA,SAAgB,EAAwB,EAAuC,CAAC,EAAW,CAEzF,IACM,GAFM,EAAQ,KAAO,QAAQ,IAAA,CACZ,qBAAgB,KAAK,GACDA,EAAAA,QAAK,KAAK,EAAQ,OAAA,EAAQC,EAAAA,QAAAA,CAAQ,EAAG,CAAiB,EACjG,OAAOD,EAAAA,QAAK,KAAK,EAAU,SAAW,CACxC,CAEA,SAAgB,EAA2B,EAAuC,CAAC,EAAqB,CACtG,IAAM,EAAY,EAAwB,CAAO,EAC3C,EAAY,GAAyBA,EAAAA,QAAK,KAAK,EAAW,GAAG,MAAwB,EAE3F,MAAO,CACL,MAAM,MAAwC,CAC5C,IAAI,EACJ,GAAI,CACF,EAAQ,MAAA,EAAME,EAAAA,QAAAA,CAAQ,CAAS,CACjC,OAAS,EAAO,CAEd,GAAI,EAAU,CAAK,EAAG,MAAO,CAAC,EAC9B,MAAM,CACR,CAEA,IAAM,EAAyB,CAAC,EAChC,IAAK,IAAM,KAAS,EAAM,OAAQ,GAAS,EAAK,SAAS,KAAe,CAAC,CAAC,CAAC,KAAK,EAAG,CACjF,IAAM,EAAO,EAAM,MAAM,EAAG,EAAuB,EACnD,GAAI,CACF,EAAQ,KAAKC,EAAAA,oBAAoB,EAAM,MAAA,EAAMC,EAAAA,SAAAA,CAASJ,EAAAA,QAAK,KAAK,EAAW,CAAK,EAAG,MAAM,CAAC,CAAC,CAC7F,OAAS,EAAO,CAGd,GAAI,CAAC,EAAU,CAAK,GAAK,EAAU,CAAK,IAAM,SAAqB,MAAM,CAC3E,CACF,CACA,OAAO,CACT,EAEA,MAAM,IAAI,EAAgC,CACxC,GAAI,CAEF,OAAO,MAAA,EADaK,EAAAA,KAAAA,CAAK,EAAS,CAAI,CAAC,EAAA,CAC1B,OAAO,CACtB,OAAS,EAAO,CACd,GAAI,EAAU,CAAK,EAAG,MAAO,GAC7B,MAAM,CACR,CACF,EAEA,MAAM,KAAK,EAAgD,CACzD,MAAA,EAAMC,EAAAA,MAAAA,CAAM,EAAW,CAAE,UAAW,GAAM,KAAM,GAAe,CAAC,EAChE,IAAM,EAAS,EAAS,EAAO,IAAI,EAC7B,EAAY,GAAG,EAAO,MAG5B,OAFA,MAAA,EAAMC,EAAAA,UAAAA,CAAU,EAAWC,EAAAA,oBAAoB,CAAM,EAAG,CAAE,SAAU,OAAQ,KAAM,GAAU,CAAC,EAC7F,MAAA,EAAMC,EAAAA,OAAAA,CAAO,EAAW,CAAM,EACvB,CAAE,KAAM,EAAO,KAAM,KAAM,CAAO,CAC3C,EAEA,MAAM,OAAO,EAAgC,CAC3C,GAAI,CAEF,OADA,MAAA,EAAMC,EAAAA,GAAAA,CAAG,EAAS,CAAI,CAAC,EAChB,EACT,OAAS,EAAO,CAEd,GAAI,EAAU,CAAK,EAAG,MAAO,GAC7B,MAAM,CACR,CACF,CACF,CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SavedPromptStore } from "../../types/prompt.cjs";
|
|
2
|
+
//#region src/adapters/node/promptStore.d.ts
|
|
3
|
+
interface NodeSavedPromptStoreOptions {
|
|
4
|
+
env?: NodeJS.ProcessEnv;
|
|
5
|
+
home?: string;
|
|
6
|
+
}
|
|
7
|
+
/** `<agentDir>/prompts`, honouring the same override Pi itself reads. */
|
|
8
|
+
declare function resolvePromptsDirectory(options?: NodeSavedPromptStoreOptions): string;
|
|
9
|
+
declare function createNodeSavedPromptStore(options?: NodeSavedPromptStoreOptions): SavedPromptStore;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { NodeSavedPromptStoreOptions, createNodeSavedPromptStore, resolvePromptsDirectory };
|
|
12
|
+
//# sourceMappingURL=promptStore.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptStore.d.cts","names":[],"sources":["../../../src/adapters/node/promptStore.ts"],"mappings":";;UA+BiB;EACf,MAAM,OAAO;EACb;;;iBAcc,wBAAwB,UAAS;iBAOjC,2BAA2B,UAAS,8BAAmC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SavedPromptStore } from "../../types/prompt.mjs";
|
|
2
|
+
//#region src/adapters/node/promptStore.d.ts
|
|
3
|
+
interface NodeSavedPromptStoreOptions {
|
|
4
|
+
env?: NodeJS.ProcessEnv;
|
|
5
|
+
home?: string;
|
|
6
|
+
}
|
|
7
|
+
/** `<agentDir>/prompts`, honouring the same override Pi itself reads. */
|
|
8
|
+
declare function resolvePromptsDirectory(options?: NodeSavedPromptStoreOptions): string;
|
|
9
|
+
declare function createNodeSavedPromptStore(options?: NodeSavedPromptStoreOptions): SavedPromptStore;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { NodeSavedPromptStoreOptions, createNodeSavedPromptStore, resolvePromptsDirectory };
|
|
12
|
+
//# sourceMappingURL=promptStore.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptStore.d.mts","names":[],"sources":["../../../src/adapters/node/promptStore.ts"],"mappings":";;UA+BiB;EACf,MAAM,OAAO;EACb;;;iBAcc,wBAAwB,UAAS;iBAOjC,2BAA2B,UAAS,8BAAmC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{buildPromptDocument as e,parsePromptDocument as t}from"../../services/savedPromptDocument.mjs";import{mkdir as n,readFile as r,readdir as i,rename as a,rm as o,stat as s,writeFile as c}from"node:fs/promises";import{homedir as l}from"node:os";import u from"node:path";const d=u.join(`.pi`,`agent`);function f(e){if(typeof e!=`object`||!e||!(`code`in e))return;let{code:t}=e;return typeof t==`string`?t:void 0}function p(e){return f(e)===`ENOENT`}function m(e={}){let t=(e.env??process.env).PI_CODING_AGENT_DIR?.trim()||u.join(e.home??l(),d);return u.join(t,`prompts`)}function h(l={}){let d=m(l),h=e=>u.join(d,`${e}.md`);return{async list(){let e;try{e=await i(d)}catch(e){if(p(e))return[];throw e}let n=[];for(let i of e.filter(e=>e.endsWith(`.md`)).sort()){let e=i.slice(0,-3);try{n.push(t(e,await r(u.join(d,i),`utf8`)))}catch(e){if(!p(e)&&f(e)!==`EISDIR`)throw e}}return n},async has(e){try{return(await s(h(e))).isFile()}catch(e){if(p(e))return!1;throw e}},async save(t){await n(d,{recursive:!0,mode:448});let r=h(t.name),i=`${r}.tmp`;return await c(i,e(t),{encoding:`utf8`,mode:384}),await a(i,r),{name:t.name,path:r}},async remove(e){try{return await o(h(e)),!0}catch(e){if(p(e))return!1;throw e}}}}export{h as createNodeSavedPromptStore,m as resolvePromptsDirectory};
|
|
2
|
+
//# sourceMappingURL=promptStore.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptStore.mjs","names":[],"sources":["../../../src/adapters/node/promptStore.ts"],"sourcesContent":["import { mkdir, readdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport path from 'node:path';\nimport { buildPromptDocument, parsePromptDocument } from '../../services/savedPromptDocument.ts';\nimport type { SavedPrompt, SavedPromptStore, SavedPromptWrite } from '../../types/prompt.ts';\n\n/**\n * Saved prompts on disk, as Pi prompt templates.\n *\n * DESIGN PATTERNS:\n * - Writes into Pi's own global prompts directory, so `/<name>` resolves on the\n * next start with no resource contribution from this package.\n * - Private modes and temp-file-then-rename, matching how the rest of the\n * distribution writes user state under the agent directory.\n * - The environment and the home directory are injected, so tests get a real\n * directory without touching the developer's own prompts.\n *\n * AVOID:\n * - Inventing a parallel store. The template file is the only source of truth.\n */\n\nconst AGENT_DIR_ENV = 'PI_CODING_AGENT_DIR';\nconst DEFAULT_AGENT_DIR = path.join('.pi', 'agent');\nconst PROMPTS_DIR = 'prompts';\nconst TEMPLATE_SUFFIX = '.md';\nconst DIRECTORY_MODE = 0o700;\nconst FILE_MODE = 0o600;\nconst MISSING_FILE_CODE = 'ENOENT';\n/** A directory named like a template is not one, and reading it fails this way. */\nconst DIRECTORY_READ_CODE = 'EISDIR';\n\nexport interface NodeSavedPromptStoreOptions {\n env?: NodeJS.ProcessEnv;\n home?: string;\n}\n\nfunction errorCode(error: unknown): string | undefined {\n if (typeof error !== 'object' || error === null || !('code' in error)) return undefined;\n const { code } = error;\n return typeof code === 'string' ? code : undefined;\n}\n\nfunction isMissing(error: unknown): boolean {\n return errorCode(error) === MISSING_FILE_CODE;\n}\n\n/** `<agentDir>/prompts`, honouring the same override Pi itself reads. */\nexport function resolvePromptsDirectory(options: NodeSavedPromptStoreOptions = {}): string {\n const env = options.env ?? process.env;\n const configured = env[AGENT_DIR_ENV]?.trim();\n const agentDir = configured ? configured : path.join(options.home ?? homedir(), DEFAULT_AGENT_DIR);\n return path.join(agentDir, PROMPTS_DIR);\n}\n\nexport function createNodeSavedPromptStore(options: NodeSavedPromptStoreOptions = {}): SavedPromptStore {\n const directory = resolvePromptsDirectory(options);\n const filePath = (name: string): string => path.join(directory, `${name}${TEMPLATE_SUFFIX}`);\n\n return {\n async list(): Promise<readonly SavedPrompt[]> {\n let names: string[];\n try {\n names = await readdir(directory);\n } catch (error) {\n // No prompts directory yet is the normal first-run state, not a fault.\n if (isMissing(error)) return [];\n throw error;\n }\n\n const prompts: SavedPrompt[] = [];\n for (const entry of names.filter((name) => name.endsWith(TEMPLATE_SUFFIX)).sort()) {\n const name = entry.slice(0, -TEMPLATE_SUFFIX.length);\n try {\n prompts.push(parsePromptDocument(name, await readFile(path.join(directory, entry), 'utf8')));\n } catch (error) {\n // A template that vanished or turned into a directory between the\n // listing and the read is skipped; anything else is a real fault.\n if (!isMissing(error) && errorCode(error) !== DIRECTORY_READ_CODE) throw error;\n }\n }\n return prompts;\n },\n\n async has(name: string): Promise<boolean> {\n try {\n const entry = await stat(filePath(name));\n return entry.isFile();\n } catch (error) {\n if (isMissing(error)) return false;\n throw error;\n }\n },\n\n async save(prompt: SavedPrompt): Promise<SavedPromptWrite> {\n await mkdir(directory, { recursive: true, mode: DIRECTORY_MODE });\n const target = filePath(prompt.name);\n const temporary = `${target}.tmp`;\n await writeFile(temporary, buildPromptDocument(prompt), { encoding: 'utf8', mode: FILE_MODE });\n await rename(temporary, target);\n return { name: prompt.name, path: target };\n },\n\n async remove(name: string): Promise<boolean> {\n try {\n await rm(filePath(name));\n return true;\n } catch (error) {\n // Deleting a template that is already gone is the caller's answer, not a fault.\n if (isMissing(error)) return false;\n throw error;\n }\n },\n };\n}\n"],"mappings":"kRAqBA,MACM,EAAoB,EAAK,KAAK,MAAO,OAAO,EAclD,SAAS,EAAU,EAAoC,CACrD,GAAI,OAAO,GAAU,WAAY,GAAkB,EAAE,SAAU,GAAQ,OACvE,GAAM,CAAE,QAAS,EACjB,OAAO,OAAO,GAAS,SAAW,EAAO,IAAA,EAC3C,CAEA,SAAS,EAAU,EAAyB,CAC1C,OAAO,EAAU,CAAK,IAAM,QAC9B,CAGA,SAAgB,EAAwB,EAAuC,CAAC,EAAW,CAEzF,IACM,GAFM,EAAQ,KAAO,QAAQ,IAAA,CACZ,qBAAgB,KAAK,GACD,EAAK,KAAK,EAAQ,MAAQ,EAAQ,EAAG,CAAiB,EACjG,OAAO,EAAK,KAAK,EAAU,SAAW,CACxC,CAEA,SAAgB,EAA2B,EAAuC,CAAC,EAAqB,CACtG,IAAM,EAAY,EAAwB,CAAO,EAC3C,EAAY,GAAyB,EAAK,KAAK,EAAW,GAAG,MAAwB,EAE3F,MAAO,CACL,MAAM,MAAwC,CAC5C,IAAI,EACJ,GAAI,CACF,EAAQ,MAAM,EAAQ,CAAS,CACjC,OAAS,EAAO,CAEd,GAAI,EAAU,CAAK,EAAG,MAAO,CAAC,EAC9B,MAAM,CACR,CAEA,IAAM,EAAyB,CAAC,EAChC,IAAK,IAAM,KAAS,EAAM,OAAQ,GAAS,EAAK,SAAS,KAAe,CAAC,CAAC,CAAC,KAAK,EAAG,CACjF,IAAM,EAAO,EAAM,MAAM,EAAG,EAAuB,EACnD,GAAI,CACF,EAAQ,KAAK,EAAoB,EAAM,MAAM,EAAS,EAAK,KAAK,EAAW,CAAK,EAAG,MAAM,CAAC,CAAC,CAC7F,OAAS,EAAO,CAGd,GAAI,CAAC,EAAU,CAAK,GAAK,EAAU,CAAK,IAAM,SAAqB,MAAM,CAC3E,CACF,CACA,OAAO,CACT,EAEA,MAAM,IAAI,EAAgC,CACxC,GAAI,CAEF,OAAO,MADa,EAAK,EAAS,CAAI,CAAC,EAAA,CAC1B,OAAO,CACtB,OAAS,EAAO,CACd,GAAI,EAAU,CAAK,EAAG,MAAO,GAC7B,MAAM,CACR,CACF,EAEA,MAAM,KAAK,EAAgD,CACzD,MAAM,EAAM,EAAW,CAAE,UAAW,GAAM,KAAM,GAAe,CAAC,EAChE,IAAM,EAAS,EAAS,EAAO,IAAI,EAC7B,EAAY,GAAG,EAAO,MAG5B,OAFA,MAAM,EAAU,EAAW,EAAoB,CAAM,EAAG,CAAE,SAAU,OAAQ,KAAM,GAAU,CAAC,EAC7F,MAAM,EAAO,EAAW,CAAM,EACvB,CAAE,KAAM,EAAO,KAAM,KAAM,CAAO,CAC3C,EAEA,MAAM,OAAO,EAAgC,CAC3C,GAAI,CAEF,OADA,MAAM,EAAG,EAAS,CAAI,CAAC,EAChB,EACT,OAAS,EAAO,CAEd,GAAI,EAAU,CAAK,EAAG,MAAO,GAC7B,MAAM,CACR,CACF,CACF,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../../commands/promptsCommand.cjs"),t=require("../../commands/promptSaveCommand.cjs"),n=require("../../container/index.cjs"),r=require("./inputCapture.cjs"),i=require("./promptConstants.cjs"),a=require("./leader.cjs");let o=require("@agimon-ai/doompi-extension-contracts/cordis-host"),s=require("@agimon-ai/doompi-extension-contracts/help"),c=require("@agimon-ai/doompi-extension-contracts/ui-hub");function l(o,l,u=n.createPromptContainer()){o.inject([s.DOOM_HELP_SERVICE],e=>{let t=(0,s.requireDoomHelpService)(e).register({source:i.PACKAGE_SOURCE,moduleUrl:require("url").pathToFileURL(__filename).href,skills:[{name:`doompi-use-prompt`,description:`Use @agimon-ai/doompi-prompt: Staged recent prompts and saved prompt templates for DoomPi`}]});return()=>t.dispose()}),o.inject([c.DOOM_UI_HUB_SERVICE],e=>a.registerLeaderContribution((0,c.requireDoomUiHub)(e))),r.registerInputCapture(l,u.recent),e.registerPromptsCommand(l,u),t.registerPromptSaveCommand(l,u)}function u(e,t){l(e,t.pi,t.dependencies)}async function d(e,t=n.createPromptContainer()){let r=await(0,o.connectDoomCordisHost)(e,i.PACKAGE_SOURCE),a=r.root.plugin(u,{pi:e,dependencies:t});try{await a}catch(e){try{await a.dispose()}finally{await r.dispose()}throw e}let s;e.on(`session_shutdown`,()=>s??=(async()=>{try{await a.dispose()}finally{await r.dispose()}})())}exports.activatePromptExtension=d,exports.default=d,exports.installPromptRuntime=l;
|
|
2
|
+
//# sourceMappingURL=extension.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.cjs","names":["createPromptContainer","DOOM_HELP_SERVICE","requireDoomHelpService","PACKAGE_SOURCE","DOOM_UI_HUB_SERVICE","registerLeaderContribution","requireDoomUiHub","connectDoomCordisHost"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { registerPromptSaveCommand } from '../../commands/promptSaveCommand.ts';\nimport { registerPromptsCommand } from '../../commands/promptsCommand.ts';\nimport { createPromptContainer } from '../../container';\nimport type { PromptExtensionDependencies } from '../../types/prompt.ts';\nimport { registerInputCapture } from './inputCapture.ts';\nimport { registerLeaderContribution } from './leader.ts';\nimport { PACKAGE_SOURCE } from './promptConstants.ts';\n\n/**\n * Installs the package runtime inside its host-owned Cordis plugin fiber.\n *\n * Everything registered inside the effect is owned by the cordis fiber, so one\n * dispose on session shutdown unwinds it in reverse order. Yield a disposer for\n * each registration rather than tracking cleanup by hand; Pi can reload an\n * extension in-process, and a leaked listener duplicates work against stale\n * session state.\n */\nexport function installPromptRuntime(\n cordis: Context,\n pi: ExtensionAPI,\n dependencies: PromptExtensionDependencies = createPromptContainer(),\n): void {\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const contribution = requireDoomHelpService(helpContext).register({\n source: PACKAGE_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\n name: 'doompi-use-prompt',\n description: 'Use @agimon-ai/doompi-prompt: Staged recent prompts and saved prompt templates for DoomPi',\n },\n ],\n });\n return () => contribution.dispose();\n });\n\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => registerLeaderContribution(requireDoomUiHub(uiContext)));\n\n // Staging is fed by the host's input event rather than the editor, so pi-tui's\n // own arrow up/down history keeps working exactly as it does without this package.\n registerInputCapture(pi, dependencies.recent);\n\n registerPromptsCommand(pi, dependencies);\n registerPromptSaveCommand(pi, dependencies);\n}\n\ninterface PromptPluginConfig {\n readonly pi: ExtensionAPI;\n readonly dependencies: PromptExtensionDependencies;\n}\n\nfunction promptPlugin(cordis: Context, config: PromptPluginConfig): void {\n installPromptRuntime(cordis, config.pi, config.dependencies);\n}\n\n/** Connects the package's standard Pi entry to DoomPi's shared Cordis host. */\nexport async function activatePromptExtension(\n pi: ExtensionAPI,\n dependencies: PromptExtensionDependencies = createPromptContainer(),\n): Promise<void> {\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fiber = connection.root.plugin(promptPlugin, { pi, dependencies });\n\n try {\n await fiber;\n } catch (error) {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n throw error;\n }\n\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\nexport default activatePromptExtension;\n"],"mappings":"gaAsBA,SAAgB,EACd,EACA,EACA,EAA4CA,EAAAA,sBAAsB,EAC5D,CACN,EAAO,OAAO,CAACC,EAAAA,iBAAiB,EAAI,GAAgB,CAClD,IAAM,GAAA,EAAeC,EAAAA,uBAAAA,CAAuB,CAAW,CAAC,CAAC,SAAS,CAChE,OAAQC,EAAAA,eACR,UAAA,QAAA,KAAA,CAAA,CAAA,cAAA,UAAA,CAAA,CAAA,KACA,OAAQ,CACN,CACE,KAAM,oBACN,YAAa,2FACf,CACF,CACF,CAAC,EACD,UAAa,EAAa,QAAQ,CACpC,CAAC,EAED,EAAO,OAAO,CAACC,EAAAA,mBAAmB,EAAI,GAAcC,EAAAA,4BAAAA,EAA2BC,EAAAA,iBAAAA,CAAiB,CAAS,CAAC,CAAC,EAI3G,EAAA,qBAAqB,EAAI,EAAa,MAAM,EAE5C,EAAA,uBAAuB,EAAI,CAAY,EACvC,EAAA,0BAA0B,EAAI,CAAY,CAC5C,CAOA,SAAS,EAAa,EAAiB,EAAkC,CACvE,EAAqB,EAAQ,EAAO,GAAI,EAAO,YAAY,CAC7D,CAGA,eAAsB,EACpB,EACA,EAA4CN,EAAAA,sBAAsB,EACnD,CACf,IAAM,EAAa,MAAA,EAAMO,EAAAA,sBAAAA,CAAsB,EAAIJ,EAAAA,cAAc,EAC3D,EAAQ,EAAW,KAAK,OAAO,EAAc,CAAE,KAAI,cAAa,CAAC,EAEvE,GAAI,CACF,MAAM,CACR,OAAS,EAAO,CACd,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACA,MAAM,CACR,CAEA,IAAI,EACJ,EAAG,GACD,uBAEG,KAAc,SAAY,CACzB,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACF,EAAA,CAAG,CACP,CACF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PromptExtensionDependencies } from "../../types/prompt.cjs";
|
|
2
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
3
|
+
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
//#region src/adapters/pi/extension.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Installs the package runtime inside its host-owned Cordis plugin fiber.
|
|
7
|
+
*
|
|
8
|
+
* Everything registered inside the effect is owned by the cordis fiber, so one
|
|
9
|
+
* dispose on session shutdown unwinds it in reverse order. Yield a disposer for
|
|
10
|
+
* each registration rather than tracking cleanup by hand; Pi can reload an
|
|
11
|
+
* extension in-process, and a leaked listener duplicates work against stale
|
|
12
|
+
* session state.
|
|
13
|
+
*/
|
|
14
|
+
declare function installPromptRuntime(cordis: Context, pi: ExtensionAPI, dependencies?: PromptExtensionDependencies): void;
|
|
15
|
+
/** Connects the package's standard Pi entry to DoomPi's shared Cordis host. */
|
|
16
|
+
declare function activatePromptExtension(pi: ExtensionAPI, dependencies?: PromptExtensionDependencies): Promise<void>;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { activatePromptExtension, activatePromptExtension as default, installPromptRuntime };
|
|
19
|
+
//# sourceMappingURL=extension.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;;;;;;;;;;iBAsBgB,qBACd,QAAQ,SACR,IAAI,cACJ,eAAc;;iBAoCM,wBACpB,IAAI,cACJ,eAAc,8BACb"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PromptExtensionDependencies } from "../../types/prompt.mjs";
|
|
2
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
3
|
+
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
//#region src/adapters/pi/extension.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Installs the package runtime inside its host-owned Cordis plugin fiber.
|
|
7
|
+
*
|
|
8
|
+
* Everything registered inside the effect is owned by the cordis fiber, so one
|
|
9
|
+
* dispose on session shutdown unwinds it in reverse order. Yield a disposer for
|
|
10
|
+
* each registration rather than tracking cleanup by hand; Pi can reload an
|
|
11
|
+
* extension in-process, and a leaked listener duplicates work against stale
|
|
12
|
+
* session state.
|
|
13
|
+
*/
|
|
14
|
+
declare function installPromptRuntime(cordis: Context, pi: ExtensionAPI, dependencies?: PromptExtensionDependencies): void;
|
|
15
|
+
/** Connects the package's standard Pi entry to DoomPi's shared Cordis host. */
|
|
16
|
+
declare function activatePromptExtension(pi: ExtensionAPI, dependencies?: PromptExtensionDependencies): Promise<void>;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { activatePromptExtension, activatePromptExtension as default, installPromptRuntime };
|
|
19
|
+
//# sourceMappingURL=extension.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;;;;;;;;;;iBAsBgB,qBACd,QAAQ,SACR,IAAI,cACJ,eAAc;;iBAoCM,wBACpB,IAAI,cACJ,eAAc,8BACb"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{registerPromptsCommand as e}from"../../commands/promptsCommand.mjs";import{registerPromptSaveCommand as t}from"../../commands/promptSaveCommand.mjs";import{createPromptContainer as n}from"../../container/index.mjs";import{registerInputCapture as r}from"./inputCapture.mjs";import{PACKAGE_SOURCE as i}from"./promptConstants.mjs";import{registerLeaderContribution as a}from"./leader.mjs";import{connectDoomCordisHost as o}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{DOOM_HELP_SERVICE as s,requireDoomHelpService as c}from"@agimon-ai/doompi-extension-contracts/help";import{DOOM_UI_HUB_SERVICE as l,requireDoomUiHub as u}from"@agimon-ai/doompi-extension-contracts/ui-hub";function d(o,d,f=n()){o.inject([s],e=>{let t=c(e).register({source:i,moduleUrl:import.meta.url,skills:[{name:`doompi-use-prompt`,description:`Use @agimon-ai/doompi-prompt: Staged recent prompts and saved prompt templates for DoomPi`}]});return()=>t.dispose()}),o.inject([l],e=>a(u(e))),r(d,f.recent),e(d,f),t(d,f)}function f(e,t){d(e,t.pi,t.dependencies)}async function p(e,t=n()){let r=await o(e,i),a=r.root.plugin(f,{pi:e,dependencies:t});try{await a}catch(e){try{await a.dispose()}finally{await r.dispose()}throw e}let s;e.on(`session_shutdown`,()=>s??=(async()=>{try{await a.dispose()}finally{await r.dispose()}})())}export{p as activatePromptExtension,p as default,d as installPromptRuntime};
|
|
2
|
+
//# sourceMappingURL=extension.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService } from '@agimon-ai/doompi-extension-contracts/help';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { registerPromptSaveCommand } from '../../commands/promptSaveCommand.ts';\nimport { registerPromptsCommand } from '../../commands/promptsCommand.ts';\nimport { createPromptContainer } from '../../container';\nimport type { PromptExtensionDependencies } from '../../types/prompt.ts';\nimport { registerInputCapture } from './inputCapture.ts';\nimport { registerLeaderContribution } from './leader.ts';\nimport { PACKAGE_SOURCE } from './promptConstants.ts';\n\n/**\n * Installs the package runtime inside its host-owned Cordis plugin fiber.\n *\n * Everything registered inside the effect is owned by the cordis fiber, so one\n * dispose on session shutdown unwinds it in reverse order. Yield a disposer for\n * each registration rather than tracking cleanup by hand; Pi can reload an\n * extension in-process, and a leaked listener duplicates work against stale\n * session state.\n */\nexport function installPromptRuntime(\n cordis: Context,\n pi: ExtensionAPI,\n dependencies: PromptExtensionDependencies = createPromptContainer(),\n): void {\n cordis.inject([DOOM_HELP_SERVICE], (helpContext) => {\n const contribution = requireDoomHelpService(helpContext).register({\n source: PACKAGE_SOURCE,\n moduleUrl: import.meta.url,\n skills: [\n {\n name: 'doompi-use-prompt',\n description: 'Use @agimon-ai/doompi-prompt: Staged recent prompts and saved prompt templates for DoomPi',\n },\n ],\n });\n return () => contribution.dispose();\n });\n\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => registerLeaderContribution(requireDoomUiHub(uiContext)));\n\n // Staging is fed by the host's input event rather than the editor, so pi-tui's\n // own arrow up/down history keeps working exactly as it does without this package.\n registerInputCapture(pi, dependencies.recent);\n\n registerPromptsCommand(pi, dependencies);\n registerPromptSaveCommand(pi, dependencies);\n}\n\ninterface PromptPluginConfig {\n readonly pi: ExtensionAPI;\n readonly dependencies: PromptExtensionDependencies;\n}\n\nfunction promptPlugin(cordis: Context, config: PromptPluginConfig): void {\n installPromptRuntime(cordis, config.pi, config.dependencies);\n}\n\n/** Connects the package's standard Pi entry to DoomPi's shared Cordis host. */\nexport async function activatePromptExtension(\n pi: ExtensionAPI,\n dependencies: PromptExtensionDependencies = createPromptContainer(),\n): Promise<void> {\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fiber = connection.root.plugin(promptPlugin, { pi, dependencies });\n\n try {\n await fiber;\n } catch (error) {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n throw error;\n }\n\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\nexport default activatePromptExtension;\n"],"mappings":"urBAsBA,SAAgB,EACd,EACA,EACA,EAA4C,EAAsB,EAC5D,CACN,EAAO,OAAO,CAAC,CAAiB,EAAI,GAAgB,CAClD,IAAM,EAAe,EAAuB,CAAW,CAAC,CAAC,SAAS,CAChE,OAAQ,EACR,UAAW,YAAY,IACvB,OAAQ,CACN,CACE,KAAM,oBACN,YAAa,2FACf,CACF,CACF,CAAC,EACD,UAAa,EAAa,QAAQ,CACpC,CAAC,EAED,EAAO,OAAO,CAAC,CAAmB,EAAI,GAAc,EAA2B,EAAiB,CAAS,CAAC,CAAC,EAI3G,EAAqB,EAAI,EAAa,MAAM,EAE5C,EAAuB,EAAI,CAAY,EACvC,EAA0B,EAAI,CAAY,CAC5C,CAOA,SAAS,EAAa,EAAiB,EAAkC,CACvE,EAAqB,EAAQ,EAAO,GAAI,EAAO,YAAY,CAC7D,CAGA,eAAsB,EACpB,EACA,EAA4C,EAAsB,EACnD,CACf,IAAM,EAAa,MAAM,EAAsB,EAAI,CAAc,EAC3D,EAAQ,EAAW,KAAK,OAAO,EAAc,CAAE,KAAI,cAAa,CAAC,EAEvE,GAAI,CACF,MAAM,CACR,OAAS,EAAO,CACd,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACA,MAAM,CACR,CAEA,IAAI,EACJ,EAAG,GACD,uBAEG,KAAc,SAAY,CACzB,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACF,EAAA,CAAG,CACP,CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inputCapture.cjs","names":[],"sources":["../../../src/adapters/pi/inputCapture.ts"],"sourcesContent":["import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { RecentPrompts } from '../../types/prompt.ts';\n\n/**\n * Staging what the user submits.\n *\n * DESIGN PATTERNS:\n * - Listens on the host's input event, which carries the raw text before skill\n * and template expansion. This keeps the editor component untouched, so\n * arrow up/down behaviour is exactly Pi's and cannot regress here.\n * - Stages interactive and RPC input, which are the two ways a person submits a\n * prompt. Text an extension injected is that extension's business, not\n * something the user asked to reuse.\n *\n * AVOID:\n * - Returning anything but continue. This handler observes, it never rewrites.\n */\n\nconst EXTENSION_SOURCE = 'extension';\nconst CONTINUE = { action: 'continue' } as const;\n\nexport function registerInputCapture(pi: ExtensionAPI, recent: RecentPrompts): void {\n pi.on('input', (event) => {\n if (event.source !== EXTENSION_SOURCE) recent.push(event.text);\n return CONTINUE;\n });\n}\n"],"mappings":"AAkBA,MACM,EAAW,CAAE,OAAQ,UAAW,EAEtC,SAAgB,EAAqB,EAAkB,EAA6B,CAClF,EAAG,GAAG,QAAU,IACV,EAAM,SAAW,aAAkB,EAAO,KAAK,EAAM,IAAI,EACtD,EACR,CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inputCapture.mjs","names":[],"sources":["../../../src/adapters/pi/inputCapture.ts"],"sourcesContent":["import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { RecentPrompts } from '../../types/prompt.ts';\n\n/**\n * Staging what the user submits.\n *\n * DESIGN PATTERNS:\n * - Listens on the host's input event, which carries the raw text before skill\n * and template expansion. This keeps the editor component untouched, so\n * arrow up/down behaviour is exactly Pi's and cannot regress here.\n * - Stages interactive and RPC input, which are the two ways a person submits a\n * prompt. Text an extension injected is that extension's business, not\n * something the user asked to reuse.\n *\n * AVOID:\n * - Returning anything but continue. This handler observes, it never rewrites.\n */\n\nconst EXTENSION_SOURCE = 'extension';\nconst CONTINUE = { action: 'continue' } as const;\n\nexport function registerInputCapture(pi: ExtensionAPI, recent: RecentPrompts): void {\n pi.on('input', (event) => {\n if (event.source !== EXTENSION_SOURCE) recent.push(event.text);\n return CONTINUE;\n });\n}\n"],"mappings":"AAkBA,MACM,EAAW,CAAE,OAAQ,UAAW,EAEtC,SAAgB,EAAqB,EAAkB,EAA6B,CAClF,EAAG,GAAG,QAAU,IACV,EAAM,SAAW,aAAkB,EAAO,KAAK,EAAM,IAAI,EACtD,EACR,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../../commands/promptsCommand.cjs"),t=require("./promptConstants.cjs");function n(n){let r=n.registerLeader({source:t.PACKAGE_SOURCE,bindings:[{id:`${t.LEADER_BINDING_PREFIX}.open`,path:[t.LEADER_GROUP,{key:`p`,label:t.LEADER_LABEL,detail:t.LEADER_DETAIL}],command:{name:e.COMMAND_NAME}}]});return()=>r.dispose()}exports.registerLeaderContribution=n;
|
|
2
|
+
//# sourceMappingURL=leader.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leader.cjs","names":["PACKAGE_SOURCE","LEADER_BINDING_PREFIX","LEADER_GROUP","LEADER_LABEL","LEADER_DETAIL","COMMAND_NAME"],"sources":["../../../src/adapters/pi/leader.ts"],"sourcesContent":["import type { DoomUiHubService } from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport { COMMAND_NAME } from '../../commands/promptsCommand.ts';\nimport {\n LEADER_BINDING_PREFIX,\n LEADER_DETAIL,\n LEADER_GROUP,\n LEADER_KEY,\n LEADER_LABEL,\n PACKAGE_SOURCE,\n} from './promptConstants.ts';\n\n/** Puts the picker on the leader map, and takes it back off on dispose. */\nexport function registerLeaderContribution(hub: DoomUiHubService): () => void {\n const contribution = hub.registerLeader({\n source: PACKAGE_SOURCE,\n bindings: [\n {\n id: `${LEADER_BINDING_PREFIX}.open`,\n path: [LEADER_GROUP, { key: LEADER_KEY, label: LEADER_LABEL, detail: LEADER_DETAIL }],\n command: { name: COMMAND_NAME },\n },\n ],\n });\n return () => contribution.dispose();\n}\n"],"mappings":"wFAYA,SAAgB,EAA2B,EAAmC,CAC5E,IAAM,EAAe,EAAI,eAAe,CACtC,OAAQA,EAAAA,eACR,SAAU,CACR,CACE,GAAI,GAAGC,EAAAA,sBAAsB,OAC7B,KAAM,CAACC,EAAAA,aAAc,CAAE,IAAA,IAAiB,MAAOC,EAAAA,aAAc,OAAQC,EAAAA,aAAc,CAAC,EACpF,QAAS,CAAE,KAAMC,EAAAA,YAAa,CAChC,CACF,CACF,CAAC,EACD,UAAa,EAAa,QAAQ,CACpC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{COMMAND_NAME as e}from"../../commands/promptsCommand.mjs";import{LEADER_BINDING_PREFIX as t,LEADER_DETAIL as n,LEADER_GROUP as r,LEADER_LABEL as i,PACKAGE_SOURCE as a}from"./promptConstants.mjs";function o(o){let s=o.registerLeader({source:a,bindings:[{id:`${t}.open`,path:[r,{key:`p`,label:i,detail:n}],command:{name:e}}]});return()=>s.dispose()}export{o as registerLeaderContribution};
|
|
2
|
+
//# sourceMappingURL=leader.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leader.mjs","names":[],"sources":["../../../src/adapters/pi/leader.ts"],"sourcesContent":["import type { DoomUiHubService } from '@agimon-ai/doompi-extension-contracts/ui-hub';\nimport { COMMAND_NAME } from '../../commands/promptsCommand.ts';\nimport {\n LEADER_BINDING_PREFIX,\n LEADER_DETAIL,\n LEADER_GROUP,\n LEADER_KEY,\n LEADER_LABEL,\n PACKAGE_SOURCE,\n} from './promptConstants.ts';\n\n/** Puts the picker on the leader map, and takes it back off on dispose. */\nexport function registerLeaderContribution(hub: DoomUiHubService): () => void {\n const contribution = hub.registerLeader({\n source: PACKAGE_SOURCE,\n bindings: [\n {\n id: `${LEADER_BINDING_PREFIX}.open`,\n path: [LEADER_GROUP, { key: LEADER_KEY, label: LEADER_LABEL, detail: LEADER_DETAIL }],\n command: { name: COMMAND_NAME },\n },\n ],\n });\n return () => contribution.dispose();\n}\n"],"mappings":"0MAYA,SAAgB,EAA2B,EAAmC,CAC5E,IAAM,EAAe,EAAI,eAAe,CACtC,OAAQ,EACR,SAAU,CACR,CACE,GAAI,GAAG,EAAsB,OAC7B,KAAM,CAAC,EAAc,CAAE,IAAA,IAAiB,MAAO,EAAc,OAAQ,CAAc,CAAC,EACpF,QAAS,CAAE,KAAM,CAAa,CAChC,CACF,CACF,CAAC,EACD,UAAa,EAAa,QAAQ,CACpC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e={key:`e`,label:`extension`,detail:`tools, skills and config`,order:50};exports.LEADER_BINDING_PREFIX=`doom-prompt`,exports.LEADER_DETAIL=`staged and saved prompts`,exports.LEADER_GROUP=e,exports.LEADER_LABEL=`prompts`,exports.PACKAGE_SOURCE=`@agimon-ai/doompi-prompt`;
|
|
2
|
+
//# sourceMappingURL=promptConstants.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptConstants.cjs","names":[],"sources":["../../../src/adapters/pi/promptConstants.ts"],"sourcesContent":["/** Values shared by this package's Pi-facing adapters. */\n\nexport const PACKAGE_SOURCE = '@agimon-ai/doompi-prompt';\nexport const LEADER_BINDING_PREFIX = 'doom-prompt';\n/**\n * The shared extension group other packages already contribute to. Key, label\n * and order have to match theirs or the leader panel shows two `e` groups.\n */\nexport const LEADER_GROUP = { key: 'e', label: 'extension', detail: 'tools, skills and config', order: 50 } as const;\nexport const LEADER_KEY = 'p';\nexport const LEADER_LABEL = 'prompts';\nexport const LEADER_DETAIL = 'staged and saved prompts';\n"],"mappings":"AAEA,MAMa,EAAe,CAAE,IAAK,IAAK,MAAO,YAAa,OAAQ,2BAA4B,MAAO,EAAG"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=`@agimon-ai/doompi-prompt`,t=`doom-prompt`,n={key:`e`,label:`extension`,detail:`tools, skills and config`,order:50},r=`prompts`,i=`staged and saved prompts`;export{t as LEADER_BINDING_PREFIX,i as LEADER_DETAIL,n as LEADER_GROUP,r as LEADER_LABEL,e as PACKAGE_SOURCE};
|
|
2
|
+
//# sourceMappingURL=promptConstants.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptConstants.mjs","names":[],"sources":["../../../src/adapters/pi/promptConstants.ts"],"sourcesContent":["/** Values shared by this package's Pi-facing adapters. */\n\nexport const PACKAGE_SOURCE = '@agimon-ai/doompi-prompt';\nexport const LEADER_BINDING_PREFIX = 'doom-prompt';\n/**\n * The shared extension group other packages already contribute to. Key, label\n * and order have to match theirs or the leader panel shows two `e` groups.\n */\nexport const LEADER_GROUP = { key: 'e', label: 'extension', detail: 'tools, skills and config', order: 50 } as const;\nexport const LEADER_KEY = 'p';\nexport const LEADER_LABEL = 'prompts';\nexport const LEADER_DETAIL = 'staged and saved prompts';\n"],"mappings":"AAEA,MAAa,EAAiB,2BACjB,EAAwB,cAKxB,EAAe,CAAE,IAAK,IAAK,MAAO,YAAa,OAAQ,2BAA4B,MAAO,EAAG,EAE7F,EAAe,UACf,EAAgB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../services/savedPromptDocument.cjs"),t=require("./promptsCommand.cjs"),n=`prompt-save`,r=`Save the current prompt for reuse in later sessions`,i=`warning`;function a(a,o){a.registerCommand(n,{description:r,handler:async(r,a)=>{if(!a.hasUI)return;let s=r.trim();if(!s){a.ui.notify(`Usage: /${n} <name>`,i);return}if(!e.isValidPromptName(s)){a.ui.notify(`"${s}" is not a usable prompt name. Use ${e.PROMPT_NAME_RULE}.`,i);return}let c=a.ui.getEditorText().trim()||o.recent.list()[0]||``;if(!c){a.ui.notify(`Nothing to save: the editor is empty and no prompt is staged.`,i);return}try{if(await o.store.has(s)&&!await a.ui.confirm(`Replace saved prompt`,`A prompt named "${s}" already exists.`))return;let n=await o.store.save({name:s,description:e.describePrompt(c),text:c}),r=e.hasArgumentTokens(c)?` It contains $ tokens, which Pi substitutes as template arguments when you run the command.`:``;a.ui.notify(`Saved ${n.path}. Use it from /${t.COMMAND_NAME}, or as /${s} after the next start.${r}`,`info`)}catch(e){let t=e instanceof Error?e.message:String(e);a.ui.notify(`Could not save "${s}": ${t}`,i)}}})}exports.COMMAND_DESCRIPTION=r,exports.COMMAND_NAME=n,exports.registerPromptSaveCommand=a;
|
|
2
|
+
//# sourceMappingURL=promptSaveCommand.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptSaveCommand.cjs","names":["isValidPromptName","PROMPT_NAME_RULE","describePrompt","hasArgumentTokens","PROMPTS_COMMAND_NAME"],"sources":["../../src/commands/promptSaveCommand.ts"],"sourcesContent":["import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport {\n describePrompt,\n hasArgumentTokens,\n isValidPromptName,\n PROMPT_NAME_RULE,\n} from '../services/savedPromptDocument.ts';\nimport { COMMAND_NAME as PROMPTS_COMMAND_NAME } from './promptsCommand.ts';\nimport type { PromptExtensionDependencies } from '../types/prompt.ts';\n\n/**\n * `/prompt-save <name>`: keep the current prompt for later sessions.\n *\n * DESIGN PATTERNS:\n * - Saves what the user is looking at: the editor draft, or the newest staged\n * prompt when the editor is empty.\n * - Writes a plain Pi prompt template, so the saved prompt becomes `/<name>`\n * on the next start without this package registering a command for it.\n *\n * AVOID:\n * - Overwriting an existing template without asking.\n */\n\nexport const COMMAND_NAME = 'prompt-save';\nexport const COMMAND_DESCRIPTION = 'Save the current prompt for reuse in later sessions';\n\nconst WARNING = 'warning';\nconst INFO = 'info';\n\nexport function registerPromptSaveCommand(\n pi: Pick<ExtensionAPI, 'registerCommand'>,\n deps: PromptExtensionDependencies,\n): void {\n pi.registerCommand(COMMAND_NAME, {\n description: COMMAND_DESCRIPTION,\n handler: async (args, ctx) => {\n if (!ctx.hasUI) return;\n\n const name = args.trim();\n if (!name) {\n ctx.ui.notify(`Usage: /${COMMAND_NAME} <name>`, WARNING);\n return;\n }\n if (!isValidPromptName(name)) {\n ctx.ui.notify(`\"${name}\" is not a usable prompt name. Use ${PROMPT_NAME_RULE}.`, WARNING);\n return;\n }\n\n const draft = ctx.ui.getEditorText().trim();\n const text = draft || deps.recent.list()[0] || '';\n if (!text) {\n ctx.ui.notify('Nothing to save: the editor is empty and no prompt is staged.', WARNING);\n return;\n }\n\n try {\n if (await deps.store.has(name)) {\n const replace = await ctx.ui.confirm('Replace saved prompt', `A prompt named \"${name}\" already exists.`);\n if (!replace) return;\n }\n\n const written = await deps.store.save({ name, description: describePrompt(text), text });\n const warning = hasArgumentTokens(text)\n ? ' It contains $ tokens, which Pi substitutes as template arguments when you run the command.'\n : '';\n ctx.ui.notify(\n `Saved ${written.path}. Use it from /${PROMPTS_COMMAND_NAME}, or as /${name} after the next start.${warning}`,\n INFO,\n );\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n ctx.ui.notify(`Could not save \"${name}\": ${reason}`, WARNING);\n }\n },\n });\n}\n"],"mappings":"yFAuBa,EAAe,cACf,EAAsB,sDAE7B,EAAU,UAGhB,SAAgB,EACd,EACA,EACM,CACN,EAAG,gBAAgB,EAAc,CAC/B,YAAa,EACb,QAAS,MAAO,EAAM,IAAQ,CAC5B,GAAI,CAAC,EAAI,MAAO,OAEhB,IAAM,EAAO,EAAK,KAAK,EACvB,GAAI,CAAC,EAAM,CACT,EAAI,GAAG,OAAO,WAAW,EAAa,SAAU,CAAO,EACvD,MACF,CACA,GAAI,CAACA,EAAAA,kBAAkB,CAAI,EAAG,CAC5B,EAAI,GAAG,OAAO,IAAI,EAAK,qCAAqCC,EAAAA,iBAAiB,GAAI,CAAO,EACxF,MACF,CAGA,IAAM,EADQ,EAAI,GAAG,cAAc,CAAC,CAAC,KACpB,GAAK,EAAK,OAAO,KAAK,CAAC,CAAC,IAAM,GAC/C,GAAI,CAAC,EAAM,CACT,EAAI,GAAG,OAAO,gEAAiE,CAAO,EACtF,MACF,CAEA,GAAI,CACF,GAAI,MAAM,EAAK,MAAM,IAAI,CAAI,GAEvB,CAAC,MADiB,EAAI,GAAG,QAAQ,uBAAwB,mBAAmB,EAAK,kBAAkB,EACzF,OAGhB,IAAM,EAAU,MAAM,EAAK,MAAM,KAAK,CAAE,OAAM,YAAaC,EAAAA,eAAe,CAAI,EAAG,MAAK,CAAC,EACjF,EAAUC,EAAAA,kBAAkB,CAAI,EAClC,8FACA,GACJ,EAAI,GAAG,OACL,SAAS,EAAQ,KAAK,iBAAiBC,EAAAA,aAAqB,WAAW,EAAK,wBAAwB,IACpG,MACF,CACF,OAAS,EAAO,CACd,IAAM,EAAS,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EACpE,EAAI,GAAG,OAAO,mBAAmB,EAAK,KAAK,IAAU,CAAO,CAC9D,CACF,CACF,CAAC,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{PROMPT_NAME_RULE as e,describePrompt as t,hasArgumentTokens as n,isValidPromptName as r}from"../services/savedPromptDocument.mjs";import{COMMAND_NAME as i}from"./promptsCommand.mjs";const a=`prompt-save`,o=`Save the current prompt for reuse in later sessions`,s=`warning`;function c(c,l){c.registerCommand(a,{description:o,handler:async(o,c)=>{if(!c.hasUI)return;let u=o.trim();if(!u){c.ui.notify(`Usage: /${a} <name>`,s);return}if(!r(u)){c.ui.notify(`"${u}" is not a usable prompt name. Use ${e}.`,s);return}let d=c.ui.getEditorText().trim()||l.recent.list()[0]||``;if(!d){c.ui.notify(`Nothing to save: the editor is empty and no prompt is staged.`,s);return}try{if(await l.store.has(u)&&!await c.ui.confirm(`Replace saved prompt`,`A prompt named "${u}" already exists.`))return;let e=await l.store.save({name:u,description:t(d),text:d}),r=n(d)?` It contains $ tokens, which Pi substitutes as template arguments when you run the command.`:``;c.ui.notify(`Saved ${e.path}. Use it from /${i}, or as /${u} after the next start.${r}`,`info`)}catch(e){let t=e instanceof Error?e.message:String(e);c.ui.notify(`Could not save "${u}": ${t}`,s)}}})}export{o as COMMAND_DESCRIPTION,a as COMMAND_NAME,c as registerPromptSaveCommand};
|
|
2
|
+
//# sourceMappingURL=promptSaveCommand.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptSaveCommand.mjs","names":["PROMPTS_COMMAND_NAME"],"sources":["../../src/commands/promptSaveCommand.ts"],"sourcesContent":["import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport {\n describePrompt,\n hasArgumentTokens,\n isValidPromptName,\n PROMPT_NAME_RULE,\n} from '../services/savedPromptDocument.ts';\nimport { COMMAND_NAME as PROMPTS_COMMAND_NAME } from './promptsCommand.ts';\nimport type { PromptExtensionDependencies } from '../types/prompt.ts';\n\n/**\n * `/prompt-save <name>`: keep the current prompt for later sessions.\n *\n * DESIGN PATTERNS:\n * - Saves what the user is looking at: the editor draft, or the newest staged\n * prompt when the editor is empty.\n * - Writes a plain Pi prompt template, so the saved prompt becomes `/<name>`\n * on the next start without this package registering a command for it.\n *\n * AVOID:\n * - Overwriting an existing template without asking.\n */\n\nexport const COMMAND_NAME = 'prompt-save';\nexport const COMMAND_DESCRIPTION = 'Save the current prompt for reuse in later sessions';\n\nconst WARNING = 'warning';\nconst INFO = 'info';\n\nexport function registerPromptSaveCommand(\n pi: Pick<ExtensionAPI, 'registerCommand'>,\n deps: PromptExtensionDependencies,\n): void {\n pi.registerCommand(COMMAND_NAME, {\n description: COMMAND_DESCRIPTION,\n handler: async (args, ctx) => {\n if (!ctx.hasUI) return;\n\n const name = args.trim();\n if (!name) {\n ctx.ui.notify(`Usage: /${COMMAND_NAME} <name>`, WARNING);\n return;\n }\n if (!isValidPromptName(name)) {\n ctx.ui.notify(`\"${name}\" is not a usable prompt name. Use ${PROMPT_NAME_RULE}.`, WARNING);\n return;\n }\n\n const draft = ctx.ui.getEditorText().trim();\n const text = draft || deps.recent.list()[0] || '';\n if (!text) {\n ctx.ui.notify('Nothing to save: the editor is empty and no prompt is staged.', WARNING);\n return;\n }\n\n try {\n if (await deps.store.has(name)) {\n const replace = await ctx.ui.confirm('Replace saved prompt', `A prompt named \"${name}\" already exists.`);\n if (!replace) return;\n }\n\n const written = await deps.store.save({ name, description: describePrompt(text), text });\n const warning = hasArgumentTokens(text)\n ? ' It contains $ tokens, which Pi substitutes as template arguments when you run the command.'\n : '';\n ctx.ui.notify(\n `Saved ${written.path}. Use it from /${PROMPTS_COMMAND_NAME}, or as /${name} after the next start.${warning}`,\n INFO,\n );\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n ctx.ui.notify(`Could not save \"${name}\": ${reason}`, WARNING);\n }\n },\n });\n}\n"],"mappings":"6LAuBA,MAAa,EAAe,cACf,EAAsB,sDAE7B,EAAU,UAGhB,SAAgB,EACd,EACA,EACM,CACN,EAAG,gBAAgB,EAAc,CAC/B,YAAa,EACb,QAAS,MAAO,EAAM,IAAQ,CAC5B,GAAI,CAAC,EAAI,MAAO,OAEhB,IAAM,EAAO,EAAK,KAAK,EACvB,GAAI,CAAC,EAAM,CACT,EAAI,GAAG,OAAO,WAAW,EAAa,SAAU,CAAO,EACvD,MACF,CACA,GAAI,CAAC,EAAkB,CAAI,EAAG,CAC5B,EAAI,GAAG,OAAO,IAAI,EAAK,qCAAqC,EAAiB,GAAI,CAAO,EACxF,MACF,CAGA,IAAM,EADQ,EAAI,GAAG,cAAc,CAAC,CAAC,KACpB,GAAK,EAAK,OAAO,KAAK,CAAC,CAAC,IAAM,GAC/C,GAAI,CAAC,EAAM,CACT,EAAI,GAAG,OAAO,gEAAiE,CAAO,EACtF,MACF,CAEA,GAAI,CACF,GAAI,MAAM,EAAK,MAAM,IAAI,CAAI,GAEvB,CAAC,MADiB,EAAI,GAAG,QAAQ,uBAAwB,mBAAmB,EAAK,kBAAkB,EACzF,OAGhB,IAAM,EAAU,MAAM,EAAK,MAAM,KAAK,CAAE,OAAM,YAAa,EAAe,CAAI,EAAG,MAAK,CAAC,EACjF,EAAU,EAAkB,CAAI,EAClC,8FACA,GACJ,EAAI,GAAG,OACL,SAAS,EAAQ,KAAK,iBAAiBA,EAAqB,WAAW,EAAK,wBAAwB,IACpG,MACF,CACF,OAAS,EAAO,CACd,IAAM,EAAS,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EACpE,EAAI,GAAG,OAAO,mBAAmB,EAAK,KAAK,IAAU,CAAO,CAC9D,CACF,CACF,CAAC,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../services/promptItems.cjs"),t=`prompts`,n=`Browse staged and saved prompts`,r=`Prompts`;function i(){let e;return()=>e??=import(`@agimon-ai/doompi-ui/components/matrixPicker`)}async function a(e,t,n){if(e.mode!==`tui`){let n=t.map(e=>e.label),i=await e.ui.select(r,n),a=i===void 0?-1:n.indexOf(i);return a===-1?void 0:t[a]?.value}let{MatrixPickerComponent:i}=await n();return(await e.ui.custom((e,n,a,o)=>new i({title:r,items:[...t],selected:[],multi:!1},n,a,o)))?.[0]}function o(r,o){let s=i();r.registerCommand(t,{description:n,handler:async(t,n)=>{if(!n.hasUI)return;let r=o.recent.list(),i;try{i=await o.store.list()}catch(e){let t=e instanceof Error?e.message:String(e);n.ui.notify(`Could not read saved prompts: ${t}`,`warning`);return}let c=e.promptItems(r,i);if(c.length===0){n.ui.notify(`No staged or saved prompts yet. Submit a prompt, or save one with /prompt-save.`,`info`);return}let l=await a(n,c,s);if(!l)return;let u=e.resolvePromptSelection(l,r,i);u&&n.ui.setEditorText(e.stagedEditorText(n.ui.getEditorText(),u))}})}exports.COMMAND_DESCRIPTION=n,exports.COMMAND_NAME=t,exports.registerPromptsCommand=o;
|
|
2
|
+
//# sourceMappingURL=promptsCommand.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptsCommand.cjs","names":["promptItems","resolvePromptSelection","stagedEditorText"],"sources":["../../src/commands/promptsCommand.ts"],"sourcesContent":["import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport type { SelectItem } from '@earendil-works/pi-tui';\nimport { promptItems, resolvePromptSelection, stagedEditorText } from '../services/promptItems.ts';\nimport type { PromptExtensionDependencies } from '../types/prompt.ts';\n\n/**\n * `/prompts`: browse staged and saved prompts, and stage one in the editor.\n *\n * DESIGN PATTERNS:\n * - Reads both sources at open time, so a prompt saved earlier in this session\n * is listed even though Pi only rediscovers templates on the next start.\n * - The shared picker component loads on first use: every session registers this\n * command and almost none of them open it.\n * - Stages text through the host's editor API instead of sending it, because\n * reuse usually means editing before submitting.\n *\n * AVOID:\n * - Replacing a draft the user is in the middle of writing.\n */\n\nexport const COMMAND_NAME = 'prompts';\nexport const COMMAND_DESCRIPTION = 'Browse staged and saved prompts';\n\nconst PICKER_TITLE = 'Prompts';\nconst TUI_MODE = 'tui';\nconst WARNING = 'warning';\nconst INFO = 'info';\n\ntype PickerModule = typeof import('@agimon-ai/doompi-ui/components/matrixPicker');\n\nfunction lazyPicker(): () => Promise<PickerModule> {\n let picker: Promise<PickerModule> | undefined;\n return () => (picker ??= import('@agimon-ai/doompi-ui/components/matrixPicker'));\n}\n\n/** Opens the picker and resolves the chosen row's value, or undefined. */\nasync function pickPrompt(\n ctx: ExtensionContext,\n items: readonly SelectItem[],\n loadPicker: () => Promise<PickerModule>,\n): Promise<string | undefined> {\n if (ctx.mode !== TUI_MODE) {\n // The cockpit and the RPC runtime have no component to render into.\n const labels = items.map((item) => item.label);\n const chosen = await ctx.ui.select(PICKER_TITLE, labels);\n const index = chosen === undefined ? -1 : labels.indexOf(chosen);\n return index === -1 ? undefined : items[index]?.value;\n }\n\n const { MatrixPickerComponent } = await loadPicker();\n const chosen = await ctx.ui.custom<string[] | undefined>(\n (_tui, theme, keybindings, done) =>\n new MatrixPickerComponent(\n { title: PICKER_TITLE, items: [...items], selected: [], multi: false },\n theme,\n keybindings,\n done,\n ),\n );\n return chosen?.[0];\n}\n\nexport function registerPromptsCommand(\n pi: Pick<ExtensionAPI, 'registerCommand'>,\n deps: PromptExtensionDependencies,\n): void {\n const loadPicker = lazyPicker();\n\n pi.registerCommand(COMMAND_NAME, {\n description: COMMAND_DESCRIPTION,\n handler: async (_args, ctx) => {\n if (!ctx.hasUI) return;\n\n const recent = deps.recent.list();\n let saved: Awaited<ReturnType<typeof deps.store.list>>;\n try {\n saved = await deps.store.list();\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n ctx.ui.notify(`Could not read saved prompts: ${reason}`, WARNING);\n return;\n }\n\n const items = promptItems(recent, saved);\n if (items.length === 0) {\n ctx.ui.notify('No staged or saved prompts yet. Submit a prompt, or save one with /prompt-save.', INFO);\n return;\n }\n\n const value = await pickPrompt(ctx, items, loadPicker);\n if (!value) return;\n\n const text = resolvePromptSelection(value, recent, saved);\n if (!text) return;\n ctx.ui.setEditorText(stagedEditorText(ctx.ui.getEditorText(), text));\n },\n });\n}\n"],"mappings":"+CAoBa,EAAe,UACf,EAAsB,kCAE7B,EAAe,UAOrB,SAAS,GAA0C,CACjD,IAAI,EACJ,UAAc,IAAW,OAAO,+CAClC,CAGA,eAAe,EACb,EACA,EACA,EAC6B,CAC7B,GAAI,EAAI,OAAS,MAAU,CAEzB,IAAM,EAAS,EAAM,IAAK,GAAS,EAAK,KAAK,EACvC,EAAS,MAAM,EAAI,GAAG,OAAO,EAAc,CAAM,EACjD,EAAQ,IAAW,IAAA,GAAY,GAAK,EAAO,QAAQ,CAAM,EAC/D,OAAO,IAAU,GAAK,IAAA,GAAY,EAAM,EAAM,EAAE,KAClD,CAEA,GAAM,CAAE,yBAA0B,MAAM,EAAW,EAUnD,OAAO,MATc,EAAI,GAAG,QACzB,EAAM,EAAO,EAAa,IACzB,IAAI,EACF,CAAE,MAAO,EAAc,MAAO,CAAC,GAAG,CAAK,EAAG,SAAU,CAAC,EAAG,MAAO,EAAM,EACrE,EACA,EACA,CACF,CACJ,EAAA,GACgB,EAClB,CAEA,SAAgB,EACd,EACA,EACM,CACN,IAAM,EAAa,EAAW,EAE9B,EAAG,gBAAgB,EAAc,CAC/B,YAAa,EACb,QAAS,MAAO,EAAO,IAAQ,CAC7B,GAAI,CAAC,EAAI,MAAO,OAEhB,IAAM,EAAS,EAAK,OAAO,KAAK,EAC5B,EACJ,GAAI,CACF,EAAQ,MAAM,EAAK,MAAM,KAAK,CAChC,OAAS,EAAO,CACd,IAAM,EAAS,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EACpE,EAAI,GAAG,OAAO,iCAAiC,IAAU,SAAO,EAChE,MACF,CAEA,IAAM,EAAQA,EAAAA,YAAY,EAAQ,CAAK,EACvC,GAAI,EAAM,SAAW,EAAG,CACtB,EAAI,GAAG,OAAO,kFAAmF,MAAI,EACrG,MACF,CAEA,IAAM,EAAQ,MAAM,EAAW,EAAK,EAAO,CAAU,EACrD,GAAI,CAAC,EAAO,OAEZ,IAAM,EAAOC,EAAAA,uBAAuB,EAAO,EAAQ,CAAK,EACnD,GACL,EAAI,GAAG,cAAcC,EAAAA,iBAAiB,EAAI,GAAG,cAAc,EAAG,CAAI,CAAC,CACrE,CACF,CAAC,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{promptItems as e,resolvePromptSelection as t,stagedEditorText as n}from"../services/promptItems.mjs";const r=`prompts`,i=`Browse staged and saved prompts`,a=`Prompts`;function o(){let e;return()=>e??=import(`@agimon-ai/doompi-ui/components/matrixPicker`)}async function s(e,t,n){if(e.mode!==`tui`){let n=t.map(e=>e.label),r=await e.ui.select(a,n),i=r===void 0?-1:n.indexOf(r);return i===-1?void 0:t[i]?.value}let{MatrixPickerComponent:r}=await n();return(await e.ui.custom((e,n,i,o)=>new r({title:a,items:[...t],selected:[],multi:!1},n,i,o)))?.[0]}function c(a,c){let l=o();a.registerCommand(r,{description:i,handler:async(r,i)=>{if(!i.hasUI)return;let a=c.recent.list(),o;try{o=await c.store.list()}catch(e){let t=e instanceof Error?e.message:String(e);i.ui.notify(`Could not read saved prompts: ${t}`,`warning`);return}let u=e(a,o);if(u.length===0){i.ui.notify(`No staged or saved prompts yet. Submit a prompt, or save one with /prompt-save.`,`info`);return}let d=await s(i,u,l);if(!d)return;let f=t(d,a,o);f&&i.ui.setEditorText(n(i.ui.getEditorText(),f))}})}export{i as COMMAND_DESCRIPTION,r as COMMAND_NAME,c as registerPromptsCommand};
|
|
2
|
+
//# sourceMappingURL=promptsCommand.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptsCommand.mjs","names":[],"sources":["../../src/commands/promptsCommand.ts"],"sourcesContent":["import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport type { SelectItem } from '@earendil-works/pi-tui';\nimport { promptItems, resolvePromptSelection, stagedEditorText } from '../services/promptItems.ts';\nimport type { PromptExtensionDependencies } from '../types/prompt.ts';\n\n/**\n * `/prompts`: browse staged and saved prompts, and stage one in the editor.\n *\n * DESIGN PATTERNS:\n * - Reads both sources at open time, so a prompt saved earlier in this session\n * is listed even though Pi only rediscovers templates on the next start.\n * - The shared picker component loads on first use: every session registers this\n * command and almost none of them open it.\n * - Stages text through the host's editor API instead of sending it, because\n * reuse usually means editing before submitting.\n *\n * AVOID:\n * - Replacing a draft the user is in the middle of writing.\n */\n\nexport const COMMAND_NAME = 'prompts';\nexport const COMMAND_DESCRIPTION = 'Browse staged and saved prompts';\n\nconst PICKER_TITLE = 'Prompts';\nconst TUI_MODE = 'tui';\nconst WARNING = 'warning';\nconst INFO = 'info';\n\ntype PickerModule = typeof import('@agimon-ai/doompi-ui/components/matrixPicker');\n\nfunction lazyPicker(): () => Promise<PickerModule> {\n let picker: Promise<PickerModule> | undefined;\n return () => (picker ??= import('@agimon-ai/doompi-ui/components/matrixPicker'));\n}\n\n/** Opens the picker and resolves the chosen row's value, or undefined. */\nasync function pickPrompt(\n ctx: ExtensionContext,\n items: readonly SelectItem[],\n loadPicker: () => Promise<PickerModule>,\n): Promise<string | undefined> {\n if (ctx.mode !== TUI_MODE) {\n // The cockpit and the RPC runtime have no component to render into.\n const labels = items.map((item) => item.label);\n const chosen = await ctx.ui.select(PICKER_TITLE, labels);\n const index = chosen === undefined ? -1 : labels.indexOf(chosen);\n return index === -1 ? undefined : items[index]?.value;\n }\n\n const { MatrixPickerComponent } = await loadPicker();\n const chosen = await ctx.ui.custom<string[] | undefined>(\n (_tui, theme, keybindings, done) =>\n new MatrixPickerComponent(\n { title: PICKER_TITLE, items: [...items], selected: [], multi: false },\n theme,\n keybindings,\n done,\n ),\n );\n return chosen?.[0];\n}\n\nexport function registerPromptsCommand(\n pi: Pick<ExtensionAPI, 'registerCommand'>,\n deps: PromptExtensionDependencies,\n): void {\n const loadPicker = lazyPicker();\n\n pi.registerCommand(COMMAND_NAME, {\n description: COMMAND_DESCRIPTION,\n handler: async (_args, ctx) => {\n if (!ctx.hasUI) return;\n\n const recent = deps.recent.list();\n let saved: Awaited<ReturnType<typeof deps.store.list>>;\n try {\n saved = await deps.store.list();\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n ctx.ui.notify(`Could not read saved prompts: ${reason}`, WARNING);\n return;\n }\n\n const items = promptItems(recent, saved);\n if (items.length === 0) {\n ctx.ui.notify('No staged or saved prompts yet. Submit a prompt, or save one with /prompt-save.', INFO);\n return;\n }\n\n const value = await pickPrompt(ctx, items, loadPicker);\n if (!value) return;\n\n const text = resolvePromptSelection(value, recent, saved);\n if (!text) return;\n ctx.ui.setEditorText(stagedEditorText(ctx.ui.getEditorText(), text));\n },\n });\n}\n"],"mappings":"4GAoBA,MAAa,EAAe,UACf,EAAsB,kCAE7B,EAAe,UAOrB,SAAS,GAA0C,CACjD,IAAI,EACJ,UAAc,IAAW,OAAO,+CAClC,CAGA,eAAe,EACb,EACA,EACA,EAC6B,CAC7B,GAAI,EAAI,OAAS,MAAU,CAEzB,IAAM,EAAS,EAAM,IAAK,GAAS,EAAK,KAAK,EACvC,EAAS,MAAM,EAAI,GAAG,OAAO,EAAc,CAAM,EACjD,EAAQ,IAAW,IAAA,GAAY,GAAK,EAAO,QAAQ,CAAM,EAC/D,OAAO,IAAU,GAAK,IAAA,GAAY,EAAM,EAAM,EAAE,KAClD,CAEA,GAAM,CAAE,yBAA0B,MAAM,EAAW,EAUnD,OAAO,MATc,EAAI,GAAG,QACzB,EAAM,EAAO,EAAa,IACzB,IAAI,EACF,CAAE,MAAO,EAAc,MAAO,CAAC,GAAG,CAAK,EAAG,SAAU,CAAC,EAAG,MAAO,EAAM,EACrE,EACA,EACA,CACF,CACJ,EAAA,GACgB,EAClB,CAEA,SAAgB,EACd,EACA,EACM,CACN,IAAM,EAAa,EAAW,EAE9B,EAAG,gBAAgB,EAAc,CAC/B,YAAa,EACb,QAAS,MAAO,EAAO,IAAQ,CAC7B,GAAI,CAAC,EAAI,MAAO,OAEhB,IAAM,EAAS,EAAK,OAAO,KAAK,EAC5B,EACJ,GAAI,CACF,EAAQ,MAAM,EAAK,MAAM,KAAK,CAChC,OAAS,EAAO,CACd,IAAM,EAAS,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EACpE,EAAI,GAAG,OAAO,iCAAiC,IAAU,SAAO,EAChE,MACF,CAEA,IAAM,EAAQ,EAAY,EAAQ,CAAK,EACvC,GAAI,EAAM,SAAW,EAAG,CACtB,EAAI,GAAG,OAAO,kFAAmF,MAAI,EACrG,MACF,CAEA,IAAM,EAAQ,MAAM,EAAW,EAAK,EAAO,CAAU,EACrD,GAAI,CAAC,EAAO,OAEZ,IAAM,EAAO,EAAuB,EAAO,EAAQ,CAAK,EACnD,GACL,EAAI,GAAG,cAAc,EAAiB,EAAI,GAAG,cAAc,EAAG,CAAI,CAAC,CACrE,CACF,CAAC,CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["createNodeSavedPromptStore","createRecentPrompts"],"sources":["../../src/container/index.ts"],"sourcesContent":["import { createNodeSavedPromptStore } from '../adapters/node/promptStore.ts';\nimport { createRecentPrompts } from '../services/recentPrompts.ts';\nimport type { PromptExtensionDependencies } from '../types/prompt.ts';\n\n/** Plain factory construction, with per-dependency overrides for tests. */\nexport function createPromptContainer(\n overrides: Partial<PromptExtensionDependencies> = {},\n): PromptExtensionDependencies {\n return {\n store: overrides.store ?? createNodeSavedPromptStore(),\n recent: overrides.recent ?? createRecentPrompts(),\n };\n}\n"],"mappings":"+FAKA,SAAgB,EACd,EAAkD,CAAC,EACtB,CAC7B,MAAO,CACL,MAAO,EAAU,OAASA,EAAAA,2BAA2B,EACrD,OAAQ,EAAU,QAAUC,EAAAA,oBAAoB,CAClD,CACF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { PromptExtensionDependencies } from "../types/prompt.cjs";
|
|
2
|
+
//#region src/container/index.d.ts
|
|
3
|
+
/** Plain factory construction, with per-dependency overrides for tests. */
|
|
4
|
+
declare function createPromptContainer(overrides?: Partial<PromptExtensionDependencies>): PromptExtensionDependencies;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { createPromptContainer };
|
|
7
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/container/index.ts"],"mappings":";;;iBAKgB,sBACd,YAAW,QAAQ,+BAClB"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { PromptExtensionDependencies } from "../types/prompt.mjs";
|
|
2
|
+
//#region src/container/index.d.ts
|
|
3
|
+
/** Plain factory construction, with per-dependency overrides for tests. */
|
|
4
|
+
declare function createPromptContainer(overrides?: Partial<PromptExtensionDependencies>): PromptExtensionDependencies;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { createPromptContainer };
|
|
7
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/container/index.ts"],"mappings":";;;iBAKgB,sBACd,YAAW,QAAQ,+BAClB"}
|