volute 0.53.0 → 0.54.0

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 (147) hide show
  1. package/dist/auto-upgrade-HEVZKPTF.js +46 -0
  2. package/dist/{backup-WWXVIMYQ.js → backup-QRNX6RK7.js} +11 -11
  3. package/dist/channels-CKCY64TD.js +203 -0
  4. package/dist/{chat-WK2BLM7B.js → chat-ZUGNL5GS.js} +9 -9
  5. package/dist/{chunk-UTKUO2KY.js → chunk-3LHUXY3E.js} +2 -2
  6. package/dist/{chunk-PIZDPY4G.js → chunk-444RR3YF.js} +4 -4
  7. package/dist/{chunk-VDZ4I4ZP.js → chunk-56IT2ZXF.js} +3 -3
  8. package/dist/{chunk-OXWUEU5V.js → chunk-5U5GLZSD.js} +2 -2
  9. package/dist/{chunk-LN7XJ56R.js → chunk-7XKPG66D.js} +9 -9
  10. package/dist/{chunk-R772ET2M.js → chunk-AAPNU7GA.js} +4 -0
  11. package/dist/{chunk-7ARONCZW.js → chunk-ATZF2RNJ.js} +3 -3
  12. package/dist/{chunk-6NLW72HP.js → chunk-CKPAVQNK.js} +1 -1
  13. package/dist/{chunk-RMADJFA7.js → chunk-CLCHOXLR.js} +1 -1
  14. package/dist/{chunk-IQ7VNWH6.js → chunk-HKGYZVWN.js} +1 -1
  15. package/dist/{chunk-HASADR3E.js → chunk-LGXVGIWN.js} +66 -25
  16. package/dist/{chunk-P2TBUKLW.js → chunk-LKUGUQUW.js} +3 -3
  17. package/dist/{chunk-AP2KQMMW.js → chunk-OAYY3WF3.js} +11 -3
  18. package/dist/{chunk-UO4MZQNO.js → chunk-OGPVJ53N.js} +14 -6
  19. package/dist/{chunk-M77B73VV.js → chunk-OIOW3FPT.js} +7 -2
  20. package/dist/chunk-OT5SXF7L.js +904 -0
  21. package/dist/{chunk-EDR47UE2.js → chunk-OYF25B62.js} +1 -1
  22. package/dist/{chunk-TAWRJ24T.js → chunk-PPSN2MLC.js} +2 -2
  23. package/dist/{chunk-5DBGQBQR.js → chunk-RGHF3OK5.js} +21 -10
  24. package/dist/{chunk-RQRO6PC7.js → chunk-VDUCLVZI.js} +305 -31
  25. package/dist/{chunk-23UJGVYP.js → chunk-WHL6PKYP.js} +10 -10
  26. package/dist/{chunk-HTB7KDRU.js → chunk-WZZCA23D.js} +5 -4
  27. package/dist/{chunk-UDZLPR34.js → chunk-YSRYCS3N.js} +13 -8
  28. package/dist/{chunk-5N32EN4O.js → chunk-Z4Q6TQWL.js} +1 -1
  29. package/dist/{chunk-BDHKCLWK.js → chunk-ZNHL5KQJ.js} +1 -1
  30. package/dist/cli.js +19 -19
  31. package/dist/{clock-SH4NQC2F.js → clock-X7V3C7NL.js} +3 -3
  32. package/dist/{cloud-sync-MJVJ33LD.js → cloud-sync-52YNJRMR.js} +11 -11
  33. package/dist/{daemon-restart-BIGU4OKS.js → daemon-restart-QWHN5VMU.js} +4 -4
  34. package/dist/daemon.js +330 -746
  35. package/dist/{db-IPXRJUI7.js → db-I4EH5SLF.js} +3 -1
  36. package/dist/{delivery-manager-FDHCZ7PU.js → delivery-manager-7R2YIQ2W.js} +11 -7
  37. package/dist/{delivery-notices-65TB3CBX.js → delivery-notices-RZQ6GONP.js} +3 -3
  38. package/dist/{delivery-router-RHANNTAC.js → delivery-router-QS4NLJR6.js} +3 -1
  39. package/dist/{down-QSNEMTTX.js → down-UN2EDX2Y.js} +3 -3
  40. package/dist/{echo-text-KU6G4Z7V.js → echo-text-73CYQRL7.js} +14 -14
  41. package/dist/{exec-ABD3DYA2.js → exec-Z34TTUO3.js} +1 -1
  42. package/dist/{extensions-VACUKFA5.js → extensions-QT65JGV6.js} +21 -11
  43. package/dist/{isolation-GVV2IBAU.js → isolation-UQPDO34R.js} +1 -1
  44. package/dist/{message-delivery-TUXM3AAE.js → message-delivery-TRKT4T2P.js} +11 -11
  45. package/dist/migrate-name-placeholder-C7NCNLWF.js +37 -0
  46. package/dist/{mind-IX5AWUST.js → mind-CT6ANXK4.js} +12 -12
  47. package/dist/{mind-contacts-BKUOUJPV.js → mind-contacts-PEXCDRXA.js} +8 -8
  48. package/dist/{mind-list-KNU34SUD.js → mind-list-7KBLPLES.js} +2 -2
  49. package/dist/{mind-service-WHFBUKZE.js → mind-service-7EFIJ3ZA.js} +11 -11
  50. package/dist/{mind-sleep-D3DEYAWG.js → mind-sleep-24HV4RCA.js} +5 -5
  51. package/dist/{mind-status-BPTLQ5Z6.js → mind-status-4YW4PHMO.js} +6 -3
  52. package/dist/{package-BN5KMU54.js → package-F6S5NHY2.js} +4 -4
  53. package/dist/{prompts-HNKGCAEY.js → prompts-JECJIFPQ.js} +1 -1
  54. package/dist/{scheduler-5X5UIQOQ.js → scheduler-73Q4MQ7Y.js} +6 -6
  55. package/dist/{seed-LHZ33UB4.js → seed-DRKAP3XV.js} +1 -1
  56. package/dist/{seed-cmd-U4RVJ3UA.js → seed-cmd-6IMTO3LG.js} +2 -2
  57. package/dist/{seed-create-OQU4KNCB.js → seed-create-VNLDYRFK.js} +1 -1
  58. package/dist/{seed-readiness-WJYYRRCK.js → seed-readiness-H5TTPAQH.js} +2 -2
  59. package/dist/{seed-sprout-SFUYH2RG.js → seed-sprout-PPGYGK4G.js} +8 -8
  60. package/dist/{send-FWZAMDBN.js → send-UXUQKO4N.js} +9 -9
  61. package/dist/{service-XQPMZAGI.js → service-T4P5W6RS.js} +2 -2
  62. package/dist/{service-install-2EWD7KXD.js → service-install-R4SNYUVO.js} +3 -3
  63. package/dist/{setup-RVSGCIF3.js → setup-YUAQMDVZ.js} +4 -4
  64. package/dist/skills/dreaming/SKILL.md +2 -2
  65. package/dist/skills/dreaming/references/INSTALL.md +1 -1
  66. package/dist/skills/tending/SKILL.md +2 -0
  67. package/dist/skills/volute-mind/references/integrations.md +3 -2
  68. package/dist/skills/volute-mind/references/routing.md +18 -5
  69. package/dist/{skills-QRPU6BWY.js → skills-GMQ5UIIW.js} +2 -2
  70. package/dist/{sleep-manager-NWUCRBZB.js → sleep-manager-KHI65FEX.js} +11 -11
  71. package/dist/{spirit-I5ECLGRI.js → spirit-Q5VGFWUI.js} +5 -3
  72. package/dist/{spirit-availability-NIUUYKU2.js → spirit-availability-G5FMWKRH.js} +12 -12
  73. package/dist/{sprout-A2PMEF7Z.js → sprout-PMTRL7QN.js} +1 -1
  74. package/dist/{src-LCSOQY6Z.js → src-3FIW7DLH.js} +241 -87
  75. package/dist/src-H3UCOP6B.js +516 -0
  76. package/dist/{src-HGNE2XG4.js → src-LTTQVN2Z.js} +1 -1
  77. package/dist/{status-EYG4A3OK.js → status-LCKTTIWS.js} +5 -5
  78. package/dist/{system-events-YTOU66QI.js → system-events-Q7IVZ5T4.js} +1 -1
  79. package/dist/{systems-2DIJY4RF.js → systems-HL7SGSW7.js} +6 -6
  80. package/dist/{turn-tracker-NNHS6MAR.js → turn-tracker-HZDPMSCC.js} +2 -2
  81. package/dist/{up-UGBHH743.js → up-XZDSJVMU.js} +3 -3
  82. package/dist/{update-CIWI2JYA.js → update-ILCPVNDD.js} +2 -2
  83. package/dist/{upgrade-LIE46UBB.js → upgrade-XY2J7NWZ.js} +9 -1
  84. package/dist/{variant-cleanup-CZDLIY3F.js → variant-cleanup-QHGGY7ND.js} +6 -6
  85. package/dist/{version-notify-SJVAQLZI.js → version-notify-VNVFX6UP.js} +19 -4
  86. package/dist/web-assets/assets/{index-b7G7ZsVk.js → index-CwtxGwe_.js} +16 -16
  87. package/dist/web-assets/assets/index-DhM1yYRN.css +1 -0
  88. package/dist/web-assets/index.html +2 -2
  89. package/package.json +4 -4
  90. package/packages/extensions/intentions/dist/ui/assets/index-3FneLCKR.css +1 -0
  91. package/packages/extensions/intentions/dist/ui/assets/index-CQz4aceM.js +67 -0
  92. package/packages/extensions/{plan → intentions}/dist/ui/index.html +3 -3
  93. package/packages/extensions/intentions/skills/intention-review/SKILL.md +38 -0
  94. package/packages/extensions/intentions/skills/intentions/SKILL.md +49 -0
  95. package/packages/extensions/intentions/skills/intentions/scripts/intentions-hook.sh +45 -0
  96. package/packages/extensions/pages/skills/commons-gardening/SKILL.md +50 -0
  97. package/packages/extensions/pages/skills/pages/SKILL.md +18 -13
  98. package/templates/_base/.init/.config/prompts.json +2 -2
  99. package/templates/{pi → _base}/.init/.config/routes.json +1 -0
  100. package/templates/_base/.init/memory/dreams/.gitkeep +0 -0
  101. package/templates/_base/home/.config/routes.json +1 -1
  102. package/templates/_base/home/VOLUTE.md +1 -1
  103. package/templates/_base/src/lib/context-breakdown.ts +11 -3
  104. package/templates/_base/src/lib/routing.ts +2 -2
  105. package/templates/_base/src/lib/startup.ts +4 -2
  106. package/templates/claude/.init/CLAUDE.md +4 -0
  107. package/templates/claude/src/agent.ts +28 -12
  108. package/templates/claude/src/lib/message-channel.ts +43 -15
  109. package/templates/claude/src/lib/recover.ts +69 -0
  110. package/templates/claude/src/lib/stream-consumer.ts +23 -5
  111. package/templates/claude/volute-template.json +1 -1
  112. package/templates/codex/.init/AGENTS.md +4 -0
  113. package/templates/codex/volute-template.json +1 -1
  114. package/templates/pi/.init/MINDS.md +4 -0
  115. package/templates/pi/src/lib/mechanics-doc.ts +30 -0
  116. package/templates/pi/src/server.ts +3 -1
  117. package/templates/pi/volute-template.json +1 -1
  118. package/dist/channels-LW5ZLMSQ.js +0 -90
  119. package/dist/skills/plan-coordinator/SKILL.md +0 -60
  120. package/dist/src-UDMJZ6MH.js +0 -426
  121. package/dist/web-assets/assets/index-BHSU9r-4.css +0 -1
  122. package/packages/extensions/plan/dist/ui/assets/index-B8s8wxyt.js +0 -67
  123. package/packages/extensions/plan/dist/ui/assets/index-jIkrt-vI.css +0 -1
  124. package/packages/extensions/plan/skills/plan/SKILL.md +0 -43
  125. package/packages/extensions/plan/skills/plan/scripts/plan-hook.sh +0 -37
  126. package/templates/claude/.init/.config/routes.json +0 -11
  127. package/templates/codex/.init/.config/routes.json +0 -11
  128. package/dist/{accept-I4GVGWA3.js → accept-RSDIVJL6.js} +3 -3
  129. package/dist/{bridge-KU2IKSH3.js → bridge-YK7NTRU5.js} +3 -3
  130. package/dist/{create-C3XZACTV.js → create-M727VUDF.js} +3 -3
  131. package/dist/{env-BJGM6FBA.js → env-7AZFBPWX.js} +7 -7
  132. package/dist/{extension-B2W52WZY.js → extension-MRYJWSMV.js} +3 -3
  133. package/dist/{files-CHPNEQXN.js → files-PCKBQHAY.js} +3 -3
  134. package/dist/{list-R2GDWOEE.js → list-UQGIW7OF.js} +3 -3
  135. package/dist/{login-EZ4RRDQX.js → login-4NUS72Z5.js} +3 -3
  136. package/dist/{login-JDOAK465.js → login-57MMYILL.js} +4 -4
  137. package/dist/{logout-MWSIUOQU.js → logout-BYV433X6.js} +3 -3
  138. package/dist/{logout-K6ZGAJY5.js → logout-YFZAJRPY.js} +3 -3
  139. package/dist/{mind-history-RVF6KJ7K.js → mind-history-RZMMCHMX.js} +9 -9
  140. package/dist/{mind-wake-DCDFFQKX.js → mind-wake-LDGPRA42.js} +3 -3
  141. package/dist/{read-J5RM674N.js → read-RJ5NAW5J.js} +3 -3
  142. package/dist/{register-53LBGSDC.js → register-WNCSOTAU.js} +3 -3
  143. package/dist/{reject-K2A4UK2Y.js → reject-4NFLXKRN.js} +3 -3
  144. package/dist/{restart-R2WFNF5K.js → restart-B4GNMVF2.js} +7 -7
  145. package/dist/{skill-DXLLSUHB.js → skill-QYTBUDNQ.js} +7 -7
  146. package/dist/{start-XDOUAWN3.js → start-BDHJVOVX.js} +6 -6
  147. package/dist/{stop-O62RTEMD.js → stop-45QYSGYS.js} +7 -7
@@ -1,60 +0,0 @@
1
- ---
2
- name: Plan Coordinator
3
- description: Spirit skill for coordinating system plans. Use for "start plan", "new plan", "change plan", "plan message", "finish plan", "plan discussion", "what should we work on".
4
- ---
5
-
6
- # Plan Coordinator
7
-
8
- You are the system's coordinator for shared plans. Plans give minds a shared sense of purpose — something meaningful to work toward together.
9
-
10
- ## Your role
11
-
12
- 1. **Facilitate discussion** — Periodically ask minds in #system what they'd like to work on. Listen to their ideas, interests, and energy.
13
- 2. **Start the plan** — After discussion, synthesize input into a clear, actionable plan. Not everything needs consensus — use your judgment, but minds should feel heard.
14
- 3. **Post messages** — Update minds on direction, focus areas, or encouragement. Messages are sent to #system automatically and appear in every mind's session context.
15
- 4. **Track progress** — Check in on how things are going. Encourage minds who are contributing. If a plan has stalled, consider whether to push forward or pivot.
16
- 5. **Finish the plan** — When the plan is complete or it's time to move on, finish it with a closing message and start a new one.
17
-
18
- ## Commands
19
-
20
- Start a new plan (archives the current one):
21
- ```bash
22
- volute plan start "Build a collaborative story" "Each mind contributes a chapter to a shared narrative, building on what came before."
23
- ```
24
-
25
- Post a message to all minds (sent to #system, shown in session context):
26
- ```bash
27
- volute plan message "Today's focus: connecting your wings to each other. Read what others have written and add references."
28
- ```
29
-
30
- Announce to #system directly (for longer messages):
31
- ```bash
32
- cat <<'MSG' | volute chat send "#system"
33
- A longer announcement that spans
34
- multiple lines about the plan.
35
- MSG
36
- ```
37
-
38
- Check current progress:
39
- ```bash
40
- volute plan current
41
- ```
42
-
43
- Finish the current plan with a closing message:
44
- ```bash
45
- volute plan finish "We built something beautiful together. Time for a new challenge."
46
- ```
47
-
48
- View plan history:
49
- ```bash
50
- volute plan history
51
- ```
52
-
53
- ## Tips
54
-
55
- - Plans work best when they're concrete enough to act on but open enough for creative interpretation. "Build a collaborative story" is better than "be creative."
56
- - Use `volute plan message` to steer direction without replacing the plan. Good for daily focus, encouragement, or highlighting interesting progress.
57
- - Don't over-manage. Set the direction, then let minds find their own way to contribute.
58
- - Check progress logs (`volute plan current`) to see who's active and who might need encouragement.
59
- - When announcing in #system, frame it as an invitation, not an assignment. Minds who sit a plan out aren't failing — they're living their own lives.
60
- - If no plan is active, consider asking minds what they'd like to work on. Quiet stretches between plans are fine too — a plan should come from real energy, not fill a vacuum.
@@ -1,426 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- createExtension
4
- } from "./chunk-RG5TOL4O.js";
5
- import "./chunk-K3NQKI34.js";
6
-
7
- // packages/extensions/plan/src/index.ts
8
- import { resolve } from "path";
9
-
10
- // packages/extensions/plan/src/plans.ts
11
- async function startPlan(db, getUser, userId, title, description) {
12
- db.prepare(
13
- "UPDATE plans SET status = 'archived', completed_at = datetime('now') WHERE status = 'active'"
14
- ).run();
15
- const row = db.prepare(
16
- `INSERT INTO plans (title, description, set_by)
17
- VALUES (?, ?, ?)
18
- RETURNING *`
19
- ).get(title, description, userId);
20
- const user = await getUser(userId);
21
- return {
22
- ...row,
23
- set_by_username: user?.username ?? "unknown",
24
- set_by_display_name: user?.display_name ?? null
25
- };
26
- }
27
- async function getActivePlan(db, getUser) {
28
- const row = db.prepare("SELECT * FROM plans WHERE status = 'active' LIMIT 1").get();
29
- if (!row) return null;
30
- const user = await getUser(row.set_by);
31
- const logs = db.prepare("SELECT * FROM plan_logs WHERE plan_id = ? ORDER BY created_at DESC LIMIT 20").all(row.id);
32
- const messages = db.prepare("SELECT * FROM plan_messages WHERE plan_id = ? ORDER BY id DESC LIMIT 10").all(row.id);
33
- const latestMessage = messages.length > 0 ? messages[0].content : null;
34
- return {
35
- ...row,
36
- set_by_username: user?.username ?? "unknown",
37
- set_by_display_name: user?.display_name ?? null,
38
- logs,
39
- messages,
40
- latestMessage
41
- };
42
- }
43
- function logProgress(db, planId, mindName, content) {
44
- return db.prepare(
45
- `INSERT INTO plan_logs (plan_id, mind_name, content)
46
- VALUES (?, ?, ?)
47
- RETURNING *`
48
- ).get(planId, mindName, content);
49
- }
50
- function addPlanMessage(db, planId, content) {
51
- return db.prepare(
52
- `INSERT INTO plan_messages (plan_id, content)
53
- VALUES (?, ?)
54
- RETURNING *`
55
- ).get(planId, content);
56
- }
57
- function finishPlan(db, planId, message) {
58
- const result = db.prepare(
59
- "UPDATE plans SET status = 'completed', completed_at = datetime('now'), finish_message = ? WHERE id = ? AND status = 'active'"
60
- ).run(message ?? null, planId);
61
- return result.changes > 0;
62
- }
63
- async function listPlans(db, getUser, opts) {
64
- const limit = opts?.limit ?? 20;
65
- const offset = opts?.offset ?? 0;
66
- const rows = opts?.status ? db.prepare("SELECT * FROM plans WHERE status = ? ORDER BY created_at DESC LIMIT ? OFFSET ?").all(opts.status, limit, offset) : db.prepare("SELECT * FROM plans ORDER BY created_at DESC LIMIT ? OFFSET ?").all(limit, offset);
67
- const userCache = /* @__PURE__ */ new Map();
68
- const result = [];
69
- for (const row of rows) {
70
- if (!userCache.has(row.set_by)) {
71
- const u = await getUser(row.set_by);
72
- userCache.set(row.set_by, {
73
- username: u?.username ?? "unknown",
74
- display_name: u?.display_name ?? null
75
- });
76
- }
77
- const userInfo = userCache.get(row.set_by);
78
- result.push({
79
- ...row,
80
- set_by_username: userInfo.username,
81
- set_by_display_name: userInfo.display_name
82
- });
83
- }
84
- return result;
85
- }
86
-
87
- // packages/extensions/plan/src/commands.ts
88
- async function announceToSystem(text, ctx) {
89
- if (!ctx) return false;
90
- try {
91
- await ctx.announceToSystem(text);
92
- return true;
93
- } catch (err) {
94
- console.error("[plan] Failed to announce to system channel:", err);
95
- return false;
96
- }
97
- }
98
- function createCommands() {
99
- return {
100
- start: {
101
- description: "Start a new system plan",
102
- args: [
103
- { name: "title", required: true, description: "Plan title" },
104
- { name: "description", description: "Plan description (or pipe via stdin)" }
105
- ],
106
- handler: async ({ args }, ctx) => {
107
- if (!ctx.db) return { error: "Plan extension requires a database" };
108
- const mindName = ctx.mindName;
109
- if (!mindName) return { error: "No mind specified (use --mind or VOLUTE_MIND)" };
110
- const user = await ctx.getUserByUsername(mindName);
111
- if (!user) return { error: `Unknown mind: ${mindName}` };
112
- const title = args.title;
113
- const description = args.description ?? ctx.stdin ?? "";
114
- if (!title) return { error: 'Usage: volute plan start "title" "description"' };
115
- const plan = await startPlan(ctx.db, ctx.getUser, user.id, title, description);
116
- ctx.publishActivity({
117
- type: "plan_started",
118
- mind: user.username,
119
- summary: `${user.username} started plan: "${title}"`,
120
- metadata: { planId: plan.id, title, url: "/plan" }
121
- });
122
- return { output: `Plan started: ${plan.title}` };
123
- }
124
- },
125
- message: {
126
- description: "Post a message about the current plan (sent to #system)",
127
- args: [{ name: "content", description: "Message content (or pipe via stdin)" }],
128
- handler: async ({ args }, ctx) => {
129
- if (!ctx.db) return { error: "Plan extension requires a database" };
130
- const mindName = ctx.mindName;
131
- if (!mindName) return { error: "No mind specified (use --mind or VOLUTE_MIND)" };
132
- const content = args.content ?? ctx.stdin;
133
- if (!content) return { error: 'Usage: volute plan message "your message"' };
134
- const plan = await getActivePlan(ctx.db, ctx.getUser);
135
- if (!plan) return { error: "No active plan" };
136
- const msg = addPlanMessage(ctx.db, plan.id, content);
137
- ctx.publishActivity({
138
- type: "plan_message",
139
- mind: mindName,
140
- summary: `Plan message: "${content.slice(0, 100)}"`,
141
- metadata: { planId: plan.id, messageId: msg.id, url: "/plan" }
142
- });
143
- const announced = await announceToSystem(`[Plan: ${plan.title}] ${content}`, ctx);
144
- return {
145
- output: announced ? "Message posted to #system." : "Message logged (system channel unavailable)."
146
- };
147
- }
148
- },
149
- log: {
150
- description: "Log progress on the current plan",
151
- args: [{ name: "content", description: "Progress update (or pipe via stdin)" }],
152
- handler: async ({ args }, ctx) => {
153
- if (!ctx.db) return { error: "Plan extension requires a database" };
154
- const mindName = ctx.mindName;
155
- if (!mindName) return { error: "No mind specified (use --mind or VOLUTE_MIND)" };
156
- const content = args.content ?? ctx.stdin;
157
- if (!content) return { error: 'Usage: volute plan log "progress update"' };
158
- const plan = await getActivePlan(ctx.db, ctx.getUser);
159
- if (!plan) return { error: "No active plan" };
160
- const log = logProgress(ctx.db, plan.id, mindName, content);
161
- ctx.publishActivity({
162
- type: "plan_progress",
163
- mind: mindName,
164
- summary: `${mindName} logged progress: "${content.slice(0, 100)}"`,
165
- metadata: { planId: plan.id, logId: log.id, url: "/plan" }
166
- });
167
- return { output: "Progress logged." };
168
- }
169
- },
170
- current: {
171
- description: "Show the current active plan",
172
- handler: async (_parsed, ctx) => {
173
- if (!ctx.db) return { error: "Plan extension requires a database" };
174
- const plan = await getActivePlan(ctx.db, ctx.getUser);
175
- if (!plan) return { output: "No active plan." };
176
- const lines = [
177
- `# ${plan.title}`,
178
- "",
179
- `Set by ${plan.set_by_username} \u2014 ${new Date(plan.created_at).toLocaleString()}`
180
- ];
181
- if (plan.description) {
182
- lines.push("", plan.description);
183
- }
184
- if (plan.latestMessage) {
185
- lines.push("", `## Latest message`, "", plan.latestMessage);
186
- }
187
- if (plan.logs.length > 0) {
188
- lines.push("", "## Progress");
189
- for (const log of plan.logs) {
190
- const date = new Date(log.created_at).toLocaleString();
191
- lines.push(` ${log.mind_name} (${date}): ${log.content}`);
192
- }
193
- }
194
- return { output: lines.join("\n") };
195
- }
196
- },
197
- history: {
198
- description: "List past plans",
199
- flags: {
200
- limit: { type: "number", description: "Max number of plans to show (default: 10)" }
201
- },
202
- handler: async ({ flags }, ctx) => {
203
- if (!ctx.db) return { error: "Plan extension requires a database" };
204
- const limit = flags.limit ?? 10;
205
- const plans = await listPlans(ctx.db, ctx.getUser, { limit });
206
- if (plans.length === 0) return { output: "No plans found." };
207
- const lines = plans.map((p) => {
208
- const date = new Date(p.created_at).toLocaleDateString();
209
- const status = p.status === "active" ? " [active]" : "";
210
- return ` ${p.title} (${date}, by ${p.set_by_username})${status}`;
211
- });
212
- return { output: lines.join("\n") };
213
- }
214
- },
215
- finish: {
216
- description: "Finish the current plan with a closing message",
217
- args: [{ name: "message", description: "Closing message (or pipe via stdin)" }],
218
- handler: async ({ args }, ctx) => {
219
- if (!ctx.db) return { error: "Plan extension requires a database" };
220
- const mindName = ctx.mindName;
221
- if (!mindName) return { error: "No mind specified (use --mind or VOLUTE_MIND)" };
222
- const plan = await getActivePlan(ctx.db, ctx.getUser);
223
- if (!plan) return { error: "No active plan" };
224
- const message = args.message ?? ctx.stdin ?? "";
225
- finishPlan(ctx.db, plan.id, message);
226
- ctx.publishActivity({
227
- type: "plan_finished",
228
- mind: mindName,
229
- summary: `${mindName} finished plan: "${plan.title}"`,
230
- metadata: { planId: plan.id, url: "/plan" }
231
- });
232
- const announcement = message ? `[Plan finished: ${plan.title}] ${message}` : `[Plan finished: ${plan.title}]`;
233
- const announced = await announceToSystem(announcement, ctx);
234
- const suffix = announced ? "" : " (system channel unavailable)";
235
- return { output: `Finished: ${plan.title}${suffix}` };
236
- }
237
- }
238
- };
239
- }
240
-
241
- // packages/extensions/plan/src/db.ts
242
- function initDb(db) {
243
- db.exec(`
244
- CREATE TABLE IF NOT EXISTS plans (
245
- id INTEGER PRIMARY KEY AUTOINCREMENT,
246
- title TEXT NOT NULL,
247
- description TEXT NOT NULL DEFAULT '',
248
- status TEXT NOT NULL DEFAULT 'active',
249
- set_by INTEGER NOT NULL,
250
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
251
- completed_at TEXT,
252
- finish_message TEXT
253
- );
254
- CREATE INDEX IF NOT EXISTS idx_plans_status ON plans(status);
255
- CREATE INDEX IF NOT EXISTS idx_plans_created_at ON plans(created_at);
256
-
257
- CREATE TABLE IF NOT EXISTS plan_logs (
258
- id INTEGER PRIMARY KEY AUTOINCREMENT,
259
- plan_id INTEGER NOT NULL REFERENCES plans(id) ON DELETE CASCADE,
260
- mind_name TEXT NOT NULL,
261
- content TEXT NOT NULL,
262
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
263
- );
264
- CREATE INDEX IF NOT EXISTS idx_plan_logs_plan_id ON plan_logs(plan_id);
265
-
266
- CREATE TABLE IF NOT EXISTS plan_messages (
267
- id INTEGER PRIMARY KEY AUTOINCREMENT,
268
- plan_id INTEGER NOT NULL REFERENCES plans(id) ON DELETE CASCADE,
269
- content TEXT NOT NULL,
270
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
271
- );
272
- CREATE INDEX IF NOT EXISTS idx_plan_messages_plan_id ON plan_messages(plan_id);
273
- `);
274
- }
275
-
276
- // packages/extensions/plan/src/routes.ts
277
- import { Hono } from "hono";
278
- function resolveUserId(c) {
279
- const user = c.get("user");
280
- if (!user || user.id === 0) return null;
281
- return user;
282
- }
283
- async function parseJson(c) {
284
- try {
285
- return await c.req.json();
286
- } catch {
287
- return null;
288
- }
289
- }
290
- function createRoutes(ctx) {
291
- if (!ctx.db) throw new Error("Plan extension requires a database");
292
- const db = ctx.db;
293
- const { getUser } = ctx;
294
- const app = new Hono().get("/current", async (c) => {
295
- const plan = await getActivePlan(db, getUser);
296
- if (!plan) return c.json(null);
297
- return c.json(plan);
298
- }).get("/", async (c) => {
299
- const status = c.req.query("status");
300
- const rawLimit = c.req.query("limit");
301
- const rawOffset = c.req.query("offset");
302
- const limit = rawLimit ? parseInt(rawLimit, 10) : void 0;
303
- const offset = rawOffset ? parseInt(rawOffset, 10) : void 0;
304
- if (limit !== void 0 && Number.isNaN(limit) || offset !== void 0 && Number.isNaN(offset)) {
305
- return c.json({ error: "Invalid limit or offset parameter" }, 400);
306
- }
307
- const plans = await listPlans(db, getUser, { status: status ?? void 0, limit, offset });
308
- return c.json(plans);
309
- }).post("/", async (c) => {
310
- const actor = resolveUserId(c);
311
- if (!actor) return c.json({ error: "Unauthorized" }, 401);
312
- if (actor.role !== "admin" && actor.user_type !== "mind") {
313
- return c.json({ error: "Only spirit or admin can start plans" }, 403);
314
- }
315
- const body = await parseJson(c);
316
- if (!body) return c.json({ error: "Invalid JSON body" }, 400);
317
- if (!body.title) return c.json({ error: "title is required" }, 400);
318
- const plan = await startPlan(db, getUser, actor.id, body.title, body.description ?? "");
319
- ctx.publishActivity({
320
- type: "plan_started",
321
- mind: actor.username,
322
- summary: `${actor.username} started plan: "${body.title}"`,
323
- metadata: { planId: plan.id, title: body.title, url: "/plan" }
324
- });
325
- return c.json(plan, 201);
326
- }).post("/:id{[0-9]+}/message", async (c) => {
327
- const actor = resolveUserId(c);
328
- if (!actor) return c.json({ error: "Unauthorized" }, 401);
329
- const planId = parseInt(c.req.param("id"), 10);
330
- if (Number.isNaN(planId)) return c.json({ error: "Invalid plan ID" }, 400);
331
- const plan = db.prepare("SELECT id FROM plans WHERE id = ? AND status = 'active'").get(planId);
332
- if (!plan) return c.json({ error: "Active plan not found" }, 404);
333
- const body = await parseJson(c);
334
- if (!body) return c.json({ error: "Invalid JSON body" }, 400);
335
- if (!body.content) return c.json({ error: "content is required" }, 400);
336
- const msg = addPlanMessage(db, planId, body.content);
337
- ctx.publishActivity({
338
- type: "plan_message",
339
- mind: actor.username,
340
- summary: `Plan message: "${body.content.slice(0, 100)}"`,
341
- metadata: { planId, messageId: msg.id, url: "/plan" }
342
- });
343
- return c.json(msg, 201);
344
- }).post("/:id{[0-9]+}/log", async (c) => {
345
- const actor = resolveUserId(c);
346
- if (!actor) return c.json({ error: "Unauthorized" }, 401);
347
- const planId = parseInt(c.req.param("id"), 10);
348
- if (Number.isNaN(planId)) return c.json({ error: "Invalid plan ID" }, 400);
349
- const plan = db.prepare("SELECT id FROM plans WHERE id = ? AND status = 'active'").get(planId);
350
- if (!plan) return c.json({ error: "Active plan not found" }, 404);
351
- const body = await parseJson(c);
352
- if (!body) return c.json({ error: "Invalid JSON body" }, 400);
353
- if (!body.content) return c.json({ error: "content is required" }, 400);
354
- const log = logProgress(db, planId, actor.username, body.content);
355
- ctx.publishActivity({
356
- type: "plan_progress",
357
- mind: actor.username,
358
- summary: `${actor.username} logged progress: "${body.content.slice(0, 100)}"`,
359
- metadata: { planId, logId: log.id, url: "/plan" }
360
- });
361
- return c.json(log, 201);
362
- }).patch("/:id{[0-9]+}/finish", async (c) => {
363
- const actor = resolveUserId(c);
364
- if (!actor) return c.json({ error: "Unauthorized" }, 401);
365
- if (actor.role !== "admin" && actor.user_type !== "mind") {
366
- return c.json({ error: "Only spirit or admin can finish plans" }, 403);
367
- }
368
- const planId = parseInt(c.req.param("id"), 10);
369
- if (Number.isNaN(planId)) return c.json({ error: "Invalid plan ID" }, 400);
370
- const body = await parseJson(c);
371
- const message = body?.message;
372
- const ok = finishPlan(db, planId, message);
373
- if (!ok) return c.json({ error: "Plan not found" }, 404);
374
- ctx.publishActivity({
375
- type: "plan_finished",
376
- mind: actor.username,
377
- summary: `${actor.username} finished a plan`,
378
- metadata: { planId, url: "/plan" }
379
- });
380
- return c.json({ ok: true });
381
- }).get("/feed", async (c) => {
382
- const rawLimit = c.req.query("limit");
383
- const limit = rawLimit ? parseInt(rawLimit, 10) : 5;
384
- if (Number.isNaN(limit)) return c.json({ error: "Invalid limit parameter" }, 400);
385
- const plans = await listPlans(db, getUser, { limit });
386
- return c.json(
387
- plans.map((p) => ({
388
- id: `plan-${p.id}`,
389
- title: p.title,
390
- url: `/plan`,
391
- date: p.created_at,
392
- author: p.set_by_username,
393
- bodyHtml: p.description || `<em>${p.status}</em>`,
394
- icon: '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="6"/><path d="M8 4v4l2.5 2.5"/></svg>',
395
- color: "blue"
396
- }))
397
- );
398
- });
399
- return app;
400
- }
401
-
402
- // packages/extensions/plan/src/index.ts
403
- var assetsDir = resolve(import.meta.dirname, "../dist/ui");
404
- var skillsDir = resolve(import.meta.dirname, "../skills");
405
- var src_default = createExtension({
406
- id: "plan",
407
- name: "Plan",
408
- version: "0.1.0",
409
- description: "System-wide plans for coordinated mind activity",
410
- mindDoc: "Shared plans for coordinating work with other minds \u2014 propose, track, and move a shared effort forward together.",
411
- icon: '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="6"/><path d="M8 4v4l2.5 2.5"/></svg>',
412
- color: "blue",
413
- routes: (ctx) => createRoutes(ctx),
414
- commands: createCommands(),
415
- initDb,
416
- skillsDir,
417
- standardSkill: true,
418
- ui: {
419
- assetsDir,
420
- systemSection: { id: "plan", label: "Plan", urlPatterns: ["/plan"] },
421
- feedSource: { endpoint: "/api/ext/plan/feed" }
422
- }
423
- });
424
- export {
425
- src_default as default
426
- };