@zalom/plastic 1.7.1 → 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.
Files changed (72) hide show
  1. package/PLASTIC.md +75 -535
  2. package/README.md +3 -2
  3. package/agents/plastic-intent-curator.md +2 -2
  4. package/deprecations.yml +10 -2
  5. package/hooks/{links-gate → edit-gates} +1 -1
  6. package/hooks/hooks.json +4 -44
  7. package/hooks/power-tools +8 -0
  8. package/package.json +1 -2
  9. package/scripts/codex-hook +17 -22
  10. package/scripts/doctor.rb +688 -186
  11. package/scripts/end-intent +65 -22
  12. package/scripts/hook-code-gate +13 -28
  13. package/scripts/hook-create-gate +10 -57
  14. package/scripts/hook-edit-gates +58 -0
  15. package/scripts/hook-links-gate +8 -31
  16. package/scripts/hook-lock-gate +13 -58
  17. package/scripts/hook-power-tools +38 -0
  18. package/scripts/hook-savepoint-pre +8 -11
  19. package/scripts/lib/bridge.rb +2 -2
  20. package/scripts/lib/edit_gates.rb +342 -0
  21. package/scripts/lib/hook_registry.rb +61 -29
  22. package/scripts/lib/installer_core.rb +100 -18
  23. package/scripts/lib/outcome_guard.rb +38 -0
  24. package/scripts/lib/qmd_hook.rb +21 -41
  25. package/scripts/lib/qmd_sync.rb +0 -15
  26. package/scripts/lib/revisions_writer.rb +1 -1
  27. package/scripts/maintenance-run +56 -2
  28. package/scripts/restore-intent-v1 +16 -11
  29. package/skills/auto/SKILL.md +15 -4
  30. package/skills/auto/evals/evals.json +2 -2
  31. package/skills/conventions/SKILL.md +31 -0
  32. package/skills/conventions/references/completion-and-done.md +40 -0
  33. package/skills/conventions/references/gates-and-enforcement.md +23 -0
  34. package/skills/conventions/references/knowledge-graph.md +47 -0
  35. package/skills/conventions/references/lifecycle-and-savepoints.md +11 -0
  36. package/skills/conventions/references/locks-and-worktrees.md +113 -0
  37. package/skills/conventions/references/maintenance-and-revisions.md +170 -0
  38. package/skills/conventions/references/roadmaps.md +44 -0
  39. package/skills/conventions/references/tiers-and-dispatch.md +134 -0
  40. package/skills/doctor/SKILL.md +59 -19
  41. package/skills/intent-brainstorming/SKILL.md +4 -0
  42. package/skills/intent-continuing/SKILL.md +4 -0
  43. package/skills/intent-creating/SKILL.md +7 -2
  44. package/skills/intent-ending/SKILL.md +20 -6
  45. package/skills/intent-executing/SKILL.md +10 -0
  46. package/skills/intent-linking/SKILL.md +4 -0
  47. package/skills/intent-locking/SKILL.md +4 -0
  48. package/skills/intent-planning/SKILL.md +7 -0
  49. package/skills/intent-savepoint/SKILL.md +4 -0
  50. package/skills/intent-speccing/SKILL.md +4 -0
  51. package/skills/intent-starting/SKILL.md +10 -0
  52. package/skills/project-creating/references/project-scaffolding.md +2 -2
  53. package/skills/roadmap/SKILL.md +4 -0
  54. package/skills/roadmap-continuing/SKILL.md +4 -0
  55. package/skills/skill-creating/SKILL.md +3 -0
  56. package/skills/skill-creating/references/defaults-first.md +23 -0
  57. package/skills/skill-creating/references/hooks.md +4 -1
  58. package/skills/store-curating/SKILL.md +8 -0
  59. package/skills/store-indexing/SKILL.md +8 -0
  60. package/skills/tutorial/references/track-2-auto.md +2 -3
  61. package/templates/agents.md +8 -0
  62. package/PLASTIC-reference.md +0 -138
  63. package/hooks/code-gate +0 -27
  64. package/hooks/create-gate +0 -3
  65. package/hooks/lock-gate +0 -21
  66. package/hooks/qmd-search +0 -8
  67. package/hooks/retrieval-gate +0 -10
  68. package/hooks/savepoint-pre +0 -10
  69. package/scripts/hook-qmd-search +0 -44
  70. package/scripts/hook-retrieval-gate +0 -148
  71. package/scripts/lib/legacy_bookend_amnesty.rb +0 -35
  72. package/scripts/lib/retrieval_gate.rb +0 -211
package/README.md CHANGED
@@ -183,8 +183,9 @@ behind releases are part of the repository, not a hidden process.
183
183
 
184
184
  ## Conventions
185
185
 
186
- Plastic conventions live in `PLASTIC.md`, distributed to `~/.plastic/PLASTIC.md`
187
- and overwritten on every update. Project-specific rules live in `AGENTS.md`.
186
+ The always-on core conventions live in `PLASTIC.md`, distributed to `~/.plastic/PLASTIC.md`
187
+ and overwritten on every update. Deeper doctrine lives in the `plastic-conventions` skill,
188
+ loaded on demand by the skills that need it. Project-specific rules live in `AGENTS.md`.
188
189
  Run `plastic-doctor` to check installation health; it compares files against
189
190
  the manifests, checks store state, and runs automatically after every update.
190
191
 
@@ -27,9 +27,9 @@ You are the Plastic Intent Curator. Your role is to maintain the health and navi
27
27
  5. On a terminal-state transition (Completed OR Abandoned), do these things:
28
28
  a. 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).
29
29
  b. Call `plastic-intent-ending` for the terminal-transition close (INDEX move, savepoint `Done` bookend, store commit, disarm, and the QMD reindex last): `ruby ~/.plastic/scripts/end-intent --store <store> --id <id> --disposition delivered|abandoned`, then follow that skill's own disarm and reindex steps. Never restate the INDEX/savepoint/reindex one-liners here.
30
- 6. Structural maintenance is move-and-record, and it is NEVER done without its receipt: remove the misplaced section, file, or ref from its artifact, then create or append `revisions.md` in that intent directory (copy the FORM from `~/.plastic/templates/revisions.md`) IN THE SAME PASS as the edit. If you cannot write `revisions.md` for any reason (permissions, a read-only path), you MUST NOT make the structural edit either - report the blocker instead of leaving an unrecorded change (this mirrors the tool-side rule: project-links, rebuild-graph, and restore-intent-v1 refuse rather than write a change with no receipt; you hold yourself to the same rule by hand). One entry per relocated item, newest at the bottom: a `## Revision vN - YYYY-MM-DD-HH:MM` header, a one-sentence `Why` ending with `[rule: <tag>]`, `Prior location`, and either `Content held` (verbatim) or a one-line `Change` for a frontmatter edit. For a stray file, embed its full content and delete the original. The violation-tag catalog is canonical in PLASTIC.md. A graph edit must move TOWARD ground truth (drop a dangling/false edge, add a reciprocity-forced or documented-real one) and must NEVER invent a relationship - "might be related" is never a valid `[rule:]` reason (PLASTIC.md > WORK vs MAINTENANCE).
30
+ 6. Structural maintenance is move-and-record, and it is NEVER done without its receipt: remove the misplaced section, file, or ref from its artifact, then create or append `revisions.md` in that intent directory (copy the FORM from `~/.plastic/templates/revisions.md`) IN THE SAME PASS as the edit. If you cannot write `revisions.md` for any reason (permissions, a read-only path), you MUST NOT make the structural edit either - report the blocker instead of leaving an unrecorded change (this mirrors the tool-side rule: project-links, rebuild-graph, and restore-intent-v1 refuse rather than write a change with no receipt; you hold yourself to the same rule by hand). One entry per relocated item, newest at the bottom: a `## Revision vN - YYYY-MM-DD-HH:MM` header, a one-sentence `Why` ending with `[rule: <tag>]`, `Prior location`, and either `Content held` (verbatim) or a one-line `Change` for a frontmatter edit. For a stray file, embed its full content and delete the original. The violation-tag catalog is canonical in `plastic-conventions > references/maintenance-and-revisions.md`. A graph edit must move TOWARD ground truth (drop a dangling/false edge, add a reciprocity-forced or documented-real one) and must NEVER invent a relationship - "might be related" is never a valid `[rule:]` reason (`plastic-conventions > references/maintenance-and-revisions.md`, WORK vs MAINTENANCE).
31
31
  7. Before performing structural maintenance on ANY intent that is NOT the one your own session is currently delivering under its own held delivery lock, you must:
32
- a. Check the target's lock freshness: `ruby ~/.plastic/scripts/plastic-lock status --intent-dir <target-intent-dir>` and read the `lock_fresh` field of its JSON output. If `true`, DEFER: make no edit to that intent, and report it as skipped (an active delivery is in progress). This is DETECT-ONLY - you never acquire, create, or hold any lock of your own for maintenance (PLASTIC.md > WORK vs MAINTENANCE; there is exactly one lock in Plastic, the delivery lock).
32
+ a. Check the target's lock freshness: `ruby ~/.plastic/scripts/plastic-lock status --intent-dir <target-intent-dir>` and read the `lock_fresh` field of its JSON output. If `true`, DEFER: make no edit to that intent, and report it as skipped (an active delivery is in progress). This is DETECT-ONLY - you never acquire, create, or hold any lock of your own for maintenance (`plastic-conventions > references/maintenance-and-revisions.md`, WORK vs MAINTENANCE; there is exactly one lock in Plastic, the delivery lock).
33
33
  b. Require a clean store working tree before starting: `git -C ~/.plastic status --porcelain` (or the project store's own root, if not global) must be empty. If it is not, STOP and report the dirty paths rather than risk sweeping an unrelated concurrent change into your own commit; do not proceed until the tree is clean.
34
34
  c. Create a fresh branch from the current tip of that repo's main: `git -C <repo-root> checkout -b maintenance/curator-<UTC-timestamp> main`.
35
35
  d. Make the scoped edit plus its `revisions.md` receipt (step 6 above), touching nothing else.
package/deprecations.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # Plastic deprecation notices
2
2
  # Each entry is shown to the user during SessionStart.
3
- # See PLASTIC.md "Deprecation Process" for the full convention.
3
+ # See the plastic-releasing skill for the full convention.
4
4
  #
5
5
  # Schema:
6
6
  # id: unique-slug (used for dismissal tracking)
@@ -11,4 +11,12 @@
11
11
  # removal: semver where feature is removed
12
12
  # link: optional URL to migration guide
13
13
 
14
- deprecations: []
14
+ deprecations:
15
+ - id: plastic-reference-md-absorbed
16
+ severity: info
17
+ summary: "PLASTIC-reference.md has been absorbed into the plastic-conventions skill and removed; its content now lives in that skill's reference chapters."
18
+ migration_steps:
19
+ - "No action is required. Running plastic update removes the leftover ~/.plastic/PLASTIC-reference.md automatically."
20
+ - "If you hand-copied or hand-edited that file, delete your own copy; it is no longer read by Plastic."
21
+ introduced: "1.9.0"
22
+ removal: "1.11.0"
@@ -1,3 +1,3 @@
1
1
  #!/bin/bash
2
2
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
3
- exec ruby "$SCRIPT_DIR/../scripts/hook-links-gate"
3
+ exec ruby "$SCRIPT_DIR/../scripts/hook-edit-gates"
package/hooks/hooks.json CHANGED
@@ -35,38 +35,8 @@
35
35
  "hooks": [
36
36
  {
37
37
  "type": "command",
38
- "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" code-gate",
39
- "statusMessage": "Checking lifecycle gate..."
40
- },
41
- {
42
- "type": "command",
43
- "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" lock-gate",
44
- "statusMessage": "Checking lock gate..."
45
- }
46
- ]
47
- },
48
- {
49
- "matcher": "Write|Edit",
50
- "hooks": [
51
- {
52
- "type": "command",
53
- "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" savepoint-pre",
54
- "statusMessage": "Recording stage start..."
55
- },
56
- {
57
- "type": "command",
58
- "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" links-gate",
59
- "statusMessage": "Checking Links gate..."
60
- }
61
- ]
62
- },
63
- {
64
- "matcher": "Write|Edit|mcp__serena__replace_content|mcp__serena__replace_symbol_body|mcp__serena__insert_after_symbol|mcp__serena__insert_before_symbol|mcp__serena__safe_delete_symbol|mcp__serena__rename_symbol",
65
- "hooks": [
66
- {
67
- "type": "command",
68
- "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" create-gate",
69
- "statusMessage": "Checking create gate..."
38
+ "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" edit-gates",
39
+ "statusMessage": "Checking Plastic gates..."
70
40
  }
71
41
  ]
72
42
  },
@@ -79,16 +49,6 @@
79
49
  "statusMessage": "Checking lifecycle gate..."
80
50
  }
81
51
  ]
82
- },
83
- {
84
- "matcher": "Bash|Read|Grep|Glob",
85
- "hooks": [
86
- {
87
- "type": "command",
88
- "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" retrieval-gate",
89
- "statusMessage": "Checking retrieval gate..."
90
- }
91
- ]
92
52
  }
93
53
  ],
94
54
  "PostToolUse": [
@@ -139,8 +99,8 @@
139
99
  "hooks": [
140
100
  {
141
101
  "type": "command",
142
- "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" qmd-search",
143
- "statusMessage": "Searching QMD..."
102
+ "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook\" power-tools",
103
+ "statusMessage": "Checking power tools..."
144
104
  }
145
105
  ]
146
106
  }
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+ # power-tools mandate hook launcher (intents 66, 66b, 187, 246). 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-power-tools"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalom/plastic",
3
- "version": "1.7.1",
3
+ "version": "1.9.0",
4
4
  "description": "Intent-driven idea development system for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,7 +38,6 @@
38
38
  "agents/",
39
39
  "templates/",
40
40
  "PLASTIC.md",
41
- "PLASTIC-reference.md",
42
41
  "deprecations.yml"
43
42
  ]
44
43
  }
@@ -5,8 +5,8 @@
5
5
  # Usage: codex-hook <gate>
6
6
  # file-mutation gates: code-gate | lock-gate | savepoint-pre | gate-check | create-gate | links-gate
7
7
  # live-state hooks (intent 199): session-start | check-update | continue |
8
- # future-intent-check | auto-arm | qmd-search | savepoint
9
- # shell-tool gates (intent 203): bash-gate | retrieval-gate
8
+ # future-intent-check | auto-arm | power-tools | savepoint
9
+ # shell-tool gate (intent 203): bash-gate
10
10
  #
11
11
  # The Codex input adapter (intent 102, extended by 199 and 203). Reads a Codex
12
12
  # hook stdin payload once. Three shapes exist and the gate name alone selects
@@ -20,17 +20,17 @@
20
20
  # that hook (hooks/<name>), which is already harness-agnostic (resolves
21
21
  # ~/.plastic off $HOME, reads only the common stdin fields the guide confirms
22
22
  # Codex shares with Claude for these three events), so the body is reused, not
23
- # rewritten (D3). The shell-tool gates (bash-gate, retrieval-gate) report
24
- # tool_name: "Bash" with the command in tool_input.command, but MUST NOT be
25
- # routed through ApplyPatchEnvelope.parse below: a plain shell command has no
23
+ # rewritten (D3). The shell-tool gate (bash-gate) reports tool_name: "Bash" with
24
+ # the command in tool_input.command, but MUST NOT be routed through
25
+ # ApplyPatchEnvelope.parse below: a plain shell command has no
26
26
  # *** Begin Patch/*** End Patch envelope, so it would parse to an empty op list
27
27
  # and hit this file's own `exit 0 if ops.empty?` line, silently allowing every
28
- # Bash call and reopening the exact hole intent 203 closes. So these two exec
29
- # the SAME scripts/hook-bash-gate / scripts/hook-retrieval-gate files Claude
30
- # already runs, unmodified, the identical "drive the body, relay its output"
31
- # pattern used for the live-state hooks. The one adaptation shared by both the
32
- # live-state and shell-tool paths is threading the payload's session_id into
33
- # CLAUDE_CODE_SESSION_ID, since Codex's own process env never carries it, plus
28
+ # Bash call and reopening the exact hole intent 203 closes. So it execs the SAME
29
+ # scripts/hook-bash-gate file Claude already runs, unmodified, the identical
30
+ # "drive the body, relay its output" pattern used for the live-state hooks. The
31
+ # one adaptation shared by both the live-state and shell-tool paths is threading
32
+ # the payload's session_id into CLAUDE_CODE_SESSION_ID, since Codex's own
33
+ # process env never carries it, plus
34
34
  # a bounded timeout: hooks/check-update backgrounds a real npm network call
35
35
  # without redirecting its output away from the inherited stdout/stderr pipes,
36
36
  # and Codex invokes hooks synchronously, so without a bound a slow network call
@@ -50,9 +50,9 @@ require_relative "lib/links_gate"
50
50
  # caller that merges the dispatcher's own stdout and stderr.
51
51
  $stdout.sync = true
52
52
 
53
- STATE_HOOKS = %w[session-start check-update continue future-intent-check auto-arm qmd-search savepoint].freeze
53
+ STATE_HOOKS = %w[session-start check-update continue future-intent-check auto-arm power-tools savepoint].freeze
54
54
  STATE_TIMEOUT = 5
55
- SHELL_HOOKS = %w[bash-gate retrieval-gate].freeze
55
+ SHELL_HOOKS = %w[bash-gate].freeze
56
56
 
57
57
  gate = ARGV[0].to_s
58
58
  raw = ($stdin.read rescue nil)
@@ -78,20 +78,15 @@ if STATE_HOOKS.include?(gate)
78
78
  end
79
79
 
80
80
  if SHELL_HOOKS.include?(gate)
81
- # bash-gate / retrieval-gate (intent 203): a shell command has no apply_patch
82
- # diff envelope, so this branch execs the SAME scripts/hook-bash-gate /
83
- # scripts/hook-retrieval-gate files Claude already runs, unmodified, and
84
- # relays their exit code and stderr. It must never fall through to
85
- # ApplyPatchEnvelope.parse below (see header comment).
81
+ # bash-gate (intent 203): a shell command has no apply_patch diff envelope, so
82
+ # this branch execs the SAME scripts/hook-bash-gate file Claude already runs,
83
+ # unmodified, and relays its exit code and stderr. It must never fall through
84
+ # to ApplyPatchEnvelope.parse below (see header comment).
86
85
  cwd = payload["cwd"].to_s
87
86
  cwd = Dir.pwd if cwd.empty? || !Dir.exist?(cwd)
88
87
  env = { "CLAUDE_CODE_SESSION_ID" => (session.empty? ? nil : session) }
89
88
  script = File.join(__dir__, "hook-#{gate}")
90
89
  argv = [script]
91
- # hook-retrieval-gate reads plastic_home off ARGV[0] (mirrors hooks/retrieval-gate's
92
- # own launcher, which passes "$HOME/.plastic"); hook-bash-gate takes no args, it
93
- # resolves Dir.home directly.
94
- argv << ENV.fetch("PLASTIC_HOME") { File.join(Dir.home, ".plastic") } if gate == "retrieval-gate"
95
90
  out, err, status = begin
96
91
  Timeout.timeout(STATE_TIMEOUT) { Open3.capture3(env, RbConfig.ruby, *argv, stdin_data: raw, chdir: cwd) }
97
92
  rescue StandardError