@zalom/plastic 1.8.0 → 1.9.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/PLASTIC.md +75 -552
- package/README.md +3 -2
- package/agents/plastic-intent-curator.md +2 -2
- package/deprecations.yml +10 -2
- package/hooks/{links-gate → edit-gates} +1 -1
- package/hooks/hooks.json +4 -44
- package/hooks/power-tools +8 -0
- package/package.json +1 -2
- package/scripts/codex-hook +17 -22
- package/scripts/doctor.rb +85 -3
- package/scripts/hook-code-gate +13 -28
- package/scripts/hook-create-gate +10 -57
- package/scripts/hook-edit-gates +58 -0
- package/scripts/hook-links-gate +8 -31
- package/scripts/hook-lock-gate +13 -58
- package/scripts/hook-power-tools +38 -0
- package/scripts/hook-savepoint-pre +8 -11
- package/scripts/lib/bridge.rb +2 -2
- package/scripts/lib/edit_gates.rb +342 -0
- package/scripts/lib/hook_registry.rb +61 -29
- package/scripts/lib/installer_core.rb +99 -17
- package/scripts/lib/qmd_hook.rb +21 -41
- package/scripts/lib/qmd_sync.rb +0 -15
- package/scripts/lib/revisions_writer.rb +1 -1
- package/scripts/restore-intent-v1 +16 -11
- package/skills/auto/SKILL.md +10 -0
- package/skills/auto/evals/evals.json +2 -2
- package/skills/conventions/SKILL.md +31 -0
- package/skills/conventions/references/completion-and-done.md +40 -0
- package/skills/conventions/references/gates-and-enforcement.md +23 -0
- package/skills/conventions/references/knowledge-graph.md +47 -0
- package/skills/conventions/references/lifecycle-and-savepoints.md +11 -0
- package/skills/conventions/references/locks-and-worktrees.md +113 -0
- package/skills/conventions/references/maintenance-and-revisions.md +170 -0
- package/skills/conventions/references/roadmaps.md +44 -0
- package/skills/conventions/references/tiers-and-dispatch.md +134 -0
- package/skills/doctor/SKILL.md +9 -1
- package/skills/intent-brainstorming/SKILL.md +4 -0
- package/skills/intent-continuing/SKILL.md +4 -0
- package/skills/intent-creating/SKILL.md +7 -2
- package/skills/intent-ending/SKILL.md +4 -0
- package/skills/intent-executing/SKILL.md +10 -0
- package/skills/intent-linking/SKILL.md +4 -0
- package/skills/intent-locking/SKILL.md +4 -0
- package/skills/intent-planning/SKILL.md +7 -0
- package/skills/intent-savepoint/SKILL.md +4 -0
- package/skills/intent-speccing/SKILL.md +4 -0
- package/skills/intent-starting/SKILL.md +10 -0
- package/skills/project-creating/references/project-scaffolding.md +2 -2
- package/skills/roadmap/SKILL.md +4 -0
- package/skills/roadmap-continuing/SKILL.md +4 -0
- package/skills/skill-creating/SKILL.md +3 -0
- package/skills/skill-creating/references/defaults-first.md +23 -0
- package/skills/skill-creating/references/hooks.md +4 -1
- package/skills/store-curating/SKILL.md +8 -0
- package/skills/store-indexing/SKILL.md +8 -0
- package/skills/tutorial/references/track-2-auto.md +2 -3
- package/PLASTIC-reference.md +0 -138
- package/hooks/code-gate +0 -27
- package/hooks/create-gate +0 -3
- package/hooks/lock-gate +0 -21
- package/hooks/qmd-search +0 -8
- package/hooks/retrieval-gate +0 -10
- package/hooks/savepoint-pre +0 -10
- package/scripts/hook-qmd-search +0 -44
- package/scripts/hook-retrieval-gate +0 -148
- package/scripts/lib/retrieval_gate.rb +0 -211
|
@@ -22,6 +22,10 @@ It is **sugar on top of the conventions**, not a source of truth. The gate hook
|
|
|
22
22
|
nothing to save by hand. State is always derivable from files-on-disk; the ledger just lets
|
|
23
23
|
a resuming agent read the cycle's succession from one glance (last line = where we are).
|
|
24
24
|
|
|
25
|
+
Read `../plastic-conventions/references/lifecycle-and-savepoints.md` for the subagent
|
|
26
|
+
report-home contract that this ledger feeds. This path resolves relative to this skill's own
|
|
27
|
+
installed directory.
|
|
28
|
+
|
|
25
29
|
## When to Use
|
|
26
30
|
- A PreCompact hook fires, or the user says "save" / "savepoint": verify the ledger is current.
|
|
27
31
|
- Resuming an intent: read the ledger to learn the cycle's succession quickly.
|
|
@@ -47,6 +47,10 @@ not the Transition Gates table row). Writing `plan.md` is what opens the code ga
|
|
|
47
47
|
writing `plan.md` is not this skill's job, that is `plastic-intent-planning`. State this to the user
|
|
48
48
|
at step 6 so the handoff at step 8 is expected, not a surprise.
|
|
49
49
|
|
|
50
|
+
Read `../plastic-conventions/references/lifecycle-and-savepoints.md` for the subagent
|
|
51
|
+
report-home contract behind this handoff. This path resolves relative to this skill's own
|
|
52
|
+
installed directory.
|
|
53
|
+
|
|
50
54
|
## Tier stamp (step 3, convention only)
|
|
51
55
|
|
|
52
56
|
Write `Tier: S|M|L` as the literal first line of the file, above the `# Spec:` heading. This line
|
|
@@ -32,6 +32,10 @@ The lock is non-negotiable and comes before any mutating work. The ACTION-3 lock
|
|
|
32
32
|
enforces it: without a held lock, mutating writes to this active intent's dir are denied with
|
|
33
33
|
"run /plastic-intent-starting to lock and begin".
|
|
34
34
|
|
|
35
|
+
Read `../plastic-conventions/references/locks-and-worktrees.md` for delivery isolation: the
|
|
36
|
+
single-owner lock, claims, worktrees, solo mode, and the station ledger behind everything below.
|
|
37
|
+
This path resolves relative to this skill's own installed directory.
|
|
38
|
+
|
|
35
39
|
1. **Ensure the intent is in INDEX `## Active`.** If it sits in `## Future`, activate it
|
|
36
40
|
(move it to `## Active`, auto-commit) before arming. Creation precedes activation, so a
|
|
37
41
|
brand-new What intent is activated here, then locked.
|
|
@@ -106,12 +110,18 @@ Never re-ask at a later station.
|
|
|
106
110
|
- **auto** → `arm_auto` (lock + auto), then hand off to `plastic-auto`. The auto branch's
|
|
107
111
|
only remaining job is the handoff; `plastic-auto` runs the cycle from here.
|
|
108
112
|
|
|
113
|
+
Read `../plastic-conventions/references/tiers-and-dispatch.md` for tier sizing and the
|
|
114
|
+
stage-to-agent dispatch rules behind the auto branch above.
|
|
115
|
+
|
|
109
116
|
## Board at the latest delivered station
|
|
110
117
|
|
|
111
118
|
The station is derived from `savepoint.md` last line + real artifacts on disk. See
|
|
112
119
|
`references/boarding-matrix.md` for the full table (last line → latest delivered → boards at →
|
|
113
120
|
continue with) and the per-station notes. Summary of what "continue" means per station:
|
|
114
121
|
|
|
122
|
+
Read `../plastic-conventions/references/gates-and-enforcement.md` for the transition-gate
|
|
123
|
+
mechanics, the audited escape, and gate logging that govern moving between the stations below.
|
|
124
|
+
|
|
115
125
|
- **What** → do what What requires (106-expanded), then brainstorm → `spec.md`.
|
|
116
126
|
- **Why** → continue brainstorming → `spec.md`.
|
|
117
127
|
- **How** → continue `plan.md` + `actions/` + `checklist.md`.
|
|
@@ -17,8 +17,8 @@ Create `AGENTS.md` in the project root with:
|
|
|
17
17
|
```markdown
|
|
18
18
|
# <Project Name> — Agent Instructions
|
|
19
19
|
|
|
20
|
-
Read `PLASTIC.md` in `~/.plastic
|
|
21
|
-
Follow it exactly.
|
|
20
|
+
Read `PLASTIC.md` in `~/.plastic/` for the core conventions; deeper doctrine lives in
|
|
21
|
+
the `plastic-conventions` skill's chapters. Follow it exactly.
|
|
22
22
|
|
|
23
23
|
This file is the operating contract for this project. Any agent entering
|
|
24
24
|
this project reads this file first.
|
package/skills/roadmap/SKILL.md
CHANGED
|
@@ -43,6 +43,10 @@ See `references/file-format.md` for the exact entry-line shape, status vocabular
|
|
|
43
43
|
format, and a worked example. See `references/operations.md` for step-by-step mechanics of each
|
|
44
44
|
verb above.
|
|
45
45
|
|
|
46
|
+
Read `../plastic-conventions/references/roadmaps.md` for the roadmap file format, batch
|
|
47
|
+
semantics, and the status-mirror rule that this skill's own file-format reference builds on. This
|
|
48
|
+
path resolves relative to this skill's own installed directory.
|
|
49
|
+
|
|
46
50
|
## Notes
|
|
47
51
|
|
|
48
52
|
- File location and the four-section shape are identical across tiers; do not invent a different
|
|
@@ -45,6 +45,10 @@ roadmap handoff had to be resumed by hand, carried as a free-prose note in `171`
|
|
|
45
45
|
4. A genuine tie (two candidates equally live) is presented to the user and resolved by the
|
|
46
46
|
single ask below, not silently picked.
|
|
47
47
|
|
|
48
|
+
Read `../plastic-conventions/references/roadmaps.md` for the roadmap file format, batch
|
|
49
|
+
semantics, and the status-mirror rule behind the candidates gathered above. This path resolves
|
|
50
|
+
relative to this skill's own installed directory.
|
|
51
|
+
|
|
48
52
|
## Present state
|
|
49
53
|
|
|
50
54
|
Present the chosen roadmap's `## Goal`, the current batch with each entry's mirrored status, the
|
|
@@ -49,6 +49,9 @@ routes each authoring task to the reference that holds the depth.
|
|
|
49
49
|
| Deciding script versus prose, or writing a script | `references/scripts.md` |
|
|
50
50
|
| Building evals for a skill | `references/evals.md` |
|
|
51
51
|
|
|
52
|
+
Read `references/defaults-first.md` before naming an external skill as a default, for the
|
|
53
|
+
Plastic-first, delegate-by-exception rule.
|
|
54
|
+
|
|
52
55
|
## Shrink context, or let a skill self-improve
|
|
53
56
|
|
|
54
57
|
- When prompts or tool output blow the context budget, open `references/hooks.md` (E7) for
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Defaults First
|
|
2
|
+
|
|
3
|
+
This chapter holds the Plastic-first, delegate-by-exception rule for naming an external skill as a default.
|
|
4
|
+
|
|
5
|
+
### Defaults-First
|
|
6
|
+
|
|
7
|
+
Plastic stands on its own. Skills and agents use Plastic's own defaults; an
|
|
8
|
+
external skill (for example `superpowers:*`) is opt-in, never load-bearing.
|
|
9
|
+
|
|
10
|
+
- **Default to Plastic, delegate by exception.** Name the Plastic-native path as
|
|
11
|
+
the default. Delegate to an external skill only when (a) it is available in the
|
|
12
|
+
harness, or (b) the user explicitly asks for it. A user without that plugin must
|
|
13
|
+
still get the core behavior.
|
|
14
|
+
- **Phrase external skills as enhancements.** Write "use Plastic's native X by
|
|
15
|
+
default; if `superpowers:<skill>` is available, or the user prefers it, delegate
|
|
16
|
+
to it" never "delegate to `superpowers:<skill>`" as the only path.
|
|
17
|
+
- **Optional dependencies detect then degrade.** `qmd` is the reference shape:
|
|
18
|
+
`scripts/lib/qmd_sync.rb` detects the binary first and every verb no-ops cleanly
|
|
19
|
+
when it is absent (see `scripts/qmd-sync`). Optional CLIs and MCP servers follow
|
|
20
|
+
the same detect-then-skip pattern, so a missing tool never crashes a session.
|
|
21
|
+
- **Legitimate hard dependencies are exempt.** Ruby, Node, git, and POSIX tools are
|
|
22
|
+
the cost of running Plastic, not silent coupling. The principle targets accidental
|
|
23
|
+
dependence on external skills doing work Plastic should do itself.
|
|
@@ -77,7 +77,10 @@ Notes that change the choice of event: [E2]
|
|
|
77
77
|
- PreCompact fires before the runtime compacts the conversation. Use it to write a savepoint
|
|
78
78
|
while the full context still exists.
|
|
79
79
|
|
|
80
|
-
Plastic ships working instances of each: `scripts/hook-
|
|
80
|
+
Plastic ships working instances of each: `hooks/edit-gates` -> `scripts/hook-edit-gates`
|
|
81
|
+
(PreToolUse, the merged dispatcher that runs code-gate, lock-gate, savepoint-pre, links-gate,
|
|
82
|
+
and create-gate in one process; each gate's own logic lives in `scripts/lib/edit_gates.rb`
|
|
83
|
+
and is also reachable through the standalone `scripts/hook-code-gate` CLI wrapper),
|
|
81
84
|
`scripts/hook-session-start` (SessionStart boot and inject), `scripts/hook-savepoint-pre`
|
|
82
85
|
(PreCompact savepoint). Read one before authoring a new hook of the same shape.
|
|
83
86
|
|
|
@@ -39,6 +39,10 @@ The agent handles:
|
|
|
39
39
|
- Cluster management (create, merge, rename)
|
|
40
40
|
- Orphan detection
|
|
41
41
|
|
|
42
|
+
Read `../plastic-conventions/references/knowledge-graph.md` for the linking doctrine: tiers of
|
|
43
|
+
influence, sources versus chain, and the `## Links` projection, before judging a link discovery or
|
|
44
|
+
orphan finding. This path resolves relative to this skill's own installed directory.
|
|
45
|
+
|
|
42
46
|
When an intent reaches a terminal state, moved to Completed OR Abandoned, do these things:
|
|
43
47
|
|
|
44
48
|
1. Author a real `outcome.md` in the intent directory from `~/.plastic/templates/outcome.md`, with the frontmatter `disposition: delivered` for a completed intent or `disposition: abandoned` for an abandoned one. `outcome.md` is MANDATORY at every terminal, delivered and abandoned alike: on abandon it records the abandonment reason and replaces the scaffolded placeholder sentinel (never leave `outcome.md` a placeholder at a terminal).
|
|
@@ -54,3 +58,7 @@ follows its own step 7: it detects (never acquires) the target's delivery lock,
|
|
|
54
58
|
clean working tree, and performs the fix on a fresh branch merged back to main as one closed
|
|
55
59
|
operation, with an append-only `revisions.md` receipt in the same pass as the edit. See
|
|
56
60
|
`agents/plastic-intent-curator.md` for the exact mechanics.
|
|
61
|
+
|
|
62
|
+
Read `../plastic-conventions/references/maintenance-and-revisions.md` for WORK versus
|
|
63
|
+
MAINTENANCE, the `revisions.md` move-and-record contract, and the violation-tag catalog before
|
|
64
|
+
running a maintenance dispatch like this one.
|
|
@@ -75,8 +75,16 @@ When 3+ intents share tags but aren't in a cluster, suggest a new cluster headin
|
|
|
75
75
|
### Flag Orphans
|
|
76
76
|
Intents with no links (empty `sources`, empty `chain`, no `## Links` entries, not in any cluster) should be flagged for curation.
|
|
77
77
|
|
|
78
|
+
Before reclassifying a structural finding outside routine indexing, read
|
|
79
|
+
`../plastic-conventions/references/maintenance-and-revisions.md` for WORK versus MAINTENANCE, the
|
|
80
|
+
`revisions.md` move-and-record contract, and the violation-tag catalog.
|
|
81
|
+
|
|
78
82
|
REQUIRED BACKGROUND: intent-linking (for understanding connection types and Zettelkasten theory)
|
|
79
83
|
|
|
84
|
+
Read `../plastic-conventions/references/knowledge-graph.md` for the linking doctrine: tiers of
|
|
85
|
+
influence, sources versus chain, and the `## Links` projection. This path resolves relative to
|
|
86
|
+
this skill's own installed directory.
|
|
87
|
+
|
|
80
88
|
## References
|
|
81
89
|
|
|
82
90
|
- Read `references/zettelkasten-linking.md` for the three structural layers (Folgezettel, directed graph, tags) and how they map to INDEX.md organization
|
|
@@ -56,10 +56,9 @@ Checkpoint: name one thing auto will always stop and ask about, rather than deci
|
|
|
56
56
|
No new command. Auto still honors every hard gate a guided session would hit: the delivery
|
|
57
57
|
lock (one owner at a time), the code gate (shut until `plan.md` and `checklist.md` exist),
|
|
58
58
|
and the create gate (intents only come from the tool that makes them, never hand-authored).
|
|
59
|
-
One gate, retrieval, is advisory only and never blocks anything; it just adds a note.
|
|
60
59
|
|
|
61
|
-
Checkpoint: read one gate message from the run so far and
|
|
62
|
-
|
|
60
|
+
Checkpoint: read one gate message from the run so far and name which of the three gates it
|
|
61
|
+
came from.
|
|
63
62
|
|
|
64
63
|
### 4. Reading the per-stage reports
|
|
65
64
|
|
package/PLASTIC-reference.md
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
# Plastic - Reference
|
|
2
|
-
|
|
3
|
-
> **This file is maintained by Plastic.** It will be overwritten when the
|
|
4
|
-
> plugin is updated. It holds reference material: read it on demand, it is
|
|
5
|
-
> not injected at session start.
|
|
6
|
-
|
|
7
|
-
### Structural maintenance and revisions.md
|
|
8
|
-
|
|
9
|
-
When a delivered intent accumulates structural junk (an unsanctioned section, a stray file, a
|
|
10
|
-
frontmatter edge to an intent that no longer exists), the intent-curator relocates it into
|
|
11
|
-
`revisions.md` instead of reopening the work. Each entry is a versioned, dated header
|
|
12
|
-
(`## Revision vN - YYYY-MM-DD-HH:MM`) plus `Why` (one sentence naming the broken rule, ending
|
|
13
|
-
with `[rule: <tag>]`), `Prior location`, and either `Content held` (the verbatim removed
|
|
14
|
-
content) or, for a frontmatter edit, a one-line `Change` (before and after). A stray file has
|
|
15
|
-
its full content embedded and the original is deleted.
|
|
16
|
-
|
|
17
|
-
Violation tags (starter set, free-text tags allowed):
|
|
18
|
-
- `unsanctioned-section`: a top-level section the sanctioned-section rule now rejects
|
|
19
|
-
- `phantom-section`: a section referenced but not present or not sanctioned
|
|
20
|
-
- `stray-file`: a file that does not belong in the intent directory
|
|
21
|
-
- `dangling-ref`: a link or reference to something that no longer exists
|
|
22
|
-
- `broken-chain`: a chain frontmatter edge to an intent that no longer exists
|
|
23
|
-
- `broken-source`: a sources frontmatter edge to an intent that no longer exists
|
|
24
|
-
- `misplaced-content`: content that belongs in a different artifact or section
|
|
25
|
-
- `links-projection`: a tool-authored `## Links` regeneration (project-links; intent 197)
|
|
26
|
-
- `graph-rebuild`: a tool-authored sources/chain frontmatter rebuild (rebuild-graph; intent 197)
|
|
27
|
-
|
|
28
|
-
## Two Processes
|
|
29
|
-
|
|
30
|
-
| Process | Scope | Type | Actor |
|
|
31
|
-
|---|---|---|---|
|
|
32
|
-
| **Build → Observe → Repeat** | The system | Continuous loop | Coordinator |
|
|
33
|
-
| **What → Why → How → Exec** | One intent | Finite lifecycle | Agent |
|
|
34
|
-
|
|
35
|
-
B→O→R is the Coordinator's heartbeat. W→W→H→E is what happens inside each intent.
|
|
36
|
-
The connection: an intent's `## Insights` feeds the Coordinator's Observe phase.
|
|
37
|
-
|
|
38
|
-
## Defaults-First
|
|
39
|
-
|
|
40
|
-
Plastic stands on its own. Skills and agents use Plastic's own defaults; an
|
|
41
|
-
external skill (for example `superpowers:*`) is opt-in, never load-bearing.
|
|
42
|
-
|
|
43
|
-
- **Default to Plastic, delegate by exception.** Name the Plastic-native path as
|
|
44
|
-
the default. Delegate to an external skill only when (a) it is available in the
|
|
45
|
-
harness, or (b) the user explicitly asks for it. A user without that plugin must
|
|
46
|
-
still get the core behavior.
|
|
47
|
-
- **Phrase external skills as enhancements.** Write "use Plastic's native X by
|
|
48
|
-
default; if `superpowers:<skill>` is available, or the user prefers it, delegate
|
|
49
|
-
to it" never "delegate to `superpowers:<skill>`" as the only path.
|
|
50
|
-
- **Optional dependencies detect then degrade.** `qmd` is the reference shape:
|
|
51
|
-
`scripts/lib/qmd_sync.rb` detects the binary first and every verb no-ops cleanly
|
|
52
|
-
when it is absent (see `scripts/qmd-sync`). Optional CLIs and MCP servers follow
|
|
53
|
-
the same detect-then-skip pattern, so a missing tool never crashes a session.
|
|
54
|
-
- **Legitimate hard dependencies are exempt.** Ruby, Node, git, and POSIX tools are
|
|
55
|
-
the cost of running Plastic, not silent coupling. The principle targets accidental
|
|
56
|
-
dependence on external skills doing work Plastic should do itself.
|
|
57
|
-
|
|
58
|
-
## Roadmaps
|
|
59
|
-
|
|
60
|
-
Roadmaps exist for planned parallel delivery of intents in a coherent and organized way. A roadmap
|
|
61
|
-
is a named, ordered, delivery-side collection of intents: the delivery-side counterpart to a
|
|
62
|
-
release (completion-side, tracked in `CHANGELOG.md`). Use `plastic-roadmap` to create, order,
|
|
63
|
-
close, and consume one.
|
|
64
|
-
|
|
65
|
-
File location: `roadmaps/{slug}.md`, a sibling of `INDEX.md`, wherever `INDEX.md` lives, never
|
|
66
|
-
inside `store/` (store holds intent directories, not project artifacts). For a project that is its
|
|
67
|
-
root, `~/.plastic/projects/{slug}/roadmaps/`, beside `project.yml`; for the global tier it is
|
|
68
|
-
`~/.plastic/roadmaps/`, beside `~/.plastic/INDEX.md`. `roadmaps/` lists only live (open or
|
|
69
|
-
in-flight) roadmaps: once a roadmap's goal is reached, it moves to `roadmaps/archived/{slug}.md`,
|
|
70
|
-
a sibling subdirectory scaffolded once with a `.gitkeep`.
|
|
71
|
-
|
|
72
|
-
A roadmap file has four sections, in order: a title/meta header, `## Goal`, `## Batches`, and an
|
|
73
|
-
append-only dated `## Log`. `## Goal` is a checkable prose condition read by a human or agent, not
|
|
74
|
-
an executable checker. `## Batches` holds ordered batches; entries inside a batch are
|
|
75
|
-
parallel-safe, batches run sequentially, top to bottom. A roadmap written before owner ruling 145
|
|
76
|
-
may instead use the legacy `## Waves` heading; the tooling accepts both, but never renames an
|
|
77
|
-
existing roadmap file to migrate it.
|
|
78
|
-
|
|
79
|
-
Each batch entry carries a status token (`queued`/`delivering`/`delivered`/`abandoned`/`blocked`)
|
|
80
|
-
that mirrors that intent's status in `INDEX.md`. `INDEX.md` is the single writer of intent status;
|
|
81
|
-
on any conflict INDEX wins and the roadmap entry is corrected to match.
|
|
82
|
-
|
|
83
|
-
**Human-comprehension surface.** A roadmap is also written to be read cold. Batch entries render as
|
|
84
|
-
checkboxes (checked once delivered, unchecked otherwise) next to the status token, and each `## Log`
|
|
85
|
-
line is one plain-language sentence, starting `YYYY-MM-DD HH:MM UTC`, written the way an
|
|
86
|
-
engineering manager would brief a non-expert executive: what shipped and why it matters, no jargon
|
|
87
|
-
or codenames, ending with a link
|
|
88
|
-
to that intent's `outcome.md`. The log points at the detail instead of repeating it, so a person
|
|
89
|
-
opening the file with no other context can tell what shipped, what is running now, and what is
|
|
90
|
-
next in under a minute.
|
|
91
|
-
|
|
92
|
-
**Relationship to loop engineering (intent 69).** A roadmap is the planning half of the work; the
|
|
93
|
-
loop is its runtime. Batches lay out the parallelism plan: what can run together, and in what order.
|
|
94
|
-
Loop engineering (intent 69, not yet delivered) is expected to consume that plan and supply the
|
|
95
|
-
running parts, the heartbeat, how many dispatches run at once, checking the goal, and resuming
|
|
96
|
-
after a stop. This section only states the relationship and points to intent 69 as the future
|
|
97
|
-
consumer; it does not change intent 69's own design.
|
|
98
|
-
|
|
99
|
-
## Context-economy measurement buckets (84a)
|
|
100
|
-
|
|
101
|
-
Intent 84 defines three buckets for sibling 84a to audit against; 84 does not run the audit.
|
|
102
|
-
|
|
103
|
-
- (a) gate-hook prose tokens: the per-transition narration emitted by the gate hook.
|
|
104
|
-
- (b) main-loop store-read tokens: tokens the main agent spends reading or grepping the store
|
|
105
|
-
in the transcript.
|
|
106
|
-
- (c) authored-section sizes: sizes of authored artifacts (INDEX entries and the like).
|
|
107
|
-
|
|
108
|
-
## Deprecation Process
|
|
109
|
-
|
|
110
|
-
Deprecations live in `deprecations.yml` and are shown at SessionStart. While Plastic is
|
|
111
|
-
pre-1.0, a satisfied deprecation (its migration is already done on installed machines) may be
|
|
112
|
-
removed immediately instead of waiting for its declared `removal` version. From `1.0.0` on,
|
|
113
|
-
the steady-state grace rule applies (removal at least two minors ahead). For the full process,
|
|
114
|
-
severity levels, and the pre-1.0 exception, see the `plastic-releasing` skill.
|
|
115
|
-
|
|
116
|
-
## Skills Reference
|
|
117
|
-
|
|
118
|
-
Detailed conventions live inside the skills that use them, not in this file.
|
|
119
|
-
|
|
120
|
-
| Topic | Skill | References in skill |
|
|
121
|
-
|-------|-------|-------------------|
|
|
122
|
-
| Creating intents, lifecycle | `plastic-intent-creating` | lifecycle, wikilinks |
|
|
123
|
-
| Brainstorming, spec writing | `plastic-intent-brainstorming` | — |
|
|
124
|
-
| Planning, actions | `plastic-intent-planning` | — |
|
|
125
|
-
| Execution, delivery | `plastic-intent-executing` | — |
|
|
126
|
-
| Autonomous delivery | `plastic-auto` | agent architecture |
|
|
127
|
-
| Save/restore state | `plastic-intent-savepoint`, `plastic-intent-continuing` | context management |
|
|
128
|
-
| Knowledge graph, linking | `plastic-intent-linking` | zettelkasten, wikilinks |
|
|
129
|
-
| Projects, hubs | `plastic-project-creating` | hubs, project stores |
|
|
130
|
-
| Provision a project store | `plastic-store-provisioning` | project stores |
|
|
131
|
-
| Index maintenance | `plastic-store-indexing` | — |
|
|
132
|
-
| Releases, deprecations | `plastic-releasing` | deprecation process |
|
|
133
|
-
| 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 |
|
|
134
|
-
| Report a Plastic quirk, bug, or feature idea | `plastic-feedback` | transport and privacy (redaction checklist, why a prefilled URL) |
|
|
135
|
-
| Authoring skills, agents, hooks | `plastic-skill-creating` | progressive disclosure, agentskills.io spec |
|
|
136
|
-
| Evaluating skills, evals | `plastic-skill-evaluating` | eval methodology, convention checks |
|
|
137
|
-
| Create, order, and consume a roadmap of intents | `plastic-roadmap` | file format, operations |
|
|
138
|
-
|
package/hooks/code-gate
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
INPUT=$(cat)
|
|
3
|
-
FILE_PATH=$(echo "$INPUT" | ruby -rjson -e '
|
|
4
|
-
data = JSON.parse(STDIN.read)
|
|
5
|
-
ti = data["tool_input"] || data["tool_params"] || {}
|
|
6
|
-
path = ti["file_path"] || ti["notebook_path"] || ti["relative_path"] || ""
|
|
7
|
-
if !path.empty? && !path.start_with?("/")
|
|
8
|
-
root = ti["project_root"] || data["cwd"] || ""
|
|
9
|
-
path = File.join(root, path) unless root.empty?
|
|
10
|
-
end
|
|
11
|
-
puts path
|
|
12
|
-
' 2>/dev/null)
|
|
13
|
-
|
|
14
|
-
if [ -z "$FILE_PATH" ]; then
|
|
15
|
-
exit 0
|
|
16
|
-
fi
|
|
17
|
-
|
|
18
|
-
SESSION_ID=$(echo "$INPUT" | ruby -rjson -e 'data = JSON.parse(STDIN.read); puts data.dig("session_id") || ""' 2>/dev/null)
|
|
19
|
-
|
|
20
|
-
NEW_CONTENT=$(echo "$INPUT" | ruby -rjson -e '
|
|
21
|
-
data = JSON.parse(STDIN.read)
|
|
22
|
-
ti = data["tool_input"] || data["tool_params"] || {}
|
|
23
|
-
print ti["content"] || ti["new_string"] || ""
|
|
24
|
-
' 2>/dev/null)
|
|
25
|
-
|
|
26
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
27
|
-
ruby "$SCRIPT_DIR/../scripts/hook-code-gate" "$FILE_PATH" "$SESSION_ID" "$NEW_CONTENT"
|
package/hooks/create-gate
DELETED
package/hooks/lock-gate
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
INPUT=$(cat)
|
|
3
|
-
FILE_PATH=$(echo "$INPUT" | ruby -rjson -e '
|
|
4
|
-
data = JSON.parse(STDIN.read)
|
|
5
|
-
ti = data["tool_input"] || data["tool_params"] || {}
|
|
6
|
-
path = ti["file_path"] || ti["notebook_path"] || ti["relative_path"] || ""
|
|
7
|
-
if !path.empty? && !path.start_with?("/")
|
|
8
|
-
root = ti["project_root"] || data["cwd"] || ""
|
|
9
|
-
path = File.join(root, path) unless root.empty?
|
|
10
|
-
end
|
|
11
|
-
puts path
|
|
12
|
-
' 2>/dev/null)
|
|
13
|
-
|
|
14
|
-
if [ -z "$FILE_PATH" ]; then
|
|
15
|
-
exit 0
|
|
16
|
-
fi
|
|
17
|
-
|
|
18
|
-
SESSION_ID=$(echo "$INPUT" | ruby -rjson -e 'data = JSON.parse(STDIN.read); puts data.dig("session_id") || ""' 2>/dev/null)
|
|
19
|
-
|
|
20
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
21
|
-
ruby "$SCRIPT_DIR/../scripts/hook-lock-gate" "$FILE_PATH" "$SESSION_ID"
|
package/hooks/qmd-search
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# qmd-first search hook launcher (intent 66). No-op when there is no global store.
|
|
3
|
-
GLOBAL_INDEX="$HOME/.plastic/INDEX.md"
|
|
4
|
-
if [ ! -f "$GLOBAL_INDEX" ]; then
|
|
5
|
-
exit 0
|
|
6
|
-
fi
|
|
7
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
-
exec ruby "$SCRIPT_DIR/../scripts/hook-qmd-search" "$HOME/.plastic"
|
package/hooks/retrieval-gate
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
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"
|
package/hooks/savepoint-pre
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
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/scripts/hook-qmd-search
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# encoding: UTF-8
|
|
3
|
-
# frozen_string_literal: true
|
|
4
|
-
|
|
5
|
-
# qmd-first UserPromptSubmit hook (intent 66). Reads the prompt from stdin JSON,
|
|
6
|
-
# delegates the decision to QmdHook.run under a hard timeout, and prints
|
|
7
|
-
# hookSpecificOutput when there is something to inject/remind. Silent no-op (exit
|
|
8
|
-
# 0) when qmd is absent, the prompt is trivial, or anything goes wrong. ARGV[0]
|
|
9
|
-
# is the plastic_home (passed by the launcher); defaults to ~/.plastic.
|
|
10
|
-
require "json"
|
|
11
|
-
require "timeout"
|
|
12
|
-
require_relative "lib/qmd_hook"
|
|
13
|
-
|
|
14
|
-
raw = begin
|
|
15
|
-
STDIN.read
|
|
16
|
-
rescue StandardError
|
|
17
|
-
""
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
prompt = begin
|
|
21
|
-
parsed = JSON.parse(raw)
|
|
22
|
-
parsed.is_a?(Hash) ? parsed["user_prompt"].to_s : ""
|
|
23
|
-
rescue StandardError
|
|
24
|
-
""
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
plastic_home = (ARGV[0] && !ARGV[0].empty?) ? ARGV[0] : File.expand_path("~/.plastic")
|
|
28
|
-
|
|
29
|
-
context = begin
|
|
30
|
-
Timeout.timeout(2) do
|
|
31
|
-
QmdHook.run(prompt: prompt, cwd: Dir.pwd, plastic_home: plastic_home)
|
|
32
|
-
end
|
|
33
|
-
rescue Exception
|
|
34
|
-
nil
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
exit 0 if context.nil? || context.strip.empty?
|
|
38
|
-
|
|
39
|
-
puts JSON.generate(
|
|
40
|
-
"hookSpecificOutput" => {
|
|
41
|
-
"hookEventName" => "UserPromptSubmit",
|
|
42
|
-
"additionalContext" => context,
|
|
43
|
-
}
|
|
44
|
-
)
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# encoding: UTF-8
|
|
3
|
-
# frozen_string_literal: true
|
|
4
|
-
|
|
5
|
-
# PreToolUse retrieval gate (intent 84, Lever 2; operation-based redesign 89a;
|
|
6
|
-
# ADVISORY since intent 108, D8). Reads the tool call (JSON on stdin: tool_name +
|
|
7
|
-
# tool_input), computes capabilities (QMD detect + freshness), and delegates the
|
|
8
|
-
# decision to RetrievalGate. It NEVER blocks: reads and searches always run
|
|
9
|
-
# (exit 0). A store content search that QMD could serve better gets a QMD hint
|
|
10
|
-
# via PreToolUse additionalContext on stdout. Hard gates guard writes, locks,
|
|
11
|
-
# and structure; never reads.
|
|
12
|
-
# Fail-open: any parse error, timeout, or unexpected exception exits 0. On the
|
|
13
|
-
# STALE QMD path RetrievalGate fires QmdSync.reindex_async (NEVER synchronous).
|
|
14
|
-
# Binds subagents (PreToolUse hooks apply to subagent tool calls too).
|
|
15
|
-
#
|
|
16
|
-
# Only CONTENT SEARCH over store markdown gets the hint; reads and structural
|
|
17
|
-
# ops stay silent. Code navigation is a soft prompt recommendation
|
|
18
|
-
# (UserPromptSubmit power-tools), not handled here.
|
|
19
|
-
#
|
|
20
|
-
# Scope: only the agent's own Bash/Read/Grep/Glob calls. Ruby `File.read` inside
|
|
21
|
-
# scripts is invisible to a PreToolUse hook and is out of scope (no exemptions).
|
|
22
|
-
#
|
|
23
|
-
# ARGV[0] is plastic_home (passed by the launcher), like hook-qmd-search.
|
|
24
|
-
|
|
25
|
-
require "json"
|
|
26
|
-
require "timeout"
|
|
27
|
-
require_relative "lib/retrieval_gate"
|
|
28
|
-
require_relative "lib/qmd_sync"
|
|
29
|
-
|
|
30
|
-
module RetrievalGateHook
|
|
31
|
-
module_function
|
|
32
|
-
|
|
33
|
-
# Pure-ish core: capabilities and reindex are injected so this is unit-testable
|
|
34
|
-
# with no real qmd. Returns [exit_code, stderr_string, stdout_json].
|
|
35
|
-
# stdin: raw PreToolUse JSON
|
|
36
|
-
# capabilities: { qmd:, qmd_fresh: }
|
|
37
|
-
# reindex: callable fired on the STALE path
|
|
38
|
-
def run(stdin:, plastic_home:, cwd:, capabilities:, reindex: -> {})
|
|
39
|
-
payload = parse(stdin)
|
|
40
|
-
return [0, nil, nil] unless payload
|
|
41
|
-
|
|
42
|
-
tool_name = payload["tool_name"].to_s
|
|
43
|
-
tool_input = payload["tool_input"]
|
|
44
|
-
tool_input = {} unless tool_input.is_a?(Hash)
|
|
45
|
-
|
|
46
|
-
bypassed = false
|
|
47
|
-
reason = RetrievalGate.decision(
|
|
48
|
-
tool_name: tool_name, tool_input: tool_input,
|
|
49
|
-
plastic_home: plastic_home, cwd: cwd,
|
|
50
|
-
capabilities: capabilities, reindex: reindex
|
|
51
|
-
) { |_sig| bypassed = true }
|
|
52
|
-
|
|
53
|
-
if reason
|
|
54
|
-
# Advisory (intent 108, D8): reads and searches are never denied. The
|
|
55
|
-
# would-block reason becomes a hint the agent sees alongside the result.
|
|
56
|
-
hint = JSON.generate(
|
|
57
|
-
"hookSpecificOutput" => {
|
|
58
|
-
"hookEventName" => "PreToolUse",
|
|
59
|
-
"additionalContext" =>
|
|
60
|
-
"PLASTIC advisory: #{reason} (this search ran; the hint is not a block)"
|
|
61
|
-
}
|
|
62
|
-
)
|
|
63
|
-
[0, nil, hint]
|
|
64
|
-
else
|
|
65
|
-
# The `# qmd-ok` bypass token is still accepted (bypassed is set), but
|
|
66
|
-
# with nothing to bypass it no longer announces itself.
|
|
67
|
-
[0, nil, nil]
|
|
68
|
-
end
|
|
69
|
-
rescue StandardError
|
|
70
|
-
[0, nil, nil] # fail-open
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def parse(raw)
|
|
74
|
-
data = JSON.parse(raw.to_s)
|
|
75
|
-
data.is_a?(Hash) ? data : nil
|
|
76
|
-
rescue StandardError
|
|
77
|
-
nil
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Detect real capabilities for the live executable. Probes are injected so the
|
|
81
|
-
# three-tier QMD failure model is unit-testable:
|
|
82
|
-
# - absent : QMD not on PATH -> allow, no warn.
|
|
83
|
-
# - broken : QMD present but freshness probe -> tier-b: WARN once, then treat
|
|
84
|
-
# raises/times out as absent this turn (allow).
|
|
85
|
-
# - fresh : probe returns truthy -> gate is live.
|
|
86
|
-
# A slow `qmd status` cannot stall a tool call: a 2s Timeout bounds the probe.
|
|
87
|
-
def detect_capabilities(cwd:,
|
|
88
|
-
detect: -> { QmdSync.detect },
|
|
89
|
-
fresh: -> { QmdSync.fresh? },
|
|
90
|
-
warn: ->(m) { $stderr.puts(m) })
|
|
91
|
-
qmd = detect.call
|
|
92
|
-
qmd_fresh = false
|
|
93
|
-
if qmd
|
|
94
|
-
begin
|
|
95
|
-
qmd_fresh = Timeout.timeout(2) { fresh.call }
|
|
96
|
-
rescue StandardError
|
|
97
|
-
# Tier-b: QMD is present but its freshness probe broke/stalled. Distinct
|
|
98
|
-
# from QMD being absent — warn so a degraded QMD is visible, then fail open
|
|
99
|
-
# (allow this turn, no reindex).
|
|
100
|
-
warn.call("PLASTIC GATE — QMD is present but its freshness probe failed; " \
|
|
101
|
-
"allowing this turn without routing search to QMD (check qmd).")
|
|
102
|
-
qmd = false
|
|
103
|
-
qmd_fresh = false
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
{ qmd: qmd, qmd_fresh: qmd_fresh }
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Best-effort reindex callable for the STALE path. Resolves the collection from
|
|
110
|
-
# cwd (project + global) and fires the async reindexer for each; never raises.
|
|
111
|
-
def reindex_for(cwd:, plastic_home:)
|
|
112
|
-
lambda do
|
|
113
|
-
begin
|
|
114
|
-
cols = QmdSync.collections_for_cwd(cwd, plastic_home: plastic_home)
|
|
115
|
-
cols.each { |c| QmdSync.reindex_async(collection: c) }
|
|
116
|
-
rescue StandardError
|
|
117
|
-
# non-fatal; the read is already allowed this turn
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
# --- executable entrypoint ---
|
|
124
|
-
if $PROGRAM_NAME == __FILE__
|
|
125
|
-
raw = begin
|
|
126
|
-
$stdin.read
|
|
127
|
-
rescue StandardError
|
|
128
|
-
""
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
plastic_home = (ARGV[0] && !ARGV[0].empty?) ? ARGV[0] : File.expand_path("~/.plastic")
|
|
132
|
-
cwd = Dir.pwd
|
|
133
|
-
|
|
134
|
-
code, err, out = begin
|
|
135
|
-
caps = RetrievalGateHook.detect_capabilities(cwd: cwd)
|
|
136
|
-
RetrievalGateHook.run(
|
|
137
|
-
stdin: raw, plastic_home: plastic_home, cwd: cwd,
|
|
138
|
-
capabilities: caps,
|
|
139
|
-
reindex: RetrievalGateHook.reindex_for(cwd: cwd, plastic_home: plastic_home)
|
|
140
|
-
)
|
|
141
|
-
rescue StandardError
|
|
142
|
-
[0, nil, nil] # fail-open at the outermost boundary too
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
$stderr.puts(err) if err && !err.empty?
|
|
146
|
-
print out if out && !out.empty?
|
|
147
|
-
exit code
|
|
148
|
-
end
|