opencode-sat 0.0.12
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 +180 -0
- package/dist/sat.d.ts +4 -0
- package/dist/sat.d.ts.map +1 -0
- package/dist/sat.js +62 -0
- package/dist/sat.js.map +1 -0
- package/dist/src/append.d.ts +23 -0
- package/dist/src/append.d.ts.map +1 -0
- package/dist/src/append.js +57 -0
- package/dist/src/append.js.map +1 -0
- package/dist/src/discover.d.ts +11 -0
- package/dist/src/discover.d.ts.map +1 -0
- package/dist/src/discover.js +98 -0
- package/dist/src/discover.js.map +1 -0
- package/dist/src/format-prompt.d.ts +3 -0
- package/dist/src/format-prompt.d.ts.map +1 -0
- package/dist/src/format-prompt.js +92 -0
- package/dist/src/format-prompt.js.map +1 -0
- package/dist/src/opencode/notify.d.ts +10 -0
- package/dist/src/opencode/notify.d.ts.map +1 -0
- package/dist/src/opencode/notify.js +14 -0
- package/dist/src/opencode/notify.js.map +1 -0
- package/dist/src/process-prompt.d.ts +21 -0
- package/dist/src/process-prompt.d.ts.map +1 -0
- package/dist/src/process-prompt.js +20 -0
- package/dist/src/process-prompt.js.map +1 -0
- package/dist/src/process.d.ts +26 -0
- package/dist/src/process.d.ts.map +1 -0
- package/dist/src/process.js +56 -0
- package/dist/src/process.js.map +1 -0
- package/dist/src/prompt-prompt.d.ts +3 -0
- package/dist/src/prompt-prompt.d.ts.map +1 -0
- package/dist/src/prompt-prompt.js +47 -0
- package/dist/src/prompt-prompt.js.map +1 -0
- package/dist/src/prompt-schema.d.ts +22 -0
- package/dist/src/prompt-schema.d.ts.map +1 -0
- package/dist/src/prompt-schema.js +33 -0
- package/dist/src/prompt-schema.js.map +1 -0
- package/dist/src/prompt.d.ts +7 -0
- package/dist/src/prompt.d.ts.map +1 -0
- package/dist/src/prompt.js +84 -0
- package/dist/src/prompt.js.map +1 -0
- package/dist/src/resolve.d.ts +4 -0
- package/dist/src/resolve.d.ts.map +1 -0
- package/dist/src/resolve.js +19 -0
- package/dist/src/resolve.js.map +1 -0
- package/dist/src/rule-schema.d.ts +69 -0
- package/dist/src/rule-schema.d.ts.map +1 -0
- package/dist/src/rule-schema.js +47 -0
- package/dist/src/rule-schema.js.map +1 -0
- package/dist/src/session.d.ts +23 -0
- package/dist/src/session.d.ts.map +1 -0
- package/dist/src/session.js +112 -0
- package/dist/src/session.js.map +1 -0
- package/dist/src/tools.d.ts +59 -0
- package/dist/src/tools.d.ts.map +1 -0
- package/dist/src/tools.js +221 -0
- package/dist/src/tools.js.map +1 -0
- package/dist/src/utils/compare.d.ts +23 -0
- package/dist/src/utils/compare.d.ts.map +1 -0
- package/dist/src/utils/compare.js +116 -0
- package/dist/src/utils/compare.js.map +1 -0
- package/dist/src/utils/extractLlmError.d.ts +13 -0
- package/dist/src/utils/extractLlmError.d.ts.map +1 -0
- package/dist/src/utils/extractLlmError.js +12 -0
- package/dist/src/utils/extractLlmError.js.map +1 -0
- package/dist/src/utils/safe.d.ts +14 -0
- package/dist/src/utils/safe.d.ts.map +1 -0
- package/dist/src/utils/safe.js +31 -0
- package/dist/src/utils/safe.js.map +1 -0
- package/dist/src/utils/stripCodeFences.d.ts +2 -0
- package/dist/src/utils/stripCodeFences.d.ts.map +1 -0
- package/dist/src/utils/stripCodeFences.js +9 -0
- package/dist/src/utils/stripCodeFences.js.map +1 -0
- package/dist/src/utils/validate.d.ts +19 -0
- package/dist/src/utils/validate.d.ts.map +1 -0
- package/dist/src/utils/validate.js +30 -0
- package/dist/src/utils/validate.js.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Dustin Dowell
|
|
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,180 @@
|
|
|
1
|
+
# OpenCode SAT (Speech Act Theory)
|
|
2
|
+
|
|
3
|
+
An OpenCode plugin that converts unstructured text into structured, consistent formats using speech act theory.
|
|
4
|
+
|
|
5
|
+
<img width="612" height="256" alt="Rule formatting table output" src="https://github.com/user-attachments/assets/51edf4b5-831a-4e13-96de-8cad453ea13e" />
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
Add the plugin to your `opencode.json` and restart OpenCode:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"plugin": ["opencode-sat"]
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then just tell OpenCode what you want:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
Rewrite my instruction files
|
|
21
|
+
Add a rule about using early returns
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Messy or voice-transcribed input can be restructured into a clear task hierarchy using the `refine-prompt` tool.
|
|
25
|
+
|
|
26
|
+
## Tools
|
|
27
|
+
|
|
28
|
+
For the theory behind the plugin, see [Theoretical Foundation](#theoretical-foundation).
|
|
29
|
+
|
|
30
|
+
### rewrite-instructions
|
|
31
|
+
|
|
32
|
+
Rewrites all matched instruction files through the parse/format pipeline. Discovers files from the `instructions` array in your `opencode.json`. Accepts an optional `mode` (`verbose`, `balanced`, or `concise`, default `balanced`) and an optional `files` string of comma-separated paths to process specific files instead of running discovery.
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
rewrite-instructions
|
|
36
|
+
rewrite-instructions [mode=concise]
|
|
37
|
+
rewrite-instructions [files=fixtures/testing.md]
|
|
38
|
+
rewrite-instructions [files=a.md,b.md, mode=verbose]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Input:**
|
|
42
|
+
```
|
|
43
|
+
Always use return await when returning promises from async functions. This provides
|
|
44
|
+
better stack traces and error handling. Arrow functions are the standard function
|
|
45
|
+
syntax. Do not use function declarations or function expressions because arrow
|
|
46
|
+
functions provide lexical this binding and a more compact syntax.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**verbose** - Full Rule/Reason pairs for every rule.
|
|
50
|
+
```
|
|
51
|
+
Rule: Always use return await when returning promises from async functions.
|
|
52
|
+
Reason: Provides better stack traces and error handling.
|
|
53
|
+
|
|
54
|
+
Rule: Use arrow functions as the standard function syntax.
|
|
55
|
+
Reason: Arrow functions provide lexical this binding and a more compact syntax.
|
|
56
|
+
|
|
57
|
+
Rule: Never use function declarations or function expressions.
|
|
58
|
+
Reason: Arrow functions are the standard syntax for the project.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**balanced** (default) - The LLM decides which rules need reasons.
|
|
62
|
+
```
|
|
63
|
+
Rule: Always use return await when returning promises from async functions.
|
|
64
|
+
Reason: Provides better stack traces and error handling.
|
|
65
|
+
|
|
66
|
+
Rule: Use arrow functions as the standard function syntax.
|
|
67
|
+
|
|
68
|
+
Rule: Never use function declarations or function expressions.
|
|
69
|
+
Reason: Arrow functions provide lexical this binding and a more compact syntax.
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**concise** - Bullet list of directives only, no reasons.
|
|
73
|
+
```
|
|
74
|
+
- Always use return await when returning promises from async functions.
|
|
75
|
+
- Use arrow functions as the standard function syntax.
|
|
76
|
+
- Never use function declarations or function expressions.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### add-instruction
|
|
80
|
+
|
|
81
|
+
Appends new rules to the end of an instruction file without rewriting existing content. Takes an `input` string of unstructured rule text to parse, format, and append. Accepts an optional `file` path (defaults to the first discovered instruction file) and an optional `mode` (`verbose`, `balanced`, or `concise`, default `balanced`).
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
add-instruction [input=Always use early returns]
|
|
85
|
+
add-instruction [input=Use early returns, mode=concise]
|
|
86
|
+
add-instruction [input=Use early returns, file=docs/rules.md]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### automatic-rule
|
|
90
|
+
|
|
91
|
+
Detects when the user corrects the agent or expresses a coding preference, extracts the implicit rule, and appends it to the instruction file. This tool is invoked automatically by the LLM, not by the user. Takes an `input` string of the user's correction or feedback. Accepts an optional `file` path (defaults to the first discovered instruction file).
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
automatic-rule [input=Don't use semicolons in this project]
|
|
95
|
+
automatic-rule [input=I prefer early returns, file=docs/rules.md]
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### refine-prompt
|
|
99
|
+
|
|
100
|
+
Restructures messy or unstructured user input into a clear task hierarchy. Takes an `input` string of raw text (often from voice transcription) and decomposes it into structured tasks with intent, targets, constraints, context, and recursive subtasks.
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
refine-prompt [input=refactor the search module add guards to each provider make sure bsky and wiki get validated then run the tests]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Output:
|
|
107
|
+
```
|
|
108
|
+
┌ 1. Refactor the search module
|
|
109
|
+
│ > Targets: src/search.ts, src/providers/
|
|
110
|
+
│ > Constraints: use safeAsync, no optional chaining
|
|
111
|
+
│ > Context: Current error handling is inconsistent
|
|
112
|
+
│
|
|
113
|
+
├──┬ 2. Add guards to providers
|
|
114
|
+
│ │ > Targets: src/providers/
|
|
115
|
+
│ │ > Constraints: use isRecord helper
|
|
116
|
+
│ │
|
|
117
|
+
│ ├─── 3. Validate bsky responses
|
|
118
|
+
│ │ > Targets: bsky-search.ts
|
|
119
|
+
│ │
|
|
120
|
+
│ └─── 4. Validate wiki responses
|
|
121
|
+
│ > Targets: wiki-search.ts
|
|
122
|
+
│
|
|
123
|
+
├─── 5. Update error handling
|
|
124
|
+
│ > Targets: src/utils/safe.ts
|
|
125
|
+
│
|
|
126
|
+
└─── 6. Run the tests
|
|
127
|
+
> Constraints: fix any failures
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Theoretical Foundation
|
|
131
|
+
|
|
132
|
+
The plugin is built on [speech act theory](https://en.wikipedia.org/wiki/Speech_act) (Austin, Searle). All instructions are **directives**: speech acts that get the hearer to do something. But directives come in two forms, and each needs a different formal framework.
|
|
133
|
+
|
|
134
|
+
### Rule Formatting (deontic logic, regulative directives)
|
|
135
|
+
|
|
136
|
+
Rules constrain ongoing behavior. They are standing obligations, prohibitions, and permissions that persist across all future actions. The formal framework is [deontic logic](https://en.wikipedia.org/wiki/Deontic_logic): what is obligatory, forbidden, and permissible.
|
|
137
|
+
|
|
138
|
+
The plugin parses unstructured rule text into structured components:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
type ParsedRule = {
|
|
142
|
+
strength: 'obligatory' | 'forbidden' | 'permissible' | 'optional' | 'supererogatory' | 'indifferent' | 'omissible'
|
|
143
|
+
action: string
|
|
144
|
+
target: string
|
|
145
|
+
context?: string
|
|
146
|
+
reason: string
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Prompt Formatting (action/planning logic, performative directives)
|
|
151
|
+
|
|
152
|
+
Prompts request a specific one-shot action. They are not standing rules but immediate instructions. The formal framework is closer to [action languages](https://en.wikipedia.org/wiki/Action_language) from AI planning (STRIPS, ADL, HTN): what the goal is, what must be true before acting, and what changes after.
|
|
153
|
+
|
|
154
|
+
A messy user prompt typically mixes three levels together:
|
|
155
|
+
|
|
156
|
+
- **Goal** (desired end state): "I want search results to show up in chat"
|
|
157
|
+
- **Tasks** (what to do): "add a postResult call, update the providers"
|
|
158
|
+
- **Constraints** (conditions/preferences): "don't break existing tests, use safeAsync"
|
|
159
|
+
|
|
160
|
+
The plugin parses raw input into structured components:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
type ParsedTask = {
|
|
164
|
+
intent: string
|
|
165
|
+
targets: Array<string>
|
|
166
|
+
constraints: Array<string>
|
|
167
|
+
context?: string
|
|
168
|
+
subtasks: Array<ParsedTask>
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The schema is recursive. A `ParsedTask` can contain subtasks, which can contain their own subtasks. This follows the HTN (Hierarchical Task Network) model where compound tasks decompose into subtask trees.
|
|
173
|
+
|
|
174
|
+
## Disclaimer
|
|
175
|
+
|
|
176
|
+
I'm not an NLP expert. I stumbled onto speech act theory and deontic logic while researching NLP and thought it could be a good fit for structuring instructions. The implementation may not perfectly align with academic definitions, but the goal is practical utility.
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT
|
package/dist/sat.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sat.d.ts","sourceRoot":"","sources":["../sat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAGjD,QAAA,MAAM,MAAM,EAAE,MA8Db,CAAA;AAED,eAAe,MAAM,CAAA"}
|
package/dist/sat.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createAppendTool, createRefineTool, createRewriteTool } from "./src/tools.js";
|
|
2
|
+
const plugin = async ({ directory, client }) => {
|
|
3
|
+
const deps = { directory, client };
|
|
4
|
+
return {
|
|
5
|
+
tool: {
|
|
6
|
+
'rewrite-instructions': createRewriteTool({
|
|
7
|
+
deps,
|
|
8
|
+
description: [
|
|
9
|
+
'- Discover instruction files from opencode.json configuration.',
|
|
10
|
+
'- Parse discovered instruction files into structured rules.',
|
|
11
|
+
'- Format structured rules into human-readable rules.',
|
|
12
|
+
'- Write formatted rules back to the original instruction files.',
|
|
13
|
+
'- Optionally accept a mode parameter (verbose, balanced, or concise) to control formatting.',
|
|
14
|
+
'- Default to balanced mode when no mode is specified.',
|
|
15
|
+
'- Optionally accept a files parameter to process specific files instead of running discovery.',
|
|
16
|
+
].join('\n'),
|
|
17
|
+
}),
|
|
18
|
+
'add-instruction': createAppendTool({
|
|
19
|
+
deps,
|
|
20
|
+
toolName: 'add-instruction',
|
|
21
|
+
sessionTitle: 'SAT Add',
|
|
22
|
+
defaultMode: 'balanced',
|
|
23
|
+
hasMode: true,
|
|
24
|
+
successPrefix: 'Added',
|
|
25
|
+
description: [
|
|
26
|
+
'- Parse unstructured input into structured rules.',
|
|
27
|
+
'- Format parsed rules after parsing unstructured input into structured rules.',
|
|
28
|
+
'- Append formatted rules to the instruction file without rewriting existing content.',
|
|
29
|
+
'- Optionally accept a mode parameter (verbose, balanced, or concise).',
|
|
30
|
+
'- Default mode to balanced when no mode parameter is specified.',
|
|
31
|
+
'- Optionally accept a file parameter to specify the target instruction file.',
|
|
32
|
+
'- Append to the first discovered instruction file when no file parameter is specified.',
|
|
33
|
+
].join('\n'),
|
|
34
|
+
}),
|
|
35
|
+
'automatic-rule': createAppendTool({
|
|
36
|
+
deps,
|
|
37
|
+
toolName: 'automatic-rule',
|
|
38
|
+
sessionTitle: 'SAT Candidate',
|
|
39
|
+
defaultMode: 'balanced',
|
|
40
|
+
hasMode: false,
|
|
41
|
+
successPrefix: 'Learned',
|
|
42
|
+
description: [
|
|
43
|
+
'- Detect user corrections when the user says something was done wrong, expresses a preference about how code should be written, or gives feedback that implies a repeatable guideline.',
|
|
44
|
+
'- Extract the implicit rule from the correction after detecting a user correction or preference.',
|
|
45
|
+
'- Persist the extracted rule as a new instruction rule.',
|
|
46
|
+
'- Append the new rule to the instruction file.',
|
|
47
|
+
].join('\n'),
|
|
48
|
+
}),
|
|
49
|
+
'refine-prompt': createRefineTool({
|
|
50
|
+
deps,
|
|
51
|
+
description: [
|
|
52
|
+
'- Restructure messy, ambiguous, or voice-transcribed user input before starting work when the message is vague, run-on, contains multiple interleaved requests, or reads like unpunctuated speech.',
|
|
53
|
+
'- Invoke this tool BEFORE starting work on vague, run-on, multi-request, or speech-like user messages.',
|
|
54
|
+
'- Decompose the input into a hierarchical task breakdown when restructuring user input.',
|
|
55
|
+
'- Return formatted markdown after decomposing the input.',
|
|
56
|
+
].join('\n'),
|
|
57
|
+
}),
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
export default plugin;
|
|
62
|
+
//# sourceMappingURL=sat.js.map
|
package/dist/sat.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sat.js","sourceRoot":"","sources":["../sat.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEtF,MAAM,MAAM,GAAW,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE;IACrD,MAAM,IAAI,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,CAAA;IAElC,OAAO;QACL,IAAI,EAAE;YACJ,sBAAsB,EAAE,iBAAiB,CAAC;gBACxC,IAAI;gBACJ,WAAW,EAAE;oBACX,gEAAgE;oBAChE,6DAA6D;oBAC7D,sDAAsD;oBACtD,iEAAiE;oBACjE,6FAA6F;oBAC7F,uDAAuD;oBACvD,+FAA+F;iBAChG,CAAC,IAAI,CAAC,IAAI,CAAC;aACb,CAAC;YAEF,iBAAiB,EAAE,gBAAgB,CAAC;gBAClC,IAAI;gBACJ,QAAQ,EAAE,iBAAiB;gBAC3B,YAAY,EAAE,SAAS;gBACvB,WAAW,EAAE,UAAU;gBACvB,OAAO,EAAE,IAAI;gBACb,aAAa,EAAE,OAAO;gBACtB,WAAW,EAAE;oBACX,mDAAmD;oBACnD,+EAA+E;oBAC/E,sFAAsF;oBACtF,uEAAuE;oBACvE,iEAAiE;oBACjE,8EAA8E;oBAC9E,wFAAwF;iBACzF,CAAC,IAAI,CAAC,IAAI,CAAC;aACb,CAAC;YAEF,gBAAgB,EAAE,gBAAgB,CAAC;gBACjC,IAAI;gBACJ,QAAQ,EAAE,gBAAgB;gBAC1B,YAAY,EAAE,eAAe;gBAC7B,WAAW,EAAE,UAAU;gBACvB,OAAO,EAAE,KAAK;gBACd,aAAa,EAAE,SAAS;gBACxB,WAAW,EAAE;oBACX,wLAAwL;oBACxL,kGAAkG;oBAClG,yDAAyD;oBACzD,gDAAgD;iBACjD,CAAC,IAAI,CAAC,IAAI,CAAC;aACb,CAAC;YAEF,eAAe,EAAE,gBAAgB,CAAC;gBAChC,IAAI;gBACJ,WAAW,EAAE;oBACX,oMAAoM;oBACpM,wGAAwG;oBACxG,yFAAyF;oBACzF,0DAA0D;iBAC3D,CAAC,IAAI,CAAC,IAAI,CAAC;aACb,CAAC;SACH;KACF,CAAA;AACH,CAAC,CAAA;AAED,eAAe,MAAM,CAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PromptFn } from './process';
|
|
2
|
+
import { type FormatMode } from './prompt';
|
|
3
|
+
type AppendResultSuccess = {
|
|
4
|
+
status: 'success';
|
|
5
|
+
path: string;
|
|
6
|
+
rulesCount: number;
|
|
7
|
+
};
|
|
8
|
+
type AppendResultError = {
|
|
9
|
+
status: 'parseError' | 'formatError' | 'readError' | 'writeError';
|
|
10
|
+
path: string;
|
|
11
|
+
error: string;
|
|
12
|
+
};
|
|
13
|
+
type AppendResult = AppendResultSuccess | AppendResultError;
|
|
14
|
+
type AppendRulesOptions = {
|
|
15
|
+
input: string;
|
|
16
|
+
filePath: string;
|
|
17
|
+
directory: string;
|
|
18
|
+
prompt: PromptFn;
|
|
19
|
+
mode?: FormatMode;
|
|
20
|
+
};
|
|
21
|
+
export declare const appendRules: (options: AppendRulesOptions) => Promise<AppendResult>;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=append.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"append.d.ts","sourceRoot":"","sources":["../../src/append.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAuC,KAAK,UAAU,EAAE,MAAM,UAAU,CAAA;AAI/E,KAAK,mBAAmB,GAAG;IACzB,MAAM,EAAE,SAAS,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,KAAK,iBAAiB,GAAG;IACvB,MAAM,EAAE,YAAY,GAAG,aAAa,GAAG,WAAW,GAAG,YAAY,CAAA;IACjE,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,KAAK,YAAY,GAAG,mBAAmB,GAAG,iBAAiB,CAAA;AAE3D,KAAK,kBAAkB,GAAG;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,QAAQ,CAAA;IAChB,IAAI,CAAC,EAAE,UAAU,CAAA;CAClB,CAAA;AAGD,eAAO,MAAM,WAAW,GAAU,SAAS,kBAAkB,KAAG,OAAO,CAAC,YAAY,CAuDnF,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { buildFormatPrompt, buildParsePrompt } from './prompt';
|
|
4
|
+
import { FormatResponseSchema, ParseResponseSchema } from './rule-schema';
|
|
5
|
+
import { safeAsync } from './utils/safe';
|
|
6
|
+
// parse unstructured input, format it, and append to end of file
|
|
7
|
+
export const appendRules = async (options) => {
|
|
8
|
+
const mode = options.mode ?? 'balanced';
|
|
9
|
+
const fullPath = resolve(options.directory, options.filePath);
|
|
10
|
+
// read existing file content
|
|
11
|
+
const existing = await safeAsync(() => readFile(fullPath, 'utf-8'));
|
|
12
|
+
if (existing.error !== null) {
|
|
13
|
+
return {
|
|
14
|
+
status: 'readError',
|
|
15
|
+
path: options.filePath,
|
|
16
|
+
error: existing.error.message,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
// step 1: parse input -> structured rules
|
|
20
|
+
const parseResult = await options.prompt(buildParsePrompt(options.input), ParseResponseSchema);
|
|
21
|
+
if (parseResult.error !== null) {
|
|
22
|
+
return {
|
|
23
|
+
status: 'parseError',
|
|
24
|
+
path: options.filePath,
|
|
25
|
+
error: String(parseResult.error),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// step 2: format structured rules -> human-readable rules
|
|
29
|
+
const formatPrompt = buildFormatPrompt(JSON.stringify(parseResult.data), mode);
|
|
30
|
+
const formatResult = await options.prompt(formatPrompt, FormatResponseSchema);
|
|
31
|
+
if (formatResult.error !== null) {
|
|
32
|
+
return {
|
|
33
|
+
status: 'formatError',
|
|
34
|
+
path: options.filePath,
|
|
35
|
+
error: String(formatResult.error),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// step 3: append formatted rules to end of file
|
|
39
|
+
const joiner = mode === 'concise' ? '\n' : '\n\n';
|
|
40
|
+
const newRules = formatResult.data.rules.join(joiner);
|
|
41
|
+
const separator = existing.data.endsWith('\n') ? '\n' : '\n\n';
|
|
42
|
+
const content = existing.data + separator + newRules + '\n';
|
|
43
|
+
const writeResult = await safeAsync(() => writeFile(fullPath, content, 'utf-8'));
|
|
44
|
+
if (writeResult.error) {
|
|
45
|
+
return {
|
|
46
|
+
status: 'writeError',
|
|
47
|
+
path: options.filePath,
|
|
48
|
+
error: writeResult.error.message,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
status: 'success',
|
|
53
|
+
path: options.filePath,
|
|
54
|
+
rulesCount: formatResult.data.rules.length,
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=append.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"append.js","sourceRoot":"","sources":["../../src/append.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Result } from './utils/safe';
|
|
2
|
+
export type InstructionFile = {
|
|
3
|
+
path: string;
|
|
4
|
+
content: string;
|
|
5
|
+
error?: string;
|
|
6
|
+
};
|
|
7
|
+
type DiscoverResult = Result<InstructionFile[]>;
|
|
8
|
+
export declare const readFilePaths: (directory: string, paths: string[]) => Promise<InstructionFile[]>;
|
|
9
|
+
export declare const discover: (directory: string) => Promise<DiscoverResult>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=discover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../src/discover.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAmB,MAAM,cAAc,CAAA;AAE3D,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,KAAK,cAAc,GAAG,MAAM,CAAC,eAAe,EAAE,CAAC,CAAA;AAoF/C,eAAO,MAAM,aAAa,GAAU,WAAW,MAAM,EAAE,OAAO,MAAM,EAAE,KAAG,OAAO,CAAC,eAAe,EAAE,CAGjG,CAAA;AAED,eAAO,MAAM,QAAQ,GAAU,WAAW,MAAM,KAAG,OAAO,CAAC,cAAc,CAsBxE,CAAA"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { glob, readFile } from 'node:fs/promises';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { safe, safeAsync } from './utils/safe';
|
|
4
|
+
const readConfig = async (directory) => {
|
|
5
|
+
const configPath = join(directory, 'opencode.json');
|
|
6
|
+
const readResult = await safeAsync(() => readFile(configPath, 'utf-8'));
|
|
7
|
+
if (readResult.error) {
|
|
8
|
+
return {
|
|
9
|
+
data: null,
|
|
10
|
+
error: 'Could not read ' + configPath + ': ' + readResult.error.message,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const parseResult = safe(() => JSON.parse(readResult.data));
|
|
14
|
+
if (parseResult.error) {
|
|
15
|
+
return {
|
|
16
|
+
data: null,
|
|
17
|
+
error: 'Invalid JSON in ' + configPath + ': ' + parseResult.error.message,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const instructions = parseResult.data.instructions;
|
|
21
|
+
if (!instructions || !Array.isArray(instructions) || instructions.length === 0) {
|
|
22
|
+
return {
|
|
23
|
+
data: null,
|
|
24
|
+
error: 'No "instructions" array found in ' + configPath,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const patterns = instructions.filter((entry) => typeof entry === 'string');
|
|
28
|
+
if (patterns.length === 0) {
|
|
29
|
+
return {
|
|
30
|
+
data: null,
|
|
31
|
+
error: 'No valid string patterns in "instructions" in ' + configPath,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
data: patterns,
|
|
36
|
+
error: null,
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const resolveFiles = async (directory, patterns) => {
|
|
40
|
+
const seen = new Set();
|
|
41
|
+
const files = [];
|
|
42
|
+
for (const pattern of patterns) {
|
|
43
|
+
for await (const path of glob(pattern, { cwd: directory })) {
|
|
44
|
+
const full = join(directory, path);
|
|
45
|
+
if (!seen.has(full)) {
|
|
46
|
+
seen.add(full);
|
|
47
|
+
files.push(full);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return files;
|
|
52
|
+
};
|
|
53
|
+
const readFiles = async (files) => {
|
|
54
|
+
const results = [];
|
|
55
|
+
for (const file of files) {
|
|
56
|
+
const readResult = await safeAsync(() => readFile(file, 'utf-8'));
|
|
57
|
+
if (readResult.error) {
|
|
58
|
+
results.push({
|
|
59
|
+
path: file,
|
|
60
|
+
content: '',
|
|
61
|
+
error: readResult.error.message,
|
|
62
|
+
});
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
results.push({
|
|
66
|
+
path: file,
|
|
67
|
+
content: readResult.data,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return results;
|
|
71
|
+
};
|
|
72
|
+
// read specific file paths into InstructionFile entries, resolving relative paths against directory
|
|
73
|
+
export const readFilePaths = async (directory, paths) => {
|
|
74
|
+
const resolved = paths.map((p) => resolve(directory, p));
|
|
75
|
+
return await readFiles(resolved);
|
|
76
|
+
};
|
|
77
|
+
export const discover = async (directory) => {
|
|
78
|
+
const config = await readConfig(directory);
|
|
79
|
+
if (config.error !== null) {
|
|
80
|
+
return {
|
|
81
|
+
data: null,
|
|
82
|
+
error: config.error,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const patterns = config.data;
|
|
86
|
+
const files = await resolveFiles(directory, patterns);
|
|
87
|
+
if (files.length === 0) {
|
|
88
|
+
return {
|
|
89
|
+
data: null,
|
|
90
|
+
error: 'No instruction files found matching patterns: ' + patterns.join(', '),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
data: await readFiles(files),
|
|
95
|
+
error: null,
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=discover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/discover.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-prompt.d.ts","sourceRoot":"","sources":["../../src/format-prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,oBAAoB,CAAA;AA4GlE,eAAO,MAAM,YAAY,GAAI,QAAQ,YAAY,KAAG,MA8BnD,CAAA"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const buildNode = (options) => {
|
|
2
|
+
const { task, counter } = options;
|
|
3
|
+
const label = String(counter.value) + '. ' + task.intent;
|
|
4
|
+
counter.value++;
|
|
5
|
+
const metaLines = [];
|
|
6
|
+
if (task.targets.length > 0) {
|
|
7
|
+
metaLines.push('> Targets: ' + task.targets.join(', '));
|
|
8
|
+
}
|
|
9
|
+
if (task.constraints.length > 0) {
|
|
10
|
+
metaLines.push('> Constraints: ' + task.constraints.join(', '));
|
|
11
|
+
}
|
|
12
|
+
if (task.context) {
|
|
13
|
+
metaLines.push('> Context: ' + task.context);
|
|
14
|
+
}
|
|
15
|
+
const children = task.subtasks.map((subtask) => {
|
|
16
|
+
return buildNode({ task: subtask, counter });
|
|
17
|
+
});
|
|
18
|
+
return { label, metaLines, children };
|
|
19
|
+
};
|
|
20
|
+
const flatten = (nodes) => {
|
|
21
|
+
const result = [];
|
|
22
|
+
for (const node of nodes) {
|
|
23
|
+
result.push({ label: node.label, metaLines: node.metaLines, children: [] });
|
|
24
|
+
for (const child of node.children) {
|
|
25
|
+
result.push(child);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
};
|
|
30
|
+
const renderNode = (options) => {
|
|
31
|
+
const { node, prefix, childPrefix } = options;
|
|
32
|
+
const hasChildren = node.children.length > 0;
|
|
33
|
+
const firstLine = prefix + node.label;
|
|
34
|
+
const lines = [firstLine];
|
|
35
|
+
const numEnd = node.label.indexOf('. ') + 2;
|
|
36
|
+
const textStart = prefix.length + numEnd;
|
|
37
|
+
const pipe = hasChildren ? '│' : ' ';
|
|
38
|
+
const padLen = textStart - childPrefix.length - 1;
|
|
39
|
+
const metaPad = childPrefix + pipe + ' '.repeat(padLen > 0 ? padLen : 0);
|
|
40
|
+
for (const meta of node.metaLines) {
|
|
41
|
+
lines.push(metaPad + meta);
|
|
42
|
+
}
|
|
43
|
+
if (hasChildren) {
|
|
44
|
+
lines.push(childPrefix + '│');
|
|
45
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
46
|
+
const child = node.children[i];
|
|
47
|
+
if (!child)
|
|
48
|
+
continue;
|
|
49
|
+
if (i > 0) {
|
|
50
|
+
lines.push(childPrefix + '│');
|
|
51
|
+
}
|
|
52
|
+
const isLast = i === node.children.length - 1;
|
|
53
|
+
const childHasChildren = child.children.length > 0;
|
|
54
|
+
const connector = (isLast ? '└──' : '├──') + (childHasChildren ? '┬ ' : '─ ');
|
|
55
|
+
const continuation = isLast
|
|
56
|
+
? (childHasChildren ? ' ' : ' ')
|
|
57
|
+
: (childHasChildren ? '│ ' : '│ ');
|
|
58
|
+
const childLines = renderNode({
|
|
59
|
+
node: child,
|
|
60
|
+
prefix: childPrefix + connector,
|
|
61
|
+
childPrefix: childPrefix + continuation,
|
|
62
|
+
});
|
|
63
|
+
lines.push(...childLines);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return lines;
|
|
67
|
+
};
|
|
68
|
+
export const formatPrompt = (parsed) => {
|
|
69
|
+
if (parsed.tasks.length === 0) {
|
|
70
|
+
return '';
|
|
71
|
+
}
|
|
72
|
+
const counter = { value: 1 };
|
|
73
|
+
const nodes = parsed.tasks.map((task) => buildNode({ task, counter }));
|
|
74
|
+
const totalNodes = counter.value - 1;
|
|
75
|
+
if (totalNodes === 1 && nodes[0]) {
|
|
76
|
+
const node = nodes[0];
|
|
77
|
+
const lines = [node.label];
|
|
78
|
+
for (const meta of node.metaLines) {
|
|
79
|
+
lines.push(' ' + meta);
|
|
80
|
+
}
|
|
81
|
+
return lines.join('\n');
|
|
82
|
+
}
|
|
83
|
+
const flat = flatten(nodes);
|
|
84
|
+
const root = {
|
|
85
|
+
label: flat[0]?.label ?? '',
|
|
86
|
+
metaLines: flat[0]?.metaLines ?? [],
|
|
87
|
+
children: flat.slice(1),
|
|
88
|
+
};
|
|
89
|
+
const rootPrefix = root.children.length > 0 ? '┌ ' : '';
|
|
90
|
+
return renderNode({ node: root, prefix: rootPrefix, childPrefix: '' }).join('\n');
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=format-prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-prompt.js","sourceRoot":"","sources":["../../src/format-prompt.ts"],"names":[],"mappings":"AAeA,MAAM,SAAS,GAAG,CAAC,OAAyB,EAAY,EAAE;IACxD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IACjC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,MAAM,CAAA;IACxD,OAAO,CAAC,KAAK,EAAE,CAAA;IAEf,MAAM,SAAS,GAAkB,EAAE,CAAA;IAEnC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,SAAS,CAAC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IACzD,CAAC;IAED,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,SAAS,CAAC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IACjE,CAAC;IAED,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,SAAS,CAAC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,CAAA;IAC9C,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7C,OAAO,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IAEF,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAA;AACvC,CAAC,CAAA;AAED,MAAM,OAAO,GAAG,CAAC,KAAsB,EAAmB,EAAE;IAC1D,MAAM,MAAM,GAAoB,EAAE,CAAA;IAElC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAA;QAE3E,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACpB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAQD,MAAM,UAAU,GAAG,CAAC,OAAsB,EAAiB,EAAE;IAC3D,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAA;IAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;IAC5C,MAAM,SAAS,GAAG,MAAM,GAAG,IAAI,CAAC,KAAK,CAAA;IACrC,MAAM,KAAK,GAAkB,CAAC,SAAS,CAAC,CAAA;IAExC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC3C,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAA;IACxC,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;IACpC,MAAM,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAA;IACjD,MAAM,OAAO,GAAG,WAAW,GAAG,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAExE,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IAC5B,CAAC;IAED,IAAI,WAAW,EAAE,CAAC;QAChB,KAAK,CAAC,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC,CAAA;QAE7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;YAC9B,IAAI,CAAC,KAAK;gBAAE,SAAQ;YAEpB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACV,KAAK,CAAC,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC,CAAA;YAC/B,CAAC;YAED,MAAM,MAAM,GAAG,CAAC,KAAK,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;YAC7C,MAAM,gBAAgB,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;YAClD,MAAM,SAAS,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;YAC7E,MAAM,YAAY,GAAG,MAAM;gBACzB,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;gBACrC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;YAEvC,MAAM,UAAU,GAAG,UAAU,CAAC;gBAC5B,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE,WAAW,GAAG,SAAS;gBAC/B,WAAW,EAAE,WAAW,GAAG,YAAY;aACxC,CAAC,CAAA;YAEF,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAA;QAC3B,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAoB,EAAU,EAAE;IAC3D,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,CAAA;IACX,CAAC;IAED,MAAM,OAAO,GAAY,EAAE,KAAK,EAAE,CAAC,EAAE,CAAA;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAA;IACtE,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,GAAG,CAAC,CAAA;IAEpC,IAAI,UAAU,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACrB,MAAM,KAAK,GAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAEzC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAClC,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;QACzB,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;IAE3B,MAAM,IAAI,GAAa;QACrB,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,EAAE;QAC3B,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,EAAE;QACnC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;KACxB,CAAA;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;IACvD,OAAO,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACnF,CAAC,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PluginInput } from '@opencode-ai/plugin';
|
|
2
|
+
type Client = PluginInput['client'];
|
|
3
|
+
type SendResultOptions = {
|
|
4
|
+
client: Client;
|
|
5
|
+
sessionID: string;
|
|
6
|
+
text: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const sendResult: (options: SendResultOptions) => Promise<void>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=notify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../../src/opencode/notify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEtD,KAAK,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;AAEnC,KAAK,iBAAiB,GAAG;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,eAAO,MAAM,UAAU,GAAU,SAAS,iBAAiB,KAAG,OAAO,CAAC,IAAI,CAYzE,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const sendResult = async (options) => {
|
|
2
|
+
await options.client.session.prompt({
|
|
3
|
+
path: { id: options.sessionID },
|
|
4
|
+
body: {
|
|
5
|
+
noReply: true,
|
|
6
|
+
parts: [{
|
|
7
|
+
type: 'text',
|
|
8
|
+
text: options.text,
|
|
9
|
+
ignored: true,
|
|
10
|
+
}],
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../../src/opencode/notify.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAAE,OAA0B,EAAiB,EAAE;IAC5E,MAAM,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAClC,IAAI,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,SAAS,EAAE;QAC/B,IAAI,EAAE;YACJ,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,CAAC;oBACN,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,OAAO,EAAE,IAAI;iBACd,CAAC;SACH;KACF,CAAC,CAAA;AACJ,CAAC,CAAA"}
|