@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.
Files changed (326) hide show
  1. package/PLASTIC.md +13 -136
  2. package/README.md +357 -133
  3. package/agents/plastic-enforcer.md +21 -16
  4. package/agents/plastic-executor.md +15 -4
  5. package/agents/plastic-node-research.md +30 -0
  6. package/agents/plastic-node-verify.md +28 -0
  7. package/agents/plastic-node-work.md +33 -0
  8. package/agents/{plastic-advisor.md → plastic-primary-advisor.md} +7 -9
  9. package/agents/{plastic-faux-advisor.md → plastic-secondary-advisor.md} +9 -12
  10. package/assets/plastic-logo.svg +1 -0
  11. package/bin/crap +4 -0
  12. package/bin/lib/context_budget.rb +43 -6
  13. package/bin/lib/skill_census.rb +839 -0
  14. package/bin/plastic +8 -0
  15. package/bin/plastic-skill-census +114 -0
  16. package/bin/test +24 -4
  17. package/bin/verify-change +345 -0
  18. package/config_asks.yml +4 -4
  19. package/deprecations.yml +1 -1
  20. package/{skills/agent-advisor/references → docs/help}/advisor-protocol.md +19 -24
  21. package/{skills/auto/references → docs/help}/agent-architecture.md +16 -14
  22. package/docs/help/completion-and-done.md +53 -0
  23. package/docs/help/human-report-contract.md +152 -0
  24. package/{skills/conventions/references → docs/help}/knowledge-graph.md +9 -0
  25. package/{skills/conventions/references → docs/help}/locks-and-worktrees.md +11 -13
  26. package/{skills/conventions/references → docs/help}/maintenance-and-revisions.md +1 -1
  27. package/{skills/conventions/references → docs/help}/roadmaps.md +5 -4
  28. package/{skills/tutorial/references → docs/help}/track-1-guided.md +28 -47
  29. package/{skills/tutorial/references → docs/help}/track-2-auto.md +8 -8
  30. package/{skills/tutorial/references → docs/help}/track-3-projects-and-roadmaps.md +24 -17
  31. package/hooks/call-budget +4 -0
  32. package/hooks/hooks.json +24 -0
  33. package/hooks/message-display +55 -2
  34. package/hooks/session-start +5 -1
  35. package/hooks/statusline +32 -27
  36. package/hooks/stop +5 -0
  37. package/package.json +5 -3
  38. package/scripts/append-ledger +2 -1
  39. package/scripts/dashboard.rb +267 -16
  40. package/scripts/day-summary +2 -1
  41. package/scripts/doctor.rb +680 -39
  42. package/scripts/end-intent +378 -56
  43. package/scripts/exec-worktree +5 -5
  44. package/scripts/file-session-intent +2 -1
  45. package/scripts/graph-measure +249 -0
  46. package/scripts/hook-call-budget +222 -0
  47. package/scripts/hook-capture +24 -123
  48. package/scripts/hook-close +2 -1
  49. package/scripts/hook-message-display +7 -0
  50. package/scripts/hook-record +24 -16
  51. package/scripts/hook-savepoint +27 -3
  52. package/scripts/hook-session-start +359 -321
  53. package/scripts/hook-stop +58 -0
  54. package/scripts/index-projection +74 -0
  55. package/scripts/insight-append +17 -4
  56. package/scripts/install.rb +9 -7
  57. package/scripts/lib/action_graph_shim.rb +279 -0
  58. package/scripts/lib/active_delivery.rb +105 -0
  59. package/scripts/lib/agent_models.rb +63 -25
  60. package/scripts/lib/arm.rb +74 -91
  61. package/scripts/lib/atomic_write.rb +31 -0
  62. package/scripts/lib/backup.rb +65 -0
  63. package/scripts/lib/cli/command.rb +92 -0
  64. package/scripts/lib/cli/commands/auto.rb +18 -0
  65. package/scripts/lib/cli/commands/auto_brief.rb +44 -0
  66. package/scripts/lib/cli/commands/auto_lock.rb +111 -0
  67. package/scripts/lib/cli/commands/auto_report.rb +50 -0
  68. package/scripts/lib/cli/commands/auto_take.rb +68 -0
  69. package/scripts/lib/cli/commands/backup.rb +43 -0
  70. package/scripts/lib/cli/commands/checkout.rb +25 -0
  71. package/scripts/lib/cli/commands/continue.rb +66 -0
  72. package/scripts/lib/cli/commands/doctor.rb +81 -0
  73. package/scripts/lib/cli/commands/feedback.rb +40 -0
  74. package/scripts/lib/cli/commands/help.rb +69 -0
  75. package/scripts/lib/cli/commands/hook.rb +32 -0
  76. package/scripts/lib/cli/commands/index.rb +23 -0
  77. package/scripts/lib/cli/commands/install.rb +21 -0
  78. package/scripts/lib/cli/commands/installer_verb.rb +37 -0
  79. package/scripts/lib/cli/commands/intent.rb +19 -0
  80. package/scripts/lib/cli/commands/intent_answer.rb +37 -0
  81. package/scripts/lib/cli/commands/intent_command.rb +58 -0
  82. package/scripts/lib/cli/commands/intent_end.rb +75 -0
  83. package/scripts/lib/cli/commands/intent_new.rb +62 -0
  84. package/scripts/lib/cli/commands/intent_note.rb +43 -0
  85. package/scripts/lib/cli/commands/intent_rule.rb +36 -0
  86. package/scripts/lib/cli/commands/intent_show.rb +32 -0
  87. package/scripts/lib/cli/commands/intent_spec.rb +44 -0
  88. package/scripts/lib/cli/commands/intent_step.rb +79 -0
  89. package/scripts/lib/cli/commands/intent_verify.rb +26 -0
  90. package/scripts/lib/cli/commands/migrate.rb +16 -0
  91. package/scripts/lib/cli/commands/migrate_stores.rb +31 -0
  92. package/scripts/lib/cli/commands/next.rb +54 -0
  93. package/scripts/lib/cli/commands/project.rb +19 -0
  94. package/scripts/lib/cli/commands/project_links.rb +46 -0
  95. package/scripts/lib/cli/commands/project_list.rb +20 -0
  96. package/scripts/lib/cli/commands/project_new.rb +95 -0
  97. package/scripts/lib/cli/commands/query.rb +31 -0
  98. package/scripts/lib/cli/commands/render.rb +35 -0
  99. package/scripts/lib/cli/commands/roadmap.rb +19 -0
  100. package/scripts/lib/cli/commands/roadmap_check.rb +54 -0
  101. package/scripts/lib/cli/commands/roadmap_log.rb +54 -0
  102. package/scripts/lib/cli/commands/roadmap_migrate.rb +53 -0
  103. package/scripts/lib/cli/commands/roadmap_next.rb +77 -0
  104. package/scripts/lib/cli/commands/roadmap_show.rb +65 -0
  105. package/scripts/lib/cli/commands/rollback.rb +20 -0
  106. package/scripts/lib/cli/commands/search.rb +60 -0
  107. package/scripts/lib/cli/commands/session.rb +18 -0
  108. package/scripts/lib/cli/commands/session_commit.rb +42 -0
  109. package/scripts/lib/cli/commands/session_handoff.rb +34 -0
  110. package/scripts/lib/cli/commands/session_summary.rb +35 -0
  111. package/scripts/lib/cli/commands/status.rb +68 -0
  112. package/scripts/lib/cli/commands/subcommand_list.rb +36 -0
  113. package/scripts/lib/cli/commands/sync.rb +55 -0
  114. package/scripts/lib/cli/commands/uninstall.rb +20 -0
  115. package/scripts/lib/cli/commands/update.rb +20 -0
  116. package/scripts/lib/cli/commands/version.rb +53 -0
  117. package/scripts/lib/cli/frontier.rb +86 -0
  118. package/scripts/lib/cli/intent_progress.rb +36 -0
  119. package/scripts/lib/cli/legacy.rb +80 -0
  120. package/scripts/lib/cli/output.rb +137 -0
  121. package/scripts/lib/cli/scope.rb +134 -0
  122. package/scripts/lib/cli/table.rb +65 -0
  123. package/scripts/lib/cli.rb +94 -0
  124. package/scripts/lib/codex_adapter.rb +198 -0
  125. package/scripts/lib/compact_instructions.rb +13 -5
  126. package/scripts/lib/core_integrity.rb +71 -0
  127. package/scripts/lib/dashboard_screen.rb +40 -0
  128. package/scripts/lib/data_boundary.rb +132 -0
  129. package/scripts/lib/day_summary.rb +23 -14
  130. package/scripts/lib/doctor_core.rb +114 -38
  131. package/scripts/lib/doctor_session_ledger.rb +5 -53
  132. package/scripts/lib/engine_permissions.rb +88 -0
  133. package/scripts/lib/exec_worktree.rb +24 -21
  134. package/scripts/lib/feedback_report.rb +1 -1
  135. package/scripts/lib/graph_edges.rb +137 -0
  136. package/scripts/lib/graph_file.rb +246 -0
  137. package/scripts/lib/graph_measure.rb +645 -0
  138. package/scripts/lib/graph_measure_budget.rb +409 -0
  139. package/scripts/lib/graph_measure_cohorts.rb +487 -0
  140. package/scripts/lib/graph_measure_models.rb +413 -0
  141. package/scripts/lib/graph_measure_report.rb +532 -0
  142. package/scripts/lib/graph_tree.rb +98 -0
  143. package/scripts/lib/guarded_append.rb +155 -0
  144. package/scripts/lib/handoff.rb +40 -13
  145. package/scripts/lib/harness_adapter.rb +184 -0
  146. package/scripts/lib/hook_registry.rb +27 -3
  147. package/scripts/lib/hook_replay.rb +235 -0
  148. package/scripts/lib/index_entry.rb +62 -0
  149. package/scripts/lib/index_projection.rb +201 -0
  150. package/scripts/lib/insights.rb +1 -1
  151. package/scripts/lib/installer_core.rb +543 -71
  152. package/scripts/lib/intent_screen.rb +4 -4
  153. package/scripts/lib/intent_screen_ansi.rb +73 -12
  154. package/scripts/lib/intent_validator.rb +2 -2
  155. package/scripts/lib/lock.rb +10 -11
  156. package/scripts/lib/message_display.rb +350 -54
  157. package/scripts/lib/meter_watch.rb +185 -0
  158. package/scripts/lib/node_file.rb +234 -0
  159. package/scripts/lib/node_ids.rb +99 -0
  160. package/scripts/lib/node_input.rb +921 -0
  161. package/scripts/lib/node_input_compatibility.rb +62 -0
  162. package/scripts/lib/node_ledger.rb +386 -0
  163. package/scripts/lib/node_progress.rb +153 -0
  164. package/scripts/lib/node_return.rb +204 -0
  165. package/scripts/lib/node_worktree.rb +337 -0
  166. package/scripts/lib/outcome_report.rb +440 -0
  167. package/scripts/lib/preflight.rb +4 -6
  168. package/scripts/lib/project_config.rb +46 -0
  169. package/scripts/lib/project_validator.rb +3 -2
  170. package/scripts/lib/qmd_sync.rb +8 -7
  171. package/scripts/lib/ready_set.rb +462 -0
  172. package/scripts/lib/reference_archive.rb +45 -0
  173. package/scripts/lib/release_guard.rb +18 -0
  174. package/scripts/lib/report_screen.rb +1371 -47
  175. package/scripts/lib/rlm/corpus.rb +13 -0
  176. package/scripts/lib/rlm/probe.rb +29 -0
  177. package/scripts/lib/rlm/query.rb +22 -0
  178. package/scripts/lib/roadmap_graph.rb +210 -0
  179. package/scripts/lib/roadmap_migration.rb +95 -0
  180. package/scripts/lib/roadmap_queue.rb +170 -12
  181. package/scripts/lib/roadmap_render.rb +150 -0
  182. package/scripts/lib/roadmap_savepoint.rb +64 -14
  183. package/scripts/lib/runner_absorb.rb +703 -0
  184. package/scripts/lib/runner_answer.rb +206 -0
  185. package/scripts/lib/runner_core.rb +194 -0
  186. package/scripts/lib/runner_dispatch.rb +526 -0
  187. package/scripts/lib/runner_policy.rb +191 -0
  188. package/scripts/lib/runner_proposals.rb +275 -0
  189. package/scripts/lib/runner_rewind.rb +201 -0
  190. package/scripts/lib/runner_sweep.rb +231 -0
  191. package/scripts/lib/runner_until_empty.rb +252 -0
  192. package/scripts/lib/runner_watch.rb +389 -0
  193. package/scripts/lib/savepoint.rb +141 -19
  194. package/scripts/lib/scaffold_intent.rb +6 -3
  195. package/scripts/lib/screen_paint.rb +365 -28
  196. package/scripts/lib/screens/dashboard.rb +20 -0
  197. package/scripts/lib/screens/plan.rb +18 -0
  198. package/scripts/lib/screens/roadmap.rb +15 -0
  199. package/scripts/lib/search_index.rb +55 -0
  200. package/scripts/lib/session_close.rb +30 -28
  201. package/scripts/lib/session_git.rb +67 -19
  202. package/scripts/lib/session_ledger.rb +48 -4
  203. package/scripts/lib/session_usage.rb +190 -0
  204. package/scripts/lib/sqlite.rb +22 -0
  205. package/scripts/lib/stop_gate.rb +95 -0
  206. package/scripts/lib/store_discovery.rb +7 -6
  207. package/scripts/lib/store_layout.rb +54 -0
  208. package/scripts/lib/store_provisioning.rb +2 -1
  209. package/scripts/lib/store_sync.rb +85 -0
  210. package/scripts/lib/stores_move.rb +93 -0
  211. package/scripts/lib/untouched_scaffold.rb +51 -0
  212. package/scripts/lib/verify_intent.rb +36 -8
  213. package/scripts/lib/version_number.rb +48 -0
  214. package/scripts/lib/work_graph.rb +59 -0
  215. package/scripts/lib/work_graph_validator.rb +201 -0
  216. package/scripts/lib/worktree.rb +27 -32
  217. package/scripts/lib/worktree_sweep.rb +6 -5
  218. package/scripts/link-suggest +2 -1
  219. package/scripts/meter-watch +57 -0
  220. package/scripts/migrate-to-global +1 -1
  221. package/scripts/new-intent +4 -13
  222. package/scripts/node-input +92 -0
  223. package/scripts/node-run +225 -0
  224. package/scripts/node-transition +291 -0
  225. package/scripts/outcome-report +74 -0
  226. package/scripts/plastic-lock +65 -60
  227. package/scripts/project-links +6 -21
  228. package/scripts/promote-session-item +3 -2
  229. package/scripts/read-config +53 -9
  230. package/scripts/ready-set +126 -0
  231. package/scripts/release-check +123 -0
  232. package/scripts/report-screen +177 -16
  233. package/scripts/roadmap-graph +125 -0
  234. package/scripts/roadmap-savepoint +7 -0
  235. package/scripts/rollback.rb +5 -1
  236. package/scripts/runner +581 -0
  237. package/scripts/savepoint-note +11 -9
  238. package/scripts/session-commit +2 -1
  239. package/scripts/session-usage +56 -0
  240. package/scripts/skill-lint +115 -6
  241. package/scripts/spawn-preamble +2 -2
  242. package/scripts/update.rb +31 -4
  243. package/scripts/validate-work-graph +39 -0
  244. package/scripts/verify-intent +3 -2
  245. package/scripts/write-handoff +2 -1
  246. package/templates/agents.md +7 -7
  247. package/templates/config.yml +16 -9
  248. package/templates/dashboard-screen.md +22 -0
  249. package/templates/display-fixture.md +21 -0
  250. package/templates/graph.md +16 -0
  251. package/templates/index.md +1 -1
  252. package/templates/intent-screen.md +1 -1
  253. package/templates/node-decision.md +11 -0
  254. package/templates/node-research.md +13 -0
  255. package/templates/node-verify.md +13 -0
  256. package/templates/node-work.md +22 -0
  257. package/templates/outcome.md +8 -3
  258. package/templates/project.yml +1 -1
  259. package/templates/render.css +10 -0
  260. package/templates/report-plan.md +15 -0
  261. package/templates/report-roadmap-delivered.md +10 -0
  262. package/templates/report-roadmap-plan.md +9 -0
  263. package/templates/report-roadmap-state.md +9 -0
  264. package/templates/report-state.md +1 -1
  265. package/templates/roadmap.md +13 -0
  266. package/bin/plastic.js +0 -70
  267. package/scripts/lib/bridge.rb +0 -116
  268. package/skills/agent-advisor/SKILL.md +0 -92
  269. package/skills/auto/SKILL.md +0 -296
  270. package/skills/auto/evals/evals.json +0 -255
  271. package/skills/auto/references/end-tail.md +0 -66
  272. package/skills/auto/references/human-report-contract.md +0 -78
  273. package/skills/conventions/SKILL.md +0 -29
  274. package/skills/conventions/references/completion-and-done.md +0 -43
  275. package/skills/dashboard/SKILL.md +0 -169
  276. package/skills/dashboard/evals/evals.json +0 -38
  277. package/skills/dashboard/references/classification.md +0 -22
  278. package/skills/dashboard/templates/dashboard-global.md +0 -20
  279. package/skills/dashboard/templates/dashboard-project.md +0 -19
  280. package/skills/direct/SKILL.md +0 -66
  281. package/skills/direct/references/request-signals.md +0 -59
  282. package/skills/doctor/SKILL.md +0 -299
  283. package/skills/doctor/report.md +0 -102
  284. package/skills/feedback/SKILL.md +0 -98
  285. package/skills/feedback/references/transport-and-privacy.md +0 -65
  286. package/skills/feedback/report.md +0 -36
  287. package/skills/install/SKILL.md +0 -217
  288. package/skills/intent-continuing/SKILL.md +0 -154
  289. package/skills/intent-continuing/references/board-fill.md +0 -43
  290. package/skills/intent-continuing/references/boarding-matrix.md +0 -34
  291. package/skills/intent-continuing/references/context-management.md +0 -28
  292. package/skills/intent-continuing/references/liveness-ranking.md +0 -57
  293. package/skills/intent-creating/SKILL.md +0 -164
  294. package/skills/intent-creating/evals/evals.json +0 -72
  295. package/skills/intent-creating/references/lifecycle.md +0 -81
  296. package/skills/intent-creating/references/wikilinks.md +0 -8
  297. package/skills/intent-ending/SKILL.md +0 -176
  298. package/skills/intent-ending/evals/evals.json +0 -74
  299. package/skills/intent-executing/SKILL.md +0 -170
  300. package/skills/intent-executing/evals/evals.json +0 -66
  301. package/skills/intent-executing/implementer-prompt.md +0 -42
  302. package/skills/intent-executing/spec-reviewer-prompt.md +0 -27
  303. package/skills/intent-speccing/SKILL.md +0 -130
  304. package/skills/intent-speccing/evals/evals.json +0 -126
  305. package/skills/intent-speccing/references/design-principles.md +0 -44
  306. package/skills/intent-speccing/references/per-section-fill-rules.md +0 -92
  307. package/skills/intent-speccing/references/self-verify-checklist.md +0 -37
  308. package/skills/project-creating/SKILL.md +0 -162
  309. package/skills/project-creating/references/hubs-projects.md +0 -55
  310. package/skills/project-creating/references/project-scaffolding.md +0 -97
  311. package/skills/releasing/SKILL.md +0 -337
  312. package/skills/releasing/references/deprecations.md +0 -60
  313. package/skills/releasing/references/promotion-and-tagging.md +0 -66
  314. package/skills/releasing/references/release-lines.md +0 -105
  315. package/skills/roadmap/SKILL.md +0 -64
  316. package/skills/roadmap/references/file-format.md +0 -124
  317. package/skills/roadmap/references/operations.md +0 -112
  318. package/skills/rollback/SKILL.md +0 -91
  319. package/skills/tutorial/SKILL.md +0 -65
  320. package/skills/tutorial/evals/evals.json +0 -186
  321. package/skills/uninstall/SKILL.md +0 -75
  322. package/skills/update/SKILL.md +0 -126
  323. /package/{skills/auto/references → docs/help}/agent-report-contract.md +0 -0
  324. /package/{skills/intent-executing → docs/help}/code-quality-reviewer-prompt.md +0 -0
  325. /package/{skills/conventions/references → docs/help}/lifecycle-and-savepoints.md +0 -0
  326. /package/{skills/intent-executing → docs/help}/plan-reviewer-prompt.md +0 -0
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+ require_relative "../legacy"
5
+
6
+ # `plastic project links` - projects every store's `## Links` sections from
7
+ # frontmatter (`project-links`, run through Legacy).
8
+ module Plastic
9
+ class CLI
10
+ module Commands
11
+ class ProjectLinks < Command
12
+ USAGE_LINE = "plastic project links [--dry-run] [--json]"
13
+
14
+ def initialize(argv, runner: nil, **streams)
15
+ super(argv, **streams)
16
+ @runner = runner
17
+ end
18
+
19
+ def call
20
+ status = legacy.run("project-links", "--plastic-home", scope.plastic_home, *dry_run_flag)
21
+ raise Failure, "project-links exited #{status}" unless status.zero?
22
+
23
+ if options[:dry_run]
24
+ @output.next_step("none", because: "the preview wrote no files")
25
+ else
26
+ @output.next_step("plastic status", because: "the Links sections just rebuilt across every store")
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def switches(parser)
33
+ parser.on("--dry-run") { @options[:dry_run] = true }
34
+ end
35
+
36
+ def dry_run_flag
37
+ options[:dry_run] ? ["--dry-run"] : []
38
+ end
39
+
40
+ def legacy
41
+ @legacy ||= Legacy.new(env: @env, runner: @runner, output: @output, json: options[:json])
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+
5
+ # `plastic project list` - every store this machine holds, global and
6
+ # project, straight from Scope#stores, in this process.
7
+ module Plastic
8
+ class CLI
9
+ module Commands
10
+ class ProjectList < Command
11
+ USAGE_LINE = "plastic project list [--json]"
12
+
13
+ def call
14
+ scope.stores.each { |store| @output.row(store[:slug], store[:root]) }
15
+ @output.next_step("plastic status", because: "status shows what is active in each store this just listed")
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "date"
5
+ require_relative "../command"
6
+ require_relative "../legacy"
7
+ require_relative "../../store_provisioning"
8
+
9
+ # `plastic project new` - registers a project in projects.yml, then provisions
10
+ # and validates its store (`provision-project-store`, `validate-project`, run
11
+ # through Legacy). The project directory must already exist; this command
12
+ # never makes one. A slug is lowercase letters, digits and hyphens, starting
13
+ # with a letter or digit, and never `global`. A slug or a path already
14
+ # registered is a Failure.
15
+ module Plastic
16
+ class CLI
17
+ module Commands
18
+ class ProjectNew < Command
19
+ USAGE_LINE = "plastic project new SLUG --path PATH [--parent ID] [--json]"
20
+
21
+ SLUG = /\A[a-z0-9][a-z0-9-]*\z/
22
+ RESERVED = "global"
23
+
24
+ def initialize(argv, runner: nil, **streams)
25
+ super(argv, **streams)
26
+ @runner = runner
27
+ end
28
+
29
+ def call
30
+ raise Usage, "SLUG is required" if slug.to_s.empty?
31
+ raise Usage, "--path is required" if options[:path].to_s.empty?
32
+ unless slug.match?(SLUG) && slug != RESERVED
33
+ raise Usage, "SLUG must be lowercase letters, digits and hyphens, start with a letter or digit, and not be global"
34
+ end
35
+ raise Failure, "#{options[:path]} does not exist" unless Dir.exist?(options[:path])
36
+ raise Failure, "#{options[:path]} has no AGENTS.md; write one first" unless File.file?(File.join(options[:path], "AGENTS.md"))
37
+ raise Failure, "#{slug} is already registered" if StoreProvisioning.registered?(slug, scope.plastic_home)
38
+ owner = slug_for_path
39
+ raise Failure, "#{options[:path]} is already registered as #{owner}" if owner
40
+
41
+ register
42
+ run("provision-project-store", slug, "--home", scope.plastic_home)
43
+ run("validate-project", slug, "--home", scope.plastic_home)
44
+ @output.next_step("plastic project links", because: "the new store's Links section needs projecting before anything cites it")
45
+ end
46
+
47
+ private
48
+
49
+ def slug
50
+ arguments.first
51
+ end
52
+
53
+ def switches(parser)
54
+ parser.on("--path PATH") { |v| @options[:path] = v }
55
+ parser.on("--parent ID") { |v| @options[:parent] = v }
56
+ end
57
+
58
+ def slug_for_path
59
+ path = File.realpath(options[:path])
60
+ projects.find do |_slug, info|
61
+ registered = info.is_a?(Hash) && info["path"].is_a?(String) && info["path"]
62
+ registered && Dir.exist?(registered) && File.realpath(registered) == path
63
+ end&.first
64
+ end
65
+
66
+ def projects
67
+ data = File.exist?(projects_yml) ? YAML.safe_load_file(projects_yml) : nil
68
+ (data.is_a?(Hash) && data["projects"].is_a?(Hash)) ? data["projects"] : {}
69
+ end
70
+
71
+ def projects_yml
72
+ File.join(scope.plastic_home, "projects.yml")
73
+ end
74
+
75
+ def register
76
+ data = File.exist?(projects_yml) ? (YAML.safe_load_file(projects_yml) || {}) : {}
77
+ data["projects"] ||= {}
78
+ entry = {"path" => File.expand_path(options[:path]), "registered" => Date.today.to_s, "status" => "active"}
79
+ entry["parent"] = options[:parent] if options[:parent]
80
+ data["projects"][slug] = entry
81
+ File.write(projects_yml, YAML.dump(data))
82
+ end
83
+
84
+ def run(script, *args)
85
+ status = legacy.run(script, *args)
86
+ raise Failure, "#{script} exited #{status}" unless status.zero?
87
+ end
88
+
89
+ def legacy
90
+ @legacy ||= Legacy.new(env: @env, runner: @runner, output: @output, json: options[:json])
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,31 @@
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 Query < Command
10
+ USAGE_LINE = "plastic query SQL [--json]"
11
+
12
+ def call
13
+ raise Usage, "SQL is missing" if arguments.empty?
14
+ raise Failure, "no index at #{database}; run plastic index" unless File.file?(database)
15
+
16
+ rows = Sqlite.call(database, arguments.join(" "), readonly: true)
17
+ rows.each_with_index { |row, index| @output.row((index + 1).to_s, row.map { |key, value| "#{key}=#{value}" }.join(" ")) }
18
+ @output.next_step("none", because: "the rows are the whole answer; the database is opened read-only")
19
+ rescue Sqlite::Error => e
20
+ raise Failure, e.message
21
+ end
22
+
23
+ private
24
+
25
+ def database
26
+ SearchIndex.path(scope.plastic_home)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+
5
+ module Plastic
6
+ class CLI
7
+ module Commands
8
+ class Render < Command
9
+ USAGE_LINE = "plastic render FILE"
10
+ STYLE = File.expand_path("../../../../templates/render.css", __dir__)
11
+ # A leading YAML block is metadata; RDoc would render it as body text.
12
+ FRONTMATTER = /\A---\r?\n.*?^---[ \t]*(\r?\n|\z)/m
13
+
14
+ def call
15
+ file = arguments.first or raise Usage, "FILE is the markdown file to render"
16
+ raise Failure, "no file at #{file}" unless File.file?(file)
17
+
18
+ require "rubygems"
19
+ require "rdoc"
20
+ body = RDoc::Markdown.parse(File.read(file, encoding: "UTF-8").sub(FRONTMATTER, "")).accept(html_formatter)
21
+ @output.raw("<!doctype html>\n<meta charset=\"utf-8\">\n<title>#{File.basename(file)}</title>\n" \
22
+ "<style>\n#{File.read(STYLE)}</style>\n#{body}")
23
+ end
24
+
25
+ private
26
+
27
+ def html_formatter(formatter_class = RDoc::Markup::ToHtml)
28
+ initializer = formatter_class.instance_method(:initialize)
29
+ options = (initializer.parameters.any? { |_, name| name == :options }) ? [RDoc::Options.new] : []
30
+ formatter_class.new(*options)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "subcommand_list"
4
+
5
+ # `plastic roadmap` - the list of `plastic roadmap ...` 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 Roadmap < SubcommandList
12
+ USAGE_LINE = "plastic roadmap SUBCOMMAND [options]"
13
+ PREFIX = "roadmap"
14
+ NEXT = "plastic roadmap next"
15
+ BECAUSE = "next names the roadmap most worth continuing, with no slug to guess"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+ require_relative "../legacy"
5
+
6
+ # `plastic roadmap check` - the roadmap graph's batches, ready set and any
7
+ # cycle or dangling id (`roadmap-graph check`, run through Legacy).
8
+ #
9
+ # A roadmap with no `## Graph` section has nothing to check, and the script's
10
+ # own exit code says only that something failed. That case is named here, with
11
+ # the command that repairs it, so the reader never has to know which script ran.
12
+ module Plastic
13
+ class CLI
14
+ module Commands
15
+ class RoadmapCheck < Command
16
+ USAGE_LINE = "plastic roadmap check SLUG [--json]"
17
+
18
+ def initialize(argv, runner: nil, **streams)
19
+ super(argv, **streams)
20
+ @runner = runner
21
+ end
22
+
23
+ def call
24
+ raise Usage, "SLUG is required" if slug.to_s.empty?
25
+ raise Failure, "no roadmap #{slug} in #{File.basename(scope.roadmaps_dir)}" unless File.file?(path)
26
+ raise Failure, "#{slug} has no ## Graph section; plastic roadmap migrate #{slug} writes one from its batches" unless graph?
27
+
28
+ status = legacy.run("roadmap-graph", "check", path)
29
+ raise Failure, "roadmap-graph exited #{status}" unless status.zero?
30
+
31
+ @output.next_step("plastic roadmap show #{slug}", because: "the graph just checked out clean")
32
+ end
33
+
34
+ private
35
+
36
+ def graph?
37
+ File.foreach(path).any? { |line| line.start_with?("## Graph") }
38
+ end
39
+
40
+ def slug
41
+ arguments.first
42
+ end
43
+
44
+ def path
45
+ File.join(scope.roadmaps_dir, "#{slug}.md")
46
+ end
47
+
48
+ def legacy
49
+ @legacy ||= Legacy.new(env: @env, runner: @runner, output: @output, json: options[:json])
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+ require_relative "../legacy"
5
+
6
+ # `plastic roadmap log` - appends a savepoint line to a roadmap's ledger
7
+ # (`roadmap-savepoint append`, run through Legacy).
8
+ module Plastic
9
+ class CLI
10
+ module Commands
11
+ class RoadmapLog < Command
12
+ USAGE_LINE = 'plastic roadmap log SLUG EVENT "TEXT" [--json]'
13
+
14
+ def initialize(argv, runner: nil, **streams)
15
+ super(argv, **streams)
16
+ @runner = runner
17
+ end
18
+
19
+ def call
20
+ raise Usage, "SLUG is required" if slug.to_s.empty?
21
+ raise Usage, "EVENT is required" if event.to_s.empty?
22
+ raise Usage, "TEXT is required" if text.to_s.empty?
23
+
24
+ status = legacy.run("roadmap-savepoint", "append", "--roadmap", path, "--event", event, "--detail", text)
25
+ raise Failure, "roadmap-savepoint exited #{status}" unless status.zero?
26
+
27
+ @output.next_step("plastic roadmap show #{slug}", because: "the log line just landed on the state screen")
28
+ end
29
+
30
+ private
31
+
32
+ def slug
33
+ arguments[0]
34
+ end
35
+
36
+ def event
37
+ arguments[1]
38
+ end
39
+
40
+ def text
41
+ arguments[2]
42
+ end
43
+
44
+ def path
45
+ File.join(scope.roadmaps_dir, "#{slug}.md")
46
+ end
47
+
48
+ def legacy
49
+ @legacy ||= Legacy.new(env: @env, runner: @runner, output: @output, json: options[:json])
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+ require_relative "../legacy"
5
+
6
+ # `plastic roadmap migrate` - writes a roadmap's `## Graph` section from the
7
+ # batch headings it already carries (`roadmap-graph migrate`, run through
8
+ # Legacy). This is the repair `plastic roadmap check` names when a roadmap has
9
+ # no graph, so the CLI can perform every repair it asks for.
10
+ module Plastic
11
+ class CLI
12
+ module Commands
13
+ class RoadmapMigrate < Command
14
+ USAGE_LINE = "plastic roadmap migrate SLUG [--dry-run] [--json]"
15
+
16
+ def initialize(argv, runner: nil, **streams)
17
+ super(argv, **streams)
18
+ @runner = runner
19
+ end
20
+
21
+ def call
22
+ raise Usage, "SLUG is required" if slug.to_s.empty?
23
+ raise Failure, "no roadmap #{slug} in #{File.basename(scope.roadmaps_dir)}" unless File.file?(path)
24
+
25
+ arguments_for_script = ["migrate", path]
26
+ arguments_for_script << "--dry-run" if options[:dry_run]
27
+ status = legacy.run("roadmap-graph", *arguments_for_script)
28
+ raise Failure, "roadmap-graph exited #{status}" unless status.zero?
29
+
30
+ @output.next_step("plastic roadmap check #{slug}", because: "a written graph is what check reads")
31
+ end
32
+
33
+ private
34
+
35
+ def switches(parser)
36
+ parser.on("--dry-run") { @options[:dry_run] = true }
37
+ end
38
+
39
+ def slug
40
+ arguments.first
41
+ end
42
+
43
+ def path
44
+ File.join(scope.roadmaps_dir, "#{slug}.md")
45
+ end
46
+
47
+ def legacy
48
+ @legacy ||= Legacy.new(env: @env, runner: @runner, output: @output, json: options[:json])
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require_relative "../command"
5
+ require_relative "../legacy"
6
+
7
+ # `plastic roadmap next` - the roadmap most worth continuing right now
8
+ # (`roadmap-next --which`, run through Legacy). The script speaks JSON, so this
9
+ # command renders it as a screen and leaves the document to `--json`, the way
10
+ # every other command reads.
11
+ module Plastic
12
+ class CLI
13
+ module Commands
14
+ class RoadmapNext < Command
15
+ USAGE_LINE = "plastic roadmap next [--json]"
16
+ AFTER = "plastic roadmap show SLUG"
17
+ BECAUSE = "the winner it just named is the slug that command wants"
18
+ TIED = "the roadmaps tie; the first is shown, and any of them may be picked"
19
+
20
+ def initialize(argv, runner: nil, **streams)
21
+ super(argv, **streams)
22
+ @runner = runner
23
+ end
24
+
25
+ def call
26
+ report = parse(*legacy.capture("roadmap-next", "--roadmaps-dir", scope.roadmaps_dir, "--which"))
27
+ screen(report)
28
+ @output.next_step(after(report), because: (report["state"] == "tie") ? TIED : BECAUSE)
29
+ end
30
+
31
+ private
32
+
33
+ def parse(text, status)
34
+ raise Failure, "roadmap-next exited #{status}" unless status.zero?
35
+
36
+ JSON.parse(text)
37
+ rescue JSON::ParserError
38
+ raise Failure, "roadmap-next did not print a report"
39
+ end
40
+
41
+ def after(report)
42
+ slug = (report["roadmap"] || tied(report).first).to_s
43
+ slug.empty? ? AFTER : AFTER.sub("SLUG", slug)
44
+ end
45
+
46
+ # roadmap-next --which reports a tie as state "tie" with "tie" false and
47
+ # each candidate as an object; older reports carried plain names.
48
+ def tied(report)
49
+ Array(report["tie_candidates"]).map { |candidate| candidate.is_a?(Hash) ? candidate["roadmap"] : candidate }
50
+ end
51
+
52
+ def screen(report)
53
+ @output.row("state", report["state"])
54
+ @output.row("roadmap", report["roadmap"] || "none")
55
+ @output.row("wave", report["frontier_wave"]) unless report["frontier_wave"].to_s.empty?
56
+ @output.row("next", queue(report["dispatchable_queue"]))
57
+ @output.row("delivering", entries(report["in_flight"]))
58
+ @output.row("blocked", entries(report["blocked"]))
59
+ @output.row("tie", tied(report).join(", ")) if report["tie"] || report["state"] == "tie"
60
+ end
61
+
62
+ def queue(list)
63
+ rows = entries(list)
64
+ rows.empty? ? "none" : rows
65
+ end
66
+
67
+ def entries(list)
68
+ Array(list).map { |entry| [entry["id"], entry["status"], entry["wave"]].compact.join(" ") }
69
+ end
70
+
71
+ def legacy
72
+ @legacy ||= Legacy.new(env: @env, runner: @runner, output: @output, json: options[:json])
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+ require_relative "../legacy"
5
+ require_relative "../../roadmap_graph"
6
+
7
+ # `plastic roadmap show` - a roadmap's state screen (`report-screen roadmap
8
+ # FILE state`, run through Legacy). SLUG is required: `plastic roadmap next`
9
+ # is the command that picks one when the caller has none in mind.
10
+ module Plastic
11
+ class CLI
12
+ module Commands
13
+ class RoadmapShow < Command
14
+ USAGE_LINE = "plastic roadmap show SLUG [--json]"
15
+
16
+ def initialize(argv, runner: nil, **streams)
17
+ super(argv, **streams)
18
+ @runner = runner
19
+ end
20
+
21
+ def call
22
+ raise Usage, "SLUG is required" if slug.to_s.empty?
23
+
24
+ status = legacy.run("report-screen", "roadmap", path, "state")
25
+ raise Failure, "report-screen exited #{status}" unless status.zero?
26
+
27
+ problems = graph_problems
28
+ problems.each { |problem| @output.row("warning", problem) }
29
+ if problems.empty?
30
+ @output.next_step("plastic roadmap log #{slug} EVENT \"TEXT\"", because: "a savepoint is how this state screen keeps moving")
31
+ else
32
+ @output.next_step("plastic roadmap check #{slug}", because: "the graph has problems the state screen cannot show")
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ # A cycle or a graph id no batch lists (acceptance N8). The state screen
39
+ # cannot show either, so they are named under it.
40
+ def graph_problems
41
+ return [] unless File.file?(path)
42
+
43
+ result = RoadmapGraph.analyze(path)
44
+ problems = []
45
+ problems << "cyclic graph: #{result[:cycle].join(" > ")}" if result[:cycle]
46
+ dangling = Array(result[:dangling])
47
+ problems << "graph names #{dangling.join(", ")}, no batch entry" unless dangling.empty?
48
+ problems
49
+ end
50
+
51
+ def slug
52
+ arguments.first
53
+ end
54
+
55
+ def path
56
+ File.join(scope.roadmaps_dir, "#{slug}.md")
57
+ end
58
+
59
+ def legacy
60
+ @legacy ||= Legacy.new(env: @env, runner: @runner, output: @output, json: options[:json])
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "installer_verb"
4
+
5
+ # `plastic rollback` - steps back to a version this machine has already run. The
6
+ # work is still scripts/rollback.rb, run through Legacy.
7
+ module Plastic
8
+ class CLI
9
+ module Commands
10
+ class Rollback < InstallerVerb
11
+ USAGE_LINE = "plastic rollback [--version VERSION] [--downgrade] [--upgrade] [--reinstall]"
12
+ FLAGS = %w[--version --downgrade --upgrade --reinstall].freeze
13
+
14
+ SCRIPT = "rollback.rb"
15
+ AFTER = "plastic version"
16
+ BECAUSE = "the version says which build is installed"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+ require_relative "../../search_index"
5
+ require_relative "../../rlm/query"
6
+
7
+ module Plastic
8
+ class CLI
9
+ module Commands
10
+ class Search < Command
11
+ USAGE_LINE = "plastic search TERMS [--ask] [--project SLUG] [--limit N] [--json]"
12
+ DEFAULT_LIMIT = 10
13
+
14
+ def call
15
+ raise Usage, "TERMS is missing" if arguments.empty?
16
+ raise Failure, "no index at #{database}; run plastic index" unless File.file?(database)
17
+
18
+ rows = options[:ask] ? RLM::Query.new(RLM::Probe.new(corpus, limit: limit)).call(arguments.join(" ")) : found(arguments, limit)
19
+ rows.each { |row| @output.row(row["path"], row["excerpt"].gsub(/\s+/, " ").strip) }
20
+ @output.row("result", "no match") if rows.empty?
21
+ @output.next_step("none", because: "the search is complete")
22
+ rescue Sqlite::Error => e
23
+ raise Failure, e.message
24
+ end
25
+
26
+ private
27
+
28
+ def switches(parser)
29
+ parser.on("--ask") { @options[:ask] = true }
30
+ parser.on("--project SLUG") { |slug| @options[:project] = slug }
31
+ parser.on("--limit N", Integer) { |limit| @options[:limit] = limit }
32
+ end
33
+
34
+ def limit
35
+ options[:limit] || DEFAULT_LIMIT
36
+ end
37
+
38
+ def found(terms, count)
39
+ SearchIndex.search(scope.plastic_home, terms, limit: count, prefix: prefix)
40
+ end
41
+
42
+ def corpus
43
+ RLM::Corpus.new(method(:found))
44
+ end
45
+
46
+ def database
47
+ SearchIndex.path(scope.plastic_home)
48
+ end
49
+
50
+ def prefix
51
+ return "" unless options[:project]
52
+
53
+ home = scope.plastic_home
54
+ root = (scope.slug == Scope::GLOBAL) ? StoreLayout.global_store(home) : StoreLayout.project_root(home, scope.slug)
55
+ "#{root.delete_prefix("#{home}/")}/"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "subcommand_list"
4
+
5
+ # `plastic session` - the list of `plastic session ...` subcommands, straight
6
+ # out of the table.
7
+ module Plastic
8
+ class CLI
9
+ module Commands
10
+ class Session < SubcommandList
11
+ USAGE_LINE = "plastic session SUBCOMMAND [options]"
12
+ PREFIX = "session"
13
+ NEXT = 'plastic session commit "SUMMARY"'
14
+ BECAUSE = "a commit is the smallest unit every other session subcommand builds on"
15
+ end
16
+ end
17
+ end
18
+ end