@kivimedia/kmhub 2.9.1 → 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 -110
  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,155 +1,155 @@
1
- /**
2
- * Tool family: capabilities - what KM Hub can do, and honestly where.
3
- *
4
- * km_capabilities answers "what is KM Hub" from ./capabilities.json, which is
5
- * GENERATED from the web app's own nav registry (scripts/generate-capabilities.mjs).
6
- * That generation is the whole point: a hand-written feature list is wrong within a
7
- * month, and a connector that confidently describes a product that has moved is
8
- * worse than one that says nothing.
9
- *
10
- * WHY THIS READS A LOCAL FILE AND NOT THE API. The answer is the same for every
11
- * workspace on a given build, it is needed at the START of a session when the model
12
- * is deciding what is even possible, and it must work when the API is slow. A round
13
- * trip would buy nothing. Per-workspace filtering (which pillars this org has switched
14
- * on) is a later addition and belongs on the API when it comes.
15
- *
16
- * 🚨 THE `reach` FIELD IS THE HONEST PART. Every page reports `terminal`,
17
- * `terminal_read` or `web_only`. Claude MUST NOT promise to do a web_only thing
18
- * from the terminal, and MUST NOT promise to EDIT a terminal_read page: those can
19
- * be looked at from here and changed only in the web app. Say KM Hub does it, name
20
- * the page, and point at the web app. Telling a client "yes I can" and then
21
- * failing is worse than "KM Hub does that, here is where". The read distinction
22
- * exists because Mark Fuller's 01-Sep-26 gap report caught two read-only pages
23
- * reporting plain `terminal`, which reads as "you can work on this from here".
24
- *
25
- * The family contract this file follows is documented in ./README.md.
26
- */
27
- import { readFileSync } from 'node:fs';
28
- import { z } from 'zod';
29
-
30
- export const FAMILY = 'capabilities';
31
-
32
- export const TOOLS = ['km_capabilities'];
33
-
34
- // Knowing what the product is costs one small file and changes every other answer,
35
- // so it loads in every profile.
36
- export const PROFILES = ['*'];
37
-
38
- let CACHE = null;
39
-
40
- function load() {
41
- if (CACHE) return CACHE;
42
- try {
43
- CACHE = JSON.parse(readFileSync(new URL('../capabilities.json', import.meta.url), 'utf8'));
44
- } catch (e) {
45
- CACHE = { error: String(e?.message || e) };
46
- }
47
- return CACHE;
48
- }
49
-
50
- /** Trim a page down to what a model needs to decide, dropping the empty fields. */
51
- function slim(p, withTools) {
52
- const o = { id: p.id, label: p.label, reach: p.reach };
53
- // A page can be web-only because nobody built it yet, or because it should never
54
- // be driven from a terminal at all. Saying which is the difference between "not
55
- // yet" and "not ever", and a model that blurs them will keep offering to try.
56
- if (p.web_only_by_design) o.web_only_by_design = true;
57
- if (p.benefit) o.what_it_does = p.benefit;
58
- if (withTools && p.tools?.length) o.tools = p.tools;
59
- if (withTools && p.plays?.length) o.plays = p.plays;
60
- return o;
61
- }
62
-
63
- export function register(server, call, { text }) {
64
- server.tool(
65
- 'km_capabilities',
66
- 'The map of everything KM Hub does, and honestly which parts you can do from here. ' +
67
- 'CALL THIS EARLY in any session where the user is deciding what to work on, asks what KM Hub can do, ' +
68
- 'asks whether it can handle some area of their business, wonders if it does something a different tool does, ' +
69
- 'or asks a question you are about to answer with "I do not think KM Hub does that". You very likely have not ' +
70
- 'seen the whole product: it is 7 pillars and over 100 feature pages, far more than the tools in front of you, ' +
71
- 'so an answer based only on your tool list will understate it badly. ' +
72
- 'What comes back is the same nav the web app renders - Marketing, Sales, Operations, HR, Clients, AI Team, ' +
73
- 'Settings - each with its groups, its pages, and one plain sentence per page about what that page is FOR. ' +
74
- '🚨 Every page carries `reach`. `terminal` means tools or plays here can do it. `terminal_read` means you can ' +
75
- 'look from here but every change happens in the web app, so say where the editing lives up front. `web_only` ' +
76
- 'means KM Hub does it but this connector cannot reach it at all. NEVER offer to do a web_only thing or to ' +
77
- 'edit a terminal_read one. Say KM Hub does it, name the page, and ' +
78
- 'point them at https://hub.kivimedia.co. Claiming a capability you do not have and then failing costs more ' +
79
- 'trust than saying where it lives. ' +
80
- 'Use it to answer the whole question rather than the part you happen to hold: someone asking about follow-up ' +
81
- 'should hear that KM Hub also runs their newsletter, their SEO and their reviews. It reads a file, costs ' +
82
- 'nothing, sends nothing and changes nothing.',
83
- {
84
- pillar: z
85
- .enum(['marketing', 'sales', 'operations', 'hr', 'support', 'strategy', 'setup'])
86
- .optional()
87
- .describe(
88
- 'Narrow to one pillar when the user asked about one area. marketing = being found and reaching out, ' +
89
- 'sales = pipeline through to close, operations = diary, money and delivery, hr = team, pay and time, ' +
90
- 'support = clients, conversations and reputation, strategy = the AI officers, decisions and reporting, ' +
91
- 'setup = account, billing and connections. Leave it out for the whole product.',
92
- ),
93
- reach: z
94
- .enum(['terminal', 'terminal_read', 'web_only', 'all'])
95
- .optional()
96
- .describe(
97
- "Default 'all'. Use 'terminal' when you want what you can reach from here; it includes 'terminal_read' " +
98
- "pages, so check each page's own reach before offering to CHANGE anything on it. Use 'terminal_read' " +
99
- "for only the look-but-not-touch pages, and 'web_only' to answer \"what else is in there\" honestly.",
100
- ),
101
- detail: z
102
- .enum(['summary', 'full'])
103
- .optional()
104
- .describe(
105
- "Default 'summary': pillars, groups, pages and what each is for. 'full' adds the exact tool and play " +
106
- 'names behind each page, which you want when planning a piece of work rather than describing the product.',
107
- ),
108
- },
109
- async ({ pillar, reach = 'all', detail = 'summary' }) => {
110
- const doc = load();
111
- if (doc.error) {
112
- return text(
113
- 'The capability map did not load on this connector, so I cannot list what KM Hub does. ' +
114
- 'The workspace itself is fine and every other tool works as normal. Detail: ' + doc.error,
115
- true,
116
- );
117
- }
118
-
119
- const withTools = detail === 'full';
120
- const pillars = [];
121
- for (const p of doc.pillars) {
122
- if (pillar && p.id !== pillar) continue;
123
- const groups = [];
124
- for (const g of p.groups) {
125
- const pages = g.pages
126
- // 'terminal' includes 'terminal_read': both are reachable, and a
127
- // caller narrowing to what it can reach must not lose the read side.
128
- .filter((x) => reach === 'all' || x.reach === reach || (reach === 'terminal' && x.reach === 'terminal_read'))
129
- .map((x) => slim(x, withTools));
130
- if (pages.length) groups.push({ group: g.header, pages });
131
- }
132
- if (groups.length) pillars.push({ id: p.id, label: p.label, tagline: p.tagline, groups });
133
- }
134
-
135
- const shown = pillars.reduce((n, p) => n + p.groups.reduce((m, g) => m + g.pages.length, 0), 0);
136
-
137
- const body = {
138
- product: 'KM Hub',
139
- web_app: 'https://hub.kivimedia.co',
140
- totals: doc.counts,
141
- showing: { pillar: pillar || 'all', reach, pages: shown },
142
- how_to_read:
143
- "reach 'terminal' means you can do it from here. reach 'terminal_read' means you can LOOK at it from " +
144
- 'here but every change happens in the web app: say where the editing lives up front, never offer the ' +
145
- "edit and fail at it. reach 'web_only' means KM Hub does it but this connector cannot yet, so name the " +
146
- 'page and point at the web app rather than offering to do it. ' +
147
- "A page also carrying web_only_by_design will NEVER be reachable from a terminal: it is a deliberate " +
148
- 'boundary, not a backlog item, so do not imply it is coming.',
149
- pillars,
150
- };
151
-
152
- return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }] };
153
- },
154
- );
155
- }
1
+ /**
2
+ * Tool family: capabilities - what KM Hub can do, and honestly where.
3
+ *
4
+ * km_capabilities answers "what is KM Hub" from ./capabilities.json, which is
5
+ * GENERATED from the web app's own nav registry (scripts/generate-capabilities.mjs).
6
+ * That generation is the whole point: a hand-written feature list is wrong within a
7
+ * month, and a connector that confidently describes a product that has moved is
8
+ * worse than one that says nothing.
9
+ *
10
+ * WHY THIS READS A LOCAL FILE AND NOT THE API. The answer is the same for every
11
+ * workspace on a given build, it is needed at the START of a session when the model
12
+ * is deciding what is even possible, and it must work when the API is slow. A round
13
+ * trip would buy nothing. Per-workspace filtering (which pillars this org has switched
14
+ * on) is a later addition and belongs on the API when it comes.
15
+ *
16
+ * 🚨 THE `reach` FIELD IS THE HONEST PART. Every page reports `terminal`,
17
+ * `terminal_read` or `web_only`. Claude MUST NOT promise to do a web_only thing
18
+ * from the terminal, and MUST NOT promise to EDIT a terminal_read page: those can
19
+ * be looked at from here and changed only in the web app. Say KM Hub does it, name
20
+ * the page, and point at the web app. Telling a client "yes I can" and then
21
+ * failing is worse than "KM Hub does that, here is where". The read distinction
22
+ * exists because Mark Fuller's 01-Sep-26 gap report caught two read-only pages
23
+ * reporting plain `terminal`, which reads as "you can work on this from here".
24
+ *
25
+ * The family contract this file follows is documented in ./README.md.
26
+ */
27
+ import { readFileSync } from 'node:fs';
28
+ import { z } from 'zod';
29
+
30
+ export const FAMILY = 'capabilities';
31
+
32
+ export const TOOLS = ['km_capabilities'];
33
+
34
+ // Knowing what the product is costs one small file and changes every other answer,
35
+ // so it loads in every profile.
36
+ export const PROFILES = ['*'];
37
+
38
+ let CACHE = null;
39
+
40
+ function load() {
41
+ if (CACHE) return CACHE;
42
+ try {
43
+ CACHE = JSON.parse(readFileSync(new URL('../capabilities.json', import.meta.url), 'utf8'));
44
+ } catch (e) {
45
+ CACHE = { error: String(e?.message || e) };
46
+ }
47
+ return CACHE;
48
+ }
49
+
50
+ /** Trim a page down to what a model needs to decide, dropping the empty fields. */
51
+ function slim(p, withTools) {
52
+ const o = { id: p.id, label: p.label, reach: p.reach };
53
+ // A page can be web-only because nobody built it yet, or because it should never
54
+ // be driven from a terminal at all. Saying which is the difference between "not
55
+ // yet" and "not ever", and a model that blurs them will keep offering to try.
56
+ if (p.web_only_by_design) o.web_only_by_design = true;
57
+ if (p.benefit) o.what_it_does = p.benefit;
58
+ if (withTools && p.tools?.length) o.tools = p.tools;
59
+ if (withTools && p.plays?.length) o.plays = p.plays;
60
+ return o;
61
+ }
62
+
63
+ export function register(server, call, { text }) {
64
+ server.tool(
65
+ 'km_capabilities',
66
+ 'The map of everything KM Hub does, and honestly which parts you can do from here. ' +
67
+ 'CALL THIS EARLY in any session where the user is deciding what to work on, asks what KM Hub can do, ' +
68
+ 'asks whether it can handle some area of their business, wonders if it does something a different tool does, ' +
69
+ 'or asks a question you are about to answer with "I do not think KM Hub does that". You very likely have not ' +
70
+ 'seen the whole product: it is 7 pillars and over 100 feature pages, far more than the tools in front of you, ' +
71
+ 'so an answer based only on your tool list will understate it badly. ' +
72
+ 'What comes back is the same nav the web app renders - Marketing, Sales, Operations, HR, Clients, AI Team, ' +
73
+ 'Settings - each with its groups, its pages, and one plain sentence per page about what that page is FOR. ' +
74
+ '🚨 Every page carries `reach`. `terminal` means tools or plays here can do it. `terminal_read` means you can ' +
75
+ 'look from here but every change happens in the web app, so say where the editing lives up front. `web_only` ' +
76
+ 'means KM Hub does it but this connector cannot reach it at all. NEVER offer to do a web_only thing or to ' +
77
+ 'edit a terminal_read one. Say KM Hub does it, name the page, and ' +
78
+ 'point them at https://hub.kivimedia.co. Claiming a capability you do not have and then failing costs more ' +
79
+ 'trust than saying where it lives. ' +
80
+ 'Use it to answer the whole question rather than the part you happen to hold: someone asking about follow-up ' +
81
+ 'should hear that KM Hub also runs their newsletter, their SEO and their reviews. It reads a file, costs ' +
82
+ 'nothing, sends nothing and changes nothing.',
83
+ {
84
+ pillar: z
85
+ .enum(['marketing', 'sales', 'operations', 'hr', 'support', 'strategy', 'setup'])
86
+ .optional()
87
+ .describe(
88
+ 'Narrow to one pillar when the user asked about one area. marketing = being found and reaching out, ' +
89
+ 'sales = pipeline through to close, operations = diary, money and delivery, hr = team, pay and time, ' +
90
+ 'support = clients, conversations and reputation, strategy = the AI officers, decisions and reporting, ' +
91
+ 'setup = account, billing and connections. Leave it out for the whole product.',
92
+ ),
93
+ reach: z
94
+ .enum(['terminal', 'terminal_read', 'web_only', 'all'])
95
+ .optional()
96
+ .describe(
97
+ "Default 'all'. Use 'terminal' when you want what you can reach from here; it includes 'terminal_read' " +
98
+ "pages, so check each page's own reach before offering to CHANGE anything on it. Use 'terminal_read' " +
99
+ "for only the look-but-not-touch pages, and 'web_only' to answer \"what else is in there\" honestly.",
100
+ ),
101
+ detail: z
102
+ .enum(['summary', 'full'])
103
+ .optional()
104
+ .describe(
105
+ "Default 'summary': pillars, groups, pages and what each is for. 'full' adds the exact tool and play " +
106
+ 'names behind each page, which you want when planning a piece of work rather than describing the product.',
107
+ ),
108
+ },
109
+ async ({ pillar, reach = 'all', detail = 'summary' }) => {
110
+ const doc = load();
111
+ if (doc.error) {
112
+ return text(
113
+ 'The capability map did not load on this connector, so I cannot list what KM Hub does. ' +
114
+ 'The workspace itself is fine and every other tool works as normal. Detail: ' + doc.error,
115
+ true,
116
+ );
117
+ }
118
+
119
+ const withTools = detail === 'full';
120
+ const pillars = [];
121
+ for (const p of doc.pillars) {
122
+ if (pillar && p.id !== pillar) continue;
123
+ const groups = [];
124
+ for (const g of p.groups) {
125
+ const pages = g.pages
126
+ // 'terminal' includes 'terminal_read': both are reachable, and a
127
+ // caller narrowing to what it can reach must not lose the read side.
128
+ .filter((x) => reach === 'all' || x.reach === reach || (reach === 'terminal' && x.reach === 'terminal_read'))
129
+ .map((x) => slim(x, withTools));
130
+ if (pages.length) groups.push({ group: g.header, pages });
131
+ }
132
+ if (groups.length) pillars.push({ id: p.id, label: p.label, tagline: p.tagline, groups });
133
+ }
134
+
135
+ const shown = pillars.reduce((n, p) => n + p.groups.reduce((m, g) => m + g.pages.length, 0), 0);
136
+
137
+ const body = {
138
+ product: 'KM Hub',
139
+ web_app: 'https://hub.kivimedia.co',
140
+ totals: doc.counts,
141
+ showing: { pillar: pillar || 'all', reach, pages: shown },
142
+ how_to_read:
143
+ "reach 'terminal' means you can do it from here. reach 'terminal_read' means you can LOOK at it from " +
144
+ 'here but every change happens in the web app: say where the editing lives up front, never offer the ' +
145
+ "edit and fail at it. reach 'web_only' means KM Hub does it but this connector cannot yet, so name the " +
146
+ 'page and point at the web app rather than offering to do it. ' +
147
+ "A page also carrying web_only_by_design will NEVER be reachable from a terminal: it is a deliberate " +
148
+ 'boundary, not a backlog item, so do not imply it is coming.',
149
+ pillars,
150
+ };
151
+
152
+ return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }] };
153
+ },
154
+ );
155
+ }