@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
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
# Exit codes: 0 promoted (prints the intent dir last), 1 new-intent failed,
|
|
22
22
|
# 2 usage error or nothing to promote.
|
|
23
23
|
|
|
24
|
+
require_relative "lib/store_layout"
|
|
24
25
|
require "open3"
|
|
25
26
|
require "yaml"
|
|
26
27
|
require_relative "lib/session_ledger"
|
|
@@ -61,7 +62,7 @@ usage_abort("--day must be a real calendar date, YYYYMMDD") unless SessionLedger
|
|
|
61
62
|
usage_abort("--match is required") if opts[:match].nil? || opts[:match].empty?
|
|
62
63
|
|
|
63
64
|
plastic_home = File.expand_path(opts[:plastic_home] || ENV.fetch("PLASTIC_HOME", "~/.plastic"))
|
|
64
|
-
store = opts[:store] ||
|
|
65
|
+
store = opts[:store] || Plastic::StoreLayout.global_store(plastic_home)
|
|
65
66
|
templates = opts[:templates] || File.expand_path("../templates", __dir__)
|
|
66
67
|
session = opts[:session] ? SessionLedger.short_session_id(opts[:session]) : nil
|
|
67
68
|
|
|
@@ -69,7 +70,7 @@ target_store = store
|
|
|
69
70
|
if opts[:project]
|
|
70
71
|
projects = StoreProvisioning.load_projects(plastic_home)
|
|
71
72
|
usage_abort("--project #{opts[:project]} is not a registered project") unless projects.key?(opts[:project])
|
|
72
|
-
target_store = File.join(plastic_home,
|
|
73
|
+
target_store = File.join(Plastic::StoreLayout.project_root(plastic_home, opts[:project]), "store")
|
|
73
74
|
end
|
|
74
75
|
|
|
75
76
|
# --- pick the target line ---------------------------------------------------
|
package/scripts/read-config
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
-
# Usage: read-config <key> [--default VALUE] [--project PATH]
|
|
2
|
+
# Usage: read-config <key> [--default VALUE] [--project PATH] [--harness claude|codex]
|
|
3
3
|
# Resolves a config value: project -> global -> built-in defaults.
|
|
4
4
|
# Nested keys use dot notation: agent.type, architect.style
|
|
5
5
|
# Returns scalar values as strings, hash/array values as JSON.
|
|
@@ -14,9 +14,9 @@ require_relative "lib/agent_models"
|
|
|
14
14
|
DEFAULTS = {
|
|
15
15
|
"version" => 3,
|
|
16
16
|
"stale_threshold_days" => 3,
|
|
17
|
-
# Absolute token counts for a 1M window,
|
|
18
|
-
"context_offer_tokens" =>
|
|
19
|
-
"context_insist_tokens" =>
|
|
17
|
+
# Absolute token counts for a 1M window, 15 and 25 percent (intent 355, n5, D7).
|
|
18
|
+
"context_offer_tokens" => 150_000,
|
|
19
|
+
"context_insist_tokens" => 250_000,
|
|
20
20
|
"execution_mode" => "subagent-driven",
|
|
21
21
|
"hash_length" => 6,
|
|
22
22
|
"hash_algorithm" => "sha256-base36",
|
|
@@ -33,6 +33,12 @@ DEFAULTS = {
|
|
|
33
33
|
},
|
|
34
34
|
"architect" => {
|
|
35
35
|
"style" => nil
|
|
36
|
+
},
|
|
37
|
+
# Intent 340b (G7c, n4, D9): the Stop hook's runtime arm. Off until the
|
|
38
|
+
# owner's C32 ruling; read-config emits it as the string "false", which
|
|
39
|
+
# StopGate parses as a boolean itself rather than trusting.
|
|
40
|
+
"runner" => {
|
|
41
|
+
"stop_hook" => false
|
|
36
42
|
}
|
|
37
43
|
}.freeze
|
|
38
44
|
|
|
@@ -40,6 +46,7 @@ def parse_args(argv)
|
|
|
40
46
|
key = nil
|
|
41
47
|
default_val = nil
|
|
42
48
|
project_dir = nil
|
|
49
|
+
harness = nil
|
|
43
50
|
|
|
44
51
|
i = 0
|
|
45
52
|
while i < argv.length
|
|
@@ -52,13 +59,17 @@ def parse_args(argv)
|
|
|
52
59
|
abort "Error: --project requires a value" unless argv[i + 1]
|
|
53
60
|
project_dir = argv[i + 1]
|
|
54
61
|
i += 2
|
|
62
|
+
when "--harness"
|
|
63
|
+
abort "Error: --harness requires a value" unless argv[i + 1]
|
|
64
|
+
harness = argv[i + 1]
|
|
65
|
+
i += 2
|
|
55
66
|
else
|
|
56
67
|
key = argv[i]
|
|
57
68
|
i += 1
|
|
58
69
|
end
|
|
59
70
|
end
|
|
60
71
|
|
|
61
|
-
[key, default_val, project_dir]
|
|
72
|
+
[key, default_val, project_dir, harness]
|
|
62
73
|
end
|
|
63
74
|
|
|
64
75
|
def load_yaml(path)
|
|
@@ -113,10 +124,10 @@ if ARGV.include?("--migrate")
|
|
|
113
124
|
exit 0
|
|
114
125
|
end
|
|
115
126
|
|
|
116
|
-
key, explicit_default, project_dir = parse_args(ARGV)
|
|
127
|
+
key, explicit_default, project_dir, harness = parse_args(ARGV)
|
|
117
128
|
|
|
118
129
|
if key.nil? || key.empty?
|
|
119
|
-
$stderr.puts "Usage: read-config <key> [--default VALUE] [--project PATH]"
|
|
130
|
+
$stderr.puts "Usage: read-config <key> [--default VALUE] [--project PATH] [--harness claude|codex]"
|
|
120
131
|
exit 1
|
|
121
132
|
end
|
|
122
133
|
|
|
@@ -129,9 +140,42 @@ if project_dir
|
|
|
129
140
|
project_config = load_yaml(project_config_path)
|
|
130
141
|
end
|
|
131
142
|
|
|
132
|
-
|
|
133
|
-
|
|
143
|
+
def canonical_harness(value)
|
|
144
|
+
return nil if value.nil?
|
|
145
|
+
return "claude" if %w[claude claude-code].include?(value.to_s)
|
|
146
|
+
return "codex" if value.to_s == "codex"
|
|
147
|
+
|
|
148
|
+
abort "Error: --harness must be claude or codex"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def harness_agent_value(config, key, harness)
|
|
152
|
+
match = key.match(/\Aagents\.(models|efforts)\.([^.]+)\z/)
|
|
153
|
+
return nil unless match
|
|
154
|
+
|
|
155
|
+
section, agent = match.captures
|
|
156
|
+
if section == "models"
|
|
157
|
+
AgentModels.models_section(config, harness: harness)[agent]
|
|
158
|
+
else
|
|
159
|
+
AgentModels.efforts_section(config, harness)[agent]
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
harness = canonical_harness(harness)
|
|
164
|
+
if harness
|
|
165
|
+
value = harness_agent_value(project_config, key, harness)
|
|
166
|
+
value = harness_agent_value(global_config, key, harness) if value.nil?
|
|
167
|
+
else
|
|
168
|
+
value = dig_key(project_config, key)
|
|
169
|
+
value = dig_key(global_config, key) if value.nil?
|
|
170
|
+
end
|
|
134
171
|
value = explicit_default if value.nil? && explicit_default
|
|
172
|
+
if value.nil? && harness && (match = key.match(/\Aagents\.(models|efforts)\.([^.]+)\z/))
|
|
173
|
+
value = if match[1] == "models"
|
|
174
|
+
AgentModels.shipped_model_for(match[2], harness: harness)
|
|
175
|
+
else
|
|
176
|
+
AgentModels::DEFAULT_EFFORT
|
|
177
|
+
end
|
|
178
|
+
end
|
|
135
179
|
value = dig_key(DEFAULTS, key) if value.nil?
|
|
136
180
|
|
|
137
181
|
puts format_value(value)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
# ready-set - the CLI over ReadySet (intent 336, G3). No computed field this
|
|
6
|
+
# intent adds ships without a script that reads it (327 D18, C19): prints the
|
|
7
|
+
# ready order, the blockers for every unready node, the batches, and every
|
|
8
|
+
# maximal-length critical path; emits JSON for a caller like the runner (G7).
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# ready-set <intent_dir> [--json] [--ranker finish-first|critical-path]
|
|
12
|
+
#
|
|
13
|
+
# Exit codes (matching scripts/validate-work-graph's shape):
|
|
14
|
+
# 0 - printed (or emitted as JSON)
|
|
15
|
+
# 1 - the graph is invalid (missing, cyclic, or a malformed node file)
|
|
16
|
+
# 2 - usage: not an intent directory, or an unknown --ranker value
|
|
17
|
+
|
|
18
|
+
require "json"
|
|
19
|
+
require_relative "lib/ready_set"
|
|
20
|
+
require_relative "lib/savepoint"
|
|
21
|
+
|
|
22
|
+
module ReadySetCli
|
|
23
|
+
module_function
|
|
24
|
+
|
|
25
|
+
RANKERS = {
|
|
26
|
+
"finish-first" => -> { ReadySet::FinishFirstRanker.new },
|
|
27
|
+
"critical-path" => -> { ReadySet::CriticalPathRanker.new },
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
def usage
|
|
31
|
+
warn "Usage: ready-set <intent_dir> [--json] [--ranker #{RANKERS.keys.join('|')}]"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def intent_directory?(dir)
|
|
35
|
+
dir && File.directory?(dir) && File.exist?(Savepoint.intent_file(dir))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def main(argv)
|
|
39
|
+
args = argv.dup
|
|
40
|
+
json = !!args.delete("--json")
|
|
41
|
+
|
|
42
|
+
ranker_name = nil
|
|
43
|
+
if (i = args.index("--ranker"))
|
|
44
|
+
ranker_name = args[i + 1]
|
|
45
|
+
args.slice!(i, 2)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
intent_dir_arg = args.shift
|
|
49
|
+
unless intent_directory?(intent_dir_arg && File.expand_path(intent_dir_arg))
|
|
50
|
+
warn "ready-set: #{intent_dir_arg.inspect} is not an intent directory"
|
|
51
|
+
usage
|
|
52
|
+
return 2
|
|
53
|
+
end
|
|
54
|
+
intent_dir = File.expand_path(intent_dir_arg)
|
|
55
|
+
|
|
56
|
+
ranker = nil
|
|
57
|
+
if ranker_name
|
|
58
|
+
factory = RANKERS[ranker_name]
|
|
59
|
+
unless factory
|
|
60
|
+
warn "ready-set: unknown ranker #{ranker_name.inspect} (use #{RANKERS.keys.join(', ')})"
|
|
61
|
+
return 2
|
|
62
|
+
end
|
|
63
|
+
ranker = factory.call
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
analysis = ReadySet.analyze(intent_dir, ranker: ranker)
|
|
67
|
+
unless analysis[:ok]
|
|
68
|
+
warn "ready-set: #{analysis[:errors].join('; ')}"
|
|
69
|
+
return 1
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
if json
|
|
73
|
+
puts JSON.pretty_generate(json_payload(analysis))
|
|
74
|
+
else
|
|
75
|
+
print_report(analysis)
|
|
76
|
+
end
|
|
77
|
+
0
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def json_payload(analysis)
|
|
81
|
+
unready = analysis[:nodes].reject { |_, v| v[:ready] }
|
|
82
|
+
{
|
|
83
|
+
"ready" => analysis[:ranked_ready].map { |r| r[:id] },
|
|
84
|
+
"ranker" => analysis[:ranker_name],
|
|
85
|
+
"batches" => analysis[:batches],
|
|
86
|
+
"critical_path" => analysis[:critical_path],
|
|
87
|
+
"paths" => analysis[:paths],
|
|
88
|
+
"hops" => analysis[:hops],
|
|
89
|
+
"max_paths_total" => analysis[:max_paths_total],
|
|
90
|
+
"blockers" => unready.transform_values { |v| v[:blockers] },
|
|
91
|
+
"errors" => analysis[:errors],
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def print_report(analysis)
|
|
96
|
+
unready = analysis[:nodes].reject { |_, v| v[:ready] }
|
|
97
|
+
|
|
98
|
+
puts "Ranker: #{analysis[:ranker_name]}"
|
|
99
|
+
puts
|
|
100
|
+
puts "Ready (#{analysis[:ranked_ready].length}):"
|
|
101
|
+
if analysis[:ranked_ready].empty?
|
|
102
|
+
puts " (none)"
|
|
103
|
+
else
|
|
104
|
+
analysis[:ranked_ready].each { |r| puts " #{r[:id]}" }
|
|
105
|
+
end
|
|
106
|
+
puts
|
|
107
|
+
puts "Blockers:"
|
|
108
|
+
if unready.empty?
|
|
109
|
+
puts " (none)"
|
|
110
|
+
else
|
|
111
|
+
unready.each do |id, view|
|
|
112
|
+
puts " #{id}:"
|
|
113
|
+
view[:blockers].each { |b| puts " - #{b}" }
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
puts
|
|
117
|
+
puts "Batches:"
|
|
118
|
+
analysis[:batches].each_with_index { |batch, i| puts " #{i + 1}: #{batch.join(', ')}" }
|
|
119
|
+
puts
|
|
120
|
+
puts "Critical path (#{analysis[:hops]} hops): #{(analysis[:critical_path] || []).join(' > ')}"
|
|
121
|
+
puts "Every maximal-length path (#{analysis[:max_paths_total]} total, #{analysis[:paths].length} shown):"
|
|
122
|
+
analysis[:paths].each { |p| puts " #{p.join(' > ')}" }
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
exit(ReadySetCli.main(ARGV)) if $PROGRAM_NAME == __FILE__
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
# release-check - deterministic CLI over ReleaseGuard (intent 347).
|
|
6
|
+
#
|
|
7
|
+
# Guards a tagged publish before the workflow ever reaches `npm publish`:
|
|
8
|
+
# the pushed tag must equal `v` plus the version in package.json, the three
|
|
9
|
+
# repo version files must agree, and the runner's npm must meet the 11.5.1
|
|
10
|
+
# floor npm requires for OIDC trusted publishing. Writes the derived
|
|
11
|
+
# dist-tag and version to $GITHUB_OUTPUT, so the alpha/beta/latest rule has
|
|
12
|
+
# exactly one implementation, ReleaseGuard.dist_tag, shared with the test
|
|
13
|
+
# suite.
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
# release-check (--tag <tag> | --branch <branch>) --npm-version <version>
|
|
17
|
+
# [--github-output <path>] [--root <dir>]
|
|
18
|
+
#
|
|
19
|
+
# Exit codes: 0 (clean), 1 (violations; reported on stderr, one per line,
|
|
20
|
+
# each naming the specific thing that is wrong), 2 (usage).
|
|
21
|
+
|
|
22
|
+
require "json"
|
|
23
|
+
|
|
24
|
+
require_relative "lib/release_guard"
|
|
25
|
+
|
|
26
|
+
NPM_FLOOR = [11, 5, 1].freeze
|
|
27
|
+
KNOWN_FLAGS = %w[--tag --branch --npm-version --github-output --root].freeze
|
|
28
|
+
|
|
29
|
+
def usage
|
|
30
|
+
warn "usage: release-check (--tag <tag> | --branch <branch>) --npm-version <version> [--github-output <path>] [--root <dir>]"
|
|
31
|
+
exit 2
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def parse_args(argv)
|
|
35
|
+
opts = {}
|
|
36
|
+
i = 0
|
|
37
|
+
while i < argv.length
|
|
38
|
+
flag = argv[i]
|
|
39
|
+
usage unless KNOWN_FLAGS.include?(flag)
|
|
40
|
+
|
|
41
|
+
value = argv[i + 1]
|
|
42
|
+
usage if value.nil?
|
|
43
|
+
|
|
44
|
+
opts[flag] = value
|
|
45
|
+
i += 2
|
|
46
|
+
end
|
|
47
|
+
opts
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def read_version(path)
|
|
51
|
+
JSON.parse(File.read(path))["version"]
|
|
52
|
+
rescue Errno::ENOENT, JSON::ParserError
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
opts = parse_args(ARGV)
|
|
57
|
+
usage unless (opts["--tag"] || opts["--branch"]) && opts["--npm-version"]
|
|
58
|
+
|
|
59
|
+
root = opts["--root"] ? File.expand_path(opts["--root"]) : File.expand_path("..", __dir__)
|
|
60
|
+
package_json_path = File.join(root, "package.json")
|
|
61
|
+
plugin_json_path = File.join(root, ".claude-plugin", "plugin.json")
|
|
62
|
+
marketplace_json_path = File.join(root, ".claude-plugin", "marketplace.json")
|
|
63
|
+
|
|
64
|
+
violations = []
|
|
65
|
+
|
|
66
|
+
canonical_version = read_version(package_json_path)
|
|
67
|
+
violations << "could not read a version from #{package_json_path}" if canonical_version.nil?
|
|
68
|
+
|
|
69
|
+
branch = opts["--branch"]
|
|
70
|
+
dist_tag = nil
|
|
71
|
+
|
|
72
|
+
if canonical_version
|
|
73
|
+
expected_tag = "v#{canonical_version}"
|
|
74
|
+
tag = opts.fetch("--tag", expected_tag).sub(%r{\Arefs/tags/}, "")
|
|
75
|
+
if tag != expected_tag
|
|
76
|
+
violations << "tag #{tag} does not match package.json version #{canonical_version} (expected #{expected_tag})"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
dist_tag = ReleaseGuard.dist_tag(canonical_version)
|
|
80
|
+
if branch && ReleaseGuard::CHANNELS[branch] != dist_tag
|
|
81
|
+
violations << "branch #{branch} publishes #{ReleaseGuard::CHANNELS[branch] || "nothing"}, and version #{canonical_version} is #{dist_tag}"
|
|
82
|
+
end
|
|
83
|
+
stable = dist_tag == "latest"
|
|
84
|
+
|
|
85
|
+
result = ReleaseGuard.check(
|
|
86
|
+
package_json: package_json_path,
|
|
87
|
+
plugin_json: plugin_json_path,
|
|
88
|
+
marketplace_json: marketplace_json_path,
|
|
89
|
+
stable: stable
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
unless result.ok?
|
|
93
|
+
result.mismatches.each do |file|
|
|
94
|
+
violations << "version file #{file} does not agree with package.json (#{canonical_version})"
|
|
95
|
+
end
|
|
96
|
+
if stable && result.prerelease_suffix
|
|
97
|
+
violations << "stable cut #{canonical_version} carries the pre-release suffix -#{result.prerelease_suffix}"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
npm_version = opts["--npm-version"].to_s.strip
|
|
103
|
+
if npm_version.empty? || !npm_version.match?(/\A\d+(\.\d+)*\z/)
|
|
104
|
+
violations << "npm --version reported #{opts["--npm-version"].inspect}, which is empty or not numeric"
|
|
105
|
+
elsif (npm_version.split(".").map(&:to_i) <=> NPM_FLOOR) < 0
|
|
106
|
+
violations << "npm #{npm_version} is below the 11.5.1 floor OIDC trusted publishing requires"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
if violations.empty?
|
|
110
|
+
if opts["--github-output"]
|
|
111
|
+
File.open(opts["--github-output"], "a") do |f|
|
|
112
|
+
f.puts "version=#{canonical_version}"
|
|
113
|
+
f.puts "dist_tag=#{dist_tag}"
|
|
114
|
+
f.puts "tag=#{tag}"
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
puts "OK: tag #{tag}, version #{canonical_version}, dist-tag #{dist_tag}"
|
|
118
|
+
exit 0
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
warn "VIOLATIONS:"
|
|
122
|
+
violations.each { |v| warn "- #{v}" }
|
|
123
|
+
exit 1
|
package/scripts/report-screen
CHANGED
|
@@ -2,15 +2,22 @@
|
|
|
2
2
|
# encoding: UTF-8
|
|
3
3
|
# frozen_string_literal: true
|
|
4
4
|
|
|
5
|
-
# report-screen - the
|
|
6
|
-
# state, post-delivery delivered, and delay
|
|
5
|
+
# report-screen - the pre-delivery plan (intent 331b), the four delivery
|
|
6
|
+
# reports: mid-delivery state, post-delivery delivered, and delay (intent
|
|
7
|
+
# 317), plus session, the whole of one session's delivered work followed by
|
|
8
|
+
# the roster (intent 330). Each fills from the record via
|
|
7
9
|
# scripts/lib/report_screen.rb; no number here is written by eye.
|
|
8
10
|
#
|
|
9
11
|
# Usage:
|
|
12
|
+
# report-screen plan <intent_dir> [--ansi]
|
|
10
13
|
# report-screen state <intent_dir> [--changed "<text>"] [--ansi]
|
|
11
14
|
# report-screen state --all <store_root> [--changed "<text>"] [--ansi]
|
|
12
|
-
# report-screen delivered <intent_dir> [--ansi]
|
|
15
|
+
# report-screen delivered <intent_dir> [--ansi] [--repo <dir>]
|
|
13
16
|
# report-screen delay <intent_dir> [--ansi]
|
|
17
|
+
# report-screen session <tier_root> [--session <id>] [--since <iso>]
|
|
18
|
+
# [--ledger-root <dir>] [--ansi]
|
|
19
|
+
# report-screen roadmap <roadmap.md> plan|state|delivered [--ansi] [--store-root <dir>]
|
|
20
|
+
# report-screen archive <store_root> [--ansi]
|
|
14
21
|
#
|
|
15
22
|
# --ansi delegates to ScreenPaint (intent 317a, D1), the parser/re-layouter
|
|
16
23
|
# in the shared TUI core. Selection is by capability, never by harness:
|
|
@@ -23,24 +30,93 @@
|
|
|
23
30
|
# 2 - usage error, or the path/store is not what the verb expects (one line
|
|
24
31
|
# on stderr, stdout empty)
|
|
25
32
|
|
|
33
|
+
require "yaml"
|
|
34
|
+
require "shellwords"
|
|
26
35
|
require_relative "lib/report_screen"
|
|
27
36
|
require_relative "lib/intent_screen"
|
|
28
37
|
require_relative "lib/screen_paint"
|
|
38
|
+
require_relative "lib/session_ledger"
|
|
39
|
+
|
|
40
|
+
# Intent 331a (D6/R8): every caller-added screen kind file registers itself
|
|
41
|
+
# on load (ScreenPaint.register), so this glob is the ONLY wiring a new kind
|
|
42
|
+
# needs - no edit here, no edit to screen_paint.rb. Sorted for a
|
|
43
|
+
# deterministic load order; tolerates the directory being absent or empty
|
|
44
|
+
# (Dir.glob answers [] either way), which is this repo's own state today.
|
|
45
|
+
Dir.glob(File.join(__dir__, "lib", "screens", "*.rb")).sort.each { |f| require_relative f }
|
|
29
46
|
|
|
30
47
|
def usage_abort(message)
|
|
31
48
|
warn "report-screen: #{message}"
|
|
32
49
|
exit 2
|
|
33
50
|
end
|
|
34
51
|
|
|
35
|
-
# The one place allowed to shell out (D2/D20): resolves the
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
# The one place allowed to shell out (D2/D20): resolves the tag that CONTAINS
|
|
53
|
+
# the intent's merge commit, in the project's own repository, so the version
|
|
54
|
+
# on the delivered screen is the release that merge shipped in. The
|
|
55
|
+
# repository comes from --repo, else from projects.yml beside the store (the
|
|
56
|
+
# installed layout, <home>/projects/<slug>/store/<id>), else the repository
|
|
57
|
+
# this script lives in (the in-repo layout, `include_self: true`) - unless
|
|
58
|
+
# `include_self: false` (the branch reader, D23), in which case a global
|
|
59
|
+
# intent's repo never falls back to the script's own repository, since that
|
|
60
|
+
# is not the intent's repository at all. No merge sha, no repository, or no
|
|
61
|
+
# containing tag all answer nil, and the screen says "not recorded" rather
|
|
62
|
+
# than guessing from HEAD (D14). The pure module never reads git.
|
|
63
|
+
def resolve_repo(intent_dir, explicit_repo, script_dir, include_self: true)
|
|
64
|
+
candidates = []
|
|
65
|
+
candidates << File.expand_path(explicit_repo) if explicit_repo
|
|
66
|
+
if (m = intent_dir.match(%r{\A(.*)/(?:projects|stores)/([^/]+)/store/[^/]+\z}))
|
|
67
|
+
home, slug = m[1], m[2]
|
|
68
|
+
projects_yml = File.join(home, "projects.yml")
|
|
69
|
+
if File.exist?(projects_yml)
|
|
70
|
+
data = begin
|
|
71
|
+
YAML.safe_load(File.read(projects_yml))
|
|
72
|
+
rescue StandardError
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
entry = data.is_a?(Hash) && data["projects"].is_a?(Hash) ? data["projects"][slug] : nil
|
|
76
|
+
path = entry.is_a?(Hash) ? entry["path"].to_s : ""
|
|
77
|
+
candidates << File.expand_path(path) unless path.empty?
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
candidates << File.expand_path("..", script_dir) if include_self
|
|
81
|
+
candidates.find { |c| File.exist?(File.join(c, ".git")) }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def git_tag_reader(explicit_repo:, script_dir:)
|
|
85
|
+
lambda do |intent_dir|
|
|
86
|
+
sha = ReportScreen.merge_sha(intent_dir)
|
|
87
|
+
return nil unless sha
|
|
88
|
+
repo = resolve_repo(intent_dir, explicit_repo, script_dir)
|
|
89
|
+
return nil unless repo
|
|
90
|
+
tags = `git -C #{repo.shellescape} tag --contains #{sha.shellescape} 2>/dev/null`
|
|
91
|
+
versions = tags.lines.map(&:strip).reject(&:empty?).filter_map do |tag|
|
|
92
|
+
v = tag.sub(/\Av/, "")
|
|
93
|
+
Gem::Version.correct?(v) ? [Gem::Version.new(v), tag] : nil
|
|
94
|
+
end
|
|
95
|
+
versions.min_by(&:first)&.last
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# D9/D23: the injected branch reader. flow_base (a pure YAML read, never
|
|
100
|
+
# git) wins when the intent's project names a flow base; otherwise the
|
|
101
|
+
# repository's remote HEAD, else whichever of main/master exists. Resolves
|
|
102
|
+
# the repo with `include_self: false` (D23): a global-store intent must
|
|
103
|
+
# never fall back to the script's OWN repository (~/.plastic itself, on
|
|
104
|
+
# "main" in the installed layout) and print a confident, wrong branch.
|
|
105
|
+
def git_branch_reader(explicit_repo:, script_dir:)
|
|
106
|
+
lambda do |intent_dir|
|
|
107
|
+
flow = ReportScreen.flow_base(intent_dir)
|
|
108
|
+
next flow if flow && !flow.to_s.empty?
|
|
109
|
+
|
|
110
|
+
repo = resolve_repo(intent_dir, explicit_repo, script_dir, include_self: false)
|
|
111
|
+
next nil unless repo
|
|
112
|
+
|
|
113
|
+
branch = `git -C #{repo.shellescape} symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null`.strip
|
|
114
|
+
branch = branch.sub(%r{\Aorigin/}, "")
|
|
115
|
+
next branch unless branch.empty?
|
|
116
|
+
|
|
117
|
+
%w[main master].find do |b|
|
|
118
|
+
system("git", "-C", repo, "rev-parse", "--verify", "--quiet", b, out: File::NULL, err: File::NULL)
|
|
119
|
+
end
|
|
44
120
|
end
|
|
45
121
|
end
|
|
46
122
|
|
|
@@ -49,6 +125,11 @@ verb = args.shift
|
|
|
49
125
|
changed = nil
|
|
50
126
|
ansi = false
|
|
51
127
|
template_path = nil
|
|
128
|
+
repo_flag = nil
|
|
129
|
+
session_flag = nil
|
|
130
|
+
since_flag = nil
|
|
131
|
+
ledger_root_flag = nil
|
|
132
|
+
store_root_flag = nil
|
|
52
133
|
positional = []
|
|
53
134
|
|
|
54
135
|
while (arg = args.shift)
|
|
@@ -60,15 +141,25 @@ while (arg = args.shift)
|
|
|
60
141
|
ansi = true
|
|
61
142
|
when "--all"
|
|
62
143
|
positional << "--all"
|
|
144
|
+
when "--repo"
|
|
145
|
+
repo_flag = args.shift or usage_abort("--repo needs a path")
|
|
63
146
|
when "--template"
|
|
64
147
|
template_path = args.shift or usage_abort("--template needs a path")
|
|
148
|
+
when "--session"
|
|
149
|
+
session_flag = args.shift or usage_abort("--session needs a value")
|
|
150
|
+
when "--since"
|
|
151
|
+
since_flag = args.shift or usage_abort("--since needs a value")
|
|
152
|
+
when "--ledger-root"
|
|
153
|
+
ledger_root_flag = args.shift or usage_abort("--ledger-root needs a path")
|
|
154
|
+
when "--store-root"
|
|
155
|
+
store_root_flag = args.shift or usage_abort("--store-root needs a path")
|
|
65
156
|
else
|
|
66
157
|
usage_abort("unknown flag #{arg.inspect}") if arg.start_with?("--") && arg != "--all"
|
|
67
158
|
positional << arg
|
|
68
159
|
end
|
|
69
160
|
end
|
|
70
161
|
|
|
71
|
-
usage_abort("usage: report-screen state|delivered|delay <intent_dir> [--changed \"<text>\"] [--ansi]") unless verb
|
|
162
|
+
usage_abort("usage: report-screen plan|state|delivered|delay|session <intent_dir> [--changed \"<text>\"] [--ansi]") unless verb
|
|
72
163
|
|
|
73
164
|
all_mode = positional.delete("--all") ? true : false
|
|
74
165
|
target = positional.first
|
|
@@ -82,6 +173,16 @@ def paint(text, ansi_enabled, _renderer_path = nil)
|
|
|
82
173
|
end
|
|
83
174
|
|
|
84
175
|
case verb
|
|
176
|
+
when "plan"
|
|
177
|
+
usage_abort("usage: report-screen plan <intent_dir> [--ansi]") unless target
|
|
178
|
+
intent_dir = File.expand_path(target)
|
|
179
|
+
usage_abort("#{intent_dir} is not an intent directory") unless IntentScreen.intent_dir?(intent_dir)
|
|
180
|
+
store_root = File.expand_path("../..", intent_dir)
|
|
181
|
+
template_path ||= File.expand_path("../templates/report-plan.md", __dir__)
|
|
182
|
+
usage_abort("template not found at #{template_path}") unless File.exist?(template_path)
|
|
183
|
+
out = ReportScreen.render_plan(intent_dir: intent_dir, store_root: store_root,
|
|
184
|
+
template: File.read(template_path))
|
|
185
|
+
$stdout.write paint(out, ansi_enabled)
|
|
85
186
|
when "state"
|
|
86
187
|
if all_mode
|
|
87
188
|
usage_abort("usage: report-screen state --all <store_root>") unless target
|
|
@@ -104,8 +205,9 @@ when "delivered"
|
|
|
104
205
|
usage_abort("usage: report-screen delivered <intent_dir>") unless target
|
|
105
206
|
intent_dir = File.expand_path(target)
|
|
106
207
|
usage_abort("#{intent_dir} is not an intent directory") unless IntentScreen.intent_dir?(intent_dir)
|
|
107
|
-
|
|
108
|
-
|
|
208
|
+
out = ReportScreen.render_delivered(intent_dir: intent_dir,
|
|
209
|
+
tag_reader: git_tag_reader(explicit_repo: repo_flag, script_dir: __dir__),
|
|
210
|
+
branch_reader: git_branch_reader(explicit_repo: repo_flag, script_dir: __dir__))
|
|
109
211
|
$stdout.write paint(out, ansi_enabled)
|
|
110
212
|
when "delay"
|
|
111
213
|
usage_abort("usage: report-screen delay <intent_dir>") unless target
|
|
@@ -113,8 +215,67 @@ when "delay"
|
|
|
113
215
|
usage_abort("#{intent_dir} is not an intent directory") unless IntentScreen.intent_dir?(intent_dir)
|
|
114
216
|
out = ReportScreen.render_delay(intent_dir: intent_dir)
|
|
115
217
|
$stdout.write paint(out, ansi_enabled)
|
|
218
|
+
when "session"
|
|
219
|
+
usage_abort("usage: report-screen session <tier_root> [--session <id>] [--since <iso>] [--ledger-root <dir>] [--ansi]") unless target
|
|
220
|
+
store_root = File.expand_path(target)
|
|
221
|
+
usage_abort("#{store_root} is not a store (no INDEX.md)") unless File.exist?(File.join(store_root, "INDEX.md"))
|
|
222
|
+
|
|
223
|
+
now = Time.now
|
|
224
|
+
session_id = session_flag || ENV[SessionLedger::SESSION_ID_ENV_KEY]
|
|
225
|
+
ledger_root = ledger_root_flag ? File.expand_path(ledger_root_flag) : ReportScreen.default_ledger_root(store_root)
|
|
226
|
+
|
|
227
|
+
if since_flag
|
|
228
|
+
begin
|
|
229
|
+
Time.parse(since_flag)
|
|
230
|
+
rescue ArgumentError
|
|
231
|
+
usage_abort("--since needs an ISO timestamp, got #{since_flag.inspect}")
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
dirs, skipped = ReportScreen.session_delivered_dirs(ledger_root: ledger_root, tier_root: store_root,
|
|
236
|
+
session: session_id, since: since_flag, now: now)
|
|
237
|
+
# D17: the widened window is announced whether the id was missing or simply
|
|
238
|
+
# matched nothing. A resumed background job carries a session id that is not
|
|
239
|
+
# the one on the ledger lines, and that case must not answer silently. An
|
|
240
|
+
# explicit --since is the caller naming the window, so it needs no note.
|
|
241
|
+
note =
|
|
242
|
+
if since_flag
|
|
243
|
+
nil
|
|
244
|
+
elsif session_id.nil? || session_id.to_s.strip.empty?
|
|
245
|
+
ReportScreen.window_note(ReportScreen.fallback_day(ledger_root, SessionLedger.day_id(now)),
|
|
246
|
+
"no session id given")
|
|
247
|
+
elsif !ReportScreen.session_tagged?(ledger_root: ledger_root, session: session_id, now: now)
|
|
248
|
+
ReportScreen.window_note(ReportScreen.fallback_day(ledger_root, SessionLedger.day_id(now)),
|
|
249
|
+
"this session has no line in the day ledger")
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
out = ReportScreen.render_session(
|
|
253
|
+
dirs: dirs, skipped: skipped, store_root: store_root,
|
|
254
|
+
tag_reader: git_tag_reader(explicit_repo: nil, script_dir: __dir__),
|
|
255
|
+
branch_reader: git_branch_reader(explicit_repo: nil, script_dir: __dir__),
|
|
256
|
+
note: note, now: now, painter: ->(text) { paint(text, ansi_enabled) }
|
|
257
|
+
)
|
|
258
|
+
$stdout.write out
|
|
259
|
+
when "roadmap"
|
|
260
|
+
usage_abort("usage: report-screen roadmap <roadmap.md> plan|state|delivered [--ansi] [--store-root <dir>]") if positional.empty?
|
|
261
|
+
roadmap_path = File.expand_path(positional[0])
|
|
262
|
+
usage_abort("#{roadmap_path} does not exist") unless File.exist?(roadmap_path)
|
|
263
|
+
sub_verb = positional[1]
|
|
264
|
+
usage_abort("usage: report-screen roadmap <roadmap.md> plan|state|delivered") unless sub_verb
|
|
265
|
+
unless %w[plan state delivered].include?(sub_verb)
|
|
266
|
+
usage_abort("unknown sub-verb #{sub_verb.inspect} (use plan|state|delivered)")
|
|
267
|
+
end
|
|
268
|
+
roadmap_store_root = store_root_flag ? File.expand_path(store_root_flag) : nil
|
|
269
|
+
out = ReportScreen.render_roadmap(path: roadmap_path, verb: sub_verb, store_root: roadmap_store_root)
|
|
270
|
+
$stdout.write paint(out, ansi_enabled)
|
|
271
|
+
when "archive"
|
|
272
|
+
usage_abort("usage: report-screen archive <store_root>") unless target
|
|
273
|
+
store_root = File.expand_path(target)
|
|
274
|
+
usage_abort("#{store_root} is not a store (no INDEX.md)") unless File.exist?(File.join(store_root, "INDEX.md"))
|
|
275
|
+
out = ReportScreen.render_archive(store_root)
|
|
276
|
+
$stdout.write paint(out, ansi_enabled)
|
|
116
277
|
else
|
|
117
|
-
usage_abort("unknown verb #{verb.inspect} (use state|delivered|delay)")
|
|
278
|
+
usage_abort("unknown verb #{verb.inspect} (use plan|state|delivered|delay|session|roadmap|archive)")
|
|
118
279
|
end
|
|
119
280
|
|
|
120
281
|
exit 0
|