@zalom/plastic 2.0.0-alpha.9 → 2.0.1
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 +20 -15
- 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 +6 -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/{skills/conventions/references → docs/help}/completion-and-done.md +15 -16
- 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 +2 -2
- 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 +153 -30
- 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 +48 -83
- 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 +99 -0
- package/scripts/lib/cli/commands/auto_report.rb +50 -0
- package/scripts/lib/cli/commands/auto_take.rb +31 -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 +53 -0
- package/scripts/lib/cli/commands/intent_end.rb +62 -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 +71 -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 +42 -0
- package/scripts/lib/cli/commands/project_list.rb +20 -0
- package/scripts/lib/cli/commands/project_new.rb +72 -0
- package/scripts/lib/cli/commands/query.rb +31 -0
- package/scripts/lib/cli/commands/render.rb +33 -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 +70 -0
- package/scripts/lib/cli/commands/roadmap_show.rb +45 -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 +46 -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 +229 -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 +531 -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 +913 -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 +158 -8
- 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 +525 -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 +25 -18
- 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/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 +42 -33
- 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 +119 -0
- package/scripts/roadmap-savepoint +7 -0
- 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 +25 -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/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,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "../command"
|
|
5
|
+
require_relative "../legacy"
|
|
6
|
+
|
|
7
|
+
# `plastic auto lock` - inspects, repairs, or releases an intent's delivery
|
|
8
|
+
# lock (`plastic-lock status|fix|release --intent-dir DIR`, run through
|
|
9
|
+
# Legacy). The verb comes first, then the id, since the verb is what the
|
|
10
|
+
# table's two-token match consumes as part of the command name.
|
|
11
|
+
#
|
|
12
|
+
# `status` speaks JSON, so this command renders it as a screen and leaves the
|
|
13
|
+
# document to `--json`. `fix` and `release` already report in prose.
|
|
14
|
+
module Plastic
|
|
15
|
+
class CLI
|
|
16
|
+
module Commands
|
|
17
|
+
class AutoLock < Command
|
|
18
|
+
USAGE_LINE = "plastic auto lock status|fix|release ID [--json]"
|
|
19
|
+
|
|
20
|
+
VERBS = %w[status fix release].freeze
|
|
21
|
+
AFTER = {"status" => "none", "fix" => "plastic auto brief ID",
|
|
22
|
+
"release" => "plastic session summary"}.freeze
|
|
23
|
+
BECAUSE = {"status" => "inspection is complete; a held lock does not authorize repair",
|
|
24
|
+
"fix" => "a repaired lock is ready for the preamble again",
|
|
25
|
+
"release" => "the summary is what a freed lock leads to"}.freeze
|
|
26
|
+
|
|
27
|
+
def initialize(argv, runner: nil, **streams)
|
|
28
|
+
super(argv, **streams)
|
|
29
|
+
@runner = runner
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def call
|
|
33
|
+
raise Usage, "VERB must be one of #{VERBS.join(", ")}" unless VERBS.include?(verb)
|
|
34
|
+
|
|
35
|
+
(verb == "status") ? screen(report) : run_verb
|
|
36
|
+
@output.next_step(AFTER.fetch(verb).sub("ID", id.to_s), because: BECAUSE.fetch(verb))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def run_verb
|
|
42
|
+
status = legacy.run("plastic-lock", verb, "--intent-dir", intent_dir)
|
|
43
|
+
raise Failure, "plastic-lock exited #{status}" unless status.zero?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def report
|
|
47
|
+
text, status = legacy.capture("plastic-lock", "status", "--intent-dir", intent_dir)
|
|
48
|
+
raise Failure, "plastic-lock exited #{status}" unless status.zero?
|
|
49
|
+
|
|
50
|
+
JSON.parse(text)
|
|
51
|
+
rescue JSON::ParserError
|
|
52
|
+
raise Failure, "plastic-lock did not print a report"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def screen(report)
|
|
56
|
+
@output.row("intent", File.basename(report["intent_dir"].to_s))
|
|
57
|
+
@output.row("lock", lock_line(report))
|
|
58
|
+
@output.row("worktree", worktree_line(report["worktree"]))
|
|
59
|
+
@output.row("delivering", report["delivering"] ? "yes" : "no")
|
|
60
|
+
@output.row("claims", Array(report["claims"]).map(&:to_s))
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def lock_line(report)
|
|
64
|
+
lock = report["lock"]
|
|
65
|
+
return "corrupt, plastic auto lock fix #{id} rewrites it" if report["lock_corrupt"]
|
|
66
|
+
return "none" if lock.nil?
|
|
67
|
+
|
|
68
|
+
holder = lock.is_a?(Hash) ? (lock["owner_session"] || lock["session"]) : lock
|
|
69
|
+
"held by #{holder}#{", stale" unless report["lock_fresh"]}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def worktree_line(worktree)
|
|
73
|
+
return "none" unless worktree.is_a?(Hash) && worktree["provisioned"]
|
|
74
|
+
|
|
75
|
+
[worktree["code"], worktree["code_branch"]].compact.join(" on ")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def verb
|
|
79
|
+
arguments[0]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def id
|
|
83
|
+
arguments[1]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def intent_dir
|
|
87
|
+
dir = scope.intent_dir(id)
|
|
88
|
+
raise Failure, "no intent #{id.inspect} in #{scope.slug}; plastic status lists the ones that exist" unless dir
|
|
89
|
+
|
|
90
|
+
dir
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def legacy
|
|
94
|
+
@legacy ||= Legacy.new(env: @env, runner: @runner, output: @output, json: options[:json])
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "intent_command"
|
|
4
|
+
|
|
5
|
+
# `plastic auto report` - an intent's completion report (`agent-report`, run
|
|
6
|
+
# through Legacy), then the review-by-risk rules a lead checks it against.
|
|
7
|
+
module Plastic
|
|
8
|
+
class CLI
|
|
9
|
+
module Commands
|
|
10
|
+
class AutoReport < IntentCommand
|
|
11
|
+
USAGE_LINE = "plastic auto report ID [--role ROLE] [--json]"
|
|
12
|
+
|
|
13
|
+
SCRIPT = "agent-report"
|
|
14
|
+
AFTER = "plastic auto lock release ID"
|
|
15
|
+
BECAUSE = "closing the lock is what a delivered or abandoned report leads to"
|
|
16
|
+
|
|
17
|
+
REVIEW_RULES = <<~TEXT.chomp
|
|
18
|
+
Review by risk, boot 3, only when a rule fires; otherwise the green suite is
|
|
19
|
+
the review. Dispatch the post-execution reviewer when any of these holds:
|
|
20
|
+
1. the diff touches a path on the risk list (hooks, the lock, the arming
|
|
21
|
+
module, the installer, a release file).
|
|
22
|
+
2. a failure-mode matrix row names a test not in the diff, or a test the
|
|
23
|
+
green run did not execute.
|
|
24
|
+
3. the completion report carries a status other than delivered, or a
|
|
25
|
+
non-empty deviations or blockers field.
|
|
26
|
+
The reviewer returns a pass or fixes; at most one review-fix round, ever.
|
|
27
|
+
TEXT
|
|
28
|
+
|
|
29
|
+
def call
|
|
30
|
+
super
|
|
31
|
+
@output.row("review rules", REVIEW_RULES)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def role
|
|
37
|
+
options[:role]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def switches(parser)
|
|
41
|
+
parser.on("--role ROLE") { |v| @options[:role] = v }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def script_arguments
|
|
45
|
+
role ? [intent_dir, "--role", role] : [intent_dir]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "intent_command"
|
|
4
|
+
|
|
5
|
+
# `plastic auto take` - arms the intent's delivery lock for this session
|
|
6
|
+
# (`plastic-lock arm --intent-dir DIR --mode auto`, run through Legacy).
|
|
7
|
+
module Plastic
|
|
8
|
+
class CLI
|
|
9
|
+
module Commands
|
|
10
|
+
class AutoTake < IntentCommand
|
|
11
|
+
USAGE_LINE = "plastic auto take ID [--allow-inline] [--json]"
|
|
12
|
+
|
|
13
|
+
SCRIPT = "plastic-lock"
|
|
14
|
+
AFTER = "plastic auto brief ID"
|
|
15
|
+
BECAUSE = "the preamble is the live state a taken intent is read from next"
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def switches(parser)
|
|
20
|
+
parser.on("--allow-inline") { @options[:allow_inline] = true }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def script_arguments
|
|
24
|
+
args = ["arm", "--intent-dir", intent_dir, "--mode", "auto"]
|
|
25
|
+
args << "--allow-inline" if options[:allow_inline]
|
|
26
|
+
args
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../command"
|
|
4
|
+
require_relative "version"
|
|
5
|
+
require_relative "../../backup"
|
|
6
|
+
|
|
7
|
+
module Plastic
|
|
8
|
+
class CLI
|
|
9
|
+
module Commands
|
|
10
|
+
class Backup < Command
|
|
11
|
+
USAGE_LINE = "plastic backup [--list] [--json]"
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
options[:list] ? list : write
|
|
15
|
+
rescue Plastic::Backup::Missing => e
|
|
16
|
+
@output.next_step("plastic sync", because: "a backup copies the three databases, and sync builds them")
|
|
17
|
+
raise Failure, "no #{e.message} at #{scope.plastic_home}; run plastic sync"
|
|
18
|
+
rescue Sqlite::Error => e
|
|
19
|
+
raise Failure, e.message
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def switches(parser)
|
|
25
|
+
parser.on("--list") { options[:list] = true }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def write
|
|
29
|
+
version = Version.new([], out: nil, err: nil, env: @env).found.last
|
|
30
|
+
@output.row("archive", Plastic::Backup.call(scope.plastic_home, version: version))
|
|
31
|
+
@output.next_step("plastic backup --list", because: "to restore, unpack the archive into an empty ~/.plastic and run plastic checkout")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def list
|
|
35
|
+
archives = Plastic::Backup.list(scope.plastic_home)
|
|
36
|
+
archives.each { |path| @output.row(File.basename(path), "#{File.size(path)} bytes #{File.mtime(path).strftime("%Y-%m-%d %H:%M")}") }
|
|
37
|
+
@output.row("result", "no backup yet") if archives.empty?
|
|
38
|
+
@output.next_step("plastic backup", because: "copying backups/ off this machine is your choice; Plastic sends nothing")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../command"
|
|
4
|
+
require_relative "../../store_sync"
|
|
5
|
+
|
|
6
|
+
module Plastic
|
|
7
|
+
class CLI
|
|
8
|
+
module Commands
|
|
9
|
+
class Checkout < Command
|
|
10
|
+
USAGE_LINE = "plastic checkout [--json]"
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
home = scope.plastic_home
|
|
14
|
+
raise Failure, "no databases at #{home}; run plastic sync" unless File.exist?(ReferenceArchive.path(home))
|
|
15
|
+
|
|
16
|
+
restored = StoreSync.checkout(home)
|
|
17
|
+
@output.row("restored", restored.empty? ? "nothing was missing" : restored)
|
|
18
|
+
@output.next_step("plastic sync --dry-run", because: "checkout restores missing files only; a changed file is never overwritten")
|
|
19
|
+
rescue Sqlite::Error => e
|
|
20
|
+
raise Failure, e.message
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../command"
|
|
4
|
+
require_relative "../frontier"
|
|
5
|
+
require_relative "../scope"
|
|
6
|
+
|
|
7
|
+
# `plastic continue` - where one project stands and what runs next: the store it
|
|
8
|
+
# uses, the intents it has open, its liveliest roadmap and that roadmap's
|
|
9
|
+
# frontier, then the step to take and the rule behind it.
|
|
10
|
+
#
|
|
11
|
+
# With no roadmap the first active intent answers instead, so a project that
|
|
12
|
+
# never grew a roadmap still gets a next step.
|
|
13
|
+
module Plastic
|
|
14
|
+
class CLI
|
|
15
|
+
module Commands
|
|
16
|
+
class Continue < Command
|
|
17
|
+
USAGE_LINE = "plastic continue [--project SLUG] [--json]"
|
|
18
|
+
|
|
19
|
+
def call
|
|
20
|
+
@output.row("project", scope.slug)
|
|
21
|
+
@output.row("root", scope.root)
|
|
22
|
+
@output.row("active", active)
|
|
23
|
+
@output.row("roadmap", roadmap)
|
|
24
|
+
command, because = decision
|
|
25
|
+
@output.next_step(command, because: because)
|
|
26
|
+
rescue RoadmapSavepoint::MissingGroupingHeading => e
|
|
27
|
+
raise Failure, e.message
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def switches(parser)
|
|
33
|
+
parser.on("--project SLUG") { |slug| @options[:project] = slug }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def active
|
|
37
|
+
scope.active_entries.map { |id, title| "#{id} #{title}" }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def roadmap
|
|
41
|
+
return Frontier::NONE unless frontier.roadmap
|
|
42
|
+
|
|
43
|
+
"#{frontier.roadmap} frontier #{frontier.heading}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def decision
|
|
47
|
+
return [frontier.next_step, frontier.because] if frontier.roadmap
|
|
48
|
+
|
|
49
|
+
id = scope.active_ids.first
|
|
50
|
+
return [plan_for(id), "#{id} is the first active intent in #{scope.slug}"] if id
|
|
51
|
+
|
|
52
|
+
["plastic help", "#{scope.slug} has no active work and no roadmap"]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def plan_for(id)
|
|
56
|
+
dir = scope.intent_dir(id)
|
|
57
|
+
dir ? "plastic intent show #{id}" : "plastic status"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def frontier
|
|
61
|
+
@frontier ||= Frontier.new(scope)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "installer_verb"
|
|
5
|
+
|
|
6
|
+
# `plastic doctor` - diagnoses Plastic installation health. The work is still
|
|
7
|
+
# scripts/doctor.rb, run through Legacy; each finding prints its own repair.
|
|
8
|
+
# The script speaks JSON, so this command renders it as a screen and keeps the
|
|
9
|
+
# document itself behind `--json`, the way every other command reads. A report
|
|
10
|
+
# that is not `pass` exits non-zero, because findings are what doctor is for.
|
|
11
|
+
#
|
|
12
|
+
# InstallerVerb keeps the raw argv rather than running the option parser, so
|
|
13
|
+
# `--json` is taken off the line here instead of through `options`.
|
|
14
|
+
module Plastic
|
|
15
|
+
class CLI
|
|
16
|
+
module Commands
|
|
17
|
+
class Doctor < InstallerVerb
|
|
18
|
+
USAGE_LINE = "plastic doctor [--core] [--store WHICH] [--json]"
|
|
19
|
+
FLAGS = %w[--core --store].freeze
|
|
20
|
+
|
|
21
|
+
SCRIPT = "doctor.rb"
|
|
22
|
+
AFTER = "none"
|
|
23
|
+
BECAUSE = "the findings are the whole answer, and each one names its own repair"
|
|
24
|
+
|
|
25
|
+
def call
|
|
26
|
+
as_document = !@argv.delete("--json").nil?
|
|
27
|
+
unknown = arguments.grep(/\A-/) - FLAGS
|
|
28
|
+
raise Usage, "#{unknown.first} is not a flag of this command" unless unknown.empty?
|
|
29
|
+
|
|
30
|
+
report = parse(legacy.capture(SCRIPT, *arguments).first)
|
|
31
|
+
as_document ? document(report) : screen(report)
|
|
32
|
+
@output.next_step(AFTER, because: BECAUSE)
|
|
33
|
+
flush
|
|
34
|
+
raise Failure, verdict(report) unless report["status"] == "pass"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def flush
|
|
38
|
+
@output.flush(json: false) unless @document_printed
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def parse(report)
|
|
44
|
+
JSON.parse(report)
|
|
45
|
+
rescue JSON::ParserError
|
|
46
|
+
raise Failure, "#{SCRIPT} did not print a report"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def document(report)
|
|
50
|
+
@document_printed = true
|
|
51
|
+
@output.document(report)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def findings(report)
|
|
55
|
+
report["checks"].to_a.reject { |check| check["status"] == "pass" }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def verdict(report)
|
|
59
|
+
found = findings(report)
|
|
60
|
+
"doctor found #{found.size} #{(found.size == 1) ? "finding" : "findings"}, listed above"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def screen(report)
|
|
64
|
+
checks = report["checks"].to_a
|
|
65
|
+
found = findings(report)
|
|
66
|
+
|
|
67
|
+
@output.row("status", "#{report["status"]} #{checks.size - found.size} of #{checks.size} checks pass")
|
|
68
|
+
@output.row("version", report["version"])
|
|
69
|
+
@output.row("agent", report["agent"])
|
|
70
|
+
found.each { |finding| finding_rows(finding) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def finding_rows(finding)
|
|
74
|
+
name = [finding["category"], finding["name"]].compact.join("/")
|
|
75
|
+
@output.row(finding["status"], "#{name} #{finding["message"]}")
|
|
76
|
+
@output.row("", "fix: #{finding["fix_hint"]}") if finding["fix_hint"]
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../command"
|
|
4
|
+
require_relative "../legacy"
|
|
5
|
+
|
|
6
|
+
# `plastic feedback "TITLE"` - files a report with feedback-report, the body
|
|
7
|
+
# read from standard input and passed to the child process unchanged.
|
|
8
|
+
module Plastic
|
|
9
|
+
class CLI
|
|
10
|
+
module Commands
|
|
11
|
+
class Feedback < Command
|
|
12
|
+
USAGE_LINE = 'plastic feedback "TITLE"'
|
|
13
|
+
|
|
14
|
+
def initialize(argv, runner: nil, **streams)
|
|
15
|
+
super(argv, **streams)
|
|
16
|
+
@runner = runner
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def call
|
|
20
|
+
raise Usage, "plastic feedback needs a title" if title.nil? || title.strip.empty?
|
|
21
|
+
|
|
22
|
+
status = legacy.run("feedback-report", "--title", title)
|
|
23
|
+
raise Failure, "feedback-report exited #{status}" unless status.zero?
|
|
24
|
+
|
|
25
|
+
@output.next_step("none", because: "the report is saved as a draft; open the printed URL to send it")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def title
|
|
31
|
+
arguments.first
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def legacy
|
|
35
|
+
@legacy ||= Legacy.new(env: @env, runner: @runner, output: @output, json: options[:json])
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../command"
|
|
4
|
+
require_relative "../table"
|
|
5
|
+
|
|
6
|
+
# `plastic help` - the command list plus the help topics, straight out of the
|
|
7
|
+
# table and docs/help, or one command's usage line, or one topic's chapter. A
|
|
8
|
+
# command name always wins over a topic of the same name. The list loads no
|
|
9
|
+
# command file: that is what the table's summary column is for.
|
|
10
|
+
module Plastic
|
|
11
|
+
class CLI
|
|
12
|
+
module Commands
|
|
13
|
+
class Help < Command
|
|
14
|
+
USAGE_LINE = "plastic help [COMMAND|TOPIC] [--json]"
|
|
15
|
+
|
|
16
|
+
def call
|
|
17
|
+
name = arguments.join(" ")
|
|
18
|
+
return list if name.empty?
|
|
19
|
+
return one_command(name) if TABLE.key?(name)
|
|
20
|
+
return one_topic(name) if topic?(name)
|
|
21
|
+
|
|
22
|
+
raise Usage, "no command or help topic named #{name.inspect}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def list
|
|
28
|
+
@output.row("usage", "plastic <command> [options]")
|
|
29
|
+
TABLE.sort.each { |name, (_file, _const, summary)| @output.row(name, summary) }
|
|
30
|
+
@output.row("topics", topic_names)
|
|
31
|
+
@output.next_step("plastic status", because: "it needs no argument and names the rest")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def one_command(name)
|
|
35
|
+
file, const, summary = TABLE.fetch(name)
|
|
36
|
+
require_relative "../#{file}"
|
|
37
|
+
@output.row("usage", Commands.const_get(const)::USAGE_LINE)
|
|
38
|
+
@output.row("summary", summary)
|
|
39
|
+
@output.next_step("none", because: "the usage line is the whole answer")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def one_topic(name)
|
|
43
|
+
@output.row(name, File.read(topic_path(name)))
|
|
44
|
+
@output.next_step("none", because: "the chapter is the whole answer")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def topic?(name)
|
|
48
|
+
name == File.basename(name) && File.file?(topic_path(name))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def topic_names
|
|
52
|
+
Dir.glob(File.join(help_dir, "*.md")).map { |path| File.basename(path, ".md") }.sort
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def topic_path(name)
|
|
56
|
+
File.join(help_dir, "#{name}.md")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def help_dir
|
|
60
|
+
File.join(package_root, "docs", "help")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def package_root
|
|
64
|
+
@env["PLASTIC_PACKAGE_ROOT"] || File.expand_path("../../../..", __dir__)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "English"
|
|
4
|
+
require_relative "../command"
|
|
5
|
+
require_relative "../legacy"
|
|
6
|
+
|
|
7
|
+
module Plastic
|
|
8
|
+
class CLI
|
|
9
|
+
module Commands
|
|
10
|
+
class Hook < Command
|
|
11
|
+
USAGE_LINE = "plastic hook EVENT"
|
|
12
|
+
EVENTS = %w[call-budget capture close record savepoint session-start stop].freeze
|
|
13
|
+
|
|
14
|
+
DEFAULT_RUNNER = lambda do |path|
|
|
15
|
+
system(path)
|
|
16
|
+
Legacy.exit_code($CHILD_STATUS)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.call(argv, runner: DEFAULT_RUNNER, **seams)
|
|
20
|
+
return super(argv, **seams) unless EVENTS.include?(argv.first)
|
|
21
|
+
|
|
22
|
+
root = Legacy.new(env: seams.fetch(:env, ENV)).package_root
|
|
23
|
+
runner.call(File.join(root, "hooks", argv.first))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def call
|
|
27
|
+
raise Usage, "EVENT is one of #{EVENTS.join(", ")}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../command"
|
|
4
|
+
require_relative "../../search_index"
|
|
5
|
+
|
|
6
|
+
module Plastic
|
|
7
|
+
class CLI
|
|
8
|
+
module Commands
|
|
9
|
+
class Index < Command
|
|
10
|
+
USAGE_LINE = "plastic index [--json]"
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
count = SearchIndex.build(scope.plastic_home)
|
|
14
|
+
@output.row("indexed", "#{count} files")
|
|
15
|
+
@output.row("database", SearchIndex.path(scope.plastic_home))
|
|
16
|
+
@output.next_step("plastic search TERMS", because: "the index is whole and new; it is rebuilt, never updated")
|
|
17
|
+
rescue Sqlite::Error => e
|
|
18
|
+
raise Failure, e.message
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "installer_verb"
|
|
4
|
+
|
|
5
|
+
# `plastic install` - wires Plastic into this machine's agents. The work is
|
|
6
|
+
# still scripts/install.rb, run as a child process through Legacy; this command
|
|
7
|
+
# is what replaces the Node shim the npm package used to carry.
|
|
8
|
+
module Plastic
|
|
9
|
+
class CLI
|
|
10
|
+
module Commands
|
|
11
|
+
class Install < InstallerVerb
|
|
12
|
+
USAGE_LINE = "plastic install [--claude] [--codex] [--hermes] [--all] [--reinstall] [--force] [--advisor NAME] [--no-advisor] [--statusline] [--ledger-action NAME]"
|
|
13
|
+
FLAGS = %w[--claude --codex --hermes --all --reinstall --force --advisor --no-advisor --statusline --ledger-action].freeze
|
|
14
|
+
|
|
15
|
+
SCRIPT = "install.rb"
|
|
16
|
+
AFTER = "plastic version"
|
|
17
|
+
BECAUSE = "the install is done, and the version proves the command works"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -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
|