@zalom/plastic 1.0.0-beta.9 → 1.0.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 (107) hide show
  1. package/PLASTIC-reference.md +133 -0
  2. package/PLASTIC.md +398 -82
  3. package/README.md +99 -82
  4. package/agents/plastic-brainstorming.md +12 -9
  5. package/agents/plastic-enforcer.md +52 -12
  6. package/agents/plastic-executor.md +10 -10
  7. package/agents/plastic-future-intent-researcher.md +4 -8
  8. package/agents/plastic-intent-curator.md +12 -14
  9. package/agents/plastic-intent-discovery.md +35 -0
  10. package/agents/plastic-planner.md +15 -10
  11. package/agents/plastic-spec-specialist.md +15 -10
  12. package/bin/plastic.js +7 -3
  13. package/hooks/code-gate +10 -1
  14. package/hooks/hooks.json +28 -3
  15. package/hooks/lock-gate +21 -0
  16. package/hooks/retrieval-gate +10 -0
  17. package/hooks/savepoint-pre +10 -0
  18. package/hooks/statusline +48 -6
  19. package/package.json +2 -1
  20. package/scripts/agent-report +21 -0
  21. package/scripts/dashboard.rb +98 -23
  22. package/scripts/doctor.rb +232 -5
  23. package/scripts/hook-auto-arm +1 -1
  24. package/scripts/hook-bash-gate +22 -4
  25. package/scripts/hook-code-gate +11 -6
  26. package/scripts/hook-continue +17 -0
  27. package/scripts/hook-create-gate +45 -9
  28. package/scripts/hook-gate-check +24 -23
  29. package/scripts/hook-lock-gate +83 -0
  30. package/scripts/hook-retrieval-gate +148 -0
  31. package/scripts/hook-savepoint-pre +32 -0
  32. package/scripts/hook-session-start +1 -1
  33. package/scripts/insight-append +51 -0
  34. package/scripts/install.rb +44 -7
  35. package/scripts/lib/agent_models.rb +43 -0
  36. package/scripts/lib/bridge.rb +924 -84
  37. package/scripts/lib/dashboard_banner.rb +42 -0
  38. package/scripts/lib/hook_registry.rb +95 -0
  39. package/scripts/lib/insights.rb +86 -0
  40. package/scripts/lib/installer_core.rb +100 -60
  41. package/scripts/lib/link_suggestions.rb +319 -0
  42. package/scripts/lib/lock.rb +375 -0
  43. package/scripts/lib/power_tools.rb +22 -21
  44. package/scripts/lib/preflight.rb +79 -0
  45. package/scripts/lib/qmd_sync.rb +15 -0
  46. package/scripts/lib/retrieval_gate.rb +211 -0
  47. package/scripts/lib/worktree.rb +384 -0
  48. package/scripts/link-suggest +213 -0
  49. package/scripts/new-intent +9 -1
  50. package/scripts/plastic-lock +164 -0
  51. package/scripts/read-config +4 -0
  52. package/scripts/spawn-preamble +11 -3
  53. package/scripts/update.rb +16 -7
  54. package/skills/auto/SKILL.md +126 -19
  55. package/skills/auto/references/agent-architecture.md +7 -4
  56. package/skills/auto/references/agent-report-contract.md +36 -1
  57. package/skills/auto/references/end-tail.md +56 -0
  58. package/skills/auto/references/human-report-contract.md +55 -0
  59. package/skills/auto/references/tiers.md +77 -0
  60. package/skills/brainstorming/SKILL.md +7 -34
  61. package/skills/brainstorming/references/design-principles.md +49 -0
  62. package/skills/continuing/SKILL.md +26 -7
  63. package/skills/creating-intent/SKILL.md +13 -28
  64. package/skills/creating-project/SKILL.md +11 -74
  65. package/skills/creating-project/references/project-scaffolding.md +97 -0
  66. package/skills/creating-skills/SKILL.md +65 -0
  67. package/skills/creating-skills/evals/evals.json +108 -0
  68. package/skills/creating-skills/references/agents.md +168 -0
  69. package/skills/creating-skills/references/evals.md +41 -0
  70. package/skills/creating-skills/references/hooks.md +248 -0
  71. package/skills/creating-skills/references/progressive-disclosure.md +176 -0
  72. package/skills/creating-skills/references/scripts.md +166 -0
  73. package/skills/creating-skills/references/skills.md +165 -0
  74. package/skills/creating-skills/scripts/scaffold.rb +313 -0
  75. package/skills/dashboard/SKILL.md +13 -11
  76. package/skills/dashboard/references/classification.md +22 -0
  77. package/skills/doctor/SKILL.md +10 -7
  78. package/skills/executing-plan/SKILL.md +4 -4
  79. package/skills/humanizer/SKILL.md +39 -0
  80. package/skills/humanizer/evals/evals.json +70 -0
  81. package/skills/humanizer/references/always-on-snippet.md +9 -0
  82. package/skills/humanizer/references/examples.md +48 -0
  83. package/skills/install/SKILL.md +75 -84
  84. package/skills/intent-curator/SKILL.md +5 -1
  85. package/skills/intent-discovery/SKILL.md +47 -0
  86. package/skills/intent-starting/SKILL.md +130 -0
  87. package/skills/intent-starting/evals/evals.json +117 -0
  88. package/skills/intent-starting/references/boarding-matrix.md +35 -0
  89. package/skills/linking-intents/SKILL.md +48 -12
  90. package/skills/lock/SKILL.md +41 -0
  91. package/skills/managing-index/SKILL.md +6 -0
  92. package/skills/releasing/SKILL.md +44 -29
  93. package/skills/releasing/references/promotion-and-tagging.md +60 -0
  94. package/skills/roadmap/SKILL.md +51 -0
  95. package/skills/roadmap/references/file-format.md +91 -0
  96. package/skills/roadmap/references/operations.md +78 -0
  97. package/skills/uninstall/SKILL.md +29 -11
  98. package/skills/update/SKILL.md +34 -23
  99. package/skills/versions/SKILL.md +27 -12
  100. package/skills/writing-plans/SKILL.md +10 -88
  101. package/skills/writing-plans/references/plan-format.md +102 -0
  102. package/templates/config.yml +8 -0
  103. package/templates/outcome.md +3 -0
  104. package/templates/revisions.md +58 -0
  105. package/templates/roadmap.md +30 -0
  106. package/skills/writing-instructions/SKILL.md +0 -159
  107. package/skills/writing-instructions/references/agentskills-spec.md +0 -135
@@ -0,0 +1,313 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # scaffold.rb - born-slim starting files for a new skill, agent, or hook.
5
+ #
6
+ # Plastic skills, agents, and hooks start small and grow by progressive
7
+ # disclosure. This scaffolder emits the minimum valid skeleton so the body
8
+ # stays slim and detail moves into references/ as the work earns it.
9
+ #
10
+ # Usage:
11
+ # ruby scaffold.rb skill <name> [--out <dir>] [dest]
12
+ # ruby scaffold.rb agent <name> [--out <dir>] [dest]
13
+ # ruby scaffold.rb hook [<Event>] <name> [--out <dir>] [dest]
14
+ # ruby scaffold.rb --help
15
+ #
16
+ # All input is positional plus flags. There are no interactive prompts, so an
17
+ # agent never hangs waiting on input. The destination defaults to the current
18
+ # directory; pass --out or a trailing positional to choose another directory.
19
+ #
20
+ # Exit codes:
21
+ # 0 success, or --help
22
+ # 1 usage error (unknown subcommand, missing argument)
23
+ # 2 validation error (bad name or event)
24
+ # 3 refused to overwrite an existing target, or a filesystem error
25
+ #
26
+ # This script and every file it emits contain no em-dashes; the emitted files
27
+ # are user-facing.
28
+
29
+ require "json"
30
+ require "fileutils"
31
+
32
+ EXIT_OK = 0
33
+ EXIT_USAGE = 1
34
+ EXIT_VALIDATION = 2
35
+ EXIT_CONFLICT = 3
36
+
37
+ NAME_PATTERN = /\A[a-z0-9]+(-[a-z0-9]+)*\z/.freeze
38
+ EVENT_PATTERN = /\A[A-Z][A-Za-z]+\z/.freeze
39
+ DEFAULT_EVENT = "PostToolUse"
40
+
41
+ USAGE = <<~TEXT
42
+ scaffold.rb - born-slim starting files for a new skill, agent, or hook.
43
+
44
+ Usage:
45
+ ruby scaffold.rb skill <name> [--out <dir>] [dest]
46
+ ruby scaffold.rb agent <name> [--out <dir>] [dest]
47
+ ruby scaffold.rb hook [<Event>] <name> [--out <dir>] [dest]
48
+ ruby scaffold.rb --help
49
+
50
+ Subcommands:
51
+ skill Emit <dest>/<name>/SKILL.md, references/.gitkeep, evals/evals.json.
52
+ agent Emit <dest>/<name>.md agent role file.
53
+ hook Emit <dest>/<name> no-op hook handler (Ruby) wired for <Event>.
54
+
55
+ Names must be lowercase alphanumeric and hyphens, with no leading,
56
+ trailing, or repeated hyphens, 1 to 64 characters.
57
+ The hook <Event> is optional and defaults to #{DEFAULT_EVENT}.
58
+ The destination defaults to the current directory.
59
+
60
+ Exit codes:
61
+ 0 success, or --help
62
+ 1 usage error (unknown subcommand, missing argument)
63
+ 2 validation error (bad name or event)
64
+ 3 refused to overwrite an existing target, or a filesystem error
65
+ TEXT
66
+
67
+ # A small error that carries the exit code to use when it reaches the top.
68
+ class ScaffoldError < StandardError
69
+ attr_reader :code
70
+
71
+ def initialize(message, code)
72
+ super(message)
73
+ @code = code
74
+ end
75
+ end
76
+
77
+ def fail_with(message, code)
78
+ raise ScaffoldError.new(message, code)
79
+ end
80
+
81
+ def validate_name!(name)
82
+ if name.nil? || name.empty?
83
+ fail_with("missing <name>. See --help for usage.", EXIT_USAGE)
84
+ end
85
+ unless name.length <= 64 && name =~ NAME_PATTERN
86
+ fail_with(
87
+ "invalid name #{name.inspect}: use lowercase letters, digits, and " \
88
+ "hyphens, no leading, trailing, or repeated hyphens, 1 to 64 chars.",
89
+ EXIT_VALIDATION
90
+ )
91
+ end
92
+ name
93
+ end
94
+
95
+ def validate_event!(event)
96
+ unless event =~ EVENT_PATTERN
97
+ fail_with(
98
+ "invalid event #{event.inspect}: use a CamelCase hook event name, " \
99
+ "for example PostToolUse or SessionStart.",
100
+ EXIT_VALIDATION
101
+ )
102
+ end
103
+ event
104
+ end
105
+
106
+ # Separate --out and any trailing positional dest from the bare positionals.
107
+ # Returns [positionals, out_dir]. out_dir is nil when not given.
108
+ def parse_args(args)
109
+ positionals = []
110
+ out_dir = nil
111
+ i = 0
112
+ while i < args.length
113
+ arg = args[i]
114
+ case arg
115
+ when "--out"
116
+ out_dir = args[i + 1]
117
+ if out_dir.nil?
118
+ fail_with("--out needs a directory argument.", EXIT_USAGE)
119
+ end
120
+ i += 2
121
+ else
122
+ if arg.start_with?("--")
123
+ fail_with("unknown option #{arg.inspect}. See --help for usage.", EXIT_USAGE)
124
+ end
125
+ positionals << arg
126
+ i += 1
127
+ end
128
+ end
129
+ [positionals, out_dir]
130
+ end
131
+
132
+ def refuse_if_exists!(path)
133
+ if File.exist?(path)
134
+ fail_with("refusing to overwrite existing #{path}. Remove it or pick another destination.", EXIT_CONFLICT)
135
+ end
136
+ end
137
+
138
+ def write_file(path, content)
139
+ refuse_if_exists!(path)
140
+ FileUtils.mkdir_p(File.dirname(path))
141
+ File.write(path, content)
142
+ puts "created #{path}"
143
+ end
144
+
145
+ # -- generators ----------------------------------------------------------
146
+
147
+ def skill_body(name)
148
+ <<~MD
149
+ ---
150
+ name: #{name}
151
+ description: >
152
+ Use when the user needs #{name}. State here WHEN this skill should
153
+ trigger, in the third person, so the agent can match it. Replace this
154
+ placeholder with one or two concrete trigger conditions before shipping.
155
+ ---
156
+
157
+ # #{name}
158
+
159
+ One line on what this skill does and why it exists.
160
+
161
+ ## Gotchas
162
+
163
+ - List the non-obvious failure modes here, one per line.
164
+
165
+ ## Tasks
166
+
167
+ Keep the body slim. Route detail to references as the work earns it.
168
+
169
+ | Task | Reference |
170
+ | ---- | --------- |
171
+ | Replace this row with a real task | references/REPLACE-ME.md |
172
+ MD
173
+ end
174
+
175
+ def eval_stub(name)
176
+ data = {
177
+ "skill_name" => name,
178
+ "evals" => [
179
+ {
180
+ "id" => 1,
181
+ "prompt" => "",
182
+ "expected_output" => "",
183
+ "files" => [],
184
+ "assertions" => []
185
+ }
186
+ ]
187
+ }
188
+ JSON.pretty_generate(data) + "\n"
189
+ end
190
+
191
+ def generate_skill(name, out_dir)
192
+ base = File.join(out_dir, name)
193
+ refuse_if_exists!(base)
194
+ write_file(File.join(base, "SKILL.md"), skill_body(name))
195
+ write_file(File.join(base, "references", ".gitkeep"), "")
196
+ write_file(File.join(base, "evals", "evals.json"), eval_stub(name))
197
+ puts "skill scaffold ready at #{base}"
198
+ end
199
+
200
+ def agent_body(name)
201
+ <<~MD
202
+ ---
203
+ name: #{name}
204
+ description: >
205
+ Use this agent when the user needs #{name}. State here WHEN to delegate
206
+ to this agent, in the third person, so the orchestrator can route to it.
207
+ Replace this placeholder with concrete delegation conditions.
208
+ tools: Read, Edit, Bash
209
+ ---
210
+
211
+ You are the #{name} agent.
212
+
213
+ ## Responsibilities
214
+
215
+ - One line per durable responsibility this agent owns.
216
+
217
+ ## How you work
218
+
219
+ Keep this body slim. State the contract, the inputs, and the outputs.
220
+ Move long procedures into a references file as the work earns it.
221
+
222
+ ## Completion
223
+
224
+ End your turn with a short report of what you did and how you verified it.
225
+ MD
226
+ end
227
+
228
+ def generate_agent(name, out_dir)
229
+ write_file(File.join(out_dir, "#{name}.md"), agent_body(name))
230
+ puts "agent scaffold ready at #{File.join(out_dir, "#{name}.md")}"
231
+ end
232
+
233
+ def hook_body(name, event)
234
+ <<~RUBY
235
+ #!/usr/bin/env ruby
236
+ # frozen_string_literal: true
237
+
238
+ # #{name} - #{event} hook handler (no-op by default).
239
+ #
240
+ # This skeleton does nothing until you opt in. Set the environment flag
241
+ # below to a non-empty value to activate the real behavior. Until then it
242
+ # exits 0 so it never blocks the session.
243
+ #
244
+ # Wire it in settings.json under hooks.#{event}:
245
+ # { "type": "command", "command": "ruby /absolute/path/to/#{name}" }
246
+ #
247
+ # Exit codes:
248
+ # 0 no-op, or success
249
+ # 2 block the action (only when you add real logic that should block)
250
+
251
+ OPT_IN = "#{name.tr("-", "_").upcase}_ENABLED"
252
+
253
+ if ENV[OPT_IN].nil? || ENV[OPT_IN].empty?
254
+ exit 0
255
+ end
256
+
257
+ # Opt-in is set. Add the real handler here. The hook receives event JSON on
258
+ # stdin; parse it only when you need it. Exit 0 to allow, exit 2 to block.
259
+ exit 0
260
+ RUBY
261
+ end
262
+
263
+ def generate_hook(name, event, out_dir)
264
+ path = File.join(out_dir, name)
265
+ write_file(path, hook_body(name, event))
266
+ FileUtils.chmod("+x", path)
267
+ puts "hook scaffold ready at #{path} (executable, #{event}, no-op until opt-in)"
268
+ end
269
+
270
+ # -- dispatch ------------------------------------------------------------
271
+
272
+ def run(argv)
273
+ if argv.empty? || argv.include?("--help") || argv.include?("-h")
274
+ puts USAGE
275
+ return EXIT_OK
276
+ end
277
+
278
+ subcommand = argv.shift
279
+ positionals, out_flag = parse_args(argv)
280
+
281
+ case subcommand
282
+ when "skill", "agent"
283
+ name = validate_name!(positionals[0])
284
+ out_dir = out_flag || positionals[1] || "."
285
+ subcommand == "skill" ? generate_skill(name, out_dir) : generate_agent(name, out_dir)
286
+ when "hook"
287
+ # Accept "hook <name>" or "hook <Event> <name>", with an optional trailing dest.
288
+ if positionals.length >= 2 && positionals[0] =~ EVENT_PATTERN
289
+ event = validate_event!(positionals[0])
290
+ name = validate_name!(positionals[1])
291
+ out_dir = out_flag || positionals[2] || "."
292
+ else
293
+ event = DEFAULT_EVENT
294
+ name = validate_name!(positionals[0])
295
+ out_dir = out_flag || positionals[1] || "."
296
+ end
297
+ generate_hook(name, event, out_dir)
298
+ else
299
+ fail_with("unknown subcommand #{subcommand.inspect}. See --help for usage.", EXIT_USAGE)
300
+ end
301
+
302
+ EXIT_OK
303
+ end
304
+
305
+ begin
306
+ exit run(ARGV.dup)
307
+ rescue ScaffoldError => e
308
+ warn "scaffold.rb: #{e.message}"
309
+ exit e.code
310
+ rescue Errno::EACCES, Errno::ENOENT, Errno::EEXIST => e
311
+ warn "scaffold.rb: filesystem error: #{e.message}"
312
+ exit EXIT_CONFLICT
313
+ end
@@ -61,9 +61,17 @@ Fill mechanically — no rewriting, no re-sorting:
61
61
 
62
62
  ### Step 3 — Present it (mandatory, every invocation)
63
63
 
64
- **Paste the filled Markdown into your reply.** This is non-optional: the board only reaches
65
- the user when it is in the chat reply, not in tool-call stdout. Never describe the board
66
- instead of showing it.
64
+ **Paste the filled Markdown into your reply.** This is non-optional: if the reply does not
65
+ contain the filled Markdown, the user sees nothing tool-call stdout and hook
66
+ `additionalContext` are both invisible to them. Never describe the board instead of showing
67
+ it, and never assume a hook already showed it for you.
68
+
69
+ `hook-continue` also emits a one-line `systemMessage` summary (counts, and the next big thing
70
+ when there is one) as a hook-owned fallback, independent of the agent's reply. Treat that line
71
+ as a floor only, not a substitute for this step: it carries no matrix, no recently-worked
72
+ section, and no entry-flow prompt. Presenting the full board here remains mandatory regardless
73
+ of whether the summary line fired. This stays a soft, agent-followed mechanism — there is no
74
+ stronger enforcement for a full multi-section Markdown document in this harness today.
67
75
 
68
76
  ### Step 4 — Entry flow (the board is the menu)
69
77
 
@@ -102,14 +110,8 @@ a raw terminal. The Markdown board (`--data` + template) is the surface for the
102
110
 
103
111
  ## How classification works (deterministic)
104
112
 
105
- - **Effort** small for `research`/`exploration`/`bugfix`, for already-scoped intents
106
- (plan/checklist exists), or deep refinement branches; big otherwise.
107
- - **Value → high** when any of: explicit `value: high`; a human-authored **root** intent; an
108
- intent with a non-empty `chain`; or an intent that is a `source` of ≥1 other intent. Else low.
109
- - **Flags** — `unblocked` only when a **future** intent has **all** its `sources` done;
110
- `stale` only on future intents past the staleness threshold. Both kept low-noise by design.
111
- - **Override** — a `value: high|low` frontmatter field always wins (pre-stamped data, never
112
- model judgment at render time).
113
+ The script computes Effort/Value/Flags/Override/Caps; the agent never re-derives them.
114
+ To explain or debug a quadrant assignment, read `references/classification.md`.
113
115
 
114
116
  ## Eval
115
117
 
@@ -0,0 +1,22 @@
1
+ # How Classification Works (Deterministic)
2
+
3
+ The script (`dashboard.rb`) computes Effort/Value/Flags/Override/Caps deterministically;
4
+ the agent never re-derives them. Read this to explain or debug a quadrant assignment.
5
+
6
+ - **Effort** — small for `research`/`exploration`/`bugfix`, for already-scoped intents
7
+ (plan/checklist exists), or a **branch id** (folgezettel depth ≥ 2, e.g. `4a`, `12b3`); big
8
+ otherwise. A root id (a bare number) is always depth 1, so it is never demoted by this rule.
9
+ - **Value → high** when any of: explicit `value: high`; a human-authored **root** intent; or
10
+ an intent that is a `source` of ≥1 other intent (it has spawned follow-on work). A purely
11
+ relational `chain` entry alone is **not** a value signal (intent 68) — else low.
12
+ - **Flags** — `unblocked` only when a **future** intent has **all** its `sources` done AND at
13
+ least one source's completion date is strictly later than the intent's own `created` date (a
14
+ genuine wait, not a birth-time default); `in-progress` only when the savepoint ledger shows
15
+ real post-birth activity, not just the creation stamp; `stale` only on future intents past
16
+ the staleness threshold. All three kept low-noise by design.
17
+ - **Override** — a `value: high|low` frontmatter field always wins (pre-stamped data, never
18
+ model judgment at render time).
19
+ - **Caps** — quadrant lists and the project board's `active`/`future` lists are capped at 8
20
+ entries plus a trailing "+N more" line; each entry's text is truncated to 120 characters
21
+ with a trailing ellipsis. Applies to the Markdown board only (the ASCII renderer has its own
22
+ separate `CELL_CAP`).
@@ -3,7 +3,7 @@ name: plastic-doctor
3
3
  description: Use when diagnosing Plastic installation health, after updates, or when something seems broken. Runs checks and reports findings with fix options.
4
4
  ---
5
5
 
6
- # Doctor Plastic Health Check
6
+ # Doctor: Plastic Health Check
7
7
 
8
8
  ## Scopes
9
9
 
@@ -67,9 +67,9 @@ Parse the JSON output from stdout. The script is read-only and never modifies
67
67
  files. Errors go to stderr.
68
68
 
69
69
  Exit codes indicate check results, not script failure:
70
- - `0` all checks passed
71
- - `1` warnings found
72
- - `2` failures found
70
+ - `0`: all checks passed
71
+ - `1`: warnings found
72
+ - `2`: failures found
73
73
 
74
74
  All three exit codes mean the script ran successfully. Do not treat non-zero
75
75
  as an error.
@@ -118,10 +118,13 @@ Use the `fix_hint` value to determine the correct action:
118
118
  | "Remove stale references from INDEX.md" | Edit INDEX.md to remove ghost references |
119
119
  | "Inject the missing required frontmatter field(s)" | Edit the intent's `{ID}--{slug}.md` frontmatter to add the missing key (e.g. `chain: []`) without touching other keys |
120
120
  | "Run: provision-project-store {slug}" | Run `provision-project-store <slug>` (or invoke the `plastic-add-project-store` skill) to create the missing store |
121
- | "Re-run installer" | Run `npx @zalom/plastic@latest --agent` |
121
+ | "Re-run installer" | Run `npx -y @zalom/plastic@<channel> install --agent <agent>` (channel: -alpha->@alpha, -beta->@beta, else @latest) |
122
+ | "Dispatch plastic-intent-curator ... revisions.md ..." | Invoke the `plastic-intent-curator` (or the agent) to relocate the flagged section or ref into the intent's `revisions.md` via move-and-record (one dated, `[rule: <tag>]`-tagged entry per item), per PLASTIC.md > Structural maintenance and revisions.md. For a missing required section, restore or reproject it instead. |
122
123
 
123
124
  For fixes the agent cannot handle automatically, explain what the user needs
124
- to do manually.
125
+ to do manually. The `revisions.md` remedy is curator-applied (a move-and-record
126
+ relocation, not a mechanical edit) and stays human-gated by the Step 4
127
+ Fix / Select / Skip prompt.
125
128
 
126
129
  ### Step 6: Verify
127
130
 
@@ -141,7 +144,7 @@ Show the updated results.
141
144
  When invoked from `plastic-update` (not directly by the user):
142
145
 
143
146
  1. Run the diagnostic script as in Step 1.
144
- 2. If all checks pass: show a single line **"Health check: all clear."**
147
+ 2. If all checks pass, show a single line: **"Health check: all clear."**
145
148
  3. If issues are found: show the full report (Steps 3-6).
146
149
 
147
150
  This keeps the update flow clean when nothing is wrong.
@@ -70,12 +70,12 @@ After all tasks complete, dispatch a final reviewer for the entire implementatio
70
70
  ### Step 4: Update Intent and Complete
71
71
  Capture observations in `## Insights`. When ALL checklist items are checked:
72
72
 
73
- 1. Write `outcome.md` with detailed results in the intent directory, using the `${CLAUDE_PLUGIN_ROOT}/templates/outcome.md` form
73
+ 1. Write `outcome.md` with detailed results in the intent directory, using the `${CLAUDE_PLUGIN_ROOT}/templates/outcome.md` form. Set the frontmatter `disposition: delivered` (the delivered terminal). `outcome.md` is mandatory at every terminal and self-declares its disposition (canonical done-marker in PLASTIC.md).
74
74
  2. Write `## Outcome` summary in the intent file (1-2 sentences)
75
75
  3. Move intent from `## Active` to `## Completed` in INDEX.md (with today's date)
76
76
  4. Update cluster entries to show `_(completed)_`
77
77
  5. Auto-commit: `cd <store-root> && git add . && git commit -m "feat: complete intent <ID> — <name>"`
78
- 6. On completion, ALWAYS refresh the QMD search index for this store (no-op when QMD is absent), running in the background so it never blocks the turn: `ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async`. Completion is the lifecycle event that keeps the search index fresh.
78
+ 6. QMD reindex LAST (canonical End tail). As the final End-tail step, after the terminal move and any disarm, ALWAYS refresh the QMD search index for this store (no-op when QMD is absent), running in the background so it never blocks the turn: `ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async`. Completion is the lifecycle event that keeps the search index fresh, and the reindex runs last so the index never references a bridge or lock that is about to disappear (see PLASTIC.md `## Delivery Isolation and the Single-Owner Lock`).
79
79
 
80
80
  **This is NOT optional.** An intent with all checklist items done but no Outcome is a broken state. Complete the intent immediately — do not leave it for later.
81
81
 
@@ -96,12 +96,12 @@ For each task:
96
96
  ### Step 3: Update Intent and Complete
97
97
  Capture observations in `## Insights`. When ALL checklist items are checked:
98
98
 
99
- 1. Write `outcome.md` with detailed results in the intent directory, using the `${CLAUDE_PLUGIN_ROOT}/templates/outcome.md` form
99
+ 1. Write `outcome.md` with detailed results in the intent directory, using the `${CLAUDE_PLUGIN_ROOT}/templates/outcome.md` form. Set the frontmatter `disposition: delivered` (the delivered terminal). `outcome.md` is mandatory at every terminal and self-declares its disposition (canonical done-marker in PLASTIC.md).
100
100
  2. Write `## Outcome` summary in the intent file (1-2 sentences)
101
101
  3. Move intent from `## Active` to `## Completed` in INDEX.md (with today's date)
102
102
  4. Update cluster entries to show `_(completed)_`
103
103
  5. Auto-commit: `cd <store-root> && git add . && git commit -m "feat: complete intent <ID> — <name>"`
104
- 6. On completion, ALWAYS refresh the QMD search index for this store (no-op when QMD is absent), running in the background so it never blocks the turn: `ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async`. Completion is the lifecycle event that keeps the search index fresh.
104
+ 6. QMD reindex LAST (canonical End tail). As the final End-tail step, after the terminal move and any disarm, ALWAYS refresh the QMD search index for this store (no-op when QMD is absent), running in the background so it never blocks the turn: `ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root> --async`. Completion is the lifecycle event that keeps the search index fresh, and the reindex runs last so the index never references a bridge or lock that is about to disappear (see PLASTIC.md `## Delivery Isolation and the Single-Owner Lock`).
105
105
 
106
106
  **This is NOT optional.** Complete the intent immediately when work is done.
107
107
 
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: plastic-humanizer
3
+ description: Use to clean authored prose so it reads human and clutter-free. Removes AI tells and slop from a document, spec, outcome, README, report, or release note. Use when the user says "humanize", "de-slop", "remove the AI tells", or "clean up the wording". For documents, not for every chat reply, and not for code.
4
+ ---
5
+
6
+ # Humanizer
7
+
8
+ Make writing read like a person wrote it: plain, direct, low-clutter. Run this as a pass over authored prose (specs, outcomes, README, reports). Do not run it on code or on every chat turn.
9
+
10
+ ## Lead (house style)
11
+ Reframe your answers and any questions you have for me like you are speaking to a well-educated non-English native person. Reduce slang, and rarely used words and terms in the general communication. And cut all the fancy words from explanations. Technical terms and concepts are fine and need no gloss.
12
+
13
+ ## Voice
14
+ Answer like a busy bartender or a senior support engineer who has seen almost every ticket. Help fast, give the fix first, earn the tip.
15
+
16
+ ## Job 1 - remove these surface tells
17
+ 1. Em-dashes and en-dashes - use a comma or a full stop.
18
+ 2. "Not X but Y" (and "it's not just X, it's Y").
19
+ 3. Rule of three - three items only for rhythm.
20
+ 4. Hype / AI words - delve, robust, comprehensive, seamless, leverage, crucial, unlock, landscape.
21
+ 5. Filler openers / signposting - "It's worth noting", "It's important to", "Let's dive in".
22
+ 6. Hedging pile-up - might, could, perhaps, generally, when not needed.
23
+ 7. Sycophancy - "Great question", "You're absolutely right".
24
+ 8. Over-bolding - bold only what carries weight.
25
+
26
+ ## Job 2 - fix the structure
27
+ - Lead with the one main point.
28
+ - Cut sentences that only restate.
29
+ - Pick concrete words over abstract ones.
30
+ - Match the user's voice when samples of their writing exist.
31
+
32
+ ## Process
33
+ Write, check once against the rules above, then send. On documents, run this pass last.
34
+
35
+ ## Length
36
+ Keep it short. If a sentence does not help the reader, cut it.
37
+
38
+ ## More
39
+ For before/after examples, read `references/examples.md`. To make the house style always-on in chat, see `references/always-on-snippet.md`. The full 33-pattern catalog and the research behind this skill live in intent 92's `resources/`. They are background and are not loaded here.
@@ -0,0 +1,70 @@
1
+ {
2
+ "skill_name": "plastic-humanizer",
3
+ "notes": "Intent 92. Cleans authored prose: removes 8 surface tells and applies structural levers (one point, cut restatement, concrete words, match voice). Triggers on document-cleanup asks, not on code. Runner is intent 76; assertions are human for now.",
4
+ "evals": [
5
+ {
6
+ "id": 1,
7
+ "scope": "trigger",
8
+ "set": "validation",
9
+ "prompt": "Humanize this README - it reads like AI wrote it.",
10
+ "expected_output": "Selects plastic-humanizer and runs the cleanup pass over the document.",
11
+ "files": [],
12
+ "assertions": [
13
+ {
14
+ "type": "human",
15
+ "check": "plastic-humanizer is selected for a document-cleanup request",
16
+ "observed": "description triggers on 'humanize' / 'reads like AI' for a document",
17
+ "result": "pass"
18
+ }
19
+ ]
20
+ },
21
+ {
22
+ "id": 2,
23
+ "scope": "trigger",
24
+ "set": "validation",
25
+ "prompt": "Clean up the wording in this spec.",
26
+ "expected_output": "Selects plastic-humanizer and cleans the spec prose.",
27
+ "files": [],
28
+ "assertions": [
29
+ {
30
+ "type": "human",
31
+ "check": "plastic-humanizer is selected for 'clean up the wording'",
32
+ "observed": "description triggers on 'clean up the wording'",
33
+ "result": "pass"
34
+ }
35
+ ]
36
+ },
37
+ {
38
+ "id": 3,
39
+ "scope": "non-trigger",
40
+ "set": "validation",
41
+ "prompt": "Fix the failing test in foo.rb.",
42
+ "expected_output": "Does NOT select plastic-humanizer; this is a code task, not prose cleanup.",
43
+ "files": [],
44
+ "assertions": [
45
+ {
46
+ "type": "human",
47
+ "check": "plastic-humanizer is not selected for a code task",
48
+ "observed": "description scopes the skill to documents, not code",
49
+ "result": "pass"
50
+ }
51
+ ]
52
+ },
53
+ {
54
+ "id": 4,
55
+ "scope": "output",
56
+ "set": "validation",
57
+ "prompt": "Given a slop sample: 'It's worth noting that this is not just a fix, but a comprehensive, robust, and seamless improvement that will unlock real value.' Humanize it.",
58
+ "expected_output": "Cleaned text has no em-dashes and none of the 8 tells, leads with one point, and drops restatement. For example: 'This fix makes X faster.'",
59
+ "files": [],
60
+ "assertions": [
61
+ {
62
+ "type": "human",
63
+ "check": "output drops the 8 tells, leads with one point, cuts restatement",
64
+ "observed": "no em-dashes; no 'not X but Y'; no rule-of-three; no hype words; no filler opener; one load-bearing point",
65
+ "result": "pass"
66
+ }
67
+ ]
68
+ }
69
+ ]
70
+ }
@@ -0,0 +1,9 @@
1
+ # Always-on chat style (copy-paste)
2
+
3
+ Paste this into your global `CLAUDE.md` (applies to all projects) or a project `AGENTS.md` (one project). It keeps the chat style on at near-zero cost. The full cleanup for documents is the `plastic-humanizer` skill.
4
+
5
+ ```
6
+ Reframe your answers and any questions like you are speaking to a well-educated person who is not a native English speaker. Reduce slang and rarely used words. Cut fancy words from explanations. Technical terms and concepts are fine.
7
+
8
+ Answer like a busy bartender or a senior support engineer who has seen almost every ticket: help fast, give the fix first, earn the tip.
9
+ ```
@@ -0,0 +1,48 @@
1
+ # Examples - before and after
2
+
3
+ Each pair shows a tell or a slop pattern, then the fix.
4
+
5
+ ## 1. Em-dash
6
+ Before: The release is ready - we just need sign-off.
7
+ After: The release is ready. We just need sign-off.
8
+ Why: Use a full stop or comma, not an em-dash.
9
+
10
+ ## 2. "Not X but Y"
11
+ Before: This is not just a config change, it's a rethink of the flow.
12
+ After: This change reworks the flow.
13
+ Why: State the point directly.
14
+
15
+ ## 3. Rule of three
16
+ Before: The tool is fast, robust, and powerful.
17
+ After: The tool is fast.
18
+ Why: Drop the list-for-rhythm; keep the claim you can back.
19
+
20
+ ## 4. Hype / AI word
21
+ Before: We leverage a comprehensive caching layer to unlock performance.
22
+ After: We cache results to make it faster.
23
+ Why: Plain verbs and nouns.
24
+
25
+ ## 5. Filler opener / signposting
26
+ Before: It's worth noting that the cache expires after five minutes.
27
+ After: The cache expires after five minutes.
28
+ Why: Say the thing; skip the runway.
29
+
30
+ ## 6. Hedging pile-up
31
+ Before: This might possibly help reduce cost in some cases, generally.
32
+ After: This reduces cost.
33
+ Why: One clear claim. Add a caveat only if it is real.
34
+
35
+ ## 7. Sycophancy
36
+ Before: Great question! That's a really insightful point. Here's the answer.
37
+ After: Here's the answer.
38
+ Why: Drop the praise; answer.
39
+
40
+ ## 8. Structural fix (the mush)
41
+ Before: There are several important considerations to keep in mind when thinking about how best to approach the broad and multifaceted topic of caching, each of which carries its own trade-offs.
42
+ After: Cache reads, not writes. Reads repeat; writes do not.
43
+ Why: One load-bearing point. Cut the restatement and the abstract throat-clearing.
44
+
45
+ ## 9. Over-bolding
46
+ Before: **Note:** the cache **expires** after **five minutes**, so **always** check it.
47
+ After: The cache expires after five minutes, so check it first.
48
+ Why: Bold nothing here; the sentence carries itself.