@zalom/plastic 2.0.0-alpha.9 → 2.0.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 +13 -136
- package/README.md +357 -133
- package/agents/plastic-enforcer.md +21 -16
- package/agents/plastic-executor.md +15 -4
- package/agents/plastic-node-research.md +30 -0
- package/agents/plastic-node-verify.md +28 -0
- package/agents/plastic-node-work.md +33 -0
- package/agents/{plastic-advisor.md → plastic-primary-advisor.md} +7 -9
- package/agents/{plastic-faux-advisor.md → plastic-secondary-advisor.md} +9 -12
- package/assets/plastic-logo.svg +1 -0
- package/bin/crap +4 -0
- package/bin/lib/context_budget.rb +43 -6
- package/bin/lib/skill_census.rb +839 -0
- package/bin/plastic +8 -0
- package/bin/plastic-skill-census +114 -0
- package/bin/test +24 -4
- package/bin/verify-change +345 -0
- package/config_asks.yml +4 -4
- package/deprecations.yml +1 -1
- package/{skills/agent-advisor/references → docs/help}/advisor-protocol.md +19 -24
- package/{skills/auto/references → docs/help}/agent-architecture.md +16 -14
- package/docs/help/completion-and-done.md +53 -0
- package/docs/help/human-report-contract.md +152 -0
- package/{skills/conventions/references → docs/help}/knowledge-graph.md +9 -0
- package/{skills/conventions/references → docs/help}/locks-and-worktrees.md +11 -13
- package/{skills/conventions/references → docs/help}/maintenance-and-revisions.md +1 -1
- package/{skills/conventions/references → docs/help}/roadmaps.md +5 -4
- package/{skills/tutorial/references → docs/help}/track-1-guided.md +28 -47
- package/{skills/tutorial/references → docs/help}/track-2-auto.md +8 -8
- package/{skills/tutorial/references → docs/help}/track-3-projects-and-roadmaps.md +24 -17
- package/hooks/call-budget +4 -0
- package/hooks/hooks.json +24 -0
- package/hooks/message-display +55 -2
- package/hooks/session-start +5 -1
- package/hooks/statusline +32 -27
- package/hooks/stop +5 -0
- package/package.json +5 -3
- package/scripts/append-ledger +2 -1
- package/scripts/dashboard.rb +267 -16
- package/scripts/day-summary +2 -1
- package/scripts/doctor.rb +680 -39
- package/scripts/end-intent +378 -56
- package/scripts/exec-worktree +5 -5
- package/scripts/file-session-intent +2 -1
- package/scripts/graph-measure +249 -0
- package/scripts/hook-call-budget +222 -0
- package/scripts/hook-capture +24 -123
- package/scripts/hook-close +2 -1
- package/scripts/hook-message-display +7 -0
- package/scripts/hook-record +24 -16
- package/scripts/hook-savepoint +27 -3
- package/scripts/hook-session-start +359 -321
- package/scripts/hook-stop +58 -0
- package/scripts/index-projection +74 -0
- package/scripts/insight-append +17 -4
- package/scripts/install.rb +9 -7
- package/scripts/lib/action_graph_shim.rb +279 -0
- package/scripts/lib/active_delivery.rb +105 -0
- package/scripts/lib/agent_models.rb +63 -25
- package/scripts/lib/arm.rb +74 -91
- package/scripts/lib/atomic_write.rb +31 -0
- package/scripts/lib/backup.rb +65 -0
- package/scripts/lib/cli/command.rb +92 -0
- package/scripts/lib/cli/commands/auto.rb +18 -0
- package/scripts/lib/cli/commands/auto_brief.rb +44 -0
- package/scripts/lib/cli/commands/auto_lock.rb +111 -0
- package/scripts/lib/cli/commands/auto_report.rb +50 -0
- package/scripts/lib/cli/commands/auto_take.rb +68 -0
- package/scripts/lib/cli/commands/backup.rb +43 -0
- package/scripts/lib/cli/commands/checkout.rb +25 -0
- package/scripts/lib/cli/commands/continue.rb +66 -0
- package/scripts/lib/cli/commands/doctor.rb +81 -0
- package/scripts/lib/cli/commands/feedback.rb +40 -0
- package/scripts/lib/cli/commands/help.rb +69 -0
- package/scripts/lib/cli/commands/hook.rb +32 -0
- package/scripts/lib/cli/commands/index.rb +23 -0
- package/scripts/lib/cli/commands/install.rb +21 -0
- package/scripts/lib/cli/commands/installer_verb.rb +37 -0
- package/scripts/lib/cli/commands/intent.rb +19 -0
- package/scripts/lib/cli/commands/intent_answer.rb +37 -0
- package/scripts/lib/cli/commands/intent_command.rb +58 -0
- package/scripts/lib/cli/commands/intent_end.rb +75 -0
- package/scripts/lib/cli/commands/intent_new.rb +62 -0
- package/scripts/lib/cli/commands/intent_note.rb +43 -0
- package/scripts/lib/cli/commands/intent_rule.rb +36 -0
- package/scripts/lib/cli/commands/intent_show.rb +32 -0
- package/scripts/lib/cli/commands/intent_spec.rb +44 -0
- package/scripts/lib/cli/commands/intent_step.rb +79 -0
- package/scripts/lib/cli/commands/intent_verify.rb +26 -0
- package/scripts/lib/cli/commands/migrate.rb +16 -0
- package/scripts/lib/cli/commands/migrate_stores.rb +31 -0
- package/scripts/lib/cli/commands/next.rb +54 -0
- package/scripts/lib/cli/commands/project.rb +19 -0
- package/scripts/lib/cli/commands/project_links.rb +46 -0
- package/scripts/lib/cli/commands/project_list.rb +20 -0
- package/scripts/lib/cli/commands/project_new.rb +95 -0
- package/scripts/lib/cli/commands/query.rb +31 -0
- package/scripts/lib/cli/commands/render.rb +35 -0
- package/scripts/lib/cli/commands/roadmap.rb +19 -0
- package/scripts/lib/cli/commands/roadmap_check.rb +54 -0
- package/scripts/lib/cli/commands/roadmap_log.rb +54 -0
- package/scripts/lib/cli/commands/roadmap_migrate.rb +53 -0
- package/scripts/lib/cli/commands/roadmap_next.rb +77 -0
- package/scripts/lib/cli/commands/roadmap_show.rb +65 -0
- package/scripts/lib/cli/commands/rollback.rb +20 -0
- package/scripts/lib/cli/commands/search.rb +60 -0
- package/scripts/lib/cli/commands/session.rb +18 -0
- package/scripts/lib/cli/commands/session_commit.rb +42 -0
- package/scripts/lib/cli/commands/session_handoff.rb +34 -0
- package/scripts/lib/cli/commands/session_summary.rb +35 -0
- package/scripts/lib/cli/commands/status.rb +68 -0
- package/scripts/lib/cli/commands/subcommand_list.rb +36 -0
- package/scripts/lib/cli/commands/sync.rb +55 -0
- package/scripts/lib/cli/commands/uninstall.rb +20 -0
- package/scripts/lib/cli/commands/update.rb +20 -0
- package/scripts/lib/cli/commands/version.rb +53 -0
- package/scripts/lib/cli/frontier.rb +86 -0
- package/scripts/lib/cli/intent_progress.rb +36 -0
- package/scripts/lib/cli/legacy.rb +80 -0
- package/scripts/lib/cli/output.rb +137 -0
- package/scripts/lib/cli/scope.rb +134 -0
- package/scripts/lib/cli/table.rb +65 -0
- package/scripts/lib/cli.rb +94 -0
- package/scripts/lib/codex_adapter.rb +198 -0
- package/scripts/lib/compact_instructions.rb +13 -5
- package/scripts/lib/core_integrity.rb +71 -0
- package/scripts/lib/dashboard_screen.rb +40 -0
- package/scripts/lib/data_boundary.rb +132 -0
- package/scripts/lib/day_summary.rb +23 -14
- package/scripts/lib/doctor_core.rb +114 -38
- package/scripts/lib/doctor_session_ledger.rb +5 -53
- package/scripts/lib/engine_permissions.rb +88 -0
- package/scripts/lib/exec_worktree.rb +24 -21
- package/scripts/lib/feedback_report.rb +1 -1
- package/scripts/lib/graph_edges.rb +137 -0
- package/scripts/lib/graph_file.rb +246 -0
- package/scripts/lib/graph_measure.rb +645 -0
- package/scripts/lib/graph_measure_budget.rb +409 -0
- package/scripts/lib/graph_measure_cohorts.rb +487 -0
- package/scripts/lib/graph_measure_models.rb +413 -0
- package/scripts/lib/graph_measure_report.rb +532 -0
- package/scripts/lib/graph_tree.rb +98 -0
- package/scripts/lib/guarded_append.rb +155 -0
- package/scripts/lib/handoff.rb +40 -13
- package/scripts/lib/harness_adapter.rb +184 -0
- package/scripts/lib/hook_registry.rb +27 -3
- package/scripts/lib/hook_replay.rb +235 -0
- package/scripts/lib/index_entry.rb +62 -0
- package/scripts/lib/index_projection.rb +201 -0
- package/scripts/lib/insights.rb +1 -1
- package/scripts/lib/installer_core.rb +543 -71
- package/scripts/lib/intent_screen.rb +4 -4
- package/scripts/lib/intent_screen_ansi.rb +73 -12
- package/scripts/lib/intent_validator.rb +2 -2
- package/scripts/lib/lock.rb +10 -11
- package/scripts/lib/message_display.rb +350 -54
- package/scripts/lib/meter_watch.rb +185 -0
- package/scripts/lib/node_file.rb +234 -0
- package/scripts/lib/node_ids.rb +99 -0
- package/scripts/lib/node_input.rb +921 -0
- package/scripts/lib/node_input_compatibility.rb +62 -0
- package/scripts/lib/node_ledger.rb +386 -0
- package/scripts/lib/node_progress.rb +153 -0
- package/scripts/lib/node_return.rb +204 -0
- package/scripts/lib/node_worktree.rb +337 -0
- package/scripts/lib/outcome_report.rb +440 -0
- package/scripts/lib/preflight.rb +4 -6
- package/scripts/lib/project_config.rb +46 -0
- package/scripts/lib/project_validator.rb +3 -2
- package/scripts/lib/qmd_sync.rb +8 -7
- package/scripts/lib/ready_set.rb +462 -0
- package/scripts/lib/reference_archive.rb +45 -0
- package/scripts/lib/release_guard.rb +18 -0
- package/scripts/lib/report_screen.rb +1371 -47
- package/scripts/lib/rlm/corpus.rb +13 -0
- package/scripts/lib/rlm/probe.rb +29 -0
- package/scripts/lib/rlm/query.rb +22 -0
- package/scripts/lib/roadmap_graph.rb +210 -0
- package/scripts/lib/roadmap_migration.rb +95 -0
- package/scripts/lib/roadmap_queue.rb +170 -12
- package/scripts/lib/roadmap_render.rb +150 -0
- package/scripts/lib/roadmap_savepoint.rb +64 -14
- package/scripts/lib/runner_absorb.rb +703 -0
- package/scripts/lib/runner_answer.rb +206 -0
- package/scripts/lib/runner_core.rb +194 -0
- package/scripts/lib/runner_dispatch.rb +526 -0
- package/scripts/lib/runner_policy.rb +191 -0
- package/scripts/lib/runner_proposals.rb +275 -0
- package/scripts/lib/runner_rewind.rb +201 -0
- package/scripts/lib/runner_sweep.rb +231 -0
- package/scripts/lib/runner_until_empty.rb +252 -0
- package/scripts/lib/runner_watch.rb +389 -0
- package/scripts/lib/savepoint.rb +141 -19
- package/scripts/lib/scaffold_intent.rb +6 -3
- package/scripts/lib/screen_paint.rb +365 -28
- package/scripts/lib/screens/dashboard.rb +20 -0
- package/scripts/lib/screens/plan.rb +18 -0
- package/scripts/lib/screens/roadmap.rb +15 -0
- package/scripts/lib/search_index.rb +55 -0
- package/scripts/lib/session_close.rb +30 -28
- package/scripts/lib/session_git.rb +67 -19
- package/scripts/lib/session_ledger.rb +48 -4
- package/scripts/lib/session_usage.rb +190 -0
- package/scripts/lib/sqlite.rb +22 -0
- package/scripts/lib/stop_gate.rb +95 -0
- package/scripts/lib/store_discovery.rb +7 -6
- package/scripts/lib/store_layout.rb +54 -0
- package/scripts/lib/store_provisioning.rb +2 -1
- package/scripts/lib/store_sync.rb +85 -0
- package/scripts/lib/stores_move.rb +93 -0
- package/scripts/lib/untouched_scaffold.rb +51 -0
- package/scripts/lib/verify_intent.rb +36 -8
- package/scripts/lib/version_number.rb +48 -0
- package/scripts/lib/work_graph.rb +59 -0
- package/scripts/lib/work_graph_validator.rb +201 -0
- package/scripts/lib/worktree.rb +27 -32
- package/scripts/lib/worktree_sweep.rb +6 -5
- package/scripts/link-suggest +2 -1
- package/scripts/meter-watch +57 -0
- package/scripts/migrate-to-global +1 -1
- package/scripts/new-intent +4 -13
- package/scripts/node-input +92 -0
- package/scripts/node-run +225 -0
- package/scripts/node-transition +291 -0
- package/scripts/outcome-report +74 -0
- package/scripts/plastic-lock +65 -60
- package/scripts/project-links +6 -21
- package/scripts/promote-session-item +3 -2
- package/scripts/read-config +53 -9
- package/scripts/ready-set +126 -0
- package/scripts/release-check +123 -0
- package/scripts/report-screen +177 -16
- package/scripts/roadmap-graph +125 -0
- package/scripts/roadmap-savepoint +7 -0
- package/scripts/rollback.rb +5 -1
- package/scripts/runner +581 -0
- package/scripts/savepoint-note +11 -9
- package/scripts/session-commit +2 -1
- package/scripts/session-usage +56 -0
- package/scripts/skill-lint +115 -6
- package/scripts/spawn-preamble +2 -2
- package/scripts/update.rb +31 -4
- package/scripts/validate-work-graph +39 -0
- package/scripts/verify-intent +3 -2
- package/scripts/write-handoff +2 -1
- package/templates/agents.md +7 -7
- package/templates/config.yml +16 -9
- package/templates/dashboard-screen.md +22 -0
- package/templates/display-fixture.md +21 -0
- package/templates/graph.md +16 -0
- package/templates/index.md +1 -1
- package/templates/intent-screen.md +1 -1
- package/templates/node-decision.md +11 -0
- package/templates/node-research.md +13 -0
- package/templates/node-verify.md +13 -0
- package/templates/node-work.md +22 -0
- package/templates/outcome.md +8 -3
- package/templates/project.yml +1 -1
- package/templates/render.css +10 -0
- package/templates/report-plan.md +15 -0
- package/templates/report-roadmap-delivered.md +10 -0
- package/templates/report-roadmap-plan.md +9 -0
- package/templates/report-roadmap-state.md +9 -0
- package/templates/report-state.md +1 -1
- package/templates/roadmap.md +13 -0
- package/bin/plastic.js +0 -70
- package/scripts/lib/bridge.rb +0 -116
- package/skills/agent-advisor/SKILL.md +0 -92
- package/skills/auto/SKILL.md +0 -296
- package/skills/auto/evals/evals.json +0 -255
- package/skills/auto/references/end-tail.md +0 -66
- package/skills/auto/references/human-report-contract.md +0 -78
- package/skills/conventions/SKILL.md +0 -29
- package/skills/conventions/references/completion-and-done.md +0 -43
- package/skills/dashboard/SKILL.md +0 -169
- package/skills/dashboard/evals/evals.json +0 -38
- package/skills/dashboard/references/classification.md +0 -22
- package/skills/dashboard/templates/dashboard-global.md +0 -20
- package/skills/dashboard/templates/dashboard-project.md +0 -19
- package/skills/direct/SKILL.md +0 -66
- package/skills/direct/references/request-signals.md +0 -59
- package/skills/doctor/SKILL.md +0 -299
- package/skills/doctor/report.md +0 -102
- package/skills/feedback/SKILL.md +0 -98
- package/skills/feedback/references/transport-and-privacy.md +0 -65
- package/skills/feedback/report.md +0 -36
- package/skills/install/SKILL.md +0 -217
- package/skills/intent-continuing/SKILL.md +0 -154
- package/skills/intent-continuing/references/board-fill.md +0 -43
- package/skills/intent-continuing/references/boarding-matrix.md +0 -34
- package/skills/intent-continuing/references/context-management.md +0 -28
- package/skills/intent-continuing/references/liveness-ranking.md +0 -57
- package/skills/intent-creating/SKILL.md +0 -164
- package/skills/intent-creating/evals/evals.json +0 -72
- package/skills/intent-creating/references/lifecycle.md +0 -81
- package/skills/intent-creating/references/wikilinks.md +0 -8
- package/skills/intent-ending/SKILL.md +0 -176
- package/skills/intent-ending/evals/evals.json +0 -74
- package/skills/intent-executing/SKILL.md +0 -170
- package/skills/intent-executing/evals/evals.json +0 -66
- package/skills/intent-executing/implementer-prompt.md +0 -42
- package/skills/intent-executing/spec-reviewer-prompt.md +0 -27
- package/skills/intent-speccing/SKILL.md +0 -130
- package/skills/intent-speccing/evals/evals.json +0 -126
- package/skills/intent-speccing/references/design-principles.md +0 -44
- package/skills/intent-speccing/references/per-section-fill-rules.md +0 -92
- package/skills/intent-speccing/references/self-verify-checklist.md +0 -37
- package/skills/project-creating/SKILL.md +0 -162
- package/skills/project-creating/references/hubs-projects.md +0 -55
- package/skills/project-creating/references/project-scaffolding.md +0 -97
- package/skills/releasing/SKILL.md +0 -337
- package/skills/releasing/references/deprecations.md +0 -60
- package/skills/releasing/references/promotion-and-tagging.md +0 -66
- package/skills/releasing/references/release-lines.md +0 -105
- package/skills/roadmap/SKILL.md +0 -64
- package/skills/roadmap/references/file-format.md +0 -124
- package/skills/roadmap/references/operations.md +0 -112
- package/skills/rollback/SKILL.md +0 -91
- package/skills/tutorial/SKILL.md +0 -65
- package/skills/tutorial/evals/evals.json +0 -186
- package/skills/uninstall/SKILL.md +0 -75
- package/skills/update/SKILL.md +0 -126
- /package/{skills/auto/references → docs/help}/agent-report-contract.md +0 -0
- /package/{skills/intent-executing → docs/help}/code-quality-reviewer-prompt.md +0 -0
- /package/{skills/conventions/references → docs/help}/lifecycle-and-savepoints.md +0 -0
- /package/{skills/intent-executing → docs/help}/plan-reviewer-prompt.md +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../command"
|
|
4
|
+
require_relative "../legacy"
|
|
5
|
+
|
|
6
|
+
module Plastic
|
|
7
|
+
class CLI
|
|
8
|
+
module Commands
|
|
9
|
+
class InstallerVerb < Command
|
|
10
|
+
def initialize(argv, runner: nil, **streams)
|
|
11
|
+
super(argv, **streams)
|
|
12
|
+
@runner = runner
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call
|
|
16
|
+
unknown = arguments.grep(/\A-/) - self.class::FLAGS
|
|
17
|
+
raise Usage, "#{unknown.first} is not a flag of this command" unless unknown.empty?
|
|
18
|
+
|
|
19
|
+
status = legacy.run(self.class::SCRIPT, *arguments)
|
|
20
|
+
raise Failure, "#{self.class::SCRIPT} exited #{status}" unless status.zero?
|
|
21
|
+
|
|
22
|
+
@output.next_step(self.class::AFTER, because: self.class::BECAUSE)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def options
|
|
28
|
+
@options ||= {}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def legacy
|
|
32
|
+
@legacy ||= Legacy.new(env: @env, runner: @runner, output: @output, json: options[:json])
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "subcommand_list"
|
|
4
|
+
|
|
5
|
+
# `plastic intent` - the list of `plastic intent ...` subcommands, straight out
|
|
6
|
+
# of the table; a word that names none of them is a usage error naming the
|
|
7
|
+
# same list, so a typo never falls through to a script.
|
|
8
|
+
module Plastic
|
|
9
|
+
class CLI
|
|
10
|
+
module Commands
|
|
11
|
+
class Intent < SubcommandList
|
|
12
|
+
USAGE_LINE = "plastic intent SUBCOMMAND [options]"
|
|
13
|
+
PREFIX = "intent"
|
|
14
|
+
NEXT = "plastic intent show ID"
|
|
15
|
+
BECAUSE = "an id is the one thing every other subcommand needs"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "intent_command"
|
|
4
|
+
|
|
5
|
+
# `plastic intent answer` - closes a `needs_decision` node with the owner's
|
|
6
|
+
# ruling (`runner answer`, run through Legacy).
|
|
7
|
+
module Plastic
|
|
8
|
+
class CLI
|
|
9
|
+
module Commands
|
|
10
|
+
class IntentAnswer < IntentCommand
|
|
11
|
+
USAGE_LINE = 'plastic intent answer ID --node NODE --decision "TEXT" [--json]'
|
|
12
|
+
|
|
13
|
+
SCRIPT = "runner"
|
|
14
|
+
AFTER = "plastic intent step ID"
|
|
15
|
+
BECAUSE = "step resumes the graph past the node this decision just closed"
|
|
16
|
+
|
|
17
|
+
def call
|
|
18
|
+
raise Usage, "--node is required" if options[:node].to_s.empty?
|
|
19
|
+
raise Usage, "--decision is required" if options[:decision].to_s.empty?
|
|
20
|
+
|
|
21
|
+
super
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def switches(parser)
|
|
27
|
+
parser.on("--node ID") { |v| @options[:node] = v }
|
|
28
|
+
parser.on("--decision TEXT") { |v| @options[:decision] = v }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def script_arguments
|
|
32
|
+
["answer", intent_dir, "--node", options[:node], "--answer", options[:decision]]
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../command"
|
|
4
|
+
require_relative "../legacy"
|
|
5
|
+
|
|
6
|
+
# Plastic::CLI::Commands::IntentCommand (intent 372, family 2) - the shared
|
|
7
|
+
# base every `plastic intent ...` subcommand runs on: it turns the positional
|
|
8
|
+
# id into an intent directory through Scope, runs the one script the
|
|
9
|
+
# subclass names, and prints the `next:`/`because:` trailer the plan calls
|
|
10
|
+
# for. A subcommand that needs more than that (extra usage checks, printed
|
|
11
|
+
# guidance, a different script-exit contract) overrides `call`.
|
|
12
|
+
module Plastic
|
|
13
|
+
class CLI
|
|
14
|
+
module Commands
|
|
15
|
+
class IntentCommand < Command
|
|
16
|
+
def initialize(argv, runner: nil, **streams)
|
|
17
|
+
super(argv, **streams)
|
|
18
|
+
@runner = runner
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def call
|
|
22
|
+
status = legacy.run(self.class::SCRIPT, *script_arguments)
|
|
23
|
+
raise Failure, "#{self.class::SCRIPT} exited #{status}" unless status.zero?
|
|
24
|
+
|
|
25
|
+
command, reason = after_run
|
|
26
|
+
@output.next_step(command, because: reason)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def after_run
|
|
32
|
+
[self.class::AFTER.sub("ID", id.to_s), self.class::BECAUSE]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def id
|
|
36
|
+
arguments.first
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def intent_dir
|
|
40
|
+
return @intent_dir if defined?(@intent_dir)
|
|
41
|
+
|
|
42
|
+
@intent_dir = scope.intent_dir(id)
|
|
43
|
+
return @intent_dir if @intent_dir
|
|
44
|
+
|
|
45
|
+
raise Failure, "no intent #{id.inspect} in #{scope.slug}; plastic status lists the ones that exist"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def legacy
|
|
49
|
+
@legacy ||= Legacy.new(env: @env, runner: @runner, output: @output, json: options[:json])
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def script_arguments
|
|
53
|
+
raise NoMethodError, "#{self.class} must define script_arguments"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "intent_command"
|
|
4
|
+
|
|
5
|
+
# `plastic intent end` - closes an intent as delivered or abandoned
|
|
6
|
+
# (`end-intent`, run through Legacy). end-intent's own exit code 4 is a
|
|
7
|
+
# pre-flight refusal (a live foreign session holds the delivery lock), not
|
|
8
|
+
# the exit 3 Legacy's generic contract turns into a Refusal on its own, so
|
|
9
|
+
# this command maps it by hand rather than through Legacy#run.
|
|
10
|
+
module Plastic
|
|
11
|
+
class CLI
|
|
12
|
+
module Commands
|
|
13
|
+
class IntentEnd < IntentCommand
|
|
14
|
+
USAGE_LINE = 'plastic intent end ID --delivered|--abandoned --summary "TEXT" [--note "TEXT"] [--dry-run] [--json]'
|
|
15
|
+
|
|
16
|
+
REFUSAL_STATUS = 4
|
|
17
|
+
|
|
18
|
+
# end-intent's own refusals the agent can act on: the reason is theirs to
|
|
19
|
+
# fix, so they stay failures (exit 1), but each is named rather than
|
|
20
|
+
# surfaced as a bare exit code.
|
|
21
|
+
REFUSED = {
|
|
22
|
+
7 => "end-intent refused a hollow delivered close: write outcome.md's ## Delivered rows to match the action headings",
|
|
23
|
+
8 => "end-intent refused to deliver an untouched scaffold: do the work first, or close it with --abandoned",
|
|
24
|
+
9 => "end-intent refused a delivered close whose code is not merged: run the git merge it names in the repo, then run the close again"
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
SUMMARY_GUIDANCE = [
|
|
28
|
+
"a good summary is written for the reader deciding whether to merge, release, or accept",
|
|
29
|
+
"delivered: what shipped, impact and risk first, in plain language, never the checklist",
|
|
30
|
+
"abandoned: why, and the trail if part of the work was dropped mid-flight"
|
|
31
|
+
].freeze
|
|
32
|
+
|
|
33
|
+
def call
|
|
34
|
+
raise Usage, "choose only one of --delivered or --abandoned" if options[:delivered] && options[:abandoned]
|
|
35
|
+
raise Usage, "one of --delivered or --abandoned is required" unless disposition
|
|
36
|
+
raise Usage, SUMMARY_GUIDANCE.join("\n") if options[:summary].to_s.empty?
|
|
37
|
+
|
|
38
|
+
status = legacy.run("end-intent", *end_intent_arguments)
|
|
39
|
+
raise Refusal, "end-intent needs the owner: the delivery lock is held" if status == REFUSAL_STATUS
|
|
40
|
+
raise Failure, REFUSED.fetch(status) { "end-intent exited #{status}" } unless status.zero?
|
|
41
|
+
|
|
42
|
+
if options[:dry_run]
|
|
43
|
+
@output.next_step("none", because: "the dry run wrote nothing and found nothing that would refuse the close")
|
|
44
|
+
else
|
|
45
|
+
@output.next_step("plastic status", because: "the intent moved out of Active")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def disposition
|
|
52
|
+
return "delivered" if options[:delivered]
|
|
53
|
+
"abandoned" if options[:abandoned]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def switches(parser)
|
|
57
|
+
parser.on("--delivered") { @options[:delivered] = true }
|
|
58
|
+
parser.on("--abandoned") { @options[:abandoned] = true }
|
|
59
|
+
parser.on("--summary TEXT") { |v| @options[:summary] = v }
|
|
60
|
+
parser.on("--note TEXT") { |v| @options[:note] = v }
|
|
61
|
+
parser.on("--dry-run") { @options[:dry_run] = true }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def end_intent_arguments
|
|
65
|
+
intent_dir
|
|
66
|
+
args = ["--store", scope.store, "--id", id, "--disposition", disposition,
|
|
67
|
+
"--outcome-summary", options[:summary]]
|
|
68
|
+
args += ["--index-note", options[:note]] if options[:note]
|
|
69
|
+
args << "--dry-run" if options[:dry_run]
|
|
70
|
+
args
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../command"
|
|
4
|
+
require_relative "../legacy"
|
|
5
|
+
require_relative "../../index_entry"
|
|
6
|
+
|
|
7
|
+
module Plastic
|
|
8
|
+
class CLI
|
|
9
|
+
module Commands
|
|
10
|
+
class IntentNew < Command
|
|
11
|
+
USAGE_LINE = 'plastic intent new "LINE" [--slug SLUG] [--parent ID] [--sources IDS] [--tags TAGS] [--json]'
|
|
12
|
+
PASSED_THROUGH = %i[parent sources tags].freeze
|
|
13
|
+
|
|
14
|
+
def initialize(argv, runner: nil, **streams)
|
|
15
|
+
super(argv, **streams)
|
|
16
|
+
@runner = runner
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def call
|
|
20
|
+
raise Usage, "the one-line intent statement is required" if line.to_s.empty?
|
|
21
|
+
|
|
22
|
+
status = Legacy.new(env: @env, runner: @runner, output: @output, json: options[:json]).run("new-intent", *new_intent_arguments)
|
|
23
|
+
raise Failure, "new-intent exited #{status}" unless status.zero?
|
|
24
|
+
|
|
25
|
+
IndexEntry.add_active(scope.index_path, dir_name: dir_name, title: line)
|
|
26
|
+
@output.next_step("plastic intent spec #{id}", because: "a new intent has no specification yet")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def line
|
|
32
|
+
arguments.first
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def slug
|
|
36
|
+
options[:slug] || line.downcase.scan(/[a-z0-9]+/).first(5).join("-")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def dir_name
|
|
40
|
+
newest = Dir.glob(File.join(scope.store, "*--#{slug}")).max_by { |path| File.mtime(path) }
|
|
41
|
+
raise Failure, "new-intent made no directory for #{slug}" unless newest
|
|
42
|
+
|
|
43
|
+
File.basename(newest)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def id
|
|
47
|
+
dir_name.split("--").first
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def switches(parser)
|
|
51
|
+
parser.on("--slug SLUG") { |v| @options[:slug] = v }
|
|
52
|
+
PASSED_THROUGH.each { |key| parser.on("--#{key} VALUE") { |v| @options[key] = v } }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def new_intent_arguments
|
|
56
|
+
given = PASSED_THROUGH.select { |key| options[key] }
|
|
57
|
+
["--store", scope.store, "--intent", line, "--slug", slug] + given.flat_map { |key| ["--#{key}", options[key]] }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "intent_command"
|
|
4
|
+
|
|
5
|
+
# `plastic intent note` - appends a savepoint line to an intent
|
|
6
|
+
# (`savepoint-note`, run through Legacy). `--kind` defaults to Report, the
|
|
7
|
+
# most general of the three savepoint kinds, for a note that names no kind
|
|
8
|
+
# of its own.
|
|
9
|
+
module Plastic
|
|
10
|
+
class CLI
|
|
11
|
+
module Commands
|
|
12
|
+
class IntentNote < IntentCommand
|
|
13
|
+
USAGE_LINE = 'plastic intent note ID "TEXT" [--kind Review|Commit|Report] [--json]'
|
|
14
|
+
|
|
15
|
+
SCRIPT = "savepoint-note"
|
|
16
|
+
AFTER = "plastic intent show ID"
|
|
17
|
+
BECAUSE = "the state screen carries the savepoint trail this just wrote"
|
|
18
|
+
|
|
19
|
+
DEFAULT_KIND = "Report"
|
|
20
|
+
|
|
21
|
+
def call
|
|
22
|
+
raise Usage, "the note text is required" if text.to_s.empty?
|
|
23
|
+
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def text
|
|
30
|
+
arguments[1]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def switches(parser)
|
|
34
|
+
parser.on("--kind KIND") { |v| @options[:kind] = v }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def script_arguments
|
|
38
|
+
[intent_dir, "--kind", options[:kind] || DEFAULT_KIND, "--text", text]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "intent_command"
|
|
4
|
+
|
|
5
|
+
# `plastic intent rule` - records one ruling in an intent's `## Insights`
|
|
6
|
+
# (`insight-append`, run through Legacy, stamped `--stage Why --author human`
|
|
7
|
+
# since a rule is always the owner's).
|
|
8
|
+
module Plastic
|
|
9
|
+
class CLI
|
|
10
|
+
module Commands
|
|
11
|
+
class IntentRule < IntentCommand
|
|
12
|
+
USAGE_LINE = 'plastic intent rule ID "TEXT" [--json]'
|
|
13
|
+
|
|
14
|
+
SCRIPT = "insight-append"
|
|
15
|
+
AFTER = "plastic intent show ID"
|
|
16
|
+
BECAUSE = "the state screen reads Insights straight off the ledger this just wrote"
|
|
17
|
+
|
|
18
|
+
def call
|
|
19
|
+
raise Usage, "the ruling text is required" if text.to_s.empty?
|
|
20
|
+
|
|
21
|
+
super
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def text
|
|
27
|
+
arguments[1]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def script_arguments
|
|
31
|
+
[intent_dir, text, "--stage", "Why", "--author", "human"]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "intent_command"
|
|
4
|
+
require_relative "../intent_progress"
|
|
5
|
+
|
|
6
|
+
# `plastic intent show` - an intent's state screen (`report-screen state`, run
|
|
7
|
+
# through Legacy).
|
|
8
|
+
module Plastic
|
|
9
|
+
class CLI
|
|
10
|
+
module Commands
|
|
11
|
+
class IntentShow < IntentCommand
|
|
12
|
+
USAGE_LINE = "plastic intent show ID [--json]"
|
|
13
|
+
|
|
14
|
+
SCRIPT = "report-screen"
|
|
15
|
+
AFTER = "plastic intent step ID"
|
|
16
|
+
BECAUSE = "the state screen is where the next step comes from"
|
|
17
|
+
|
|
18
|
+
def call
|
|
19
|
+
super
|
|
20
|
+
command, reason = IntentProgress.new(scope, id).decision
|
|
21
|
+
@output.next_step(command, because: reason)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def script_arguments
|
|
27
|
+
["state", intent_dir]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "intent_command"
|
|
4
|
+
|
|
5
|
+
# `plastic intent spec` - an intent's state screen (`report-screen state`,
|
|
6
|
+
# same as `plastic intent show`), then the five rules the speccing
|
|
7
|
+
# conversation itself runs on, so the reply carries both without a second
|
|
8
|
+
# call.
|
|
9
|
+
module Plastic
|
|
10
|
+
class CLI
|
|
11
|
+
module Commands
|
|
12
|
+
class IntentSpec < IntentCommand
|
|
13
|
+
USAGE_LINE = "plastic intent spec ID [--json]"
|
|
14
|
+
|
|
15
|
+
SCRIPT = "report-screen"
|
|
16
|
+
AFTER = 'plastic intent rule ID "TEXT"'
|
|
17
|
+
BECAUSE = "a ruling is recorded the moment it lands, never batched"
|
|
18
|
+
|
|
19
|
+
RULES = [
|
|
20
|
+
"1. context first: read the project state and the intent's Context and Insights",
|
|
21
|
+
"2. assess scope: several independent subsystems become separate thinking conversations",
|
|
22
|
+
"3. one question per message, in prose; look at the code before asking how something works",
|
|
23
|
+
"4. propose two or three approaches, trade-offs first, leading with the recommendation",
|
|
24
|
+
"5. present the design in sections scaled to complexity; get a ruling after each section",
|
|
25
|
+
"6. record every ruling the instant it lands, never batched:",
|
|
26
|
+
' plastic intent rule ID "<ruling text>"',
|
|
27
|
+
"7. a later ruling that conflicts with an earlier one gets a new insight naming it superseded",
|
|
28
|
+
"8. both rulings stay on record; the later one wins"
|
|
29
|
+
].freeze
|
|
30
|
+
|
|
31
|
+
def call
|
|
32
|
+
super
|
|
33
|
+
@output.row("rules", RULES)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def script_arguments
|
|
39
|
+
["state", intent_dir]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "intent_command"
|
|
4
|
+
require_relative "../intent_progress"
|
|
5
|
+
require_relative "../../runner_core"
|
|
6
|
+
|
|
7
|
+
# `plastic intent step` - runs the graph's next ready step (`runner step`, run
|
|
8
|
+
# through Legacy). Direct intents report their next checklist item. Graph
|
|
9
|
+
# execution requires ownership, resolved by the same rules as the runner.
|
|
10
|
+
module Plastic
|
|
11
|
+
class CLI
|
|
12
|
+
module Commands
|
|
13
|
+
class IntentStep < IntentCommand
|
|
14
|
+
USAGE_LINE = "plastic intent step ID [--return NODE=PATH] [--harness NAME] [--allow-core-drift] [--json]"
|
|
15
|
+
|
|
16
|
+
SCRIPT = "runner"
|
|
17
|
+
AFTER = "plastic intent step ID"
|
|
18
|
+
BECAUSE = "call step again after each dispatched node returns"
|
|
19
|
+
COMPLETE = "every node in the graph has finished"
|
|
20
|
+
|
|
21
|
+
def call
|
|
22
|
+
intent_dir
|
|
23
|
+
command, reason = IntentProgress.new(scope, id).decision
|
|
24
|
+
return @output.next_step(command, because: reason) if command == "none"
|
|
25
|
+
|
|
26
|
+
if graph?
|
|
27
|
+
return unless graph_owner?
|
|
28
|
+
|
|
29
|
+
super
|
|
30
|
+
elsif command == "plastic intent step #{id}"
|
|
31
|
+
item = IntentScreen.checklist_items(intent_dir).find { |entry| !entry[:done] }
|
|
32
|
+
@output.row("work", item[:text])
|
|
33
|
+
@output.row("checklist", File.join(intent_dir, "checklist.md"))
|
|
34
|
+
@output.next_step("none", because: "perform this checklist item, record its verification, then run plastic intent show #{id} --project #{scope.slug}")
|
|
35
|
+
else
|
|
36
|
+
@output.next_step(command, because: reason)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def switches(parser)
|
|
43
|
+
parser.on("--return NODE=PATH") { |pair| (@options[:returns] ||= []) << pair }
|
|
44
|
+
parser.on("--harness NAME") { |name| @options[:harness] = name }
|
|
45
|
+
parser.on("--allow-core-drift") { @options[:allow_core_drift] = true }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def graph_owner?
|
|
49
|
+
session = RunnerCore.resolve_owning_session(intent_dir, explicit: nil,
|
|
50
|
+
env_session: @env["CLAUDE_CODE_SESSION_ID"], store: scope.store, intent_id: id)
|
|
51
|
+
return true if session
|
|
52
|
+
raise Refusal, "intent #{id} is held by another session; inspect with plastic auto lock status #{id} --project #{scope.slug}" if Lock.read(intent_dir)
|
|
53
|
+
raise Refusal, "return not accepted: take intent #{id} with plastic auto take #{id} --project #{scope.slug} before submitting it" unless Array(options[:returns]).empty?
|
|
54
|
+
|
|
55
|
+
@output.next_step("plastic auto take #{id}", because: "graph execution requires this session to take the delivery lock")
|
|
56
|
+
false
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def after_run
|
|
60
|
+
return super unless RunnerCore.complete?(RunnerCore.context(intent_dir: intent_dir, home: File.dirname(scope.plastic_home)))
|
|
61
|
+
|
|
62
|
+
["plastic intent verify #{id}", COMPLETE]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def graph?
|
|
66
|
+
File.exist?(File.join(intent_dir, "graph.md"))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def script_arguments
|
|
70
|
+
args = ["step", intent_dir]
|
|
71
|
+
Array(options[:returns]).each { |pair| args.concat(["--return", pair]) }
|
|
72
|
+
args.concat(["--harness", options[:harness]]) if options[:harness]
|
|
73
|
+
args << "--allow-core-drift" if options[:allow_core_drift]
|
|
74
|
+
args
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "intent_command"
|
|
4
|
+
|
|
5
|
+
# `plastic intent verify` - the merge-gate checks one intent needs before a
|
|
6
|
+
# close is trustworthy (`verify-intent`, run through Legacy).
|
|
7
|
+
module Plastic
|
|
8
|
+
class CLI
|
|
9
|
+
module Commands
|
|
10
|
+
class IntentVerify < IntentCommand
|
|
11
|
+
USAGE_LINE = "plastic intent verify ID [--json]"
|
|
12
|
+
|
|
13
|
+
SCRIPT = "verify-intent"
|
|
14
|
+
AFTER = 'plastic intent end ID --delivered --summary "TEXT"'
|
|
15
|
+
BECAUSE = "a clean verify is what makes the close trustworthy"
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def script_arguments
|
|
20
|
+
intent_dir
|
|
21
|
+
["--store", scope.store, "--id", id]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "subcommand_list"
|
|
4
|
+
|
|
5
|
+
module Plastic
|
|
6
|
+
class CLI
|
|
7
|
+
module Commands
|
|
8
|
+
class Migrate < SubcommandList
|
|
9
|
+
USAGE_LINE = "plastic migrate SUBCOMMAND [options]"
|
|
10
|
+
PREFIX = "migrate"
|
|
11
|
+
NEXT = "plastic migrate stores --dry-run"
|
|
12
|
+
BECAUSE = "a dry run prints every move and rewrite and changes nothing"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../command"
|
|
4
|
+
require_relative "../../stores_move"
|
|
5
|
+
|
|
6
|
+
module Plastic
|
|
7
|
+
class CLI
|
|
8
|
+
module Commands
|
|
9
|
+
class MigrateStores < Command
|
|
10
|
+
USAGE_LINE = "plastic migrate stores [--dry-run] [--json]"
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
move = StoresMove.new(scope.plastic_home, qmd_index: File.join(@home, ".config", "qmd", "index.yml"), dry_run: options[:dry_run])
|
|
14
|
+
move.call.each { |verb, text| @output.row(verb, text) }
|
|
15
|
+
return @output.next_step("plastic migrate stores", because: "the dry run changed nothing") if options[:dry_run]
|
|
16
|
+
|
|
17
|
+
@output.row("remove", "rm -rf #{move.copy}")
|
|
18
|
+
@output.next_step("plastic doctor", because: "it checks the moved stores, and the copy stays until you remove it")
|
|
19
|
+
rescue StoresMove::Refused => e
|
|
20
|
+
raise Refusal, e.message
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def switches(parser)
|
|
26
|
+
parser.on("--dry-run") { options[:dry_run] = true }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../command"
|
|
4
|
+
require_relative "../frontier"
|
|
5
|
+
require_relative "../scope"
|
|
6
|
+
|
|
7
|
+
# `plastic next` - one line saying what to pick up, then the step to take and
|
|
8
|
+
# the rule that chose it. `--why` adds the roadmap, its frontier batch, and
|
|
9
|
+
# what is dispatchable, in flight and blocked.
|
|
10
|
+
module Plastic
|
|
11
|
+
class CLI
|
|
12
|
+
module Commands
|
|
13
|
+
class Next < Command
|
|
14
|
+
USAGE_LINE = "plastic next [--why] [--project SLUG] [--json]"
|
|
15
|
+
|
|
16
|
+
def call
|
|
17
|
+
active_id = scope.active_ids.first unless frontier.roadmap
|
|
18
|
+
@output.row("next work", active_id ? "#{active_id} in #{scope.slug}" : frontier.summary)
|
|
19
|
+
detail if options[:why]
|
|
20
|
+
if active_id
|
|
21
|
+
@output.next_step("plastic intent show #{active_id}", because: "#{active_id} is the first active intent in #{scope.slug}")
|
|
22
|
+
else
|
|
23
|
+
@output.next_step(frontier.next_step, because: frontier.because)
|
|
24
|
+
end
|
|
25
|
+
rescue RoadmapSavepoint::MissingGroupingHeading => e
|
|
26
|
+
raise Failure, e.message
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def switches(parser)
|
|
32
|
+
parser.on("--why") { @options[:why] = true }
|
|
33
|
+
parser.on("--project SLUG") { |slug| @options[:project] = slug }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def detail
|
|
37
|
+
@output.row("roadmap", frontier.roadmap || Frontier::NONE)
|
|
38
|
+
@output.row("frontier", frontier.heading || Frontier::NONE)
|
|
39
|
+
@output.row("dispatchable", listed(frontier.dispatchable_ids))
|
|
40
|
+
@output.row("in flight", listed(frontier.in_flight_ids))
|
|
41
|
+
@output.row("blocked", listed(frontier.blocked_ids))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def listed(ids)
|
|
45
|
+
ids.empty? ? Frontier::NONE : ids.join(", ")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def frontier
|
|
49
|
+
@frontier ||= Frontier.new(scope)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "subcommand_list"
|
|
4
|
+
|
|
5
|
+
# `plastic project` - the list of `plastic project ...` subcommands, straight
|
|
6
|
+
# out of the table; a word that names none of them is a usage error naming
|
|
7
|
+
# the same list, so a typo never falls through to a script.
|
|
8
|
+
module Plastic
|
|
9
|
+
class CLI
|
|
10
|
+
module Commands
|
|
11
|
+
class Project < SubcommandList
|
|
12
|
+
USAGE_LINE = "plastic project SUBCOMMAND [options]"
|
|
13
|
+
PREFIX = "project"
|
|
14
|
+
NEXT = "plastic project list"
|
|
15
|
+
BECAUSE = "the stores it lists are what every other subcommand names a slug from"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|