@zalom/plastic 1.0.0-beta.2 → 1.0.0-beta.20
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/PLASTIC.md +131 -7
- package/agents/plastic-brainstorming.md +9 -1
- package/agents/plastic-enforcer.md +1 -1
- package/agents/plastic-executor.md +11 -1
- package/agents/plastic-intent-curator.md +7 -5
- package/agents/plastic-planner.md +11 -1
- package/agents/plastic-spec-specialist.md +9 -1
- package/hooks/hooks.json +20 -0
- package/hooks/retrieval-gate +10 -0
- package/hooks/savepoint-pre +10 -0
- package/hooks/statusline +150 -41
- package/package.json +1 -1
- package/scripts/agent-report +163 -0
- package/scripts/doctor.rb +172 -0
- package/scripts/hook-auto-arm +1 -1
- package/scripts/hook-bash-gate +2 -2
- package/scripts/hook-code-gate +11 -6
- package/scripts/hook-create-gate +2 -2
- package/scripts/hook-gate-check +14 -23
- package/scripts/hook-retrieval-gate +136 -0
- package/scripts/hook-savepoint-pre +32 -0
- package/scripts/hook-session-start +1 -1
- package/scripts/insight-append +51 -0
- package/scripts/lib/bridge.rb +374 -34
- package/scripts/lib/frontmatter_writer.rb +130 -0
- package/scripts/lib/graph_rebuild.rb +328 -0
- package/scripts/lib/insights.rb +86 -0
- package/scripts/lib/installer_core.rb +23 -0
- package/scripts/lib/link_suggestions.rb +322 -0
- package/scripts/lib/links_projection.rb +160 -0
- package/scripts/lib/links_section.rb +207 -0
- package/scripts/lib/power_tools.rb +76 -0
- package/scripts/lib/qmd_hook.rb +38 -25
- package/scripts/lib/qmd_sync.rb +36 -0
- package/scripts/lib/retrieval_gate.rb +211 -0
- package/scripts/lib/worktree.rb +409 -0
- package/scripts/link-suggest +211 -0
- package/scripts/new-intent +138 -29
- package/scripts/project-links +287 -0
- package/scripts/qmd-sync +50 -3
- package/scripts/rebuild-graph +244 -0
- package/scripts/spawn-preamble +26 -1
- package/skills/auto/SKILL.md +58 -11
- package/skills/auto/evals/evals.json +48 -0
- package/skills/auto/references/agent-architecture.md +27 -4
- package/skills/auto/references/agent-report-contract.md +121 -0
- package/skills/brainstorming/SKILL.md +1 -0
- package/skills/brainstorming/evals/evals.json +22 -0
- package/skills/continuing/SKILL.md +30 -8
- package/skills/continuing/evals/evals.json +9 -0
- package/skills/creating-intent/SKILL.md +16 -2
- package/skills/creating-intent/evals/evals.json +16 -0
- package/skills/creating-intent/references/lifecycle.md +9 -4
- package/skills/creating-skills/SKILL.md +56 -0
- package/skills/creating-skills/evals/evals.json +75 -0
- package/skills/creating-skills/references/agents.md +168 -0
- package/skills/creating-skills/references/evals.md +41 -0
- package/skills/creating-skills/references/hooks.md +193 -0
- package/skills/creating-skills/references/progressive-disclosure.md +176 -0
- package/skills/creating-skills/references/scripts.md +166 -0
- package/skills/creating-skills/references/skills.md +165 -0
- package/skills/creating-skills/scripts/scaffold.rb +313 -0
- package/skills/dashboard/SKILL.md +5 -0
- package/skills/dashboard/evals/evals.json +22 -0
- package/skills/executing-plan/SKILL.md +2 -2
- package/skills/humanizer/SKILL.md +39 -0
- package/skills/humanizer/evals/evals.json +70 -0
- package/skills/humanizer/references/always-on-snippet.md +9 -0
- package/skills/humanizer/references/examples.md +48 -0
- package/skills/intent-curator/SKILL.md +6 -1
- package/skills/intent-curator/evals/evals.json +22 -0
- package/skills/linking-intents/SKILL.md +54 -12
- package/skills/linking-intents/evals/evals.json +22 -0
- package/skills/linking-intents/references/zettelkasten.md +7 -0
- package/skills/managing-index/SKILL.md +8 -0
- package/skills/managing-index/evals/evals.json +22 -0
- package/skills/managing-index/references/zettelkasten-linking.md +6 -1
- package/skills/releasing/SKILL.md +32 -0
- package/skills/research/SKILL.md +8 -0
- package/skills/research/evals/evals.json +22 -0
- package/skills/writing-instructions/SKILL.md +0 -159
- package/skills/writing-instructions/references/agentskills-spec.md +0 -135
package/PLASTIC.md
CHANGED
|
@@ -50,6 +50,31 @@ tags: [plastic, architecture]
|
|
|
50
50
|
- Context contract: load `sources` strongly (they are what the intent was built from);
|
|
51
51
|
traverse `chain` lightly for discovery. See
|
|
52
52
|
docs/concepts/how-plastic-sources-and-chains-intents.md for the full model.
|
|
53
|
+
- `## Links` (I5) is the human-readable projection of the graph. It mirrors the
|
|
54
|
+
frontmatter exactly: every entry is `- [[id--slug|<target's full intent: text>]]`, a
|
|
55
|
+
clickable `id--slug` wikilink target with the target intent's full `intent:` text as the
|
|
56
|
+
label (cross-store targets render `- [[store:id--slug|<target's full intent: text>]]`).
|
|
57
|
+
Ordering is mandatory: all `sources` first (top), then all `chain`, frontmatter order
|
|
58
|
+
preserved within each group. Sources never appear at the end. No source/chain tags, no
|
|
59
|
+
sub-grouping. An intent with empty `sources` and `chain` carries the empty-state comment.
|
|
60
|
+
- `## Links` is a DERIVED view, not a place to author links (Convention over Configuration).
|
|
61
|
+
It equals the projection of `sources` (first) then `chain`. Never hand-write or hand-edit a
|
|
62
|
+
`## Links` line, and never auto-delete one. The edge lives in the frontmatter graph; the
|
|
63
|
+
section is regenerated from it (doctor `graph_links_projection` enforces this identity). To
|
|
64
|
+
add a link, add the frontmatter edge, then reproject.
|
|
65
|
+
- Links are decided by CONTEXT INFLUENCE, not by shared files, shared symbols, or a topic
|
|
66
|
+
similarity score. The question is whether one intent's context actually informed another.
|
|
67
|
+
Three tiers:
|
|
68
|
+
- **sources:** the foundational context that shaped this intent's creation (a split, an idea
|
|
69
|
+
born during development, a merge). Earns an edge.
|
|
70
|
+
- **chain:** the context that materially helps DELIVER this intent. This is a HIGH bar: only
|
|
71
|
+
the genuinely delivery-moving intents, not everything in the same area. Earns an edge,
|
|
72
|
+
reflected in `## Links`.
|
|
73
|
+
- **tags:** a loose theme grouping for search. NOT a link. A shared tag is a door INTO the
|
|
74
|
+
store (filtered discovery), not a pathway BETWEEN two notes.
|
|
75
|
+
Judging influence is an agent's call, made by reading the candidate's Intent and Context. A
|
|
76
|
+
script cannot grade it, so `scripts/link-suggest` only gathers candidates with that evidence,
|
|
77
|
+
records a confirmed edge with a rating and reason, and flags drift.
|
|
53
78
|
- IDs use Luhmann's alternating convention: `1` → `1a` → `1a1` → `1a1a`
|
|
54
79
|
- Multiple branches increment: `1a`, `1b`, `1c`
|
|
55
80
|
|
|
@@ -72,10 +97,29 @@ The connection: an intent's `## Insights` feeds the Coordinator's Observe phase.
|
|
|
72
97
|
| **How** | Planning | `plan.md` + `actions/` + `checklist.md` | `plastic-writing-plans` |
|
|
73
98
|
| **Exec** | Execution | `outcome.md` | `plastic-executing-plan` |
|
|
74
99
|
|
|
75
|
-
`## Insights`
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
100
|
+
`## Insights` is the append-only log of durable discoveries captured throughout ALL stages.
|
|
101
|
+
An insight is a discovery worth keeping for later reads: novel, or old but newly relevant,
|
|
102
|
+
surfaced at any stage (What, Why, How, Exec). It is the most interesting residue of an
|
|
103
|
+
intent, the part a future reader most wants. **Append-only means newest entry at the bottom;
|
|
104
|
+
never prepend.** This ordering is a hard convention: Insights are the semantic trace of an
|
|
105
|
+
intent, and a consistent newest-last order keeps that trace readable across every intent.
|
|
106
|
+
|
|
107
|
+
Every entry leads with a fixed, machine-parseable prefix `{utc-iso8601} · {stage} · {author}`,
|
|
108
|
+
for example `2026-06-24T08:13:05Z · Why · plastic-brainstorming (autonomous)`. The UTC ISO8601
|
|
109
|
+
timestamp (to the second, trailing `Z`) is the same convention the savepoint ledger uses, so
|
|
110
|
+
the store has one timestamp convention. This per-entry prefix is not prepending the entry:
|
|
111
|
+
entries stay append-only, newest at the bottom; the prefix only stamps each line with when,
|
|
112
|
+
which stage, and who.
|
|
113
|
+
|
|
114
|
+
The blessed write path is the `insight-append` helper
|
|
115
|
+
(`scripts/insight-append <intent_dir> <text> --stage S --author A`), which formats the prefix,
|
|
116
|
+
validates it, and appends at the bottom. Hand-editing `## Insights` is an escape hatch; the
|
|
117
|
+
helper is the default so the format cannot drift.
|
|
118
|
+
|
|
119
|
+
Background sessions and dispatched sub-agents do not write the insight themselves. They carry
|
|
120
|
+
each nugget home in the completion report's `insights:` field, and the orchestrator (or any
|
|
121
|
+
agent that can write the file) persists it via the helper. A session that cannot write the
|
|
122
|
+
intent file still returns its report, so the insight survives.
|
|
79
123
|
For full lifecycle detail, the skills in the Detail column have references/.
|
|
80
124
|
|
|
81
125
|
`savepoint.md` — a deterministic, append-only ledger of cycle-step milestones (one line per
|
|
@@ -164,8 +208,8 @@ Format: `ID--three-to-five-words` (all stores).
|
|
|
164
208
|
- **Root (`15`, `16`)**: an independent thought, even if inspired by another intent.
|
|
165
209
|
Reserve `sources` for true created-from provenance (intents this was built out of). An
|
|
166
210
|
independent intent merely related to or inspired by another carries NO `sources`; record
|
|
167
|
-
the relation on the PREDECESSOR's `chain` (and mirror it as a
|
|
168
|
-
`## Links`).
|
|
211
|
+
the relation on the PREDECESSOR's `chain` (and mirror it as a
|
|
212
|
+
`[[id--slug|<target's full intent: text>]]` wikilink in `## Links`).
|
|
169
213
|
- **Rule of thumb:** if the intent could exist without its parent, it's a root.
|
|
170
214
|
|
|
171
215
|
## INDEX.md
|
|
@@ -176,6 +220,10 @@ Sections: `## Active`, `## Future`, `## Clusters`, `## Abandoned`, `## Completed
|
|
|
176
220
|
|
|
177
221
|
For index maintenance, use `plastic-managing-index`.
|
|
178
222
|
|
|
223
|
+
One-line entry convention. Each index entry is ONE line: `- [<id> <terse title>](<dir>) <tags>`.
|
|
224
|
+
The title is the title, not a summary: aim for about 80 characters, no multi-sentence
|
|
225
|
+
descriptions. This is a self-check, not a gate.
|
|
226
|
+
|
|
179
227
|
## Rules for Skills
|
|
180
228
|
|
|
181
229
|
ALL work flows through intents.
|
|
@@ -187,6 +235,62 @@ ALL work flows through intents.
|
|
|
187
235
|
5. Researches are intents. No separate folder.
|
|
188
236
|
6. Intents are created only via `plastic-creating-intent`. Never hand-author an intent file. The skill self-verifies the written intent with `scripts/validate-intent` before announcing or committing, so every intent is born complete.
|
|
189
237
|
|
|
238
|
+
## House Style (self-check)
|
|
239
|
+
|
|
240
|
+
The agent is the heaviest contributor to the transcript, so terseness pays every turn. These
|
|
241
|
+
are pre-send self-checks the agent applies to its own output. They are not gated.
|
|
242
|
+
|
|
243
|
+
- Answer or decision first. Lead with the result, then support it.
|
|
244
|
+
- Bullets over paragraphs.
|
|
245
|
+
- No preamble, no end-recap. Do not restate the question or summarize what you just said.
|
|
246
|
+
- One question-cluster at a time when asking the human.
|
|
247
|
+
- Reasoning goes in the thinking channel, not duplicated into the visible reply. This keeps
|
|
248
|
+
the human's visibility into your reasoning without paying for it twice in the transcript.
|
|
249
|
+
|
|
250
|
+
Active-intent cache rule. For the intent under active development you already hold its
|
|
251
|
+
delivered artifacts in your own context: prefer revisiting that in-context memory (hit the
|
|
252
|
+
cache) over re-reading them from disk, which only widens context. QMD is for OTHER or indexed
|
|
253
|
+
intents, not for re-reading what you just wrote. Pairs with `/clear` plus savepoint-resume
|
|
254
|
+
hygiene after each intent. Advisory self-check, not hard-verifiable.
|
|
255
|
+
|
|
256
|
+
## Retrieval Gate
|
|
257
|
+
|
|
258
|
+
A single capability-aware PreToolUse gate enforces retrieval-first routing on the agent's own
|
|
259
|
+
Bash/Read/Grep/Glob calls (and on subagents, since PreToolUse binds them). The gate is
|
|
260
|
+
OPERATION-based: it separates searching from reading, and it never stands between you and
|
|
261
|
+
reading something you have already located.
|
|
262
|
+
|
|
263
|
+
- Only CONTENT SEARCH over a Plastic store is gated. The Grep tool and bash `grep`/`rg`/`ag`
|
|
264
|
+
whose target is at or under a store route to QMD when QMD is present and the index is fresh:
|
|
265
|
+
the raw scan is blocked and you use `qmd search`/`qmd query` (or `scripts/qmd-sync search`)
|
|
266
|
+
instead. When QMD is present but stale, the search is allowed this turn and a background
|
|
267
|
+
reindex is fired so the next turn enforces against a fresh index; reindex is never
|
|
268
|
+
synchronous. When QMD is absent, the search is allowed.
|
|
269
|
+
- Reading a known target (the Read tool, bash `cat`/`head`/`tail`) and structural discovery
|
|
270
|
+
(the Glob tool, bash `find`/`ls`) are always allowed, including over the store. QMD cannot
|
|
271
|
+
list directories or hand back one specific file, so these are never gated.
|
|
272
|
+
- Code is never hard-gated here. Symbolic code navigation via Serena is a soft prompt mandate
|
|
273
|
+
(the UserPromptSubmit power-tools hook), not a block: content grep over code is allowed,
|
|
274
|
+
because Serena navigates symbols and cannot grep arbitrary strings.
|
|
275
|
+
- QMD failure model. Absent or stale degrades to allow (stale also fires the background
|
|
276
|
+
reindex). A broken QMD, where the freshness probe errors or times out, also fails open, and
|
|
277
|
+
the hook emits a one-line warning so a degraded QMD is visible rather than silent.
|
|
278
|
+
- Bypass: append a trailing `# qmd-ok` shell comment to a Bash command when you attempted
|
|
279
|
+
discovery and it did not serve you (no hits, or results that do not answer your need by your
|
|
280
|
+
reading of the snippets, not their score). A quoted or echoed occurrence does not bypass.
|
|
281
|
+
Bypasses are logged. The gate enforces that discovery was attempted, never that it succeeded.
|
|
282
|
+
- Scope: only the agent's tool calls. Ruby `File.read` inside a script is invisible to the gate
|
|
283
|
+
and is out of scope by design.
|
|
284
|
+
|
|
285
|
+
## Context-economy measurement buckets (84a)
|
|
286
|
+
|
|
287
|
+
Intent 84 defines three buckets for sibling 84a to audit against; 84 does not run the audit.
|
|
288
|
+
|
|
289
|
+
- (a) gate-hook prose tokens: the per-transition narration emitted by the gate hook.
|
|
290
|
+
- (b) main-loop store-read tokens: tokens the main agent spends reading or grepping the store
|
|
291
|
+
in the transcript.
|
|
292
|
+
- (c) authored-section sizes: sizes of authored artifacts (INDEX entries and the like).
|
|
293
|
+
|
|
190
294
|
## Transition Gates
|
|
191
295
|
|
|
192
296
|
| Transition | Trigger | Gate |
|
|
@@ -198,6 +302,26 @@ ALL work flows through intents.
|
|
|
198
302
|
|
|
199
303
|
Hard blocking — hooks exit code 2 on gate failure.
|
|
200
304
|
|
|
305
|
+
## Delivery Isolation and the Single-Owner Lock
|
|
306
|
+
|
|
307
|
+
Exactly one session or agent develops an intent's delivery at a time. Ownership is the armed
|
|
308
|
+
session bridge, which doubles as the delivery lock: arming records the owning session, the
|
|
309
|
+
owner pid, an acquired-at timestamp, and the host. Another session that finds an armed bridge
|
|
310
|
+
for the same intent with a live owner backs off; if the owner pid is dead the lock is
|
|
311
|
+
reclaimable. This is mandatory, not a convention.
|
|
312
|
+
|
|
313
|
+
Every code-touching intent gets its own git worktree named `{id}--{slug}`, and all code edits
|
|
314
|
+
for that intent happen only inside it. Plastic provisions the worktree deterministically: it
|
|
315
|
+
resolves the project repo from `projects.yml` and runs `git -C <repo> worktree add`, so
|
|
316
|
+
isolation never depends on the current working directory. There are two worktrees per project
|
|
317
|
+
intent: a code worktree at `<repo>/.claude/worktrees/{id}--{slug}` (branch `plastic/{id}--{slug}`)
|
|
318
|
+
and a store worktree at `<plastic_home>/.worktrees/{id}--{slug}` (branch
|
|
319
|
+
`plastic-store/{id}--{slug}`), so lifecycle-doc commits and code commits move as one unit.
|
|
320
|
+
|
|
321
|
+
Provisioning fails open for intents that touch no project code (pure research or decision
|
|
322
|
+
intents in the global store, or a non-git repo): those get the lock only, and the worktree
|
|
323
|
+
block stays unprovisioned. The fail-open path is always logged, never silent.
|
|
324
|
+
|
|
201
325
|
## Deprecation Process
|
|
202
326
|
|
|
203
327
|
Deprecations live in `deprecations.yml` and are shown at SessionStart. While Plastic is
|
|
@@ -224,5 +348,5 @@ Detailed conventions live inside the skills that use them, not in this file.
|
|
|
224
348
|
| Index maintenance | `plastic-managing-index` | — |
|
|
225
349
|
| Releases, deprecations | `plastic-releasing` | deprecation process |
|
|
226
350
|
| Health diagnostics | `plastic-doctor` | three scopes: `--core` (binary install-integrity check, runs on SessionStart), `--store [global\|<slug>]` (per-store check, runs on dashboard load), no flag = full check (runs after every update); gate enforcement, stuck detection |
|
|
227
|
-
|
|
|
351
|
+
| Authoring skills, agents, hooks | `plastic-creating-skills` | progressive disclosure, agentskills.io spec |
|
|
228
352
|
| Evaluating skills, evals | `plastic-evaluating-skills` | eval methodology, convention checks |
|
|
@@ -27,7 +27,15 @@ When dispatched in auto mode you receive the standard Plastic spawn preamble (fr
|
|
|
27
27
|
2. Read the intent file, its `## Links`, and related code or docs
|
|
28
28
|
3. Research with the adaptive budget the enforcer set (simple 2-3, medium 5-8, complex 10-15 steps)
|
|
29
29
|
4. Produce (output handoff): an enriched `## Context` plus `### Decisions` with rationale
|
|
30
|
-
5. Log autonomous calls in `## Insights` with the `(autonomous)` marker, then report for gating
|
|
30
|
+
5. Log autonomous calls in `## Insights` with the `(autonomous)` marker, then report for gating (see `## Completion Report`)
|
|
31
|
+
|
|
32
|
+
## Completion Report
|
|
33
|
+
|
|
34
|
+
END your turn with a structured completion report as your final message, per the spawn preamble's `REPORT_CONTRACT` and `skills/auto/references/agent-report-contract.md`. Do not finish silently. Carry the common envelope (role, intent id, stage, status, artifacts written, verification, checklist deltas, deviations, blockers, insights) plus the brainstorming payload:
|
|
35
|
+
|
|
36
|
+
- Decisions recorded in `### Decisions`, each with its one-line rationale
|
|
37
|
+
- Context enriched: what was researched and the key findings
|
|
38
|
+
- Open questions resolved, and any deliberately left for the spec
|
|
31
39
|
|
|
32
40
|
## Constraints
|
|
33
41
|
|
|
@@ -30,7 +30,7 @@ You are the Plastic Enforcer. You ARE the auto orchestrator, spanning the whole
|
|
|
30
30
|
|
|
31
31
|
## Constraints
|
|
32
32
|
|
|
33
|
-
- Enforce gates manually; do not rely on hooks, because
|
|
33
|
+
- Enforce gates manually; do not rely on hooks, because the session id may be unset in headless or background runs
|
|
34
34
|
- You never delegate gate ownership; the orchestrator is always the gate-keeper
|
|
35
35
|
- Roles are thin handoff contracts, not an execution engine; dispatch through `plastic-executing-plan` by default, and through the superpowers skills only when they are available or the user prefers them
|
|
36
36
|
- Fall back by case: if the harness supports subagents but superpowers is absent, use the native `plastic-executing-plan` engine; if the harness has no subagent dispatch at all, fall back to a single agent walking the full cycle
|
|
@@ -18,7 +18,7 @@ When dispatched in auto mode you receive the standard Plastic spawn preamble (fr
|
|
|
18
18
|
|
|
19
19
|
1. **Implement the actions** — make the code changes for each action in order
|
|
20
20
|
2. **Track progress** — check off `checklist.md` items as they complete
|
|
21
|
-
3. **Record insights** —
|
|
21
|
+
3. **Record insights** — capture durable discoveries and report them in the `insights:` field; persist each to `## Insights` via the `insight-append` helper (`scripts/insight-append <intent_dir> <text> --stage Exec --author "plastic-executor (autonomous)"`), the blessed write path that stamps the `{utc-iso8601} · {stage} · {author}` prefix
|
|
22
22
|
4. **Prove it green** — run the full test suite and reach zero failures before reporting done
|
|
23
23
|
|
|
24
24
|
## How You Work
|
|
@@ -28,6 +28,16 @@ When dispatched in auto mode you receive the standard Plastic spawn preamble (fr
|
|
|
28
28
|
3. Edit project code (the gate is open now that plan and checklist exist)
|
|
29
29
|
4. Run the full suite, iterate to zero failures and zero errors
|
|
30
30
|
5. Produce (output handoff): the code changes, a checked-off `checklist.md`, and `## Insights`
|
|
31
|
+
6. Report for gating (see `## Completion Report`); the enforcer reviews the work at the final gate
|
|
32
|
+
|
|
33
|
+
## Completion Report
|
|
34
|
+
|
|
35
|
+
END your turn with a structured completion report as your final message, per the spawn preamble's `REPORT_CONTRACT` and `skills/auto/references/agent-report-contract.md`. Do not finish silently. Carry the common envelope (role, intent id, stage, status, artifacts written, verification, checklist deltas, deviations, blockers, insights) plus the executor payload:
|
|
36
|
+
|
|
37
|
+
- Actions implemented this turn, mapped to checklist items checked off (checked / total)
|
|
38
|
+
- A summary of the code changed (files and the shape of the change)
|
|
39
|
+
- Test result: the full-suite command and its pass / fail counts
|
|
40
|
+
- Insights appended, with the `(autonomous)` marker
|
|
31
41
|
|
|
32
42
|
## Constraints
|
|
33
43
|
|
|
@@ -18,23 +18,25 @@ You are the Plastic Intent Curator. Your role is to maintain the health and navi
|
|
|
18
18
|
|
|
19
19
|
## Your Responsibilities
|
|
20
20
|
|
|
21
|
-
1. **Intent lifecycle management** — move intents between Active/Future/Completed in INDEX.md, fill in `## Outcome` sections
|
|
22
|
-
2. **INDEX.md maintenance** — keep Active/Future/Clusters/Completed sections accurate and well-organized
|
|
21
|
+
1. **Intent lifecycle management** — move intents between Active/Future/Completed/Abandoned in INDEX.md, fill in `## Outcome` sections (including the abandonment rationale when an intent is abandoned)
|
|
22
|
+
2. **INDEX.md maintenance** — keep Active/Future/Clusters/Completed/Abandoned sections accurate and well-organized
|
|
23
23
|
3. **Link discovery** — suggest connections between intents that share topics but aren't linked
|
|
24
24
|
4. **Cluster management** — create new clusters when 3+ unlinked intents share tags, merge or rename clusters as topics evolve
|
|
25
25
|
5. **Orphan detection** — flag intents with no links and no cluster membership
|
|
26
26
|
|
|
27
27
|
## How You Work
|
|
28
28
|
|
|
29
|
+
0. QMD-first (when available): when you need to locate a specific intent (to reclassify, link, or cluster it) rather than rebuild the whole landscape, before scanning the store with grep/Read run `ruby ~/.plastic/scripts/qmd-sync search "<terms>"` to surface candidate or related intents, then open the authoritative intent file for any hit you act on. The command is a no-op when QMD is absent, so fall back to the full scan below. (This is discovery; the reindex step at a terminal-state transition is separate.)
|
|
29
30
|
1. Scan `~/.plastic/store/*/ID--slug.md` (or project store) to understand the full intent landscape
|
|
30
31
|
2. Read `~/.plastic/INDEX.md` (or project INDEX.md) to understand current organization
|
|
31
|
-
3. Compare: are there intents not in any cluster? Missing from Active/Completed? Status mismatches?
|
|
32
|
+
3. Compare: are there intents not in any cluster? Missing from Active/Completed/Abandoned? Status mismatches?
|
|
32
33
|
4. Make targeted edits to INDEX.md and intent frontmatter/links
|
|
33
|
-
5.
|
|
34
|
+
5. On a terminal-state transition: whenever you move an intent to Completed OR Abandoned, refresh the QMD index for that store so the new outcome (or abandonment rationale) is searchable. This is mandatory on any terminal-state move and a no-op when QMD is absent, and it runs in the background so it never blocks: `ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async`
|
|
35
|
+
6. Report what you changed
|
|
34
36
|
|
|
35
37
|
## Constraints
|
|
36
38
|
|
|
37
39
|
- You only edit `~/.plastic/INDEX.md` (or project INDEX.md) and `~/.plastic/store/*/ID--slug.md` (or project store) files
|
|
38
40
|
- You never create new intents — that's the creating-intent skill's job
|
|
39
41
|
- You never modify `## Insights`, `## Context`, or `## Outcome` content sections — those belong to the worker
|
|
40
|
-
-
|
|
42
|
+
- For discovery, put QMD first when available (`qmd-sync search`), then fall back to Read and grep/find; use Edit for targeted changes
|
|
@@ -27,7 +27,17 @@ When dispatched in auto mode you receive the standard Plastic spawn preamble (fr
|
|
|
27
27
|
2. Read `spec.md` and the plan/checklist templates; match their FORM
|
|
28
28
|
3. Write `plan.md`, the `actions/` directory, and `checklist.md` into the intent directory
|
|
29
29
|
4. Produce (output handoff): `plan.md` plus `actions/` plus `checklist.md`
|
|
30
|
-
5. Report for gating; the enforcer verifies plan and checklist exist before Exec is allowed
|
|
30
|
+
5. Report for gating (see `## Completion Report`); the enforcer verifies plan and checklist exist before Exec is allowed
|
|
31
|
+
|
|
32
|
+
## Completion Report
|
|
33
|
+
|
|
34
|
+
END your turn with a structured completion report as your final message, per the spawn preamble's `REPORT_CONTRACT` and `skills/auto/references/agent-report-contract.md`. Do not finish silently. Carry the common envelope (role, intent id, stage, status, artifacts written, verification, checklist deltas, deviations, blockers, insights) plus the planner payload, which EXPLAINS THE PLAN BACK TO THE ORCHESTRATOR:
|
|
35
|
+
|
|
36
|
+
- The ordered actions, one line each: what the action does and how it is verified
|
|
37
|
+
- Decomposition rationale: why this order, and why the actions are independent
|
|
38
|
+
- Checklist coverage: the item count, and that every action plus suite-green is covered
|
|
39
|
+
|
|
40
|
+
The plan is an argument; the orchestrator gates on whether that argument is sound before any code is written, so make the report make that case.
|
|
31
41
|
|
|
32
42
|
## Constraints
|
|
33
43
|
|
|
@@ -27,7 +27,15 @@ When dispatched in auto mode you receive the standard Plastic spawn preamble (fr
|
|
|
27
27
|
2. Read the spec template (`templates/spec.md`) and match its FORM exactly
|
|
28
28
|
3. Write `spec.md` into the intent directory, resolving the decisions into a coherent approach
|
|
29
29
|
4. Produce (output handoff): a complete `spec.md`
|
|
30
|
-
5. Report for gating; the enforcer checks the spec against its exit criteria before How begins
|
|
30
|
+
5. Report for gating (see `## Completion Report`); the enforcer checks the spec against its exit criteria before How begins
|
|
31
|
+
|
|
32
|
+
## Completion Report
|
|
33
|
+
|
|
34
|
+
END your turn with a structured completion report as your final message, per the spawn preamble's `REPORT_CONTRACT` and `skills/auto/references/agent-report-contract.md`. Do not finish silently. Carry the common envelope (role, intent id, stage, status, artifacts written, verification, checklist deltas, deviations, blockers, insights) plus the spec payload:
|
|
35
|
+
|
|
36
|
+
- The spec sections produced (Problem, Goals, Non-Goals, Approach, Decisions, Acceptance Criteria)
|
|
37
|
+
- How the recorded decisions resolved into the chosen approach
|
|
38
|
+
- The acceptance-criteria count, so the planner knows the surface to cover
|
|
31
39
|
|
|
32
40
|
## Constraints
|
|
33
41
|
|
package/hooks/hooks.json
CHANGED
|
@@ -40,6 +40,16 @@
|
|
|
40
40
|
}
|
|
41
41
|
]
|
|
42
42
|
},
|
|
43
|
+
{
|
|
44
|
+
"matcher": "Write|Edit",
|
|
45
|
+
"hooks": [
|
|
46
|
+
{
|
|
47
|
+
"type": "command",
|
|
48
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" savepoint-pre",
|
|
49
|
+
"statusMessage": "Recording stage start..."
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
},
|
|
43
53
|
{
|
|
44
54
|
"matcher": "Write",
|
|
45
55
|
"hooks": [
|
|
@@ -59,6 +69,16 @@
|
|
|
59
69
|
"statusMessage": "Checking lifecycle gate..."
|
|
60
70
|
}
|
|
61
71
|
]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"matcher": "Bash|Read|Grep|Glob",
|
|
75
|
+
"hooks": [
|
|
76
|
+
{
|
|
77
|
+
"type": "command",
|
|
78
|
+
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" retrieval-gate",
|
|
79
|
+
"statusMessage": "Checking retrieval gate..."
|
|
80
|
+
}
|
|
81
|
+
]
|
|
62
82
|
}
|
|
63
83
|
],
|
|
64
84
|
"PostToolUse": [
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Retrieval-gate hook launcher (intent 84, Lever 2). No-op when there is no
|
|
3
|
+
# global store. Passes the PreToolUse JSON through on stdin (exec inherits it)
|
|
4
|
+
# and the plastic_home as ARGV[0], mirroring the qmd-search launcher.
|
|
5
|
+
GLOBAL_INDEX="$HOME/.plastic/INDEX.md"
|
|
6
|
+
if [ ! -f "$GLOBAL_INDEX" ]; then
|
|
7
|
+
exit 0
|
|
8
|
+
fi
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
exec ruby "$SCRIPT_DIR/../scripts/hook-retrieval-gate" "$HOME/.plastic"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
INPUT=$(cat)
|
|
3
|
+
FILE_PATH=$(echo "$INPUT" | ruby -rjson -e 'data = JSON.parse(STDIN.read); puts data.dig("tool_params", "file_path") || data.dig("tool_input", "file_path") || ""' 2>/dev/null)
|
|
4
|
+
|
|
5
|
+
if [ -z "$FILE_PATH" ]; then
|
|
6
|
+
exit 0
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
ruby "$SCRIPT_DIR/../scripts/hook-savepoint-pre" "$FILE_PATH"
|
package/hooks/statusline
CHANGED
|
@@ -1,56 +1,165 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# plastic-hook-version:
|
|
3
|
-
# StatusLine hook
|
|
2
|
+
# plastic-hook-version: 3.0.0
|
|
3
|
+
# StatusLine hook - Plastic owns the full statusline (no chaining).
|
|
4
|
+
# Renders: {model} - Plastic {version}[ -> {next}] - {path} - [{project}: {id} {title}]
|
|
5
|
+
# Pure bash (macOS 3.2 compatible). No ruby, no jq. Reads stdin once; small file reads only.
|
|
4
6
|
|
|
5
7
|
INPUT=$(cat)
|
|
6
8
|
|
|
7
|
-
# ---
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
# --- Colors (ANSI-C quoting; bash 3.2 supports $'\033') ---
|
|
10
|
+
C_MODEL=$'\033[38;2;90;140;220m' # navy blue - model (lightened for legibility)
|
|
11
|
+
C_VER=$'\033[37m' # white - "Plastic {version}" and update arrow
|
|
12
|
+
C_NEXT=$'\033[33m' # yellow - next version
|
|
13
|
+
C_PATH=$'\033[38;2;128;134;144m' # gray - path (matches dim UI text)
|
|
14
|
+
C_BRACK=$'\033[33m' # yellow - work-unit brackets
|
|
15
|
+
C_PROJ=$'\033[38;2;249;140;30m' # orange - project name
|
|
16
|
+
C_ID=$'\033[38;2;45;212;191m' # teal - intent id (accent between orange + yellow)
|
|
17
|
+
C_INTENT=$'\033[33m' # yellow - intent title
|
|
18
|
+
C_SEP=$'\033[38;2;128;134;144m' # gray - separators
|
|
19
|
+
RST=$'\033[0m'
|
|
20
|
+
SEP="${C_SEP} "$'\302\267'" ${RST}" # " . " middot, dim
|
|
21
|
+
|
|
22
|
+
# --- Extract a flat JSON string field from stdin: json_str <key> ---
|
|
23
|
+
json_str() {
|
|
24
|
+
printf '%s' "$INPUT" \
|
|
25
|
+
| grep -o "\"$1\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" \
|
|
26
|
+
| head -1 \
|
|
27
|
+
| sed "s/.*\"$1\"[[:space:]]*:[[:space:]]*\"//;s/\"$//"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# --- Model (model.display_name) ---
|
|
31
|
+
MODEL=$(json_str "display_name")
|
|
32
|
+
|
|
33
|
+
# --- Path (workspace.current_dir, fallback top-level cwd) ---
|
|
34
|
+
REAL_CWD=$(json_str "current_dir")
|
|
35
|
+
[ -z "$REAL_CWD" ] && REAL_CWD=$(json_str "cwd")
|
|
36
|
+
# Abbreviate $HOME -> ~
|
|
37
|
+
CWD="$REAL_CWD"
|
|
38
|
+
case "$CWD" in
|
|
39
|
+
"$HOME") CWD="~" ;;
|
|
40
|
+
"$HOME"/*) CWD="~${CWD#"$HOME"}" ;;
|
|
41
|
+
esac
|
|
42
|
+
|
|
43
|
+
# --- Plastic version ---
|
|
44
|
+
VERSION=""
|
|
45
|
+
[ -f "$HOME/.plastic/VERSION" ] && VERSION=$(tr -d ' \t\n' < "$HOME/.plastic/VERSION")
|
|
46
|
+
|
|
47
|
+
# --- Update (next version when one is available) ---
|
|
48
|
+
NEXT=""
|
|
49
|
+
CACHE_FILE="$HOME/.plastic/.cache/update-check.json"
|
|
50
|
+
if [ -f "$CACHE_FILE" ]; then
|
|
51
|
+
if grep -q '"updateAvailable"[[:space:]]*:[[:space:]]*true' "$CACHE_FILE" 2>/dev/null; then
|
|
52
|
+
LATEST=$(grep -o '"latest"[[:space:]]*:[[:space:]]*"[^"]*"' "$CACHE_FILE" 2>/dev/null \
|
|
53
|
+
| head -1 | sed 's/.*"latest"[[:space:]]*:[[:space:]]*"//;s/"$//')
|
|
54
|
+
if [ -n "$LATEST" ]; then
|
|
55
|
+
case "$LATEST" in
|
|
56
|
+
*-*) NEXT="${LATEST##*-}" ;; # 1.0.0-beta.3 -> beta.3
|
|
57
|
+
*) NEXT="$LATEST" ;;
|
|
58
|
+
esac
|
|
59
|
+
fi
|
|
17
60
|
fi
|
|
18
61
|
fi
|
|
19
62
|
|
|
20
|
-
# ---
|
|
21
|
-
|
|
63
|
+
# --- Scope resolution: cwd -> registered project store, else global ---
|
|
64
|
+
# Match the longest registered project path that prefixes the real cwd.
|
|
65
|
+
PROJECTS_YML="$HOME/.plastic/projects.yml"
|
|
66
|
+
SLUG=""
|
|
67
|
+
if [ -n "$REAL_CWD" ] && [ -f "$PROJECTS_YML" ]; then
|
|
68
|
+
SLUG=$(awk -v cwd="$REAL_CWD" '
|
|
69
|
+
/^ [A-Za-z0-9_.-]+:[[:space:]]*$/ { s=$1; sub(/:$/,"",s); next }
|
|
70
|
+
/^ path:/ {
|
|
71
|
+
p=$0; sub(/^[[:space:]]*path:[[:space:]]*/,"",p); gsub(/"/,"",p);
|
|
72
|
+
if (length(p)>0 && (cwd==p || index(cwd, p"/")==1) && length(p)>best) { best=length(p); win=s }
|
|
73
|
+
}
|
|
74
|
+
END { if (win!="") print win }
|
|
75
|
+
' "$PROJECTS_YML")
|
|
76
|
+
fi
|
|
22
77
|
|
|
23
|
-
|
|
24
|
-
INDEX="$HOME/.plastic/INDEX.md"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
PLASTIC_PARTS="plastic: ${INTENT}"
|
|
30
|
-
fi
|
|
78
|
+
if [ -n "$SLUG" ]; then
|
|
79
|
+
INDEX="$HOME/.plastic/projects/$SLUG/INDEX.md"
|
|
80
|
+
PROJ_LABEL="$SLUG"
|
|
81
|
+
else
|
|
82
|
+
INDEX="$HOME/.plastic/INDEX.md"
|
|
83
|
+
PROJ_LABEL="global"
|
|
31
84
|
fi
|
|
85
|
+
INDEX_DIR=$(dirname "$INDEX")
|
|
32
86
|
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
87
|
+
# --- Current-session work-unit: the live bridge wins over savepoint recency ---
|
|
88
|
+
# The statusline receives the real session_id on stdin; the session's bridge
|
|
89
|
+
# (plastic-{session_id}.json) is the authoritative "what THIS session is driving".
|
|
90
|
+
# This beats the shared, savepoint-recency heuristic so parallel sessions on one
|
|
91
|
+
# store no longer overwrite each other's line. grep/sed only - no jq, no ruby.
|
|
92
|
+
WORK=""
|
|
93
|
+
SID=$(json_str "session_id")
|
|
94
|
+
if [ -n "$SID" ]; then
|
|
95
|
+
BRIDGE="${PLASTIC_TMP:-/tmp}/plastic-${SID}.json"
|
|
96
|
+
if [ -f "$BRIDGE" ]; then
|
|
97
|
+
# Bridge JSON is pretty-printed (one field per line). Scope extraction to the
|
|
98
|
+
# "intent" object so a sibling top-level "id"/"name" can never win (the intent
|
|
99
|
+
# object holds only string fields, so the first "}" closes it). grep/sed only.
|
|
100
|
+
INTENT_BLOCK=$(sed -n '/"intent"[[:space:]]*:[[:space:]]*{/,/}/p' "$BRIDGE" 2>/dev/null)
|
|
101
|
+
B_ID=$(printf '%s\n' "$INTENT_BLOCK" | grep -o '"id"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 \
|
|
102
|
+
| sed 's/.*"id"[[:space:]]*:[[:space:]]*"//;s/"$//')
|
|
103
|
+
B_NAME=$(printf '%s\n' "$INTENT_BLOCK" | grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 \
|
|
104
|
+
| sed 's/.*"name"[[:space:]]*:[[:space:]]*"//;s/"$//')
|
|
105
|
+
if [ -n "$B_ID" ] && [ -n "$B_NAME" ]; then
|
|
106
|
+
EMDASH=$'\342\200\224'
|
|
107
|
+
WORK="${B_ID} ${EMDASH} ${B_NAME}"
|
|
45
108
|
fi
|
|
46
109
|
fi
|
|
47
110
|
fi
|
|
48
111
|
|
|
49
|
-
# ---
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
112
|
+
# --- Most-recently-accessed active intent (mirror dashboard: last savepoint ts) ---
|
|
113
|
+
# Fallback only: runs when no session bridge resolved a work-unit above.
|
|
114
|
+
if [ -z "$WORK" ] && [ -f "$INDEX" ]; then
|
|
115
|
+
BEST_TS=""
|
|
116
|
+
while IFS= read -r line; do
|
|
117
|
+
[ -z "$line" ] && continue
|
|
118
|
+
inside=${line#*[}; inside=${inside%%]*} # "{id} - {title}"
|
|
119
|
+
dir=${line#*](store/}; dir=${dir%%/*} # intent directory name
|
|
120
|
+
[ -z "$dir" ] && continue
|
|
121
|
+
sp="$INDEX_DIR/store/$dir/savepoint.md"
|
|
122
|
+
ts=""
|
|
123
|
+
if [ -f "$sp" ]; then
|
|
124
|
+
ts=$(grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z' "$sp" 2>/dev/null | tail -1)
|
|
125
|
+
fi
|
|
126
|
+
# ISO8601 sorts lexicographically; keep the newest. Empty ts sorts lowest.
|
|
127
|
+
if [ -z "$BEST_TS" ] || { [ -n "$ts" ] && [ "$ts" \> "$BEST_TS" ]; }; then
|
|
128
|
+
BEST_TS="$ts"
|
|
129
|
+
WORK="$inside"
|
|
130
|
+
fi
|
|
131
|
+
done <<EOF
|
|
132
|
+
$(sed -n '/^## Active$/,/^## /{/^- \[/p;}' "$INDEX")
|
|
133
|
+
EOF
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# --- Build the work-unit "[{project}: {id} {title}]" with title truncation ---
|
|
137
|
+
WORK_SEG=""
|
|
138
|
+
if [ -n "$WORK" ]; then
|
|
139
|
+
# Split "{id} - {title}" on the em-dash (U+2014).
|
|
140
|
+
EMDASH=$'\342\200\224'
|
|
141
|
+
id=${WORK%% $EMDASH *}
|
|
142
|
+
title=${WORK#* $EMDASH }
|
|
143
|
+
# Fallback if the em-dash form is absent: split on first space.
|
|
144
|
+
if [ "$id" = "$WORK" ]; then id=${WORK%% *}; title=${WORK#* }; fi
|
|
145
|
+
# Truncate long titles (~40 chars).
|
|
146
|
+
if [ ${#title} -gt 40 ]; then title="$(printf '%.40s' "$title")..."; fi
|
|
147
|
+
WORK_SEG="${C_BRACK}[${RST}${C_PROJ}${PROJ_LABEL}${RST}${C_INTENT}: ${RST}${C_ID}${id}${RST}${C_INTENT} ${title}${RST}${C_BRACK}]${RST}"
|
|
56
148
|
fi
|
|
149
|
+
|
|
150
|
+
# --- Assemble segments in order, joined by " . " ---
|
|
151
|
+
PARTS=""
|
|
152
|
+
add() { [ -z "$1" ] && return; if [ -z "$PARTS" ]; then PARTS="$1"; else PARTS="$PARTS$SEP$1"; fi; }
|
|
153
|
+
|
|
154
|
+
# Order: stable values first (model, work-unit), then the volatile-length
|
|
155
|
+
# values last (version, path) so the line does not visually jump as they change.
|
|
156
|
+
[ -n "$MODEL" ] && add "${C_MODEL}${MODEL}${RST}"
|
|
157
|
+
[ -n "$WORK_SEG" ] && add "$WORK_SEG"
|
|
158
|
+
if [ -n "$VERSION" ]; then
|
|
159
|
+
vseg="${C_VER}Plastic ${VERSION}${RST}"
|
|
160
|
+
[ -n "$NEXT" ] && vseg="${vseg}${C_VER} "$'\342\206\222'" ${RST}${C_NEXT}${NEXT}${RST}"
|
|
161
|
+
add "$vseg"
|
|
162
|
+
fi
|
|
163
|
+
[ -n "$CWD" ] && add "${C_PATH}${CWD}${RST}"
|
|
164
|
+
|
|
165
|
+
printf '%s\n' "$PARTS"
|