@kivimedia/kmhub 2.9.0 → 2.10.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 (57) hide show
  1. package/README.md +170 -170
  2. package/bin/kmhub.mjs +896 -896
  3. package/coach-book-output-guard.mjs +760 -760
  4. package/index.mjs +57 -57
  5. package/package.json +56 -56
  6. package/prompts/briefing.md +29 -29
  7. package/prompts/luxury.md +70 -70
  8. package/prompts/play.md +49 -49
  9. package/prompts/run.md +36 -36
  10. package/prompts/setup.md +33 -33
  11. package/prompts/vs-booked.md +46 -46
  12. package/prompts/what-can-you-do.md +40 -40
  13. package/prompts.mjs +110 -109
  14. package/read-only-tools.json +143 -142
  15. package/remote.mjs +929 -929
  16. package/tools/balloon-costing.mjs +80 -80
  17. package/tools/booking-equipment.mjs +110 -110
  18. package/tools/bridges.mjs +54 -54
  19. package/tools/briefing.mjs +91 -91
  20. package/tools/calendar.mjs +170 -170
  21. package/tools/capabilities.mjs +155 -155
  22. package/tools/catalog.mjs +288 -288
  23. package/tools/clubs.mjs +176 -176
  24. package/tools/coach.mjs +771 -771
  25. package/tools/compare.mjs +76 -76
  26. package/tools/core.mjs +244 -244
  27. package/tools/crm.mjs +209 -209
  28. package/tools/dubsado.mjs +137 -137
  29. package/tools/exports.mjs +128 -128
  30. package/tools/fact-review.mjs +125 -125
  31. package/tools/flows.mjs +261 -261
  32. package/tools/forms.mjs +158 -158
  33. package/tools/gols.mjs +134 -134
  34. package/tools/hr.mjs +162 -162
  35. package/tools/knowledge.mjs +125 -125
  36. package/tools/marketing.mjs +396 -396
  37. package/tools/meta.mjs +245 -245
  38. package/tools/military.mjs +244 -244
  39. package/tools/money.mjs +235 -197
  40. package/tools/outreach.mjs +238 -238
  41. package/tools/pending.mjs +122 -122
  42. package/tools/photos.mjs +140 -140
  43. package/tools/plays.mjs +244 -244
  44. package/tools/profile.mjs +118 -118
  45. package/tools/radar.mjs +173 -173
  46. package/tools/recurring-invoices.mjs +149 -149
  47. package/tools/reengage.mjs +434 -434
  48. package/tools/schedules.mjs +55 -55
  49. package/tools/setup.mjs +168 -168
  50. package/tools/sops-bridges.mjs +86 -86
  51. package/tools/sops.mjs +314 -314
  52. package/tools/sourcing.mjs +268 -268
  53. package/tools/strategy.mjs +146 -146
  54. package/tools/studio.mjs +132 -132
  55. package/tools/venueradar.mjs +151 -151
  56. package/tools/voice.mjs +134 -134
  57. package/tools.mjs +407 -407
@@ -1,149 +1,149 @@
1
- /**
2
- * Tool family: recurring-invoices - subscription billing from the terminal.
3
- *
4
- * WHY THIS FAMILY EXISTS. On 22-Sep-2026 Jackie (Dazzling Balloons) set out to
5
- * launch a monthly decor subscription - billed monthly in advance on the 1st,
6
- * three tiers, a three-month minimum. Her terminal could read every invoice she
7
- * had ever raised and could not set up a single repeating one: the
8
- * recurring_invoices table was writable only from the "Make recurring" panel on
9
- * an existing invoice in the web app. A recurring plan is one bill with a
10
- * schedule inside it, and the schedule was mouse-only.
11
- *
12
- * 🚨 auto_send DEFAULTS FALSE, on purpose. Off, each month's invoice is created
13
- * as a DRAFT and nobody is emailed - the owner looks at it and sends it. On, the
14
- * cron marks it sent and emails the client the pay link unattended, every month,
15
- * forever. That second one is a standing instruction to charge somebody, so a
16
- * terminal never picks it by omission: the person has to ask, and the
17
- * confirmation sentence says in plain words which one they are getting.
18
- *
19
- * Every write here moves money, so every write is confirm-gated.
20
- *
21
- * The family contract this file follows is documented in ./README.md.
22
- */
23
- import { z } from 'zod';
24
-
25
- export const FAMILY = 'recurring-invoices';
26
-
27
- export const TOOLS = [
28
- 'km_list_recurring_invoices', 'km_create_recurring_invoice', 'km_update_recurring_invoice',
29
- ];
30
-
31
- // Lands in `full` only. An EMPTY list means exactly that - ['*'] would opt into
32
- // every profile, which is the mistake tools.mjs documents six families making.
33
- export const PROFILES = [];
34
-
35
- /** Route-level 404 (carries a routes list) vs a handler's not_found. See forms.mjs. */
36
- function routeMissing(r) {
37
- if (r.status === 405 || r.status === 501) return true;
38
- return r.status === 404 && Array.isArray(r.data?.routes);
39
- }
40
-
41
- const NOT_SUPPORTED =
42
- 'This KM Hub is running a version that cannot set up repeating invoices from the terminal yet. The workspace is ' +
43
- 'fine and every other tool works as normal. Recurring invoices are set up in the web app at ' +
44
- 'https://hub.kivimedia.co: open an invoice, then "Make recurring".';
45
-
46
- const CONFIRM_DESC =
47
- 'Leave this out on the first call. This action commits to billing somebody money on a schedule, so it needs an '
48
- + 'explicit yes from the person you are working with: KM Hub answers 409 with a plain sentence saying who gets '
49
- + 'billed how much and how often, plus a token. Show them that sentence in those words, wait for a real answer, '
50
- + 'and only then call again with the token and the SAME arguments. A yes for one plan never authorises a different '
51
- + 'one.';
52
-
53
- const LINE_ITEM = z.object({
54
- description: z.string().min(1).max(500).describe('What the line says on the invoice, e.g. "Dazzling Decor Club - Gold tier, monthly".'),
55
- unit_price_cents: z.number().int().min(0).describe('Price per unit in CENTS. 45000 is $450.00.'),
56
- quantity: z.number().gt(0).max(9999).optional().describe('How many. Leave it out for 1; decimals like 1.5 are fine.'),
57
- });
58
-
59
- export function register(server, call, { out, text }) {
60
- server.tool(
61
- 'km_list_recurring_invoices',
62
- 'The repeating invoices this workspace bills: what each one charges, to whom, when it next runs, whether it ' +
63
- 'is paused, and whether it emails itself or waits as a draft for approval. Each row carries the computed ' +
64
- 'monthly total in real money, plus a plain_english line, and the response totals up the active monthly ' +
65
- 'recurring revenue. ' +
66
- 'THE MODEL, so you never have to guess: a recurring invoice is a TEMPLATE, not an invoice. A daily cron ' +
67
- 'creates the next real invoice when next_run_date arrives and moves the date on a month. So nothing here ' +
68
- 'appears in km_list_invoices until it has actually run at least once, and pausing one (active false) never ' +
69
- 'touches invoices it has already raised - those are still owed. Read only.',
70
- {
71
- active: z.boolean().optional().describe('True for only the live plans, false for only the paused ones. Leave it out for all of them.'),
72
- },
73
- async ({ active }) => {
74
- const q = active === undefined ? '' : `?active=${active ? 'true' : 'false'}`;
75
- const r = await call('GET', `/recurring-invoices${q}`);
76
- if (routeMissing(r)) return text(NOT_SUPPORTED, true);
77
- return out(r);
78
- },
79
- );
80
-
81
- server.tool(
82
- 'km_create_recurring_invoice',
83
- 'Set up a repeating invoice: a subscription, a retainer, a payment plan - anything billed on the same day ' +
84
- 'every month. This is how a monthly membership actually gets charged; the booking catalog only sells it. ' +
85
- 'Prices are CENTS: unit_price_cents 45000 is $450. cadence is monthly and only monthly (the table refuses ' +
86
- 'anything else, so weekly and annual plans cannot be stored yet). next_run_date is the day the FIRST ' +
87
- 'invoice is raised AND the day of the month every later one repeats on, so 2026-10-01 means the 1st. ' +
88
- 'due_in_days 0 means due the day it is raised, which is what "billed in advance" looks like. ' +
89
- '🚨 auto_send defaults to FALSE and you should usually leave it alone: each month the invoice appears as a ' +
90
- 'DRAFT and the owner approves and sends it. Pass auto_send true ONLY if the owner has explicitly said they ' +
91
- 'want the client emailed and charged automatically with nobody looking first - and say out loud that it ' +
92
- 'repeats unattended every month until paused. It also needs a client with an email address on file. ' +
93
- 'This is a confirmed write; expect a 409 spelling out who is billed how much and how often.',
94
- {
95
- title: z.string().min(1).max(200).describe('Name the plan, e.g. "Dazzling Decor Club - Gold". The owner reads this in the list, and the generated invoices quote it in their notes.'),
96
- line_items: z.array(LINE_ITEM).min(1).max(50).describe('What is billed each month. At least one line: a template with none generates nothing at all.'),
97
- client_id: z.string().optional().describe('UUID from km_list_clients: who gets billed. Leave it out only for a plan not yet tied to a client (and then auto_send cannot be used).'),
98
- cadence: z.enum(['monthly']).optional().describe("Leave it out. 'monthly' is the only value the table accepts today."),
99
- next_run_date: z.string().describe('yyyy-mm-dd, e.g. 2026-10-01. The first billing date, and the day of the month it repeats on. The 31st clamps to the 28th in February.'),
100
- due_in_days: z.number().int().min(0).max(365).optional().describe('Days from issue to due date. Leave it out for 14; use 0 for billed-in-advance, due immediately.'),
101
- discount_cents: z.number().int().min(0).optional().describe('A flat discount off each month, in cents. Cannot exceed what the line items add up to.'),
102
- tax_rate: z.number().min(0).max(100).optional().describe('Tax as a PERCENTAGE: 7.5 means 7.5%, not 0.075.'),
103
- currency: z.string().length(3).optional().describe('3-letter code like USD. Leave it out to use the workspace currency.'),
104
- auto_send: z.boolean().optional().describe('Leave it out for false: the invoice is a draft each month and the owner sends it. True emails the client the pay link automatically, unattended, every month, with nobody checking it first.'),
105
- booking_id: z.string().optional().describe('UUID of a booking every generated invoice should hang off, if there is one.'),
106
- source_invoice_id: z.string().optional().describe('UUID of the invoice this plan was modelled on, for provenance. Optional.'),
107
- confirm_token: z.string().optional().describe(CONFIRM_DESC),
108
- },
109
- async (body) => {
110
- const r = await call('POST', '/recurring-invoices', body);
111
- if (routeMissing(r)) return text(NOT_SUPPORTED, true);
112
- return out(r);
113
- },
114
- );
115
-
116
- server.tool(
117
- 'km_update_recurring_invoice',
118
- 'Change a repeating invoice: PAUSE it (active false - no invoice is generated at all until resumed), resume ' +
119
- 'it, change what it bills each month (line_items, discount_cents, tax_rate), move the next billing date, ' +
120
- 'change the payment terms, or switch auto-send on or off. ' +
121
- 'Run km_list_recurring_invoices first and work from real ids. Send only the fields you are changing - but ' +
122
- 'line_items REPLACES the whole list, so send every line, not just the changed one. ' +
123
- 'Everything here affects FUTURE invoices only: invoices already raised keep their amounts and are still ' +
124
- 'owed, so ending a subscription means pausing the plan, not undoing the bills. ' +
125
- '🚨 Moving next_run_date EARLIER can bill a month again - the generator bills on whatever date it finds, ' +
126
- 'and it has no memory of which months it already covered. The confirmation sentence and the response both ' +
127
- 'say so when that is what you are doing. There is no way to change who a plan bills: create a new plan and ' +
128
- 'pause this one, so a template that has billed somebody cannot quietly start billing somebody else. ' +
129
- 'This is a confirmed write; expect a 409 naming the plan and the change first.',
130
- {
131
- recurring_invoice_id: z.string().describe('UUID of the plan, from km_list_recurring_invoices.'),
132
- active: z.boolean().optional().describe('false pauses it: nothing is generated until it is true again. true resumes it.'),
133
- title: z.string().min(1).max(200).optional(),
134
- line_items: z.array(LINE_ITEM).min(1).max(50).optional().describe('The COMPLETE list of what is billed each month. It replaces what is there.'),
135
- discount_cents: z.number().int().min(0).optional().describe('Flat discount off each month, in cents.'),
136
- tax_rate: z.number().min(0).max(100).optional().describe('Tax as a PERCENTAGE: 7.5 means 7.5%.'),
137
- currency: z.string().length(3).nullable().optional().describe('3-letter code, or null to fall back to the workspace currency.'),
138
- next_run_date: z.string().optional().describe('yyyy-mm-dd. The next billing date, and from then on the day of the month it repeats on. Moving it LATER skips ahead; moving it EARLIER can re-bill a month.'),
139
- due_in_days: z.number().int().min(0).max(365).optional().describe('Days from issue to due date on each generated invoice.'),
140
- auto_send: z.boolean().optional().describe('True starts emailing the client the pay link automatically each month with nobody checking first; false goes back to a draft the owner approves. Only ask for true if the owner explicitly wants it.'),
141
- confirm_token: z.string().optional().describe(CONFIRM_DESC),
142
- },
143
- async ({ recurring_invoice_id, ...changes }) => {
144
- const r = await call('PATCH', `/recurring-invoices/${encodeURIComponent(String(recurring_invoice_id || '').trim())}`, changes);
145
- if (routeMissing(r)) return text(NOT_SUPPORTED, true);
146
- return out(r);
147
- },
148
- );
149
- }
1
+ /**
2
+ * Tool family: recurring-invoices - subscription billing from the terminal.
3
+ *
4
+ * WHY THIS FAMILY EXISTS. On 22-Sep-2026 Jackie (Dazzling Balloons) set out to
5
+ * launch a monthly decor subscription - billed monthly in advance on the 1st,
6
+ * three tiers, a three-month minimum. Her terminal could read every invoice she
7
+ * had ever raised and could not set up a single repeating one: the
8
+ * recurring_invoices table was writable only from the "Make recurring" panel on
9
+ * an existing invoice in the web app. A recurring plan is one bill with a
10
+ * schedule inside it, and the schedule was mouse-only.
11
+ *
12
+ * 🚨 auto_send DEFAULTS FALSE, on purpose. Off, each month's invoice is created
13
+ * as a DRAFT and nobody is emailed - the owner looks at it and sends it. On, the
14
+ * cron marks it sent and emails the client the pay link unattended, every month,
15
+ * forever. That second one is a standing instruction to charge somebody, so a
16
+ * terminal never picks it by omission: the person has to ask, and the
17
+ * confirmation sentence says in plain words which one they are getting.
18
+ *
19
+ * Every write here moves money, so every write is confirm-gated.
20
+ *
21
+ * The family contract this file follows is documented in ./README.md.
22
+ */
23
+ import { z } from 'zod';
24
+
25
+ export const FAMILY = 'recurring-invoices';
26
+
27
+ export const TOOLS = [
28
+ 'km_list_recurring_invoices', 'km_create_recurring_invoice', 'km_update_recurring_invoice',
29
+ ];
30
+
31
+ // Lands in `full` only. An EMPTY list means exactly that - ['*'] would opt into
32
+ // every profile, which is the mistake tools.mjs documents six families making.
33
+ export const PROFILES = [];
34
+
35
+ /** Route-level 404 (carries a routes list) vs a handler's not_found. See forms.mjs. */
36
+ function routeMissing(r) {
37
+ if (r.status === 405 || r.status === 501) return true;
38
+ return r.status === 404 && Array.isArray(r.data?.routes);
39
+ }
40
+
41
+ const NOT_SUPPORTED =
42
+ 'This KM Hub is running a version that cannot set up repeating invoices from the terminal yet. The workspace is ' +
43
+ 'fine and every other tool works as normal. Recurring invoices are set up in the web app at ' +
44
+ 'https://hub.kivimedia.co: open an invoice, then "Make recurring".';
45
+
46
+ const CONFIRM_DESC =
47
+ 'Leave this out on the first call. This action commits to billing somebody money on a schedule, so it needs an '
48
+ + 'explicit yes from the person you are working with: KM Hub answers 409 with a plain sentence saying who gets '
49
+ + 'billed how much and how often, plus a token. Show them that sentence in those words, wait for a real answer, '
50
+ + 'and only then call again with the token and the SAME arguments. A yes for one plan never authorises a different '
51
+ + 'one.';
52
+
53
+ const LINE_ITEM = z.object({
54
+ description: z.string().min(1).max(500).describe('What the line says on the invoice, e.g. "Dazzling Decor Club - Gold tier, monthly".'),
55
+ unit_price_cents: z.number().int().min(0).describe('Price per unit in CENTS. 45000 is $450.00.'),
56
+ quantity: z.number().gt(0).max(9999).optional().describe('How many. Leave it out for 1; decimals like 1.5 are fine.'),
57
+ });
58
+
59
+ export function register(server, call, { out, text }) {
60
+ server.tool(
61
+ 'km_list_recurring_invoices',
62
+ 'The repeating invoices this workspace bills: what each one charges, to whom, when it next runs, whether it ' +
63
+ 'is paused, and whether it emails itself or waits as a draft for approval. Each row carries the computed ' +
64
+ 'monthly total in real money, plus a plain_english line, and the response totals up the active monthly ' +
65
+ 'recurring revenue. ' +
66
+ 'THE MODEL, so you never have to guess: a recurring invoice is a TEMPLATE, not an invoice. A daily cron ' +
67
+ 'creates the next real invoice when next_run_date arrives and moves the date on a month. So nothing here ' +
68
+ 'appears in km_list_invoices until it has actually run at least once, and pausing one (active false) never ' +
69
+ 'touches invoices it has already raised - those are still owed. Read only.',
70
+ {
71
+ active: z.boolean().optional().describe('True for only the live plans, false for only the paused ones. Leave it out for all of them.'),
72
+ },
73
+ async ({ active }) => {
74
+ const q = active === undefined ? '' : `?active=${active ? 'true' : 'false'}`;
75
+ const r = await call('GET', `/recurring-invoices${q}`);
76
+ if (routeMissing(r)) return text(NOT_SUPPORTED, true);
77
+ return out(r);
78
+ },
79
+ );
80
+
81
+ server.tool(
82
+ 'km_create_recurring_invoice',
83
+ 'Set up a repeating invoice: a subscription, a retainer, a payment plan - anything billed on the same day ' +
84
+ 'every month. This is how a monthly membership actually gets charged; the booking catalog only sells it. ' +
85
+ 'Prices are CENTS: unit_price_cents 45000 is $450. cadence is monthly and only monthly (the table refuses ' +
86
+ 'anything else, so weekly and annual plans cannot be stored yet). next_run_date is the day the FIRST ' +
87
+ 'invoice is raised AND the day of the month every later one repeats on, so 2026-10-01 means the 1st. ' +
88
+ 'due_in_days 0 means due the day it is raised, which is what "billed in advance" looks like. ' +
89
+ '🚨 auto_send defaults to FALSE and you should usually leave it alone: each month the invoice appears as a ' +
90
+ 'DRAFT and the owner approves and sends it. Pass auto_send true ONLY if the owner has explicitly said they ' +
91
+ 'want the client emailed and charged automatically with nobody looking first - and say out loud that it ' +
92
+ 'repeats unattended every month until paused. It also needs a client with an email address on file. ' +
93
+ 'This is a confirmed write; expect a 409 spelling out who is billed how much and how often.',
94
+ {
95
+ title: z.string().min(1).max(200).describe('Name the plan, e.g. "Dazzling Decor Club - Gold". The owner reads this in the list, and the generated invoices quote it in their notes.'),
96
+ line_items: z.array(LINE_ITEM).min(1).max(50).describe('What is billed each month. At least one line: a template with none generates nothing at all.'),
97
+ client_id: z.string().optional().describe('UUID from km_list_clients: who gets billed. Leave it out only for a plan not yet tied to a client (and then auto_send cannot be used).'),
98
+ cadence: z.enum(['monthly']).optional().describe("Leave it out. 'monthly' is the only value the table accepts today."),
99
+ next_run_date: z.string().describe('yyyy-mm-dd, e.g. 2026-10-01. The first billing date, and the day of the month it repeats on. The 31st clamps to the 28th in February.'),
100
+ due_in_days: z.number().int().min(0).max(365).optional().describe('Days from issue to due date. Leave it out for 14; use 0 for billed-in-advance, due immediately.'),
101
+ discount_cents: z.number().int().min(0).optional().describe('A flat discount off each month, in cents. Cannot exceed what the line items add up to.'),
102
+ tax_rate: z.number().min(0).max(100).optional().describe('Tax as a PERCENTAGE: 7.5 means 7.5%, not 0.075.'),
103
+ currency: z.string().length(3).optional().describe('3-letter code like USD. Leave it out to use the workspace currency.'),
104
+ auto_send: z.boolean().optional().describe('Leave it out for false: the invoice is a draft each month and the owner sends it. True emails the client the pay link automatically, unattended, every month, with nobody checking it first.'),
105
+ booking_id: z.string().optional().describe('UUID of a booking every generated invoice should hang off, if there is one.'),
106
+ source_invoice_id: z.string().optional().describe('UUID of the invoice this plan was modelled on, for provenance. Optional.'),
107
+ confirm_token: z.string().optional().describe(CONFIRM_DESC),
108
+ },
109
+ async (body) => {
110
+ const r = await call('POST', '/recurring-invoices', body);
111
+ if (routeMissing(r)) return text(NOT_SUPPORTED, true);
112
+ return out(r);
113
+ },
114
+ );
115
+
116
+ server.tool(
117
+ 'km_update_recurring_invoice',
118
+ 'Change a repeating invoice: PAUSE it (active false - no invoice is generated at all until resumed), resume ' +
119
+ 'it, change what it bills each month (line_items, discount_cents, tax_rate), move the next billing date, ' +
120
+ 'change the payment terms, or switch auto-send on or off. ' +
121
+ 'Run km_list_recurring_invoices first and work from real ids. Send only the fields you are changing - but ' +
122
+ 'line_items REPLACES the whole list, so send every line, not just the changed one. ' +
123
+ 'Everything here affects FUTURE invoices only: invoices already raised keep their amounts and are still ' +
124
+ 'owed, so ending a subscription means pausing the plan, not undoing the bills. ' +
125
+ '🚨 Moving next_run_date EARLIER can bill a month again - the generator bills on whatever date it finds, ' +
126
+ 'and it has no memory of which months it already covered. The confirmation sentence and the response both ' +
127
+ 'say so when that is what you are doing. There is no way to change who a plan bills: create a new plan and ' +
128
+ 'pause this one, so a template that has billed somebody cannot quietly start billing somebody else. ' +
129
+ 'This is a confirmed write; expect a 409 naming the plan and the change first.',
130
+ {
131
+ recurring_invoice_id: z.string().describe('UUID of the plan, from km_list_recurring_invoices.'),
132
+ active: z.boolean().optional().describe('false pauses it: nothing is generated until it is true again. true resumes it.'),
133
+ title: z.string().min(1).max(200).optional(),
134
+ line_items: z.array(LINE_ITEM).min(1).max(50).optional().describe('The COMPLETE list of what is billed each month. It replaces what is there.'),
135
+ discount_cents: z.number().int().min(0).optional().describe('Flat discount off each month, in cents.'),
136
+ tax_rate: z.number().min(0).max(100).optional().describe('Tax as a PERCENTAGE: 7.5 means 7.5%.'),
137
+ currency: z.string().length(3).nullable().optional().describe('3-letter code, or null to fall back to the workspace currency.'),
138
+ next_run_date: z.string().optional().describe('yyyy-mm-dd. The next billing date, and from then on the day of the month it repeats on. Moving it LATER skips ahead; moving it EARLIER can re-bill a month.'),
139
+ due_in_days: z.number().int().min(0).max(365).optional().describe('Days from issue to due date on each generated invoice.'),
140
+ auto_send: z.boolean().optional().describe('True starts emailing the client the pay link automatically each month with nobody checking first; false goes back to a draft the owner approves. Only ask for true if the owner explicitly wants it.'),
141
+ confirm_token: z.string().optional().describe(CONFIRM_DESC),
142
+ },
143
+ async ({ recurring_invoice_id, ...changes }) => {
144
+ const r = await call('PATCH', `/recurring-invoices/${encodeURIComponent(String(recurring_invoice_id || '').trim())}`, changes);
145
+ if (routeMissing(r)) return text(NOT_SUPPORTED, true);
146
+ return out(r);
147
+ },
148
+ );
149
+ }