@zalom/plastic 1.0.0-beta.2 → 1.0.0-beta.21

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 (82) hide show
  1. package/PLASTIC.md +131 -7
  2. package/agents/plastic-brainstorming.md +9 -1
  3. package/agents/plastic-enforcer.md +1 -1
  4. package/agents/plastic-executor.md +11 -1
  5. package/agents/plastic-intent-curator.md +7 -5
  6. package/agents/plastic-planner.md +11 -1
  7. package/agents/plastic-spec-specialist.md +9 -1
  8. package/hooks/hooks.json +20 -0
  9. package/hooks/retrieval-gate +10 -0
  10. package/hooks/savepoint-pre +10 -0
  11. package/hooks/statusline +150 -41
  12. package/package.json +1 -1
  13. package/scripts/agent-report +163 -0
  14. package/scripts/doctor.rb +172 -0
  15. package/scripts/hook-auto-arm +1 -1
  16. package/scripts/hook-bash-gate +2 -2
  17. package/scripts/hook-code-gate +11 -6
  18. package/scripts/hook-create-gate +2 -2
  19. package/scripts/hook-gate-check +14 -23
  20. package/scripts/hook-retrieval-gate +136 -0
  21. package/scripts/hook-savepoint-pre +32 -0
  22. package/scripts/hook-session-start +1 -1
  23. package/scripts/insight-append +51 -0
  24. package/scripts/lib/bridge.rb +374 -34
  25. package/scripts/lib/frontmatter_writer.rb +130 -0
  26. package/scripts/lib/graph_rebuild.rb +328 -0
  27. package/scripts/lib/insights.rb +86 -0
  28. package/scripts/lib/installer_core.rb +23 -0
  29. package/scripts/lib/link_suggestions.rb +322 -0
  30. package/scripts/lib/links_projection.rb +160 -0
  31. package/scripts/lib/links_section.rb +207 -0
  32. package/scripts/lib/power_tools.rb +76 -0
  33. package/scripts/lib/qmd_hook.rb +38 -25
  34. package/scripts/lib/qmd_sync.rb +36 -0
  35. package/scripts/lib/retrieval_gate.rb +211 -0
  36. package/scripts/lib/worktree.rb +409 -0
  37. package/scripts/link-suggest +211 -0
  38. package/scripts/new-intent +138 -29
  39. package/scripts/project-links +287 -0
  40. package/scripts/qmd-sync +50 -3
  41. package/scripts/rebuild-graph +244 -0
  42. package/scripts/spawn-preamble +26 -1
  43. package/skills/auto/SKILL.md +58 -11
  44. package/skills/auto/evals/evals.json +48 -0
  45. package/skills/auto/references/agent-architecture.md +27 -4
  46. package/skills/auto/references/agent-report-contract.md +121 -0
  47. package/skills/brainstorming/SKILL.md +1 -0
  48. package/skills/brainstorming/evals/evals.json +22 -0
  49. package/skills/continuing/SKILL.md +30 -8
  50. package/skills/continuing/evals/evals.json +9 -0
  51. package/skills/creating-intent/SKILL.md +16 -2
  52. package/skills/creating-intent/evals/evals.json +16 -0
  53. package/skills/creating-intent/references/lifecycle.md +9 -4
  54. package/skills/creating-skills/SKILL.md +65 -0
  55. package/skills/creating-skills/evals/evals.json +108 -0
  56. package/skills/creating-skills/references/agents.md +168 -0
  57. package/skills/creating-skills/references/evals.md +41 -0
  58. package/skills/creating-skills/references/hooks.md +248 -0
  59. package/skills/creating-skills/references/progressive-disclosure.md +176 -0
  60. package/skills/creating-skills/references/scripts.md +166 -0
  61. package/skills/creating-skills/references/skills.md +165 -0
  62. package/skills/creating-skills/scripts/scaffold.rb +313 -0
  63. package/skills/dashboard/SKILL.md +5 -0
  64. package/skills/dashboard/evals/evals.json +22 -0
  65. package/skills/executing-plan/SKILL.md +2 -2
  66. package/skills/humanizer/SKILL.md +39 -0
  67. package/skills/humanizer/evals/evals.json +70 -0
  68. package/skills/humanizer/references/always-on-snippet.md +9 -0
  69. package/skills/humanizer/references/examples.md +48 -0
  70. package/skills/intent-curator/SKILL.md +6 -1
  71. package/skills/intent-curator/evals/evals.json +22 -0
  72. package/skills/linking-intents/SKILL.md +54 -12
  73. package/skills/linking-intents/evals/evals.json +22 -0
  74. package/skills/linking-intents/references/zettelkasten.md +7 -0
  75. package/skills/managing-index/SKILL.md +8 -0
  76. package/skills/managing-index/evals/evals.json +22 -0
  77. package/skills/managing-index/references/zettelkasten-linking.md +6 -1
  78. package/skills/releasing/SKILL.md +32 -0
  79. package/skills/research/SKILL.md +8 -0
  80. package/skills/research/evals/evals.json +22 -0
  81. package/skills/writing-instructions/SKILL.md +0 -159
  82. package/skills/writing-instructions/references/agentskills-spec.md +0 -135
@@ -0,0 +1,165 @@
1
+ # Authoring an Agent Skill
2
+
3
+ Open this when writing or restructuring a single Agent Skill: the frontmatter, the
4
+ description that triggers it, the body voice, and the on-disk layout. Rule ids in brackets
5
+ (A1 through B10) point at the synthesis standard the rules come from.
6
+
7
+ For the three load levels (metadata, body, resources), the thin-router pattern, and how to
8
+ split a body into `references/`, read `progressive-disclosure.md`. This file does not repeat
9
+ that model.
10
+
11
+ ## Contents
12
+
13
+ - Skill file layout
14
+ - Frontmatter fields
15
+ - The `name` field
16
+ - The `description` field (triggering)
17
+ - Optional frontmatter fields
18
+ - Body voice
19
+ - Body content
20
+ - Self-check
21
+
22
+ ## Skill file layout
23
+
24
+ A skill is a directory whose name matches the `name` field. The directory holds `SKILL.md`
25
+ plus optional buckets. Pick a bucket by how the file touches context.
26
+
27
+ | Path | Loads when | Holds |
28
+ |------|-----------|-------|
29
+ | `SKILL.md` | The skill triggers | Frontmatter plus the body |
30
+ | `references/` | The body points at it, on demand | Deep how-to, specs, variant material |
31
+ | `scripts/` | Executed, never read into context | Repeated deterministic code |
32
+ | `assets/` | Copied into output, never read | Templates, boilerplate the output needs |
33
+ | `evals/` | Run by the eval harness | Eval cases for the skill |
34
+
35
+ When picking a bucket, follow `progressive-disclosure.md` for the cross-cutting layout rules:
36
+ references one level deep (C5), no orphan or auxiliary files in the skill (C9), and each fact
37
+ stored once (C7).
38
+
39
+ ## Frontmatter fields
40
+
41
+ `SKILL.md` opens with YAML frontmatter. Two fields are required.
42
+
43
+ | Field | Required | Constraints |
44
+ |-------|----------|-------------|
45
+ | `name` | Yes | 1 to 64 chars. Lowercase alphanumeric plus hyphens. No leading, trailing, or consecutive hyphens. Matches the directory name. |
46
+ | `description` | Yes | 1 to 1024 chars. Non-empty. States what and when. |
47
+
48
+ Invent no other top-level fields. Unknown fields are ignored or rejected and add noise [A6].
49
+
50
+ ## The `name` field
51
+
52
+ Rules [A5]:
53
+
54
+ 1. 64 chars or fewer.
55
+ 2. Lowercase letters, numbers, and hyphens only.
56
+ 3. No leading, trailing, or consecutive hyphens.
57
+ 4. Must match the parent directory name exactly.
58
+ 5. Must not contain `anthropic` or `claude`.
59
+ 6. Prefer the gerund form, which reads as a capability (`processing-pdfs`, `creating-skills`,
60
+ not `pdf-tool`).
61
+
62
+ ## The `description` field (triggering)
63
+
64
+ The description is the only text loaded at discovery time. The body is not loaded when the
65
+ agent decides whether to trigger, so the description alone has to win the match.
66
+
67
+ Rules:
68
+
69
+ 1. Write the description as triggering conditions only ("Use when ..."), never as a summary of
70
+ the workflow [A1]. A workflow summary makes the agent act on the summary and skip the body,
71
+ dropping steps. Documented failure: a description that summarized "code review between
72
+ tasks" produced one review instead of two.
73
+ 2. Write in the third person [A2]. The description is injected into the system prompt, where
74
+ mixed point of view degrades discovery.
75
+ 3. State both what the skill does and when to use it, and front-load concrete trigger terms
76
+ [A3]. The model picks from many skills on this text and the listing is budget-truncated, so
77
+ terms placed late may never be read.
78
+ 4. Include at least one indirect trigger where the user does not name the domain [A4]. Real
79
+ prompts rarely name the skill, so a keyword-only description misses oblique requests.
80
+
81
+ Shape:
82
+
83
+ ```yaml
84
+ description: >
85
+ [One clause: what it does]. Use when [primary trigger], [secondary trigger],
86
+ or when [indirect trigger where the user does not name the domain].
87
+ ```
88
+
89
+ For side-effecting workflows (deploy, release, destructive ops), set
90
+ `disable-model-invocation: true` so only the user fires the skill [A7]. Auto-triggering on
91
+ irreversible work is a known failure mode.
92
+
93
+ ## Optional frontmatter fields
94
+
95
+ Use only the documented optionals below. Add nothing beyond them [A6].
96
+
97
+ | Field | Holds |
98
+ |-------|-------|
99
+ | `license` | Short name or filename reference |
100
+ | `compatibility` | Environment requirements, 1 to 500 chars, only when needed |
101
+ | `metadata` | String-to-string map with unique keys |
102
+ | `allowed-tools` | Space-separated tool list (experimental) |
103
+ | `model` | Claude Code extension: pin the model for this skill |
104
+ | `disable-model-invocation` | Claude Code extension: only the user may fire the skill |
105
+ | `context: fork` | Claude Code extension: run the body as a forked task |
106
+ | `paths` | Claude Code extension: scope the skill to matching paths |
107
+
108
+ ## Body voice
109
+
110
+ Rules:
111
+
112
+ 1. Write in imperative or infinitive voice ("Run the validator", "Extract the text"), never
113
+ second person ("you should", "you can") [B1]. Command voice is shorter and binds tighter;
114
+ hedged second-person language is a known slop pattern.
115
+ 2. Use one consistent term per concept ("extract", never also "pull", "get", "retrieve")
116
+ [B6]. Synonym drift makes the agent unsure whether two terms name the same operation.
117
+
118
+ ## Body content
119
+
120
+ The body persists in context every turn the skill is active, so each line is a recurring
121
+ cost. Spend lines only where the agent would otherwise go wrong.
122
+
123
+ Rules:
124
+
125
+ 1. State what to do, not how or why, and cut anything the model already knows [B2]. Claude is
126
+ already capable; the body is for the project-specific and the non-obvious.
127
+ 2. Apply the test "would the agent get this wrong without it?" to every instruction. If no,
128
+ delete it [B7]. Instructions that restate default competence are pure token cost.
129
+ 3. Challenge every paragraph with "does this justify its token cost?" and prefer one excellent
130
+ example over many mediocre ones [B3].
131
+ 4. Lead each section with one bold maxim and close it with one concrete self-check [B4]. The
132
+ maxim is what survives in context; the check lets the agent apply the rule mid-task.
133
+ 5. Put gotchas as concrete corrections, placed early [B5]. A correction only helps if the
134
+ agent reads it before it makes the mistake. Write the specific fact, not general advice:
135
+
136
+ ```markdown
137
+ ## Gotchas
138
+ - The `users` table uses soft deletes. Queries must include `WHERE deleted_at IS NULL`.
139
+ - User id is `user_id` in the DB, `uid` in auth, `accountId` in billing. Same value.
140
+ - The `/health` endpoint returns 200 even when the DB is down. Use `/ready`.
141
+ ```
142
+
143
+ 6. Carry reference and decision material in tables and numbered lists. Use a flowchart only
144
+ for a genuinely non-obvious decision or an early-stop loop [B10]. Tables are denser per
145
+ token; flowcharts waste tokens on reference, code, or linear steps.
146
+ 7. State the tradeoff and ship an escape hatch instead of policing edge cases with long
147
+ rationalization tables [B8]. Add an Excuse-and-Reality table only for a discipline skill
148
+ where an eval has actually shown the agent rationalizing a violation.
149
+ 8. Avoid time-sensitive content. Move deprecated material into a collapsed "Old patterns"
150
+ section [B9]. Dated instructions rot and mislead.
151
+
152
+ ## Self-check
153
+
154
+ Before shipping the skill, confirm each line:
155
+
156
+ - [ ] `name` matches the directory, is lowercase-hyphen, and carries no `claude`/`anthropic`.
157
+ - [ ] `description` reads as "Use when ...", third person, with front-loaded triggers and at
158
+ least one indirect trigger.
159
+ - [ ] No invented frontmatter fields; optionals are drawn only from the table above.
160
+ - [ ] Side-effecting skill sets `disable-model-invocation`.
161
+ - [ ] Body is imperative, with one term per concept.
162
+ - [ ] Every instruction passes "would the agent get this wrong without it?".
163
+ - [ ] Gotchas are concrete corrections, placed early.
164
+ - [ ] Reference and decision material is in tables or lists, not prose.
165
+ - [ ] `references/` files are one level deep; no orphan or auxiliary files in the skill.
@@ -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
@@ -67,6 +67,11 @@ instead of showing it.
67
67
 
68
68
  ### Step 4 — Entry flow (the board is the menu)
69
69
 
70
+ QMD-first (when available): when the user navigates by describing an intent rather than giving its
71
+ id, before scanning the store with grep/Read run `ruby ~/.plastic/scripts/qmd-sync search "<terms>"`
72
+ to surface the candidate intent, then open the authoritative intent file for the hit. The command is
73
+ a no-op when QMD is absent, so fall back to the existing INDEX.md / file scan.
74
+
70
75
  The board lists everything; the user navigates by free prose (no capped picker):
71
76
  - On the **global** board, the user replies with an **intent id** (work it), a **project
72
77
  name** (re-run `project <slug> --data` and present that board), or **"new"** (start a new
@@ -0,0 +1,22 @@
1
+ {
2
+ "skill_name": "plastic-dashboard",
3
+ "notes": "Intent 66a. Spec for the QMD-first step in the entry flow (resolve a free-prose intent reference before grep/Read). Runner is intent 76; spec only.",
4
+ "evals": [
5
+ {
6
+ "id": 1,
7
+ "scope": "behavior",
8
+ "set": "validation",
9
+ "prompt": "QMD is present. On the board, the user navigates by describing an intent (the search verb work) instead of giving its id.",
10
+ "expected_output": "Before scanning the store with grep/Read to resolve the free-prose reference, runs `ruby ~/.plastic/scripts/qmd-sync search \"search verb\"` to surface the candidate intent, then opens the authoritative intent file for the hit. No-op fallback to INDEX.md / file scan when QMD is absent.",
11
+ "files": [],
12
+ "assertions": [
13
+ {
14
+ "type": "human",
15
+ "check": "qmd-sync search is run before grep/Read when the user navigates by description; authoritative file opened for the hit",
16
+ "observed": "SKILL.md (or agent file) carries the QMD-first step: run qmd-sync search before grep/Read, then open the authoritative file; no-op fallback when QMD is absent",
17
+ "result": "pass"
18
+ }
19
+ ]
20
+ }
21
+ ]
22
+ }
@@ -75,7 +75,7 @@ Capture observations in `## Insights`. When ALL checklist items are checked:
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. Refresh the QMD search index for this store (optional, no-op when QMD is absent): `ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root>`. Delivery is the lifecycle event that keeps the search index fresh.
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.
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
 
@@ -101,7 +101,7 @@ Capture observations in `## Insights`. When ALL checklist items are checked:
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. Refresh the QMD search index for this store (optional, no-op when QMD is absent): `ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root>`. Delivery is the lifecycle event that keeps the search index fresh.
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.
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
+ ```