spexcode 0.3.0 → 0.4.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/spec-cli/src/anchors.ts +341 -0
- package/spec-cli/src/cli.ts +20 -20
- package/spec-cli/src/gateway.ts +8 -1
- package/spec-cli/src/git.ts +21 -13
- package/spec-cli/src/graph.ts +13 -7
- package/spec-cli/src/guide.ts +63 -15
- package/spec-cli/src/harness.ts +85 -34
- package/spec-cli/src/help.ts +33 -17
- package/spec-cli/src/index.ts +39 -7
- package/spec-cli/src/init.ts +5 -4
- package/spec-cli/src/lint.ts +103 -28
- package/spec-cli/src/localIssues.ts +19 -0
- package/spec-cli/src/migrate-table.ts +27 -16
- package/spec-cli/src/search.bench.mjs +2 -2
- package/spec-cli/src/session-timeline.ts +148 -0
- package/spec-cli/src/sessions.ts +29 -4
- package/spec-cli/src/specs.ts +44 -21
- package/spec-cli/templates/hooks/prepare-commit-msg +6 -1
- package/spec-cli/templates/spec/project/.plugins/{extract → commands/extract}/spec.md +1 -1
- package/spec-cli/templates/spec/project/.plugins/commands/spec.md +16 -0
- package/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +8 -0
- package/spec-cli/templates/spec/project/.plugins/core/spec.md +1 -1
- package/spec-cli/templates/spec/project/.plugins/prompts/spec.md +20 -0
- package/spec-cli/templates/spec/project/.plugins/skills/spec.md +17 -0
- package/spec-cli/templates/spec/project/.plugins/spec.md +5 -2
- package/spec-cli/templates/spec/project/spec.md +1 -1
- package/spec-dashboard/dist/assets/{Dashboard-C7Bzsv86.js → Dashboard-C_fGmOKK.js} +3 -3
- package/spec-dashboard/dist/assets/EvalsPage-Cnr1s3bq.js +2 -0
- package/spec-dashboard/dist/assets/{FoldToggle-D5iB4Ac2.js → FoldToggle-x9gtO1OQ.js} +1 -1
- package/spec-dashboard/dist/assets/{IssuesPage-CMFTsQhg.js → IssuesPage-5f_vL-JV.js} +1 -1
- package/spec-dashboard/dist/assets/MobileApp-DEO1jgGM.js +1 -0
- package/spec-dashboard/dist/assets/SessionInterface-CAlbMOFR.js +66 -0
- package/spec-dashboard/dist/assets/SessionWindow-JYbpPwNB.js +13 -0
- package/spec-dashboard/dist/assets/{Settings-BW5f0OaW.js → Settings-DKb5Ji_X.js} +1 -1
- package/spec-dashboard/dist/assets/index-BQu-oJ8J.js +41 -0
- package/spec-dashboard/dist/assets/index-BbMkwuix.css +1 -0
- package/spec-dashboard/dist/assets/launch-BM9GgvkX.js +6 -0
- package/spec-dashboard/dist/index.html +2 -2
- package/spec-eval/src/cli.ts +82 -21
- package/spec-eval/src/evaltab.ts +15 -6
- package/spec-eval/src/humanok.ts +43 -0
- package/spec-eval/src/scenarios.ts +116 -4
- package/spec-eval/src/sidecar.ts +35 -9
- package/spec-cli/templates/presets/careful/.plugins/clarify-before-code/spec.md +0 -11
- package/spec-cli/templates/spec/project/.plugins/supervisor/spec.md +0 -8
- package/spec-dashboard/dist/assets/EvalsPage-DKZZIdHq.js +0 -2
- package/spec-dashboard/dist/assets/MobileApp-DwuTKgdP.js +0 -1
- package/spec-dashboard/dist/assets/SessionInterface-CBS5_cmK.js +0 -71
- package/spec-dashboard/dist/assets/SessionWindow-CqAnjWfI.js +0 -9
- package/spec-dashboard/dist/assets/index-Cc26X4ce.css +0 -1
- package/spec-dashboard/dist/assets/index-Ce0wDyQS.js +0 -41
- /package/spec-cli/templates/spec/project/.plugins/{regroup → commands/regroup}/spec.md +0 -0
- /package/spec-cli/templates/spec/project/.plugins/{tidy → commands/tidy}/spec.md +0 -0
- /package/spec-cli/templates/spec/project/.plugins/{forge-link → prompts/forge-link}/spec.md +0 -0
- /package/spec-cli/templates/spec/project/.plugins/{memory-hygiene → prompts/memory-hygiene}/spec.md +0 -0
- /package/spec-cli/templates/spec/project/.plugins/{reproduce-before-fix → prompts/reproduce-before-fix}/spec.md +0 -0
- /package/spec-cli/templates/spec/project/.plugins/{distill → skills/distill}/digest.mjs +0 -0
- /package/spec-cli/templates/spec/project/.plugins/{distill → skills/distill}/spec.md +0 -0
package/spec-cli/src/specs.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { readFileSync, readdirSync, existsSync } from 'node:fs'
|
|
|
2
2
|
import { readFile, readdir } from 'node:fs/promises'
|
|
3
3
|
import { join, relative, basename } from 'node:path'
|
|
4
4
|
import { repoRoot, historyIndex, rowsFor, statsFor, pathsStats, driftIndex, driftFor, fileDiffAt } from './git.js'
|
|
5
|
+
import { parseCodeEntry, parseRelation } from './anchors.js'
|
|
5
6
|
|
|
6
7
|
// a node is any directory under .spec holding a spec.md; its parent is the nearest ancestor that also holds one.
|
|
7
8
|
const ROOT = repoRoot()
|
|
@@ -97,7 +98,7 @@ function walk(dir: string, parent: string | null, acc: Raw[]) {
|
|
|
97
98
|
// single token — never a '/'-joined path, which would break every `:id` route and fetch that treats an id
|
|
98
99
|
// as one path segment. So the disambiguation separator is '_': like '/' it never occurs inside a dir
|
|
99
100
|
// basename (so the join stays unambiguous), but unlike '/' it is a URL/wikilink/DOM-safe unreserved char,
|
|
100
|
-
// so a collision-qualified id (e.g. `.
|
|
101
|
+
// so a collision-qualified id (e.g. `.plugins_<id>`) stays a single token everywhere it is resolved.
|
|
101
102
|
// Exported as the ONE mint every id producer shares: spec-eval mints its node ids through this same
|
|
102
103
|
// function over this same universe (every spec node), so a colliding leaf carries one canonical id
|
|
103
104
|
// system-wide instead of a second, diverging bare-leaf scheme.
|
|
@@ -171,17 +172,21 @@ function claimMatcher(file: string): (cf: string) => boolean {
|
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
// spec node(s) that GOVERN a file (frontmatter `code:` — source of truth, drives drift + eval freshness); reads only
|
|
174
|
-
// frontmatter (cheap, no git) so a per-edit hook can call it.
|
|
175
|
-
|
|
175
|
+
// frontmatter (cheap, no git) so a per-edit hook can call it. `scoped` = every claiming entry carries a
|
|
176
|
+
// `#selector` — such a governor still displays, but does not count toward the owners bound ([[code-anchor]]).
|
|
177
|
+
export function specOwners(file: string): { id: string; desc: string; scoped: boolean }[] {
|
|
176
178
|
const claims = claimMatcher(file)
|
|
177
|
-
return raws().
|
|
179
|
+
return raws().flatMap((r) => {
|
|
180
|
+
const entries = list(r.fm.code).map(parseCodeEntry).filter((e) => claims(e.path))
|
|
181
|
+
return entries.length ? [{ id: r.id, desc: str(r.fm.desc), scoped: entries.every((e) => e.anchor !== null) }] : []
|
|
182
|
+
})
|
|
178
183
|
}
|
|
179
184
|
|
|
180
185
|
// spec node(s) that REFERENCE a file (frontmatter `related:` — carries coverage, never drift, never eval freshness):
|
|
181
186
|
// [[governed-related]]'s other half, same claim rule, same cheap frontmatter-only read.
|
|
182
187
|
export function specRelated(file: string): { id: string; desc: string }[] {
|
|
183
188
|
const claims = claimMatcher(file)
|
|
184
|
-
return raws().filter((r) => list(r.fm.related).some(claims)).map((r) => ({ id: r.id, desc: str(r.fm.desc) }))
|
|
189
|
+
return raws().filter((r) => list(r.fm.related).some((e) => claims(parseCodeEntry(e).path))).map((r) => ({ id: r.id, desc: str(r.fm.desc) }))
|
|
185
190
|
}
|
|
186
191
|
|
|
187
192
|
// memo fileDiffAt by (version sha + spec.md path) — a commit's patch is immutable. Keyed by path too: one
|
|
@@ -226,8 +231,18 @@ export async function loadSpecs() {
|
|
|
226
231
|
// session = the Session: trailer of the node's latest version; frontmatter `session:` is the fallback.
|
|
227
232
|
const fmSession = str(r.fm.session)
|
|
228
233
|
const session = h[0]?.session || (fmSession && fmSession !== 'null' ? fmSession : null)
|
|
229
|
-
|
|
230
|
-
|
|
234
|
+
// a code:/related: row may pin symbols (`path#fn` — [[code-anchor]]): parseRelation groups each
|
|
235
|
+
// relation per BASE path, so `code`/`related` carry the distinct PATHS (what every path consumer —
|
|
236
|
+
// drift, claims, eval attribution — expects, file-level as before), the scoped entries (path +
|
|
237
|
+
// selectors) ride separately for lint's anchor engine, and structural problems (duplicates,
|
|
238
|
+
// bare/scoped mixing, glob selectors, the code cap) surface as lint integrity errors.
|
|
239
|
+
const codeRel = parseRelation(list(r.fm.code), 'code')
|
|
240
|
+
const relatedRel = parseRelation(list(r.fm.related), 'related')
|
|
241
|
+
const code = codeRel.entries.map((e) => e.path)
|
|
242
|
+
const codeScoped = codeRel.entries.filter((e) => e.selectors.length > 0)
|
|
243
|
+
const related = relatedRel.entries.map((e) => e.path)
|
|
244
|
+
const relatedScoped = relatedRel.entries.filter((e) => e.selectors.length > 0)
|
|
245
|
+
const relationProblems = [...codeRel.problems, ...relatedRel.problems]
|
|
231
246
|
const S = h[0]?.hash || ''
|
|
232
247
|
const driftFiles = code
|
|
233
248
|
.map((f) => ({ file: f, behind: driftFor(didx, S, f) }))
|
|
@@ -235,8 +250,10 @@ export async function loadSpecs() {
|
|
|
235
250
|
const drift = driftFiles.reduce((a, d) => a + d.behind, 0)
|
|
236
251
|
// related drift is the SOFT tier ([[governed-related]]): same ancestry basis, but it stays OUT of
|
|
237
252
|
// `drift` — it never feeds status, the commit gate, or eval freshness. It surfaces only as a lint warn nudge.
|
|
238
|
-
|
|
239
|
-
|
|
253
|
+
// A SCOPED related entry is excluded here: its file-level movement is silent by design — only a
|
|
254
|
+
// selector HIT warns, and that verdict needs the anchor engine, so lint derives it, not the loader.
|
|
255
|
+
const relatedDriftFiles = relatedRel.entries.filter((e) => !e.selectors.length)
|
|
256
|
+
.map((e) => ({ file: e.path, behind: driftFor(didx, S, e.path) }))
|
|
240
257
|
.filter((d) => d.behind > 0)
|
|
241
258
|
const fmStatus = str(r.fm.status, '') || null
|
|
242
259
|
return {
|
|
@@ -250,7 +267,10 @@ export async function loadSpecs() {
|
|
|
250
267
|
hue: Number(str(r.fm.hue, '210')),
|
|
251
268
|
desc: str(r.fm.desc),
|
|
252
269
|
code,
|
|
270
|
+
codeScoped,
|
|
253
271
|
related,
|
|
272
|
+
relatedScoped,
|
|
273
|
+
relationProblems,
|
|
254
274
|
version: h.length,
|
|
255
275
|
reason: h[0]?.reason || '',
|
|
256
276
|
// ISO date of the node's latest version commit (h is newest-first), or null if unversioned.
|
|
@@ -271,7 +291,7 @@ export async function loadSpecs() {
|
|
|
271
291
|
export async function specHistory(id: string) {
|
|
272
292
|
const node = raws().find((r) => r.id === id)
|
|
273
293
|
if (!node) return []
|
|
274
|
-
const codePaths = list(node.fm.code)
|
|
294
|
+
const codePaths = [...new Set(list(node.fm.code).map((e) => parseCodeEntry(e).path))]
|
|
275
295
|
// index (cached) and the code-path walk are independent — run them in parallel, both async git.
|
|
276
296
|
const [idx, cStats] = await Promise.all([historyIndex(ROOT), pathsStats(ROOT, codePaths)])
|
|
277
297
|
const sStats = statsFor(idx, node.relPath)
|
|
@@ -302,11 +322,11 @@ export async function specDiffAt(id: string, hash: string) {
|
|
|
302
322
|
// harness lifecycle events the node binds, its deterministic intra-event order, and whether it intends to
|
|
303
323
|
// block (honored only on block-capable events). See loadHookConfig + the hook compiler/dispatcher.
|
|
304
324
|
export type ConfigPreset = { name: string; title: string; desc: string; kind: string; dir: string; files: string[]; body: string; events: string[]; order: number; block: boolean; tools: string[] }
|
|
305
|
-
// field-driven surface - a plugin is a
|
|
306
|
-
//
|
|
325
|
+
// field-driven surface - a plugin is a spec node at ANY depth under a plugin root that carries a
|
|
326
|
+
// `surface: system|command|hook|skill|agent|review` frontmatter field naming where it plugs in. There are no
|
|
307
327
|
// `command/`/`system/`/`hook/`/`skill/`/`agent/` bucket dirs (those were graph-invisible grouping dirs with no spec.md, so
|
|
308
328
|
// the spec graph skipped them — path != graph); the surface is a FIELD on the node, so the plugin is a real
|
|
309
|
-
// graph child
|
|
329
|
+
// graph child (a grouping parent like `.plugins/prompts` is itself a spec node, never a bare dir). BOTH plugin roots participate: `.plugins` (the instance — DIY dev-flow plugins) and
|
|
310
330
|
// `plugin-system` (the project system spec). loadConfig gathers the `command` surface, loadSystemConfig the `system`
|
|
311
331
|
// surface, loadHookConfig the `hook` surface, loadSkillConfig the `skill` surface, loadAgentConfig the `agent`
|
|
312
332
|
// surface (sub-agent definitions); each scans the children under every root and filters by the field. The plugins also show on the board as ordinary spec nodes (via loadSpecs).
|
|
@@ -354,14 +374,13 @@ function bundleFiles(dir: string): string[] {
|
|
|
354
374
|
return out.sort()
|
|
355
375
|
}
|
|
356
376
|
// gather the preset nodes under a plugin root that declare `surface: <surface>`. The scan is RECURSIVE —
|
|
357
|
-
// `surface` is a FIELD, not a path (the design's core tenet), so a plugin may live at ANY depth
|
|
358
|
-
// grouping
|
|
359
|
-
//
|
|
360
|
-
//
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
|
|
364
|
-
function loadSurface(surface: 'command' | 'system' | 'hook' | 'skill' | 'agent'): ConfigPreset[] {
|
|
377
|
+
// `surface` is a FIELD, not a path (the design's core tenet), so a plugin may live at ANY depth: under a
|
|
378
|
+
// surface-less grouping shelf (the auxiliary `surface: system` contracts live under `.plugins/prompts/`),
|
|
379
|
+
// or under a plugin that is itself a grouping parent (`.plugins/core` is a `surface: system` contract whose
|
|
380
|
+
// CHILDREN are `surface: hook` nodes). The field filter keeps it safe: a node only gathers if it declares THIS
|
|
381
|
+
// surface, so descending past a matched node never double-counts (children carry a different surface),
|
|
382
|
+
// and the gather set is path-independent — regrouping a plugin never changes what materializes.
|
|
383
|
+
function loadSurface(surface: 'command' | 'system' | 'hook' | 'skill' | 'agent' | 'review'): ConfigPreset[] {
|
|
365
384
|
const out: ConfigPreset[] = []
|
|
366
385
|
const visit = (nodeDir: string, name: string) => {
|
|
367
386
|
if (existsSync(join(nodeDir, 'spec.md'))) {
|
|
@@ -412,3 +431,7 @@ export function loadSkillConfig(): ConfigPreset[] { return loadSurface('skill')
|
|
|
412
431
|
// .claude/agents/<name>.md). Like a skill, the node's `desc` is the on-demand load-trigger and its `body` is the
|
|
413
432
|
// agent's system prompt; additionally its `tools` field is the harness tool allowlist for the spawned agent.
|
|
414
433
|
export function loadAgentConfig(): ConfigPreset[] { return loadSurface('agent') }
|
|
434
|
+
// the review-track prose presets ([[review-commands]]): offered in the eval detail's remark-composer `/`
|
|
435
|
+
// dropdown; picking one PREFILLS the composer with the node's `body` ({node}/{scenario}/{expected}
|
|
436
|
+
// placeholders filled at insert time). Display+prefill only — the send stays the ordinary remark write.
|
|
437
|
+
export function loadReviewConfig(): ConfigPreset[] { return loadSurface('review') }
|
|
@@ -26,7 +26,12 @@ if [ -z "$sid" ] && [ -n "${CODEX_THREAD_ID:-}" ]; then # codex: alias the threa
|
|
|
26
26
|
if [ -e "$rd/sessions/$CODEX_THREAD_ID/session.json" ]; then
|
|
27
27
|
sid="$CODEX_THREAD_ID" # a governed record keyed by the thread id (rare/direct)
|
|
28
28
|
else # else the record that captured it as harness_session_id
|
|
29
|
-
|
|
29
|
+
# @@@ unmatched-is-noop - grep's no-match (exit 1) and an absent sessions glob (exit 2) are the
|
|
30
|
+
# NOT-GOVERNED case, not an error: any repo inherits a foreign CODEX_THREAD_ID from a codex session's
|
|
31
|
+
# shell, and under `set -euo pipefail` a bare grep|head assignment would abort the hook — and the
|
|
32
|
+
# commit — before the intended `[ -z "$sid" ] && exit 0` no-op. Genuine errors past this lookup
|
|
33
|
+
# (interpret-trailers etc.) still fail loud via set -e.
|
|
34
|
+
hit=$(grep -lF "\"harness_session_id\": \"$CODEX_THREAD_ID\"" "$rd"/sessions/*/session.json 2>/dev/null | head -1) || hit=""
|
|
30
35
|
[ -n "$hit" ] && sid=$(basename "$(dirname "$hit")")
|
|
31
36
|
fi
|
|
32
37
|
fi
|
|
@@ -45,7 +45,7 @@ Then grow nodes under that spine:
|
|
|
45
45
|
`.svelte`/`.css`, or the dashboard) is a blind spot until it carries a `eval.md` — so write one as you
|
|
46
46
|
extract it: a **real user-path** scenario — a goal and the steps to reach it through the running app (never a
|
|
47
47
|
bare render-check), covering a failure/empty/edge state — with a **description** of those steps and the
|
|
48
|
-
**expected** zero-loss result. Frontend scenarios are measured by looking
|
|
48
|
+
**expected** zero-loss result. Frontend scenarios are measured by looking — through the running product, as a real user would — a screenshot filed with
|
|
49
49
|
`spex eval add <node> --image <png> --pass`. Backend nodes don't need one yet; run `spex eval lint` to
|
|
50
50
|
list the frontend nodes still uncovered.
|
|
51
51
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: commands
|
|
3
|
+
status: active
|
|
4
|
+
hue: 40
|
|
5
|
+
desc: Grouping shelf for the `surface: command` plugins — the `/`-dropdown launch presets a new session can pick. A shelf, not a surface — routing stays field-driven.
|
|
6
|
+
---
|
|
7
|
+
# commands
|
|
8
|
+
|
|
9
|
+
The invocable **command** plugins live here: leaf plugins whose body is a launch preset a new session
|
|
10
|
+
picks from the `/` dropdown, each carrying `surface: command`. Grouping them keeps `.plugins/` legible at
|
|
11
|
+
a glance — the command presets on this shelf, the skill plugins on `skills/`, the auxiliary system
|
|
12
|
+
contracts on `prompts/`, with `core` a flat child beside them.
|
|
13
|
+
|
|
14
|
+
This node is a **shelf, not a surface**: it declares no `surface` field and gathers nothing itself.
|
|
15
|
+
Discovery is recursive and field-driven, so a resident plugs in exactly as it would at the root — a
|
|
16
|
+
plugin that serves both surfaces shelves once by its primary identity, never duplicated.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: supervisor
|
|
3
|
+
surface: command
|
|
4
|
+
status: active
|
|
5
|
+
hue: 280
|
|
6
|
+
desc: Launch a supervisor agent that manages other agents from the main checkout to drive a goal to completion.
|
|
7
|
+
---
|
|
8
|
+
You are a SpexCode supervisor — a **manager**, not a feature worker. Your work base is the main checkout (the repository root), NOT your own worktree: do all git via `git -C <root>`, everything else via the `spex` CLI, and never write feature code. **FIRST, read the project's agent notes (`<root>/CLAUDE.md` / `AGENTS.md`, including the materialized `<!-- spexcode -->` contract block)** for project-specific rules; the loop below (dispatch → monitor → review → merge → close, parallelizing independent tasks) is your playbook. Then drive the goal: decompose it into worker-sized tasks and dispatch one worker per independent task (`spex session new "<task>"` — give each ONLY its task; a task about one specific node mentions it as `[[<id>]]`, which only sets the branch name and board attribution; the session's real node links come from what it edits), monitor with `spex session watch`, review proposals with `spex session review <id>`, merge good ones with `git -C <root> merge --no-ff <branch>`, then close. Never let a worker self-merge; keep `spex spec lint` at 0 errors. To WAIT on a worker, POLL one-shot (`spex session review <id>` or `spex session ls` — both return immediately); never block on `spex session watch`, which STREAMS forever and will freeze your turn. One footgun that bites a fresh supervisor: before `spex session close <id>`, confirm the merge landed (`git -C <root> log -1` shows HEAD at the new merge commit) — closing an unmerged branch discards the work. Report progress as you go and when the goal is complete. Your goal follows:
|
|
@@ -10,7 +10,7 @@ The CLI speaks ONE grammar: `spex <noun> <verb> [object] [flags]`. Six nouns —
|
|
|
10
10
|
|
|
11
11
|
Four disciplines, non-negotiable:
|
|
12
12
|
|
|
13
|
-
1. SPEC FIRST. Before you touch code — and merely READING it counts, not only editing — read the governing spec's BODY: its actual prose, not the title, not the one-line desc, not your memory of it. The body is the current contract; code and its comments tell you what the code DOES, only the spec tells you what it is SUPPOSED to do. Don't know which node governs the area? `spex spec search <topic>` — not grep: grep finds code by architectural centrality, search finds intent by user-story
|
|
13
|
+
1. SPEC FIRST. Before you touch code — and merely READING it counts, not only editing — read the governing spec's BODY: its actual prose, not the title, not the one-line desc, not your memory of it. The body is the current contract; code and its comments tell you what the code DOES, only the spec tells you what it is SUPPOSED to do. Don't know which node governs the area? `spex spec search <topic>` — not grep: grep finds code by architectural centrality, search finds intent by user-story. Read the NEIGHBORS' bodies too — the parent that scopes it, the siblings it borders, the children that refine it — a node's intent is only legible against the tree around it. If your task changes the intent, edit the spec first so spec and code land together; if it implements existing intent, make the code honor the spec. The one forbidden move is code that silently diverges from its spec.
|
|
14
14
|
|
|
15
15
|
2. COMMIT BEFORE YOU DECLARE. Commit your spec node and the code it justifies BEFORE you declare done or propose merge — the commit comes first, never as an afterthought to a declaration. An independently-scoped feature gets its OWN sibling spec node, not a ride-along in your assigned node's commit (cosmetic polish riding along is the smell).
|
|
16
16
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: prompts
|
|
3
|
+
status: active
|
|
4
|
+
hue: 110
|
|
5
|
+
desc: Grouping shelf for the auxiliary `surface: system` prompt contracts — single-body always-on prose. `core` is not a resident — the core contract subsystem is a direct `.plugins` child. A shelf, not a surface — routing stays field-driven.
|
|
6
|
+
---
|
|
7
|
+
# prompts
|
|
8
|
+
|
|
9
|
+
The **auxiliary** `surface: system` prompt contracts live here: leaf plugins whose whole substance is one
|
|
10
|
+
prose body an agent must always carry — materialized (in name order, together with every other system
|
|
11
|
+
body) into the `<!-- spexcode -->` contract block — rather than a verb it invokes. Grouping them keeps
|
|
12
|
+
`.plugins/` legible at a glance: peripheral prompt contracts on this shelf; the invocable surfaces
|
|
13
|
+
(command/skill/agent) and `core` as flat children beside it.
|
|
14
|
+
|
|
15
|
+
The shelf boundary is **leafness, not surface**: a single-body prose contract shelves here, while a
|
|
16
|
+
contract that anchors its own subtree outranks the shelf — `core`, the contract subsystem whose children
|
|
17
|
+
are the `surface: hook` gates, is a *peer* of this shelf, never a resident.
|
|
18
|
+
|
|
19
|
+
This node is a **shelf, not a surface**: it declares no `surface` field and gathers nothing itself.
|
|
20
|
+
Discovery is recursive and field-driven, so residents plug in exactly as they would at the root.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: skills
|
|
3
|
+
status: active
|
|
4
|
+
hue: 280
|
|
5
|
+
desc: Grouping shelf for the `surface: skill` plugins — the harness skills an agent invokes on demand. A shelf, not a surface — routing stays field-driven.
|
|
6
|
+
---
|
|
7
|
+
# skills
|
|
8
|
+
|
|
9
|
+
The invocable **skill** plugins live here: leaf plugins that materialize into the harness's skill dir,
|
|
10
|
+
where an agent invokes them on demand, each carrying `surface: skill`. Grouping them keeps `.plugins/`
|
|
11
|
+
legible at a glance — the skill plugins on this shelf, the command presets on `commands/`, the auxiliary
|
|
12
|
+
system contracts on `prompts/`, with `core` a flat child beside them.
|
|
13
|
+
|
|
14
|
+
This node is a **shelf, not a surface**: it declares no `surface` field and gathers nothing itself.
|
|
15
|
+
Discovery is recursive and field-driven, so a resident plugs in exactly as it would at the root. A plugin
|
|
16
|
+
that serves both surfaces shelves here by its primary (skill) identity and still gathers as a command
|
|
17
|
+
through its field.
|
|
@@ -6,8 +6,11 @@ desc: The instance home — this repo's DIY dev-flow plugins live here as skill-
|
|
|
6
6
|
---
|
|
7
7
|
`.plugins/` is the **instance** of the plugin system: the concrete dev-flow plugins this repo ships for
|
|
8
8
|
working in it. Each plugin is a skill-shaped node — its folder *is* the unit (a `spec.md` plus any
|
|
9
|
-
co-located scripts) —
|
|
10
|
-
|
|
9
|
+
co-located scripts) — carrying a `surface: command|system|…` field that names where it plugs in.
|
|
10
|
+
Discovery is recursive and field-driven, so plugins may sit under a grouping shelf: the auxiliary
|
|
11
|
+
`surface: system` prompt contracts live under `prompts/`, the `surface: command` presets under
|
|
12
|
+
`commands/`, the `surface: skill` plugins under `skills/`, while `core` — the contract subsystem whose
|
|
13
|
+
children are the `surface: hook` gates — sits as a flat child here.
|
|
11
14
|
|
|
12
15
|
The launcher's system gather and the new-session dropdown read from here. Only **active** plugins
|
|
13
16
|
gather: a `pending` node is declared intent, not yet an active plugin. The seed ships `core`
|
|
@@ -12,7 +12,7 @@ made it. This node is the founding spec everything else hangs from — **rewrite
|
|
|
12
12
|
your own project**, then grow child package/feature nodes beneath it (each its own directory with a
|
|
13
13
|
`spec.md`).
|
|
14
14
|
|
|
15
|
-
`.plugins/` holds the dev-flow plugins this instance ships — skill-shaped
|
|
15
|
+
`.plugins/` holds the dev-flow plugins this instance ships — skill-shaped child nodes, each tagged
|
|
16
16
|
with a `surface` field: a `surface: system` node folds into every launched agent's system prompt as
|
|
17
17
|
always-on contract (the seed ships `core`), and a `surface: command` node is a prompt preset the
|
|
18
18
|
new-session dropdown composes over target nodes (the seed ships `tidy`). Add, edit, or
|