@plannotator/pi-extension 0.27.5 → 0.27.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 +2 -0
- package/generated/ai/providers/child-io.ts +93 -0
- package/generated/ai/providers/codex-app-server.ts +38 -2
- package/generated/ai/providers/pi-sdk-node.ts +46 -4
- package/generated/ai/providers/pi-sdk.ts +54 -6
- package/generated/annotate-args.ts +25 -3
- package/generated/bridge-script.ts +4590 -0
- package/generated/call-flow-types.ts +18 -5
- package/generated/config.ts +2 -1
- package/generated/jj-core.ts +51 -3
- package/generated/live-probe.ts +133 -0
- package/generated/live-proxy-core.ts +555 -0
- package/generated/live-proxy-node.ts +419 -0
- package/generated/prompts.ts +1 -0
- package/generated/review-core.ts +11 -0
- package/index.ts +86 -15
- package/package.json +6 -1
- package/plannotator-browser.ts +22 -1
- package/plannotator.html +3 -3
- package/review-editor.html +3 -3
- package/server/serverAnnotate-live.test.ts +273 -0
- package/server/serverAnnotate.ts +146 -10
- package/skills/plannotator/SKILL.md +185 -0
- package/skills/plannotator/agents/openai.yaml +9 -0
package/README.md
CHANGED
|
@@ -270,6 +270,8 @@ Plannotator does not send `Continue with the approved plan`, enter its executing
|
|
|
270
270
|
|
|
271
271
|
Run `/plannotator-annotate <file.md>` to open any markdown file in the annotation UI. Useful for reviewing documentation or design specs with the agent.
|
|
272
272
|
|
|
273
|
+
URL targets work too. A loopback `http` URL that answers with an HTML page (a running dev app, e.g. `http://localhost:5173`) opens **live**: the app is served through a local reverse proxy and annotated in place, with HMR and WebSockets passed through. `--static` forces the classic markdown conversion; `--app` requires a live session and errors instead of falling back. Live sessions are unavailable in remote mode (`PLANNOTATOR_REMOTE`).
|
|
274
|
+
|
|
273
275
|
### Annotate last message
|
|
274
276
|
|
|
275
277
|
Run `/plannotator-last` to annotate the agent's most recent response. The message opens in the annotation UI where you can highlight text, add comments, and send structured feedback back to the agent.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// @generated — DO NOT EDIT. Source: packages/ai/providers/child-io.ts
|
|
2
|
+
/**
|
|
3
|
+
* Stdio guards for the JSONL/JSON-RPC child processes the AI providers drive.
|
|
4
|
+
*
|
|
5
|
+
* Both `PiProcessNode` (pi-sdk-node.ts) and `CodexAppServerProcess`
|
|
6
|
+
* (codex-app-server.ts) talk to a nested agent over pipes, and a pipe can
|
|
7
|
+
* break at any instant — the child exits, is killed, or closes stdin while a
|
|
8
|
+
* command is in flight. Two Node behaviors turn that ordinary condition into
|
|
9
|
+
* a host-killing crash:
|
|
10
|
+
*
|
|
11
|
+
* 1. `stream.write()` on a broken pipe reports `EPIPE` either synchronously
|
|
12
|
+
* (throw) or asynchronously (an `error` event on the stream), and which
|
|
13
|
+
* one you get is a timing race. A `destroyed` check before the write
|
|
14
|
+
* cannot close that race: the child can close the pipe between the check
|
|
15
|
+
* and the write.
|
|
16
|
+
* 2. An `error` event on a Node stream (or on the `ChildProcess` itself)
|
|
17
|
+
* with NO listener is re-thrown as an `uncaughtException`. Inside an
|
|
18
|
+
* embedded extension that is not "the provider failed" — it terminates
|
|
19
|
+
* the HOST agent process.
|
|
20
|
+
*
|
|
21
|
+
* That is issue #1378: a Plannotator plan review opened from Pi on Windows
|
|
22
|
+
* took the whole Pi host down with `write EPIPE` out of `PiProcessNode.send()`.
|
|
23
|
+
* Windows only made it likelier to land on the async path during teardown;
|
|
24
|
+
* the mechanism is platform-independent.
|
|
25
|
+
*
|
|
26
|
+
* The contract these helpers enforce: a broken pipe is a PROVIDER failure.
|
|
27
|
+
* It never escalates past the provider, it is reported exactly like any other
|
|
28
|
+
* process end (in-flight requests reject, listeners see the process end), and
|
|
29
|
+
* the provider is left dead so the next query re-spawns it.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import type { ChildProcess } from "node:child_process";
|
|
33
|
+
|
|
34
|
+
/** Normalize an unknown thrown/emitted value to an Error. */
|
|
35
|
+
export function toChildError(err: unknown): Error {
|
|
36
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Attach `error` listeners to the child process and every piped stream so a
|
|
41
|
+
* broken pipe can never reach `uncaughtException`.
|
|
42
|
+
*
|
|
43
|
+
* Call this immediately after `spawn()` returns — before awaiting the spawn
|
|
44
|
+
* handshake — so a stream that fails during startup is already covered. The
|
|
45
|
+
* spawn handshake may register its own one-shot `error` listener; Node allows
|
|
46
|
+
* several, and `onFailure` is expected to be idempotent.
|
|
47
|
+
*/
|
|
48
|
+
export function guardChildStreams(
|
|
49
|
+
proc: ChildProcess,
|
|
50
|
+
label: string,
|
|
51
|
+
onFailure: (error: Error) => void,
|
|
52
|
+
): void {
|
|
53
|
+
const report = (stream: string) => (err: unknown) => {
|
|
54
|
+
onFailure(new Error(`${label} ${stream} failed: ${toChildError(err).message}`));
|
|
55
|
+
};
|
|
56
|
+
proc.on("error", report("process"));
|
|
57
|
+
proc.stdin?.on("error", report("stdin"));
|
|
58
|
+
proc.stdout?.on("error", report("stdout"));
|
|
59
|
+
proc.stderr?.on("error", report("stderr"));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Write one already-newline-terminated line to a child's stdin.
|
|
64
|
+
*
|
|
65
|
+
* Returns the Error the write failed with, or `null` when the bytes were
|
|
66
|
+
* accepted by the stream. Failures that only surface later (the write
|
|
67
|
+
* callback, or an `error` event covered by {@link guardChildStreams}) are
|
|
68
|
+
* reported through `onAsyncFailure` instead, so BOTH the sync-throw and the
|
|
69
|
+
* async-event paths end up at the caller's failure handling.
|
|
70
|
+
*/
|
|
71
|
+
export function writeChildLine(
|
|
72
|
+
proc: ChildProcess | null,
|
|
73
|
+
line: string,
|
|
74
|
+
label: string,
|
|
75
|
+
onAsyncFailure: (error: Error) => void,
|
|
76
|
+
): Error | null {
|
|
77
|
+
const stdin = proc?.stdin;
|
|
78
|
+
if (!stdin || stdin.destroyed || stdin.writableEnded) {
|
|
79
|
+
return new Error(`${label} stdin is closed`);
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
stdin.write(line, (err) => {
|
|
83
|
+
if (err) {
|
|
84
|
+
onAsyncFailure(
|
|
85
|
+
new Error(`${label} stdin write failed: ${toChildError(err).message}`),
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
} catch (err) {
|
|
90
|
+
return new Error(`${label} stdin write failed: ${toChildError(err).message}`);
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
@@ -44,8 +44,10 @@ import {
|
|
|
44
44
|
killWindowsProcessTree,
|
|
45
45
|
resolveWindowsCommandShim,
|
|
46
46
|
} from "./command-path.ts";
|
|
47
|
+
import { guardChildStreams, writeChildLine } from "./child-io.ts";
|
|
47
48
|
|
|
48
49
|
const PROVIDER_NAME = "codex-sdk";
|
|
50
|
+
const CODEX_PROCESS_LABEL = "Codex app-server";
|
|
49
51
|
const DEFAULT_MODEL = "gpt-5.6-sol";
|
|
50
52
|
const CLIENT_NAME = "plannotator";
|
|
51
53
|
/** Kill an idle app-server process after this long with no query. */
|
|
@@ -335,6 +337,10 @@ class CodexAppServerProcess {
|
|
|
335
337
|
}
|
|
336
338
|
|
|
337
339
|
this.proc = proc;
|
|
340
|
+
// Cover every pipe BEFORE the spawn handshake: an `error` event on a child
|
|
341
|
+
// stream with no listener becomes an uncaughtException and kills the host
|
|
342
|
+
// process, not just this provider (#1378).
|
|
343
|
+
guardChildStreams(proc, CODEX_PROCESS_LABEL, (error) => this.failProcess(error));
|
|
338
344
|
proc.once("exit", () => {
|
|
339
345
|
this.handleProcessEnd(new Error("Codex app-server exited unexpectedly"));
|
|
340
346
|
});
|
|
@@ -378,6 +384,23 @@ class CodexAppServerProcess {
|
|
|
378
384
|
this.send({ method: "initialized", params: {} });
|
|
379
385
|
}
|
|
380
386
|
|
|
387
|
+
/**
|
|
388
|
+
* A pipe to the child broke: resolve it as a provider failure and reap the
|
|
389
|
+
* child, leaving `alive` false so the next query re-spawns (#1378).
|
|
390
|
+
*/
|
|
391
|
+
private failProcess(error: Error): void {
|
|
392
|
+
const proc = this.proc;
|
|
393
|
+
this.startPromise = null;
|
|
394
|
+
this.handleProcessEnd(error);
|
|
395
|
+
if (proc) {
|
|
396
|
+
try {
|
|
397
|
+
if (!killWindowsProcessTree(proc.pid)) proc.kill();
|
|
398
|
+
} catch {
|
|
399
|
+
// Already gone.
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
381
404
|
private handleProcessEnd(error: Error): void {
|
|
382
405
|
if (!this.proc && this.pendingRequests.size === 0) return;
|
|
383
406
|
this._alive = false;
|
|
@@ -439,9 +462,22 @@ class CodexAppServerProcess {
|
|
|
439
462
|
}
|
|
440
463
|
}
|
|
441
464
|
|
|
465
|
+
/**
|
|
466
|
+
* Send a JSON-RPC message without waiting for a response.
|
|
467
|
+
*
|
|
468
|
+
* A closed or broken stdin is a provider failure, never a throw at the
|
|
469
|
+
* caller and never an unhandled stream error: both the synchronous throw
|
|
470
|
+
* and the asynchronous `error`/write-callback paths land in failProcess,
|
|
471
|
+
* which rejects anything in flight.
|
|
472
|
+
*/
|
|
442
473
|
send(message: RpcMessage): void {
|
|
443
|
-
|
|
444
|
-
|
|
474
|
+
const error = writeChildLine(
|
|
475
|
+
this.proc,
|
|
476
|
+
`${JSON.stringify(message)}\n`,
|
|
477
|
+
CODEX_PROCESS_LABEL,
|
|
478
|
+
(err) => this.failProcess(err),
|
|
479
|
+
);
|
|
480
|
+
if (error) this.failProcess(error);
|
|
445
481
|
}
|
|
446
482
|
|
|
447
483
|
sendAndWait(message: RpcMessage, timeoutMs = RPC_TIMEOUT_MS): Promise<RpcMessage> {
|
|
@@ -25,11 +25,13 @@ import {
|
|
|
25
25
|
killWindowsProcessTree,
|
|
26
26
|
resolveWindowsCommandShim,
|
|
27
27
|
} from "./command-path.ts";
|
|
28
|
+
import { guardChildStreams, writeChildLine } from "./child-io.ts";
|
|
28
29
|
|
|
29
30
|
// Re-export mapPiEvent from shared (runtime-agnostic)
|
|
30
31
|
export { mapPiEvent } from "./pi-events.ts";
|
|
31
32
|
|
|
32
33
|
const PROVIDER_NAME = "pi-sdk";
|
|
34
|
+
const PI_PROCESS_LABEL = "Pi process";
|
|
33
35
|
|
|
34
36
|
// ---------------------------------------------------------------------------
|
|
35
37
|
// JSONL subprocess wrapper (Node.js)
|
|
@@ -37,7 +39,8 @@ const PROVIDER_NAME = "pi-sdk";
|
|
|
37
39
|
|
|
38
40
|
type EventListener = (event: Record<string, unknown>) => void;
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
/** Exported for the stdio-failure regression tests (#1378). */
|
|
43
|
+
export class PiProcessNode {
|
|
41
44
|
private proc: ChildProcess | null = null;
|
|
42
45
|
private listeners: EventListener[] = [];
|
|
43
46
|
private pendingRequests = new Map<
|
|
@@ -62,9 +65,12 @@ class PiProcessNode {
|
|
|
62
65
|
let proc: ChildProcess;
|
|
63
66
|
try {
|
|
64
67
|
const [file, ...args] = command;
|
|
68
|
+
// stderr is "ignore", not "pipe": we never read it, and an
|
|
69
|
+
// un-drained stderr pipe deadlocks the child once its buffer fills
|
|
70
|
+
// (same reasoning as codex-app-server.ts).
|
|
65
71
|
proc = spawn(file, args, {
|
|
66
72
|
cwd,
|
|
67
|
-
stdio: ["pipe", "pipe", "
|
|
73
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
68
74
|
});
|
|
69
75
|
} catch (err) {
|
|
70
76
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -73,6 +79,10 @@ class PiProcessNode {
|
|
|
73
79
|
}
|
|
74
80
|
|
|
75
81
|
this.proc = proc;
|
|
82
|
+
// Cover every pipe BEFORE the spawn handshake: an `error` event on a
|
|
83
|
+
// child stream with no listener becomes an uncaughtException and kills
|
|
84
|
+
// the host agent process, not just this provider (#1378).
|
|
85
|
+
guardChildStreams(proc, PI_PROCESS_LABEL, (error) => this.failProcess(error));
|
|
76
86
|
proc.once("exit", () => {
|
|
77
87
|
this.handleProcessEnd(new Error("Pi process exited unexpectedly"));
|
|
78
88
|
});
|
|
@@ -99,6 +109,24 @@ class PiProcessNode {
|
|
|
99
109
|
});
|
|
100
110
|
}
|
|
101
111
|
|
|
112
|
+
/**
|
|
113
|
+
* A pipe to the child broke. Resolve it as a provider failure: reject
|
|
114
|
+
* everything in flight, tell listeners the process ended, and reap the
|
|
115
|
+
* child so it cannot linger with an unusable RPC channel. `alive` flips
|
|
116
|
+
* false, so the next query re-spawns a fresh process.
|
|
117
|
+
*/
|
|
118
|
+
private failProcess(error: Error): void {
|
|
119
|
+
const proc = this.proc;
|
|
120
|
+
this.handleProcessEnd(error);
|
|
121
|
+
if (proc) {
|
|
122
|
+
try {
|
|
123
|
+
if (!killWindowsProcessTree(proc.pid)) proc.kill();
|
|
124
|
+
} catch {
|
|
125
|
+
// Already gone.
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
102
130
|
private handleProcessEnd(error: Error): void {
|
|
103
131
|
if (!this.proc && this.pendingRequests.size === 0) return;
|
|
104
132
|
|
|
@@ -153,9 +181,23 @@ class PiProcessNode {
|
|
|
153
181
|
}
|
|
154
182
|
}
|
|
155
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Send a command without waiting for a response.
|
|
186
|
+
*
|
|
187
|
+
* A closed or broken stdin is a provider failure, never a throw at the
|
|
188
|
+
* caller and never an unhandled stream error: both the synchronous throw
|
|
189
|
+
* and the asynchronous `error`/write-callback paths land in failProcess,
|
|
190
|
+
* which rejects anything in flight (including the `sendAndWait` request
|
|
191
|
+
* this call may be carrying).
|
|
192
|
+
*/
|
|
156
193
|
send(command: Record<string, unknown>): void {
|
|
157
|
-
|
|
158
|
-
|
|
194
|
+
const error = writeChildLine(
|
|
195
|
+
this.proc,
|
|
196
|
+
`${JSON.stringify(command)}\n`,
|
|
197
|
+
PI_PROCESS_LABEL,
|
|
198
|
+
(err) => this.failProcess(err),
|
|
199
|
+
);
|
|
200
|
+
if (error) this.failProcess(error);
|
|
159
201
|
}
|
|
160
202
|
|
|
161
203
|
sendAndWait(
|
|
@@ -58,11 +58,14 @@ class PiProcess {
|
|
|
58
58
|
"rpc",
|
|
59
59
|
];
|
|
60
60
|
try {
|
|
61
|
+
// stderr is "ignore", not "pipe": we never read it, and an
|
|
62
|
+
// un-drained stderr pipe deadlocks the child once its buffer fills
|
|
63
|
+
// (same reasoning as codex-app-server.ts).
|
|
61
64
|
this.proc = Bun.spawn(command, {
|
|
62
65
|
cwd,
|
|
63
66
|
stdin: "pipe",
|
|
64
67
|
stdout: "pipe",
|
|
65
|
-
stderr: "
|
|
68
|
+
stderr: "ignore",
|
|
66
69
|
});
|
|
67
70
|
} catch (err) {
|
|
68
71
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -78,6 +81,22 @@ class PiProcess {
|
|
|
78
81
|
});
|
|
79
82
|
}
|
|
80
83
|
|
|
84
|
+
/**
|
|
85
|
+
* A pipe to the child broke: resolve it as a provider failure and reap the
|
|
86
|
+
* child, leaving `alive` false so the next query re-spawns.
|
|
87
|
+
*/
|
|
88
|
+
private failProcess(error: Error): void {
|
|
89
|
+
const proc = this.proc;
|
|
90
|
+
this.handleProcessEnd(error);
|
|
91
|
+
if (proc) {
|
|
92
|
+
try {
|
|
93
|
+
if (!killWindowsProcessTree(proc.pid)) proc.kill();
|
|
94
|
+
} catch {
|
|
95
|
+
// Already gone.
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
81
100
|
private handleProcessEnd(error: Error): void {
|
|
82
101
|
if (!this.proc && this.pendingRequests.size === 0) return;
|
|
83
102
|
|
|
@@ -144,13 +163,42 @@ class PiProcess {
|
|
|
144
163
|
}
|
|
145
164
|
}
|
|
146
165
|
|
|
147
|
-
/**
|
|
166
|
+
/**
|
|
167
|
+
* Send a command without waiting for a response.
|
|
168
|
+
*
|
|
169
|
+
* The write is guarded for the same reason the Node variant's is (#1378):
|
|
170
|
+
* the child can close the pipe between the liveness check and the write,
|
|
171
|
+
* and a raw EPIPE escaping here would surface as a synchronous throw at
|
|
172
|
+
* the caller (or an unhandled rejection out of flush()) instead of a
|
|
173
|
+
* provider failure. Failing the process rejects anything in flight.
|
|
174
|
+
*/
|
|
148
175
|
send(command: Record<string, unknown>): void {
|
|
149
|
-
|
|
176
|
+
const stdin = this.proc?.stdin;
|
|
177
|
+
if (!stdin || typeof stdin === "number") {
|
|
178
|
+
this.failProcess(new Error("Pi process stdin is closed"));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
150
181
|
// Bun.spawn stdin is a FileSink with .write(), not a WritableStream
|
|
151
|
-
const sink =
|
|
152
|
-
|
|
153
|
-
|
|
182
|
+
const sink = stdin as { write(data: string): void; flush(): unknown };
|
|
183
|
+
try {
|
|
184
|
+
sink.write(`${JSON.stringify(command)}\n`);
|
|
185
|
+
const flushed = sink.flush();
|
|
186
|
+
if (flushed && typeof (flushed as Promise<number>).then === "function") {
|
|
187
|
+
(flushed as Promise<number>).catch((err: unknown) => {
|
|
188
|
+
this.failProcess(
|
|
189
|
+
new Error(
|
|
190
|
+
`Pi process stdin write failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
191
|
+
),
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
} catch (err) {
|
|
196
|
+
this.failProcess(
|
|
197
|
+
new Error(
|
|
198
|
+
`Pi process stdin write failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
199
|
+
),
|
|
200
|
+
);
|
|
201
|
+
}
|
|
154
202
|
}
|
|
155
203
|
|
|
156
204
|
/** Send a command and wait for the correlated response. */
|
|
@@ -47,6 +47,10 @@ export interface ParsedAnnotateArgs {
|
|
|
47
47
|
renderHtml: boolean;
|
|
48
48
|
renderMarkdown: boolean;
|
|
49
49
|
noJina: boolean;
|
|
50
|
+
/** --app: force a live app session (recognized only with `liveFlags`). */
|
|
51
|
+
app: boolean;
|
|
52
|
+
/** --static: force the classic conversion pipeline (only with `liveFlags`). */
|
|
53
|
+
static: boolean;
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
type Segment = { type: "ws" | "tok"; text: string };
|
|
@@ -60,9 +64,27 @@ const FLAG_MAP = {
|
|
|
60
64
|
"--no-jina": "noJina",
|
|
61
65
|
} as const satisfies Record<string, keyof Omit<ParsedAnnotateArgs, "filePath" | "rawFilePath">>;
|
|
62
66
|
|
|
63
|
-
|
|
67
|
+
/** Live-mode flags, recognized only where the host actually supports live
|
|
68
|
+
* app sessions (`liveFlags: true` — Pi today). A host that cannot act on
|
|
69
|
+
* --app must NOT silently strip it: leaving the token in the path keeps the
|
|
70
|
+
* legacy "File not found: --app ..." error, which is honest about the flag
|
|
71
|
+
* being unsupported there. */
|
|
72
|
+
const LIVE_FLAG_MAP = {
|
|
73
|
+
"--app": "app",
|
|
74
|
+
"--static": "static",
|
|
75
|
+
} as const satisfies Record<string, keyof Omit<ParsedAnnotateArgs, "filePath" | "rawFilePath">>;
|
|
76
|
+
|
|
77
|
+
export interface ParseAnnotateArgsOptions {
|
|
78
|
+
/** Recognize --app / --static (hosts with live app annotation support). */
|
|
79
|
+
liveFlags?: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function parseAnnotateArgs(raw: string, opts?: ParseAnnotateArgsOptions): ParsedAnnotateArgs {
|
|
64
83
|
const s = (raw ?? "").trim();
|
|
65
|
-
const flags = { gate: false, json: false, hook: false, renderHtml: false, renderMarkdown: false, noJina: false };
|
|
84
|
+
const flags = { gate: false, json: false, hook: false, renderHtml: false, renderMarkdown: false, noJina: false, app: false, static: false };
|
|
85
|
+
const flagMap: Record<string, keyof typeof flags> = opts?.liveFlags
|
|
86
|
+
? { ...FLAG_MAP, ...LIVE_FLAG_MAP }
|
|
87
|
+
: { ...FLAG_MAP };
|
|
66
88
|
|
|
67
89
|
const segments: Segment[] = [];
|
|
68
90
|
for (let i = 0; i < s.length;) {
|
|
@@ -76,7 +98,7 @@ export function parseAnnotateArgs(raw: string): ParsedAnnotateArgs {
|
|
|
76
98
|
for (let j = 0; j < segments.length; j++) {
|
|
77
99
|
const seg = segments[j];
|
|
78
100
|
if (seg.type !== "tok") continue;
|
|
79
|
-
const key =
|
|
101
|
+
const key = flagMap[seg.text];
|
|
80
102
|
if (!key) continue;
|
|
81
103
|
|
|
82
104
|
flags[key] = true;
|