specpi 0.31.0 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/NPM_RELEASE.md +1 -1
- package/README.md +4 -4
- package/SECURITY_MODEL.md +2 -2
- package/extensions/tool-wishlist/authoring-tools.mjs +39 -9
- package/extensions/tool-wishlist/index.ts +25 -7
- package/extensions/workflow-controls/capabilities.mjs +17 -0
- package/extensions/workflow-controls/index.ts +17 -3
- package/package.json +1 -1
- package/skills/specpi-improve/SKILL.md +1 -0
- package/templates/AGENTS.md +2 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.32.0 - 2026-09-23
|
|
4
|
+
|
|
5
|
+
- Send less on every request. SpecPi's first request was 14,022 characters against Pi's 5,521, and that overhead rides every turn and is never removed by compaction. Measured again with all eight pins, the interactive default is now 11,119. A headless session drops the two tools below as well, which the measured parts put at about 8,300: SpecPi's overhead falls from 8,501 characters to about 2,800.
|
|
6
|
+
- Trim the installed working agreement from about 2,400 characters to 1,300, keeping the rules that shape ordinary work, including the restraint that wins `sanitize-git-repo` on Terminal-Bench. The `/scope` rule moves into the scope notice SpecPi already adds while scope is active. The wishlist and credential rules already live in the improvement skill, and the web and browser rule now lives in `request_capability`'s own description.
|
|
7
|
+
- Offer `report_capability_gap` only where a report could land: collection on, or undecided in an interactive session, where its first report asks for consent. With collection off, or undecided in a headless session, it could only ever answer "Not recorded". `/wishlist on` and `/wishlist off` switch it with collection. Its four guidance lines, which repeated its description, are now one.
|
|
8
|
+
- Offer `request_capability` only to an interactive session with a requestable group installed. Headless, it could only refuse. Its description is shorter too.
|
|
9
|
+
- Keep the improvement skill out of the model's skill list. It only ever runs from a `/harness-improvement` selection, so the kickoff now names its file, and Pi still lists it as a command.
|
|
10
|
+
- Repair `scripts/measure-context.mjs`, which failed on 0.31.0: it expected two first-party extensions and authoring tools that no longer ship by default. It now records where each Pi row's instruction characters go, section by section. The published record and charts are re-measured; Oh My Pi runs again (18.2.5), so its row is no longer carried forward.
|
|
11
|
+
- Give tier 6's Pi-family harnesses compaction settings that fit the declared window. Pi's defaults compact at 7,616 tokens of a 24,000 window and try to keep 20,000, so earlier runs compacted almost every turn; plain SpecPi compacted 17-19 times on the marathon task against Pi's 5-7, because its fixed prompt took most of what was left. Runs from before this setting are not comparable.
|
|
12
|
+
- Note on the evaluations page that `configure-git-webserver` measures a cleanup habit. Its checker's push never succeeded for any harness, so every pass came from a test file left in the web root. That also qualifies a claim in 0.31.0: SpecPi + Jev's self-stopped failures outnumber Pi's only because of this task. Without it they are level, 31 against 32.
|
|
13
|
+
- Consider a background-task system and do not build one. In the Terminal-Bench transcripts agents already start servers with `nohup` or `&`, tool timeouts are about one in eighteen attempts, and the one task that spends minutes blocked in commands (`tune-mjcf`) times out equally in both arms. Both earlier background-task integrations were removed, and a new one would add tool schema to every request.
|
|
14
|
+
- SpecPi Chat 0.15.0 needs no change: it runs Pi interactively, so both tools stay available to it, and its command menu still lists the improvement skill.
|
|
15
|
+
|
|
3
16
|
## 0.31.0 - 2026-09-22
|
|
4
17
|
|
|
5
18
|
- Report Terminal-Bench 2.0 per sitting rather than per run. Bare Pi -- unchanged software, the same thirteen tasks, the same machine -- solved 21, 22, 30, 30 and 32 of 39 across five launches, so a figure from one run is not evidence about a harness. The page pools every sitting, publishes each harness's per-sitting range beside its rate, and charts the spread directly.
|
package/NPM_RELEASE.md
CHANGED
|
@@ -6,7 +6,7 @@ Publishing, tags, deprecation, ownership changes and GitHub Releases require exp
|
|
|
6
6
|
|
|
7
7
|
## Prepare
|
|
8
8
|
|
|
9
|
-
1. This candidate is SpecPi **0.
|
|
9
|
+
1. This candidate is SpecPi **0.32.0**, paired with the Chat **0.15.0** VSIX. Browser QA **0.3.0** is already independently published; do not republish or edit its source as part of this release. Choose an unused version; update `package.json`, the dated `CHANGELOG.md` entry, README, and the website. Check Chat's version and VSIX download links. Preserve historical changelog entries.
|
|
10
10
|
2. Validate:
|
|
11
11
|
|
|
12
12
|
```sh
|
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
31
|
-
SpecPi 0.
|
|
31
|
+
SpecPi 0.32.0 is a small starting point for the [Pi coding agent](https://pi.dev/). It is one opinionated setup for how the agent should work, not a marketplace of plugins.
|
|
32
32
|
|
|
33
33
|
At the center are two built-in extensions. **Scope control** keeps each task to the files it said it would touch. The **improvement loop** turns repeated friction into small, tested changes to the setup, instead of letting prompts and workarounds pile up. Around those are eight hand-picked packages, each locked to an exact version and checked before anything installs, plus **SpecPi Chat**, a VS Code panel for working alongside the agent.
|
|
34
34
|
|
|
@@ -44,7 +44,7 @@ Everything it touches is written down, versioned, and easy to undo.
|
|
|
44
44
|
|
|
45
45
|
## Measured context
|
|
46
46
|
|
|
47
|
-
This chart shows first-call context from a clean install: all eight pinned packages, the working agreement, and the skills Pi finds.
|
|
47
|
+
This chart shows first-call context from a clean install: all eight pinned packages, the working agreement, and the skills Pi finds, in an interactive session with wishlist collection undecided. A headless session, or one with collection off, also leaves out the gap report and capability request tools. "Enabled" means browser QA, delegation, and web access are switched on, with no goal, scope, or improvement selection active.
|
|
48
48
|
|
|
49
49
|
The solid rows are measured by us, from the request each setup actually sends through one local test provider. That includes OpenCode, the DeepSeek Harness, and Oh My Pi, all measured as installed. The faded Codex CLI and Claude Code rows come from HarnessTax's published numbers, measured under their own setup. Treat those as a rough reference, not a head-to-head test. These are character counts. They say nothing about tokens, cost, or how well each tool does the job. The research page [breaks down the enabled setup by feature](https://tannermidd.github.io/SpecPi/research/#specpi), so you can see what each switch costs on its own.
|
|
50
50
|
|
|
@@ -52,7 +52,7 @@ The solid rows are measured by us, from the request each setup actually sends th
|
|
|
52
52
|
<a href="https://tannermidd.github.io/SpecPi/research/#specpi">
|
|
53
53
|
<picture>
|
|
54
54
|
<source media="(prefers-color-scheme: dark)" srcset="https://tannermidd.github.io/SpecPi/media/context-chart-dark.svg">
|
|
55
|
-
<img src="https://tannermidd.github.io/SpecPi/media/context-chart-light.svg" width="880" alt="Bar chart of characters sent on the first model call: Pi stock 5,521, SpecPi default
|
|
55
|
+
<img src="https://tannermidd.github.io/SpecPi/media/context-chart-light.svg" width="880" alt="Bar chart of characters sent on the first model call: Pi stock 5,521, SpecPi default 11,119, OpenCode 31,043, DeepSeek Harness 31,743, SpecPi enabled 35,767, Codex CLI 41,616, Oh My Pi 66,708, Claude Code 90,460.">
|
|
56
56
|
</picture>
|
|
57
57
|
</a>
|
|
58
58
|
</p>
|
|
@@ -64,7 +64,7 @@ The gap between the two SpecPi bars comes from a few separate switches, so the e
|
|
|
64
64
|
<a href="https://tannermidd.github.io/SpecPi/research/#specpi">
|
|
65
65
|
<picture>
|
|
66
66
|
<source media="(prefers-color-scheme: dark)" srcset="https://tannermidd.github.io/SpecPi/media/capability-chart-dark.svg">
|
|
67
|
-
<img src="https://tannermidd.github.io/SpecPi/media/capability-chart-light.svg" width="880" alt="Bar chart of tool-schema characters each capability adds: Pi built-ins 2,896, Improvement loop
|
|
67
|
+
<img src="https://tannermidd.github.io/SpecPi/media/capability-chart-light.svg" width="880" alt="Bar chart of tool-schema characters each capability adds: Pi built-ins 2,896, Improvement loop 1,601, Capability request 995, Goals 1,315, Browser QA 8,234, Delegation 4,453, Web access 11,298. Browser QA, Delegation, Web access are hidden until switched on.">
|
|
68
68
|
</picture>
|
|
69
69
|
</a>
|
|
70
70
|
</p>
|
package/SECURITY_MODEL.md
CHANGED
|
@@ -10,7 +10,7 @@ Scope records use Pi's current session branch. Restoring a branch does not creat
|
|
|
10
10
|
|
|
11
11
|
## Capability requests
|
|
12
12
|
|
|
13
|
-
Optional tool groups ship withdrawn. The `request_capability` tool lets the model name a withdrawn group instead of silently working around it; it never grants one. Activation requires a human decision, either accepted in the moment or recorded earlier as a standing grant, so the tool
|
|
13
|
+
Optional tool groups ship withdrawn. The `request_capability` tool lets the model name a withdrawn group instead of silently working around it; it never grants one. Activation requires a human decision, either accepted in the moment or recorded earlier as a standing grant, so the tool is not offered in headless sessions or when no requestable group is installed, refuses if called without an interactive user, and a declined prompt leaves the session unchanged. Activation is additive, offers only the named group's tools, and lasts for the session: it writes no startup preference, and the next session starts from the saved preference as before. Delegation is not requestable, because its own package requires a human command to bind a model. Acceptance offers the tools; it is not a per-call permission, and the Permission System continues to govern what those tools may do.
|
|
14
14
|
|
|
15
15
|
`/capability allow <name>` records a standing grant so that group is offered without a prompt, and `/capability ask <name>` returns it to prompting. Recording either requires an interactive human command, and a grant that fires still announces itself in the session. Grants live in `<agent-dir>/specpi/capabilities/settings.json`, written atomically with owner-only permissions; links and irregular files are refused, and an unreadable file, a foreign schema or an unknown capability name reads as no grant. A standing grant removes the prompt, not the requirement for a human: headless sessions are still refused, so a granted capability cannot be activated by an unattended run. It also does not offer the group at startup — the model must still ask, and the group stays withdrawn until it does.
|
|
16
16
|
|
|
@@ -62,7 +62,7 @@ As of 0.3.0 the guard resolves its key the way the advisor does and the way Pi r
|
|
|
62
62
|
|
|
63
63
|
## Improvement authority and evidence
|
|
64
64
|
|
|
65
|
-
Collection is off by default. Enabling it permits sanitized gap observations, not implementation. Only an exact human `/harness-improvement` selection authorizes a wishlist-sourced change. The selected contract is bound to the gap, source checkout, session, and selection generation. Web access tools (`web_search`, `source_check`, `fetch_content`, `get_search_content`) are hidden until a human offers them, through `/webaccess on` or by accepting a `request_capability` prompt.
|
|
65
|
+
Collection is off by default. Enabling it permits sanitized gap observations, not implementation. The observation tool is offered only where a report could land: collection on, or undecided in an interactive session, where its first report asks for consent. The improvement skill is kept out of the model's skill list and is named by path only in the `/harness-improvement` kickoff. Only an exact human `/harness-improvement` selection authorizes a wishlist-sourced change. The selected contract is bound to the gap, source checkout, session, and selection generation. Web access tools (`web_search`, `source_check`, `fetch_content`, `get_search_content`) are hidden until a human offers them, through `/webaccess on` or by accepting a `request_capability` prompt.
|
|
66
66
|
|
|
67
67
|
Retirement requires source registry integration, unchanged verification policy, a matching contract, bounded source snapshots, `npm run check`, and closed registered validators. Receipts distinguish machine-observed gates from model-reported acceptance evidence. Stale selections, changed source, missing evidence, and failed checks reject retirement. A validator proves only the behavior it exercises; the human remains responsible for accepting the result. The loop never commits, publishes, or installs a resulting change automatically.
|
|
68
68
|
|
|
@@ -7,36 +7,66 @@
|
|
|
7
7
|
// the answer is a boolean — no latency, no cost, no false positives, and nothing for a classifier
|
|
8
8
|
// to route.
|
|
9
9
|
//
|
|
10
|
-
// `report_capability_gap` is
|
|
11
|
-
//
|
|
12
|
-
//
|
|
10
|
+
// `report_capability_gap` is the *observation* tool and the reason the improvement loop exists, so
|
|
11
|
+
// it stays offered whenever a report could land: collection on, or undecided with a human present to
|
|
12
|
+
// answer the consent prompt its first call raises. Only when nothing can be recorded is it withdrawn
|
|
13
|
+
// -- collection switched off, or undecided in a headless session that can never ask. Its schema and
|
|
14
|
+
// guidance were about 2.5 KB of every request, paid in exactly the sessions where it always answered
|
|
15
|
+
// "Not recorded".
|
|
16
|
+
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
13
18
|
|
|
14
19
|
/** Only usable while an improvement is selected. */
|
|
15
20
|
export const AUTHORING_TOOL_NAMES = Object.freeze(["record_harness_contract", "finish_harness_improvement"]);
|
|
16
21
|
|
|
22
|
+
/** The observation tool. */
|
|
23
|
+
export const OBSERVATION_TOOL_NAME = "report_capability_gap";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The improvement skill is hidden from the model's skill list (`disable-model-invocation`), because it
|
|
27
|
+
* only ever runs from a /harness-improvement selection. Installed resources and the npm package share
|
|
28
|
+
* the same layout, so the path is resolved from this file rather than from the agent directory.
|
|
29
|
+
*/
|
|
30
|
+
export function improvementSkillPath() {
|
|
31
|
+
return fileURLToPath(new URL("../../skills/specpi-improve/SKILL.md", import.meta.url));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Whether a report could be recorded in this session, which is the only reason to offer the tool. */
|
|
35
|
+
export function observationToolWanted(mode, interactive) {
|
|
36
|
+
return mode === "on" || (mode === "undecided" && interactive === true);
|
|
37
|
+
}
|
|
38
|
+
|
|
17
39
|
/**
|
|
18
|
-
* Add or remove
|
|
40
|
+
* Add or remove named tools without disturbing any other extension's tools. Mirrors the
|
|
19
41
|
* web-access gate: it only ever touches the names it owns, and it does nothing when the active set
|
|
20
42
|
* already matches, so a no-op never costs the cached prompt prefix.
|
|
21
43
|
*/
|
|
22
|
-
|
|
44
|
+
function syncOwnedTools(pi, names, wanted) {
|
|
23
45
|
if (typeof pi?.getActiveTools !== "function" || typeof pi?.setActiveTools !== "function") {
|
|
24
46
|
return false;
|
|
25
47
|
}
|
|
26
48
|
|
|
27
|
-
const owned = new Set(
|
|
49
|
+
const owned = new Set(names);
|
|
28
50
|
const active = pi.getActiveTools();
|
|
29
51
|
const present = active.filter((name) => owned.has(name));
|
|
30
|
-
if (
|
|
52
|
+
if (wanted && present.length === owned.size) {
|
|
31
53
|
return false;
|
|
32
54
|
}
|
|
33
55
|
|
|
34
|
-
if (!
|
|
56
|
+
if (!wanted && present.length === 0) {
|
|
35
57
|
return false;
|
|
36
58
|
}
|
|
37
59
|
|
|
38
60
|
const others = active.filter((name) => !owned.has(name));
|
|
39
|
-
pi.setActiveTools(
|
|
61
|
+
pi.setActiveTools(wanted ? [...others, ...names] : others);
|
|
40
62
|
|
|
41
63
|
return true;
|
|
42
64
|
}
|
|
65
|
+
|
|
66
|
+
export function syncAuthoringTools(pi, selected) {
|
|
67
|
+
return syncOwnedTools(pi, AUTHORING_TOOL_NAMES, selected);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function syncObservationTool(pi, wanted) {
|
|
71
|
+
return syncOwnedTools(pi, [OBSERVATION_TOOL_NAME], wanted);
|
|
72
|
+
}
|
|
@@ -13,7 +13,12 @@ import { getMarkdownTheme, type ExtensionAPI } from "@earendil-works/pi-coding-a
|
|
|
13
13
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
14
14
|
import { Box, Markdown, Text } from "@earendil-works/pi-tui";
|
|
15
15
|
import { Type } from "typebox";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
improvementSkillPath,
|
|
18
|
+
observationToolWanted,
|
|
19
|
+
syncAuthoringTools,
|
|
20
|
+
syncObservationTool,
|
|
21
|
+
} from "./authoring-tools.mjs";
|
|
17
22
|
import {
|
|
18
23
|
appendWishlistDecision,
|
|
19
24
|
archiveWishlist,
|
|
@@ -468,7 +473,7 @@ function improvementPrompt(group: any, context: ImprovementContext = {}) {
|
|
|
468
473
|
|
|
469
474
|
lines.push(
|
|
470
475
|
"",
|
|
471
|
-
|
|
476
|
+
`This exact menu selection authorizes implementation of the smallest sufficient intervention in the current SpecPi source checkout. Read and follow the specpi-improve skill at ${improvementSkillPath()}. Treat the wishlist evidence as a lead, inspect current behavior, keep scope minimal, and do not ask for another approval unless scope expands or external/remote state would change.`,
|
|
472
477
|
"First call record_harness_contract with the exact gap, selection ID, source root, proposed requirements, acceptance checks, paths, rollback, and non-goals. Run direct acceptance checks and focused tests. At the end, call finish_harness_improvement with the gap ID, concise acceptance evidence, and a validation note. That tool independently runs the repository gate, verifies registry integration and frozen policy inputs, runs supported capability validators, records a bounded local receipt, and retires the item. If any check fails, do not retire it; leave it selected and report the blocker.",
|
|
473
478
|
);
|
|
474
479
|
|
|
@@ -586,9 +591,23 @@ export default function toolWishlist(pi: ExtensionAPI) {
|
|
|
586
591
|
}
|
|
587
592
|
};
|
|
588
593
|
|
|
594
|
+
// Offer the observation tool only where a report could land. Decided at session start and on an
|
|
595
|
+
// explicit /wishlist on|off, never in between, so the cached prompt prefix is not disturbed.
|
|
596
|
+
const syncObservation = (interactive: boolean) => {
|
|
597
|
+
let mode = "off";
|
|
598
|
+
try {
|
|
599
|
+
mode = readCollectionMode(stateDir);
|
|
600
|
+
} catch {
|
|
601
|
+
// An unreadable collection file cannot record anything either.
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
syncObservationTool(pi, observationToolWanted(mode, interactive));
|
|
605
|
+
};
|
|
606
|
+
|
|
589
607
|
pi.on("session_start", (_event, ctx) => {
|
|
590
608
|
restoreActiveImprovement(ctx);
|
|
591
609
|
syncAuthoring();
|
|
610
|
+
syncObservation(ctx.hasUI === true);
|
|
592
611
|
});
|
|
593
612
|
|
|
594
613
|
pi.on("session_tree", (_event, ctx) => {
|
|
@@ -653,13 +672,10 @@ export default function toolWishlist(pi: ExtensionAPI) {
|
|
|
653
672
|
name: "report_capability_gap",
|
|
654
673
|
label: "Report Capability Gap",
|
|
655
674
|
description:
|
|
656
|
-
"Privately record
|
|
675
|
+
"Privately record, in SpecPi's local wishlist, a material and reusable capability gap that reasonable existing tools or workarounds could not cover. Not for transient failures, command mistakes, credentials or permissions the user must supply, ordinary project work, or speculative nice-to-haves. At most once per gap per task. Never include secrets, source code, full commands, file paths, private URLs, or user prompt text.",
|
|
657
676
|
promptSnippet: "Record recurring, generalizable capability friction without interrupting the user task",
|
|
658
677
|
promptGuidelines: [
|
|
659
|
-
"
|
|
660
|
-
"Do not use report_capability_gap for transient errors, model mistakes, missing credentials or permissions, ordinary project-specific work, or speculative nice-to-haves.",
|
|
661
|
-
"Call report_capability_gap at most once per distinct gap per user task; use a short durable capability phrase without project names, and never include secrets, source code, full commands, private paths, or user prompt text.",
|
|
662
|
-
"After report_capability_gap, continue the requested task; never treat a report as permission to modify SpecPi or external state.",
|
|
678
|
+
"After report_capability_gap, continue the requested task; a report never authorizes changing SpecPi or external state.",
|
|
663
679
|
],
|
|
664
680
|
parameters: Type.Object(
|
|
665
681
|
{
|
|
@@ -1450,6 +1466,8 @@ export default function toolWishlist(pi: ExtensionAPI) {
|
|
|
1450
1466
|
|
|
1451
1467
|
if (action === "on" || action === "off") {
|
|
1452
1468
|
await setCollectionMode({ stateDir, mode: action });
|
|
1469
|
+
// A human command, so offering or withdrawing the tool now is the expected cost.
|
|
1470
|
+
syncObservation(ctx.hasUI === true);
|
|
1453
1471
|
ctx.ui.notify(
|
|
1454
1472
|
`Local wishlist collection is ${action}. Jev transmission requires its own switch and consent.`,
|
|
1455
1473
|
"info",
|
|
@@ -124,6 +124,23 @@ export function capabilityInstalled(allToolNames, capability) {
|
|
|
124
124
|
return capability.tools.some((name) => known.has(name));
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
/** The tool the model uses to ask for a withdrawn group. */
|
|
128
|
+
export const CAPABILITY_REQUEST_TOOL = "request_capability";
|
|
129
|
+
|
|
130
|
+
/** Whether any requestable group is installed, i.e. whether asking for one could ever succeed. */
|
|
131
|
+
export function anyCapabilityInstalled(allToolNames) {
|
|
132
|
+
return CAPABILITY_NAMES.some((id) => capabilityInstalled(allToolNames, CAPABILITIES[id]));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Whether to offer the request tool at all. It can only ever ask a human, so a session without one
|
|
137
|
+
* would carry its schema for a tool that always refuses, and a session with no requestable group
|
|
138
|
+
* installed has nothing to ask for.
|
|
139
|
+
*/
|
|
140
|
+
export function capabilityRequestOffered({ interactive, allToolNames }) {
|
|
141
|
+
return interactive === true && anyCapabilityInstalled(allToolNames);
|
|
142
|
+
}
|
|
143
|
+
|
|
127
144
|
/** One catalogue line per capability, for the tool description and its error results. */
|
|
128
145
|
export function describeCapabilities() {
|
|
129
146
|
return CAPABILITY_NAMES.map((id) => `${id}: ${CAPABILITIES[id].summary}`).join("; ");
|
|
@@ -5,8 +5,10 @@ import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
|
5
5
|
import { Type } from "typebox";
|
|
6
6
|
import {
|
|
7
7
|
CAPABILITY_NAMES,
|
|
8
|
+
CAPABILITY_REQUEST_TOOL,
|
|
8
9
|
capabilityActive,
|
|
9
10
|
capabilityInstalled,
|
|
11
|
+
capabilityRequestOffered,
|
|
10
12
|
describeCapabilities,
|
|
11
13
|
findCapability,
|
|
12
14
|
missingTools,
|
|
@@ -389,6 +391,17 @@ export default function workflowControls(pi: ExtensionAPI) {
|
|
|
389
391
|
);
|
|
390
392
|
});
|
|
391
393
|
|
|
394
|
+
// Decided once per session, before the first request, so the cached prompt prefix is never
|
|
395
|
+
// disturbed. See capabilityRequestOffered for why a session may not need the tool at all.
|
|
396
|
+
const applyCapabilityRequest = (ctx: ExtensionContext) => {
|
|
397
|
+
const allToolNames = typeof pi.getAllTools === "function" ? pi.getAllTools().map((tool) => tool.name) : [];
|
|
398
|
+
syncToolGroup(
|
|
399
|
+
pi,
|
|
400
|
+
[CAPABILITY_REQUEST_TOOL],
|
|
401
|
+
capabilityRequestOffered({ interactive: ctx.hasUI, allToolNames }),
|
|
402
|
+
);
|
|
403
|
+
};
|
|
404
|
+
|
|
392
405
|
// Web access ships hidden. A missing or unreadable preference means off, and the
|
|
393
406
|
// gate only ever touches its own four tool names.
|
|
394
407
|
let webAccessEnabled = loadStartupActivation();
|
|
@@ -397,6 +410,7 @@ export default function workflowControls(pi: ExtensionAPI) {
|
|
|
397
410
|
pi.on("session_start", (_event, ctx) => {
|
|
398
411
|
webAccessEnabled = loadStartupActivation();
|
|
399
412
|
applyWebAccess();
|
|
413
|
+
applyCapabilityRequest(ctx);
|
|
400
414
|
beginRestore(ctx);
|
|
401
415
|
});
|
|
402
416
|
|
|
@@ -561,7 +575,7 @@ export default function workflowControls(pi: ExtensionAPI) {
|
|
|
561
575
|
if (scope.active) {
|
|
562
576
|
const taskStale = scope.taskDigest !== undefined && currentTask?.digest !== scope.taskDigest;
|
|
563
577
|
guidance.push(
|
|
564
|
-
`[SPECPI SCOPE]\nDeclared paths: ${scope.entries.map((item) => `${sanitizePathLabel(item.path)}${item.directory ? "/" : ""}`).join(", ")}\nPending outside-scope paths: ${scope.pending.map(sanitizePathLabel).join(", ") || "none"}.
|
|
578
|
+
`[SPECPI SCOPE]\nDeclared paths: ${scope.entries.map((item) => `${sanitizePathLabel(item.path)}${item.directory ? "/" : ""}`).join(", ")}\nPending outside-scope paths: ${scope.pending.map(sanitizePathLabel).join(", ") || "none"}. Keep outside-scope findings pending until the human allows once, acknowledges them with /scope accept, expands scope with /scope add, or clears it. Acknowledgement does not widen scope, and pending paths are not accepted scope.${taskStale ? "\nTask-bound scope is stale; do not widen it automatically." : ""}`,
|
|
565
579
|
);
|
|
566
580
|
}
|
|
567
581
|
|
|
@@ -905,9 +919,9 @@ export default function workflowControls(pi: ExtensionAPI) {
|
|
|
905
919
|
};
|
|
906
920
|
|
|
907
921
|
pi.registerTool({
|
|
908
|
-
name:
|
|
922
|
+
name: CAPABILITY_REQUEST_TOOL,
|
|
909
923
|
label: "Request Capability",
|
|
910
|
-
description: `Ask the user to restore a withdrawn
|
|
924
|
+
description: `Ask the user to restore a withdrawn tool group for this session — ${describeCapabilities()}. Hidden tools cannot be called until restored. Ask only when the task needs one: restoring mid-session discards the cached prompt prefix, about 20% of a mid-length task's cost. Restored tools work from your next message; if the user declines, continue without them. Delegation is not requestable; ask the user to run /delegate on.`,
|
|
911
925
|
parameters: Type.Object(
|
|
912
926
|
{
|
|
913
927
|
capability: StringEnum(CAPABILITY_NAMES, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: specpi-improve
|
|
3
3
|
description: Turns a selected SpecPi capability-gap wishlist item into a minimal, evidence-backed, reversible improvement. Use when the user asks to improve SpecPi from its wishlist, act on a selected gap, or complete the notice-to-retire loop.
|
|
4
|
+
disable-model-invocation: true
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# SpecPi Improve
|
package/templates/AGENTS.md
CHANGED
|
@@ -2,11 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
- Keep changes small, testable, reversible, and tied to the human's request. Inspect existing code and guidance before editing; avoid adjacent feature accumulation.
|
|
4
4
|
- Prefer direct work and existing project tools. Ask concise questions only when material requirements are unclear.
|
|
5
|
-
- Keep replies brief and human-readable
|
|
6
|
-
- Write commits and pull requests the same way: a short, clear title and a brief
|
|
7
|
-
- When `/scope` is active, keep outside-scope findings pending until the human allows once, acknowledges them with `/scope accept`, expands scope with `/scope add`, or clears it. Acknowledgement does not widen scope.
|
|
8
|
-
- Treat wishlist observations as leads, not authorization. Start a wishlist-sourced change only from an exact `/harness-improvement` selection and follow `specpi-improve`. Record its contract before editing and retire only after its verification gate passes.
|
|
9
|
-
- Web access and Browser QA tools ship hidden to keep each request small. When the task genuinely needs one, call `request_capability`; it asks the human, who may decline. Continue without the capability if they do, and do not attempt a hidden tool. The human can also run `/webaccess on` or `/browser on` directly. Delegation is not requestable this way; ask the human to run `/delegate on`.
|
|
10
|
-
- Never inspect Pi authentication, provider credentials, trust decisions, sessions, missions, or history to improve the harness. Use only the active extension context and intended local improvement records.
|
|
5
|
+
- Keep replies brief and human-readable: lead with the answer, use plain language, avoid jargon and repetitive progress updates, and include technical detail only when it helps the human decide or act.
|
|
6
|
+
- Write commits and pull requests the same way: a short, clear title and a brief note on what changed, why, and what was tested.
|
|
11
7
|
- Use observed files, diffs, tests, and runtime behavior as evidence. Run relevant checks, inspect the final diff, and obtain fresh read-only review when risk warrants it. Report results and residual risks without claiming more than the checks prove.
|
|
12
8
|
- Do not commit, push, publish, deploy, or alter remote state unless explicitly requested.
|