@zalom/plastic 1.0.0-beta.34 → 1.0.0-beta.35
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 +8 -6
- package/agents/plastic-intent-discovery.md +7 -4
- package/bin/plastic.js +7 -3
- package/package.json +1 -1
- package/scripts/install.rb +42 -6
- package/scripts/lib/bridge.rb +11 -3
- package/scripts/lib/installer_core.rb +39 -6
- package/scripts/lib/preflight.rb +79 -0
- package/skills/doctor/SKILL.md +6 -6
- package/skills/install/SKILL.md +75 -84
- package/skills/intent-discovery/SKILL.md +8 -7
- package/skills/intent-starting/SKILL.md +11 -8
- package/skills/uninstall/SKILL.md +29 -11
- package/skills/update/SKILL.md +34 -23
- package/skills/versions/SKILL.md +27 -12
package/PLASTIC.md
CHANGED
|
@@ -213,12 +213,14 @@ run the main session on the best available thinking model (Fable, Opus, or whate
|
|
|
213
213
|
them). This is advisory only: it changes no behavior and blocks nothing if ignored, and it
|
|
214
214
|
concerns the human's main session, never a dispatched subagent.
|
|
215
215
|
|
|
216
|
-
**`plastic-intent-discovery`.** The What-stage agent. It fires at intent activation,
|
|
217
|
-
delivery lock is armed and Why begins
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
216
|
+
**`plastic-intent-discovery`.** The What-stage agent. It fires at intent activation, after the
|
|
217
|
+
delivery lock is armed and before Why begins, running under that lock as the owner session (it
|
|
218
|
+
does not acquire the lock itself and is not blocked by it): it reads the intent's
|
|
219
|
+
`chain`/`sources` frontmatter, runs QMD-first discovery over completed predecessor work and
|
|
220
|
+
related parked or future intents, and deposits findings to `resources/discovery--<slug>.md` in
|
|
221
|
+
the intent directory ONLY. It never writes the intent file, `spec.md`, or any other lifecycle
|
|
222
|
+
deliverable; the Why-stage `plastic-brainstorming` agent reads its deposit and enriches
|
|
223
|
+
`## Context`.
|
|
222
224
|
|
|
223
225
|
`savepoint.md` — a deterministic, append-only ledger of cycle-step milestones (one line per
|
|
224
226
|
lifecycle boundary, newest at the bottom), written automatically by the gate hook. It is
|
|
@@ -7,13 +7,14 @@ description: |
|
|
|
7
7
|
<example>Context: An intent is being moved from Future to Active.
|
|
8
8
|
user: "Board this intent and gather what we already know"
|
|
9
9
|
assistant: "I'll use the intent-discovery agent to run QMD discovery and deposit findings to resources/"
|
|
10
|
-
<commentary>What-stage discovery runs at activation,
|
|
10
|
+
<commentary>What-stage discovery runs at activation, after the lock is armed, under it.</commentary></example>
|
|
11
11
|
model: sonnet
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
You are the Plastic Intent Discovery agent. You own the What stage: at intent
|
|
15
|
-
activation,
|
|
16
|
-
that already exists and deposit it
|
|
15
|
+
activation, after the lock is armed and before Why begins, under the lock as
|
|
16
|
+
the owner session, you gather the context that already exists and deposit it
|
|
17
|
+
for the Why stage to consume.
|
|
17
18
|
|
|
18
19
|
## Responsibilities
|
|
19
20
|
1. **Read the intent's links.** Load the activating intent file's `chain` and
|
|
@@ -32,6 +33,8 @@ that already exists and deposit it for the Why stage to consume.
|
|
|
32
33
|
## Constraints
|
|
33
34
|
- Read-only with respect to the intent: your single output is
|
|
34
35
|
`resources/discovery--<slug>.md`.
|
|
35
|
-
-
|
|
36
|
+
- You do not ACQUIRE the delivery lock; you run under the lock the
|
|
37
|
+
orchestrator armed (owner session, inherited session id) and are not
|
|
38
|
+
blocked by it.
|
|
36
39
|
- End with a structured completion report per the spawn preamble's report
|
|
37
40
|
contract.
|
package/bin/plastic.js
CHANGED
|
@@ -50,8 +50,12 @@ try {
|
|
|
50
50
|
})
|
|
51
51
|
} catch (err) {
|
|
52
52
|
if (err.status) process.exit(err.status)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
console.error('
|
|
53
|
+
// Ruby cannot run to print its own message when it is missing, so this
|
|
54
|
+
// mirrors scripts/lib/preflight.rb's FATAL block word for word.
|
|
55
|
+
console.error('Plastic needs Ruby 3.0.0 or newer to run its scripts (found not found).')
|
|
56
|
+
console.error('Install a pinned Ruby with mise:')
|
|
57
|
+
console.error(' curl https://mise.run | sh # only if mise is not installed yet')
|
|
58
|
+
console.error(' mise use --global ruby@3.3')
|
|
59
|
+
console.error('Then re-run the Plastic installer.')
|
|
56
60
|
process.exit(1)
|
|
57
61
|
}
|
package/package.json
CHANGED
package/scripts/install.rb
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
# so install.rb is the single file-syncer; the ledger action is contextual.
|
|
15
15
|
|
|
16
16
|
require_relative "lib/installer_core"
|
|
17
|
+
require_relative "lib/preflight"
|
|
17
18
|
|
|
18
19
|
class Install < InstallerCore
|
|
19
20
|
def cli(argv = ARGV)
|
|
@@ -22,6 +23,9 @@ class Install < InstallerCore
|
|
|
22
23
|
return 0
|
|
23
24
|
end
|
|
24
25
|
|
|
26
|
+
gate = preflight_gate
|
|
27
|
+
return gate unless gate.zero?
|
|
28
|
+
|
|
25
29
|
force = argv.include?("--force")
|
|
26
30
|
reinstall = argv.include?("--reinstall")
|
|
27
31
|
ledger_action = flag_value(argv, "--ledger-action")
|
|
@@ -42,19 +46,19 @@ class Install < InstallerCore
|
|
|
42
46
|
return 1
|
|
43
47
|
end
|
|
44
48
|
|
|
45
|
-
run(selected: selected, force: force, reinstall: reinstall, ledger_action: ledger_action)
|
|
49
|
+
run(selected: selected, force: force, reinstall: reinstall, ledger_action: ledger_action, argv: argv)
|
|
46
50
|
0
|
|
47
51
|
end
|
|
48
52
|
|
|
49
53
|
# Hermetic entrypoint (no prompting / no exit). Returns the per-agent results array.
|
|
50
|
-
def run(selected:, force: false, reinstall: false, ledger_action: nil)
|
|
54
|
+
def run(selected:, force: false, reinstall: false, ledger_action: nil, argv: ARGV, input: $stdin)
|
|
51
55
|
fresh = !installed?
|
|
52
56
|
mode = fresh ? :install : :update # :update here means "re-sync, skip bootstrap"
|
|
53
57
|
|
|
54
58
|
distribute(mode)
|
|
55
59
|
bootstrap if fresh
|
|
56
60
|
|
|
57
|
-
results = selected.map { |key| install_for_agent(key, force) }
|
|
61
|
+
results = selected.map { |key| install_for_agent(key, force, argv: argv, input: input, reinstall: reinstall) }
|
|
58
62
|
|
|
59
63
|
action = ledger_action || (fresh ? "install" : "reinstall")
|
|
60
64
|
ledger_append(version, action)
|
|
@@ -72,8 +76,37 @@ class Install < InstallerCore
|
|
|
72
76
|
File.exist?(path) ? File.read(path).strip : nil
|
|
73
77
|
end
|
|
74
78
|
|
|
79
|
+
# Injectable pre-flight gate: real probes as default args, printing to an
|
|
80
|
+
# injectable `out:` IO so this is hermetically testable via StringIO. Returns
|
|
81
|
+
# 1 (stop the install) when Ruby is missing/too-old, else 0.
|
|
82
|
+
def preflight_gate(ruby_version: RUBY_VERSION, node_version: node_probe, git_present: git_probe,
|
|
83
|
+
mise_present: mise_probe, out: $stderr)
|
|
84
|
+
result = Preflight.check(ruby_version: ruby_version, node_version: node_version,
|
|
85
|
+
git_present: git_present, mise_present: mise_present)
|
|
86
|
+
result[:messages].each { |message| out.puts(message) }
|
|
87
|
+
result[:fatal] ? 1 : 0
|
|
88
|
+
end
|
|
89
|
+
|
|
75
90
|
private
|
|
76
91
|
|
|
92
|
+
def node_probe
|
|
93
|
+
`node --version`.strip
|
|
94
|
+
rescue StandardError
|
|
95
|
+
""
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def git_probe
|
|
99
|
+
!`command -v git`.strip.empty?
|
|
100
|
+
rescue StandardError
|
|
101
|
+
false
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def mise_probe
|
|
105
|
+
!`command -v mise`.strip.empty?
|
|
106
|
+
rescue StandardError
|
|
107
|
+
false
|
|
108
|
+
end
|
|
109
|
+
|
|
77
110
|
def flag_value(argv, name)
|
|
78
111
|
i = argv.index(name)
|
|
79
112
|
return nil unless i && argv[i + 1]
|
|
@@ -119,9 +152,12 @@ class Install < InstallerCore
|
|
|
119
152
|
--alpha Alpha channel
|
|
120
153
|
|
|
121
154
|
Other options:
|
|
122
|
-
--reinstall
|
|
123
|
-
--force
|
|
124
|
-
|
|
155
|
+
--reinstall Re-sync core files for the installed version (repair). Store untouched.
|
|
156
|
+
--force Overwrite existing files without prompting
|
|
157
|
+
--statusline VALUE keep or plastic. If an existing statusline is found, this
|
|
158
|
+
skips the interactive prompt. Interactive sessions ask by
|
|
159
|
+
default; non-interactive sessions default to keep.
|
|
160
|
+
-h, --help Show this help
|
|
125
161
|
|
|
126
162
|
Notes:
|
|
127
163
|
Install is one-shot. If Plastic is already installed, use `update` to upgrade or
|
package/scripts/lib/bridge.rb
CHANGED
|
@@ -1173,7 +1173,11 @@ module Bridge
|
|
|
1173
1173
|
c = line[i]
|
|
1174
1174
|
case state
|
|
1175
1175
|
when :single
|
|
1176
|
-
|
|
1176
|
+
if c == "'" || c == "<" || c == ">"
|
|
1177
|
+
out << " "
|
|
1178
|
+
else
|
|
1179
|
+
out << c
|
|
1180
|
+
end
|
|
1177
1181
|
state = :normal if c == "'"
|
|
1178
1182
|
i += 1
|
|
1179
1183
|
when :double
|
|
@@ -1181,7 +1185,11 @@ module Bridge
|
|
|
1181
1185
|
out << " "
|
|
1182
1186
|
i += 2
|
|
1183
1187
|
else
|
|
1184
|
-
|
|
1188
|
+
if c == '"' || c == "<" || c == ">"
|
|
1189
|
+
out << " "
|
|
1190
|
+
else
|
|
1191
|
+
out << c
|
|
1192
|
+
end
|
|
1185
1193
|
state = :normal if c == '"'
|
|
1186
1194
|
i += 1
|
|
1187
1195
|
end
|
|
@@ -1191,7 +1199,7 @@ module Bridge
|
|
|
1191
1199
|
elsif c == '"'
|
|
1192
1200
|
out << " "; state = :double; i += 1
|
|
1193
1201
|
elsif c == "<" && line[i + 1] == "<"
|
|
1194
|
-
m = line[i..].match(/\A<<(-?)\s*("|')?([A-Za-
|
|
1202
|
+
m = line[i..].match(/\A<<(-?)\s*("|')?([A-Za-z0-9_][A-Za-z0-9_]*)\2?/)
|
|
1195
1203
|
if m
|
|
1196
1204
|
openers << { word: m[3], dash: m[1] == "-" }
|
|
1197
1205
|
out << (" " * m[0].length)
|
|
@@ -161,6 +161,37 @@ class InstallerCore
|
|
|
161
161
|
nums.select { |n| n >= 1 && n <= agents.size }.map { |n| agents[n - 1][:key] }
|
|
162
162
|
end
|
|
163
163
|
|
|
164
|
+
# Resolve whether install should keep the user's existing statusline or switch it
|
|
165
|
+
# to Plastic's. Pure function of (settings file, argv, input, reinstall): no writes,
|
|
166
|
+
# so it stays fully unit-testable apart from merge_claude_hooks.
|
|
167
|
+
def statusline_choice(settings_path, argv: [], input: $stdin, reinstall: false)
|
|
168
|
+
existing_command = read_json_safe(settings_path)&.dig("statusLine", "command").to_s
|
|
169
|
+
return :plastic if existing_command.empty?
|
|
170
|
+
return :plastic if existing_command.include?("plastic-")
|
|
171
|
+
|
|
172
|
+
idx = argv.index("--statusline")
|
|
173
|
+
flag = idx && argv[idx + 1]
|
|
174
|
+
return flag.to_sym if %w[keep plastic].include?(flag)
|
|
175
|
+
|
|
176
|
+
return :keep if reinstall
|
|
177
|
+
return prompt_statusline(input: input) if input.tty?
|
|
178
|
+
|
|
179
|
+
:keep
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def prompt_statusline(input: $stdin)
|
|
183
|
+
return :keep unless input.tty?
|
|
184
|
+
|
|
185
|
+
puts "An existing statusline was found in your settings.\n\n"
|
|
186
|
+
puts " 1. Keep my statusline (Plastic will not change it)"
|
|
187
|
+
puts " 2. Switch to Plastic's statusline"
|
|
188
|
+
puts
|
|
189
|
+
print "Select (1 or 2, Enter to keep): "
|
|
190
|
+
answer = input.gets&.strip
|
|
191
|
+
|
|
192
|
+
answer == "2" ? :plastic : :keep
|
|
193
|
+
end
|
|
194
|
+
|
|
164
195
|
# --- Distribution phase ---
|
|
165
196
|
|
|
166
197
|
def distribute(mode)
|
|
@@ -249,6 +280,7 @@ class InstallerCore
|
|
|
249
280
|
"scripts/lib/store_provisioning.rb" => "scripts/lib/store_provisioning.rb",
|
|
250
281
|
"scripts/provision-project-store" => "scripts/provision-project-store",
|
|
251
282
|
"scripts/lib/installer_core.rb" => "scripts/lib/installer_core.rb",
|
|
283
|
+
"scripts/lib/preflight.rb" => "scripts/lib/preflight.rb",
|
|
252
284
|
"scripts/install.rb" => "scripts/install.rb",
|
|
253
285
|
"scripts/update.rb" => "scripts/update.rb",
|
|
254
286
|
"scripts/uninstall.rb" => "scripts/uninstall.rb",
|
|
@@ -321,7 +353,7 @@ class InstallerCore
|
|
|
321
353
|
(data["files"] || {}).keys
|
|
322
354
|
end
|
|
323
355
|
|
|
324
|
-
def install_for_agent(key, force)
|
|
356
|
+
def install_for_agent(key, force, argv: [], input: $stdin, reinstall: false)
|
|
325
357
|
config = agent_config(key)
|
|
326
358
|
return { agent: config[:name], success: false, reason: "Unknown agent" } unless config
|
|
327
359
|
|
|
@@ -334,7 +366,7 @@ class InstallerCore
|
|
|
334
366
|
old_files = manifest_files(manifest_path_for(key, config))
|
|
335
367
|
|
|
336
368
|
result = case key
|
|
337
|
-
when "claude" then install_claude(config, force)
|
|
369
|
+
when "claude" then install_claude(config, force, argv: argv, input: input, reinstall: reinstall)
|
|
338
370
|
when "codex" then install_codex(config, force)
|
|
339
371
|
when "hermes" then install_hermes(config, force)
|
|
340
372
|
end
|
|
@@ -363,7 +395,7 @@ class InstallerCore
|
|
|
363
395
|
removed
|
|
364
396
|
end
|
|
365
397
|
|
|
366
|
-
def install_claude(config, force)
|
|
398
|
+
def install_claude(config, force, argv: [], input: $stdin, reinstall: false)
|
|
367
399
|
hooks_dir = File.join(config[:dir], "hooks")
|
|
368
400
|
skills_root = File.join(config[:dir], "skills")
|
|
369
401
|
plastic_dir = File.join(config[:dir], "plastic")
|
|
@@ -406,7 +438,8 @@ class InstallerCore
|
|
|
406
438
|
|
|
407
439
|
# Merge hooks + statusline into settings.json (no plugin registration)
|
|
408
440
|
settings_path = File.join(config[:dir], "settings.json")
|
|
409
|
-
|
|
441
|
+
choice = statusline_choice(settings_path, argv: argv, input: input, reinstall: reinstall)
|
|
442
|
+
merge_claude_hooks(settings_path, choice: choice)
|
|
410
443
|
|
|
411
444
|
# Write manifest
|
|
412
445
|
manifest_path = File.join(plastic_dir, "manifest.json")
|
|
@@ -572,7 +605,7 @@ class InstallerCore
|
|
|
572
605
|
|
|
573
606
|
# --- settings.json merge (read-modify-write, never clobber) ---
|
|
574
607
|
|
|
575
|
-
def merge_claude_hooks(settings_path)
|
|
608
|
+
def merge_claude_hooks(settings_path, choice: :plastic)
|
|
576
609
|
settings = read_json_safe(settings_path) || {}
|
|
577
610
|
return if settings.nil?
|
|
578
611
|
|
|
@@ -614,7 +647,7 @@ class InstallerCore
|
|
|
614
647
|
File.write(File.join(cache_dir, "original-statusline.json"), JSON.pretty_generate(existing_status))
|
|
615
648
|
end
|
|
616
649
|
|
|
617
|
-
settings["statusLine"] = { "type" => "command", "command" => "#{hook_dir}/plastic-statusline" }
|
|
650
|
+
settings["statusLine"] = { "type" => "command", "command" => "#{hook_dir}/plastic-statusline" } if choice == :plastic
|
|
618
651
|
|
|
619
652
|
# No plugin/marketplace registration: skills are flat personal skills
|
|
620
653
|
# (plastic-<name>/) discovered directly from ~/.claude/skills.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "rubygems"
|
|
5
|
+
|
|
6
|
+
# Pure, dependency-injected pre-flight checks for Plastic's runtime dependencies
|
|
7
|
+
# (intent 38). Takes injected probes (ruby version, node version, git presence,
|
|
8
|
+
# mise presence) and returns a plain decision: ok / fatal plus branded messages.
|
|
9
|
+
#
|
|
10
|
+
# No I/O, no shelling out, no ENV reads here. Callers (scripts/install.rb,
|
|
11
|
+
# bin/plastic.js) own the impure probing and the printing, so this module stays
|
|
12
|
+
# hermetically testable. Voice matches boot_banner.rb (understated, "Plastic ..."
|
|
13
|
+
# prefix); no em-dash, no en-dash in any message.
|
|
14
|
+
module Preflight
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
RUBY_FLOOR = "3.0.0"
|
|
18
|
+
NODE_FLOOR = 18
|
|
19
|
+
RUBY_PIN = "3.3"
|
|
20
|
+
|
|
21
|
+
def check(ruby_version:, node_version:, git_present:, mise_present:)
|
|
22
|
+
messages = []
|
|
23
|
+
|
|
24
|
+
ruby_message = ruby_issue(ruby_version, mise_present)
|
|
25
|
+
fatal = !ruby_message.nil?
|
|
26
|
+
messages << ruby_message if ruby_message
|
|
27
|
+
|
|
28
|
+
node_message = node_issue(node_version)
|
|
29
|
+
messages << node_message if node_message
|
|
30
|
+
|
|
31
|
+
git_message = git_issue(git_present)
|
|
32
|
+
messages << git_message if git_message
|
|
33
|
+
|
|
34
|
+
{ ok: messages.empty?, fatal: fatal, messages: messages }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def ruby_issue(ruby_version, mise_present)
|
|
38
|
+
parsed = safe_version(ruby_version)
|
|
39
|
+
return nil if parsed && parsed >= safe_version(RUBY_FLOOR)
|
|
40
|
+
|
|
41
|
+
lines = []
|
|
42
|
+
lines << "Plastic needs Ruby #{RUBY_FLOOR} or newer to run its scripts (found #{found(ruby_version)})."
|
|
43
|
+
lines << "Install a pinned Ruby with mise:"
|
|
44
|
+
lines << " curl https://mise.run | sh # only if mise is not installed yet" unless mise_present
|
|
45
|
+
lines << " mise use --global ruby@#{RUBY_PIN}"
|
|
46
|
+
lines << "Then re-run the Plastic installer."
|
|
47
|
+
lines.join("\n")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def node_issue(node_version)
|
|
51
|
+
parsed = safe_version(strip_leading_v(node_version))
|
|
52
|
+
return nil if parsed && parsed >= safe_version(NODE_FLOOR.to_s)
|
|
53
|
+
|
|
54
|
+
"Plastic works best on Node #{NODE_FLOOR} or newer (found #{found(node_version)}). " \
|
|
55
|
+
"Pin it with mise: mise use --global node@25"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def git_issue(git_present)
|
|
59
|
+
return nil if git_present
|
|
60
|
+
|
|
61
|
+
"Plastic uses git for its store and worktrees (git was not found). " \
|
|
62
|
+
"Install git, e.g. macOS: xcode-select --install"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def safe_version(str)
|
|
66
|
+
Gem::Version.new(str.to_s)
|
|
67
|
+
rescue ArgumentError
|
|
68
|
+
nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def strip_leading_v(str)
|
|
72
|
+
str.to_s.strip.sub(/\Av/, "")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def found(value)
|
|
76
|
+
text = value.to_s.strip
|
|
77
|
+
text.empty? ? "not found" : text
|
|
78
|
+
end
|
|
79
|
+
end
|
package/skills/doctor/SKILL.md
CHANGED
|
@@ -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
|
|
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
|
|
71
|
-
- `1
|
|
72
|
-
- `2
|
|
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,7 +118,7 @@ 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
|
|
121
|
+
| "Re-run installer" | Run `npx -y @zalom/plastic@<channel> install --agent <agent>` (channel: -alpha->@alpha, -beta->@beta, else @latest) |
|
|
122
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. |
|
|
123
123
|
|
|
124
124
|
For fixes the agent cannot handle automatically, explain what the user needs
|
|
@@ -144,7 +144,7 @@ Show the updated results.
|
|
|
144
144
|
When invoked from `plastic-update` (not directly by the user):
|
|
145
145
|
|
|
146
146
|
1. Run the diagnostic script as in Step 1.
|
|
147
|
-
2. If all checks pass
|
|
147
|
+
2. If all checks pass, show a single line: **"Health check: all clear."**
|
|
148
148
|
3. If issues are found: show the full report (Steps 3-6).
|
|
149
149
|
|
|
150
150
|
This keeps the update flow clean when nothing is wrong.
|
package/skills/install/SKILL.md
CHANGED
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: plastic-install
|
|
3
|
-
description: Use when initializing Plastic globally (~/.plastic/) or locally in a project, or to re-install/repair a broken installation. Accepts channel flags (--alpha, --beta, --latest) to select release channel.
|
|
3
|
+
description: Use when initializing Plastic globally (~/.plastic/) or locally in a project, or to re-install/repair a broken installation. Accepts channel flags (--alpha, --beta, --latest) to select release channel. First install defaults to --beta; reinstalls match the already-installed channel. Global install is recommended: it creates the global intent store as a git-backed repository. Local install creates .plastic/ in the current project for testing.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Install Plastic
|
|
7
7
|
|
|
8
|
-
> **Recommended path:** for a first install, run `npx @zalom/plastic@
|
|
9
|
-
> in your shell (or `bunx @zalom/plastic@
|
|
8
|
+
> **Recommended path:** for a first install, run `npx -y @zalom/plastic@beta install --claude`
|
|
9
|
+
> in your shell (or `bunx -y @zalom/plastic@beta install --claude` if you use Bun). This skill
|
|
10
10
|
> exists to **re-install or repair** an existing setup from inside the agent, and to
|
|
11
11
|
> drive interactive global configuration. Whenever this skill performs an install or
|
|
12
12
|
> re-install, it **runs `/plastic-doctor` afterward** and reports the result.
|
|
13
13
|
|
|
14
|
+
## Channel rule
|
|
15
|
+
|
|
16
|
+
If Plastic is installed, derive `<channel>` from `~/.plastic/VERSION`: a version containing
|
|
17
|
+
`-alpha` means `@alpha`, `-beta` means `@beta`, otherwise `@latest`. If not installed
|
|
18
|
+
(first install), default to `@beta`. The user can always override with
|
|
19
|
+
`--alpha` / `--beta` / `--latest`.
|
|
20
|
+
|
|
14
21
|
## Re-install / repair
|
|
15
22
|
|
|
16
23
|
If Plastic is already installed but something is broken (skills missing, hooks not
|
|
17
|
-
firing, leftover legacy plugin), re-run the installer
|
|
24
|
+
firing, leftover legacy plugin), re-run the installer, it is idempotent, prunes
|
|
18
25
|
files that no longer ship, and removes any legacy plugin/marketplace layout:
|
|
19
26
|
|
|
20
27
|
```bash
|
|
21
|
-
npx @zalom/plastic
|
|
28
|
+
npx -y @zalom/plastic@<channel> install --reinstall --claude
|
|
22
29
|
```
|
|
23
30
|
|
|
24
31
|
Then **run `/plastic-doctor`** and report what it found.
|
|
@@ -27,22 +34,22 @@ Then **run `/plastic-doctor`** and report what it found.
|
|
|
27
34
|
|
|
28
35
|
| Flag | Behavior |
|
|
29
36
|
|------|----------|
|
|
30
|
-
| `--latest` | Install from stable channel
|
|
31
|
-
| `--beta` | Install from beta channel |
|
|
32
|
-
| `--alpha` | Install from alpha channel |
|
|
37
|
+
| `--latest` | Install from the stable channel |
|
|
38
|
+
| `--beta` | Install from the beta channel (default on a first install) |
|
|
39
|
+
| `--alpha` | Install from the alpha channel |
|
|
33
40
|
|
|
34
41
|
When invoked from within Claude Code (re-install or channel switch), the skill
|
|
35
42
|
runs the appropriate npx command:
|
|
36
43
|
|
|
37
44
|
```bash
|
|
38
|
-
# Stable
|
|
39
|
-
npx @zalom/plastic install --claude
|
|
45
|
+
# Stable
|
|
46
|
+
npx -y @zalom/plastic@latest install --claude
|
|
40
47
|
|
|
41
|
-
# Beta
|
|
42
|
-
npx @zalom/plastic@beta install --claude
|
|
48
|
+
# Beta (default on a first install)
|
|
49
|
+
npx -y @zalom/plastic@beta install --claude
|
|
43
50
|
|
|
44
51
|
# Alpha
|
|
45
|
-
npx @zalom/plastic@alpha install --claude
|
|
52
|
+
npx -y @zalom/plastic@alpha install --claude
|
|
46
53
|
```
|
|
47
54
|
|
|
48
55
|
The installed version and channel are recorded in `~/.plastic/VERSION`.
|
|
@@ -55,105 +62,80 @@ Run `/plastic-install` with no arguments.
|
|
|
55
62
|
|
|
56
63
|
#### Procedure
|
|
57
64
|
|
|
58
|
-
**Step 1:
|
|
59
|
-
|
|
60
|
-
Check if `~/.plastic/INDEX.md` exists.
|
|
61
|
-
- If yes: announce "Plastic is already installed at ~/.plastic/. Run `/plastic-update` to sync core files."
|
|
62
|
-
- If no: proceed with fresh install.
|
|
65
|
+
**Step 1: Run the installer**
|
|
63
66
|
|
|
64
|
-
|
|
67
|
+
Check if `~/.plastic/VERSION` exists.
|
|
68
|
+
- If yes: announce "Plastic is already installed at ~/.plastic/. Run `/plastic-update` to
|
|
69
|
+
sync core files, or use the re-install command above to repair in place."
|
|
70
|
+
- If no: run the fresh install command (default `@beta`, or the channel the user named):
|
|
65
71
|
|
|
66
72
|
```bash
|
|
67
|
-
|
|
73
|
+
npx -y @zalom/plastic@beta install --claude
|
|
68
74
|
```
|
|
69
75
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
- `
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
This single command, via `install.rb` (`bootstrap` + `distribute`), creates `store/`,
|
|
77
|
+
`projects/`, `config.yml`, `projects.yml`, `INDEX.md`, and `AGENTS.md` under `~/.plastic/`,
|
|
78
|
+
and copies the utility scripts (`folgezettel-id`, `read-config`, and the rest of
|
|
79
|
+
`scripts/`). This skill does none of that itself; it wraps the command with the
|
|
80
|
+
interactive steps the CLI does not yet own, plus reporting and a doctor pass.
|
|
75
81
|
|
|
76
|
-
|
|
77
|
-
```markdown
|
|
78
|
-
# Plastic — Agent Instructions
|
|
82
|
+
**Statusline**
|
|
79
83
|
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
On install, if an existing statusline is already configured, Plastic asks whether to
|
|
85
|
+
keep it or switch to Plastic's (interactive sessions only). The choice is honored via
|
|
86
|
+
`--statusline keep` or `--statusline plastic`, which skips the prompt. Non-interactive
|
|
87
|
+
sessions (no tty) default to keeping the user's line: nothing is silently overwritten.
|
|
88
|
+
A fresh system with no statusline configured gets Plastic's line with no prompt.
|
|
82
89
|
|
|
83
|
-
|
|
84
|
-
may add content below.
|
|
85
|
-
|
|
86
|
-
---
|
|
87
|
-
```
|
|
90
|
+
**Step 2: Initialize git (retained)**
|
|
88
91
|
|
|
89
|
-
|
|
92
|
+
Only if `~/.plastic/.git` is absent (a fresh bootstrap does not init git):
|
|
90
93
|
|
|
91
|
-
Initialize git:
|
|
92
94
|
```bash
|
|
93
95
|
cd ~/.plastic && git init && git add . && git commit -m "chore: initialize Plastic global intent store"
|
|
94
96
|
```
|
|
95
97
|
|
|
96
|
-
|
|
98
|
+
Retained here because the CLI does not git-init the store yet (follow-up).
|
|
97
99
|
|
|
98
|
-
|
|
99
|
-
mkdir -p ~/.plastic/scripts
|
|
100
|
-
cp "${CLAUDE_PLUGIN_ROOT}/scripts/folgezettel-id" ~/.plastic/scripts/folgezettel-id
|
|
101
|
-
cp "${CLAUDE_PLUGIN_ROOT}/scripts/read-config" ~/.plastic/scripts/read-config
|
|
102
|
-
chmod +x ~/.plastic/scripts/folgezettel-id ~/.plastic/scripts/read-config
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
This ensures project agents can generate hashes via `~/.plastic/scripts/folgezettel-id` without depending on a specific agent's plugin cache path.
|
|
106
|
-
|
|
107
|
-
**Step 2c: Detect agent type and set preferences**
|
|
100
|
+
**Step 3: Personalize config (retained)**
|
|
108
101
|
|
|
109
102
|
Detect which agent is running:
|
|
110
|
-
- If `CLAUDE_CODE` env var is set or we're running inside Claude Code
|
|
111
|
-
- If `HERMES_HOME` env var is set
|
|
112
|
-
- Otherwise
|
|
103
|
+
- If `CLAUDE_CODE` env var is set or we're running inside Claude Code -> `agent.type: claude-code`
|
|
104
|
+
- If `HERMES_HOME` env var is set -> `agent.type: hermes`
|
|
105
|
+
- Otherwise -> ask the user: "Which AI agent are you using? (claude-code / hermes / other)"
|
|
113
106
|
|
|
114
107
|
Ask the user:
|
|
115
|
-
> "Enable Agent Teams? (experimental
|
|
116
|
-
> - Yes
|
|
117
|
-
> - No
|
|
118
|
-
|
|
119
|
-
Update `~/.plastic/config.yml` with detected/chosen values using `read-config --migrate` first to ensure v3 schema, then write the agent-specific values.
|
|
120
|
-
|
|
121
|
-
Auto-commit the config change.
|
|
122
|
-
|
|
123
|
-
**Step 2d: Configure GitHub and push preferences**
|
|
108
|
+
> "Enable Agent Teams? (experimental: parallel project work with teammates)"
|
|
109
|
+
> - Yes -> set `parallel_mode: agent-teams`
|
|
110
|
+
> - No -> set `parallel_mode: linear` (subagents only)
|
|
124
111
|
|
|
125
112
|
Inform the user:
|
|
126
|
-
> "Plastic agents can create GitHub repositories for new projects.
|
|
127
|
-
>
|
|
128
|
-
>
|
|
113
|
+
> "Plastic agents can create GitHub repositories for new projects. By default, all
|
|
114
|
+
> agent-created repos are **private**. Your global intent store (~/.plastic/) is never
|
|
115
|
+
> pushed, it stays local-only."
|
|
129
116
|
|
|
130
117
|
Ask the user:
|
|
131
118
|
> "Default visibility for agent-created repos?"
|
|
132
|
-
> - Private (recommended)
|
|
133
|
-
> - Public
|
|
134
|
-
|
|
119
|
+
> - Private (recommended) -> set `github.default_visibility: private`
|
|
120
|
+
> - Public -> set `github.default_visibility: public`
|
|
121
|
+
>
|
|
135
122
|
> "Allow agents to push to GitHub without asking?"
|
|
136
|
-
> - No (recommended)
|
|
137
|
-
> - Yes
|
|
138
|
-
|
|
139
|
-
Update `config.yml` with chosen values. Auto-commit.
|
|
140
|
-
|
|
141
|
-
**Step 3: Configure project roots**
|
|
142
|
-
|
|
143
|
-
Ask the user:
|
|
123
|
+
> - No (recommended) -> set `github.auto_push: false`
|
|
124
|
+
> - Yes -> set `github.auto_push: true`
|
|
125
|
+
>
|
|
144
126
|
> "Where do you keep your projects? Default: ~/.plastic/projects/"
|
|
145
127
|
> "Add additional roots? (e.g., ~/apps/personal/, ~/apps/companies/)"
|
|
146
128
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
129
|
+
Write each answer via `read-config --migrate` first (ensures the v3 schema), then the
|
|
130
|
+
chosen values; auto-commit each change. Retained here because the CLI writes only
|
|
131
|
+
hardcoded defaults, so these interactive choices stay in the skill.
|
|
150
132
|
|
|
151
133
|
**Step 4: Verify with doctor**
|
|
152
134
|
|
|
153
135
|
Run `/plastic-doctor` and report the result. Resolve any fixable findings before
|
|
154
136
|
announcing success.
|
|
155
137
|
|
|
156
|
-
**Step 5: Register stores with QMD (
|
|
138
|
+
**Step 5: Register stores with QMD (retained)**
|
|
157
139
|
|
|
158
140
|
QMD is an optional search layer. If it is installed, register the Plastic stores so
|
|
159
141
|
they are searchable:
|
|
@@ -165,19 +147,27 @@ ruby ~/.plastic/scripts/qmd-sync detect && ruby ~/.plastic/scripts/qmd-sync regi
|
|
|
165
147
|
`qmd-sync` no-ops cleanly when QMD is absent, so this is safe to run unconditionally.
|
|
166
148
|
It registers `plastic-global` and every project store from `projects.yml`, then indexes
|
|
167
149
|
them. Report what was registered, or that QMD was not detected and the step was skipped.
|
|
150
|
+
Retained here because the CLI does not register at install time (follow-up).
|
|
151
|
+
|
|
152
|
+
**Step 6: Report + announce**
|
|
168
153
|
|
|
169
|
-
|
|
154
|
+
```
|
|
155
|
+
Plastic install (<channel>)
|
|
156
|
+
Command: npx -y @zalom/plastic@<channel> install --claude <flags>
|
|
157
|
+
Version: none -> <installed>
|
|
158
|
+
Doctor: <summary or "all clear">
|
|
159
|
+
```
|
|
170
160
|
|
|
171
|
-
|
|
172
|
-
> Create your first intent with `/plastic-creating-intent`."
|
|
161
|
+
Then: "Create your first intent with `/plastic-creating-intent`."
|
|
173
162
|
|
|
174
163
|
### Local Install (testing/legacy)
|
|
175
164
|
|
|
176
|
-
Run `/plastic-install --local`.
|
|
165
|
+
Run `/plastic-install --local`. `install.rb` has no `--local` verb, so this mode is
|
|
166
|
+
genuinely skill-owned.
|
|
177
167
|
|
|
178
168
|
#### Procedure
|
|
179
169
|
|
|
180
|
-
**Step 1:** Check if `.plastic/` exists in CWD
|
|
170
|
+
**Step 1:** Check if `.plastic/` exists in CWD, if so, warn and exit.
|
|
181
171
|
|
|
182
172
|
**Step 2:** Create `.plastic/` in CWD:
|
|
183
173
|
- `config.yml` from templates
|
|
@@ -192,4 +182,5 @@ Run `/plastic-install --local`.
|
|
|
192
182
|
|
|
193
183
|
**Step 4:** Commit in project: `git add .plastic/ && git commit -m "chore: initialize Plastic local store"`
|
|
194
184
|
|
|
195
|
-
**Step 5:** Announce: "Plastic initialized locally. This is a testing/legacy mode. Consider
|
|
185
|
+
**Step 5:** Announce: "Plastic initialized locally. This is a testing/legacy mode. Consider
|
|
186
|
+
`/plastic-install` for global mode."
|
|
@@ -4,22 +4,21 @@ description: >-
|
|
|
4
4
|
What-stage context deposit at intent activation: run QMD discovery over the
|
|
5
5
|
intent's chain/sources and related parked intents, and write findings to
|
|
6
6
|
resources/discovery--<slug>.md for the Why stage to consume. Use when an intent
|
|
7
|
-
is activated (moved from Future to Active),
|
|
8
|
-
begins. Never writes the intent file itself.
|
|
7
|
+
is activated (moved from Future to Active), after the lock is armed, under it,
|
|
8
|
+
and before Why begins. Never writes the intent file itself.
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# Intent Discovery — What-stage context deposit
|
|
12
12
|
|
|
13
13
|
Announce: "Discovering context for intent [ID] — [name]."
|
|
14
14
|
|
|
15
|
-
Runs once, at intent activation,
|
|
15
|
+
Runs once, at intent activation, after the lock is armed and before Why. It gathers what is
|
|
16
16
|
already known so Why does not start cold, and deposits it as a resource the
|
|
17
17
|
Why-stage brainstorming agent reads.
|
|
18
18
|
|
|
19
19
|
## When it fires
|
|
20
|
-
Inside `plastic-intent-starting`,
|
|
21
|
-
|
|
22
|
-
`plastic-intent-discovery` background agent.
|
|
20
|
+
Inside `plastic-intent-starting`, right after the bridge is armed, under the
|
|
21
|
+
lock. Dispatched as the `plastic-intent-discovery` background agent.
|
|
23
22
|
|
|
24
23
|
## What it does
|
|
25
24
|
1. **Read the intent's links.** Load the activating intent file's `chain` and
|
|
@@ -42,5 +41,7 @@ executor, Done: intent-curator).
|
|
|
42
41
|
|
|
43
42
|
## Boundaries
|
|
44
43
|
- Single output: `resources/discovery--<slug>.md`.
|
|
45
|
-
-
|
|
44
|
+
- Does not ACQUIRE the delivery lock itself; it runs under the lock the
|
|
45
|
+
orchestrator armed, as the owner session (inherited session id), and is not
|
|
46
|
+
blocked by it.
|
|
46
47
|
- Advisory input to Why, not a gate.
|
|
@@ -34,14 +34,6 @@ enforces it: without a held lock, mutating writes to this active intent's dir ar
|
|
|
34
34
|
1. **Ensure the intent is in INDEX `## Active`.** If it sits in `## Future`, activate it
|
|
35
35
|
(move it to `## Active`, auto-commit) before arming. Creation precedes activation, so a
|
|
36
36
|
brand-new What intent is activated here, then locked.
|
|
37
|
-
1a. **Dispatch What-stage discovery (before the lock).** Right after activation and before
|
|
38
|
-
arming the bridge, dispatch the `plastic-intent-discovery` agent (see the
|
|
39
|
-
`plastic-intent-discovery` skill). Resolve its model explicitly and pass it at dispatch
|
|
40
|
-
time (belt-and-braces): `read-config agents.models.plastic-intent-discovery --project
|
|
41
|
-
<repo>`. The agent runs QMD discovery over the intent's `chain`/`sources` and deposits
|
|
42
|
-
findings to `resources/discovery--<slug>.md` only; it never writes the intent file, so the
|
|
43
|
-
lock-owner-only rule is untouched. This is advisory context for Why, not a gate: if
|
|
44
|
-
discovery yields nothing, proceed to the lock normally.
|
|
45
37
|
2. **Self-heal the lock state first.** Run:
|
|
46
38
|
`ruby ~/.plastic/scripts/plastic-lock fix --intent-dir <STORE>/<dir>`
|
|
47
39
|
This is the one repair function (same one /plastic-lock exposes): it removes
|
|
@@ -61,6 +53,17 @@ enforces it: without a held lock, mutating writes to this active intent's dir ar
|
|
|
61
53
|
```
|
|
62
54
|
Replace `<ID>`, `<STORE>` (`~/.plastic/projects/<slug>/store` or `~/.plastic/store`),
|
|
63
55
|
`<dir>` (the `ID--slug` directory), and `<name>`.
|
|
56
|
+
4. **Dispatch What-stage discovery (under the lock).** Right after arming, when the intent
|
|
57
|
+
was just activated in step 1 (on a resume that already has
|
|
58
|
+
`resources/discovery--<slug>.md`, skip: discovery runs once per intent, at activation
|
|
59
|
+
only), dispatch the `plastic-intent-discovery` agent (see the `plastic-intent-discovery`
|
|
60
|
+
skill), now that this session owns the lock, deposit authorized as the owner session. Resolve its
|
|
61
|
+
model explicitly and pass it at dispatch time (belt-and-braces): `read-config
|
|
62
|
+
agents.models.plastic-intent-discovery --project <repo>`. The agent runs QMD discovery
|
|
63
|
+
over the intent's `chain`/`sources` and deposits findings to
|
|
64
|
+
`resources/discovery--<slug>.md` only; it never writes the intent file, so the
|
|
65
|
+
lock-owner-only rule is untouched. This is advisory context for Why, not a gate: if
|
|
66
|
+
discovery yields nothing, proceed to Why normally.
|
|
64
67
|
|
|
65
68
|
**Session id resolution (verbatim from `plastic-auto`).** The first argument is the session
|
|
66
69
|
id the bridge is keyed by: pass the hook stdin `session_id` when you have it, otherwise
|
|
@@ -9,15 +9,21 @@ The installer tracks every file it writes in a manifest, so uninstall is exact a
|
|
|
9
9
|
leaves no orphans. Prefer running it through the CLI; this skill wraps the same
|
|
10
10
|
underlying uninstaller and adds reporting + verification.
|
|
11
11
|
|
|
12
|
+
## Channel rule
|
|
13
|
+
|
|
14
|
+
If Plastic is installed, derive `<channel>` from `~/.plastic/VERSION`: a version containing
|
|
15
|
+
`-alpha` means `@alpha`, `-beta` means `@beta`, otherwise `@latest`. If not installed,
|
|
16
|
+
default to `@beta`. The user can always override with `--alpha` / `--beta` / `--latest`.
|
|
17
|
+
|
|
12
18
|
## Procedure
|
|
13
19
|
|
|
14
20
|
### Step 1: Run the uninstaller
|
|
15
21
|
|
|
16
22
|
```bash
|
|
17
|
-
npx @zalom/plastic
|
|
23
|
+
npx -y @zalom/plastic@<channel> uninstall --claude
|
|
18
24
|
```
|
|
19
25
|
|
|
20
|
-
(Use `--codex` / `--hermes` / `--all` to target other agents. `bunx` works too.)
|
|
26
|
+
(Use `--codex` / `--hermes` / `--all` to target other agents. `bunx -y @zalom/plastic@<channel> uninstall --claude` works too.)
|
|
21
27
|
|
|
22
28
|
This removes, for the targeted agent:
|
|
23
29
|
- all `~/.claude/skills/plastic-*/` skills
|
|
@@ -29,8 +35,8 @@ This removes, for the targeted agent:
|
|
|
29
35
|
|
|
30
36
|
### Step 2: Report removed vs left
|
|
31
37
|
|
|
32
|
-
Relay the uninstaller's output to the user
|
|
33
|
-
**left in place
|
|
38
|
+
Relay the uninstaller's output to the user: what was **removed** and what was
|
|
39
|
+
**left in place**.
|
|
34
40
|
- **Left:** `~/.plastic/` (intent store, history, projects) and any non-Plastic
|
|
35
41
|
settings.json entries.
|
|
36
42
|
|
|
@@ -39,21 +45,33 @@ Relay the uninstaller's output to the user — what was **removed** and what was
|
|
|
39
45
|
Tell the user to confirm:
|
|
40
46
|
|
|
41
47
|
```bash
|
|
42
|
-
ls ~/.claude/skills | grep '^plastic-' #
|
|
43
|
-
ls ~/.claude/hooks | grep '^plastic-' #
|
|
44
|
-
grep -n plastic ~/.claude/settings.json #
|
|
48
|
+
ls ~/.claude/skills | grep '^plastic-' # -> no output
|
|
49
|
+
ls ~/.claude/hooks | grep '^plastic-' # -> no output
|
|
50
|
+
grep -n plastic ~/.claude/settings.json # -> no plastic hook/plugin refs
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Step 4: Report + offer the data decision
|
|
54
|
+
|
|
55
|
+
Emit the reporting block, using the Step 3 checks for the verification line:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
Plastic uninstall (<channel>)
|
|
59
|
+
Command: npx -y @zalom/plastic@<channel> uninstall --claude <flags>
|
|
60
|
+
Version: removed
|
|
61
|
+
Verification: <Step 3 results, or "clean">
|
|
45
62
|
```
|
|
46
63
|
|
|
47
|
-
|
|
64
|
+
Then:
|
|
48
65
|
|
|
49
66
|
```
|
|
50
67
|
Plastic is uninstalled from [agent].
|
|
51
68
|
Your intent store at ~/.plastic/ is untouched.
|
|
52
69
|
|
|
53
70
|
Delete it too?
|
|
54
|
-
a) Keep everything (recommended)
|
|
55
|
-
b) Delete everything now
|
|
71
|
+
a) Keep everything (recommended): re-install anytime with npx
|
|
72
|
+
b) Delete everything now: removes ~/.plastic/ entirely (irreversible)
|
|
56
73
|
```
|
|
57
74
|
|
|
58
|
-
- **Keep:** "Your data is at ~/.plastic/. Re-install anytime with
|
|
75
|
+
- **Keep:** "Your data is at ~/.plastic/. Re-install anytime with
|
|
76
|
+
`npx -y @zalom/plastic@beta install --claude` (or your channel)."
|
|
59
77
|
- **Delete:** run `rm -rf ~/.plastic/` and confirm.
|
package/skills/update/SKILL.md
CHANGED
|
@@ -13,58 +13,69 @@ description: Use when updating Plastic. Runs the `update` verb, which reads the
|
|
|
13
13
|
## What it does
|
|
14
14
|
|
|
15
15
|
`update` is a single deterministic command. It reads `~/.plastic/VERSION`, derives the
|
|
16
|
-
channel from the version string (`-alpha`/`-beta`/none
|
|
16
|
+
channel from the version string (`-alpha`/`-beta`/none -> stable), queries `npm` dist-tags,
|
|
17
17
|
and advances to the **next version on the current channel**. "Already up to date" is a
|
|
18
|
-
clean no-op. You do not compute the target yourself
|
|
18
|
+
clean no-op. You do not compute the target yourself, the script does.
|
|
19
|
+
|
|
20
|
+
## Channel rule
|
|
21
|
+
|
|
22
|
+
If Plastic is installed, derive `<channel>` from `~/.plastic/VERSION`: a version containing
|
|
23
|
+
`-alpha` means `@alpha`, `-beta` means `@beta`, otherwise `@latest`. If not installed
|
|
24
|
+
(first install), default to `@beta`. The user can always override with
|
|
25
|
+
`--alpha` / `--beta` / `--latest`.
|
|
19
26
|
|
|
20
27
|
## Flags
|
|
21
28
|
|
|
22
29
|
| Flag | Behaviour |
|
|
23
30
|
|------|-----------|
|
|
24
31
|
| (none) | Advance to the next version on the **current** channel |
|
|
25
|
-
| `--latest` | Switch to / advance the **stable** channel (toward stability
|
|
32
|
+
| `--latest` | Switch to / advance the **stable** channel (toward stability, frictionless) |
|
|
26
33
|
| `--beta` | Switch to / advance the **beta** channel |
|
|
27
|
-
| `--alpha` | Switch to / advance the **alpha** channel (bleeding edge
|
|
34
|
+
| `--alpha` | Switch to / advance the **alpha** channel (bleeding edge, confirmed if moving down in stability) |
|
|
28
35
|
|
|
29
36
|
Switching toward a more stable channel is frictionless; switching toward bleeding edge is
|
|
30
37
|
confirmed. To roll **back** to a previously-installed version, use `plastic-versions`.
|
|
31
38
|
|
|
32
39
|
## Prerequisites
|
|
33
40
|
|
|
34
|
-
Plastic must be installed (`~/.plastic/VERSION` present). If not, run
|
|
35
|
-
`npx @zalom/plastic install --claude`
|
|
41
|
+
Plastic must be installed (`~/.plastic/VERSION` present). If not, run `plastic-install`
|
|
42
|
+
first (or `npx -y @zalom/plastic@beta install --claude` directly).
|
|
36
43
|
|
|
37
44
|
## Procedure
|
|
38
45
|
|
|
39
46
|
### Step 1: Run the update
|
|
40
47
|
|
|
41
48
|
```bash
|
|
42
|
-
npx @zalom/plastic update
|
|
43
|
-
#
|
|
49
|
+
npx -y @zalom/plastic@<channel> update --claude
|
|
50
|
+
# channel switch: append --beta / --latest / --alpha
|
|
51
|
+
# other agents: append --codex / --hermes / --all
|
|
44
52
|
```
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
The command prints the transition (`vX
|
|
48
|
-
move in the append-only
|
|
54
|
+
`bunx -y @zalom/plastic@<channel> update --claude` works as a fallback if `npx` is
|
|
55
|
+
unavailable. The command prints the transition (`vX -> vY`) or "already up to date", runs
|
|
56
|
+
a post-update doctor summary, and records the move in the append-only
|
|
57
|
+
`~/.plastic/versions.json` ledger.
|
|
49
58
|
|
|
50
|
-
### Step 2:
|
|
59
|
+
### Step 2: Relay the result, announce convention changes
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
Relay what `update` printed, do not recompute the version transition or the doctor
|
|
62
|
+
summary:
|
|
54
63
|
|
|
55
64
|
```
|
|
56
|
-
Plastic
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
Recommendation: run /clear for a clean session with all new conventions loaded.
|
|
65
|
+
Plastic update (<channel>)
|
|
66
|
+
Command: npx -y @zalom/plastic@<channel> update --claude <flags>
|
|
67
|
+
Version: <before> -> <after>
|
|
68
|
+
Doctor: <relayed summary, or "all clear">
|
|
62
69
|
```
|
|
63
70
|
|
|
64
|
-
|
|
71
|
+
Then read `~/.plastic/PLASTIC.md` and announce convention changes that affect the
|
|
72
|
+
current session, and recommend `/clear` for a clean session with all new conventions
|
|
73
|
+
loaded.
|
|
74
|
+
|
|
75
|
+
### Step 3: Health check only on a relayed failure
|
|
65
76
|
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
If the relayed doctor summary shows a failure, invoke `plastic-doctor` for the full
|
|
78
|
+
report and offer to fix. If it already reads clean, do not re-run doctor.
|
|
68
79
|
|
|
69
80
|
### Step 4: Commit + clear update cache
|
|
70
81
|
|
package/skills/versions/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: plastic-versions
|
|
|
3
3
|
description: Use when the user wants to see their Plastic version history or roll back to a previously-installed version after a bad release. Manages the local, append-only versions.json ledger and steps between versions the user has actually run. For moving to a brand-new release, use plastic-update instead.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Plastic Versions
|
|
6
|
+
# Plastic Versions: local version time-machine
|
|
7
7
|
|
|
8
8
|
## When to Use
|
|
9
9
|
- "show plastic versions", "version history", "what versions have I run"
|
|
@@ -11,11 +11,18 @@ description: Use when the user wants to see their Plastic version history or rol
|
|
|
11
11
|
- A new version broke something and the user wants their last known-good build
|
|
12
12
|
|
|
13
13
|
For upgrading to a **new** release, use `plastic-update`. This skill only navigates
|
|
14
|
-
versions you have **already installed
|
|
14
|
+
versions you have **already installed**, the ones recorded in the ledger.
|
|
15
|
+
|
|
16
|
+
## Channel rule
|
|
17
|
+
|
|
18
|
+
`versions` restores whichever build you pick from the ledger, it does not take a channel
|
|
19
|
+
flag for the target. The pinned `<channel>` below is only the npx invocation itself:
|
|
20
|
+
derive it from `~/.plastic/VERSION` the same way as the other lifecycle skills,
|
|
21
|
+
`-alpha` -> `@alpha`, `-beta` -> `@beta`, otherwise `@latest`.
|
|
15
22
|
|
|
16
23
|
## The ledger
|
|
17
24
|
|
|
18
|
-
`~/.plastic/versions.json` is an **append-only JSONL** ledger
|
|
25
|
+
`~/.plastic/versions.json` is an **append-only JSONL** ledger, one line per version
|
|
19
26
|
change, never modified or deleted:
|
|
20
27
|
|
|
21
28
|
```json
|
|
@@ -23,15 +30,16 @@ change, never modified or deleted:
|
|
|
23
30
|
{"version":"1.0.0-alpha.18","action":"update","at":"..."}
|
|
24
31
|
```
|
|
25
32
|
|
|
26
|
-
`action`
|
|
27
|
-
channel is derived from the version string, never stored). It is a
|
|
33
|
+
`action` is one of `install`, `reinstall`, `update`, `downgrade` (derived from version
|
|
34
|
+
direction; the channel is derived from the version string, never stored). It is a
|
|
35
|
+
troubleshooting record.
|
|
28
36
|
|
|
29
37
|
## Procedure
|
|
30
38
|
|
|
31
39
|
### Show history
|
|
32
40
|
|
|
33
41
|
```bash
|
|
34
|
-
npx @zalom/plastic versions
|
|
42
|
+
npx -y @zalom/plastic@<channel> versions
|
|
35
43
|
```
|
|
36
44
|
|
|
37
45
|
Prints the table with the currently-installed version marked. If the most recent action was
|
|
@@ -40,8 +48,8 @@ a `downgrade`, it asks whether to keep rolling back.
|
|
|
40
48
|
### Roll back
|
|
41
49
|
|
|
42
50
|
```bash
|
|
43
|
-
npx @zalom/plastic versions --downgrade # one step back
|
|
44
|
-
npx @zalom/plastic versions --downgrade --version 1.0.0-alpha.15 # to a specific run
|
|
51
|
+
npx -y @zalom/plastic@<channel> versions --downgrade # one step back
|
|
52
|
+
npx -y @zalom/plastic@<channel> versions --downgrade --version 1.0.0-alpha.15 # to a specific run
|
|
45
53
|
```
|
|
46
54
|
|
|
47
55
|
Rollback targets are restricted to versions in the ledger (only builds you have actually
|
|
@@ -51,15 +59,22 @@ re-fetched from npm and re-synced; your intent store, config, and the ledger are
|
|
|
51
59
|
### Step forward (after a rollback)
|
|
52
60
|
|
|
53
61
|
```bash
|
|
54
|
-
npx @zalom/plastic versions --upgrade # one step forward in your history
|
|
62
|
+
npx -y @zalom/plastic@<channel> versions --upgrade # one step forward in your history
|
|
55
63
|
```
|
|
56
64
|
|
|
57
65
|
### After any change
|
|
58
66
|
|
|
59
|
-
Run `plastic-doctor` to confirm health,
|
|
60
|
-
swapped conventions
|
|
67
|
+
Run `plastic-doctor` to confirm health, then emit the reporting block and suggest
|
|
68
|
+
`/clear` so the session picks up the swapped conventions:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
Plastic versions (<channel>)
|
|
72
|
+
Command: npx -y @zalom/plastic@<channel> versions <flags>
|
|
73
|
+
Version: <before> -> <after>
|
|
74
|
+
Doctor: <summary or "all clear">
|
|
75
|
+
```
|
|
61
76
|
|
|
62
77
|
## Notes
|
|
63
|
-
- The ledger is **never** edited or pruned
|
|
78
|
+
- The ledger is **never** edited or pruned, it is the audit trail.
|
|
64
79
|
- Downgrades cannot un-migrate a store-format change; if a warning appears, surface it to
|
|
65
80
|
the user rather than forcing the rollback.
|