Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ jobs:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
repository: mattpocock/skills
ref: 49dd158d1076134a641b33efb035946536778336
ref: 4ae2317ddb22b046e26455e4f9c9b3aaaf62212d
path: .upstream/mattpocock-skills
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
Expand Down
36 changes: 22 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -458,23 +458,31 @@ the remote fixture and temporary credentials were removed afterward.

### Reviewed harness versions

The latest stable releases were reviewed on 2026-10-30. Release metadata and
CLI help checks confirmed the existing runtime arguments. This review did not
repeat the live prompts or skill-discovery checks recorded above and in the
[adapter reference](./docs/harness-adapters.md).
The latest stable releases were reviewed on 2026-11-12. Release metadata and
CLI help checks confirmed the runtime arguments for every Harness. OpenCode 2
removed `run --pure`, so `profiles/harnesses.json` now runs `opencode run`
without it, and the `recall` history reader selects its OpenCode commands by
major version. This review repeated live prompts only for OpenCode; the other
live and skill-discovery checks recorded above and in the
[adapter reference](./docs/harness-adapters.md) keep their original versions.

| Harness | Latest stable | Verification |
| --- | --- | --- |
| Codex CLI | [0.166.0](https://github.com/openai/codex/releases/tag/rust-v0.166.0) | npm metadata, local version, and `exec --help` |
| Claude Code | [2.1.314](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md) | npm metadata, local version, and `--help` |
| OpenCode | [1.19.5](https://github.com/anomalyco/opencode/releases/tag/v1.19.5) | npm metadata and release, local 1.19.4 `run --help` |
| pi | [1.4.0](https://github.com/earendil-works/pi/releases/tag/v1.4.0) | npm metadata, local version, and `--help` |
| Antigravity | [1.4.11](https://antigravity-cli-auto-updater-974169037036.us-central1.run.app/manifests/windows_amd64.json) | Official manifest, binary SHA512, downloaded binary version, and `--help` |
| Grok Build | 1.0.70 | Local version, `--help`, and `grok update --check --json` |

The current pi npm package is `@earendil-works/pi-coding-agent`. The old
`@mariozechner/pi-coding-agent` package stops at 0.73.1. Antigravity's manifest
comes from its [official CLI installer](https://antigravity.google/cli).
| Codex CLI | [0.167.2](https://github.com/openai/codex/releases/tag/rust-v0.167.2) | npm metadata and release, `exec --help` from the npm package |
| Claude Code | [2.1.322](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md) | npm metadata, local version, and `--help` |
| OpenCode 2 | [2.2.7](https://github.com/anomalyco/opencode/releases/tag/v2.2.7) | npm metadata, local version, `run --help`, live `run --agent plan`, `recall` list and read, and `api skill.list` |
| OpenCode 1 | [1.19.5](https://github.com/anomalyco/opencode/releases/tag/v1.19.5) | npm metadata and release, live `run --agent plan`, and `recall` list and read from the npm package |
| pi | [1.5.2](https://github.com/earendil-works/pi/releases/tag/v1.5.2) | npm metadata and release, `--help` from the npm package |
| Antigravity | [1.5.6](https://antigravity-cli-auto-updater-974169037036.us-central1.run.app/manifests/windows_amd64.json) | Official manifest, binary SHA512, downloaded binary version, and `--help` |
| Grok Build | 1.0.77 | `grok update --check --json`; `--help` from the local 1.0.70 |

OpenCode 2 is published as `@opencode/cli` and is the install the
[OpenCode documentation](https://opencode.ai/docs) gives. The `opencode-ai`
package and the release GitHub marks as latest remain on the 1.19 line, so both
majors are supported. The current pi npm package is
`@earendil-works/pi-coding-agent`. The old `@mariozechner/pi-coding-agent`
package stops at 0.73.1. Antigravity's manifest comes from its
[official CLI installer](https://antigravity.google/cli).

## Recover recent context

Expand Down
2 changes: 1 addition & 1 deletion THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ SOFTWARE.
The following skills are adapted from
[`mattpocock/skills`](https://github.com/mattpocock/skills), pinned during the
port at commit
[`49dd158d1076134a641b33efb035946536778336`](https://github.com/mattpocock/skills/commit/49dd158d1076134a641b33efb035946536778336):
[`4ae2317ddb22b046e26455e4f9c9b3aaaf62212d`](https://github.com/mattpocock/skills/commit/4ae2317ddb22b046e26455e4f9c9b3aaaf62212d):

- `writing-for-agents`
- `codebase-design`
Expand Down
23 changes: 14 additions & 9 deletions docs/harness-adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
This page records the vendor rules that shape mstack's adapters. The links point
to the official documentation used for each entry.

On 2026-10-30, release metadata and CLI help were checked against the
[reviewed harness versions](../README.md#reviewed-harness-versions). The runtime
arguments in `profiles/harnesses.json` remain supported. That review did not
repeat live prompts or skill discovery. The behavior observations below keep
their original tested versions.
On 2026-11-12, release metadata and CLI help were checked against the
[reviewed harness versions](../README.md#reviewed-harness-versions). OpenCode 2
rejects `--pure`, so its runtime arguments dropped that flag; the other runtime
arguments in `profiles/harnesses.json` remain supported. Live prompts and
skill discovery were repeated only for OpenCode. The other behavior
observations below keep their original tested versions.

## Skill locations and frontmatter

Expand Down Expand Up @@ -76,13 +77,17 @@ the `name` field and a file named `SKILL.md`, which the canonical skills already
satisfy, so `adapters/antigravity.json` and `adapters/grok.json` are empty. Grok
also scans `~/.claude/skills/`; skills with the same name collapse to one entry.
Claude output omits frontmatter `name`; Claude's documented fallback
uses the directory name, so `/meta-mode` keeps its name. OpenCode requires an
uses the directory name, so `/meta-mode` keeps its name. OpenCode 1 requires an
explicit name before registration and skips the Claude copy. This behavior
was checked with OpenCode 1.19.3 using a named Claude control skill and an
unnamed Claude copy alongside the canonical shared skill. Claude Code 2.1.267
unnamed Claude copy alongside the canonical shared skill. OpenCode 2 falls back
to the directory name, so it also reads the unnamed Claude copy, but it keys
skills by directory name and loads `.agents` roots after `.claude` roots. With
OpenCode 2.2.7, `opencode api skill.list` returned one entry per mstack skill,
each from `~/.agents/skills/`, while `~/.claude/skills/` held the unnamed copies.
Claude Code 2.1.267
also returned the directory-derived command name during an isolated
stream-JSON initialization without a user prompt or model request. Recheck this adapter
if OpenCode adds a directory-name fallback for external skills.
stream-JSON initialization without a user prompt or model request.

pi keeps the first same-name skill and reports a collision for later
independent copies. This was observed with pi 0.85.1, and the pi 1.3.0 skills
Expand Down
30 changes: 30 additions & 0 deletions docs/upstream-sync-matt-4ae2317.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Upstream sync to mattpocock/skills 4ae2317

The mattpocock/skills pin moves from `49dd158` to
[`4ae2317`](https://github.com/mattpocock/skills/commit/4ae2317ddb22b046e26455e4f9c9b3aaaf62212d).
One commit between the pins touches the three adapted skills:
[`7739eae`](https://github.com/mattpocock/skills/commit/7739eaed7f8f3915437a8def198a6d0d7206dbc7),
"Start subagents with a fresh context window, not a fork".

## Ported

- `codebase-design` starts each design-alternative subagent with a fresh
context window instead of a fork of the current conversation. Upstream notes
that some Harnesses, Codex among them, fork by default, and a fork carries the
parent context the subagent is meant to escape.

## Not ported

- The same sentence in upstream skills that mstack does not adapt
(`implement-spec`, `code-review`, `improve-codebase-architecture`, `wayfinder`,
`research`, `grilling`, `chief-of-staff`).
- Upstream's repository rule in `CLAUDE.md`. mstack's subagent briefs live in
pstack-derived skills, which follow pstack rather than this upstream.

`diagnosing-bugs` and `writing-for-agents` have no upstream change between the
pins.

## Verification

`skill-baseline --check` passes against both pinned sources. The repository
tests do not show that a model follows the changed instruction.
2 changes: 1 addition & 1 deletion profiles/harnesses.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"suffix": ["skills"],
"runtime": {
"command": "opencode",
"headlessArgs": ["run", "--pure"],
"headlessArgs": ["run"],
"modelArgs": ["--model", "{model}"],
"readOnlyArgs": ["--agent", "plan"]
}
Expand Down
10 changes: 5 additions & 5 deletions profiles/skill-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"version": 1,
"sources": {
"pstack": "4dd1d05818d19792e7cd6e818f1667f95f7e1dbe",
"mattpocock/skills": "49dd158d1076134a641b33efb035946536778336"
"mattpocock/skills": "4ae2317ddb22b046e26455e4f9c9b3aaaf62212d"
},
"files": {
"skills/architect/references/design-red-flags.md": {
Expand Down Expand Up @@ -69,8 +69,8 @@
"skills/codebase-design/references/design-alternatives.md": {
"upstream": "mattpocock/skills",
"source": "skills/engineering/codebase-design/DESIGN-IT-TWICE.md",
"sourceDigest": "52b52e75655a27aea59c6966b410bac82448968ad46ef1ebb9f07c274e3ae010",
"targetDigest": "fe64a751fc7e1a5399126886b1c0832b484364cfeaa7929d1c8e77b6a49eb208",
"sourceDigest": "c222ab5a82e0405672eb9062d62496139a08828e45d07fc5f18e4952c10fac1a",
"targetDigest": "ff896de577b462ff28a3152e154788e1a8d922426f951444ec1b0dd5723b1f3b",
"reason": "The adapted design comparison workflow lives under references."
},
"skills/codebase-design/SKILL.md": {
Expand Down Expand Up @@ -536,7 +536,7 @@
"skills/recall/references/history-sources.md": {
"upstream": null,
"reason": "Local portability guidance or implementation; review alongside its owning skill.",
"targetDigest": "181e7a56f8ef5514a1cae4841366bf62c708fd44be8bcee3c129c50182e0b3bc"
"targetDigest": "b95748234ef2196269ffba0638a439a6fee225420eca23da57923f04a3546922"
},
"skills/recall/references/hosts/t3code.md": {
"upstream": null,
Expand All @@ -546,7 +546,7 @@
"skills/recall/scripts/history.mjs": {
"upstream": null,
"reason": "Local portability guidance or implementation; review alongside its owning skill.",
"targetDigest": "fe3284380308ecd0cf232072c7bc8e59c0f56ffecc42851d1fddb2c382ab2683"
"targetDigest": "c3fb58b7816bb58acb5c134ebeaa2fe40a4716b936977221c8f2b42910c7da47"
},
"skills/recall/SKILL.md": {
"upstream": "pstack",
Expand Down
2 changes: 1 addition & 1 deletion profiles/upstreams.json
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,6 @@
},
"mattpocock/skills": {
"repository": "https://github.com/mattpocock/skills",
"commit": "49dd158d1076134a641b33efb035946536778336"
"commit": "4ae2317ddb22b046e26455e4f9c9b3aaaf62212d"
}
}
2 changes: 1 addition & 1 deletion scripts/context.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ test("run-role resolves a configured model override", () => {
assert.equal(result.status, 0, result.stderr);
const plan = JSON.parse(result.stdout);
assert.equal(plan.model, "remote-model");
assert.deepEqual(plan.args, ["run", "--pure", "--model", "remote-model", "inspect the repository"]);
assert.deepEqual(plan.args, ["run", "--model", "remote-model", "inspect the repository"]);
} finally {
rmSync(root, { recursive: true, force: true });
}
Expand Down
2 changes: 1 addition & 1 deletion scripts/environment.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ test("OpenCode read-only plans select the built-in plan agent", () => {
], { cwd: resolve("."), encoding: "utf8" });
assert.equal(result.status, 0, result.stderr);
const plan = JSON.parse(result.stdout);
assert.deepEqual(plan.args, ["run", "--pure", "inspect the repository", "--agent", "plan"]);
assert.deepEqual(plan.args, ["run", "inspect the repository", "--agent", "plan"]);
});

test("SSH invocation rejects an unsafe host before spawning", () => {
Expand Down
42 changes: 39 additions & 3 deletions scripts/history.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ test("pi default storage uses only the requested workspace slug and keeps branch
assert.deepEqual(result.warnings, []);
});

test("OpenCode filters list metadata before exporting one sanitized session", async (t) => {
test("OpenCode 1 filters list metadata before exporting one sanitized session", async (t) => {
const { root, workspace } = fixture(t);
const calls = [];
const runOpenCode = (args, cwd) => {
Expand All @@ -132,7 +132,7 @@ test("OpenCode filters list metadata before exporting one sanitized session", as
];
return { info: { id: "wanted", directory: "[redacted:session-directory:wanted]" }, messages: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Sanitized answer" }, { type: "tool", text: "Do not emit" }] }] };
};
const options = { harness: "opencode", workspace, runOpenCode, env: {} };
const options = { harness: "opencode", workspace, runOpenCode, openCodeMajor: () => 1, env: {} };
const listing = await history({ ...options, command: "list" });
assert.deepEqual(listing.sessions.map((session) => session.id), ["wanted"]);
assert.equal(calls.length, 1);
Expand All @@ -148,6 +148,42 @@ test("OpenCode filters list metadata before exporting one sanitized session", as
}
});

test("OpenCode 2 uses session export and reads its message schema", async (t) => {
const { root, workspace } = fixture(t);
const calls = [];
const runOpenCode = (args, cwd) => {
calls.push({ args, cwd });
if (args[1] === "list") return [
{ id: "other", directory: join(root, "other"), updated: Date.now() },
{ id: "wanted", directory: workspace, updated: Date.now() },
];
return { info: { id: "wanted", location: { directory: "/[redacted:session-directory:wanted]" } }, messages: [
{ type: "user", text: "Sanitized prompt" },
{ type: "assistant", content: [{ type: "reasoning", text: "Do not emit" }, { type: "text", text: "Sanitized answer" }] },
{ type: "compaction", status: "completed", summary: "Earlier work" },
{ type: "shell", command: "Do not emit" },
] };
};
const options = { harness: "opencode", workspace, runOpenCode, openCodeMajor: () => 2, env: {} };
const listing = await history({ ...options, command: "list" });
assert.deepEqual(calls.at(-1).args, ["session", "list", "--format", "json"]);
assert.deepEqual(listing.sessions.map((session) => session.id), ["wanted"]);
const reading = await history({ ...options, command: "read", session: "wanted" });
assert.deepEqual(calls.at(-1), { args: ["session", "export", "wanted", "--sanitize"], cwd: workspace });
assert.deepEqual(reading.messages, [
{ role: "user", text: "Sanitized prompt" },
{ role: "assistant", text: "Sanitized answer" },
{ role: "summary", text: "Earlier work" },
]);
await history({ ...options, command: "read", session: "wanted", localText: true });
assert.deepEqual(calls.at(-1).args, ["session", "export", "wanted"]);
for (const directory of [join(root, "other"), "/[redacted:session-directory:other]"]) {
const wrongExport = (args, cwd) => args[1] === "list" ? runOpenCode(args, cwd)
: { info: { id: "wanted", location: { directory } }, messages: [] };
await assert.rejects(history({ ...options, runOpenCode: wrongExport, command: "read", session: "wanted" }), /does not match/);
}
});

test("missing stores and malformed records produce controlled diagnostics without raw content", async (t) => {
const { root, workspace } = fixture(t);
const options = { harness: "codex", workspace, root: join(root, "missing"), command: "list", env: {} };
Expand All @@ -170,7 +206,7 @@ test("OpenCode warns when sanitizer removes text and requires an explicit local-
const runOpenCode = (args) => args[0] === "session" ? [{ id: "session", directory: workspace, updated: Date.now() }]
: { info: { id: "session", directory: args.includes("--sanitize") ? "[redacted:session-directory:session]" : workspace },
messages: [{ info: { role: "user" }, parts: [{ type: "text", text: args.includes("--sanitize") ? "[redacted:text:part]" : "Local fixture prompt" }] }] };
const options = { harness: "opencode", workspace, runOpenCode, command: "read", session: "session" };
const options = { harness: "opencode", workspace, runOpenCode, openCodeMajor: () => 1, command: "read", session: "session" };
const sanitized = await history(options);
assert.equal(sanitized.sanitized, true);
assert.match(sanitized.warnings[0], /redacted message text/);
Expand Down
2 changes: 1 addition & 1 deletion skills/codebase-design/references/design-alternatives.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Show this to the user, then immediately proceed to Step 2. The user reads and th

### 2. Spawn sub-agents

Use the active Harness's native delegation to assign three independent candidates, within its available concurrency. Each must produce a **radically different** interface for the deepened module. If delegation is unavailable, run separate design passes with these same briefs and disclose that they share one agent's context. Keep the passes read-only; design alternatives do not need to edit a shared checkout.
Use the active Harness's native delegation to assign three independent candidates, within its available concurrency. Start each one with a fresh context window rather than a fork of this conversation; some Harnesses fork by default, and a fork carries over the context the candidate exists to escape. Each must produce a **radically different** interface for the deepened module. If delegation is unavailable, run separate design passes with these same briefs and disclose that they share one agent's context. Keep the passes read-only; design alternatives do not need to edit a shared checkout.

Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [Deepening](deepening.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint:

Expand Down
18 changes: 14 additions & 4 deletions skills/recall/references/history-sources.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,31 @@ the helper does not fall back to scanning all project directories.

## OpenCode

Prefer the stable CLI surface:
Prefer the stable CLI surface. The helper runs `opencode --version` first,
because OpenCode 2 removed `--pure` and moved `export` under `session`:

```bash
# OpenCode 1 (npm package opencode-ai)
opencode session list --format json --pure
opencode export <sessionID> --sanitize --pure
# OpenCode 2 (npm package @opencode/cli)
opencode session list --format json
opencode session export <sessionID> --sanitize
```

Use `--sanitize` whenever exported data will leave the local machine. Do not
depend on OpenCode's internal database layout unless the CLI cannot provide the
required authorized record. The helper runs these commands with the requested
workspace as its working directory. It filters list entries by exact `directory`
and excludes `parentID` sessions before exporting the selected ID, then verifies
the export's `info.id` and `info.directory`. Sanitized exports may replace the
directory with `[redacted:session-directory:<session-id>]`; the helper accepts
only the marker for the already selected session. It inherits OpenCode's configured
the export's `info.id` and directory: `info.directory` in OpenCode 1 and
`info.location.directory` in OpenCode 2. Sanitized exports may replace the
directory with `[redacted:session-directory:<session-id>]`, which OpenCode 2
prefixes with `/`; the helper accepts only the marker for the already selected
session. OpenCode 2 exports user prompts as `text`, assistant replies as
`content` parts, and compaction summaries as `summary`; the helper reads those
and skips tool, shell, and reasoning entries. OpenCode 2 lists only top-level
sessions, at most 100 by default. It inherits OpenCode's configured
environment, including `XDG_DATA_HOME`; `--root` is not supported. A missing CLI
or unsupported JSON schema is an error, not an empty-history result.

Expand Down
Loading
Loading