@zalom/plastic 2.0.0-alpha.8 → 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 +23 -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 +62 -1
- 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 +238 -22
- 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 +19 -5
- 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 +30 -16
- 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 +532 -71
- package/scripts/lib/intent_screen.rb +9 -6
- 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 +377 -173
- 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 +1460 -74
- 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 +555 -0
- 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 +192 -30
- 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 +15 -2
- 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 -295
- 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 -173
- 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
package/scripts/end-intent
CHANGED
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
# 2. Move the intent's INDEX.md line from `## Active` into `## Completed`
|
|
38
38
|
# (delivered) or `## Abandoned` (abandoned), dated today, appending
|
|
39
39
|
# --index-note (if given). Accepts a real em dash OR a plain hyphen as
|
|
40
|
-
# the id/title separator on READ (shared matcher,
|
|
40
|
+
# the id/title separator on READ (shared matcher, IndexEntry.match);
|
|
41
41
|
# always EMITS the real em dash on write. An id that resolves to neither
|
|
42
42
|
# `## Active` nor the terminal section is a loud failure (exit 1); an id
|
|
43
43
|
# already correctly in the terminal section is a quiet, idempotent success.
|
|
@@ -46,10 +46,9 @@
|
|
|
46
46
|
# 5. Disarm (intent 188, D2/D16; intent 307): check the code worktree (derived
|
|
47
47
|
# from projects.yml and the intent id, Arm.worktree_block) for uncommitted
|
|
48
48
|
# changes first (exit 5 if dirty, unless --discard-worktree-changes); call the
|
|
49
|
-
# disarm seam (default Arm.disarm: worktree released, lock cleared
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
# /tmp bridge this step once read was removed in 2.0, intent 307).
|
|
49
|
+
# disarm seam (default Arm.disarm: worktree released, lock cleared); verify
|
|
50
|
+
# the durable lock file is actually gone afterward, and release it directly
|
|
51
|
+
# when a foreign-keyed disarm left it.
|
|
53
52
|
#
|
|
54
53
|
# Exit codes:
|
|
55
54
|
# 0 ok - the intent is closed and no delivery.lock remains.
|
|
@@ -57,7 +56,7 @@
|
|
|
57
56
|
# nor the terminal section (D11). Deliberately double duty; see plan.md.
|
|
58
57
|
# 2 retired in 2.0 (intent 308): the outcome.md guard reports and proceeds.
|
|
59
58
|
# 3 steps 1-4 already committed, but the delivery lock genuinely could not
|
|
60
|
-
# be cleared (
|
|
59
|
+
# be cleared (see step 5 above).
|
|
61
60
|
# 4 pre-flight refusal: a FRESH foreign lock is held by a live,
|
|
62
61
|
# non-delegate session; a FRESH lock that will not parse (corrupt); or a
|
|
63
62
|
# lock file with no resolvable session identity at all. Authors nothing.
|
|
@@ -66,18 +65,23 @@
|
|
|
66
65
|
# refused before removal; pass --discard-worktree-changes to override
|
|
67
66
|
# deliberately.
|
|
68
67
|
# 6 retired in 2.0 (intent 308): the structure check reports and proceeds.
|
|
68
|
+
# 7 hollow delivered report refused (317a D7) - intent 308's one deliberate
|
|
69
|
+
# exception to report-and-proceed; --allow-hollow-report overrides.
|
|
69
70
|
|
|
71
|
+
require_relative "lib/store_layout"
|
|
70
72
|
require "fileutils"
|
|
71
73
|
require "date"
|
|
72
74
|
require "open3"
|
|
73
75
|
require "pathname"
|
|
74
|
-
require_relative "lib/bridge"
|
|
75
76
|
require_relative "lib/arm"
|
|
77
|
+
require_relative "lib/index_entry"
|
|
76
78
|
require_relative "lib/savepoint"
|
|
77
79
|
require_relative "lib/lock"
|
|
78
80
|
require_relative "lib/intent_validator"
|
|
79
81
|
require_relative "lib/outcome_guard"
|
|
82
|
+
require_relative "lib/report_screen"
|
|
80
83
|
require_relative "lib/backfill_intent"
|
|
84
|
+
require_relative "lib/outcome_report"
|
|
81
85
|
|
|
82
86
|
DISPOSITIONS = %w[delivered abandoned].freeze
|
|
83
87
|
|
|
@@ -85,7 +89,7 @@ DISPOSITIONS = %w[delivered abandoned].freeze
|
|
|
85
89
|
# dash (U+2014), the existing INDEX.md convention (store files are exempt from
|
|
86
90
|
# the no-em-dash shipped-file rule). Built from the codepoint, not a literal
|
|
87
91
|
# byte in this source file, so the script's own source stays em-dash free.
|
|
88
|
-
# READING accepts a plain hyphen too, via the shared
|
|
92
|
+
# READING accepts a plain hyphen too, via the shared IndexEntry.match
|
|
89
93
|
# (intent 188, D9/D12): see move_index_to_terminal below.
|
|
90
94
|
EM_DASH = "\u2014"
|
|
91
95
|
|
|
@@ -95,10 +99,15 @@ class UnresolvedIndexEntry < StandardError; end
|
|
|
95
99
|
|
|
96
100
|
# --- Explicit flag parsing (no eval, no global injection) -------------------
|
|
97
101
|
|
|
102
|
+
def blank?(value)
|
|
103
|
+
value.nil? || value.to_s.strip.empty?
|
|
104
|
+
end
|
|
105
|
+
|
|
98
106
|
def parse_args(argv)
|
|
99
107
|
opts = {
|
|
100
108
|
store: nil, id: nil, disposition: nil, index: nil,
|
|
101
109
|
outcome_summary: nil, index_note: nil, no_commit: false, dry_run: false,
|
|
110
|
+
allow_hollow_report: false,
|
|
102
111
|
session: nil, discard_worktree_changes: false,
|
|
103
112
|
}
|
|
104
113
|
i = 0
|
|
@@ -115,6 +124,7 @@ def parse_args(argv)
|
|
|
115
124
|
when "--discard-worktree-changes" then opts[:discard_worktree_changes] = true
|
|
116
125
|
when "--no-commit" then opts[:no_commit] = true
|
|
117
126
|
when "--dry-run" then opts[:dry_run] = true
|
|
127
|
+
when "--allow-hollow-report" then opts[:allow_hollow_report] = true
|
|
118
128
|
else
|
|
119
129
|
usage_abort("unknown argument #{arg.inspect}")
|
|
120
130
|
end
|
|
@@ -161,14 +171,65 @@ end
|
|
|
161
171
|
# "projects" (the StoreDiscovery convention, scripts/lib/store_discovery.rb), never by
|
|
162
172
|
# hardcoding ".plastic" as a name.
|
|
163
173
|
def resolve_plastic_home_and_scope(store)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
174
|
+
Plastic::StoreLayout.home_and_scope(store)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# --- outcome generation, before the backfill (intent 339, G6, spec D10) -----
|
|
178
|
+
#
|
|
179
|
+
# Generates outcome.md from graph.md, nodes/, and the ledger, BEFORE the
|
|
180
|
+
# backfill step runs, and only into a file that is missing or still the
|
|
181
|
+
# scaffold placeholder (`Savepoint.stage_file_present?` false) - a real,
|
|
182
|
+
# hand-written outcome.md is never touched (row 7.2). Only when a graph.md
|
|
183
|
+
# exists at all (row 7.3): every intent without one closes exactly as it did
|
|
184
|
+
# before this intent. The model is parsed AFTER the `stage_file_present?`
|
|
185
|
+
# guard, not before: a real hand-written outcome.md returns at that guard
|
|
186
|
+
# regardless of graph.md's own health, so a close that was never going to
|
|
187
|
+
# generate anything must never print a parse warning about it. Refuses a
|
|
188
|
+
# report model that reports itself broken (row v1f.2, B2): a malformed
|
|
189
|
+
# `## Graph` fails `model[:ok]` and the close falls straight through to the
|
|
190
|
+
# backfill, rather than adopting parse damage as delivery fact - agreeing
|
|
191
|
+
# with the shipped `outcome-report` CLI, which exits 3 on the same
|
|
192
|
+
# directory. The generated text is then checked against
|
|
193
|
+
# BOTH close gates - `OutcomeGuard` and the hollow-report gate below - before
|
|
194
|
+
# being adopted (row 7.4); when either would refuse it, the write is
|
|
195
|
+
# reverted and the close falls through to intent 308's unchanged backfill
|
|
196
|
+
# (row 7.5), so no close gains an exit 7 it did not have before this intent.
|
|
197
|
+
# Fails open on any exception (row 7.6), the same report-and-proceed shape
|
|
198
|
+
# every other close step in this file uses. `generator:` is the injectable
|
|
199
|
+
# seam (mirrors run_structure_check's `gate:`).
|
|
200
|
+
def run_generate_outcome(intent_dir, disposition:, generator: OutcomeReport.method(:write))
|
|
201
|
+
graph_path = File.join(intent_dir, "graph.md")
|
|
202
|
+
return unless File.exist?(graph_path)
|
|
203
|
+
|
|
204
|
+
outcome_path = File.join(intent_dir, "outcome.md")
|
|
205
|
+
return if Savepoint.stage_file_present?(outcome_path)
|
|
206
|
+
|
|
207
|
+
report_model = OutcomeReport.model(intent_dir)
|
|
208
|
+
unless report_model[:ok]
|
|
209
|
+
warn "end-intent: graph.md failed to parse (#{report_model[:errors].join('; ')}); falling back to the backfill"
|
|
210
|
+
return
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
original_exists = File.exist?(outcome_path)
|
|
214
|
+
original_content = original_exists ? File.read(outcome_path) : nil
|
|
215
|
+
|
|
216
|
+
generator.call(intent_dir, disposition: disposition)
|
|
217
|
+
|
|
218
|
+
guard_reason = OutcomeGuard.reason(intent_dir, disposition)
|
|
219
|
+
hollow = hollow_report_reason(intent_dir, disposition)
|
|
220
|
+
if guard_reason || hollow
|
|
221
|
+
if original_exists
|
|
222
|
+
File.write(outcome_path, original_content)
|
|
223
|
+
elsif File.exist?(outcome_path)
|
|
224
|
+
File.delete(outcome_path)
|
|
225
|
+
end
|
|
226
|
+
warn "end-intent: generated outcome.md would be refused (#{[guard_reason, hollow].compact.join('; ')}); " \
|
|
227
|
+
"falling back to the backfill"
|
|
169
228
|
else
|
|
170
|
-
|
|
229
|
+
warn "end-intent: outcome.md generated from graph.md and the ledger"
|
|
171
230
|
end
|
|
231
|
+
rescue StandardError => e
|
|
232
|
+
warn "end-intent: outcome generation crashed (#{e.message}); proceeding without it"
|
|
172
233
|
end
|
|
173
234
|
|
|
174
235
|
# Backfill (intent 308): write the judgment documents that are missing or still the
|
|
@@ -215,7 +276,137 @@ end
|
|
|
215
276
|
# Replace the body of the intent file's `## Outcome` section with `summary`,
|
|
216
277
|
# leaving every other section and the frontmatter untouched. No-op (returns
|
|
217
278
|
# content unchanged) when summary is blank or the section is not found.
|
|
279
|
+
# 317a S9 (D7): the self-render gate. Renders the delivered screen's sources on
|
|
280
|
+
# the intent's own record and names what would come out hollow. This is intent
|
|
281
|
+
# 308's ONE deliberate exception to report-and-proceed: a hollow DELIVERED
|
|
282
|
+
# close is refused with exit 7 (--allow-hollow-report overrides), because 317
|
|
283
|
+
# closed exactly this way and the owner read a screen full of "not recorded".
|
|
284
|
+
# Abandoned closes and machine-backfilled outcomes (the backfill marker) are
|
|
285
|
+
# exempt: the reader cannot expect labels the writer never had.
|
|
286
|
+
#
|
|
287
|
+
# Intent 334 (G1, review A3; post-execution review, blocking 2): the legacy
|
|
288
|
+
# "S3" label grammar applies only to actions/*.md headings, and the node id
|
|
289
|
+
# grammar (kind prefix n/v/d/r plus digits, D1r) applies only to nodes/*.md
|
|
290
|
+
# headings - never both over both directories. This repo's own house words
|
|
291
|
+
# for plan review and post-execution review are "v1" and "v2"; widening the
|
|
292
|
+
# node-id alternative onto actions/ headings turned that prose into a
|
|
293
|
+
# manufactured label and refused legitimate legacy closes at exit 7.
|
|
294
|
+
ACTION_ID_LABEL_RE = /\AS\d+\z/.freeze
|
|
295
|
+
NODE_ID_LABEL_RE = /\A[nvdr][1-9]\d*\z/.freeze
|
|
296
|
+
|
|
297
|
+
# The id-shaped tokens found in `paths`' headings, matched against
|
|
298
|
+
# `id_label_re` only - the caller picks the grammar for the directory.
|
|
299
|
+
def heading_id_labels(paths, id_label_re)
|
|
300
|
+
paths.flat_map do |path|
|
|
301
|
+
ReportScreen.split_by_headings(File.read(path)).flat_map do |heading, _body|
|
|
302
|
+
heading.to_s.sub(/\A#+\s*/, "").split(/[^A-Za-z0-9]+/).select { |tok| tok.match?(id_label_re) }
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# 339 S9 (D18): a node id counts as a label in an actions/*.md heading too,
|
|
308
|
+
# but only when it stands alone as one of the heading's " - "-delimited
|
|
309
|
+
# segments - this repo's own convention, "### S1 - n1 - Title" (this action
|
|
310
|
+
# file's own headings, post-merge) or the bare "### n1 - Title". Never as a
|
|
311
|
+
# free word inside ordinary prose: the comment above ACTION_ID_LABEL_RE
|
|
312
|
+
# records exactly why intent 334 restricted the node-id grammar to
|
|
313
|
+
# nodes/*.md headings in the first place, and that reasoning does not change
|
|
314
|
+
# here - a word-scan widened onto actions/ headings would let this repo's own
|
|
315
|
+
# house words ("v1", "v2" for plan review and post-execution review)
|
|
316
|
+
# manufacture a label out of ordinary prose and refuse a legitimate legacy
|
|
317
|
+
# close. Segment position is what a word-scan cannot see: "v2" embedded
|
|
318
|
+
# mid-sentence is never its own " - "-delimited segment, but a heading built
|
|
319
|
+
# to this convention always puts the node id in one.
|
|
320
|
+
def heading_node_id_segments(paths, id_label_re)
|
|
321
|
+
paths.flat_map do |path|
|
|
322
|
+
ReportScreen.split_by_headings(File.read(path)).flat_map do |heading, _body|
|
|
323
|
+
heading.to_s.sub(/\A#+\s*/, "").split(/\s+-\s+/).map(&:strip).select { |seg| seg.match?(id_label_re) }
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def hollow_report_reason(intent_dir, disposition)
|
|
329
|
+
return nil unless disposition == "delivered"
|
|
330
|
+
|
|
331
|
+
outcome = File.join(intent_dir, "outcome.md")
|
|
332
|
+
return nil unless File.exist?(outcome)
|
|
333
|
+
text = File.read(outcome)
|
|
334
|
+
return nil if text.include?("<!-- backfilled from the record by end-intent on ")
|
|
335
|
+
|
|
336
|
+
# The gate's evidence bar (A7/B7): it judges only records that CLAIM a
|
|
337
|
+
# labeled convention - at least one actions/*.md heading carrying an
|
|
338
|
+
# S-label or a node-id segment, or at least one nodes/*.md heading carrying
|
|
339
|
+
# a node id ("n1", "v2", "d3", "r4"). A terse legacy close with no labeled
|
|
340
|
+
# matrix stays report-and-proceed (intent 334, G1, D10r).
|
|
341
|
+
#
|
|
342
|
+
# Every matching token per heading is kept, not just the first (review A3):
|
|
343
|
+
# a legacy actions/ heading can carry BOTH an S-label and prose that reads
|
|
344
|
+
# like a node id ("### The v2 rewrite (S3)"). Restricting the word-scan
|
|
345
|
+
# node-id grammar to nodes/*.md headings (post-execution review, blocking
|
|
346
|
+
# 2) means "v2" in that heading is never even tested against it, so the
|
|
347
|
+
# heading's real "S3" label is never at risk.
|
|
348
|
+
#
|
|
349
|
+
# 339 S9 (D18): a graph-era action file headed by node id alone ("### n1 -
|
|
350
|
+
# Title", no S-label) used to yield an empty label set here and the gate
|
|
351
|
+
# returned before judging anything - every such record closed unjudged by
|
|
352
|
+
# silent exemption. `heading_node_id_segments` widens the actions/*.md side
|
|
353
|
+
# to see that shape without reopening the word-scan hole above: it matches
|
|
354
|
+
# a node id only when the heading's own " - "-delimited segments carry one,
|
|
355
|
+
# never a free word in prose. The node_paths call stays exactly as it was;
|
|
356
|
+
# nodes/*.md headings already resolve node ids correctly (test_node_id_
|
|
357
|
+
# labels_are_recognized) and their heading grammar is controlled, so a
|
|
358
|
+
# word-scan there carries none of the prose risk.
|
|
359
|
+
action_paths = Dir.glob(File.join(intent_dir, "actions", "*.md")).sort
|
|
360
|
+
node_paths = Dir.glob(File.join(intent_dir, "nodes", "*.md")).sort
|
|
361
|
+
heading_labels = (heading_id_labels(action_paths, ACTION_ID_LABEL_RE) +
|
|
362
|
+
heading_node_id_segments(action_paths, NODE_ID_LABEL_RE) +
|
|
363
|
+
heading_id_labels(node_paths, NODE_ID_LABEL_RE)).uniq
|
|
364
|
+
return nil if heading_labels.empty?
|
|
365
|
+
|
|
366
|
+
rows = ReportScreen.delivered_rows(intent_dir)
|
|
367
|
+
return "outcome.md ## Delivered renders no rows although a labeled action matrix exists" if rows.empty?
|
|
368
|
+
|
|
369
|
+
needs = ReportScreen.section_of(text, "## Needs you").gsub(/<!--.*?-->/m, "").strip
|
|
370
|
+
if !needs.empty? && needs != "None" && ReportScreen.needs_you_rows(intent_dir).empty?
|
|
371
|
+
return "## Needs you has content that would render as None"
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
labels = rows.map { |r| r[:label] }
|
|
375
|
+
if (labels & heading_labels).empty?
|
|
376
|
+
return "delivered row labels (#{labels.first(3).join(', ')}...) match no action-file heading (#{heading_labels.first(3).join(', ')}...)"
|
|
377
|
+
end
|
|
378
|
+
if rows.all? { |r| ReportScreen.proven_by(intent_dir, r[:label]) == ReportScreen::NOT_RECORDED }
|
|
379
|
+
return "every Proven-by cell renders not recorded although labeled action headings exist"
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
nil
|
|
383
|
+
rescue StandardError => e
|
|
384
|
+
warn "end-intent: hollow-report gate crashed (#{e.message}); proceeding without it"
|
|
385
|
+
nil
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# 317a S7 (D5, A8): stamp the live lock's run_mode into outcome.md frontmatter
|
|
389
|
+
# INSIDE the close step - after the gate, before the INDEX move and the store
|
|
390
|
+
# commit - so the stamped file rides the close commit and mode survives disarm.
|
|
391
|
+
def stamp_outcome_mode(intent_dir)
|
|
392
|
+
data = Lock.read(intent_dir)
|
|
393
|
+
run_mode = data && data["run_mode"].to_s
|
|
394
|
+
return if run_mode.nil? || run_mode.empty?
|
|
395
|
+
|
|
396
|
+
path = File.join(intent_dir, "outcome.md")
|
|
397
|
+
return unless File.exist?(path)
|
|
398
|
+
text = File.read(path)
|
|
399
|
+
return unless text.start_with?("---")
|
|
400
|
+
head, body = text[3..].split("---", 2)
|
|
401
|
+
return if body.nil? || head.match?(/^mode:/)
|
|
402
|
+
|
|
403
|
+
File.write(path, "---#{head}mode: #{run_mode}\n---#{body}")
|
|
404
|
+
rescue StandardError => e
|
|
405
|
+
warn "end-intent: mode stamp failed (#{e.message}); proceeding without it"
|
|
406
|
+
end
|
|
407
|
+
|
|
218
408
|
def stamp_outcome_summary(content, summary)
|
|
409
|
+
|
|
219
410
|
return content if summary.nil? || summary.to_s.strip.empty?
|
|
220
411
|
|
|
221
412
|
lines = content.lines
|
|
@@ -244,10 +435,10 @@ def section_stop(lines, heading_idx)
|
|
|
244
435
|
stop
|
|
245
436
|
end
|
|
246
437
|
|
|
247
|
-
# Shared matcher (
|
|
438
|
+
# Shared matcher (IndexEntry.match, intent 188 D12): accepts a real em
|
|
248
439
|
# dash OR a plain hyphen as the id/title separator on READ.
|
|
249
440
|
def active_line_id(line)
|
|
250
|
-
m =
|
|
441
|
+
m = IndexEntry.match(line)
|
|
251
442
|
m && m[1]
|
|
252
443
|
end
|
|
253
444
|
|
|
@@ -326,7 +517,7 @@ def move_index_to_terminal(content, id, disposition, today:, index_note: nil)
|
|
|
326
517
|
raise UnresolvedIndexEntry, "INDEX.md has no #{target_heading} heading; cannot complete the move"
|
|
327
518
|
end
|
|
328
519
|
|
|
329
|
-
m =
|
|
520
|
+
m = IndexEntry.match(lines[entry_idx])
|
|
330
521
|
title, link = m[2], m[3]
|
|
331
522
|
note_suffix = (index_note && !index_note.to_s.strip.empty?) ? " #{index_note.to_s.strip}" : ""
|
|
332
523
|
new_entry = "- [#{id} #{EM_DASH} #{title}](#{link}) #{EM_DASH} #{today}#{note_suffix}\n"
|
|
@@ -415,13 +606,13 @@ end
|
|
|
415
606
|
# exclusion with a raw backtrace. nil is the only safe "could not resolve"
|
|
416
607
|
# signal; main refuses explicitly on it rather than ever reaching takeover.
|
|
417
608
|
def resolve_end_session(explicit, intent_dir)
|
|
418
|
-
return explicit.to_s.strip unless
|
|
609
|
+
return explicit.to_s.strip unless blank?(explicit)
|
|
419
610
|
env = ENV["CLAUDE_CODE_SESSION_ID"]
|
|
420
|
-
return env.to_s.strip unless
|
|
611
|
+
return env.to_s.strip unless blank?(env)
|
|
421
612
|
lock = Lock.read(intent_dir)
|
|
422
613
|
return nil unless lock
|
|
423
614
|
owner = lock["owner_session"].to_s
|
|
424
|
-
|
|
615
|
+
blank?(owner) ? nil : owner
|
|
425
616
|
end
|
|
426
617
|
|
|
427
618
|
# Read-only pre-flight verdict (D4). Returns [:proceed, lock_or_nil],
|
|
@@ -469,7 +660,7 @@ end
|
|
|
469
660
|
def run_disarm(intent_dir, id, session, discard_worktree_changes:,
|
|
470
661
|
worktree_reader: ->(dir) { Arm.worktree_block(intent_dir: dir) },
|
|
471
662
|
disarm: ->(sess, dir) { Arm.disarm(intent_dir: dir, session: sess) })
|
|
472
|
-
return :ok if
|
|
663
|
+
return :ok if blank?(session)
|
|
473
664
|
|
|
474
665
|
block = worktree_reader.call(intent_dir)
|
|
475
666
|
worktree_code = block.is_a?(Hash) ? block["code"] : nil
|
|
@@ -594,7 +785,7 @@ def main(argv)
|
|
|
594
785
|
# Lock.takeover (blank session reaches Lock.acquire's blank? guard AFTER the
|
|
595
786
|
# existing lock file was already deleted) or silently mis-arbitrate. Refuse
|
|
596
787
|
# rather than guess; nothing has been written yet.
|
|
597
|
-
if
|
|
788
|
+
if blank?(session) && File.exist?(Lock.path(intent_dir))
|
|
598
789
|
warn "end-intent: a delivery lock exists at #{Lock.path(intent_dir)} but no session " \
|
|
599
790
|
"identity could be resolved (--session, CLAUDE_CODE_SESSION_ID, and the lock's " \
|
|
600
791
|
"own recorded owner are all blank); refusing rather than guessing. Pass " \
|
|
@@ -645,6 +836,11 @@ def main(argv)
|
|
|
645
836
|
end
|
|
646
837
|
end
|
|
647
838
|
|
|
839
|
+
# 0a. Generate outcome.md from graph.md and the ledger, when there is one
|
|
840
|
+
# (intent 339, G6, spec D10) - before the backfill, so a real graph never
|
|
841
|
+
# falls through to the terse backfill it exists to replace.
|
|
842
|
+
run_generate_outcome(intent_dir, disposition: disposition)
|
|
843
|
+
|
|
648
844
|
# 1. Backfill from the record (intent 308), then the structure self-check and the
|
|
649
845
|
# outcome guard, both report-only since 308.
|
|
650
846
|
run_backfill(intent_dir, store: store, id: id, disposition: disposition,
|
|
@@ -654,6 +850,26 @@ def main(argv)
|
|
|
654
850
|
reason = OutcomeGuard.reason(intent_dir, disposition)
|
|
655
851
|
warn "end-intent: outcome.md: #{reason} (proceeding; doctor --intent #{id} reports it)" if reason
|
|
656
852
|
|
|
853
|
+
# 1a2. The self-render gate (317a S9, D7): refuse a hollow delivered close
|
|
854
|
+
# BEFORE any terminal mutation - INDEX, savepoint, and store stay untouched
|
|
855
|
+
# on exit 7 (A9).
|
|
856
|
+
hollow = hollow_report_reason(intent_dir, disposition)
|
|
857
|
+
if hollow
|
|
858
|
+
if opts[:allow_hollow_report]
|
|
859
|
+
warn "end-intent: hollow delivered report overridden (--allow-hollow-report): #{hollow}"
|
|
860
|
+
else
|
|
861
|
+
warn "end-intent: refusing a hollow delivered close: #{hollow}"
|
|
862
|
+
warn "end-intent: write outcome.md's ## Delivered as the labeled | Row | What | table " \
|
|
863
|
+
"matching the action-file headings (templates/outcome.md shows the shape), " \
|
|
864
|
+
"or pass --allow-hollow-report to close anyway"
|
|
865
|
+
exit 7
|
|
866
|
+
end
|
|
867
|
+
end
|
|
868
|
+
|
|
869
|
+
# 1a3. Mode stamp (317a S7, D5): run_mode into outcome frontmatter while the
|
|
870
|
+
# lock still exists, so the stamped file rides the close commit.
|
|
871
|
+
stamp_outcome_mode(intent_dir)
|
|
872
|
+
|
|
657
873
|
# 1b. Intent-file `## Outcome` summary stamp (no-op unless given).
|
|
658
874
|
if opts[:outcome_summary]
|
|
659
875
|
intent_file = Savepoint.intent_file(intent_dir)
|
package/scripts/exec-worktree
CHANGED
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
# (--session, else CLAUDE_CODE_SESSION_ID, else the existing delivery.lock's own
|
|
22
22
|
# recorded owner_session when non-blank, else nil; the lock-owner fallback IS
|
|
23
23
|
# used here, matching end-intent's teardown resolution order).
|
|
24
|
-
# Derive the worktree block from projects.yml and the intent id (Arm.worktree_block
|
|
25
|
-
#
|
|
24
|
+
# Derive the worktree block from projects.yml and the intent id (Arm.worktree_block).
|
|
25
|
+
# No code worktree on
|
|
26
26
|
# disk means nothing was provisioned (exit 0).
|
|
27
27
|
# 1. The dirty-worktree guard (delivered only): git status --porcelain on the code
|
|
28
28
|
# worktree. Not clean, or the check itself fails, fails CLOSED (exit 3), mirroring
|
|
29
29
|
# end-intent's dirty-worktree handling. abandoned skips this guard entirely: the
|
|
30
30
|
# branch survives and can be reclaimed, so a dirty worktree is not a reason to
|
|
31
31
|
# refuse.
|
|
32
|
-
# 2. Finish: Worktree.finish(
|
|
32
|
+
# 2. Finish: Worktree.finish(delivery, home:, runner:, merge: disposition ==
|
|
33
33
|
# "delivered"). Worktree.finish is fail-open and never raises, so failure is
|
|
34
34
|
# detected by checking, for a delivered disposition only, whether the worktree
|
|
35
35
|
# directory still exists on disk afterward (release removes it on success).
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
# one concern per script (spec D8).
|
|
39
39
|
#
|
|
40
40
|
# Exit codes:
|
|
41
|
-
# 0 finished (merged or removed per disposition), or nothing to finish (no
|
|
42
|
-
# resolved, or no code worktree recorded on
|
|
41
|
+
# 0 finished (merged or removed per disposition), or nothing to finish (no delivery
|
|
42
|
+
# resolved, or no code worktree recorded on it)
|
|
43
43
|
# 1 usage or path-resolution failure
|
|
44
44
|
# 2 retired in 2.0 (intent 302): was the order precondition; never emitted, never reused
|
|
45
45
|
# 3 the worktree is not clean, or the merge/removal did not complete (delivered only)
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
# and on any filing error (reported on stderr; the next boot tries again), 2 on
|
|
15
15
|
# a usage error.
|
|
16
16
|
|
|
17
|
+
require_relative "lib/store_layout"
|
|
17
18
|
require_relative "lib/session_ledger"
|
|
18
19
|
require_relative "lib/session_backfill"
|
|
19
20
|
|
|
@@ -48,7 +49,7 @@ if opts[:carry_to] && !SessionLedger.valid_day_id?(opts[:carry_to])
|
|
|
48
49
|
usage_abort("--carry-to must be a real calendar date, YYYYMMDD")
|
|
49
50
|
end
|
|
50
51
|
|
|
51
|
-
store = opts[:store] ||
|
|
52
|
+
store = opts[:store] || Plastic::StoreLayout.global_store(File.expand_path(ENV.fetch("PLASTIC_HOME", "~/.plastic")))
|
|
52
53
|
templates = opts[:templates] || File.expand_path("../templates", __dir__)
|
|
53
54
|
|
|
54
55
|
begin
|