@zalom/plastic 2.0.0-alpha.26 → 2.0.0-alpha.28
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 -139
- package/README.md +337 -133
- package/agents/plastic-enforcer.md +12 -12
- package/agents/plastic-executor.md +2 -1
- package/agents/plastic-node-research.md +4 -2
- package/agents/plastic-node-verify.md +1 -0
- package/agents/plastic-node-work.md +1 -0
- package/agents/{plastic-advisor.md → plastic-primary-advisor.md} +6 -8
- package/agents/{plastic-faux-advisor.md → plastic-secondary-advisor.md} +9 -12
- package/bin/crap +4 -0
- package/bin/lib/context_budget.rb +35 -1
- package/bin/lib/skill_census.rb +839 -0
- package/bin/plastic +6 -0
- package/bin/plastic-skill-census +114 -0
- 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 +7 -7
- package/{skills/conventions/references → docs/help}/completion-and-done.md +1 -1
- package/{skills/auto/references → docs/help}/human-report-contract.md +17 -19
- package/{skills/conventions/references → docs/help}/roadmaps.md +2 -2
- package/{skills/tutorial/references → docs/help}/track-1-guided.md +8 -8
- package/{skills/tutorial/references → docs/help}/track-2-auto.md +5 -5
- package/{skills/tutorial/references → docs/help}/track-3-projects-and-roadmaps.md +24 -17
- package/package.json +3 -2
- package/scripts/append-ledger +2 -1
- package/scripts/dashboard.rb +10 -9
- package/scripts/day-summary +2 -1
- package/scripts/doctor.rb +57 -49
- package/scripts/end-intent +2 -8
- package/scripts/file-session-intent +2 -1
- package/scripts/hook-capture +4 -3
- package/scripts/hook-close +2 -1
- package/scripts/hook-record +3 -2
- package/scripts/hook-savepoint +3 -2
- package/scripts/hook-session-start +5 -4
- package/scripts/hook-stop +2 -1
- package/scripts/insight-append +1 -2
- package/scripts/install.rb +7 -5
- package/scripts/lib/action_graph_shim.rb +3 -1
- package/scripts/lib/active_delivery.rb +1 -1
- package/scripts/lib/agent_models.rb +44 -26
- package/scripts/lib/arm.rb +2 -1
- package/scripts/lib/backup.rb +63 -0
- package/scripts/lib/cli/command.rb +85 -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 +60 -0
- package/scripts/lib/cli/commands/auto_report.rb +50 -0
- package/scripts/lib/cli/commands/auto_take.rb +25 -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 +20 -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 +61 -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 +25 -0
- package/scripts/lib/cli/commands/intent_spec.rb +44 -0
- package/scripts/lib/cli/commands/intent_step.rb +43 -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 +49 -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 +25 -0
- package/scripts/lib/cli/commands/roadmap.rb +19 -0
- package/scripts/lib/cli/commands/roadmap_check.rb +44 -0
- package/scripts/lib/cli/commands/roadmap_log.rb +54 -0
- package/scripts/lib/cli/commands/roadmap_next.rb +34 -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 +84 -0
- package/scripts/lib/cli/legacy.rb +50 -0
- package/scripts/lib/cli/output.rb +102 -0
- package/scripts/lib/cli/scope.rb +127 -0
- package/scripts/lib/cli/table.rb +64 -0
- package/scripts/lib/cli.rb +94 -0
- package/scripts/lib/codex_adapter.rb +5 -4
- package/scripts/lib/compact_instructions.rb +8 -0
- package/scripts/lib/day_summary.rb +4 -3
- package/scripts/lib/doctor_core.rb +7 -32
- package/scripts/lib/doctor_session_ledger.rb +2 -1
- package/scripts/lib/feedback_report.rb +1 -1
- package/scripts/lib/graph_measure_models.rb +4 -2
- package/scripts/lib/index_entry.rb +9 -0
- package/scripts/lib/installer_core.rb +175 -56
- package/scripts/lib/intent_screen.rb +3 -3
- package/scripts/lib/lock.rb +2 -2
- package/scripts/lib/node_file.rb +22 -2
- package/scripts/lib/node_input.rb +11 -6
- package/scripts/lib/node_return.rb +8 -3
- package/scripts/lib/preflight.rb +2 -4
- package/scripts/lib/project_config.rb +2 -1
- package/scripts/lib/project_validator.rb +3 -2
- package/scripts/lib/qmd_sync.rb +8 -7
- package/scripts/lib/reference_archive.rb +45 -0
- package/scripts/lib/release_guard.rb +2 -0
- package/scripts/lib/report_screen.rb +4 -3
- 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_queue.rb +2 -2
- package/scripts/lib/roadmap_savepoint.rb +1 -1
- package/scripts/lib/runner_absorb.rb +59 -2
- package/scripts/lib/runner_dispatch.rb +12 -8
- package/scripts/lib/runner_policy.rb +28 -10
- package/scripts/lib/search_index.rb +55 -0
- package/scripts/lib/session_git.rb +4 -3
- package/scripts/lib/sqlite.rb +22 -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 +2 -7
- package/scripts/lib/version_number.rb +48 -0
- package/scripts/lib/work_graph.rb +59 -0
- package/scripts/lib/worktree.rb +3 -8
- package/scripts/lib/worktree_sweep.rb +3 -2
- package/scripts/link-suggest +2 -1
- package/scripts/migrate-to-global +1 -1
- package/scripts/new-intent +3 -12
- package/scripts/node-run +2 -1
- package/scripts/plastic-lock +3 -2
- package/scripts/promote-session-item +3 -2
- package/scripts/read-config +44 -6
- package/scripts/release-check +10 -5
- package/scripts/report-screen +1 -1
- package/scripts/session-commit +2 -1
- package/scripts/spawn-preamble +2 -2
- package/scripts/update.rb +25 -4
- package/scripts/write-handoff +2 -1
- package/templates/agents.md +6 -6
- package/templates/config.yml +13 -6
- package/templates/node-research.md +4 -2
- package/templates/render.css +10 -0
- package/bin/plastic.js +0 -70
- package/skills/agent-advisor/SKILL.md +0 -92
- package/skills/auto/SKILL.md +0 -297
- package/skills/auto/evals/evals.json +0 -255
- package/skills/auto/references/end-tail.md +0 -64
- package/skills/conventions/SKILL.md +0 -29
- package/skills/dashboard/SKILL.md +0 -180
- 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 -305
- 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 -156
- package/skills/intent-continuing/references/board-fill.md +0 -52
- package/skills/intent-continuing/references/boarding-matrix.md +0 -35
- 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 -89
- 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 -182
- package/skills/intent-ending/evals/evals.json +0 -74
- package/skills/intent-executing/SKILL.md +0 -87
- package/skills/intent-executing/evals/evals.json +0 -66
- package/skills/intent-executing/implementer-prompt.md +0 -47
- package/skills/intent-executing/spec-reviewer-prompt.md +0 -27
- package/skills/intent-speccing/SKILL.md +0 -136
- 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 -376
- package/skills/releasing/references/deprecations.md +0 -60
- package/skills/releasing/references/promotion-and-tagging.md +0 -70
- package/skills/releasing/references/release-lines.md +0 -105
- package/skills/roadmap/SKILL.md +0 -90
- package/skills/roadmap/references/file-format.md +0 -134
- package/skills/roadmap/references/operations.md +0 -112
- package/skills/rollback/SKILL.md +0 -91
- package/skills/tutorial/SKILL.md +0 -66
- 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}/knowledge-graph.md +0 -0
- /package/{skills/conventions/references → docs/help}/lifecycle-and-savepoints.md +0 -0
- /package/{skills/conventions/references → docs/help}/locks-and-worktrees.md +0 -0
- /package/{skills/conventions/references → docs/help}/maintenance-and-revisions.md +0 -0
- /package/{skills/intent-executing → docs/help}/plan-reviewer-prompt.md +0 -0
package/scripts/doctor.rb
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
# Exit codes: 0 (all pass), 1 (warnings only), 2 (failures present)
|
|
10
10
|
# Read-only — never modifies files.
|
|
11
11
|
|
|
12
|
+
require_relative "lib/store_layout"
|
|
12
13
|
require "date"
|
|
13
14
|
require "open3"
|
|
14
15
|
require "timeout"
|
|
@@ -40,6 +41,7 @@ require_relative "lib/preflight"
|
|
|
40
41
|
require_relative "lib/ruby_probe"
|
|
41
42
|
require_relative "lib/doctor_session_ledger"
|
|
42
43
|
require_relative "lib/intent_screen"
|
|
44
|
+
require_relative "lib/store_sync"
|
|
43
45
|
|
|
44
46
|
# Diagnostic engine, instantiable with an injected store/agent map so tests can
|
|
45
47
|
# run it hermetically (no eval, no global-constant rewriting).
|
|
@@ -216,7 +218,7 @@ class Doctor
|
|
|
216
218
|
def check_global_store
|
|
217
219
|
checks = []
|
|
218
220
|
|
|
219
|
-
index_path = File.join(plastic_home, "INDEX.md")
|
|
221
|
+
index_path = File.join(Plastic::StoreLayout.global_root(plastic_home), "INDEX.md")
|
|
220
222
|
|
|
221
223
|
# index_exists
|
|
222
224
|
if File.exist?(index_path)
|
|
@@ -252,7 +254,7 @@ class Doctor
|
|
|
252
254
|
end
|
|
253
255
|
|
|
254
256
|
# orphaned_intents — directories in store/ not referenced in INDEX.md
|
|
255
|
-
store_dir =
|
|
257
|
+
store_dir = Plastic::StoreLayout.global_store(plastic_home)
|
|
256
258
|
if File.directory?(store_dir)
|
|
257
259
|
intent_dirs = store_intent_dirs(store_dir)
|
|
258
260
|
orphans = intent_dirs.reject { |d| content.include?("store/#{d}") }
|
|
@@ -278,7 +280,7 @@ class Doctor
|
|
|
278
280
|
store_paths = content.scan(%r{store/\S+}).map { |ref| ref.gsub(/[)\]>].*/, "").chomp("/") }.uniq
|
|
279
281
|
|
|
280
282
|
ghosts = store_paths.select do |ref|
|
|
281
|
-
full_path = File.join(plastic_home, ref)
|
|
283
|
+
full_path = File.join(Plastic::StoreLayout.global_root(plastic_home), ref)
|
|
282
284
|
!File.exist?(full_path) && !File.directory?(full_path)
|
|
283
285
|
end
|
|
284
286
|
|
|
@@ -330,7 +332,7 @@ class Doctor
|
|
|
330
332
|
category: "core_files", name: name, status: "warn",
|
|
331
333
|
message: "#{config[:name]}'s agent-side VERSION file not found at #{tilde(agent_version_path)}",
|
|
332
334
|
fixable: true,
|
|
333
|
-
fix_hint: "Re-sync the stale harness: npx @zalom/plastic@latest install --reinstall <flag>, or plastic
|
|
335
|
+
fix_hint: "Re-sync the stale harness: npx @zalom/plastic@latest install --reinstall <flag>, or `plastic rollback` to a prior version"
|
|
334
336
|
)
|
|
335
337
|
else
|
|
336
338
|
agent_version = File.read(agent_version_path).strip
|
|
@@ -348,7 +350,7 @@ class Doctor
|
|
|
348
350
|
"#{tilde(agent_version_path)}: #{agent_version}",
|
|
349
351
|
],
|
|
350
352
|
fixable: true,
|
|
351
|
-
fix_hint: "Re-sync the stale harness: npx @zalom/plastic@latest install --reinstall <flag>, or plastic
|
|
353
|
+
fix_hint: "Re-sync the stale harness: npx @zalom/plastic@latest install --reinstall <flag>, or `plastic rollback` to a prior version"
|
|
352
354
|
)
|
|
353
355
|
end
|
|
354
356
|
end
|
|
@@ -613,7 +615,7 @@ class Doctor
|
|
|
613
615
|
fixable: true,
|
|
614
616
|
fix_hint: "Relocate each unsanctioned section into the " \
|
|
615
617
|
"intent's revisions.md via move-and-record (a missing required section is restored " \
|
|
616
|
-
"or reprojected instead); see plastic
|
|
618
|
+
"or reprojected instead); see `plastic help maintenance-and-revisions`"
|
|
617
619
|
)
|
|
618
620
|
end
|
|
619
621
|
|
|
@@ -644,7 +646,7 @@ class Doctor
|
|
|
644
646
|
# unpromoted_rules (intent 341, G8, C37): an Insights entry tagged
|
|
645
647
|
# `rule:` (via `insight-append --rule`) is a promise the rule will make
|
|
646
648
|
# it into project doctrine. Advisory only.
|
|
647
|
-
checks.concat(unpromoted_rules_checks(intent_dirs
|
|
649
|
+
checks.concat(unpromoted_rules_checks(intent_dirs))
|
|
648
650
|
|
|
649
651
|
# cross_store_resolution — RESOLVES (not just shape-checks) every cross-store
|
|
650
652
|
# `store:id` ref against the FULL store family via the relocation map
|
|
@@ -705,14 +707,14 @@ class Doctor
|
|
|
705
707
|
if scopes.nil? || scopes.include?("global")
|
|
706
708
|
stores << {
|
|
707
709
|
scope: "global",
|
|
708
|
-
index: File.join(plastic_home, "INDEX.md"),
|
|
709
|
-
store_dir:
|
|
710
|
+
index: File.join(Plastic::StoreLayout.global_root(plastic_home), "INDEX.md"),
|
|
711
|
+
store_dir: Plastic::StoreLayout.global_store(plastic_home),
|
|
710
712
|
}
|
|
711
713
|
end
|
|
712
714
|
|
|
713
|
-
projects_root =
|
|
715
|
+
projects_root = Plastic::StoreLayout.projects_root(plastic_home)
|
|
714
716
|
if File.directory?(projects_root)
|
|
715
|
-
|
|
717
|
+
Plastic::StoreLayout.project_slugs(plastic_home).each do |project|
|
|
716
718
|
scope = "project:#{project}"
|
|
717
719
|
next unless scopes.nil? || scopes.include?(scope)
|
|
718
720
|
|
|
@@ -1228,7 +1230,7 @@ end
|
|
|
1228
1230
|
"project-links --intent <id> --apply` over running project-links directly: it detects " \
|
|
1229
1231
|
"(never acquires) the target's delivery lock, requires a clean store working tree, and " \
|
|
1230
1232
|
"commits the scoped change plus its revisions.md receipt as one merged operation " \
|
|
1231
|
-
"(plastic
|
|
1233
|
+
"(`plastic help maintenance-and-revisions`)."
|
|
1232
1234
|
)
|
|
1233
1235
|
end
|
|
1234
1236
|
|
|
@@ -1692,7 +1694,7 @@ end
|
|
|
1692
1694
|
"Every sources/chain id resolves to a real intent (I4)",
|
|
1693
1695
|
"Record the dangling sources/chain edge as a " \
|
|
1694
1696
|
"broken-source/broken-chain move-and-record entry in the intent's revisions.md (see " \
|
|
1695
|
-
"plastic
|
|
1697
|
+
"`plastic help maintenance-and-revisions`), or restore the missing intent"
|
|
1696
1698
|
)
|
|
1697
1699
|
checks
|
|
1698
1700
|
end
|
|
@@ -1865,31 +1867,25 @@ end
|
|
|
1865
1867
|
#
|
|
1866
1868
|
# `insight-append --rule` tags an entry "... - rule: <text>". A tag is a
|
|
1867
1869
|
# promise the rule will make it into project doctrine; until the exact
|
|
1868
|
-
# rule text shows up in some
|
|
1869
|
-
#
|
|
1870
|
-
#
|
|
1871
|
-
#
|
|
1872
|
-
#
|
|
1873
|
-
#
|
|
1870
|
+
# rule text shows up in some docs/help/*.md chapter, it is only visible to
|
|
1871
|
+
# a session that happens to read this one intent file, and the next
|
|
1872
|
+
# session repeats the mistake the rule names. Advisory only (warn, never
|
|
1873
|
+
# fail): a freshly tagged rule is not yet promoted by design, and nothing
|
|
1874
|
+
# here can auto-promote it (that is an editorial call, not a mechanical
|
|
1875
|
+
# one).
|
|
1874
1876
|
RULE_ENTRY_RE = /—\s*rule:\s*(.+?)\s*\z/.freeze
|
|
1875
1877
|
|
|
1876
|
-
def unpromoted_rules_checks(intent_dirs, package_root: PACKAGE_ROOT
|
|
1877
|
-
#
|
|
1878
|
-
# (~/.plastic
|
|
1879
|
-
#
|
|
1880
|
-
#
|
|
1881
|
-
#
|
|
1882
|
-
|
|
1883
|
-
if home
|
|
1884
|
-
chapter_dirs << File.join(home, ".claude", "skills", "plastic-conventions", "references")
|
|
1885
|
-
chapter_dirs << File.join(home, ".agents", "skills", "plastic-conventions", "references")
|
|
1886
|
-
end
|
|
1887
|
-
chapter_dirs << File.join(package_root, "skills", "conventions", "references")
|
|
1878
|
+
def unpromoted_rules_checks(intent_dirs, package_root: PACKAGE_ROOT)
|
|
1879
|
+
# docs/help installs straight under PACKAGE_ROOT for every harness alike
|
|
1880
|
+
# (~/.plastic/docs/help once installed, repo docs/help in a checkout), so
|
|
1881
|
+
# a single path covers both the repo and the installed layout; unlike
|
|
1882
|
+
# the old skills-based chapters, help topics are no longer agent-home
|
|
1883
|
+
# specific, so this needs no `home:` split.
|
|
1884
|
+
chapter_dir = File.join(package_root, "docs", "help")
|
|
1888
1885
|
|
|
1889
|
-
chapters_text =
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
.join("\n\n")
|
|
1886
|
+
chapters_text = Dir.glob(File.join(chapter_dir, "*.md"))
|
|
1887
|
+
.map { |f| File.read(f) }
|
|
1888
|
+
.join("\n\n")
|
|
1893
1889
|
|
|
1894
1890
|
unpromoted = []
|
|
1895
1891
|
intent_dirs.each do |d|
|
|
@@ -1917,7 +1913,7 @@ end
|
|
|
1917
1913
|
category: "conventions", name: "unpromoted_rules", status: "warn",
|
|
1918
1914
|
message: "#{unpromoted.size} tagged rule(s) not yet carried by any conventions chapter",
|
|
1919
1915
|
details: unpromoted, fixable: false,
|
|
1920
|
-
fix_hint: "Promote the rule into the right
|
|
1916
|
+
fix_hint: "Promote the rule into the right docs/help/*.md chapter, " \
|
|
1921
1917
|
"or drop the tag if the finding does not belong in doctrine"
|
|
1922
1918
|
)]
|
|
1923
1919
|
end
|
|
@@ -1934,8 +1930,7 @@ end
|
|
|
1934
1930
|
# skill directory under agent_dir/skills that has NO corresponding entry in the
|
|
1935
1931
|
# current install manifest is a stray (e.g. a leftover old-name copy the
|
|
1936
1932
|
# install/update prune should have removed, or one it never saw because the
|
|
1937
|
-
# manifest predates it).
|
|
1938
|
-
# least one skill exists). Defers to the manifest check when the manifest itself
|
|
1933
|
+
# manifest predates it). Defers to the manifest check when the manifest itself
|
|
1939
1934
|
# is missing or malformed, so the two checks never double-report the same gap.
|
|
1940
1935
|
|
|
1941
1936
|
# Auto-mode role agents install as <dir>/agents/plastic-*.md. Pass if at least
|
|
@@ -2159,6 +2154,8 @@ end
|
|
|
2159
2154
|
|
|
2160
2155
|
global_config = load_yaml_safe(File.join(plastic_home, "config.yml")) || {}
|
|
2161
2156
|
overrides = AgentModels.override_map(project_config: {}, global_config: global_config, harness: "codex")
|
|
2157
|
+
effort_overrides = AgentModels.effort_override_map(project_config: {}, global_config: global_config,
|
|
2158
|
+
harness: "codex")
|
|
2162
2159
|
|
|
2163
2160
|
drifted = []
|
|
2164
2161
|
sanctioned = []
|
|
@@ -2169,13 +2166,13 @@ end
|
|
|
2169
2166
|
fields = codex_agent_toml_model_fields(File.read(path))
|
|
2170
2167
|
override = overrides[basename]
|
|
2171
2168
|
|
|
2172
|
-
if override
|
|
2169
|
+
if override || effort_overrides[basename]
|
|
2173
2170
|
sanctioned << "#{basename}: toml model=#{fields[:model].inspect}, " \
|
|
2174
|
-
"toml effort=#{fields[:effort].inspect}, sanctioned override=#{override.inspect}"
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
expected_model = AgentModels.
|
|
2178
|
-
expected_effort = AgentModels.
|
|
2171
|
+
"toml effort=#{fields[:effort].inspect}, sanctioned model override=#{override.inspect}, " \
|
|
2172
|
+
"effort override=#{effort_overrides[basename].inspect}"
|
|
2173
|
+
elsif AgentModels::SHIPPED_MODEL_DEFAULTS.key?(basename)
|
|
2174
|
+
expected_model = AgentModels.shipped_model_for(basename, harness: "codex")
|
|
2175
|
+
expected_effort = AgentModels.shipped_effort_for(basename)
|
|
2179
2176
|
mismatches = []
|
|
2180
2177
|
if expected_model && fields[:model] != expected_model
|
|
2181
2178
|
mismatches << "model=#{fields[:model].inspect}, resolved default model=#{expected_model.inspect}"
|
|
@@ -2187,7 +2184,7 @@ end
|
|
|
2187
2184
|
else
|
|
2188
2185
|
unclassified << "#{basename}: toml model=#{fields[:model].inspect}, " \
|
|
2189
2186
|
"toml effort=#{fields[:effort].inspect}, no resolved default (basename is in " \
|
|
2190
|
-
"
|
|
2187
|
+
"AgentModels::SHIPPED_MODEL_DEFAULTS in " \
|
|
2191
2188
|
"scripts/lib/agent_models.rb; add it there, or set agents.models.codex.#{basename} " \
|
|
2192
2189
|
"to sanction a model explicitly)"
|
|
2193
2190
|
end
|
|
@@ -2299,7 +2296,7 @@ end
|
|
|
2299
2296
|
# project can be checked in isolation (used by `--store <slug>`).
|
|
2300
2297
|
def check_project_store(slug, project_info)
|
|
2301
2298
|
checks = []
|
|
2302
|
-
project_dir =
|
|
2299
|
+
project_dir = Plastic::StoreLayout.project_root(plastic_home, slug)
|
|
2303
2300
|
|
|
2304
2301
|
# project_dir_exists
|
|
2305
2302
|
if File.directory?(project_dir)
|
|
@@ -2349,7 +2346,7 @@ end
|
|
|
2349
2346
|
end
|
|
2350
2347
|
|
|
2351
2348
|
# project_yml_exists
|
|
2352
|
-
project_yml_path = File.join(plastic_home,
|
|
2349
|
+
project_yml_path = File.join(Plastic::StoreLayout.project_root(plastic_home, slug), "project.yml")
|
|
2353
2350
|
project_yml_data = nil
|
|
2354
2351
|
|
|
2355
2352
|
if File.exist?(project_yml_path)
|
|
@@ -2396,7 +2393,7 @@ end
|
|
|
2396
2393
|
return checks unless parent_id
|
|
2397
2394
|
|
|
2398
2395
|
# Find the intent directory for the parent ID
|
|
2399
|
-
store_dir =
|
|
2396
|
+
store_dir = Plastic::StoreLayout.global_store(plastic_home)
|
|
2400
2397
|
parent_dir = nil
|
|
2401
2398
|
if File.directory?(store_dir)
|
|
2402
2399
|
parent_dir = Dir.children(store_dir).find { |d| d.start_with?("#{parent_id}--") }
|
|
@@ -2553,7 +2550,7 @@ end
|
|
|
2553
2550
|
# A config-asks warn in that tier would flip every post-update doctor to
|
|
2554
2551
|
# "fail" on an otherwise healthy install until the question is answered,
|
|
2555
2552
|
# re-noising the post-update surface intent 126 deliberately quieted. The
|
|
2556
|
-
# recoverable-later path for a pending question is a full
|
|
2553
|
+
# recoverable-later path for a pending question is a full `plastic doctor`
|
|
2557
2554
|
# run, the declared maintenance front door; the moment-it-happens path is
|
|
2558
2555
|
# update.rb#announce_pending_config_asks, which already runs on every hop.
|
|
2559
2556
|
#
|
|
@@ -3017,6 +3014,16 @@ end
|
|
|
3017
3014
|
end
|
|
3018
3015
|
end
|
|
3019
3016
|
|
|
3017
|
+
def check_graph_links
|
|
3018
|
+
return [] unless File.exist?(Plastic::WorkGraph.path(plastic_home))
|
|
3019
|
+
|
|
3020
|
+
orphans = Plastic::StoreSync.orphans(plastic_home)
|
|
3021
|
+
return [check(category: "graph_links", name: "graph_links", status: "pass", message: "every stored file belongs to an intent in work_graph.db")] if orphans.empty?
|
|
3022
|
+
|
|
3023
|
+
[check(category: "graph_links", name: "graph_links", status: "warn", details: orphans, fixable: false,
|
|
3024
|
+
message: "#{orphans.size} intent id(s) hold files and have no row in work_graph.db")]
|
|
3025
|
+
end
|
|
3026
|
+
|
|
3020
3027
|
# --- Run all checks ---
|
|
3021
3028
|
|
|
3022
3029
|
def run_checks(agent_key)
|
|
@@ -3035,6 +3042,7 @@ end
|
|
|
3035
3042
|
all_checks += check_session_ledger(scopes: ["global"])
|
|
3036
3043
|
all_checks += check_skill_lint
|
|
3037
3044
|
all_checks += check_install_integrity
|
|
3045
|
+
all_checks += check_graph_links
|
|
3038
3046
|
all_checks += check_display_registration(agent_key)
|
|
3039
3047
|
all_checks += check_display_paints(agent_key)
|
|
3040
3048
|
all_checks += check_display_not_defeated(agent_key)
|
package/scripts/end-intent
CHANGED
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
# 7 hollow delivered report refused (317a D7) - intent 308's one deliberate
|
|
69
69
|
# exception to report-and-proceed; --allow-hollow-report overrides.
|
|
70
70
|
|
|
71
|
+
require_relative "lib/store_layout"
|
|
71
72
|
require "fileutils"
|
|
72
73
|
require "date"
|
|
73
74
|
require "open3"
|
|
@@ -170,14 +171,7 @@ end
|
|
|
170
171
|
# "projects" (the StoreDiscovery convention, scripts/lib/store_discovery.rb), never by
|
|
171
172
|
# hardcoding ".plastic" as a name.
|
|
172
173
|
def resolve_plastic_home_and_scope(store)
|
|
173
|
-
|
|
174
|
-
grandparent = File.dirname(parent)
|
|
175
|
-
if File.basename(grandparent) == "projects"
|
|
176
|
-
slug = File.basename(parent)
|
|
177
|
-
[File.dirname(grandparent), "project:#{slug}"]
|
|
178
|
-
else
|
|
179
|
-
[parent, "global"]
|
|
180
|
-
end
|
|
174
|
+
Plastic::StoreLayout.home_and_scope(store)
|
|
181
175
|
end
|
|
182
176
|
|
|
183
177
|
# --- outcome generation, before the backfill (intent 339, G6, spec D10) -----
|
|
@@ -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
|
package/scripts/hook-capture
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
# runs in its own rescue and the hook always exits 0 (spec D2). Intent 345
|
|
10
10
|
# (D7, 323) removed the per-prompt Future-intent hint step entirely.
|
|
11
11
|
|
|
12
|
+
require_relative "lib/store_layout"
|
|
12
13
|
require "json"
|
|
13
14
|
require "open3"
|
|
14
15
|
require "time"
|
|
@@ -38,7 +39,7 @@ cwd = payload["cwd"].to_s
|
|
|
38
39
|
cwd = Dir.pwd if cwd.empty?
|
|
39
40
|
|
|
40
41
|
plastic_home = File.expand_path(ENV["PLASTIC_HOME"] || "~/.plastic")
|
|
41
|
-
store =
|
|
42
|
+
store = Plastic::StoreLayout.global_store(plastic_home)
|
|
42
43
|
templates = File.expand_path("../templates", __dir__)
|
|
43
44
|
sid = SessionLedger.short_session_id(nil, session_id)
|
|
44
45
|
today = SessionLedger.day_id
|
|
@@ -108,7 +109,7 @@ begin
|
|
|
108
109
|
cockpit, _err, status = Open3.capture3({ "RUBYOPT" => nil }, "ruby", dashboard, "continue")
|
|
109
110
|
if status.success? && !cockpit.strip.empty?
|
|
110
111
|
context_parts << (cockpit.rstrip +
|
|
111
|
-
"\n\
|
|
112
|
+
"\n\nRun `plastic continue` to resume: " \
|
|
112
113
|
"if active intents exist, read their state and resume; otherwise surface the " \
|
|
113
114
|
"most-valuable next work and any stale or triage intents from the board above.")
|
|
114
115
|
|
|
@@ -135,7 +136,7 @@ begin
|
|
|
135
136
|
auto_triggers = ["take it from here", "deliver this", "/plastic-auto", "auto mode"].freeze
|
|
136
137
|
triggered = auto_triggers.any? { |p| message_lower.include?(p) } || message_lower.match?(/\bauto\b/)
|
|
137
138
|
if triggered
|
|
138
|
-
context_parts << "Auto-mode request detected.
|
|
139
|
+
context_parts << "Auto-mode request detected. Run `plastic auto take ID` to deliver the " \
|
|
139
140
|
"active intent autonomously - it takes the intent under a lock and a worktree " \
|
|
140
141
|
"and points this session at it."
|
|
141
142
|
end
|
package/scripts/hook-close
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
#
|
|
9
9
|
# Usage: hook-close <plastic_home> (stdin: the hook JSON)
|
|
10
10
|
|
|
11
|
+
require_relative "lib/store_layout"
|
|
11
12
|
require "json"
|
|
12
13
|
require_relative "lib/session_ledger"
|
|
13
14
|
require_relative "lib/session_close"
|
|
@@ -23,7 +24,7 @@ payload =
|
|
|
23
24
|
{}
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
store =
|
|
27
|
+
store = Plastic::StoreLayout.global_store(File.expand_path(plastic_home))
|
|
27
28
|
filer = File.expand_path("file-session-intent", __dir__)
|
|
28
29
|
templates = File.expand_path("../templates", __dir__)
|
|
29
30
|
begin
|
package/scripts/hook-record
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
# lands, and calls the session-commit seam when that script exists. Always
|
|
11
11
|
# exits 0 (spec D3).
|
|
12
12
|
|
|
13
|
+
require_relative "lib/store_layout"
|
|
13
14
|
require "json"
|
|
14
15
|
require "timeout"
|
|
15
16
|
require "fileutils"
|
|
@@ -77,7 +78,7 @@ skip_day_ledger = inside_plastic || !intent_dir_abs.nil?
|
|
|
77
78
|
# --- (c) promote the newest pending line, Item savepoint, seam -------------
|
|
78
79
|
unless skip_day_ledger
|
|
79
80
|
begin
|
|
80
|
-
store =
|
|
81
|
+
store = Plastic::StoreLayout.global_store(plastic_home)
|
|
81
82
|
sid = SessionLedger.short_session_id(nil, session_id)
|
|
82
83
|
today = SessionLedger.day_id
|
|
83
84
|
|
|
@@ -152,7 +153,7 @@ end
|
|
|
152
153
|
# the heartbeat is not written and no directory is created here either --
|
|
153
154
|
# the session tmp directory, created by session start, is the guard.
|
|
154
155
|
begin
|
|
155
|
-
store ||=
|
|
156
|
+
store ||= Plastic::StoreLayout.global_store(plastic_home)
|
|
156
157
|
sid ||= SessionLedger.short_session_id(nil, session_id)
|
|
157
158
|
if Dir.exist?(SessionLedger.session_tmp_dir(store, sid))
|
|
158
159
|
File.write(SessionLedger.heartbeat_path(store, sid), "#{Time.now.utc.iso8601}\n")
|
package/scripts/hook-savepoint
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
#
|
|
14
14
|
# Usage: hook-savepoint <plastic_home> (stdin: the hook JSON)
|
|
15
15
|
|
|
16
|
+
require_relative "lib/store_layout"
|
|
16
17
|
require "json"
|
|
17
18
|
require_relative "lib/session_ledger"
|
|
18
19
|
require_relative "lib/handoff"
|
|
@@ -31,7 +32,7 @@ MESSAGE = "PLASTIC SAVEPOINT - context is being compacted. The hand-off for this
|
|
|
31
32
|
def resolve_intent(plastic_home, session_id, read_config_path)
|
|
32
33
|
return nil if session_id.strip.empty?
|
|
33
34
|
|
|
34
|
-
global_store =
|
|
35
|
+
global_store = Plastic::StoreLayout.global_store(plastic_home)
|
|
35
36
|
roots_raw = IO.popen({ "PLASTIC_HOME" => plastic_home }, [read_config_path, "project_roots"],
|
|
36
37
|
err: File::NULL, &:read).to_s.strip
|
|
37
38
|
project_roots = roots_raw.empty? ? [] : Array(JSON.parse(roots_raw))
|
|
@@ -51,7 +52,7 @@ begin
|
|
|
51
52
|
|
|
52
53
|
unless plastic_home.empty? || session_id.strip.empty?
|
|
53
54
|
home = File.expand_path(plastic_home)
|
|
54
|
-
store =
|
|
55
|
+
store = Plastic::StoreLayout.global_store(home)
|
|
55
56
|
session = SessionLedger.short_session_id(session_id, nil)
|
|
56
57
|
day = Handoff.day_for(store, session, today: SessionLedger.day_id)
|
|
57
58
|
read_config_path = File.expand_path("read-config", __dir__)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# Usage: hook-session-start <index_path> <plastic_home> <mode> [plugin_root]
|
|
4
4
|
# Outputs hook JSON for the session-start hook. Exits silently if nothing to show.
|
|
5
5
|
|
|
6
|
+
require_relative "lib/store_layout"
|
|
6
7
|
require "json"
|
|
7
8
|
require "date"
|
|
8
9
|
require "yaml"
|
|
@@ -58,7 +59,7 @@ end
|
|
|
58
59
|
# home (~/.plastic) as argument 2; the store lives one level below it. Compose the
|
|
59
60
|
# store exactly once here, so no later line re-derives it and no path can gain a
|
|
60
61
|
# doubled store/ segment.
|
|
61
|
-
store_dir =
|
|
62
|
+
store_dir = Plastic::StoreLayout.global_store(plastic_home)
|
|
62
63
|
|
|
63
64
|
# --- Current version ---
|
|
64
65
|
# Read early, before any of the richer INDEX/project parsing below, so the
|
|
@@ -154,7 +155,7 @@ begin
|
|
|
154
155
|
project_path = File.expand_path(info["path"])
|
|
155
156
|
if cwd.start_with?(project_path)
|
|
156
157
|
current_project = { "slug" => slug, "parent" => info["parent"], "path" => project_path }
|
|
157
|
-
project_index =
|
|
158
|
+
project_index = File.join(Plastic::StoreLayout.project_root(plastic_home, slug), "INDEX.md")
|
|
158
159
|
if File.exist?(project_index)
|
|
159
160
|
p_section = nil
|
|
160
161
|
File.readlines(project_index).each do |pline|
|
|
@@ -220,7 +221,7 @@ begin
|
|
|
220
221
|
if File.exist?(cache_file)
|
|
221
222
|
cache = JSON.parse(File.read(cache_file)) rescue {}
|
|
222
223
|
if cache["updateAvailable"]
|
|
223
|
-
update_notice = "Plastic update available: #{cache["current"]} -> #{cache["latest"]} — run
|
|
224
|
+
update_notice = "Plastic update available: #{cache["current"]} -> #{cache["latest"]} — run `plastic update`"
|
|
224
225
|
end
|
|
225
226
|
end
|
|
226
227
|
|
|
@@ -291,7 +292,7 @@ begin
|
|
|
291
292
|
if plastic_md && !subagent_session
|
|
292
293
|
if current_project
|
|
293
294
|
slug = current_project["slug"]
|
|
294
|
-
banner = "Project: #{slug} | Store:
|
|
295
|
+
banner = "Project: #{slug} | Store: #{File.join(Plastic::StoreLayout.project_root(plastic_home, slug), "store").sub(Dir.home, "~")}/"
|
|
295
296
|
if project_active.any? && project_active.first =~ /\[([^\]]+)\].*store\/([\w-]+)\//
|
|
296
297
|
intent_name, dir_name = $1, $2
|
|
297
298
|
intent_id = dir_name.split("--").first
|
package/scripts/hook-stop
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#
|
|
15
15
|
# Usage: hook-stop <plastic_home> (stdin: the hook JSON)
|
|
16
16
|
|
|
17
|
+
require_relative "lib/store_layout"
|
|
17
18
|
require "json"
|
|
18
19
|
require_relative "lib/stop_gate"
|
|
19
20
|
|
|
@@ -32,7 +33,7 @@ begin
|
|
|
32
33
|
payload = {} unless payload.is_a?(Hash)
|
|
33
34
|
|
|
34
35
|
home = File.expand_path(plastic_home)
|
|
35
|
-
global_store =
|
|
36
|
+
global_store = Plastic::StoreLayout.global_store(home)
|
|
36
37
|
read_config = File.expand_path("read-config", __dir__)
|
|
37
38
|
|
|
38
39
|
config_stop_hook = read_config_value("runner.stop_hook", home, read_config)
|
package/scripts/insight-append
CHANGED
|
@@ -16,8 +16,7 @@
|
|
|
16
16
|
# ever reaches Insights.append_insight; the library itself stays untouched,
|
|
17
17
|
# since a tag is a text-level convention, not a new field on the ledger.
|
|
18
18
|
# Doctor's unpromoted_rules check (scripts/doctor.rb) later lists any tagged
|
|
19
|
-
# entry whose exact text no
|
|
20
|
-
# carries yet.
|
|
19
|
+
# entry whose exact text no docs/help/*.md chapter carries yet.
|
|
21
20
|
#
|
|
22
21
|
# Usage:
|
|
23
22
|
# insight-append <intent_dir> <text> --stage S --author A [--rule]
|
package/scripts/install.rb
CHANGED
|
@@ -75,12 +75,15 @@ class Install < InstallerCore
|
|
|
75
75
|
# they are reported, never re-installed, so the summary line and the Codex
|
|
76
76
|
# trust reminder only ever count agents that actually changed this run.
|
|
77
77
|
def run(selected:, force: false, reinstall: false, ledger_action: nil, argv: ARGV, input: $stdin,
|
|
78
|
-
already_registered: [])
|
|
78
|
+
already_registered: [], qmd_runner: QmdSync.default_runner, qmd_detector: QmdSync.method(:detect))
|
|
79
79
|
fresh = !installed?
|
|
80
80
|
mode = fresh ? :install : :update # :update here means "re-sync, skip bootstrap"
|
|
81
81
|
|
|
82
82
|
distribute(mode)
|
|
83
83
|
bootstrap if fresh
|
|
84
|
+
git_init_if_absent
|
|
85
|
+
register_with_qmd(runner: qmd_runner, detector: qmd_detector)
|
|
86
|
+
migrate_advisor_config_file(File.join(plastic_home, "config.yml"))
|
|
84
87
|
apply_config_flags(argv)
|
|
85
88
|
|
|
86
89
|
results = selected.map do |key|
|
|
@@ -239,10 +242,9 @@ class Install < InstallerCore
|
|
|
239
242
|
--no-advisor Skip installing both advisor agents and the agent-advisor
|
|
240
243
|
skill (advisor.enabled: false)
|
|
241
244
|
--advisor VALUE Which advisor agent is the default: an agent name, or the
|
|
242
|
-
shorthand "
|
|
243
|
-
|
|
244
|
-
unset, the agent-advisor skill falls back to
|
|
245
|
-
plastic-faux-advisor at consult time.
|
|
245
|
+
shorthand "primary" or "secondary". Legacy "real" and
|
|
246
|
+
"faux" aliases remain accepted. Writes advisor.claude.default.
|
|
247
|
+
Left unset, the agent-advisor skill falls back to Primary Advisor.
|
|
246
248
|
-h, --help Show this help
|
|
247
249
|
|
|
248
250
|
Notes:
|
|
@@ -83,7 +83,7 @@ module ActionGraphShim
|
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
# Node records for either shape (D8), always carrying NodeFile.parse's own
|
|
86
|
-
# key set (ok:, node:, kind:, files:, budget:, body:, errors:) plus
|
|
86
|
+
# key set (ok:, node:, kind:, files:, budget:, report:, body:, errors:) plus
|
|
87
87
|
# needs:, path: and proven_by:. [] on :none, never a raise on a real
|
|
88
88
|
# directory.
|
|
89
89
|
def nodes(intent_dir)
|
|
@@ -153,6 +153,7 @@ module ActionGraphShim
|
|
|
153
153
|
kind: "work",
|
|
154
154
|
files: files_section_paths(text),
|
|
155
155
|
budget: nil,
|
|
156
|
+
report: nil,
|
|
156
157
|
body: text,
|
|
157
158
|
errors: [],
|
|
158
159
|
needs: needs_targets,
|
|
@@ -166,6 +167,7 @@ module ActionGraphShim
|
|
|
166
167
|
kind: "work",
|
|
167
168
|
files: [],
|
|
168
169
|
budget: nil,
|
|
170
|
+
report: nil,
|
|
169
171
|
body: nil,
|
|
170
172
|
errors: ["could not read #{path}: #{e.message}"],
|
|
171
173
|
needs: needs_targets,
|
|
@@ -60,7 +60,7 @@ module ActiveDelivery
|
|
|
60
60
|
value.nil? || value.to_s.strip.empty?
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
DEFAULT_PROJECT_ROOTS = ["~/.plastic/projects"].freeze
|
|
63
|
+
DEFAULT_PROJECT_ROOTS = ["~/.plastic/projects", "~/.plastic/stores"].freeze
|
|
64
64
|
|
|
65
65
|
# The configured project roots (344 n2, D6): the `project_roots` list in
|
|
66
66
|
# `<plastic_home>/config.yml`, each expanded, or the default
|