cueme 0.1.4 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/docs/proto.md +110 -0
- package/package.json +2 -1
- package/protocol.md +30 -33
- package/src/cli.js +128 -43
- package/src/envelope.js +95 -0
- package/src/handler.js +45 -231
- package/src/io.js +61 -4
- package/src/proto.js +391 -0
package/README.md
CHANGED
|
@@ -81,6 +81,24 @@ cueme cue <agent_id> -
|
|
|
81
81
|
cueme pause <agent_id> -
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
You can also provide the prompt as a positional argument:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
cueme pause <agent_id> "Paused. Click Continue when you are ready."
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### proto
|
|
91
|
+
|
|
92
|
+
Example (macOS + Windsurf):
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
cueme proto apply windsurf
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
For detailed `proto` usage (config, init, render, markers), see:
|
|
99
|
+
|
|
100
|
+
- [`docs/proto.md`](./docs/proto.md)
|
|
101
|
+
|
|
84
102
|
All commands output plain text to stdout.
|
|
85
103
|
|
|
86
104
|
---
|
package/docs/proto.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# cueme proto
|
|
2
|
+
|
|
3
|
+
This document describes the `cueme proto` command family.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
`cueme proto` injects the shared `protocol.md` into a specific agent file by composing:
|
|
8
|
+
|
|
9
|
+
`final_proto = prefix(agent) + "\n\n" + protocol.md`
|
|
10
|
+
|
|
11
|
+
The injected content is managed between sentinel markers and may be overwritten by `cueme proto apply`.
|
|
12
|
+
|
|
13
|
+
## Config
|
|
14
|
+
|
|
15
|
+
Config file path:
|
|
16
|
+
|
|
17
|
+
`~/.cue/cueme.json`
|
|
18
|
+
|
|
19
|
+
Required keys:
|
|
20
|
+
|
|
21
|
+
- `cueme.proto.path`: map of injection target paths by `<platform>.<agent>`
|
|
22
|
+
- `platform`: `linux` | `macos` | `windows`
|
|
23
|
+
- supports `~` and `%ENV%` expansions (e.g. `%APPDATA%`, `%USERPROFILE%`)
|
|
24
|
+
- `cueme.proto.prefix`: map of prefix by `<agent>`
|
|
25
|
+
- can be a string or string array (joined with `\n`)
|
|
26
|
+
- `cueme.proto.protocol_path`: absolute or relative path to the shared `protocol.md`
|
|
27
|
+
- supports `~` and `%ENV%` expansions
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"cueme.proto.path": {
|
|
34
|
+
"macos.vscode": "~/Library/Application Support/Code/User/prompts/human_proto.md",
|
|
35
|
+
"macos.windsurf": "~/.codeium/windsurf/memories/global_rules.md",
|
|
36
|
+
|
|
37
|
+
"windows.vscode": "%APPDATA%\\Code\\User\\prompts\\human_proto.md",
|
|
38
|
+
"windows.windsurf": "%USERPROFILE%\\.codeium\\windsurf\\memories\\global_rules.md",
|
|
39
|
+
|
|
40
|
+
"linux.vscode": "~/.config/Code/User/prompts/human_proto.md",
|
|
41
|
+
"linux.windsurf": "~/.codeium/windsurf/memories/global_rules.md"
|
|
42
|
+
},
|
|
43
|
+
"cueme.proto.prefix": {
|
|
44
|
+
"vscode": [
|
|
45
|
+
"---",
|
|
46
|
+
"applyTo: '**'",
|
|
47
|
+
"---"
|
|
48
|
+
],
|
|
49
|
+
"windsurf": []
|
|
50
|
+
},
|
|
51
|
+
"cueme.proto.protocol_path": "~/path/to/protocol.md"
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Sentinel markers
|
|
56
|
+
|
|
57
|
+
Injected content is managed between these markers:
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
<!-- HUMAN_AGENT_PROTO_BEGIN -->
|
|
61
|
+
... managed content ...
|
|
62
|
+
<!-- HUMAN_AGENT_PROTO_END -->
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Notes:
|
|
66
|
+
|
|
67
|
+
- Markers are written in the standardized `HUMAN_*` form.
|
|
68
|
+
- Existing files that still use `HUAMN_*` markers are also recognized for replacement.
|
|
69
|
+
|
|
70
|
+
## Commands
|
|
71
|
+
|
|
72
|
+
### Render (print to stdout)
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
cueme proto <agent>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Generates and prints `final_proto` to stdout.
|
|
79
|
+
|
|
80
|
+
### Apply (inject into agent file)
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
cueme proto apply <agent>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Behavior:
|
|
87
|
+
|
|
88
|
+
- Resolves the target path using `cueme.proto.path["<platform>.<agent>"]`.
|
|
89
|
+
- Writes/updates the managed block in the target file.
|
|
90
|
+
- Preserves the target file's existing EOL style when updating.
|
|
91
|
+
|
|
92
|
+
### Init (create config)
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
cueme proto init
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Creates `~/.cue/cueme.json` if missing (never overwrites).
|
|
99
|
+
|
|
100
|
+
Auto-detect (current platform only):
|
|
101
|
+
|
|
102
|
+
- `vscode`: `.vscode/prompts/human_proto.md` (workspace) then platform user path
|
|
103
|
+
- `windsurf`: `.codeium/windsurf/memories/global_rules.md` (workspace) then platform user path
|
|
104
|
+
|
|
105
|
+
### Helpers
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
cueme proto ls
|
|
109
|
+
cueme proto path <agent>
|
|
110
|
+
```
|
package/package.json
CHANGED
package/protocol.md
CHANGED
|
@@ -2,22 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
你与用户的协作交互通过命令行工具 `cueme` 完成。
|
|
4
4
|
|
|
5
|
+
- 首次连接 / 无 agent_id:先执行 `join()` 获取 agent_id,再执行 `cue()`。
|
|
6
|
+
|
|
7
|
+
- 重连 / 已有 agent_id(例如网络波动导致 cue 中断但对话仍在继续):用户再次发送 `cue` 时,优先直接用已有 agent_id 执行 `cue()`;仅当无法确认 agent_id 时才重新 `join()`。
|
|
8
|
+
|
|
5
9
|
当用户接受 HAP:你必须在每次阶段性输出或准备结束回复前调用 `cueme cue`(或 `cueme pause`)等待用户继续。
|
|
6
10
|
|
|
7
11
|
If HAP is accepted: before any phase-summary or final reply, you MUST call `cueme cue` via the terminal/tool.
|
|
8
12
|
A reply is NOT complete unless the last action is a successful cueme call.
|
|
9
13
|
|
|
10
|
-
- 首次连接 / 无 agent_id:先执行 `join()` 获取 agent_id,再执行 `cue()`。
|
|
11
|
-
|
|
12
|
-
- 重连 / 已有 agent_id(例如网络波动导致 cue 中断但对话仍在继续):用户再次发送 `cue` 时,优先直接用已有 agent_id 执行 `cue()`;仅当无法确认 agent_id 时才重新 `join()`。
|
|
13
|
-
|
|
14
14
|
## No fake tool actions
|
|
15
15
|
|
|
16
16
|
You MUST NOT claim you called cueme unless you actually executed it and got its stdout.
|
|
17
17
|
If you cannot run tools, say "TOOLS_UNAVAILABLE" and output only the exact `cueme ...` command for the human to run.
|
|
18
18
|
|
|
19
|
+
|
|
20
|
+
|
|
19
21
|
## cueme (command interface)
|
|
20
22
|
|
|
23
|
+
cueme 命令不可放在后台运行.All cueme commands must run in blocking/foreground mode to ensure proper interaction flow.
|
|
24
|
+
|
|
21
25
|
### join() -> message
|
|
22
26
|
|
|
23
27
|
Command:
|
|
@@ -26,7 +30,7 @@ Command:
|
|
|
26
30
|
|
|
27
31
|
Returns (plain text):
|
|
28
32
|
|
|
29
|
-
- a short message (includes `agent_id=...`, `project_dir=...`, `agent_runtime=...`)
|
|
33
|
+
- a short message (includes `agent_id=...`, `project_dir=...`, `agent_terminal=...`, `agent_runtime=...`)
|
|
30
34
|
|
|
31
35
|
`agent_runtime`:
|
|
32
36
|
|
|
@@ -34,29 +38,30 @@ Returns (plain text):
|
|
|
34
38
|
- Format: lowercase, `_` only.
|
|
35
39
|
- Examples: `windsurf`, `cursor`, `vscode`, `claude_code`, `terminal`, `codex`, `kiro`, `opencode`.
|
|
36
40
|
|
|
37
|
-
###
|
|
41
|
+
### cue(agent_id: str, stdin: {prompt: str!, payload?: object | null}) -> text
|
|
38
42
|
|
|
39
|
-
Command:
|
|
40
|
-
|
|
41
|
-
- `cueme recall <hints>`
|
|
43
|
+
Command (requires stdin):
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
`cueme cue <agent_id> -`
|
|
44
46
|
|
|
45
|
-
-
|
|
47
|
+
Provide via stdin (tag-block envelope, MUST NOT be empty; bash/zsh use heredoc; PowerShell use here-string):
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
<cueme_prompt>
|
|
50
|
+
Your actual prompt text here
|
|
51
|
+
</cueme_prompt>
|
|
48
52
|
|
|
49
|
-
|
|
53
|
+
<cueme_payload>
|
|
54
|
+
{"type":"confirm","text":"Continue?"}
|
|
55
|
+
</cueme_payload>
|
|
50
56
|
|
|
51
|
-
|
|
57
|
+
Rules:
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
59
|
+
- `<cueme_prompt>` block required; content must be non-empty after trim.
|
|
60
|
+
- `<cueme_payload>` block optional; if present: JSON object or null. Blank content is treated as null.
|
|
61
|
+
- Only whitespace allowed outside these blocks.
|
|
62
|
+
- Legacy JSON envelope is not supported.
|
|
58
63
|
|
|
59
|
-
Tip: when you need clearer structured interaction, prefer `payload` (choice/confirm/form) over encoding structure in `prompt`.
|
|
64
|
+
Tip: when you need clearer structured interaction, prefer `payload` (choice/confirm/form) over encoding structure in `prompt`.
|
|
60
65
|
|
|
61
66
|
Returns:
|
|
62
67
|
|
|
@@ -69,24 +74,16 @@ Payload protocol (payload object):
|
|
|
69
74
|
- confirm: {"type":"confirm","text":"...","confirm_label":"Confirm","cancel_label":"Cancel"}
|
|
70
75
|
- form: {"type":"form","fields":[{"label":"...","kind":"text","options":["...",...],"allow_multiple":false}, ...]}
|
|
71
76
|
|
|
72
|
-
Minimal examples:
|
|
73
|
-
|
|
74
|
-
- choice: {"type":"choice","options":["Continue","Stop"]}
|
|
75
|
-
- confirm: {"type":"confirm","text":"Continue?"}
|
|
76
|
-
- form: {"type":"form","fields":[{"label":"Env","options":["prod","staging"]}]}
|
|
77
|
-
|
|
78
77
|
### pause(agent_id: str, prompt?: str) -> text
|
|
79
78
|
|
|
80
79
|
Command:
|
|
80
|
+
- `cueme pause <agent_id> [<prompt> | -]`
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
stdin envelope (when `-` is used): tag blocks (prompt only; tags must be alone on their line):
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
- If `-` is used, instructions are read from stdin.
|
|
88
|
-
- If stdin is empty, `pause` will use the default pause prompt.
|
|
84
|
+
<cueme_prompt>
|
|
85
|
+
...raw prompt text...
|
|
86
|
+
</cueme_prompt>
|
|
89
87
|
|
|
90
88
|
Returns:
|
|
91
|
-
|
|
92
89
|
- plain text (stdout)
|
package/src/cli.js
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
const { readAllStdin } = require('./io');
|
|
2
2
|
const { handleCommand } = require('./handler');
|
|
3
|
+
const { parseTagBlocksEnvelope } = require('./envelope');
|
|
4
|
+
const { protoApply, protoInit, protoLs, protoPath, protoRender } = require('./proto');
|
|
5
|
+
const pkg = require('../package.json');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
async function parseStdinTagBlocksOrExit({ parsed, allow_payload }) {
|
|
10
|
+
const raw = await readAllStdin();
|
|
11
|
+
const env = parseTagBlocksEnvelope(raw, { allow_payload });
|
|
12
|
+
if (!env.ok) {
|
|
13
|
+
process.stderr.write(env.error);
|
|
14
|
+
process.exitCode = 2;
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
parsed.prompt = env.prompt;
|
|
18
|
+
if (allow_payload !== false) {
|
|
19
|
+
parsed.payload = env.payload == null ? null : JSON.stringify(env.payload);
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
3
23
|
|
|
4
24
|
function parseArgs(argv) {
|
|
5
25
|
const args = argv.slice(2);
|
|
@@ -46,6 +66,25 @@ function extractTextFromResult(result) {
|
|
|
46
66
|
}
|
|
47
67
|
|
|
48
68
|
async function main() {
|
|
69
|
+
const first = process.argv[2];
|
|
70
|
+
if (first === '-v' || first === '--version') {
|
|
71
|
+
process.stdout.write(String(pkg.version || '') + '\n');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (first === '-p' || first === '--protocol') {
|
|
76
|
+
const protocolPath = path.join(__dirname, '..', 'protocol.md');
|
|
77
|
+
try {
|
|
78
|
+
const content = fs.readFileSync(protocolPath, 'utf8');
|
|
79
|
+
process.stdout.write(String(content || ''));
|
|
80
|
+
if (!String(content || '').endsWith('\n')) process.stdout.write('\n');
|
|
81
|
+
} catch {
|
|
82
|
+
process.stderr.write('error: failed to read protocol.md\n');
|
|
83
|
+
process.exitCode = 2;
|
|
84
|
+
}
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
49
88
|
const parsed = parseArgs(process.argv);
|
|
50
89
|
const sub = parsed._[0];
|
|
51
90
|
const pos = parsed._.slice(1);
|
|
@@ -56,17 +95,32 @@ async function main() {
|
|
|
56
95
|
'cueme',
|
|
57
96
|
'',
|
|
58
97
|
'Usage:',
|
|
98
|
+
' cueme -v|--version',
|
|
99
|
+
' cueme -p|--protocol',
|
|
100
|
+
' cueme proto <agent>',
|
|
101
|
+
' cueme proto apply <agent>',
|
|
102
|
+
' cueme proto init',
|
|
103
|
+
' cueme proto ls',
|
|
104
|
+
' cueme proto path <agent>',
|
|
59
105
|
' cueme join <agent_runtime>',
|
|
60
|
-
' cueme recall <hints>',
|
|
61
106
|
' cueme cue <agent_id> -',
|
|
62
107
|
' cueme pause <agent_id> [prompt|-]',
|
|
63
|
-
' cueme migrate',
|
|
64
108
|
'',
|
|
65
|
-
'Cue stdin
|
|
66
|
-
'
|
|
109
|
+
'Cue stdin envelope (tag blocks; tags must be alone on their line):',
|
|
110
|
+
' <cueme_prompt>',
|
|
111
|
+
' ...raw prompt text...',
|
|
112
|
+
' </cueme_prompt>',
|
|
113
|
+
' <cueme_payload>',
|
|
114
|
+
' ...JSON object or null...',
|
|
115
|
+
' </cueme_payload>',
|
|
116
|
+
'',
|
|
117
|
+
'Pause stdin envelope (tag blocks; tags must be alone on their line):',
|
|
118
|
+
' <cueme_prompt>',
|
|
119
|
+
' ...raw prompt text...',
|
|
120
|
+
' </cueme_prompt>',
|
|
67
121
|
'',
|
|
68
122
|
'Output:',
|
|
69
|
-
' - join/
|
|
123
|
+
' - join/cue/pause: plain text (stdout)',
|
|
70
124
|
].join('\n') + '\n'
|
|
71
125
|
);
|
|
72
126
|
return;
|
|
@@ -78,8 +132,8 @@ async function main() {
|
|
|
78
132
|
return;
|
|
79
133
|
}
|
|
80
134
|
|
|
81
|
-
if (parsed.agent_id != null || parsed.prompt != null
|
|
82
|
-
process.stderr.write('error: --agent_id/--prompt
|
|
135
|
+
if (parsed.agent_id != null || parsed.prompt != null) {
|
|
136
|
+
process.stderr.write('error: --agent_id/--prompt flags are not supported; use positional args\n');
|
|
83
137
|
process.exitCode = 2;
|
|
84
138
|
return;
|
|
85
139
|
}
|
|
@@ -94,14 +148,57 @@ async function main() {
|
|
|
94
148
|
parsed.agent_runtime = String(agentRuntime);
|
|
95
149
|
}
|
|
96
150
|
|
|
97
|
-
if (sub === '
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
151
|
+
if (sub === 'proto') {
|
|
152
|
+
const action = pos[0];
|
|
153
|
+
try {
|
|
154
|
+
if (!action) {
|
|
155
|
+
process.stderr.write('error: missing <agent>\n');
|
|
156
|
+
process.exitCode = 2;
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (action === 'ls') {
|
|
161
|
+
process.stdout.write(protoLs());
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (action === 'init') {
|
|
166
|
+
process.stdout.write(protoInit() + '\n');
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (action === 'apply') {
|
|
171
|
+
const agent = pos[1];
|
|
172
|
+
if (!agent) {
|
|
173
|
+
process.stderr.write('error: missing <agent>\n');
|
|
174
|
+
process.exitCode = 2;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
process.stdout.write(protoApply(String(agent)) + '\n');
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (action === 'path') {
|
|
182
|
+
const agent = pos[1];
|
|
183
|
+
if (!agent) {
|
|
184
|
+
process.stderr.write('error: missing <agent>\n');
|
|
185
|
+
process.exitCode = 2;
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
process.stdout.write(protoPath(String(agent)) + '\n');
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const agent = action;
|
|
193
|
+
const rendered = protoRender(String(agent));
|
|
194
|
+
process.stdout.write(rendered);
|
|
195
|
+
if (!String(rendered || '').endsWith('\n')) process.stdout.write('\n');
|
|
196
|
+
return;
|
|
197
|
+
} catch (err) {
|
|
198
|
+
process.stderr.write((err && err.message ? err.message : 'error: proto failed') + '\n');
|
|
101
199
|
process.exitCode = 2;
|
|
102
200
|
return;
|
|
103
201
|
}
|
|
104
|
-
parsed.hints = String(hints);
|
|
105
202
|
}
|
|
106
203
|
|
|
107
204
|
if (sub === 'cue') {
|
|
@@ -113,7 +210,7 @@ async function main() {
|
|
|
113
210
|
}
|
|
114
211
|
|
|
115
212
|
if (parsed.payload != null) {
|
|
116
|
-
process.stderr.write('error: --payload is not supported for cue. Use stdin
|
|
213
|
+
process.stderr.write('error: --payload is not supported for cue. Use stdin tag-blocks envelope.\n');
|
|
117
214
|
process.exitCode = 2;
|
|
118
215
|
return;
|
|
119
216
|
}
|
|
@@ -122,42 +219,19 @@ async function main() {
|
|
|
122
219
|
|
|
123
220
|
const promptPos = pos[1];
|
|
124
221
|
if (promptPos !== '-') {
|
|
125
|
-
process.stderr.write('error: cue requires stdin
|
|
222
|
+
process.stderr.write('error: cue requires stdin tag-blocks. Usage: cueme cue <agent_id> -\n');
|
|
126
223
|
process.exitCode = 2;
|
|
127
224
|
return;
|
|
128
225
|
}
|
|
129
226
|
|
|
130
227
|
if (pos.length > 2) {
|
|
131
|
-
process.stderr.write('error: cue only accepts <agent_id> - and stdin
|
|
132
|
-
process.exitCode = 2;
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const raw = await readAllStdin();
|
|
137
|
-
let env;
|
|
138
|
-
try {
|
|
139
|
-
env = JSON.parse(raw);
|
|
140
|
-
} catch {
|
|
141
|
-
process.stderr.write('error: stdin must be valid JSON (envelope: {"prompt": "...", "payload": {...}})\n');
|
|
142
|
-
process.exitCode = 2;
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (!env || typeof env !== 'object') {
|
|
147
|
-
process.stderr.write('error: stdin JSON must be an object\n');
|
|
228
|
+
process.stderr.write('error: cue only accepts <agent_id> - and stdin tag-blocks envelope.\n');
|
|
148
229
|
process.exitCode = 2;
|
|
149
230
|
return;
|
|
150
231
|
}
|
|
151
232
|
|
|
152
|
-
const
|
|
153
|
-
if (!
|
|
154
|
-
process.stderr.write('error: stdin JSON must include non-empty string field "prompt"\n');
|
|
155
|
-
process.exitCode = 2;
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
parsed.prompt = prompt;
|
|
160
|
-
parsed.payload = env.payload == null ? null : JSON.stringify(env.payload);
|
|
233
|
+
const ok = await parseStdinTagBlocksOrExit({ parsed, allow_payload: true });
|
|
234
|
+
if (!ok) return;
|
|
161
235
|
}
|
|
162
236
|
|
|
163
237
|
if (sub === 'pause') {
|
|
@@ -167,14 +241,25 @@ async function main() {
|
|
|
167
241
|
process.exitCode = 2;
|
|
168
242
|
return;
|
|
169
243
|
}
|
|
244
|
+
|
|
245
|
+
if (parsed.payload != null) {
|
|
246
|
+
process.stderr.write('error: --payload is not supported for pause. Use tag-blocks stdin or positional prompt.\n');
|
|
247
|
+
process.exitCode = 2;
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
170
251
|
parsed.agent_id = String(agentId);
|
|
171
252
|
|
|
172
253
|
const promptPos = pos[1];
|
|
173
254
|
if (promptPos === '-') {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
255
|
+
if (pos.length > 2) {
|
|
256
|
+
process.stderr.write('error: pause only accepts <agent_id> [prompt|-] (tag-blocks stdin when "-" is used).\n');
|
|
257
|
+
process.exitCode = 2;
|
|
258
|
+
return;
|
|
177
259
|
}
|
|
260
|
+
|
|
261
|
+
const ok = await parseStdinTagBlocksOrExit({ parsed, allow_payload: false });
|
|
262
|
+
if (!ok) return;
|
|
178
263
|
} else if (promptPos != null) {
|
|
179
264
|
parsed.prompt = String(promptPos);
|
|
180
265
|
}
|
package/src/envelope.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
function parseTagBlocksEnvelope(raw, opts = {}) {
|
|
2
|
+
const allowPayload = opts.allow_payload !== false;
|
|
3
|
+
const text = String(raw == null ? '' : raw);
|
|
4
|
+
const trimmed = text.trim();
|
|
5
|
+
if (!trimmed) {
|
|
6
|
+
// Fallback: use default prompt based on command type
|
|
7
|
+
// allowPayload === true means cue command, false means pause command
|
|
8
|
+
const defaultPrompt = allowPayload ? 'hi' : 'pause';
|
|
9
|
+
return { ok: true, prompt: defaultPrompt, payload: null };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (trimmed.startsWith('{')) {
|
|
13
|
+
return {
|
|
14
|
+
ok: false,
|
|
15
|
+
error:
|
|
16
|
+
'error: legacy JSON envelope is not supported. Use <cueme_prompt>...</cueme_prompt> and optional <cueme_payload>...</cueme_payload>\n',
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const promptOpen = '<cueme_prompt>';
|
|
21
|
+
const promptClose = '</cueme_prompt>';
|
|
22
|
+
const payloadOpen = '<cueme_payload>';
|
|
23
|
+
const payloadClose = '</cueme_payload>';
|
|
24
|
+
|
|
25
|
+
function findTagLineIndex(lines, tag) {
|
|
26
|
+
return lines.findIndex((l) => l.trim() === tag);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const normalized = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
30
|
+
const lines = normalized.split('\n');
|
|
31
|
+
|
|
32
|
+
const pOpenIdx = findTagLineIndex(lines, promptOpen);
|
|
33
|
+
const pCloseIdx = findTagLineIndex(lines, promptClose);
|
|
34
|
+
if (pOpenIdx < 0 || pCloseIdx < 0 || pCloseIdx <= pOpenIdx) {
|
|
35
|
+
return { ok: false, error: 'error: missing <cueme_prompt> block\n' };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const pre = lines.slice(0, pOpenIdx).join('\n');
|
|
39
|
+
if (pre.trim().length > 0) {
|
|
40
|
+
return { ok: false, error: 'error: only whitespace is allowed outside blocks\n' };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const promptText = lines.slice(pOpenIdx + 1, pCloseIdx).join('\n');
|
|
44
|
+
if (!promptText.trim()) {
|
|
45
|
+
return { ok: false, error: 'error: <cueme_prompt> content must be non-empty\n' };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const remainingLines = lines.slice(pCloseIdx + 1);
|
|
49
|
+
const remainingText = remainingLines.join('\n');
|
|
50
|
+
|
|
51
|
+
const rOpenIdx = findTagLineIndex(remainingLines, payloadOpen);
|
|
52
|
+
const rCloseIdx = findTagLineIndex(remainingLines, payloadClose);
|
|
53
|
+
|
|
54
|
+
let payload = null;
|
|
55
|
+
|
|
56
|
+
if (rOpenIdx >= 0 || rCloseIdx >= 0) {
|
|
57
|
+
if (!allowPayload) {
|
|
58
|
+
return { ok: false, error: 'error: <cueme_payload> is not supported for pause\n' };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (rOpenIdx < 0 || rCloseIdx < 0 || rCloseIdx <= rOpenIdx) {
|
|
62
|
+
return { ok: false, error: 'error: invalid <cueme_payload> block\n' };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const between = remainingLines.slice(0, rOpenIdx).join('\n');
|
|
66
|
+
const after = remainingLines.slice(rCloseIdx + 1).join('\n');
|
|
67
|
+
if (between.trim().length > 0 || after.trim().length > 0) {
|
|
68
|
+
return { ok: false, error: 'error: only whitespace is allowed outside blocks\n' };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const payloadRaw = remainingLines.slice(rOpenIdx + 1, rCloseIdx).join('\n').trim();
|
|
72
|
+
if (!payloadRaw || payloadRaw === 'null') {
|
|
73
|
+
payload = null;
|
|
74
|
+
} else {
|
|
75
|
+
let parsed;
|
|
76
|
+
try {
|
|
77
|
+
parsed = JSON.parse(payloadRaw);
|
|
78
|
+
} catch {
|
|
79
|
+
return { ok: false, error: 'error: <cueme_payload> must be JSON object or null\n' };
|
|
80
|
+
}
|
|
81
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
82
|
+
return { ok: false, error: 'error: <cueme_payload> must be JSON object or null\n' };
|
|
83
|
+
}
|
|
84
|
+
payload = parsed;
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
if (remainingText.trim().length > 0) {
|
|
88
|
+
return { ok: false, error: 'error: only whitespace is allowed outside blocks\n' };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { ok: true, prompt: promptText, payload };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = { parseTagBlocksEnvelope };
|