@xzzpig/pi-openspec-x 0.1.0 → 0.2.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/CHANGELOG.md +28 -0
- package/README.md +6 -2
- package/dist/public.d.ts +48 -0
- package/package.json +9 -5
- package/src/agent-prompts.ts +4 -1
- package/src/agents.ts +25 -0
- package/src/child-report-extensions/gap-analysis.ts +23 -0
- package/src/child-report-extensions/plan-review.ts +21 -0
- package/src/child-report-extensions/worker.ts +26 -0
- package/src/child-report-ingestion.ts +1530 -0
- package/src/child-report-reader.ts +955 -0
- package/src/dispatch-gate.ts +25 -18
- package/src/goal-base.ts +394 -0
- package/src/implement-command.ts +620 -60
- package/src/index.ts +113 -9
- package/src/objective.ts +2 -2
- package/src/plan-command.ts +46 -0
- package/src/progress-observer.ts +48 -54
- package/src/report-tools.ts +10 -9
- package/src/worker-dispatch.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.1
|
|
4
|
+
|
|
5
|
+
- Support Pi 1.2+ custom-message wakeups in child report handlers. A wakeup
|
|
6
|
+
retains the current user-dispatch attempt without allowing an unverified
|
|
7
|
+
message to authorize a report; a new user dispatch creates a fresh attempt.
|
|
8
|
+
- Include the extension's generated public type declaration in the package.
|
|
9
|
+
|
|
10
|
+
## 0.2.0
|
|
11
|
+
|
|
12
|
+
Child report tools are now scoped to the subagents that own them, and the
|
|
13
|
+
parent session no longer transcribes reports.
|
|
14
|
+
|
|
15
|
+
- **Breaking:** `report_gap_analysis`, `report_plan_review` and `report_work`
|
|
16
|
+
are no longer registered in the main session. Each is loaded only by its own
|
|
17
|
+
opsx child extension (`subagentOnlyExtensions`), so a parent session sees
|
|
18
|
+
neither the tools nor their prompt rules.
|
|
19
|
+
- Child reports are received by the plugin from native subagent results, the
|
|
20
|
+
canonical v3 session record and the native `status.json`, and are bound to a
|
|
21
|
+
specific flow, run and attempt nonce. No parent-side review tool is required.
|
|
22
|
+
- Completion gating reads the accepted ready receipt of the task's latest run
|
|
23
|
+
rather than anything the parent model restates, so a failed, superseded or
|
|
24
|
+
stale-attempt report cannot authorize a tick.
|
|
25
|
+
- A cold restart restores the flow, the gate and its dispatch observations from
|
|
26
|
+
the branch, so previously earned receipts keep working after a restart.
|
|
27
|
+
- A new dispatch invalidates the task's earlier receipt; ordinary goals keep
|
|
28
|
+
their original behaviour and `--direct` flows keep their own completion
|
|
29
|
+
events without worker conditions.
|
|
30
|
+
|
|
3
31
|
## 0.1.0
|
|
4
32
|
|
|
5
33
|
Initial release.
|
package/README.md
CHANGED
|
@@ -58,9 +58,13 @@ fail-closed:pi-sandbox 缺失时,`/opsx:plan` 与代理实现态会被拒绝
|
|
|
58
58
|
计划态与代理实现态的写收敛由 pi-sandbox 的受限 profile 强制(fail-closed):越界写被沙箱层拒绝并给出原因,读取不受限;进入受限模式前插件发出的模式契约已预先指明可写范围并把写入指引收敛回 `openspec/`。
|
|
59
59
|
|
|
60
60
|
- **`--direct` 主会话直实现**:不切角色、不收紧权限;按 `tasks.md` 顺序实现、自行验证、验证通过才勾选,终点同样接最终审查门。
|
|
61
|
-
- **`--agent` 代理实现**:进入 `opsx-agent` 受限模式(源码只读、构建产物可写),逐任务派发全写权限的 `opsx-worker`。派发提示词固定六段(任务 / 预期产出 / 所需工具 / 必须做 / 禁止做 /
|
|
61
|
+
- **`--agent` 代理实现**:进入 `opsx-agent` 受限模式(源码只读、构建产物可写),逐任务派发全写权限的 `opsx-worker`。派发提示词固定六段(任务 / 预期产出 / 所需工具 / 必须做 / 禁止做 / 上下文),其中任务段必须带独立行的 `[OPSX WORKER DISPATCH task=<taskId>]` 标记(新派发与 resume 都要带);派发返回后逐文件核对 diff 与 worker 报告声明、运行验证,**通过才勾选 tasks.md**,并同步 goal 任务树。
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
三个上报工具(`report_gap_analysis` / `report_plan_review` / `report_work`)**只对各自的子角色可见**:gap-analysis 只见前者、plan-review 只见中间、opsx-worker 只见后者;主会话与其他角色(含 opsx-reviewer)既没有这些工具,也不会有它们的使用指引。插件从子运行的原生结果与规范会话记录里**自动接收**报告并关联到任务,**不需要主模型转录**,也不存在 `review_work` 之类的主会话验收工具。
|
|
64
|
+
|
|
65
|
+
“未审查不得勾选”是**强制门**:插件监听 `subagent`(`agent=opsx-worker`)与 `update_goal_task` 两类工具流量,在 `update_goal_task status=complete` 的 `tool_call` 阶段拦截——没有观测到派发、派发没有对应的**已接收**报告、报告声明与窗口 delta 不一致(漏报或多报文件)、或记录的验证明显失败时,勾选被 block 并返回具体原因。因此代理模式下**先派发、等报告自动落定、再勾选**。同一任务反复派发且既无报告也无改动会被判为空转并提示一次。门拦的是 goal 任务树镜像的 `update_goal_task`;直接以 bash/write 改 `tasks.md` 复选框本身不被硬拦,由「goal 任务树不完整 → goal-x 完成事务拒绝」兜底,最终语义一致。直实现模式不设此门(无派发可核)。
|
|
66
|
+
|
|
67
|
+
诊断:证据不可用(子运行失败且未留下可读取的规范记录、会话未持久化、分支/焦点在读取期间变化)时该任务的勾选**保持阻塞**并说明原因,不会用旧报告放行;超出资源预算(活跃子项、会话大小、单行长度、报告字节、上报次数、会话树节点)时报告**明确标记为不可用于门控**,同样不放行,而不是截断后当成功。
|
|
64
68
|
|
|
65
69
|
两种模式都以持久目标为执行底座:插件把 `tasks.md` 组装成 objective(`Steps` + `Boundaries`/`Don'ts` + `Verification contract:`)交给 goal-x 的 `create_goal`(代理模式 `sisyphus`,直实现模式普通 autoContinue),任务清单经 `set_goal_tasks` 单向镜像。`tasks.md` 是任务权威源,goal 任务树上的手工改动不回流。
|
|
66
70
|
|
package/dist/public.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* pi-openspec-x extension entry point.
|
|
5
|
+
*
|
|
6
|
+
* pi-sandbox is probed lazily through a dynamic import (dependencies.ts): on
|
|
7
|
+
* success the three opsx sandbox profiles (D4: opsx-planner / opsx-agent /
|
|
8
|
+
* opsx-reviewer) are registered into pi-sandbox's runtime registry — in-memory
|
|
9
|
+
* for the session lifetime, never written to a user's sandbox.json, a
|
|
10
|
+
* user-defined same-name profile always keeping priority. On failure the
|
|
11
|
+
* reason is recorded as dependency state and the extension continues: the
|
|
12
|
+
* official-track skills and every other capability keep working, and a later
|
|
13
|
+
* request for a restricted mode is refused with a typed
|
|
14
|
+
* OpsxDependencyMissingError (fail-closed) instead of the extension (or the
|
|
15
|
+
* official track) dying at load time.
|
|
16
|
+
*
|
|
17
|
+
* Registers the resources_discover handler, which:
|
|
18
|
+
* - generates (and caches, keyed by CLI version) the official-track
|
|
19
|
+
* openspec-* skills under `<agentDir>/cache/pi-openspec-x/skills/` and
|
|
20
|
+
* exposes the directory to pi via `skillPaths`;
|
|
21
|
+
* - warns once per project when static `<cwd>/.pi/skills/openspec-*`
|
|
22
|
+
* directories collide with the generated skills.
|
|
23
|
+
*
|
|
24
|
+
* Registers the session_start handler that clears a stale `active_agent`
|
|
25
|
+
* identity left in the session file by an earlier session (pi-agent-role's
|
|
26
|
+
* semantics; see mode.ts).
|
|
27
|
+
*
|
|
28
|
+
* Registers the three child reporting tools (report_gap_analysis,
|
|
29
|
+
* report_plan_review, report_work) through the role-specific child-only
|
|
30
|
+
* extensions declared in agents.ts (design D1): each tool is registered into
|
|
31
|
+
* its own subagent's process and never into the main session, so the parent's
|
|
32
|
+
* tool declaration and prompt rules stay free of them.
|
|
33
|
+
*
|
|
34
|
+
* pi-subagents is probed the same lazy way: on success the four opsx
|
|
35
|
+
* subagents (opsx-gap-analysis / opsx-plan-review / opsx-worker / opsx-reviewer) are
|
|
36
|
+
* registered into pi-subagents' runtime registry via the owner's event bus
|
|
37
|
+
* (D3). A missing pi-subagents costs only the enhanced flow's subagents —
|
|
38
|
+
* the official track and direct main-session implementation are unaffected.
|
|
39
|
+
* Because the owner listener only exists once pi-subagents has loaded (load
|
|
40
|
+
* order between extensions is not guaranteed), a failed init-time attempt is
|
|
41
|
+
* retried on session_start.
|
|
42
|
+
*
|
|
43
|
+
* Missing openspec CLI: no skills are registered and no error escapes — a
|
|
44
|
+
* one-time notice is sent instead.
|
|
45
|
+
*/
|
|
46
|
+
declare function piOpenspecX(pi: ExtensionAPI): Promise<void>;
|
|
47
|
+
|
|
48
|
+
export { piOpenspecX as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xzzpig/pi-openspec-x",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "OpenSpec workflow for the pi coding agent: spec-driven planning and implementation via dedicated subagents, sandboxed /opsx:plan and /opsx:implement commands.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"files": [
|
|
26
26
|
"index.ts",
|
|
27
27
|
"src",
|
|
28
|
+
"dist",
|
|
28
29
|
"README.md",
|
|
29
30
|
"CHANGELOG.md",
|
|
30
31
|
"LICENSE",
|
|
@@ -36,20 +37,23 @@
|
|
|
36
37
|
]
|
|
37
38
|
},
|
|
38
39
|
"peerDependencies": {
|
|
39
|
-
"@earendil-works/pi-ai": ">=0.
|
|
40
|
-
"@earendil-works/pi-coding-agent": ">=0.
|
|
40
|
+
"@earendil-works/pi-ai": ">=1.0.0 <2",
|
|
41
|
+
"@earendil-works/pi-coding-agent": ">=1.0.0 <2",
|
|
41
42
|
"@xzzpig/pi-goal-x": "*",
|
|
42
43
|
"@xzzpig/pi-sandbox": "*",
|
|
43
44
|
"@xzzpig/pi-subagents": "*"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
|
-
"@earendil-works/pi-coding-agent": "
|
|
47
|
+
"@earendil-works/pi-coding-agent": "1.1.0",
|
|
47
48
|
"@types/node": "24.12.4",
|
|
49
|
+
"rollup": "^4.62.2",
|
|
50
|
+
"rollup-plugin-dts": "^6.4.1",
|
|
48
51
|
"typescript": "5.9.3",
|
|
49
52
|
"vitest": "4.1.8"
|
|
50
53
|
},
|
|
51
54
|
"scripts": {
|
|
52
55
|
"test": "vitest run",
|
|
53
|
-
"typecheck": "tsc --noEmit"
|
|
56
|
+
"typecheck": "tsc --noEmit",
|
|
57
|
+
"build:types": "rollup -c rollup.dts.config.mjs"
|
|
54
58
|
}
|
|
55
59
|
}
|
package/src/agent-prompts.ts
CHANGED
|
@@ -59,7 +59,8 @@ The parent gates purely on the verdict field. Approve workable plans; reject onl
|
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* opsx-worker — focused single-task executor. Full write; reports through
|
|
62
|
-
* `report_work
|
|
62
|
+
* `report_work`, a tool only this role can see, whose payload the parent
|
|
63
|
+
* ingests automatically from the child run.
|
|
63
64
|
*/
|
|
64
65
|
export const WORKER_SYSTEM_PROMPT = `You are the opsx worker: a focused task executor for the OpenSpec implementation flow. You execute exactly ONE dispatched task. You do not delegate further, you do not spawn subagents, you do not pick up neighboring tasks, and you do not refactor beyond the task.
|
|
65
66
|
|
|
@@ -78,6 +79,8 @@ Report through the report_work tool with:
|
|
|
78
79
|
- claims: what you did and the result, precisely;
|
|
79
80
|
- selfVerification: the command you ran and its actual result (exit status, pass/fail counts). Subagents lie — not you: your evidence must be real command output you personally observed. If verification failed and you could not fix it, report that honestly; a truthful failure is recoverable, an invented success is not.
|
|
80
81
|
|
|
82
|
+
The parent reads this payload straight from your run; do not restate it in prose expecting the parent to transcribe it, and do not look for another tool to file it with.
|
|
83
|
+
|
|
81
84
|
If the task cannot be completed as specified (missing prerequisite, contradictory requirements), stop early, change nothing you cannot justify, and report exactly what is blocking.`;
|
|
82
85
|
|
|
83
86
|
/**
|
package/src/agents.ts
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* never collides with itself.
|
|
22
22
|
*/
|
|
23
23
|
import { createRequire } from "node:module";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
24
25
|
import type {
|
|
25
26
|
ExtensionAPI,
|
|
26
27
|
ExtensionHandler,
|
|
@@ -91,6 +92,27 @@ const SHARED_ISOLATION = {
|
|
|
91
92
|
|
|
92
93
|
const reviewerProgressExtensionPath = resolveGoalAuditorProgressPath();
|
|
93
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Child-only report-tool extensions (design D1), one per reporting role.
|
|
97
|
+
*
|
|
98
|
+
* Absolute paths on purpose, exactly like the reviewer's goal-x extension: a
|
|
99
|
+
* child pi process runs in the audited workspace, not in this package's
|
|
100
|
+
* directory, so a relative specifier would not resolve. These entries are NOT
|
|
101
|
+
* listed in `package.json::pi.extensions` — the main session must never load
|
|
102
|
+
* them, and each registers exactly one tool for its own role.
|
|
103
|
+
*/
|
|
104
|
+
const CHILD_REPORT_EXTENSION_PATHS = {
|
|
105
|
+
"opsx-gap-analysis": fileURLToPath(
|
|
106
|
+
new URL("./child-report-extensions/gap-analysis.ts", import.meta.url),
|
|
107
|
+
),
|
|
108
|
+
"opsx-plan-review": fileURLToPath(
|
|
109
|
+
new URL("./child-report-extensions/plan-review.ts", import.meta.url),
|
|
110
|
+
),
|
|
111
|
+
"opsx-worker": fileURLToPath(
|
|
112
|
+
new URL("./child-report-extensions/worker.ts", import.meta.url),
|
|
113
|
+
),
|
|
114
|
+
};
|
|
115
|
+
|
|
94
116
|
/** The four canonical runtime definitions, keyed by agent name. */
|
|
95
117
|
export const OPSX_AGENT_DEFINITIONS: Record<
|
|
96
118
|
OpsxAgentName,
|
|
@@ -108,6 +130,7 @@ export const OPSX_AGENT_DEFINITIONS: Record<
|
|
|
108
130
|
"bash",
|
|
109
131
|
REPORT_GAP_ANALYSIS_TOOL_NAME,
|
|
110
132
|
],
|
|
133
|
+
subagentOnlyExtensions: [CHILD_REPORT_EXTENSION_PATHS["opsx-gap-analysis"]],
|
|
111
134
|
...SHARED_ISOLATION,
|
|
112
135
|
acceptanceRole: "read-only",
|
|
113
136
|
thinking: "high",
|
|
@@ -116,6 +139,7 @@ export const OPSX_AGENT_DEFINITIONS: Record<
|
|
|
116
139
|
description: "Read-only work-plan reviewer for the opsx planning flow",
|
|
117
140
|
systemPrompt: PLAN_REVIEW_SYSTEM_PROMPT,
|
|
118
141
|
tools: ["read", "grep", "find", "ls", REPORT_PLAN_REVIEW_TOOL_NAME],
|
|
142
|
+
subagentOnlyExtensions: [CHILD_REPORT_EXTENSION_PATHS["opsx-plan-review"]],
|
|
119
143
|
...SHARED_ISOLATION,
|
|
120
144
|
acceptanceRole: "read-only",
|
|
121
145
|
thinking: "high",
|
|
@@ -134,6 +158,7 @@ export const OPSX_AGENT_DEFINITIONS: Record<
|
|
|
134
158
|
"ls",
|
|
135
159
|
REPORT_WORK_TOOL_NAME,
|
|
136
160
|
],
|
|
161
|
+
subagentOnlyExtensions: [CHILD_REPORT_EXTENSION_PATHS["opsx-worker"]],
|
|
137
162
|
...SHARED_ISOLATION,
|
|
138
163
|
acceptanceRole: "writer",
|
|
139
164
|
},
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Child-only extension entry for opsx-gap-analysis (design D1).
|
|
3
|
+
*
|
|
4
|
+
* Loaded exclusively through that agent's `subagentOnlyExtensions`; it
|
|
5
|
+
* registers exactly one tool and never loads the main extension, so the gap
|
|
6
|
+
* analysis reporting tool exists only inside the gap-analysis child process.
|
|
7
|
+
*/
|
|
8
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
9
|
+
|
|
10
|
+
import { registerChildReportTool } from "../child-report-reader.ts";
|
|
11
|
+
import { reportGapAnalysisTool } from "../report-tools.ts";
|
|
12
|
+
|
|
13
|
+
/** Role this entry serves; must match the agent name in `agents.ts`. */
|
|
14
|
+
export const GAP_ANALYSIS_CHILD_AGENT = "opsx-gap-analysis";
|
|
15
|
+
|
|
16
|
+
export default function gapAnalysisChildReportExtension(
|
|
17
|
+
pi: ExtensionAPI,
|
|
18
|
+
): void {
|
|
19
|
+
registerChildReportTool(pi, {
|
|
20
|
+
agent: GAP_ANALYSIS_CHILD_AGENT,
|
|
21
|
+
tool: reportGapAnalysisTool,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Child-only extension entry for opsx-plan-review (design D1).
|
|
3
|
+
*
|
|
4
|
+
* Loaded exclusively through that agent's `subagentOnlyExtensions`; it
|
|
5
|
+
* registers exactly one tool and never loads the main extension, so the plan
|
|
6
|
+
* review reporting tool exists only inside the plan-review child process.
|
|
7
|
+
*/
|
|
8
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
9
|
+
|
|
10
|
+
import { registerChildReportTool } from "../child-report-reader.ts";
|
|
11
|
+
import { reportPlanReviewTool } from "../report-tools.ts";
|
|
12
|
+
|
|
13
|
+
/** Role this entry serves; must match the agent name in `agents.ts`. */
|
|
14
|
+
export const PLAN_REVIEW_CHILD_AGENT = "opsx-plan-review";
|
|
15
|
+
|
|
16
|
+
export default function planReviewChildReportExtension(pi: ExtensionAPI): void {
|
|
17
|
+
registerChildReportTool(pi, {
|
|
18
|
+
agent: PLAN_REVIEW_CHILD_AGENT,
|
|
19
|
+
tool: reportPlanReviewTool,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Child-only extension entry for opsx-worker (design D1).
|
|
3
|
+
*
|
|
4
|
+
* Loaded exclusively through that agent's `subagentOnlyExtensions`; it
|
|
5
|
+
* registers exactly one tool and never loads the main extension, so the work
|
|
6
|
+
* report tool exists only inside the worker child process.
|
|
7
|
+
*
|
|
8
|
+
* The worker's Task section carries `[OPSX WORKER DISPATCH task=<taskId>]`.
|
|
9
|
+
* The attempt records that marker when the prompt names exactly one task; a
|
|
10
|
+
* resumed prompt without one is still recordable but cannot authorize a tick
|
|
11
|
+
* (design D3).
|
|
12
|
+
*/
|
|
13
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
14
|
+
|
|
15
|
+
import { registerChildReportTool } from "../child-report-reader.ts";
|
|
16
|
+
import { reportWorkTool } from "../report-tools.ts";
|
|
17
|
+
|
|
18
|
+
/** Role this entry serves; must match the agent name in `agents.ts`. */
|
|
19
|
+
export const WORKER_CHILD_AGENT = "opsx-worker";
|
|
20
|
+
|
|
21
|
+
export default function workerChildReportExtension(pi: ExtensionAPI): void {
|
|
22
|
+
registerChildReportTool(pi, {
|
|
23
|
+
agent: WORKER_CHILD_AGENT,
|
|
24
|
+
tool: reportWorkTool,
|
|
25
|
+
});
|
|
26
|
+
}
|