@openlines/opl-pi-sht 0.3.0 → 0.3.1
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 +13 -5
- package/extensions/opl-browser/package-lock.json +3 -3
- package/extensions/opl-configurator/README.md +1 -1
- package/extensions/opl-configurator/configurator.ts +3 -0
- package/extensions/opl-footer/configure.ts +3 -1
- package/extensions/opl-input/configure.ts +3 -1
- package/extensions/opl-modes/README.md +6 -4
- package/extensions/opl-modes/index.ts +39 -11
- package/extensions/opl-webaccess/package-lock.json +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,13 @@
|
|
|
14
14
|
|
|
15
15
|
A portable collection of various Pi coding agent extensions. Repository directories and config files use `opl-`; established Pi-facing commands and tool names stay compatible.
|
|
16
16
|
|
|
17
|
+
>
|
|
18
|
+
> **Validation**: Pi `1.4.0` passes strict typechecking and 546 tests; the `0.87.0` floor passes strict typechecking and 544 tests, with two >=1.2 host lifecycle cases skipped. All 12 extensions load on both versions. Tests cover resource-loader tab replacement on both and custom/user mode runs on Pi `1.4.0` with synthetic responses.
|
|
19
|
+
>
|
|
20
|
+
> ### v0.3.1 - mode and configurator fixes
|
|
21
|
+
>
|
|
22
|
+
> Plan paths now use the session directory, OFF and execute tool policies apply consistently, and named plans use the configured plan model. Configurator tabs refresh on reload and its failure screen closes with Escape. [CHANGELOG](CHANGELOG.md).
|
|
23
|
+
>
|
|
17
24
|
> ### BIG A👀️ UPDATE
|
|
18
25
|
>
|
|
19
26
|
> ### v0.3.0 - security hardening, fixes, and extension enhancements
|
|
@@ -29,11 +36,8 @@ A portable collection of various Pi coding agent extensions. Repository director
|
|
|
29
36
|
> - **Breaking behavior changes**: loopback access is now opt-in (`allowLoopback: false`), and the entire `64:ff9b:1::/48` local-use translation prefix is blocked even with network opt-ins enabled. The well-known `64:ff9b::/96` prefix still follows embedded-IPv4 policy. Stale browser selections now return an error requiring explicit recovery. [Network policy](extensions/opl-browser/README.md#network-policy), [Page selection](extensions/opl-browser/README.md#extension-features).
|
|
30
37
|
> - **Added**: separate, extensible [`opl-configurator`](extensions/opl-configurator/README.md#extension-features), and [`compactions`](extensions/opl-footer/README.md#available-segments) segment in `opl-footer`.
|
|
31
38
|
>
|
|
32
|
-
> See [CHANGELOG](CHANGELOG.md) for more details.
|
|
33
|
-
>
|
|
34
39
|
> Having issues? [Opl a ticket](https://github.com/linellazatin/opl-pi-sht/issues/new).
|
|
35
40
|
>
|
|
36
|
-
> **Validation**: all 12 extensions pass loader checks with Pi `1.1.0`, locked Pi `1.0.0` and the declared `0.87.0` floor.
|
|
37
41
|
|
|
38
42
|
## Installation
|
|
39
43
|
|
|
@@ -65,7 +69,7 @@ npx playwright install chromium
|
|
|
65
69
|
{
|
|
66
70
|
"packages": [
|
|
67
71
|
{
|
|
68
|
-
"source": "npm:@openlines/opl-pi-sht@0.3.
|
|
72
|
+
"source": "npm:@openlines/opl-pi-sht@0.3.1",
|
|
69
73
|
"extensions": ["extensions/opl-init/index.ts", "extensions/opl-todo/index.ts"]
|
|
70
74
|
}
|
|
71
75
|
]
|
|
@@ -245,6 +249,10 @@ A Pi package (npm or Git) still needs the one-time `npx playwright install chrom
|
|
|
245
249
|
npm test
|
|
246
250
|
```
|
|
247
251
|
|
|
248
|
-
`npm run test:shared` runs the cross-extension checks: agent-directory resolution, parity of the duplicated host and git guards, installer, package metadata, and the `globalThis` seams between `opl-modes`, `opl-footer`, `opl-input` and `opl-configurator`. `npm run typecheck` runs `tsc` in strict mode over all 90 extension sources and is separate from `npm test`. Release validation uses `npm ci`, strict typechecking and the full suite; package tests exercise npm packing to exclude live configs. Run one extension suite with `npm run test:opl-<name>` for `browser`, `configurator`, `footer`, `guardian`, `init`, `input`, `modes`, `questionnaire`, `todo`, `webaccess`, `simplebench`, or `ctxtrim`. `npm run test:pi-host` uses the Pi build this machine actually runs (the installed managed release, falling back to the `>=0.87.0` devDependency when none is present) and its real extension loader to load every entrypoint and assert no loader errors. Every helper, functional, and selected-entrypoint smoke check uses Bun's named-test reporter; output includes per-test status, timings, and pass/fail totals. Functional tests cover deterministic helpers where practical; smoke tests bundle entrypoints and parse config. They do not test live TUI behavior, provider credentials, network access, or PDF extraction.
|
|
252
|
+
`npm run test:shared` runs the cross-extension checks: agent-directory resolution, parity of the duplicated host and git guards, installer, package metadata, and the `globalThis` seams between `opl-modes`, `opl-footer`, `opl-input` and `opl-configurator`. `npm run typecheck` runs `tsc` in strict mode over all 90 extension sources and is separate from `npm test`. Release validation uses `npm ci`, strict typechecking and the full suite; package tests exercise npm packing to exclude live configs. Run one extension suite with `npm run test:opl-<name>` for `browser`, `configurator`, `footer`, `guardian`, `init`, `input`, `modes`, `questionnaire`, `todo`, `webaccess`, `simplebench`, or `ctxtrim`. `npm run test:pi-host` uses the Pi build this machine actually runs (the installed managed release, falling back to the `>=0.87.0` devDependency when none is present) and its real extension loader to load every entrypoint and assert no loader errors. On Pi >=1.2.0 it also checks custom/user `before_agent_start` events, busy-during-start semantics and mode-prompt persistence after real file-tool execution, using isolated fixtures and synthetic model responses; those host-specific cases are skipped on older Pi. Every helper, functional, and selected-entrypoint smoke check uses Bun's named-test reporter; output includes per-test status, timings, and pass/fail totals. Functional tests cover deterministic helpers where practical; smoke tests bundle entrypoints and parse config. They do not test live TUI behavior, provider credentials, network access, or PDF extraction.
|
|
253
|
+
|
|
254
|
+
Release tags must match `package.json` as `v<version>`. The release workflow publishes to npm before creating the GitHub release. If npm publication succeeds but GitHub release creation fails, verify the published version and create the GitHub release separately rather than republishing the same npm version.
|
|
255
|
+
|
|
256
|
+
CI runs strict typechecking and the suite against the committed lockfile, the Pi `0.87.0` floor, and a rolling `current` job that resolves `pi-coding-agent`, `pi-tui` and `pi-ai` from `~1.4.0`. The rolling job ignores the lockfile for range resolution, reports exact resolved versions, disables managed-install discovery and requires the loader to use its selected devDependency. The tilde range selects stable releases >=1.4.0 and <1.5.0, so only 1.4 patches are followed, not later minor releases or every version in that range; patches may still regress extension behavior, so a green run is evidence for its resolved versions, not a blanket compatibility promise. Keep the locked job for reproducibility. New upstream releases are checked on the next push or pull request, not automatically at publication.
|
|
249
257
|
|
|
250
258
|
For agents/models testing extensions in a running Pi session, follow the [live-test instructions](tests/support/opl-live-test.md): one extension per phase, safe tool probes, operator-assisted UI checks, and separate reporting of live observations, automated coverage, and unverified behavior.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opl-browser",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "opl-browser",
|
|
9
|
-
"version": "
|
|
10
|
-
"license": "
|
|
9
|
+
"version": "0.2.0",
|
|
10
|
+
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@mozilla/readability": "^0.6.0",
|
|
13
13
|
"linkedom": "^0.18.13",
|
|
@@ -28,7 +28,7 @@ const tab = {
|
|
|
28
28
|
- `label` is the short text in the tab bar.
|
|
29
29
|
- `create(api)` returns a component with the same contract as `ctx.ui.custom`'s return value. `api` carries `{ ctx, tui, theme, keybindings, done }`.
|
|
30
30
|
|
|
31
|
-
The shell reads the registry lazily when `/configurator` runs, so extension load order does not matter. Entries are validated rather than trusted: malformed entries and duplicate ids or hotkeys are dropped, and a tab whose `create` throws is skipped without taking the whole configurator down.
|
|
31
|
+
The shell reads the registry lazily when `/configurator` runs, so extension load order does not matter. Entries are validated rather than trusted: malformed entries and duplicate ids or hotkeys are dropped, and a tab whose `create` throws is skipped without taking the whole configurator down. The footer and input replace their own tab entries on reload, so their factories use the current extension instances. If every tab fails to load, Escape closes the diagnostic screen.
|
|
32
32
|
|
|
33
33
|
## Keyboard model
|
|
34
34
|
|
|
@@ -95,6 +95,9 @@ export async function showConfigurator(ctx: ExtensionContext): Promise<void> {
|
|
|
95
95
|
return {
|
|
96
96
|
render: (width: number) => [truncateToWidth(theme.fg("dim", "No instrumented extensions could be loaded."), width)],
|
|
97
97
|
invalidate() {},
|
|
98
|
+
handleInput(data: string) {
|
|
99
|
+
if (matchesKey(data, Key.escape)) done(undefined);
|
|
100
|
+
},
|
|
98
101
|
};
|
|
99
102
|
}
|
|
100
103
|
|
|
@@ -68,7 +68,9 @@ export function registerFooterConfiguratorTab(onSaved: (ctx: ExtensionContext) =
|
|
|
68
68
|
const holder = globalThis as Record<string, unknown>;
|
|
69
69
|
const registry = holder[CONFIGURATOR_REGISTRY_KEY];
|
|
70
70
|
if (Array.isArray(registry)) {
|
|
71
|
-
|
|
71
|
+
const index = registry.findIndex((entry) => entry?.id === "opl-footer");
|
|
72
|
+
if (index === -1) registry.push(tab);
|
|
73
|
+
else registry[index] = tab;
|
|
72
74
|
} else {
|
|
73
75
|
holder[CONFIGURATOR_REGISTRY_KEY] = [tab];
|
|
74
76
|
}
|
|
@@ -81,7 +81,9 @@ export function registerInputConfiguratorTab(): void {
|
|
|
81
81
|
const holder = globalThis as Record<string, unknown>;
|
|
82
82
|
const registry = holder[CONFIGURATOR_REGISTRY_KEY];
|
|
83
83
|
if (Array.isArray(registry)) {
|
|
84
|
-
|
|
84
|
+
const index = registry.findIndex((entry) => entry?.id === "opl-input");
|
|
85
|
+
if (index === -1) registry.push(tab);
|
|
86
|
+
else registry[index] = tab;
|
|
85
87
|
} else {
|
|
86
88
|
holder[CONFIGURATOR_REGISTRY_KEY] = [tab];
|
|
87
89
|
}
|
|
@@ -31,9 +31,11 @@ Any mode can start plan execution via the mode picker's `Execute:` entries or `/
|
|
|
31
31
|
|
|
32
32
|
### Tool inheritance warning
|
|
33
33
|
|
|
34
|
-
A custom mode with no `tools` array inherits
|
|
34
|
+
A custom mode with no `tools` array inherits the resting tool set, including `write` and `edit` when available. A pinned `modes.off.tools` list narrows that baseline. Always specify an explicit read-only tool list for restrictive modes.
|
|
35
35
|
|
|
36
|
-
`off` behaves the same way: it restores the tools that were active before the mode (never a wider set). Set `modes.off.tools` to pin the resting tool set instead — for example to keep `subagent`/`browser` out of normal mode entirely. Pinning replaces the snapshot taken on mode entry, so a later mode that lists no `tools` inherits the pinned set rather than the older, wider one. `load_tools` is offered in `off` because withheld lazy tools are otherwise unreachable there.
|
|
36
|
+
`off` behaves the same way: it restores the tools that were active before the mode (never a wider set). Set `modes.off.tools` to pin the resting tool set instead — for example to keep `subagent`/`browser` out of normal mode entirely. The pinned set also applies before `--chat` or `--plan` startup takes its snapshot, on startup when no mode entry is restored, and as the inherited baseline when plan or execute state is restored. Pinning replaces the snapshot taken on mode entry, so a later mode that lists no `tools` inherits the pinned set rather than the older, wider one. `load_tools` is offered in `off` because withheld lazy tools are otherwise unreachable there.
|
|
37
|
+
|
|
38
|
+
Set `modes.execute.tools` to replace the tools available during execution; otherwise execute inherits the pre-plan active set. The configured list is applied on entry and resume, with lazy loading and `allowPlanComplete` still in effect. Exiting restores the previous set or the pinned OFF baseline.
|
|
37
39
|
|
|
38
40
|
Model overrides are resolved through Pi's model registry when entering a mode. A blank or partial `model` (for example `{ "provider": "", "id": "" }`) is treated as **no override**, so the mode keeps the current model instead of warning `Model not found: /`. The previously active model is captured once per mode and restored on exit; the restore point is persisted in the mode session entry, so `/reload` and `/resume` inside a mode no longer lose it. If the restore target is gone from the registry it is dropped immediately; if it exists but has no credentials (before `/login`, say) the restore is retried once and then released, so a stale point cannot block future captures indefinitely. `pi.setModel` is session-scoped: it never rewrites your configured `defaultProvider`/`defaultModel`, but it does append a model change to the session transcript, which is why the restore point is persisted alongside the mode. Changes are serialized, so a rapid mode exit cannot leave a superseded mode model active. Note that setting `modes.off.model` makes OFF a pinned baseline: it is applied on every mode exit *and* at session start, overriding `--model` and the configured default. Use Pi theme color tokens for widget label colors.
|
|
39
41
|
|
|
@@ -127,9 +129,9 @@ Use `modes.<name>.appearance` to keep any mode's visual identity with its defini
|
|
|
127
129
|
|
|
128
130
|
Behavior:
|
|
129
131
|
|
|
130
|
-
- Listed tools are removed from the active set wherever opl-modes computes it. In practice this only affects modes that would otherwise include them: `off`/`execute` (which inherit
|
|
132
|
+
- Listed tools are removed from the active set wherever opl-modes computes it. In practice this only affects modes that would otherwise include them: `off`/`execute` (which inherit the resting tool set unless overridden) and custom modes that permit them. Read-only `chat`/`plan` already exclude these tools, so nothing changes there.
|
|
131
133
|
- When at least one lazy tool is withheld from a mode, opl-modes registers and activates a `load_tools` tool. The model calls `load_tools({ tools: [...] })` (or with no argument to enable all currently-allowed lazy tools) to activate them before use.
|
|
132
|
-
- `load_tools` is bounded by the current mode's tool policy:
|
|
134
|
+
- `load_tools` is bounded by the current mode's tool policy: explicit tool lists take precedence; execute and custom modes without a list inherit `modes.off.tools` when pinned. Both named requests and load-all respect that baseline.
|
|
133
135
|
- Core built-ins (`read`, `edit`, `write`, `bash`, `powershell`, `grep`, `find`, `ls`), `plan_complete`, and `load_tools` itself are protected and silently ignored if listed in `lazyTools`.
|
|
134
136
|
- Switching modes re-applies the policy, so a lazy tool enabled earlier returns to inactive on the next mode change and must be re-enabled.
|
|
135
137
|
- A mode `prompt` that instructs the model to use a lazy tool must also tell it to call `load_tools` first, otherwise the instruction names a tool the model cannot see. The bundled `research` mode does exactly that.
|
|
@@ -111,7 +111,7 @@ export default function modeSwitcher(pi: ExtensionAPI) {
|
|
|
111
111
|
async execute(_toolCallId, params) {
|
|
112
112
|
const mode = getMode();
|
|
113
113
|
const modeDef = getModeDefinition(mode);
|
|
114
|
-
const modeAllowed = modeDef?.tools ?? null; //
|
|
114
|
+
const modeAllowed = modeDef?.tools ?? getModeDefinition("off")?.tools ?? null; // Unlisted modes inherit the pinned baseline, if any.
|
|
115
115
|
const current = pi.getActiveTools();
|
|
116
116
|
const enable = lazyToolsToEnable(params.tools as string[] | undefined, current, modeAllowed);
|
|
117
117
|
if (enable.length > 0) pi.setActiveTools([...current, ...enable]);
|
|
@@ -129,6 +129,7 @@ export default function modeSwitcher(pi: ExtensionAPI) {
|
|
|
129
129
|
// ─── Saved model for restoring ─────────────────────────────────────────────
|
|
130
130
|
let savedModel: Model<any> | null = null;
|
|
131
131
|
const queueModelChange = createLatestModelQueue();
|
|
132
|
+
let pendingPlanModel: Promise<void> | null = null;
|
|
132
133
|
// Consecutive restore failures for the same model, so an un-authable restore point is
|
|
133
134
|
// retried once and then released instead of blocking future captures forever.
|
|
134
135
|
let restoreAttempts = 0;
|
|
@@ -363,7 +364,7 @@ export default function modeSwitcher(pi: ExtensionAPI) {
|
|
|
363
364
|
transition("plan", pi);
|
|
364
365
|
const modeDef = getModeDefinition("plan");
|
|
365
366
|
saveAndSetActiveTools(planModeTools());
|
|
366
|
-
applyModeModel(ctx, modeDef);
|
|
367
|
+
pendingPlanModel = applyModeModel(ctx, modeDef);
|
|
367
368
|
updateStatus(ctx);
|
|
368
369
|
if (ctx.hasUI && !USER_CONFIG.ui.hideNotify) {
|
|
369
370
|
const notify = modeDef?.labels?.notify ?? USER_CONFIG.labels.plan.notify;
|
|
@@ -374,10 +375,15 @@ export default function modeSwitcher(pi: ExtensionAPI) {
|
|
|
374
375
|
|
|
375
376
|
async function enterExecuteMode(ctx: ExtensionContext): Promise<void> {
|
|
376
377
|
transition("execute", pi);
|
|
377
|
-
const baseNames = savedToolNames ?? pi.getActiveTools();
|
|
378
|
-
pi.setActiveTools(applyLazyPolicy([...toolsWithoutPlanComplete(baseNames), "plan_complete"]));
|
|
379
|
-
savedToolNames = null;
|
|
380
378
|
const modeDef = getModeDefinition("execute");
|
|
379
|
+
if (modeDef?.tools) {
|
|
380
|
+
saveAndSetActiveTools(withPlanComplete("execute", toolsWithoutPlanComplete(modeDef.tools)));
|
|
381
|
+
} else {
|
|
382
|
+
const baseNames = savedToolNames ?? pi.getActiveTools();
|
|
383
|
+
pi.setActiveTools(applyLazyPolicy(withPlanComplete("execute", toolsWithoutPlanComplete(baseNames))));
|
|
384
|
+
savedToolNames = null;
|
|
385
|
+
}
|
|
386
|
+
await pendingPlanModel;
|
|
381
387
|
await applyModeModel(ctx, modeDef);
|
|
382
388
|
updateStatus(ctx);
|
|
383
389
|
const title = getPlanDisplayTitle();
|
|
@@ -397,6 +403,7 @@ export default function modeSwitcher(pi: ExtensionAPI) {
|
|
|
397
403
|
*/
|
|
398
404
|
function enterOffMode(ctx: ExtensionContext, message?: string, silent = false): void {
|
|
399
405
|
transition("off", pi);
|
|
406
|
+
pendingPlanModel = null;
|
|
400
407
|
const offModeDef = getModeDefinition("off");
|
|
401
408
|
// OFF gates tools only when the user declares modes.off.tools; otherwise it restores
|
|
402
409
|
// whatever was active before the mode.
|
|
@@ -511,7 +518,9 @@ export default function modeSwitcher(pi: ExtensionAPI) {
|
|
|
511
518
|
|
|
512
519
|
if (!restored) {
|
|
513
520
|
resetState();
|
|
514
|
-
|
|
521
|
+
const offTools = getModeDefinition("off")?.tools;
|
|
522
|
+
if (offTools) saveAndSetActiveTools(withPlanComplete("off", offTools), true);
|
|
523
|
+
else restoreAllTools();
|
|
515
524
|
applyOffModel(ctx);
|
|
516
525
|
updateStatus(ctx);
|
|
517
526
|
return;
|
|
@@ -528,13 +537,21 @@ export default function modeSwitcher(pi: ExtensionAPI) {
|
|
|
528
537
|
const mode = getMode();
|
|
529
538
|
const modeDef = getModeDefinition(mode);
|
|
530
539
|
|
|
540
|
+
// A restored plan or execution needs the pinned resting set as its inherited baseline,
|
|
541
|
+
// not the host tools active before this branch was restored.
|
|
542
|
+
const offTools = getModeDefinition("off")?.tools;
|
|
543
|
+
if ((mode === "plan" || mode === "execute") && offTools) {
|
|
544
|
+
saveAndSetActiveTools(withPlanComplete("off", offTools), true);
|
|
545
|
+
}
|
|
546
|
+
|
|
531
547
|
if (modeDef?.tools) {
|
|
532
548
|
// Honor allowPlanComplete here too, otherwise a resume drops the tool a mode was given.
|
|
533
549
|
// OFF's declared list is the resting baseline, so it must not keep a stale snapshot.
|
|
534
|
-
|
|
550
|
+
const tools = mode === "execute" ? toolsWithoutPlanComplete(modeDef.tools) : modeDef.tools;
|
|
551
|
+
saveAndSetActiveTools(withPlanComplete(mode, tools), mode === "off");
|
|
535
552
|
} else if (mode === "execute") {
|
|
536
553
|
const allNames = pi.getActiveTools();
|
|
537
|
-
pi.setActiveTools(applyLazyPolicy(
|
|
554
|
+
pi.setActiveTools(applyLazyPolicy(withPlanComplete(mode, toolsWithoutPlanComplete(allNames))));
|
|
538
555
|
} else {
|
|
539
556
|
// Default: the active set minus plan_complete, re-added when the mode allows it.
|
|
540
557
|
const allNames = pi.getActiveTools();
|
|
@@ -558,9 +575,14 @@ export default function modeSwitcher(pi: ExtensionAPI) {
|
|
|
558
575
|
// ─── Event: session_start ──────────────────────────────────────────────────────
|
|
559
576
|
|
|
560
577
|
pi.on("session_start", async (event, ctx) => {
|
|
578
|
+
setSessionCwd(ctx.cwd);
|
|
561
579
|
// Re-publish the footer seam: a previous session in this process cleared it on shutdown.
|
|
562
580
|
publishModeGlobals();
|
|
563
581
|
if (event.reason === "startup") {
|
|
582
|
+
if (getMode() === "off" && (pi.getFlag("chat") === true || pi.getFlag("plan") === true)) {
|
|
583
|
+
const offTools = getModeDefinition("off")?.tools;
|
|
584
|
+
if (offTools) saveAndSetActiveTools(withPlanComplete("off", offTools), true);
|
|
585
|
+
}
|
|
564
586
|
if (pi.getFlag("chat") === true && getMode() === "off") {
|
|
565
587
|
enterChatMode(ctx);
|
|
566
588
|
return;
|
|
@@ -799,6 +821,8 @@ export default function modeSwitcher(pi: ExtensionAPI) {
|
|
|
799
821
|
const filename = `${PLAN_FILE_PREFIX}${sanitized}.md`;
|
|
800
822
|
enterPlanWithFile(filename, pi);
|
|
801
823
|
saveAndSetActiveTools(planModeTools());
|
|
824
|
+
pendingPlanModel = applyModeModel(ctx, getModeDefinition("plan"));
|
|
825
|
+
await pendingPlanModel;
|
|
802
826
|
updateStatus(ctx);
|
|
803
827
|
const createTitle = titleFromFilename(filename);
|
|
804
828
|
if (ctx.hasUI && !USER_CONFIG.ui.hideNotify) {
|
|
@@ -865,9 +889,11 @@ export default function modeSwitcher(pi: ExtensionAPI) {
|
|
|
865
889
|
}
|
|
866
890
|
|
|
867
891
|
/** Load an existing plan file and show the action menu. */
|
|
868
|
-
function loadPlanAndShowMenu(ctx: ExtensionContext, filename: string, displayName: string): void {
|
|
892
|
+
async function loadPlanAndShowMenu(ctx: ExtensionContext, filename: string, displayName: string): Promise<void> {
|
|
869
893
|
enterPlanWithFile(filename, pi);
|
|
870
894
|
saveAndSetActiveTools(planModeTools());
|
|
895
|
+
pendingPlanModel = applyModeModel(ctx, getModeDefinition("plan"));
|
|
896
|
+
await pendingPlanModel;
|
|
871
897
|
updateStatus(ctx);
|
|
872
898
|
if (ctx.hasUI && !USER_CONFIG.ui.hideNotify) {
|
|
873
899
|
ctx.ui.notify(USER_CONFIG.labels.plan.notifyLoaded.replace("{title}", displayName), "info");
|
|
@@ -1024,7 +1050,7 @@ export default function modeSwitcher(pi: ExtensionAPI) {
|
|
|
1024
1050
|
if (choice === "__new__") {
|
|
1025
1051
|
await promptNameAndEnterPlanMode(ctx);
|
|
1026
1052
|
} else {
|
|
1027
|
-
loadPlanAndShowMenu(ctx, choice, titleFromFilename(choice));
|
|
1053
|
+
await loadPlanAndShowMenu(ctx, choice, titleFromFilename(choice));
|
|
1028
1054
|
await showPlanMenu(ctx);
|
|
1029
1055
|
}
|
|
1030
1056
|
}
|
|
@@ -1058,7 +1084,7 @@ export default function modeSwitcher(pi: ExtensionAPI) {
|
|
|
1058
1084
|
|
|
1059
1085
|
if (existsSync(filePath)) {
|
|
1060
1086
|
if (getMode() !== "off") enterOffMode(ctx, undefined, true);
|
|
1061
|
-
loadPlanAndShowMenu(ctx, filename, titleFromFilename(filename));
|
|
1087
|
+
await loadPlanAndShowMenu(ctx, filename, titleFromFilename(filename));
|
|
1062
1088
|
await showPlanMenu(ctx);
|
|
1063
1089
|
} else {
|
|
1064
1090
|
if (getMode() === "plan" || getMode() === "execute") {
|
|
@@ -1068,6 +1094,8 @@ export default function modeSwitcher(pi: ExtensionAPI) {
|
|
|
1068
1094
|
if (getMode() !== "off") enterOffMode(ctx, undefined, true);
|
|
1069
1095
|
enterPlanWithFile(filename, pi);
|
|
1070
1096
|
saveAndSetActiveTools(planModeTools());
|
|
1097
|
+
pendingPlanModel = applyModeModel(ctx, getModeDefinition("plan"));
|
|
1098
|
+
await pendingPlanModel;
|
|
1071
1099
|
updateStatus(ctx);
|
|
1072
1100
|
if (ctx.hasUI && !USER_CONFIG.ui.hideNotify) {
|
|
1073
1101
|
ctx.ui.notify(USER_CONFIG.labels.plan.notifyWithTitle.replace("{title}", titleFromFilename(filename)), USER_CONFIG.labels.plan.notifyType as "info" | "warning" | "error");
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opl-webaccess",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "opl-webaccess",
|
|
9
|
-
"version": "
|
|
10
|
-
"license": "
|
|
9
|
+
"version": "0.2.0",
|
|
10
|
+
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@mozilla/readability": "^0.6.0",
|
|
13
13
|
"linkedom": "^0.18.13",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openlines/opl-pi-sht",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "opl-pi-sht (shouldHaveThat) Pi coding-agent extension kit: token-efficient context, tool and session safety, fingerprinted /init, model benchmarking, web access, and browser automation.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|