pi-matt-pocock 0.2.2 → 0.3.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/ARCHITECTURE.zh-CN.md +37 -19
- package/README.md +80 -13
- package/package.json +12 -7
- package/procedures/code-review.md +25 -17
- package/procedures/codebase-design.md +11 -10
- package/procedures/deslop.md +4 -2
- package/procedures/diagnosing-bugs.md +25 -25
- package/procedures/domain-modeling.md +18 -20
- package/procedures/grill-me.md +1 -1
- package/procedures/grill-with-docs.md +1 -1
- package/procedures/grilling.md +29 -5
- package/procedures/handoff.md +1 -1
- package/procedures/implement.md +7 -1
- package/procedures/improve-codebase-architecture.md +22 -22
- package/procedures/pr.md +169 -0
- package/procedures/prototype.md +7 -6
- package/procedures/research.md +2 -2
- package/procedures/retro.md +42 -0
- package/procedures/setup-matt-pocock-skills.md +26 -22
- package/procedures/tdd.md +17 -15
- package/procedures/teach.md +11 -7
- package/procedures/to-questionnaire.md +7 -5
- package/procedures/to-spec.md +2 -2
- package/procedures/to-tickets.md +12 -12
- package/procedures/triage.md +22 -22
- package/procedures/wait-what.md +1 -1
- package/procedures/wayfinder.md +24 -24
- package/procedures/wizard.md +7 -7
- package/procedures/writing-for-agents.md +14 -14
- package/{procedures → references}/ADR-FORMAT.md +11 -11
- package/{procedures → references}/AGENT-BRIEF.md +4 -4
- package/{procedures → references}/DEEPENING.md +3 -3
- package/{procedures → references}/DESIGN-IT-TWICE.md +7 -7
- package/{procedures → references}/GLOSSARY-FORMAT.md +9 -2
- package/{procedures → references}/HTML-REPORT.md +15 -15
- package/{procedures → references}/LEARNING-RECORD-FORMAT.md +10 -10
- package/{procedures → references}/LOGIC.md +5 -5
- package/{procedures → references}/MISSION-FORMAT.md +2 -2
- package/{procedures → references}/OUT-OF-SCOPE.md +11 -11
- package/{procedures → references}/RESOURCES-FORMAT.md +1 -1
- package/{procedures → references}/SKILL-MECHANICS.md +3 -3
- package/{procedures → references}/UI.md +14 -14
- package/{procedures → references}/bdd-best-practices.md +3 -3
- package/{procedures → references}/bdd.md +9 -9
- package/{procedures → references}/domain.md +12 -12
- package/references/issue-tracker-github.md +50 -0
- package/{procedures → references}/issue-tracker-gitlab.md +9 -9
- package/{procedures → references}/issue-tracker-local.md +3 -3
- package/{procedures → references}/phase-boundaries.md +8 -8
- package/src/catalog.json +49 -28
- package/src/catalog.ts +58 -2
- package/src/index.ts +521 -383
- package/src/native-dialog.ts +149 -213
- package/src/resolver.ts +30 -14
- package/src/workflow.ts +55 -16
- package/upstream/UPSTREAM.md +116 -0
- package/{scripts → upstream}/check-upstream-sync.mjs +12 -12
- package/{upstream-selection.json → upstream/upstream-selection.json} +9 -7
- package/UPSTREAM.md +0 -63
- package/procedures/CONTEXT-FORMAT.md +0 -60
- package/procedures/issue-tracker-github.md +0 -45
- /package/{procedures → assets}/hitl-loop.template.sh +0 -0
- /package/{procedures → assets}/template.sh +0 -0
- /package/{procedures → references}/GLOSSARY.md +0 -0
- /package/{procedures → references}/gherkin-guide.md +0 -0
- /package/{procedures → references}/mocking.md +0 -0
- /package/{procedures → references}/testing-anti-patterns.md +0 -0
- /package/{procedures → references}/tests.md +0 -0
- /package/{procedures → references}/triage-labels.md +0 -0
- /package/{procedures → references}/writing-great-skills.md +0 -0
package/src/workflow.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
allowedTransitions,
|
|
3
3
|
modelStandaloneCapabilities,
|
|
4
4
|
normalizeProcedureId,
|
|
5
|
+
userStandaloneCapabilities,
|
|
5
6
|
workflowEntry,
|
|
6
7
|
workflowPlacement,
|
|
7
8
|
workflowRoutes as catalogWorkflowRoutes,
|
|
@@ -61,6 +62,12 @@ export function routeEntryState(route: string, workItemId: string): WorkflowStat
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
export function transitionState(state: WorkflowState, target: string): WorkflowState {
|
|
65
|
+
const currentPlacement = workflowPlacement(state.route, state.procedure);
|
|
66
|
+
if (!currentPlacement || currentPlacement.phase !== state.phase) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`Cannot transition ${state.route} from ${state.procedure}: invalid current procedure state.`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
64
71
|
const normalized = normalizeProcedureId(target);
|
|
65
72
|
const allowed = allowedTransitions(state.route, state.procedure);
|
|
66
73
|
if (!allowed.includes(normalized)) {
|
|
@@ -75,6 +82,7 @@ export function transitionState(state: WorkflowState, target: string): WorkflowS
|
|
|
75
82
|
procedure: normalized,
|
|
76
83
|
phase: placement.phase,
|
|
77
84
|
loadedReferences: [],
|
|
85
|
+
deliveredProcedures: state.deliveredProcedures ?? [],
|
|
78
86
|
};
|
|
79
87
|
}
|
|
80
88
|
|
|
@@ -106,15 +114,19 @@ export function formatReadableWorkflowSubject(route: string, phase: string): str
|
|
|
106
114
|
return `${readableRouteTitle(route)} · ${readablePhaseTitle(phase)}`;
|
|
107
115
|
}
|
|
108
116
|
|
|
109
|
-
|
|
110
|
-
if (!value || typeof value !== "object") return
|
|
117
|
+
function workflowRecord(value: unknown): Record<string, unknown> | undefined {
|
|
118
|
+
if (!value || typeof value !== "object") return undefined;
|
|
111
119
|
const candidate = value as Record<string, unknown>;
|
|
112
120
|
return candidate.version === WORKFLOW_STATE_VERSION
|
|
113
|
-
&& typeof candidate.workItemId === "string"
|
|
121
|
+
&& typeof candidate.workItemId === "string" && candidate.workItemId.trim().length > 0
|
|
114
122
|
&& typeof candidate.route === "string"
|
|
115
123
|
&& typeof candidate.procedure === "string"
|
|
116
|
-
&& typeof candidate.phase === "string"
|
|
117
|
-
|
|
124
|
+
&& typeof candidate.phase === "string" ? candidate : undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function isWorkflowState(value: unknown): value is WorkflowState {
|
|
128
|
+
const candidate = workflowRecord(value);
|
|
129
|
+
return candidate !== undefined && candidate.status === "active"
|
|
118
130
|
&& Array.isArray(candidate.loadedReferences)
|
|
119
131
|
&& candidate.loadedReferences.every((reference) => typeof reference === "string")
|
|
120
132
|
// Absent is tolerated so a workflow persisted before delivery tracking existed still
|
|
@@ -130,13 +142,8 @@ export function withDeliveryDefaults(state: WorkflowState): WorkflowState {
|
|
|
130
142
|
}
|
|
131
143
|
|
|
132
144
|
export function isTerminalWorkflowState(value: unknown): value is TerminalWorkflowState {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return candidate.version === WORKFLOW_STATE_VERSION
|
|
136
|
-
&& typeof candidate.workItemId === "string"
|
|
137
|
-
&& typeof candidate.route === "string"
|
|
138
|
-
&& typeof candidate.procedure === "string"
|
|
139
|
-
&& typeof candidate.phase === "string"
|
|
145
|
+
const candidate = workflowRecord(value);
|
|
146
|
+
return candidate !== undefined
|
|
140
147
|
&& (candidate.status === "completed" || candidate.status === "cancelled");
|
|
141
148
|
}
|
|
142
149
|
|
|
@@ -144,7 +151,9 @@ export function latestWorkflowRecord(entries: unknown[]): WorkflowSessionData |
|
|
|
144
151
|
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
145
152
|
const entry = entries[index] as { type?: unknown; customType?: unknown; data?: unknown } | undefined;
|
|
146
153
|
if (entry?.type !== "custom" || entry.customType !== WORKFLOW_STATE_ENTRY) continue;
|
|
147
|
-
|
|
154
|
+
// The newest entry is authoritative even when incompatible. Never resurrect
|
|
155
|
+
// an older active workflow by skipping a malformed terminal or future version.
|
|
156
|
+
return isWorkflowState(entry.data) || isTerminalWorkflowState(entry.data) ? entry.data : undefined;
|
|
148
157
|
}
|
|
149
158
|
return undefined;
|
|
150
159
|
}
|
|
@@ -161,17 +170,42 @@ Work item: ${state.workItemId}. Follow the loaded ${state.procedure} procedure.
|
|
|
161
170
|
|
|
162
171
|
Drive the current procedure to its end in this turn. A multi-step deliverable is not finished because one step passed: keep implementing, testing and fixing until every step the procedure covers is done, rather than stopping at the first green slice or handing back a plan for the remainder. Do not end the turn to ask whether to continue.
|
|
163
172
|
|
|
164
|
-
Use matt_pocock_active to transition only to: ${next.join(", ") || "none"}; load an available reference; complete the work; or cancel it with a reason. Available references: ${availableReferences.join(", ") || "none"}. Exact shapes: {"action": "transition", "target": "<allowedNext id>"}; {"action": "load", "reference": "<availableReferences id>"}; {"action": "complete"}; {"action": "cancel", "reason": "<why>"}. Proceed autonomously through non-user-owned work: transition to the next applicable procedure as soon as this one's deliverables are complete, without stopping to ask permission. Ask only for a genuinely user-owned decision, an unavailable fact, or a required external action, and use matt_pocock_ask for those.
|
|
173
|
+
Use matt_pocock_active to transition only to: ${next.join(", ") || "none"}; load an available reference; complete the work; or cancel it with a reason. Available references: ${availableReferences.join(", ") || "none"}. Exact shapes: {"action": "transition", "target": "<allowedNext id>"}; {"action": "load", "reference": "<availableReferences id>"}; {"action": "complete"}; {"action": "cancel", "reason": "<why>"}. Proceed autonomously through non-user-owned work: transition to the next applicable procedure as soon as this one's deliverables are complete, without stopping to ask permission. Ask only for a genuinely user-owned decision, an unavailable fact, or a required external action, and use matt_pocock_ask for those, with the required recommended option its deadline adopts.
|
|
165
174
|
|
|
166
175
|
Yield only when the remaining work genuinely depends on an external actor, on a teammate result you cannot produce yourself, or on context you no longer have — and when you yield, state plainly what is unfinished rather than implying completion. Never announce completion early: call matt_pocock_active with action complete only once the integrated candidate has applicable verification and all blocking reviews have returned with their findings resolved; a completed review assignment is not itself a PASS verdict, and evidence must match that candidate.`;
|
|
167
176
|
}
|
|
168
177
|
|
|
178
|
+
/** One terse line per user-invoked capability. The prompt budget governs this
|
|
179
|
+
* section, so it names the id and its subject rather than repeating the full
|
|
180
|
+
* catalog description the menu and the tool schema already carry. */
|
|
181
|
+
function userCapabilitySummary(definition: { id: string; label: string }): string {
|
|
182
|
+
return userCapabilitySummaries[definition.id] ?? definition.label;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const userCapabilitySummaries: Record<string, string> = {
|
|
186
|
+
"grill-me": "relentless one-decision-at-a-time interview; \"grill me\" names it",
|
|
187
|
+
handoff: "handoff document another agent can pick up cold",
|
|
188
|
+
retro: "review recent work and record what to change",
|
|
189
|
+
"improve-codebase-architecture": "rank and improve architecture deepening opportunities",
|
|
190
|
+
"setup-matt-pocock-skills": "configure the repository tracker and document layout",
|
|
191
|
+
teach: "teach a concept step by step",
|
|
192
|
+
"to-questionnaire": "turn a decision blocked on someone else into a questionnaire",
|
|
193
|
+
"wait-what": "re-pitch the last message with the missing context",
|
|
194
|
+
};
|
|
195
|
+
|
|
169
196
|
export function availableWorkflowsGuidance(): string {
|
|
170
197
|
const workflows = workflowRoutes()
|
|
171
|
-
.map((route) => `- ${route.route}: ${route.description}`)
|
|
198
|
+
.map((route) => `- ${route.route}: ${route.description} Call: ${JSON.stringify({ mode: "workflow", route: route.route })}`)
|
|
172
199
|
.join("\n");
|
|
173
200
|
const capabilities = modelStandaloneCapabilities()
|
|
174
|
-
.map((capability) => `- ${capability.id}: ${capability.description}`)
|
|
201
|
+
.map((capability) => `- ${capability.id}: ${capability.description} Call: ${JSON.stringify({ mode: "capability", capability: capability.id })}`)
|
|
202
|
+
.join("\n");
|
|
203
|
+
// Without this list the user-invoked capabilities are invisible here, so a request
|
|
204
|
+
// like "grill me" has no route and the package is silently skipped. The entries stay
|
|
205
|
+
// terse on purpose: the gateway schema carries the authoritative ids, so the prompt
|
|
206
|
+
// only has to name them and give one call shape.
|
|
207
|
+
const userCapabilities = userStandaloneCapabilities()
|
|
208
|
+
.map((capability) => `- ${capability.id}: ${userCapabilitySummary(capability)}`)
|
|
175
209
|
.join("\n");
|
|
176
210
|
return `## Available Matt Pocock Workflows and Capabilities
|
|
177
211
|
|
|
@@ -190,5 +224,10 @@ ${workflows}
|
|
|
190
224
|
Model-reachable standalone capabilities:
|
|
191
225
|
${capabilities}
|
|
192
226
|
|
|
227
|
+
User-invoked capabilities: the user starts these by name, so run one only when the user explicitly asked for it, and never substitute a different capability. Call: {"mode":"user-capability","capability":"<id>"}.
|
|
228
|
+
${userCapabilities}
|
|
229
|
+
|
|
230
|
+
For an explicitly requested capability, load its listed call before applying its method. A schema rejection means the capability was not loaded: correct mode and required fields using the exact call above; do not repeat the same rejected arguments or silently claim execution.
|
|
231
|
+
|
|
193
232
|
Workflow state is deliberate and task-scoped. When relevant, start a workflow first with matt_pocock_workflow (mode: "workflow"); the started workflow carries its procedure and next-step guidance.`;
|
|
194
233
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Upstream synchronization
|
|
2
|
+
|
|
3
|
+
## Recorded revisions
|
|
4
|
+
|
|
5
|
+
- Repository: `mattpocock/skills`
|
|
6
|
+
- Compared commit: `4ae2317ddb22b046e26455e4f9c9b3aaaf62212d`
|
|
7
|
+
- Comparison tag: `v1.3.2`
|
|
8
|
+
- Resolved tag commit: `d1478fcb140db0b93c0c8df17bcfd311844749d0`
|
|
9
|
+
- Latest checked commit: `4ae2317ddb22b046e26455e4f9c9b3aaaf62212d`
|
|
10
|
+
|
|
11
|
+
These values describe different roles. The compared commit is the content
|
|
12
|
+
baseline, the comparison tag is the human-readable release marker, the resolved
|
|
13
|
+
tag commit records what that tag points to, and the latest checked commit is
|
|
14
|
+
the newest upstream tree reviewed for additions or removals. Two roles may
|
|
15
|
+
legitimately resolve to the same commit when upstream history makes that true;
|
|
16
|
+
the checker validates each role against Git rather than requiring artificial
|
|
17
|
+
distinctions. The newest tag is not necessarily the reviewed tree: when the
|
|
18
|
+
baseline runs ahead of it, the tag names the newest release marker reachable
|
|
19
|
+
from the baseline, and the checked commits between them carry the skills
|
|
20
|
+
changes adopted here. At this revision the checked tree carries three commits
|
|
21
|
+
after `v1.3.2`: fresh-context-window guidance for delegated work, the setup
|
|
22
|
+
procedure's legacy glossary migration and reachable-repo requirement, and the
|
|
23
|
+
new `software-factory` in-progress skill. The full compare always reads the
|
|
24
|
+
checkout at the latest checked commit.
|
|
25
|
+
|
|
26
|
+
The machine-readable selection is in
|
|
27
|
+
[`upstream-selection.json`](upstream-selection.json). It classifies every
|
|
28
|
+
upstream `SKILL.md` present at the latest checked commit as either promoted or
|
|
29
|
+
intentionally excluded. Each promoted skill names its local catalog capability,
|
|
30
|
+
plain Markdown resource, and upstream invocation mode. Each exclusion carries a
|
|
31
|
+
reason; Claude Code hooks and background-agent commands are identified as
|
|
32
|
+
host-specific rather than silently ignored.
|
|
33
|
+
|
|
34
|
+
## Harness architecture
|
|
35
|
+
|
|
36
|
+
Upstream distributes nested `SKILL.md` files. Pi recursively discovers any
|
|
37
|
+
such file under a declared skill root, which exposes generic names such as
|
|
38
|
+
`tdd`, `code-review`, and `research` alongside unrelated installed skills.
|
|
39
|
+
|
|
40
|
+
This package exposes one extension command, `/matt-pocock`. The extension
|
|
41
|
+
selects and injects plain Markdown procedures from `procedures/`, `references/`
|
|
42
|
+
and `assets/`; none are Pi skills, and the package contains no `SKILL.md` file.
|
|
43
|
+
|
|
44
|
+
## Sync rules
|
|
45
|
+
|
|
46
|
+
1. Clone upstream separately and inspect both the compared and latest checked
|
|
47
|
+
commits before changing local resources.
|
|
48
|
+
2. Run the local metadata check without an upstream checkout:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
node packages/matt-pocock/upstream/check-upstream-sync.mjs
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
3. Run the complete inventory, invocation, and content comparison against an
|
|
55
|
+
already-cloned checkout. The checker never clones or fetches:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
node packages/matt-pocock/upstream/check-upstream-sync.mjs --upstream /path/to/mattpocock-skills
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
To compare a temporary selection fixture without mutating the package
|
|
62
|
+
metadata, add `--selection /path/to/selection.json`.
|
|
63
|
+
|
|
64
|
+
4. Copy selected workflows and supporting files into `procedures/`; never
|
|
65
|
+
overwrite the extension harness wholesale.
|
|
66
|
+
5. Rename copied upstream `SKILL.md` files to their catalog id plus `.md`.
|
|
67
|
+
Strip frontmatter, and never ship a `SKILL.md` file.
|
|
68
|
+
6. Remove Claude-only frontmatter, tools, paths, and invocation syntax from
|
|
69
|
+
copied content. Replace cross-procedure calls with relative links.
|
|
70
|
+
7. Preserve the local BDD/TDD split and Pi-specific interaction,
|
|
71
|
+
collaboration, instruction-file, and git-agent guidance. Follow the upstream
|
|
72
|
+
typography: adopted text keeps upstream punctuation rather than a local variant.
|
|
73
|
+
An adapted content
|
|
74
|
+
result is expected when these local contracts differ from upstream.
|
|
75
|
+
Four procedures carry such local lines for the task-path policy, which says an
|
|
76
|
+
explicit task path wins, otherwise the procedure's own default applies.
|
|
77
|
+
`procedures/pr.md`, `procedures/to-questionnaire.md`, and
|
|
78
|
+
`procedures/improve-codebase-architecture.md` each state that the deliverable
|
|
79
|
+
is a written file which must exist before the turn ends, and
|
|
80
|
+
`procedures/improve-codebase-architecture.md` also states that its survey step
|
|
81
|
+
writes the report and nothing else. `procedures/implement.md` forbids a
|
|
82
|
+
defensive default for a value the trusted path cannot miss. A second local
|
|
83
|
+
policy covers interactive questions: `procedures/grilling.md`,
|
|
84
|
+
`procedures/grill-me.md`, `procedures/grill-with-docs.md`, and
|
|
85
|
+
`procedures/to-tickets.md` require a `recommended` option whose deadline
|
|
86
|
+
adopts it, because `matt_pocock_ask` refuses a question without one; `grilling.md`
|
|
87
|
+
keeps the upstream design-tree, frontier and rounds model and delivers each
|
|
88
|
+
frontier question through that tool. A real-upstream
|
|
89
|
+
comparison that reports `adapted` above zero for these files is therefore
|
|
90
|
+
expected, not drift. The isolated sync test compares against a fixture
|
|
91
|
+
generated from the local files, so it stays exact either way. Upstream since
|
|
92
|
+
`v1.3.1` also asks that delegated work start with a fresh context window;
|
|
93
|
+
local wording keeps the teammate-or-current-context rule and adds that a
|
|
94
|
+
delegated teammate starts fresh rather than inheriting parent history. Local
|
|
95
|
+
procedure text names bun as the default task runner and uses bun-workspace
|
|
96
|
+
signals instead of upstream's other-package-manager examples. The package's
|
|
97
|
+
tests, contributor commands, and reproduction notes run on bun only.
|
|
98
|
+
8. Classify every new or removed upstream skill in
|
|
99
|
+
`upstream-selection.json`, then update the four revision fields together.
|
|
100
|
+
This tree has 39 `SKILL.md` files: 25 selected, 14 excluded. Promoted `pr`
|
|
101
|
+
and `retro` are adapted to the Pi catalog. `implement-spec` remains excluded
|
|
102
|
+
because it requires the upstream Skill tool, a fresh background implementer
|
|
103
|
+
subagent per ticket in its own worktree, and integration-branch
|
|
104
|
+
orchestration, which this package's gateway does not implement. The new
|
|
105
|
+
`software-factory` in-progress skill is excluded because it defines a
|
|
106
|
+
vocabulary for agent factory infrastructure the gateway does not run. The
|
|
107
|
+
removed `resolving-merge-conflicts` capability remains locally for
|
|
108
|
+
compatibility but is no longer claimed as upstream content.
|
|
109
|
+
9. Re-run the harness feature, isolated sync tests, package tests, and package
|
|
110
|
+
dry-run after a sync.
|
|
111
|
+
10. Supporting references and assets are not classified by
|
|
112
|
+
`upstream-selection.json`, which records `SKILL.md` files only. Match them to
|
|
113
|
+
their upstream counterparts by name, keep the local file name and its catalog
|
|
114
|
+
id, and apply the same content and typography rules. `pr` reproduces the
|
|
115
|
+
`show-me` visual menu, so it carries that attribution directly instead of
|
|
116
|
+
upstream's separate `CREDITS.md`, which this package does not ship.
|
|
@@ -9,7 +9,7 @@ const execFileAsync = promisify(execFile);
|
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
|
|
11
11
|
const packageDirectory = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
|
-
const selectionPath = resolve(packageDirectory, "upstream-selection.json");
|
|
12
|
+
const selectionPath = resolve(packageDirectory, "upstream/upstream-selection.json");
|
|
13
13
|
|
|
14
14
|
function fail(message) {
|
|
15
15
|
throw new Error(message);
|
|
@@ -54,9 +54,16 @@ function normalizedBody(value) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function parseArguments(argv) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
const options = {};
|
|
58
|
+
for (let index = 0; index < argv.length; index += 2) {
|
|
59
|
+
const key = { "--upstream": "upstream", "--selection": "selection" }[argv[index]];
|
|
60
|
+
const value = argv[index + 1];
|
|
61
|
+
if (!key || options[key] || !value || value.startsWith("--")) {
|
|
62
|
+
fail("usage: node upstream/check-upstream-sync.mjs [--upstream <already-cloned-path>] [--selection <metadata-file>]");
|
|
63
|
+
}
|
|
64
|
+
options[key] = value;
|
|
65
|
+
}
|
|
66
|
+
return options;
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
async function validateCatalogMappings(metadata) {
|
|
@@ -82,13 +89,6 @@ async function validateMetadata(metadata) {
|
|
|
82
89
|
requireCommit(metadata.resolvedTagCommit, "resolvedTagCommit");
|
|
83
90
|
requireCommit(metadata.latestCheckedCommit, "latestCheckedCommit");
|
|
84
91
|
|
|
85
|
-
const revisions = [
|
|
86
|
-
metadata.comparedCommit,
|
|
87
|
-
metadata.comparisonTag,
|
|
88
|
-
metadata.resolvedTagCommit,
|
|
89
|
-
metadata.latestCheckedCommit,
|
|
90
|
-
];
|
|
91
|
-
if (new Set(revisions).size !== revisions.length) fail("upstream revision fields must be distinct");
|
|
92
92
|
if (!Array.isArray(metadata.selected) || metadata.selected.length === 0) fail("selected must be non-empty");
|
|
93
93
|
if (!Array.isArray(metadata.excluded) || metadata.excluded.length === 0) fail("excluded must be non-empty");
|
|
94
94
|
|
|
@@ -196,7 +196,7 @@ async function compareUpstream(metadata, upstreamArgument) {
|
|
|
196
196
|
|
|
197
197
|
try {
|
|
198
198
|
const args = parseArguments(process.argv.slice(2));
|
|
199
|
-
const metadata = JSON.parse(await readFile(selectionPath, "utf8"));
|
|
199
|
+
const metadata = JSON.parse(await readFile(args.selection ?? selectionPath, "utf8"));
|
|
200
200
|
await validateMetadata(metadata);
|
|
201
201
|
const comparison = args.upstream ? await compareUpstream(metadata, args.upstream) : {};
|
|
202
202
|
console.log(JSON.stringify({
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"repository": "mattpocock/skills",
|
|
4
|
-
"comparedCommit": "
|
|
5
|
-
"comparisonTag": "v1.2
|
|
6
|
-
"resolvedTagCommit": "
|
|
7
|
-
"latestCheckedCommit": "
|
|
4
|
+
"comparedCommit": "4ae2317ddb22b046e26455e4f9c9b3aaaf62212d",
|
|
5
|
+
"comparisonTag": "v1.3.2",
|
|
6
|
+
"resolvedTagCommit": "d1478fcb140db0b93c0c8df17bcfd311844749d0",
|
|
7
|
+
"latestCheckedCommit": "4ae2317ddb22b046e26455e4f9c9b3aaaf62212d",
|
|
8
8
|
"selected": [
|
|
9
9
|
{ "upstreamSkill": "code-review", "upstreamPath": "skills/engineering/code-review/SKILL.md", "upstreamInvocation": "model", "localCatalogId": "code-review", "localResource": "procedures/code-review.md" },
|
|
10
10
|
{ "upstreamSkill": "codebase-design", "upstreamPath": "skills/engineering/codebase-design/SKILL.md", "upstreamInvocation": "model", "localCatalogId": "codebase-design", "localResource": "procedures/codebase-design.md" },
|
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
{ "upstreamSkill": "implement", "upstreamPath": "skills/engineering/implement/SKILL.md", "upstreamInvocation": "user", "localCatalogId": "implement", "localResource": "procedures/implement.md" },
|
|
15
15
|
{ "upstreamSkill": "improve-codebase-architecture", "upstreamPath": "skills/engineering/improve-codebase-architecture/SKILL.md", "upstreamInvocation": "user", "localCatalogId": "improve-codebase-architecture", "localResource": "procedures/improve-codebase-architecture.md" },
|
|
16
16
|
{ "upstreamSkill": "prototype", "upstreamPath": "skills/engineering/prototype/SKILL.md", "upstreamInvocation": "model", "localCatalogId": "prototype", "localResource": "procedures/prototype.md" },
|
|
17
|
+
{ "upstreamSkill": "retro", "upstreamPath": "skills/engineering/retro/SKILL.md", "upstreamInvocation": "user", "localCatalogId": "retro", "localResource": "procedures/retro.md" },
|
|
17
18
|
{ "upstreamSkill": "research", "upstreamPath": "skills/engineering/research/SKILL.md", "upstreamInvocation": "model", "localCatalogId": "research", "localResource": "procedures/research.md" },
|
|
18
|
-
{ "upstreamSkill": "
|
|
19
|
+
{ "upstreamSkill": "pr", "upstreamPath": "skills/engineering/pr/SKILL.md", "upstreamInvocation": "model", "localCatalogId": "pr", "localResource": "procedures/pr.md" },
|
|
19
20
|
{ "upstreamSkill": "setup-matt-pocock-skills", "upstreamPath": "skills/engineering/setup-matt-pocock-skills/SKILL.md", "upstreamInvocation": "user", "localCatalogId": "setup-matt-pocock-skills", "localResource": "procedures/setup-matt-pocock-skills.md" },
|
|
20
21
|
{ "upstreamSkill": "tdd", "upstreamPath": "skills/engineering/tdd/SKILL.md", "upstreamInvocation": "model", "localCatalogId": "tdd", "localResource": "procedures/tdd.md" },
|
|
21
22
|
{ "upstreamSkill": "to-spec", "upstreamPath": "skills/engineering/to-spec/SKILL.md", "upstreamInvocation": "user", "localCatalogId": "to-spec", "localResource": "procedures/to-spec.md" },
|
|
@@ -33,11 +34,12 @@
|
|
|
33
34
|
],
|
|
34
35
|
"excluded": [
|
|
35
36
|
{ "upstreamSkill": "ask-matt", "upstreamPath": "skills/engineering/ask-matt/SKILL.md", "reason": "Replaced by the single /matt-pocock Pi command and its catalog-backed routing gateway." },
|
|
37
|
+
{ "upstreamSkill": "chief-of-staff", "upstreamPath": "skills/in-progress/chief-of-staff/SKILL.md", "reason": "Experimental in-progress skill that depends on recurring schedules, background subagents, a dated wiki, and reading other skills' SKILL.md files; the Pi v1 gateway implements none of those contracts." },
|
|
36
38
|
{ "upstreamSkill": "claude-handoff", "upstreamPath": "skills/in-progress/claude-handoff/SKILL.md", "reason": "Host-specific: launches Claude Code background agents and duplicates the selected host-neutral handoff capability." },
|
|
37
|
-
{ "upstreamSkill": "implement-spec", "upstreamPath": "skills/
|
|
39
|
+
{ "upstreamSkill": "implement-spec", "upstreamPath": "skills/engineering/implement-spec/SKILL.md", "reason": "Requires the upstream Skill tool, a fresh background implementer subagent per ticket in its own worktree, and integration-branch orchestration; this package's Pi v1 gateway does not implement that host contract." },
|
|
38
40
|
{ "upstreamSkill": "loop-me", "upstreamPath": "skills/in-progress/loop-me/SKILL.md", "reason": "Experimental stateful Claude skill composition is superseded by the persisted Pi workflow harness." },
|
|
39
|
-
{ "upstreamSkill": "retro", "upstreamPath": "skills/in-progress/retro/SKILL.md", "reason": "Experimental capability is outside the approved promoted engineering and productivity catalog." },
|
|
40
41
|
{ "upstreamSkill": "setup-ts-deep-modules", "upstreamPath": "skills/in-progress/setup-ts-deep-modules/SKILL.md", "reason": "Experimental TypeScript-specific setup is narrower than the selected host-neutral codebase-design procedure." },
|
|
42
|
+
{ "upstreamSkill": "software-factory", "upstreamPath": "skills/in-progress/software-factory/SKILL.md", "reason": "Experimental in-progress vocabulary for designing an agent software factory (runners, orchestrators, ticket sources); the Pi v1 gateway implements no factory runtime and this package ships no SKILL.md files." },
|
|
41
43
|
{ "upstreamSkill": "writing-beats", "upstreamPath": "skills/in-progress/writing-beats/SKILL.md", "reason": "Experimental editorial workflow is outside this package's engineering workflow scope." },
|
|
42
44
|
{ "upstreamSkill": "writing-fragments", "upstreamPath": "skills/in-progress/writing-fragments/SKILL.md", "reason": "Experimental editorial workflow is outside this package's engineering workflow scope." },
|
|
43
45
|
{ "upstreamSkill": "writing-shape", "upstreamPath": "skills/in-progress/writing-shape/SKILL.md", "reason": "Experimental editorial workflow is outside this package's engineering workflow scope." },
|
package/UPSTREAM.md
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# Upstream synchronization
|
|
2
|
-
|
|
3
|
-
## Recorded revisions
|
|
4
|
-
|
|
5
|
-
- Repository: `mattpocock/skills`
|
|
6
|
-
- Compared commit: `6654f6b60cd9d5be8b54c6fafe44346dabeb3b76`
|
|
7
|
-
- Comparison tag: `v1.2.3`
|
|
8
|
-
- Resolved tag commit: `6acc160e4e0cd062dbbbd7a1b26ae92855edf07e`
|
|
9
|
-
- Latest checked commit: `3cca18b368ae95cdbdebbff572ccafa662551015`
|
|
10
|
-
|
|
11
|
-
These values are deliberately separate. The compared commit is the content
|
|
12
|
-
baseline, the comparison tag is the human-readable release marker, the resolved
|
|
13
|
-
tag commit records what that tag pointed to, and the latest checked commit is
|
|
14
|
-
the newest upstream tree reviewed for additions or removals.
|
|
15
|
-
|
|
16
|
-
The machine-readable selection is in
|
|
17
|
-
[`upstream-selection.json`](upstream-selection.json). It classifies every
|
|
18
|
-
upstream `SKILL.md` present at the latest checked commit as either promoted or
|
|
19
|
-
intentionally excluded. Each promoted skill names its local catalog capability,
|
|
20
|
-
plain Markdown resource, and upstream invocation mode. Each exclusion carries a
|
|
21
|
-
reason; Claude Code hooks and background-agent commands are identified as
|
|
22
|
-
host-specific rather than silently ignored.
|
|
23
|
-
|
|
24
|
-
## Harness architecture
|
|
25
|
-
|
|
26
|
-
Upstream distributes nested `SKILL.md` files. Pi recursively discovers any
|
|
27
|
-
such file under a declared skill root, which exposes generic names such as
|
|
28
|
-
`tdd`, `code-review`, and `research` alongside unrelated installed skills.
|
|
29
|
-
|
|
30
|
-
This package exposes one extension command, `/matt-pocock`. The extension
|
|
31
|
-
selects and injects plain Markdown procedures from `procedures/`; none are Pi
|
|
32
|
-
skills, and the package contains no `SKILL.md` file.
|
|
33
|
-
|
|
34
|
-
## Sync rules
|
|
35
|
-
|
|
36
|
-
1. Clone upstream separately and inspect both the compared and latest checked
|
|
37
|
-
commits before changing local resources.
|
|
38
|
-
2. Run the local metadata check without an upstream checkout:
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
node scripts/check-upstream-sync.mjs
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
3. Run the complete inventory, invocation, and content comparison against an
|
|
45
|
-
already-cloned checkout. The checker never clones or fetches:
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
node scripts/check-upstream-sync.mjs --upstream /path/to/mattpocock-skills
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
4. Copy selected workflows and supporting files into `procedures/`; never
|
|
52
|
-
overwrite the extension harness wholesale.
|
|
53
|
-
5. Rename copied upstream `SKILL.md` files to their catalog id plus `.md`.
|
|
54
|
-
Strip frontmatter, and never ship a `SKILL.md` file.
|
|
55
|
-
6. Remove Claude-only frontmatter, tools, paths, and invocation syntax from
|
|
56
|
-
copied content. Replace cross-procedure calls with relative links.
|
|
57
|
-
7. Preserve the local BDD/TDD split and Pi-specific interaction,
|
|
58
|
-
collaboration, instruction-file, and git-agent guidance. An adapted content
|
|
59
|
-
result is expected when these local contracts differ from upstream.
|
|
60
|
-
8. Classify every new or removed upstream skill in
|
|
61
|
-
`upstream-selection.json`, then update the four revision fields together.
|
|
62
|
-
9. Re-run the harness feature, isolated sync tests, package tests, and package
|
|
63
|
-
dry-run after a sync.
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# CONTEXT.md Format
|
|
2
|
-
|
|
3
|
-
## Structure
|
|
4
|
-
|
|
5
|
-
```md
|
|
6
|
-
# {Context Name}
|
|
7
|
-
|
|
8
|
-
{One or two sentence description of what this context is and why it exists.}
|
|
9
|
-
|
|
10
|
-
## Language
|
|
11
|
-
|
|
12
|
-
**Order**:
|
|
13
|
-
{A one or two sentence description of the term}
|
|
14
|
-
_Avoid_: Purchase, transaction
|
|
15
|
-
|
|
16
|
-
**Invoice**:
|
|
17
|
-
A request for payment sent to a customer after delivery.
|
|
18
|
-
_Avoid_: Bill, payment request
|
|
19
|
-
|
|
20
|
-
**Customer**:
|
|
21
|
-
A person or organization that places orders.
|
|
22
|
-
_Avoid_: Client, buyer, account
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Rules
|
|
26
|
-
|
|
27
|
-
- **Be opinionated.** When multiple words exist for the same concept, pick the best one and list the others under `_Avoid_`.
|
|
28
|
-
- **Keep definitions tight.** One or two sentences max. Define what it IS, not what it does.
|
|
29
|
-
- **Only include terms specific to this project's context.** General programming concepts (timeouts, error types, utility patterns) don't belong even if the project uses them extensively. Before adding a term, ask: is this a concept unique to this context, or a general programming concept? Only the former belongs.
|
|
30
|
-
- **Group terms under subheadings** when natural clusters emerge. If all terms belong to a single cohesive area, a flat list is fine.
|
|
31
|
-
|
|
32
|
-
## Single vs multi-context repos
|
|
33
|
-
|
|
34
|
-
**Single context (most repos):** One `CONTEXT.md` at the repo root.
|
|
35
|
-
|
|
36
|
-
**Multiple contexts:** A `CONTEXT-MAP.md` at the repo root lists the contexts, where they live, and how they relate to each other:
|
|
37
|
-
|
|
38
|
-
```md
|
|
39
|
-
# Context Map
|
|
40
|
-
|
|
41
|
-
## Contexts
|
|
42
|
-
|
|
43
|
-
- [Ordering](./src/ordering/CONTEXT.md) — receives and tracks customer orders
|
|
44
|
-
- [Billing](./src/billing/CONTEXT.md) — generates invoices and processes payments
|
|
45
|
-
- [Fulfillment](./src/fulfillment/CONTEXT.md) — manages warehouse picking and shipping
|
|
46
|
-
|
|
47
|
-
## Relationships
|
|
48
|
-
|
|
49
|
-
- **Ordering → Fulfillment**: Ordering emits `OrderPlaced` events; Fulfillment consumes them to start picking
|
|
50
|
-
- **Fulfillment → Billing**: Fulfillment emits `ShipmentDispatched` events; Billing consumes them to generate invoices
|
|
51
|
-
- **Ordering ↔ Billing**: Shared types for `CustomerId` and `Money`
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
The skill infers which structure applies:
|
|
55
|
-
|
|
56
|
-
- If `CONTEXT-MAP.md` exists, read it to find contexts
|
|
57
|
-
- If only a root `CONTEXT.md` exists, single context
|
|
58
|
-
- If neither exists, create a root `CONTEXT.md` lazily when the first term is resolved
|
|
59
|
-
|
|
60
|
-
When multiple contexts exist, infer which one the current topic relates to. If unclear, ask.
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# Issue tracker: GitHub
|
|
2
|
-
|
|
3
|
-
Issues and PRDs for this repo live as GitHub issues. Use the `gh` CLI for all operations.
|
|
4
|
-
|
|
5
|
-
## Conventions
|
|
6
|
-
|
|
7
|
-
- **Create an issue**: `gh issue create --title "..." --body "..."`. Use a heredoc for multi-line bodies.
|
|
8
|
-
- **Read an issue**: `gh issue view <number> --comments`, filtering comments by `jq` and also fetching labels.
|
|
9
|
-
- **List issues**: `gh issue list --state open --json number,title,body,labels,comments --jq '[.[] | {number, title, body, labels: [.labels[].name], comments: [.comments[].body]}]'` with appropriate `--label` and `--state` filters.
|
|
10
|
-
- **Comment on an issue**: `gh issue comment <number> --body "..."`
|
|
11
|
-
- **Apply / remove labels**: `gh issue edit <number> --add-label "..."` / `--remove-label "..."`
|
|
12
|
-
- **Close**: `gh issue close <number> --comment "..."`
|
|
13
|
-
|
|
14
|
-
Infer the repo from `git remote -v` — `gh` does this automatically when run inside a clone.
|
|
15
|
-
|
|
16
|
-
## Pull requests as a triage surface
|
|
17
|
-
|
|
18
|
-
**PRs as a request surface: no.** _(Set to `yes` if this repo treats external PRs as feature requests; [triage](triage.md) reads this flag.)_
|
|
19
|
-
|
|
20
|
-
When set to `yes`, PRs run through the same labels and states as issues, using the `gh pr` equivalents:
|
|
21
|
-
|
|
22
|
-
- **Read a PR**: `gh pr view <number> --comments` and `gh pr diff <number>` for the diff.
|
|
23
|
-
- **List external PRs for triage**: `gh pr list --state open --json number,title,body,labels,author,authorAssociation,comments` then keep only `authorAssociation` of `CONTRIBUTOR`, `FIRST_TIME_CONTRIBUTOR`, or `NONE` (drop `OWNER`/`MEMBER`/`COLLABORATOR`).
|
|
24
|
-
- **Comment / label / close**: `gh pr comment`, `gh pr edit --add-label`/`--remove-label`, `gh pr close`.
|
|
25
|
-
|
|
26
|
-
GitHub shares one number space across issues and PRs, so a bare `#42` may be either — resolve with `gh pr view 42` and fall back to `gh issue view 42`.
|
|
27
|
-
|
|
28
|
-
## When a skill says "publish to the issue tracker"
|
|
29
|
-
|
|
30
|
-
Create a GitHub issue.
|
|
31
|
-
|
|
32
|
-
## When a skill says "fetch the relevant ticket"
|
|
33
|
-
|
|
34
|
-
Run `gh issue view <number> --comments`.
|
|
35
|
-
|
|
36
|
-
## Wayfinding operations
|
|
37
|
-
|
|
38
|
-
Used by [wayfinder](wayfinder.md). The **map** is a single issue with **child** issues as tickets.
|
|
39
|
-
|
|
40
|
-
- **Map**: a single issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body. `gh issue create --label wayfinder:map`.
|
|
41
|
-
- **Child ticket**: an issue linked to the map as a GitHub sub-issue (`gh api` on the sub-issues endpoint). Where sub-issues aren't enabled, add the child to a task list in the map body and put `Part of #<map>` at the top of the child body. Labels: `wayfinder:<type>` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev.
|
|
42
|
-
- **Blocking**: GitHub's **native issue dependencies** — the canonical, UI-visible representation. Add an edge with `gh api --method POST repos/<owner>/<repo>/issues/<child>/dependencies/blocked_by -F issue_id=<blocker-db-id>`, where `<blocker-db-id>` is the blocker's numeric **database id** (`gh api repos/<owner>/<repo>/issues/<n> --jq .id`, _not_ the `#number` or `node_id`). GitHub reports `issue_dependencies_summary.blocked_by` (open blockers only — the live gate). Where dependencies aren't available, fall back to a `Blocked by: #<n>, #<n>` line at the top of the child body. A ticket is unblocked when every blocker is closed.
|
|
43
|
-
- **Frontier query**: list the map's open children (`gh issue list --state open`, scoped to the map's sub-issues / task list), drop any with an open blocker (`issue_dependencies_summary.blocked_by > 0`, or an open issue in the `Blocked by` line) or an assignee; first in map order wins.
|
|
44
|
-
- **Claim**: `gh issue edit <n> --add-assignee @me` — the session's first write.
|
|
45
|
-
- **Resolve**: `gh issue comment <n> --body "<answer>"`, then `gh issue close <n>`, then append a context pointer (gist + link) to the map's Decisions-so-far.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|