@uniqbit/mate-core 0.15.4 → 0.15.5-canary.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/package.json +1 -1
- package/src/cli/commands/artifact/finish/openspec.ts +180 -12
- package/src/cli/commands/companion/link.ts +9 -13
- package/src/cli/commands/working/cleanup.ts +125 -0
- package/src/cli/commands/working/working.ts +15 -0
- package/src/cli/commands/workspace/list.ts +25 -0
- package/src/cli/commands/workspace/materialize.ts +46 -0
- package/src/cli/commands/workspace/workspace.ts +22 -0
- package/src/cli/main.ts +20 -1
- package/src/cli/usage.ts +8 -0
- package/src/cli/write-json-stdout.ts +14 -0
- package/src/lib/orchestrator/companion-registry-reader.ts +37 -0
- package/src/lib/orchestrator/{working-repo-store.ts → companion-registry-store.ts} +14 -7
- package/src/lib/orchestrator/companion-resolver.ts +50 -2
- package/src/lib/orchestrator/companion-store.ts +39 -10
- package/src/lib/orchestrator/editor.ts +36 -14
- package/src/lib/orchestrator/framework-context.ts +33 -3
- package/src/lib/orchestrator/global-config-store.ts +25 -0
- package/src/lib/orchestrator/repo-local-registry.ts +2 -22
- package/src/lib/orchestrator/types.ts +1 -1
- package/src/lib/orchestrator/workspace-inventory.ts +149 -0
- package/src/lib/orchestrator/workspace-materialize.ts +80 -0
- package/src/lib/orchestrator/yaml-file-store.ts +10 -1
- package/src/templates/capabilities/openspec-cap/mate-v1/schema.yaml +40 -14
- package/src/templates/capabilities/openspec-cap/mate-v1/templates/spec.md +9 -6
- package/src/templates/root/TEMPLATE_AGENTS.md +0 -8
- package/src/templates/root/TEMPLATE_CLAUDE.md +0 -10
- package/src/tools/setup/__snapshots__/runtime-surface-golden.test.ts.snap +44 -380
- package/src/tools/setup/plugins/gitignore.ts +3 -0
- package/src/tools/setup/providers/claude.ts +68 -56
- package/src/tools/setup/working-repo-cleanup.ts +40 -0
- package/src/tools/setup/working-repo-local-state.ts +103 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
1
2
|
import fs from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
|
|
@@ -22,8 +23,16 @@ export abstract class YamlFileStore<T> {
|
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
/** Writes via a sibling temp file + `fs.rename()` so a process killed mid-write cannot leave a truncated/corrupt file. */
|
|
25
27
|
async save(data: T): Promise<void> {
|
|
26
28
|
await fs.mkdir(path.dirname(this.configPath), { recursive: true });
|
|
27
|
-
|
|
29
|
+
const tempPath = `${this.configPath}.${crypto.randomUUID()}.tmp`;
|
|
30
|
+
try {
|
|
31
|
+
await fs.writeFile(tempPath, stringify(data), "utf8");
|
|
32
|
+
await fs.rename(tempPath, this.configPath);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
await fs.rm(tempPath, { force: true }).catch(() => {});
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
28
37
|
}
|
|
29
38
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: mate-v1
|
|
2
|
-
version:
|
|
2
|
+
version: 6
|
|
3
3
|
description: Mate's repository- and Area-scoped OpenSpec workflow - explore → proposal → specs → design → tasks
|
|
4
4
|
artifacts:
|
|
5
5
|
- id: explore
|
|
@@ -46,7 +46,9 @@ artifacts:
|
|
|
46
46
|
- Scopes are recorded ONLY in the frontmatter `scopes` list — do not add a `## Scopes` body section. Every change MUST name at least one scope.
|
|
47
47
|
- Every `scopes` entry MUST pair `repository: org/repository` with an `area` selected according to the repository layout: the owning workspace/package root in a monorepo — the directory containing that package's manifest (`package.json`, `Cargo.toml`, `go.mod`, etc.) nearest the affected path (for example, `acme`, `apps/storefront`, or `packages/ui`, not `acme/src/sub-1/sub-2` or `apps/storefront/src/features/checkout`), or the exact affected path in a non-monorepo repository (for example, `docs`); use `.` only when the repository root itself is affected.
|
|
48
48
|
- Repository identity MUST come from the Git remote. Normalize SSH and HTTPS forms such as `git@github.com:org/repository.git` and `https://github.com/org/repository.git` to `org/repository`.
|
|
49
|
-
-
|
|
49
|
+
- Default a new capability to one package root: prefer one capability per owning package root over a single capability spanning several Areas.
|
|
50
|
+
- A change MAY declare scopes in several repositories, but each capability spec names exactly one. When a change spans repositories, list one capability per repository under Capabilities.
|
|
51
|
+
- Area identity is metadata: scope is read only from the frontmatter `scopes`, never from a change or capability folder name, though a descriptive name aligned with its package root is permitted.
|
|
50
52
|
- Do not use a local checkout directory basename, Mate's internal repository ID, a synthetic Area token, or `N/A` as durable metadata.
|
|
51
53
|
|
|
52
54
|
**Obsidian**: Emit this YAML frontmatter at the very top of the file. Replace `<change-name>` with the actual change folder name (e.g. `my-change`):
|
|
@@ -85,14 +87,14 @@ artifacts:
|
|
|
85
87
|
**Path and scope rules** — spec files are ALWAYS flat; paired scope metadata is required, never a folder:
|
|
86
88
|
- Every spec lives at `specs/<capability>/spec.md`. Never interpose an Area folder. `specs/<area>/<capability>/spec.md` breaks the OpenSpec CLI: it parses spec deltas only at the flat path, so an Area folder makes `openspec show`/`validate` report zero deltas.
|
|
87
89
|
- Every delta and canonical spec MUST record a `scopes` frontmatter list whose entries pair `repository: org/repository` with an `area` selected according to the repository layout. Monorepo Areas stop at workspace/package roots such as `acme`, `apps/storefront`, or `packages/ui` — the directory containing that package's manifest (`package.json`, `Cargo.toml`, `go.mod`, etc.) nearest the affected path; non-monorepo Areas remain exact paths such as `docs` or `.`.
|
|
88
|
-
- Each scope pair is independent. Preserve every repository/Area pair
|
|
89
|
-
- Example paired scopes: `{ repository: org/product, area: acme }`, `{ repository: org/product, area: apps/storefront }`, `{ repository: org/product, area: packages/api }
|
|
90
|
+
- Each scope pair is independent. Preserve every repository/Area pair; never use separate parallel `repositories` and `areas` arrays.
|
|
91
|
+
- Example paired scopes for one spec: `{ repository: org/product, area: acme }`, `{ repository: org/product, area: apps/storefront }`, and `{ repository: org/product, area: packages/api }` — same repository throughout. A capability in `org/other` is its own spec.
|
|
90
92
|
- Do not split a monorepo Area into deeper entries such as `acme/src`, `acme/public`, or `apps/storefront/src/features/checkout`; those paths remain inside the `acme` Area (or `apps/storefront`, `packages/ui`, etc.) regardless of how many source subfolders the change touches. A non-monorepo repository may use exact subpaths such as `docs` to identify the affected Area.
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
- Capability names are
|
|
93
|
+
- Default a new capability to a single scope entry for the owning package root. When a change affects two package roots, write two capability specs rather than one spec with two Areas; reserve multi-Area specs for behavior genuinely shared across those roots.
|
|
94
|
+
- EVERY requirement MUST carry an `**Area:**` marker naming the Areas it binds — in every spec, including a spec whose frontmatter names only one Area and a requirement that binds all of them. The marker may list several as backtick-quoted, comma-separated values (for example, `**Area:** \`acme\`, \`packages/api\``), and every listed Area MUST appear in the frontmatter `scopes`.
|
|
95
|
+
- Mark unconditionally, never by cascade: a requirement's Area is then readable without consulting the frontmatter, it survives archive creating a new main spec (which discards frontmatter but copies requirement blocks verbatim), and a spec that later gains an Area needs no edit to its existing requirements.
|
|
96
|
+
- A spec MUST name exactly one repository: every `scopes` entry in one spec repeats the same `repository`, and requirement-level `**Repository:**` markers do not exist in this model. Cross-repository work is specified as one capability per repository, with shared contracts in a capability owned by one of them. A change's proposal MAY still declare scopes in several repositories — this rule binds each spec, not the change.
|
|
97
|
+
- Capability names are unique per OpenSpec root (`specs/<capability>/spec.md` is flat) and carry no authority over scope: repository and Area are read ONLY from the frontmatter `scopes`, never from the capability name. A descriptive name aligned with its package root is permitted where it aids uniqueness.
|
|
96
98
|
- Do not use a local checkout directory basename, Mate's internal repository ID, a synthetic Area token, or `N/A`.
|
|
97
99
|
- Modified capabilities: match the existing `specs/<capability>/spec.md` path exactly.
|
|
98
100
|
|
|
@@ -105,11 +107,13 @@ artifacts:
|
|
|
105
107
|
Format requirements:
|
|
106
108
|
- Each requirement: `### Requirement: <name>` followed by description
|
|
107
109
|
- Use SHALL/MUST for normative requirements (avoid should/may)
|
|
108
|
-
-
|
|
110
|
+
- An `**Area:**` marker, when required, goes after the requirement's SHALL/MUST statement and before its first scenario. For a REMOVED requirement without a normative statement, put it after `**Migration**`.
|
|
109
111
|
- Each scenario: `#### Scenario: <name>` with WHEN/THEN format
|
|
110
112
|
- **CRITICAL**: Scenarios MUST use exactly 4 hashtags (`####`). Using 3 hashtags or bullets will fail silently.
|
|
111
113
|
- Every requirement MUST have at least one scenario.
|
|
112
114
|
|
|
115
|
+
New capabilities only: after the frontmatter and backlink lines, start the delta spec body with a `## Purpose` section - one or two sentences (50+ characters, or `openspec validate --strict` reports it as too brief) describing what the capability is for. Archive copies it into the main spec it creates; without it the new main spec is left with a `TBD ... Update Purpose after archive.` placeholder to fill in by hand. Do NOT add `## Purpose` to a delta for an existing capability - that spec already has one and the delta's is ignored. To change an existing capability's Purpose - including a leftover `TBD` placeholder - edit `openspec/specs/<capability>/spec.md` directly.
|
|
116
|
+
|
|
113
117
|
MODIFIED requirements workflow:
|
|
114
118
|
1. Locate the existing requirement in `openspec/specs/<capability>/spec.md`.
|
|
115
119
|
2. Copy the ENTIRE requirement block (from `### Requirement:` through all scenarios)
|
|
@@ -118,13 +122,14 @@ artifacts:
|
|
|
118
122
|
|
|
119
123
|
Common pitfall: Using MODIFIED with partial content loses detail at archive time. If adding new concerns without changing existing behavior, use ADDED instead.
|
|
120
124
|
|
|
121
|
-
Example (frontmatter scopes cover `acme` and `packages/api`; the first
|
|
125
|
+
Example (frontmatter scopes cover `acme` and `packages/api`; every requirement carries an `**Area:**` marker, as always — the first binds both, the second one):
|
|
122
126
|
|
|
123
127
|
```
|
|
124
128
|
## ADDED Requirements
|
|
125
129
|
|
|
126
130
|
### Requirement: User can export data
|
|
127
131
|
The system SHALL allow users to export their data in CSV format.
|
|
132
|
+
**Area:** `acme`, `packages/api`
|
|
128
133
|
|
|
129
134
|
#### Scenario: Successful export
|
|
130
135
|
- **WHEN** user clicks "Export" button
|
|
@@ -156,12 +161,33 @@ artifacts:
|
|
|
156
161
|
|
|
157
162
|
After the file's `#` title line, add a backlink on the next line: `← [[proposal]]`. If modifying an existing main spec, add on the line after: `Applies to: [[openspec/specs/<capability>/spec]]`.
|
|
158
163
|
|
|
164
|
+
**Canonical spec frontmatter** — a DIFFERENT block, used ONLY by canonical specs at `openspec/specs/<capability>/spec.md`. A canonical spec uses flat scalars, never a nested `scopes` list:
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
---
|
|
168
|
+
type: spec
|
|
169
|
+
capability: <capability>
|
|
170
|
+
repository: org/repository
|
|
171
|
+
areas: [<area>, <area>]
|
|
172
|
+
tags: [openspec/spec]
|
|
173
|
+
---
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Which block applies where: change artifacts — explore brief, proposal, delta spec, design, and tasks — keep paired `scopes`, because a change MAY declare scopes in several repositories. Canonical specs use the flat block above and MUST NOT carry a `scopes` key, because the one-repository-per-spec rule leaves no repository/Area pair to bind. Flat scalars are also the only shape Obsidian properties, property search, and Bases can filter or group; a list of mappings is an unsupported property type.
|
|
177
|
+
|
|
178
|
+
Projection from a delta spec to its canonical spec — both the agent-driven merge and any deterministic reconciliation MUST derive the canonical block this way, so the two agree:
|
|
179
|
+
1. Read the delta spec's `scopes` entries.
|
|
180
|
+
2. Assert every `repository` value is identical. If they differ, projection FAILS — never pick one and never emit a list; correct the delta by splitting it into one capability per repository.
|
|
181
|
+
3. Emit that repository as the scalar `repository`, and the distinct `area` values as `areas` in their original order.
|
|
182
|
+
4. Set `capability` from the spec's directory name and `tags` to `[openspec/spec]`. Delta-only keys — `change` and the delta tags `openspec/change`/`openspec/delta` — MUST NOT carry over.
|
|
183
|
+
|
|
159
184
|
Migration rules for existing artifacts and canonical specs:
|
|
160
185
|
1. Normalize each Area to the owning workspace/package root for monorepos — the directory containing that package's manifest (`package.json`, `Cargo.toml`, `go.mod`, etc.) nearest the affected path, e.g. collapse `apps/storefront/src/features/checkout` to `apps/storefront`; for non-monorepos, retain the exact affected repository-relative path and use `.` only when the repository root is the intended Area.
|
|
161
186
|
2. Replace local, internal, synthetic, or otherwise non-portable repository and Area values with canonical Git remote identities and exact repository-relative paths.
|
|
162
|
-
3. Add mandatory
|
|
163
|
-
4. Remove requirement-level `**Repository:**`
|
|
164
|
-
5. Preserve paired scope metadata and
|
|
187
|
+
3. Add mandatory scope frontmatter to every delta and canonical spec that lacks it — paired `scopes` on a delta spec, the flat `repository`/`areas` block on a canonical spec.
|
|
188
|
+
4. Remove every requirement-level `**Repository:**` marker; if a legacy spec names more than one repository, split it into one capability per repository. Add an `**Area:**` marker to every requirement that lacks one, including in a spec whose frontmatter names only one Area.
|
|
189
|
+
5. Preserve paired scope metadata and requirement-level Area markers when archiving, validating, displaying, or finishing artifacts.
|
|
190
|
+
6. Convert a canonical spec still carrying a nested `scopes` list to the flat block: collapse the repeated `repository` value to the scalar `repository`, list the distinct `area` values as `areas`, and delete the `scopes` key. Strip leaked delta-only keys — `change`, a flat legacy `area`, and the `openspec/change`/`openspec/delta` tags — and add `type: spec` and `capability` where missing. A canonical spec that predates this rule stays readable until converted; do not treat the legacy shape as a validation failure.
|
|
165
191
|
requires:
|
|
166
192
|
- proposal
|
|
167
193
|
- id: design
|
|
@@ -8,16 +8,19 @@ scopes:
|
|
|
8
8
|
area: .
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
## Purpose
|
|
12
|
+
|
|
13
|
+
<!-- New capabilities only: one or two sentences (50+ characters) on what this capability is for. Delete this section for an existing capability. -->
|
|
14
|
+
|
|
11
15
|
## ADDED Requirements
|
|
12
16
|
|
|
13
17
|
### Requirement: <!-- requirement name -->
|
|
14
18
|
|
|
15
|
-
<!-- requirement text.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
**Area:**
|
|
19
|
-
|
|
20
|
-
repository and this requirement binds fewer of them. -->
|
|
19
|
+
<!-- requirement text. EVERY requirement carries an **Area:** marker naming the Areas it
|
|
20
|
+
binds, using values from the frontmatter scopes — always, even when there is only one
|
|
21
|
+
Area. Put it on the line below, e.g.:
|
|
22
|
+
**Area:** `packages/ui`
|
|
23
|
+
Every scopes entry names the same repository, so no **Repository:** marker is ever used. -->
|
|
21
24
|
|
|
22
25
|
#### Scenario: <!-- scenario name -->
|
|
23
26
|
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
<!-- MATE:COMPANION:START -->
|
|
2
2
|
|
|
3
|
-
You are operating in a Mate-managed session.
|
|
4
|
-
|
|
5
|
-
Canonical companion policy is injected through `<companion-policy framework="mate" priority="mandatory">`.
|
|
6
|
-
This block is kept for AGENTS.md compatibility and must not restate that policy.
|
|
7
|
-
|
|
8
|
-
## Agent Notes
|
|
9
|
-
|
|
10
|
-
- Use absolute paths when a tool needs a file path. Do not create literal `$MATE_REPO_PATH` or `$MATE_ARTIFACT_PATH` directories.
|
|
11
3
|
- When reporting information to me, be extremely concise and sacrifice grammar for the sake of concision. Apply this same preference to JSDoc.
|
|
12
4
|
- Code comments: JSDoc format only (`/** ... */`), never `//`. Sparse — only non-obvious invariants or constraints, never restated artifact rationale.
|
|
13
5
|
- Never add a `Co-Authored-By: <model>` trailer or model-attribution footer to commit messages.
|
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
<!-- MATE:COMPANION:START -->
|
|
2
2
|
|
|
3
|
-
You are operating in a Mate-managed session.
|
|
4
|
-
|
|
5
|
-
Canonical companion policy is injected through `<companion-policy framework="mate" priority="mandatory">`.
|
|
6
|
-
This block is kept for Claude/AGENTS.md compatibility and must not restate that policy.
|
|
7
|
-
|
|
8
|
-
## Claude Notes
|
|
9
|
-
|
|
10
|
-
- Use absolute paths when a tool needs a file path. Do not create literal `$MATE_REPO_PATH` or `$MATE_ARTIFACT_PATH` directories.
|
|
11
3
|
- When reporting information to me, be extremely concise and sacrifice grammar for the sake of concision. Apply this same preference to JSDoc.
|
|
12
4
|
- Code comments: JSDoc format only (`/** ... */`), never `//`. Sparse — only non-obvious invariants or constraints, never restated artifact rationale.
|
|
13
|
-
- Claude Code receives `$MATE_ARTIFACT_PATH` via `--add-dir`; if `@` autocomplete does not show companion artifacts, reference them by absolute path.
|
|
14
|
-
- The root `CLAUDE.md` in the companion repo is intentional and loaded by Mate. Do not create another project-level `CLAUDE.md` in the working repo unless the user explicitly asks for one.
|
|
15
5
|
- Never add a `Co-Authored-By: <model>` trailer or model-attribution footer to commit messages.
|
|
16
6
|
- Never commit, push, or open a pull request in the working repo (`$MATE_REPO_PATH`) unless the user explicitly asks for it.
|
|
17
7
|
- Never connect to a database (local or remote), touch live/external systems (deploys, infra), or take destructive/irreversible actions without asking the user first.
|