@rubytech/create-maxy-code 0.1.97 → 0.1.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  3. package/payload/platform/plugins/founder-pack/.claude-plugin/plugin.json +8 -0
  4. package/payload/platform/plugins/founder-pack/skills/investor-data-room/SKILL.md +256 -0
  5. package/payload/platform/plugins/founder-pack/skills/investor-data-room/references/business-plan-template.md +61 -0
  6. package/payload/platform/plugins/founder-pack/skills/investor-data-room/references/compliance-research-checklist.md +53 -0
  7. package/payload/platform/plugins/founder-pack/skills/investor-data-room/references/data-room-structure.md +77 -0
  8. package/payload/platform/plugins/founder-pack/skills/investor-data-room/references/deck-blueprint-template.md +38 -0
  9. package/payload/platform/plugins/founder-pack/skills/investor-data-room/references/design-tokens-application.md +79 -0
  10. package/payload/platform/plugins/founder-pack/skills/investor-data-room/references/html-pdf-pipeline.md +236 -0
  11. package/payload/platform/plugins/founder-pack/skills/investor-data-room/references/internal-workings-scrub.md +33 -0
  12. package/payload/platform/plugins/founder-pack/skills/investor-data-room/references/termsheet-template.md +66 -0
  13. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  14. package/payload/platform/services/claude-session-manager/dist/http-server.js +79 -87
  15. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  16. package/payload/platform/services/claude-session-manager/scripts/stash-cleanup.sh +410 -0
  17. package/payload/premium-plugins/.claude-plugin/marketplace.json +5 -0
  18. package/payload/premium-plugins/founder-pack/.claude-plugin/plugin.json +8 -0
  19. package/payload/premium-plugins/founder-pack/skills/investor-data-room/SKILL.md +256 -0
  20. package/payload/premium-plugins/founder-pack/skills/investor-data-room/references/business-plan-template.md +61 -0
  21. package/payload/premium-plugins/founder-pack/skills/investor-data-room/references/compliance-research-checklist.md +53 -0
  22. package/payload/premium-plugins/founder-pack/skills/investor-data-room/references/data-room-structure.md +77 -0
  23. package/payload/premium-plugins/founder-pack/skills/investor-data-room/references/deck-blueprint-template.md +38 -0
  24. package/payload/premium-plugins/founder-pack/skills/investor-data-room/references/design-tokens-application.md +79 -0
  25. package/payload/premium-plugins/founder-pack/skills/investor-data-room/references/html-pdf-pipeline.md +236 -0
  26. package/payload/premium-plugins/founder-pack/skills/investor-data-room/references/internal-workings-scrub.md +33 -0
  27. package/payload/premium-plugins/founder-pack/skills/investor-data-room/references/termsheet-template.md +66 -0
  28. package/payload/server/public/assets/{ChatInput-CzQhPt90.css → ChatInput-u7hSrC3q.css} +1 -1
  29. package/payload/server/public/assets/{Checkbox-ynZtDWi3.js → Checkbox-CfiWjuMH.js} +1 -1
  30. package/payload/server/public/assets/{admin-WyIfDMQG.js → admin-D6VRETeg.js} +1 -1
  31. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-Ko_izKmY.js → architectureDiagram-Q4EWVU46-CSAyzBIK.js} +1 -1
  32. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-D58VDC0w.js → blockDiagram-DXYQGD6D-l_AoJo_I.js} +1 -1
  33. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-C-eRXyKi.js → c4Diagram-AHTNJAMY-Dsjc59iO.js} +1 -1
  34. package/payload/server/public/assets/channel-BlwH4wTf.js +1 -0
  35. package/payload/server/public/assets/{chunk-336JU56O-DZLEfLAb.js → chunk-336JU56O-BgAz-EJ4.js} +2 -2
  36. package/payload/server/public/assets/{chunk-426QAEUC-BriTU3L8.js → chunk-426QAEUC-DHF9zZlS.js} +1 -1
  37. package/payload/server/public/assets/{chunk-4TB4RGXK-BdxRL-AY.js → chunk-4TB4RGXK-Cqb1TREv.js} +1 -1
  38. package/payload/server/public/assets/{chunk-5FUZZQ4R-BgEmy7Y3.js → chunk-5FUZZQ4R-CV4WMN8u.js} +1 -1
  39. package/payload/server/public/assets/{chunk-5PVQY5BW-emnbDRLA.js → chunk-5PVQY5BW-CL0bAr0t.js} +1 -1
  40. package/payload/server/public/assets/{chunk-EDXVE4YY-BvCf2Xf2.js → chunk-EDXVE4YY-WY3Sg1Fq.js} +1 -1
  41. package/payload/server/public/assets/{chunk-ENJZ2VHE-DTpEYJRb.js → chunk-ENJZ2VHE-BIVdDrD2.js} +1 -1
  42. package/payload/server/public/assets/{chunk-ICPOFSXX-Ced2j48Y.js → chunk-ICPOFSXX-CUHDQxso.js} +1 -1
  43. package/payload/server/public/assets/{chunk-OYMX7WX6-B21_a3Dv.js → chunk-OYMX7WX6-D1sj6R4f.js} +1 -1
  44. package/payload/server/public/assets/{chunk-U2HBQHQK-CPS3yyXK.js → chunk-U2HBQHQK-BGso49fW.js} +1 -1
  45. package/payload/server/public/assets/{chunk-X2U36JSP-BJ_B5NW7.js → chunk-X2U36JSP-C2tUGTcL.js} +1 -1
  46. package/payload/server/public/assets/{chunk-YZCP3GAM-CMLy-WXm.js → chunk-YZCP3GAM-CLvYLDvo.js} +1 -1
  47. package/payload/server/public/assets/{chunk-ZZ45TVLE-BkLnzCHe.js → chunk-ZZ45TVLE-yHQe8STh.js} +1 -1
  48. package/payload/server/public/assets/classDiagram-6PBFFD2Q-DuEhfP67.js +1 -0
  49. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-zSqemF--.js +1 -0
  50. package/payload/server/public/assets/clone-DUSqJbTD.js +1 -0
  51. package/payload/server/public/assets/{dagre-KV5264BT-RY0E5UAn.js → dagre-KV5264BT-Df1MO_DJ.js} +1 -1
  52. package/payload/server/public/assets/{dagre-Dd_gq4yj.js → dagre-fQyUaxmN.js} +1 -1
  53. package/payload/server/public/assets/data-BFHNkj3Y.js +1 -0
  54. package/payload/server/public/assets/{diagram-5BDNPKRD-DR7XiPLh.js → diagram-5BDNPKRD-E3qTf9lX.js} +1 -1
  55. package/payload/server/public/assets/{diagram-G4DWMVQ6-2EbOfKFs.js → diagram-G4DWMVQ6-DCV-gR0I.js} +1 -1
  56. package/payload/server/public/assets/{diagram-MMDJMWI5-CGcY0Ze7.js → diagram-MMDJMWI5-CZKwrxSp.js} +1 -1
  57. package/payload/server/public/assets/{diagram-TYMM5635-Dk6MmAso.js → diagram-TYMM5635-ClXOl5De.js} +1 -1
  58. package/payload/server/public/assets/{erDiagram-SMLLAGMA-CKvdlUqk.js → erDiagram-SMLLAGMA-v2-MNn-e.js} +1 -1
  59. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BqKhvCDz.js → flowDiagram-DWJPFMVM-CDFzDH-2.js} +1 -1
  60. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-CeiE-IRb.js → ganttDiagram-T4ZO3ILL-DEA-33qn.js} +1 -1
  61. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-BbPKn7v2.js → gitGraphDiagram-UUTBAWPF-lLy_eZzu.js} +1 -1
  62. package/payload/server/public/assets/graph-Df2aJN0a.js +1 -0
  63. package/payload/server/public/assets/{graph-labels-BzMN9eev.js → graph-labels-CCTdQ2Vi.js} +1 -1
  64. package/payload/server/public/assets/{graphlib-tG6kR59B.js → graphlib-B6Y7yD4A.js} +1 -1
  65. package/payload/server/public/assets/{infoDiagram-42DDH7IO-6MiRGCI1.js → infoDiagram-42DDH7IO-D-Pm-tYB.js} +1 -1
  66. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-BwY4p5mg.js → ishikawaDiagram-UXIWVN3A-DVHkDPv1.js} +1 -1
  67. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-BXAQ_fUi.js → journeyDiagram-VCZTEJTY-CsxM47ah.js} +1 -1
  68. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-ChiiO7Yt.js → kanban-definition-6JOO6SKY-C7EYwNjH.js} +1 -1
  69. package/payload/server/public/assets/{lib-DbPz9cfu.js → lib-iH6Mg9EP.js} +1 -1
  70. package/payload/server/public/assets/{line-B5g5OrOD.js → line-CvBly01q.js} +1 -1
  71. package/payload/server/public/assets/{mermaid-parser.core-BMGzMKJx.js → mermaid-parser.core-D32akjeh.js} +1 -1
  72. package/payload/server/public/assets/{mermaid.core-DLMgkjXn.js → mermaid.core-B3O4X8ja.js} +3 -3
  73. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-BvyzRLAi.js → mindmap-definition-QFDTVHPH-yreoqnSK.js} +1 -1
  74. package/payload/server/public/assets/{page-D93GZ3YQ.js → page-BVuuY21i.js} +1 -1
  75. package/payload/server/public/assets/{page--nZ_z-z7.js → page-Dc2CtJkh.js} +1 -1
  76. package/payload/server/public/assets/{pieDiagram-DEJITSTG--EV9pVfs.js → pieDiagram-DEJITSTG-DEYXlFD5.js} +1 -1
  77. package/payload/server/public/assets/{public-sYW0Dwku.js → public-DknvBIqv.js} +3 -3
  78. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-DJcL08ft.js → quadrantDiagram-34T5L4WZ-CVTVYl5c.js} +1 -1
  79. package/payload/server/public/assets/{requirementDiagram-MS252O5E-D4VdmmSu.js → requirementDiagram-MS252O5E-D438ZRqW.js} +1 -1
  80. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-Dsw3nHRS.js → sankeyDiagram-XADWPNL6-B2Hs1q9K.js} +1 -1
  81. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-CAgemUx5.js → sequenceDiagram-FGHM5R23-BqRHtAoK.js} +1 -1
  82. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-B7dyL9_X.js → stateDiagram-FHFEXIEX-ILOj6_TV.js} +1 -1
  83. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B2cQBKI1.js +1 -0
  84. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-B7iKswhp.js → timeline-definition-GMOUNBTQ-D-uskEEs.js} +1 -1
  85. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-uFS0i1qX.js → vennDiagram-DHZGUBPP-DJjaoR79.js} +1 -1
  86. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-lDUh4LAD.js → wardleyDiagram-NUSXRM2D-B1EO_5jp.js} +1 -1
  87. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-0v7MEVwW.js → xychartDiagram-5P7HB3ND-CQnz0KUb.js} +1 -1
  88. package/payload/server/public/data.html +5 -5
  89. package/payload/server/public/graph.html +6 -6
  90. package/payload/server/public/index.html +8 -8
  91. package/payload/server/public/public.html +5 -5
  92. package/payload/server/public/assets/channel-CejaITMF.js +0 -1
  93. package/payload/server/public/assets/classDiagram-6PBFFD2Q-BmoqALf4.js +0 -1
  94. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-vMajwCNi.js +0 -1
  95. package/payload/server/public/assets/clone-mvVfHkyg.js +0 -1
  96. package/payload/server/public/assets/data-D2aPJB7w.js +0 -1
  97. package/payload/server/public/assets/graph-B5UWg8zV.js +0 -1
  98. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CImN6Wk6.js +0 -1
  99. /package/payload/server/public/assets/{ChatInput-Cj9JQtYt.js → ChatInput-BmmxQOhM.js} +0 -0
@@ -0,0 +1,410 @@
1
+ #!/usr/bin/env bash
2
+ # Task 273 — one-time JSONL stash-composite cleanup.
3
+ #
4
+ # For each `<uuid>.<unix-ms>.jsonl` file under a CLAUDE_STATE_ROOT/projects
5
+ # directory (top-level and archive/ subdir), pair it with the canonical
6
+ # `<uuid>.jsonl`. Keep the larger of the two as the canonical; move the
7
+ # smaller (with its sidecar) to QUARANTINE_ROOT, preserving the original
8
+ # basename. If only the timestamped sibling exists (no canonical), promote
9
+ # it to the canonical name. The quarantine path sits outside any directory
10
+ # the fs-watcher scans, so quarantined files do not appear in the sidebar.
11
+ #
12
+ # Safety rails:
13
+ # 1. Refuse to run if any PID file exists under CLAUDE_STATE_ROOT/sessions/.
14
+ # A live PTY holding one of the affected UUIDs is the failure mode this
15
+ # check guards against.
16
+ # 2. Refuse to overwrite an existing quarantine target — if the same
17
+ # timestamped basename already sits in quarantine from a prior run,
18
+ # surface the conflict and skip rather than clobber.
19
+ # 3. Sidecar `.meta.json` files follow the same disposition as their JSONL.
20
+ # 4. A `.log` audit trail records every rename and quarantine move with
21
+ # from=, to=, bytes=, mtime=. The transcript is the operator's
22
+ # audit before approving permanent deletion in a follow-up task.
23
+ #
24
+ # Usage:
25
+ # stash-cleanup.sh <claude-state-root> [--dry-run]
26
+ # e.g. ~/.realagent-code/.claude
27
+ #
28
+ # stash-cleanup.sh --self-test
29
+ # Seeds a temp dir with collision pairs, runs the cleanup against it,
30
+ # asserts post-conditions, and cleans up. Exits non-zero on failure.
31
+
32
+ set -euo pipefail
33
+
34
+ usage() {
35
+ cat <<USAGE
36
+ Task 273 JSONL stash-cleanup.
37
+
38
+ Usage:
39
+ $0 <claude-state-root> [--dry-run]
40
+ $0 --self-test
41
+
42
+ Arguments:
43
+ <claude-state-root> e.g. ~/.realagent-code/.claude
44
+
45
+ Flags:
46
+ --dry-run Plan-only: log what would happen, change nothing on disk.
47
+ --self-test Run the embedded smoke test; exits 0 on PASS.
48
+ USAGE
49
+ }
50
+
51
+ log() { printf '[stash-cleanup] %s\n' "$*"; }
52
+ err() { printf '[stash-cleanup] ERROR: %s\n' "$*" >&2; }
53
+
54
+ # Returns 0 if the basename matches `<uuid>.<unix-ms>.jsonl`. UUID is
55
+ # 36 chars including dashes; ms timestamp is exactly 13 digits.
56
+ is_stash_composite_basename() {
57
+ local name="$1"
58
+ [[ "$name" =~ ^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\.[0-9]{13}\.jsonl$ ]]
59
+ }
60
+
61
+ # Extract the canonical `<uuid>.jsonl` basename from a stash composite.
62
+ canonical_basename_for() {
63
+ local stash="$1"
64
+ printf '%s.jsonl\n' "${stash:0:36}"
65
+ }
66
+
67
+ bytes_of() {
68
+ if [[ -f "$1" ]]; then
69
+ stat -f%z "$1" 2>/dev/null || stat -c%s "$1"
70
+ else
71
+ printf '0\n'
72
+ fi
73
+ }
74
+
75
+ mtime_of() {
76
+ if [[ -f "$1" ]]; then
77
+ stat -f%m "$1" 2>/dev/null || stat -c%Y "$1"
78
+ else
79
+ printf '0\n'
80
+ fi
81
+ }
82
+
83
+ # Move a JSONL file (and its sidecar if present) to quarantine, preserving
84
+ # the original basename. Refuses to clobber.
85
+ move_to_quarantine() {
86
+ local src="$1" qdir="$2" reason="$3"
87
+ local bn dst sidecar_src sidecar_dst
88
+ bn=$(basename "$src")
89
+ dst="$qdir/$bn"
90
+
91
+ if [[ -e "$dst" ]]; then
92
+ err "quarantine target already exists, skipping: $dst"
93
+ return 1
94
+ fi
95
+
96
+ local sz mt
97
+ sz=$(bytes_of "$src")
98
+ mt=$(mtime_of "$src")
99
+ if [[ "$DRY_RUN" == "yes" ]]; then
100
+ log "DRY quarantine reason=$reason from=$src to=$dst bytes=$sz mtime=$mt"
101
+ else
102
+ mkdir -p "$qdir"
103
+ mv "$src" "$dst"
104
+ log "quarantine reason=$reason from=$src to=$dst bytes=$sz mtime=$mt"
105
+ fi
106
+
107
+ # Sidecar follows: `<base>.meta.json` where <base> is the JSONL path minus
108
+ # the trailing `.jsonl`.
109
+ sidecar_src="${src%.jsonl}.meta.json"
110
+ sidecar_dst="${dst%.jsonl}.meta.json"
111
+ if [[ -f "$sidecar_src" ]]; then
112
+ if [[ -e "$sidecar_dst" ]]; then
113
+ err "sidecar quarantine target already exists, skipping: $sidecar_dst"
114
+ return 1
115
+ fi
116
+ local ssz smt
117
+ ssz=$(bytes_of "$sidecar_src")
118
+ smt=$(mtime_of "$sidecar_src")
119
+ if [[ "$DRY_RUN" == "yes" ]]; then
120
+ log "DRY quarantine-sidecar from=$sidecar_src to=$sidecar_dst bytes=$ssz mtime=$smt"
121
+ else
122
+ mv "$sidecar_src" "$sidecar_dst"
123
+ log "quarantine-sidecar from=$sidecar_src to=$sidecar_dst bytes=$ssz mtime=$smt"
124
+ fi
125
+ fi
126
+ }
127
+
128
+ # Promote `src` to `dst` by renaming. Sidecar follows.
129
+ promote_in_place() {
130
+ local src="$1" dst="$2" reason="$3"
131
+ local sidecar_src sidecar_dst
132
+ local sz mt
133
+ sz=$(bytes_of "$src")
134
+ mt=$(mtime_of "$src")
135
+ if [[ "$DRY_RUN" == "yes" ]]; then
136
+ log "DRY promote reason=$reason from=$src to=$dst bytes=$sz mtime=$mt"
137
+ else
138
+ mv "$src" "$dst"
139
+ log "promote reason=$reason from=$src to=$dst bytes=$sz mtime=$mt"
140
+ fi
141
+
142
+ sidecar_src="${src%.jsonl}.meta.json"
143
+ sidecar_dst="${dst%.jsonl}.meta.json"
144
+ if [[ -f "$sidecar_src" ]]; then
145
+ local ssz smt
146
+ ssz=$(bytes_of "$sidecar_src")
147
+ smt=$(mtime_of "$sidecar_src")
148
+ if [[ "$DRY_RUN" == "yes" ]]; then
149
+ log "DRY promote-sidecar from=$sidecar_src to=$sidecar_dst bytes=$ssz mtime=$smt"
150
+ else
151
+ mv "$sidecar_src" "$sidecar_dst"
152
+ log "promote-sidecar from=$sidecar_src to=$sidecar_dst bytes=$ssz mtime=$smt"
153
+ fi
154
+ fi
155
+ }
156
+
157
+ # Process one directory (top-level or archive/). For each stash composite,
158
+ # pair with canonical; resolve.
159
+ process_dir() {
160
+ local dir="$1" qdir="$2"
161
+ [[ -d "$dir" ]] || { log "skip: $dir does not exist"; return 0; }
162
+
163
+ local handled=0
164
+ shopt -s nullglob
165
+ for path in "$dir"/*.jsonl; do
166
+ local bn
167
+ bn=$(basename "$path")
168
+ is_stash_composite_basename "$bn" || continue
169
+ handled=$((handled + 1))
170
+
171
+ local canonical_bn canonical_path
172
+ canonical_bn=$(canonical_basename_for "$bn")
173
+ canonical_path="$dir/$canonical_bn"
174
+
175
+ if [[ -f "$canonical_path" ]]; then
176
+ local stash_sz canonical_sz
177
+ stash_sz=$(bytes_of "$path")
178
+ canonical_sz=$(bytes_of "$canonical_path")
179
+ log "pair dir=$dir uuid=${bn:0:8} stash=$bn stash-bytes=$stash_sz canonical-bytes=$canonical_sz"
180
+
181
+ if (( stash_sz > canonical_sz )); then
182
+ # Stash wins. Quarantine canonical first (preserving its name),
183
+ # then promote stash to canonical.
184
+ local canonical_quarantine_name="$canonical_bn.canonical"
185
+ local canonical_qdst="$qdir/$canonical_quarantine_name"
186
+ if [[ -e "$canonical_qdst" ]]; then
187
+ err "canonical quarantine target exists, skipping pair: $canonical_qdst"
188
+ continue
189
+ fi
190
+ local csz cmt
191
+ csz=$(bytes_of "$canonical_path")
192
+ cmt=$(mtime_of "$canonical_path")
193
+ if [[ "$DRY_RUN" == "yes" ]]; then
194
+ log "DRY quarantine reason=smaller-canonical from=$canonical_path to=$canonical_qdst bytes=$csz mtime=$cmt"
195
+ else
196
+ mkdir -p "$qdir"
197
+ mv "$canonical_path" "$canonical_qdst"
198
+ log "quarantine reason=smaller-canonical from=$canonical_path to=$canonical_qdst bytes=$csz mtime=$cmt"
199
+ fi
200
+ # Canonical sidecar follows with the same suffix.
201
+ local csc_src="${canonical_path%.jsonl}.meta.json"
202
+ local csc_dst="${canonical_qdst%.jsonl}.meta.json"
203
+ if [[ -f "$csc_src" ]]; then
204
+ if [[ "$DRY_RUN" == "yes" ]]; then
205
+ log "DRY quarantine-sidecar from=$csc_src to=$csc_dst"
206
+ else
207
+ mv "$csc_src" "$csc_dst"
208
+ log "quarantine-sidecar from=$csc_src to=$csc_dst"
209
+ fi
210
+ fi
211
+ promote_in_place "$path" "$canonical_path" "stash-larger-than-canonical"
212
+ else
213
+ # Canonical wins (or equal). Quarantine stash with its original
214
+ # timestamped basename.
215
+ move_to_quarantine "$path" "$qdir" "smaller-stash" || true
216
+ fi
217
+ else
218
+ # No canonical on disk. Promote the stash to canonical.
219
+ log "lone-stash dir=$dir uuid=${bn:0:8} stash=$bn"
220
+ promote_in_place "$path" "$canonical_path" "no-canonical-present"
221
+ fi
222
+ done
223
+ shopt -u nullglob
224
+
225
+ log "summary dir=$dir composites-handled=$handled"
226
+ }
227
+
228
+ # Walks every project directory under <state-root>/projects/ and processes
229
+ # top-level + archive/ for each.
230
+ run_cleanup() {
231
+ local state_root="$1"
232
+ local projects_root="$state_root/projects"
233
+ local sessions_root="$state_root/sessions"
234
+ local quarantine_root="$state_root/quarantine"
235
+
236
+ [[ -d "$projects_root" ]] || { err "projects root not found: $projects_root"; return 2; }
237
+
238
+ # Pre-flight: no live PTY may be running. The sessions/ directory holds
239
+ # one PID file per live PTY (Task 199 contract); empty = no live PTYs.
240
+ if [[ -d "$sessions_root" ]]; then
241
+ shopt -s nullglob
242
+ local pids=("$sessions_root"/*.json)
243
+ shopt -u nullglob
244
+ if (( ${#pids[@]} > 0 )); then
245
+ err "live PTY detected — ${#pids[@]} PID file(s) under $sessions_root; refuse to touch on-disk state"
246
+ err "stop the affected sessions first, then retry"
247
+ return 3
248
+ fi
249
+ fi
250
+ log "pre-flight pid-files=0 sessions-root=$sessions_root"
251
+
252
+ shopt -s nullglob
253
+ local project_dirs=("$projects_root"/*/)
254
+ shopt -u nullglob
255
+ log "projects-found count=${#project_dirs[@]}"
256
+
257
+ for project_dir in "${project_dirs[@]}"; do
258
+ local slug
259
+ slug=$(basename "$project_dir")
260
+ local qdir="$quarantine_root/$slug"
261
+ log "project-begin slug=$slug"
262
+ process_dir "${project_dir%/}" "$qdir"
263
+ process_dir "${project_dir%/}/archive" "$qdir/archive"
264
+ log "project-end slug=$slug"
265
+ done
266
+
267
+ log "done state-root=$state_root quarantine-root=$quarantine_root dry-run=$DRY_RUN"
268
+ }
269
+
270
+ # ----- self-test --------------------------------------------------------
271
+
272
+ self_test() {
273
+ local tmp
274
+ tmp=$(mktemp -d)
275
+ # Expand at trap-set time so the path survives function-local scope.
276
+ trap "rm -rf '$tmp'" EXIT
277
+
278
+ local state="$tmp/state"
279
+ mkdir -p "$state/projects/slug-A" "$state/projects/slug-A/archive" "$state/sessions"
280
+
281
+ # Pair 1: timestamped sibling is larger; should win.
282
+ local uuid1="4bfe4274-1111-4111-8111-111111111111"
283
+ local big="payload-big"
284
+ local small="x"
285
+ printf '%s' "$big" > "$state/projects/slug-A/${uuid1}.1779477697566.jsonl"
286
+ printf '%s' "$small" > "$state/projects/slug-A/${uuid1}.jsonl"
287
+
288
+ # Pair 2: canonical is larger; sibling loses.
289
+ local uuid2="9c26cc66-2222-4222-8222-222222222222"
290
+ printf '%s' "big-canonical" > "$state/projects/slug-A/${uuid2}.jsonl"
291
+ printf '%s' "tiny" > "$state/projects/slug-A/${uuid2}.1779477700000.jsonl"
292
+
293
+ # Pair 3 (archive): canonical wins (matches the be23f93a archive-pair shape).
294
+ local uuid3="654f5fe5-3333-4333-8333-333333333333"
295
+ printf '%s' "archived-big-history" > "$state/projects/slug-A/archive/${uuid3}.jsonl"
296
+ printf '%s' "tiny" > "$state/projects/slug-A/archive/${uuid3}.1779477705000.jsonl"
297
+
298
+ # Pair 4: lone stash, no canonical present.
299
+ local uuid4="abcdef01-4444-4444-8444-444444444444"
300
+ printf '%s' "lone-stash-content" > "$state/projects/slug-A/${uuid4}.1779477710000.jsonl"
301
+
302
+ # Sidecar on uuid1's stash (the winner) — should travel with the rename
303
+ # to canonical. Sidecar on uuid2's stash (the loser) — should follow to
304
+ # quarantine.
305
+ printf '{"meta":"win"}' > "$state/projects/slug-A/${uuid1}.1779477697566.meta.json"
306
+ printf '{"meta":"lose"}' > "$state/projects/slug-A/${uuid2}.1779477700000.meta.json"
307
+
308
+ DRY_RUN=no run_cleanup "$state" >/tmp/stash-cleanup-selftest.log 2>&1 \
309
+ || { cat /tmp/stash-cleanup-selftest.log; err "self-test: run_cleanup failed"; exit 1; }
310
+
311
+ local fail=0
312
+ local q="$state/quarantine/slug-A"
313
+ local qa="$state/quarantine/slug-A/archive"
314
+
315
+ # uuid1: canonical now holds "payload-big"; old canonical lives in
316
+ # quarantine with the `.canonical` suffix.
317
+ if [[ "$(cat "$state/projects/slug-A/${uuid1}.jsonl" 2>/dev/null)" != "$big" ]]; then
318
+ err "uuid1 canonical did not receive stash content"; fail=1
319
+ fi
320
+ if [[ -e "$state/projects/slug-A/${uuid1}.1779477697566.jsonl" ]]; then
321
+ err "uuid1 stash sibling still on disk after promotion"; fail=1
322
+ fi
323
+ if [[ "$(cat "$q/${uuid1}.jsonl.canonical" 2>/dev/null)" != "$small" ]]; then
324
+ err "uuid1 old canonical not in quarantine"; fail=1
325
+ fi
326
+ # Sidecar followed promotion.
327
+ if [[ "$(cat "$state/projects/slug-A/${uuid1}.meta.json" 2>/dev/null)" != '{"meta":"win"}' ]]; then
328
+ err "uuid1 sidecar did not follow promotion"; fail=1
329
+ fi
330
+
331
+ # uuid2: canonical untouched; stash sibling in quarantine.
332
+ if [[ "$(cat "$state/projects/slug-A/${uuid2}.jsonl" 2>/dev/null)" != "big-canonical" ]]; then
333
+ err "uuid2 canonical altered"; fail=1
334
+ fi
335
+ if [[ ! -f "$q/${uuid2}.1779477700000.jsonl" ]]; then
336
+ err "uuid2 stash sibling not quarantined"; fail=1
337
+ fi
338
+ if [[ "$(cat "$q/${uuid2}.1779477700000.meta.json" 2>/dev/null)" != '{"meta":"lose"}' ]]; then
339
+ err "uuid2 sidecar did not follow to quarantine"; fail=1
340
+ fi
341
+
342
+ # uuid3 (archive): canonical wins; stash sibling in archive-quarantine.
343
+ if [[ "$(cat "$state/projects/slug-A/archive/${uuid3}.jsonl" 2>/dev/null)" != "archived-big-history" ]]; then
344
+ err "uuid3 archive canonical altered"; fail=1
345
+ fi
346
+ if [[ ! -f "$qa/${uuid3}.1779477705000.jsonl" ]]; then
347
+ err "uuid3 archive stash sibling not quarantined under archive/"; fail=1
348
+ fi
349
+
350
+ # uuid4: lone stash promoted in place; no quarantine.
351
+ if [[ "$(cat "$state/projects/slug-A/${uuid4}.jsonl" 2>/dev/null)" != "lone-stash-content" ]]; then
352
+ err "uuid4 lone stash not promoted to canonical"; fail=1
353
+ fi
354
+ if [[ -e "$state/projects/slug-A/${uuid4}.1779477710000.jsonl" ]]; then
355
+ err "uuid4 stash file still on disk after promotion"; fail=1
356
+ fi
357
+
358
+ # No <uuid>.<unix-ms>.jsonl siblings remain in the projects scan scope.
359
+ local residual
360
+ residual=$(find "$state/projects" -type f -name '*.jsonl' \
361
+ | awk -F/ '{ print $NF }' \
362
+ | grep -cE '^[a-f0-9-]{36}\.[0-9]{13}\.jsonl$' || true)
363
+ if (( residual > 0 )); then
364
+ err "self-test: $residual timestamped siblings still present"; fail=1
365
+ fi
366
+
367
+ # Live-PTY guard: seed a PID file and confirm the script refuses.
368
+ printf '{"pid":1234}' > "$state/sessions/1234.json"
369
+ if DRY_RUN=no run_cleanup "$state" >>/tmp/stash-cleanup-selftest.log 2>&1; then
370
+ err "self-test: live-PTY guard did not refuse"; fail=1
371
+ fi
372
+ rm -f "$state/sessions/1234.json"
373
+
374
+ if (( fail > 0 )); then
375
+ cat /tmp/stash-cleanup-selftest.log
376
+ err "self-test: FAILED"
377
+ exit 1
378
+ fi
379
+
380
+ log "self-test: PASS"
381
+ }
382
+
383
+ # ----- entry point ------------------------------------------------------
384
+
385
+ DRY_RUN=no
386
+ case "${1:-}" in
387
+ -h|--help|"")
388
+ usage
389
+ exit 0
390
+ ;;
391
+ --self-test)
392
+ self_test
393
+ exit 0
394
+ ;;
395
+ *)
396
+ STATE_ROOT="${1:-}"
397
+ shift || true
398
+ while (( $# > 0 )); do
399
+ case "$1" in
400
+ --dry-run) DRY_RUN=yes ;;
401
+ *) err "unknown flag: $1"; usage; exit 2 ;;
402
+ esac
403
+ shift
404
+ done
405
+ [[ -n "$STATE_ROOT" ]] || { usage; exit 2; }
406
+ # Expand ~ since the caller may pass it.
407
+ STATE_ROOT="${STATE_ROOT/#\~/$HOME}"
408
+ run_cleanup "$STATE_ROOT" || exit $?
409
+ ;;
410
+ esac
@@ -4,6 +4,11 @@
4
4
  "name": "Rubytech LLC"
5
5
  },
6
6
  "plugins": [
7
+ {
8
+ "name": "founder-pack",
9
+ "source": "./founder-pack",
10
+ "version": "0.1.0"
11
+ },
7
12
  {
8
13
  "name": "teaching",
9
14
  "source": "./teaching",
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "founder-pack",
3
+ "description": "founder-pack plugin",
4
+ "version": "0.1.0",
5
+ "author": {
6
+ "name": "Rubytech LLC"
7
+ }
8
+ }