@zalom/plastic 1.0.0-alpha.9 → 1.0.0-beta.2
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 +163 -469
- package/README.md +95 -58
- package/agents/plastic-brainstorming.md +37 -0
- package/agents/plastic-enforcer.md +36 -0
- package/agents/plastic-executor.md +37 -0
- package/agents/{future-intent-researcher.md → plastic-future-intent-researcher.md} +1 -1
- package/agents/{intent-curator.md → plastic-intent-curator.md} +1 -1
- package/agents/plastic-planner.md +37 -0
- package/agents/plastic-spec-specialist.md +37 -0
- package/bin/plastic.js +57 -0
- package/bin/test +28 -0
- package/deprecations.yml +1 -10
- package/hooks/auto-arm +5 -0
- package/hooks/bash-gate +3 -0
- package/hooks/check-update +12 -8
- package/hooks/code-gate +12 -0
- package/hooks/create-gate +3 -0
- package/hooks/gate-check +3 -1
- package/hooks/hooks.json +52 -0
- package/hooks/qmd-search +8 -0
- package/package.json +2 -2
- package/scripts/dashboard.rb +687 -0
- package/scripts/doctor.rb +1054 -628
- package/scripts/hook-auto-arm +51 -0
- package/scripts/hook-bash-gate +41 -0
- package/scripts/hook-code-gate +27 -0
- package/scripts/hook-continue +15 -114
- package/scripts/hook-create-gate +59 -0
- package/scripts/hook-gate-check +47 -32
- package/scripts/hook-qmd-search +44 -0
- package/scripts/hook-session-start +106 -38
- package/scripts/install.rb +91 -529
- package/scripts/lib/boot_banner.rb +28 -0
- package/scripts/lib/bridge.rb +404 -19
- package/scripts/lib/installer_core.rb +807 -0
- package/scripts/lib/intent_validator.rb +235 -0
- package/scripts/lib/qmd_hook.rb +44 -0
- package/scripts/lib/qmd_sync.rb +209 -0
- package/scripts/lib/store_provisioning.rb +100 -0
- package/scripts/migrate-to-global +1 -1
- package/scripts/new-intent +226 -0
- package/scripts/provision-project-store +53 -0
- package/scripts/qmd-sync +92 -0
- package/scripts/select-update-target +93 -0
- package/scripts/spawn-preamble +121 -0
- package/scripts/uninstall.rb +53 -0
- package/scripts/update.rb +164 -0
- package/scripts/validate-intent +54 -0
- package/scripts/versions.rb +141 -0
- package/skills/_active-intent-gate.md +1 -1
- package/skills/add-project-store/SKILL.md +54 -0
- package/skills/auto/SKILL.md +77 -7
- package/skills/auto/evals/evals.json +207 -0
- package/skills/auto/references/agent-architecture.md +135 -0
- package/skills/brainstorming/SKILL.md +9 -9
- package/skills/brainstorming-grill-me/SKILL.md +6 -6
- package/skills/continuing/SKILL.md +92 -82
- package/skills/continuing/evals/evals.json +136 -0
- package/skills/continuing/references/context-management.md +32 -0
- package/skills/creating-intent/SKILL.md +74 -36
- package/skills/creating-intent/evals/evals.json +56 -0
- package/skills/creating-intent/references/lifecycle.md +76 -0
- package/skills/creating-intent/references/wikilinks.md +8 -0
- package/skills/creating-project/SKILL.md +40 -8
- package/skills/creating-project/references/hubs-projects.md +55 -0
- package/skills/dashboard/SKILL.md +121 -0
- package/skills/dashboard/templates/dashboard-global.md +31 -0
- package/skills/dashboard/templates/dashboard-project.md +40 -0
- package/skills/doctor/SKILL.md +51 -4
- package/skills/doctor/references/gates-stuck-detection.md +38 -0
- package/skills/doctor/report.md +4 -0
- package/skills/evaluating-skills/SKILL.md +140 -0
- package/skills/evaluating-skills/assets/eval-template.json +12 -0
- package/skills/evaluating-skills/evals/evals.json +75 -0
- package/skills/evaluating-skills/references/convention-checks.md +76 -0
- package/skills/evaluating-skills/references/eval-methodology.md +154 -0
- package/skills/executing-plan/SKILL.md +5 -3
- package/skills/install/SKILL.md +69 -8
- package/skills/intent-curator/SKILL.md +3 -3
- package/skills/linking-intents/SKILL.md +16 -7
- package/skills/linking-intents/references/zettelkasten.md +38 -0
- package/skills/managing-index/SKILL.md +5 -1
- package/skills/managing-index/references/zettelkasten-linking.md +1 -1
- package/skills/releasing/SKILL.md +80 -23
- package/skills/releasing/references/deprecations.md +60 -0
- package/skills/research/SKILL.md +2 -2
- package/skills/savepoint/SKILL.md +46 -37
- package/skills/savepoint/references/context-management.md +32 -0
- package/skills/uninstall/SKILL.md +39 -28
- package/skills/update/SKILL.md +41 -44
- package/skills/versions/SKILL.md +65 -0
- package/skills/writing-instructions/SKILL.md +159 -0
- package/skills/writing-instructions/references/agentskills-spec.md +135 -0
- package/skills/writing-plans/SKILL.md +5 -5
- package/templates/agents.md +7 -7
- package/templates/outcome.md +13 -0
- package/templates/savepoint.md +14 -13
- package/templates/spec.md +25 -0
- package/bin/install.js +0 -29
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
# Plastic — `update` verb. Runs via `npx @zalom/plastic update` (bin/plastic.js) or directly.
|
|
6
|
+
# Usage: ruby scripts/update.rb [--beta|--latest|--alpha] [--claude|--codex|--hermes|--all] [--help]
|
|
7
|
+
#
|
|
8
|
+
# Forward-only version transitions, sourced from npm dist-tags:
|
|
9
|
+
# - no flag = next version on the CURRENT channel (derived from VERSION)
|
|
10
|
+
# - --beta/--latest/--alpha = switch channel. Toward stability (alpha->beta->latest) is
|
|
11
|
+
# frictionless; toward bleeding edge requires confirmation.
|
|
12
|
+
# "Already up to date" is a clean no-op. Performs the switch by delegating the file-sync to
|
|
13
|
+
# `install --reinstall --ledger-action update` for the chosen version via npx.
|
|
14
|
+
|
|
15
|
+
require_relative "lib/installer_core"
|
|
16
|
+
require_relative "doctor"
|
|
17
|
+
|
|
18
|
+
class Update < InstallerCore
|
|
19
|
+
PKG = "@zalom/plastic"
|
|
20
|
+
|
|
21
|
+
def cli(argv = ARGV)
|
|
22
|
+
if argv.include?("--help") || argv.include?("-h")
|
|
23
|
+
show_help
|
|
24
|
+
return 0
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
iv = installed_version
|
|
28
|
+
unless iv
|
|
29
|
+
warn "Plastic is not installed. Run: npx #{PKG} install --claude"
|
|
30
|
+
return 1
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
requested = requested_channel(argv)
|
|
34
|
+
tags = fetch_dist_tags
|
|
35
|
+
unless tags
|
|
36
|
+
warn "Could not query npm dist-tags. Are you online?"
|
|
37
|
+
return 1
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
res = compute_target(installed_version: iv, dist_tags: tags, requested_channel: requested)
|
|
41
|
+
|
|
42
|
+
case res[:status]
|
|
43
|
+
when :up_to_date
|
|
44
|
+
puts "\u{2705} Plastic v#{iv} is already up to date on the #{channel_for(iv)} channel."
|
|
45
|
+
return 0
|
|
46
|
+
when :unknown_channel
|
|
47
|
+
warn "No published version on the #{requested} channel."
|
|
48
|
+
return 1
|
|
49
|
+
when :ok
|
|
50
|
+
if res[:kind] == :cross_bleeding && !confirm_bleeding(iv, res[:target])
|
|
51
|
+
puts "Aborted."
|
|
52
|
+
return 1
|
|
53
|
+
end
|
|
54
|
+
puts "\u{2b06}\u{fe0f} Updating Plastic #{iv} \u{2192} #{res[:target]}"
|
|
55
|
+
exit_code = perform_switch(res[:target], agent_args(argv))
|
|
56
|
+
run_post_update_doctor if exit_code == 0
|
|
57
|
+
exit_code
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Run the full doctor after a successful update and print a human-readable
|
|
62
|
+
# summary. Informational only: does not raise and does not affect the update's
|
|
63
|
+
# exit code. Accepts injected `doctor` and `out` for hermetic unit tests.
|
|
64
|
+
def run_post_update_doctor(doctor: nil, out: $stdout)
|
|
65
|
+
doctor ||= Doctor.new
|
|
66
|
+
out.puts "\nRunning full doctor after update..."
|
|
67
|
+
result = doctor.run_checks("claude")
|
|
68
|
+
s = result[:summary]
|
|
69
|
+
out.puts " Doctor status: #{result[:status]} " \
|
|
70
|
+
"(pass: #{s[:pass]}, warn: #{s[:warn]}, fail: #{s[:fail]}, total: #{s[:total]})"
|
|
71
|
+
out.puts " Run /plastic-doctor for details." unless result[:status] == "pass"
|
|
72
|
+
result
|
|
73
|
+
rescue StandardError => e
|
|
74
|
+
# Non-blocking: a crash here (e.g. malformed file in the real store) must not
|
|
75
|
+
# undo or fail an update that already succeeded. Report and move on.
|
|
76
|
+
out.puts " doctor could not run: #{e.message} — run /plastic-doctor"
|
|
77
|
+
nil
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Pure decision logic (hermetically testable). Returns a status hash.
|
|
81
|
+
def compute_target(installed_version:, dist_tags:, requested_channel: nil)
|
|
82
|
+
installed_ch = channel_for(installed_version)
|
|
83
|
+
target_ch = requested_channel || installed_ch
|
|
84
|
+
cand = dist_tags[target_ch]
|
|
85
|
+
return { status: :unknown_channel } unless cand
|
|
86
|
+
|
|
87
|
+
if target_ch == installed_ch
|
|
88
|
+
return { status: :up_to_date } unless semver_gt?(cand, installed_version)
|
|
89
|
+
{ status: :ok, target: cand, kind: :in_channel }
|
|
90
|
+
else
|
|
91
|
+
bleeding = stability_rank(target_ch) < stability_rank(installed_ch)
|
|
92
|
+
{ status: :ok, target: cand, kind: bleeding ? :cross_bleeding : :cross_stable }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def installed_version
|
|
97
|
+
path = File.join(plastic_home, "VERSION")
|
|
98
|
+
File.exist?(path) ? File.read(path).strip : nil
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def requested_channel(argv)
|
|
104
|
+
return "alpha" if argv.include?("--alpha")
|
|
105
|
+
return "beta" if argv.include?("--beta")
|
|
106
|
+
return "latest" if argv.include?("--latest")
|
|
107
|
+
nil
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Agent flags to pass through to the delegated install (default --claude).
|
|
111
|
+
def agent_args(argv)
|
|
112
|
+
flags = agents.map { |a| a[:flag] }.select { |f| argv.include?(f) }
|
|
113
|
+
flags << "--all" if argv.include?("--all")
|
|
114
|
+
flags.empty? ? ["--claude"] : flags
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def fetch_dist_tags
|
|
118
|
+
raw = `npm view #{PKG} dist-tags --json 2>/dev/null`
|
|
119
|
+
return nil if raw.nil? || raw.strip.empty?
|
|
120
|
+
JSON.parse(raw)
|
|
121
|
+
rescue JSON::ParserError, StandardError
|
|
122
|
+
nil
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def confirm_bleeding(current, target)
|
|
126
|
+
return false unless $stdin.tty?
|
|
127
|
+
print "Switch from #{current} to the less-stable #{target}? [y/N]: "
|
|
128
|
+
($stdin.gets&.strip || "").downcase.start_with?("y")
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Thin npx-exec glue (not unit-tested; the decision above is). Delegates the file-sync to
|
|
132
|
+
# the target version's install verb, recording the ledger action as `update`.
|
|
133
|
+
def perform_switch(target, agent_flags)
|
|
134
|
+
cmd = ["npx", "#{PKG}@#{target}", "install", "--reinstall", "--ledger-action", "update", *agent_flags]
|
|
135
|
+
puts " $ #{cmd.join(" ")}"
|
|
136
|
+
system(*cmd) ? 0 : 1
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def show_help
|
|
140
|
+
puts <<~HELP
|
|
141
|
+
|
|
142
|
+
plastic update — upgrade Plastic to a newer version
|
|
143
|
+
|
|
144
|
+
Usage:
|
|
145
|
+
npx @zalom/plastic update [options]
|
|
146
|
+
|
|
147
|
+
Channel options (default: stay on the current channel):
|
|
148
|
+
--latest Switch to / advance the stable channel
|
|
149
|
+
--beta Switch to / advance the beta channel
|
|
150
|
+
--alpha Switch to / advance the alpha channel (bleeding edge — confirmed)
|
|
151
|
+
|
|
152
|
+
Agent options (default: --claude):
|
|
153
|
+
--claude --codex --hermes --all
|
|
154
|
+
|
|
155
|
+
Behaviour:
|
|
156
|
+
No flag advances to the next version on your current channel. Switching toward a
|
|
157
|
+
more stable channel is frictionless; switching toward bleeding edge is confirmed.
|
|
158
|
+
Use `versions` to roll back to a previously-installed version.
|
|
159
|
+
|
|
160
|
+
HELP
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
exit(Update.new(package_root: ENV["PLASTIC_PACKAGE_ROOT"] || File.expand_path("..", __dir__)).cli(ARGV)) if $PROGRAM_NAME == __FILE__
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
# validate-intent — deterministic CLI over IntentValidator (intent 60).
|
|
6
|
+
#
|
|
7
|
+
# Checks whether a single intent directory is born complete: required frontmatter
|
|
8
|
+
# fields present, and `sources`/`chain` well-formed arrays of id strings. The
|
|
9
|
+
# creating-intent skill self-verifies a freshly written intent with this CLI
|
|
10
|
+
# before announcing or committing; any agent or human can run it on one dir.
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# validate-intent <intent_dir> [--home <path>]
|
|
14
|
+
#
|
|
15
|
+
# Exit codes: 0 (born complete), 1 (incomplete; report on stderr), 2 (usage).
|
|
16
|
+
# --home <path> overrides the Plastic home (default: ~/.plastic).
|
|
17
|
+
|
|
18
|
+
require_relative "lib/intent_validator"
|
|
19
|
+
|
|
20
|
+
def plastic_home(args)
|
|
21
|
+
if (i = args.index("--home")) && args[i + 1]
|
|
22
|
+
File.expand_path(args[i + 1])
|
|
23
|
+
else
|
|
24
|
+
File.expand_path(ENV["PLASTIC_HOME"] || "~/.plastic")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
home = plastic_home(ARGV)
|
|
29
|
+
|
|
30
|
+
# First positional arg, skipping any flag and the value that follows --home.
|
|
31
|
+
home_value_index = (i = ARGV.index("--home")) ? i + 1 : nil
|
|
32
|
+
intent_dir = ARGV.each_index.find do |idx|
|
|
33
|
+
arg = ARGV[idx]
|
|
34
|
+
!arg.start_with?("--") && idx != home_value_index
|
|
35
|
+
end
|
|
36
|
+
intent_dir = intent_dir && ARGV[intent_dir]
|
|
37
|
+
|
|
38
|
+
if intent_dir.nil?
|
|
39
|
+
warn "usage: validate-intent <intent_dir> [--home <path>]"
|
|
40
|
+
exit 2
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
dir = File.expand_path(intent_dir)
|
|
44
|
+
result = IntentValidator.validate(dir, plastic_home: home)
|
|
45
|
+
|
|
46
|
+
if result[:ok]
|
|
47
|
+
puts "OK: #{dir}"
|
|
48
|
+
exit 0
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
warn "INCOMPLETE: #{dir}"
|
|
52
|
+
result[:missing].each { |field| warn "missing: #{field}" }
|
|
53
|
+
result[:errors].each { |error| warn error }
|
|
54
|
+
exit 1
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
# Plastic — `versions` verb. Runs via `npx @zalom/plastic versions` (bin/plastic.js) or directly.
|
|
6
|
+
# Usage: ruby scripts/versions.rb [--downgrade [--version <v>] | --upgrade] [--help]
|
|
7
|
+
#
|
|
8
|
+
# Local time-machine over the append-only versions.json ledger. Navigates ONLY versions you
|
|
9
|
+
# have actually run (the ledger), never npm's full list — fetching brand-new versions is
|
|
10
|
+
# `update`'s job. Lets you roll back to a known-good build after a bad release.
|
|
11
|
+
# - no flag print the ledger table (and offer to continue a rollback)
|
|
12
|
+
# - --downgrade step back one version in your history
|
|
13
|
+
# - --downgrade --version V jump to a specific previously-run version
|
|
14
|
+
# - --upgrade step forward one version (after a rollback)
|
|
15
|
+
|
|
16
|
+
require_relative "lib/installer_core"
|
|
17
|
+
|
|
18
|
+
class Versions < InstallerCore
|
|
19
|
+
PKG = "@zalom/plastic"
|
|
20
|
+
|
|
21
|
+
def cli(argv = ARGV)
|
|
22
|
+
if argv.include?("--help") || argv.include?("-h")
|
|
23
|
+
show_help
|
|
24
|
+
return 0
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
ledger = ledger_read
|
|
28
|
+
if ledger.empty?
|
|
29
|
+
puts "No version history yet (versions.json is empty). Install or update first."
|
|
30
|
+
return 0
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
timeline = version_timeline(ledger)
|
|
34
|
+
current = installed_version || timeline.last
|
|
35
|
+
|
|
36
|
+
if argv.include?("--downgrade")
|
|
37
|
+
explicit = flag_value(argv, "--version")
|
|
38
|
+
target = explicit || step_target(timeline, current, :back)
|
|
39
|
+
return no_target("No earlier version in your history.") unless target
|
|
40
|
+
unless timeline.include?(target)
|
|
41
|
+
warn "#{target} is not in your version history. Choose one of: #{timeline.join(", ")}"
|
|
42
|
+
return 1
|
|
43
|
+
end
|
|
44
|
+
return switch_to(target, current)
|
|
45
|
+
elsif argv.include?("--upgrade")
|
|
46
|
+
target = step_target(timeline, current, :forward)
|
|
47
|
+
return no_target("No later version in your history. Use `update` for new releases.") unless target
|
|
48
|
+
return switch_to(target, current)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# No flag: show the table, and detect an in-progress rollback.
|
|
52
|
+
print_table(ledger, current)
|
|
53
|
+
if ledger.last && ledger.last["action"] == "downgrade"
|
|
54
|
+
prev = step_target(timeline, current, :back)
|
|
55
|
+
if prev && $stdin.tty?
|
|
56
|
+
print "\nYou recently rolled back. Go back further to #{prev}? [y/N]: "
|
|
57
|
+
return switch_to(prev, current) if ($stdin.gets&.strip || "").downcase.start_with?("y")
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
0
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Unique versions in first-seen (chronological) order — the user's personal timeline.
|
|
64
|
+
def version_timeline(ledger)
|
|
65
|
+
ledger.map { |e| e["version"] }.compact.uniq
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Neighbour of `current` in the timeline. direction :back (older) or :forward (newer).
|
|
69
|
+
def step_target(timeline, current, direction)
|
|
70
|
+
i = timeline.index(current)
|
|
71
|
+
return nil if i.nil?
|
|
72
|
+
j = direction == :back ? i - 1 : i + 1
|
|
73
|
+
return nil if j < 0 || j >= timeline.length
|
|
74
|
+
timeline[j]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Direction-derived ledger action for moving current -> target.
|
|
78
|
+
def action_for(target, current)
|
|
79
|
+
cmp = semver_compare(target, current)
|
|
80
|
+
cmp == -1 ? "downgrade" : "update"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def installed_version
|
|
84
|
+
path = File.join(plastic_home, "VERSION")
|
|
85
|
+
File.exist?(path) ? File.read(path).strip : nil
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def flag_value(argv, name)
|
|
91
|
+
i = argv.index(name)
|
|
92
|
+
return nil unless i && argv[i + 1]
|
|
93
|
+
argv[i + 1]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def no_target(msg)
|
|
97
|
+
puts msg
|
|
98
|
+
0
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def switch_to(target, current)
|
|
102
|
+
action = action_for(target, current)
|
|
103
|
+
puts "#{action == "downgrade" ? "\u{23ea}" : "\u{23e9}"} #{current} \u{2192} #{target} (#{action})"
|
|
104
|
+
cmd = ["npx", "#{PKG}@#{target}", "install", "--reinstall", "--ledger-action", action, "--claude"]
|
|
105
|
+
puts " $ #{cmd.join(" ")}"
|
|
106
|
+
system(*cmd) ? 0 : 1
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def print_table(ledger, current)
|
|
110
|
+
puts "\nPlastic version history (versions.json — append-only):\n\n"
|
|
111
|
+
puts " %-22s %-10s %s" % ["version", "action", "at"]
|
|
112
|
+
ledger.each do |e|
|
|
113
|
+
marker = e["version"] == current ? "\u{2192} " : " "
|
|
114
|
+
puts " #{marker}%-22s %-10s %s" % [e["version"], e["action"], e["at"]]
|
|
115
|
+
end
|
|
116
|
+
puts "\n \u{2192} = currently installed (v#{current})\n\n"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def show_help
|
|
120
|
+
puts <<~HELP
|
|
121
|
+
|
|
122
|
+
plastic versions — manage and roll back your local Plastic versions
|
|
123
|
+
|
|
124
|
+
Usage:
|
|
125
|
+
npx @zalom/plastic versions [options]
|
|
126
|
+
|
|
127
|
+
Options:
|
|
128
|
+
(none) Show the version-history table
|
|
129
|
+
--downgrade Roll back one version in your history
|
|
130
|
+
--downgrade --version V Roll back to a specific previously-run version
|
|
131
|
+
--upgrade Step forward one version in your history
|
|
132
|
+
-h, --help Show this help
|
|
133
|
+
|
|
134
|
+
Navigates only versions you have already run (the append-only ledger). To move to a
|
|
135
|
+
brand-new release, use `update`.
|
|
136
|
+
|
|
137
|
+
HELP
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
exit(Versions.new(package_root: ENV["PLASTIC_PACKAGE_ROOT"] || File.expand_path("..", __dir__)).cli(ARGV)) if $PROGRAM_NAME == __FILE__
|
|
@@ -13,7 +13,7 @@ Before proceeding, resolve the active intent:
|
|
|
13
13
|
- Look under `## Active` for intent entries
|
|
14
14
|
- If exactly one active intent → use it
|
|
15
15
|
- If multiple active intents → ask user which one
|
|
16
|
-
- If no active intent → refuse: "No active intent. Create one first with /plastic
|
|
16
|
+
- If no active intent → refuse: "No active intent. Create one first with /plastic-creating-intent"
|
|
17
17
|
|
|
18
18
|
3. **Resolve paths:**
|
|
19
19
|
- Intent directory: `{store}/store/{id}--{slug}/`
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plastic-add-project-store
|
|
3
|
+
description: >-
|
|
4
|
+
Add an intent store to a project that is already registered in projects.yml
|
|
5
|
+
but has no store on disk. Use when a project is registered but has no store,
|
|
6
|
+
when you need to provision a store, or when doctor reports a missing project
|
|
7
|
+
store (project_store_dir). Thin wrapper around provision-project-store plus an
|
|
8
|
+
optional qmd register step.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Add a Project Store
|
|
12
|
+
|
|
13
|
+
Provision the intent store for a project that is already registered in
|
|
14
|
+
`projects.yml` but whose `~/.plastic/projects/{slug}/store/` is missing. This is
|
|
15
|
+
the standalone path; `plastic-creating-project` provisions the store for brand
|
|
16
|
+
new projects.
|
|
17
|
+
|
|
18
|
+
The provisioner is the single source of truth for store creation. It is pure
|
|
19
|
+
filesystem and idempotent, so re-running is always safe. The project must already
|
|
20
|
+
be registered (this skill does not edit `projects.yml`).
|
|
21
|
+
|
|
22
|
+
## 1. Resolve the slug
|
|
23
|
+
|
|
24
|
+
The slug is the project's key under `projects` in `~/.plastic/projects.yml`. If
|
|
25
|
+
the user names a project, map it to its slug. If the slug is missing from
|
|
26
|
+
`projects.yml`, stop: the provisioner requires the project to already be
|
|
27
|
+
registered. If ambiguous, list the registered slugs and ask which one.
|
|
28
|
+
|
|
29
|
+
## 2. Run the provisioner
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
ruby ~/.plastic/scripts/provision-project-store <slug>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This creates `~/.plastic/projects/<slug>/store/` with `.gitkeep`, writes
|
|
36
|
+
`INDEX.md` and `project.yml` only if they are missing, and never clobbers
|
|
37
|
+
existing files. An unregistered slug exits non-zero with a clear error and
|
|
38
|
+
creates nothing.
|
|
39
|
+
|
|
40
|
+
## 3. Register with QMD (separate, optional step)
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
ruby ~/.plastic/scripts/qmd-sync register --store ~/.plastic/projects/<slug>/store
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This is the only qmd mutation; the provisioner itself performs none. It no-ops
|
|
47
|
+
when qmd is absent, so it is safe to run unconditionally.
|
|
48
|
+
|
|
49
|
+
## Scope
|
|
50
|
+
|
|
51
|
+
- Provisioning and qmd registration are two distinct steps. Do not fold qmd into
|
|
52
|
+
the provisioner.
|
|
53
|
+
- No `projects.yml` edits, no project-tree edits, no tactical-mirror or
|
|
54
|
+
founding-intent seeding.
|
package/skills/auto/SKILL.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: plastic
|
|
2
|
+
name: plastic-auto
|
|
3
3
|
description: >-
|
|
4
4
|
Autonomous intent delivery — agent takes over How and Exec. Use when user says
|
|
5
5
|
"auto", "take it from here", "deliver this", or when brainstorming-grill-me concludes
|
|
@@ -12,14 +12,68 @@ Announce: "Taking over intent [ID] — [name] for autonomous delivery."
|
|
|
12
12
|
|
|
13
13
|
## Precondition
|
|
14
14
|
|
|
15
|
-
An active intent MUST exist in INDEX.md. If none exists, refuse: "No active intent found. Create one first with /plastic
|
|
15
|
+
An active intent MUST exist in INDEX.md. If none exists, refuse: "No active intent found. Create one first with /plastic-creating-intent."
|
|
16
16
|
|
|
17
17
|
If multiple active intents exist, ask the user which one to deliver (this is the only question auto asks).
|
|
18
18
|
|
|
19
|
+
**Picking work when no intent is specified.** If the user says "auto" without naming an
|
|
20
|
+
intent and none is active, consult the dashboard's machine-readable queue to choose the
|
|
21
|
+
next dispatchable intent:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
ruby ~/.plastic/scripts/dashboard.rb all --json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Work `dispatchable_queue` in `rank` order (these are `defer`/`research` dispositions —
|
|
28
|
+
safe to deliver autonomously). Leave `human_only` and `next_big_thing` for the user — those
|
|
29
|
+
are `drive`/`triage` items the human should lead. See the `plastic-dashboard` skill.
|
|
30
|
+
|
|
31
|
+
## Arm the Lifecycle Gate (do this FIRST)
|
|
32
|
+
|
|
33
|
+
Immediately after selecting the intent — before any other work — arm auto mode. This
|
|
34
|
+
writes the session bridge that makes the code-edit gate live, so project code cannot be
|
|
35
|
+
edited before the plan exists (the gate applies to YOU, the orchestrator):
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
ruby -r ~/.plastic/scripts/lib/bridge -e \
|
|
39
|
+
'Bridge.arm_auto(ENV["CLAUDE_SESSION_ID"], intent_id: "<ID>", intent_dir: "<STORE>/<dir>", store: "<STORE>", name: "<name>")'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Replace `<ID>`, `<STORE>` (e.g. `~/.plastic/projects/<slug>/store` or `~/.plastic/store`),
|
|
43
|
+
`<dir>` (the `ID--slug` directory), and `<name>`. If `CLAUDE_SESSION_ID` is unset, `arm_auto`
|
|
44
|
+
falls back to a deterministic derived bridge key (a hash of the store and intent id), so the
|
|
45
|
+
gate still engages; arming prints a one-line notice to stderr in that case.
|
|
46
|
+
|
|
47
|
+
**Hard rule for the rest of this run:** do NOT edit project code (anything outside the
|
|
48
|
+
intent directory / `~/.plastic/`) until `plan.md` AND `checklist.md` exist for the intent.
|
|
49
|
+
Honor the cycle: What → Why (spec.md) → How (plan.md + actions/ + checklist.md) → Exec.
|
|
50
|
+
|
|
19
51
|
## Flags
|
|
20
52
|
|
|
21
53
|
- `--skip-permissions` — bypass hard stops on destructive actions on existing projects. Full trust mode. Default: off.
|
|
22
54
|
|
|
55
|
+
## Team Spin-Up
|
|
56
|
+
|
|
57
|
+
Auto mode spins up exactly ONE enforcer-led team per intent. The plastic-enforcer IS this orchestrator (you), not a separately dispatched agent, which avoids the who-gates-the-gater regress.
|
|
58
|
+
|
|
59
|
+
Roster (one role per cycle stage):
|
|
60
|
+
|
|
61
|
+
- **plastic-brainstorming** (Why exploration): enriches `## Context` + `### Decisions`
|
|
62
|
+
- **plastic-spec-specialist** (`spec.md`)
|
|
63
|
+
- **plastic-planner** (`plan.md` + `actions/` + `checklist.md`)
|
|
64
|
+
- **plastic-executor** (code + checklist + `## Insights`)
|
|
65
|
+
- **plastic-enforcer** (orchestrates + gates; that is YOU)
|
|
66
|
+
|
|
67
|
+
Dispatch rule: sequential, one specialist per stage on one branch (the deliverables share files). Gate each deliverable against the stage's exit criteria before handing off. The How and Exec phases below default to Plastic's native dispatch (`plastic-executing-plan`) and delegate to the superpowers skills only when they are available or the user asks; do not restate the phase mechanics here.
|
|
68
|
+
|
|
69
|
+
Spawn preamble (live-state injection): before dispatching any specialist, run `scripts/spawn-preamble <intent_dir> --role <role>` and PREPEND its output to that specialist's prompt. The preamble is a deterministic, filesystem-only snapshot of the active intent (id, intent line, current stage) plus the honoring instruction, so every spawned agent boots with accurate live state instead of guessing. This is the authoritative L2 mechanism for harnesses whose sub-agents do not inherit a top-level session event (see `docs/reference/harness-adapters.md`).
|
|
70
|
+
|
|
71
|
+
Final-gate review: dispatch an independent reviewer subagent at the final gate only, not as a standing role.
|
|
72
|
+
|
|
73
|
+
Headless manual gate: when running headless or in the background, enforce gates manually and do not rely on hooks, because `CLAUDE_SESSION_ID` may be unset (this ties to the arm-gate fallback above).
|
|
74
|
+
|
|
75
|
+
Solo fallback: if the harness has no subagent dispatch, fall back to a single agent walking the full What, Why, How, Exec cycle yourself. This preserves current behavior.
|
|
76
|
+
|
|
23
77
|
## Stage-Aware Entry
|
|
24
78
|
|
|
25
79
|
Read the active intent's directory. Determine current lifecycle stage from filesystem state:
|
|
@@ -67,7 +121,7 @@ If the plan calls for creating a new project (the intent is an implementation in
|
|
|
67
121
|
1. Determine project path from `~/.plastic/config.yml` `project_roots` or from intent context
|
|
68
122
|
2. **Confirm path with user** — this is the ONE human interaction during auto delivery:
|
|
69
123
|
> "Creating project `<slug>` at `<path>`. Confirm path, or provide alternative."
|
|
70
|
-
3. Invoke `plastic
|
|
124
|
+
3. Invoke `plastic-creating-project` skill
|
|
71
125
|
4. The global intent is now Completed (creating-project handles this)
|
|
72
126
|
5. The tactical mirror in the project store becomes the active intent
|
|
73
127
|
6. Continue execution from the project directory using the tactical intent
|
|
@@ -75,7 +129,7 @@ If the plan calls for creating a new project (the intent is an implementation in
|
|
|
75
129
|
## Exec Phase
|
|
76
130
|
|
|
77
131
|
1. If `superpowers:subagent-driven-development` or `superpowers:executing-plans` is available, delegate execution to it
|
|
78
|
-
2. Otherwise invoke `plastic
|
|
132
|
+
2. Otherwise invoke `plastic-executing-plan`
|
|
79
133
|
3. Execute actions from checklist sequentially
|
|
80
134
|
4. Check off items in `checklist.md` as completed
|
|
81
135
|
5. Append observations to `## Insights` with `(autonomous)` marker
|
|
@@ -124,13 +178,25 @@ During initial project creation, all decisions are non-destructive by definition
|
|
|
124
178
|
- `fix_and_retry` — attempt to fix the failure, re-run verify (max 2 retries)
|
|
125
179
|
- `stop` — write `savepoint.md` with current state, notify user: "Verify failed — savepoint written.", **STOP**
|
|
126
180
|
- `manual` — notify user: "Verify failed: [summary]. Resolve manually."
|
|
127
|
-
5. If `release.on_green` has items, invoke `plastic
|
|
181
|
+
5. If `release.on_green` has items, invoke `plastic-releasing` to handle them (tag, changelog, publish, etc.). Do NOT duplicate release logic — delegate entirely.
|
|
128
182
|
5. Review `## Insights` for observations that should spawn future intents. If any:
|
|
129
|
-
- Create them (using `plastic
|
|
183
|
+
- Create them (using `plastic-creating-intent` conventions)
|
|
130
184
|
- Update `chain` in the current intent's frontmatter
|
|
131
185
|
6. Move intent from `## Active` to `## Completed` in INDEX.md (with today's date)
|
|
132
186
|
7. Auto-commit: `cd <store-root> && git add . && git commit -m "feat: deliver intent <ID> — <name>"`
|
|
133
|
-
8.
|
|
187
|
+
8. Refresh the QMD search index for this store (optional, no-op when QMD is absent):
|
|
188
|
+
```bash
|
|
189
|
+
ruby ~/.plastic/scripts/qmd-sync reindex --store <store-root>
|
|
190
|
+
```
|
|
191
|
+
Delivery is the lifecycle event that keeps the search index fresh. `<store-root>` is the
|
|
192
|
+
store that holds this intent (the global store or the project store).
|
|
193
|
+
9. Disarm the lifecycle gate (auto delivery is finished):
|
|
194
|
+
```bash
|
|
195
|
+
ruby -r ~/.plastic/scripts/lib/bridge -e 'Bridge.disarm_auto(ENV["CLAUDE_SESSION_ID"])'
|
|
196
|
+
```
|
|
197
|
+
Disarming also purges stale bridge files from the temp directory automatically (it keeps the
|
|
198
|
+
current bridge and any live run), so no manual `/tmp` cleanup is needed.
|
|
199
|
+
10. Notify user: "Intent [ID] — [name] delivered. [1-2 sentence summary]. See outcome.md for details."
|
|
134
200
|
|
|
135
201
|
## Error Handling
|
|
136
202
|
|
|
@@ -139,3 +205,7 @@ If the agent gets stuck (can't resolve a gap, dependency is missing, tests fail
|
|
|
139
205
|
2. Write `savepoint.md` with current state
|
|
140
206
|
3. Notify user: "Blocked on intent [ID] — [name]: [description]. Savepoint written."
|
|
141
207
|
4. **STOP.** Do not attempt workarounds that could leave the project in a broken state.
|
|
208
|
+
|
|
209
|
+
## References
|
|
210
|
+
|
|
211
|
+
- Read `references/agent-architecture.md` for the full team model (the 5-role enforcer-led team, per-stage handoffs, gate ownership, headless note, solo fallback) and the orchestrator hierarchy (Main Orchestrator, Project Orchestrators, coordination loop) when spinning up the team or understanding autonomous delivery scope
|