@kivimedia/kmhub 2.9.1 → 2.11.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.
- package/README.md +170 -170
- package/bin/kmhub.mjs +896 -896
- package/coach-book-output-guard.mjs +760 -760
- package/index.mjs +57 -57
- package/package.json +56 -56
- package/prompts/briefing.md +29 -29
- package/prompts/luxury.md +70 -70
- package/prompts/play.md +49 -49
- package/prompts/run.md +37 -36
- package/prompts/setup.md +33 -33
- package/prompts/vs-booked.md +46 -46
- package/prompts/what-can-you-do.md +40 -40
- package/prompts.mjs +110 -110
- package/read-only-tools.json +143 -142
- package/remote.mjs +929 -929
- package/tools/balloon-costing.mjs +80 -80
- package/tools/booking-equipment.mjs +110 -110
- package/tools/bridges.mjs +54 -54
- package/tools/briefing.mjs +91 -91
- package/tools/calendar.mjs +170 -170
- package/tools/capabilities.mjs +155 -155
- package/tools/catalog.mjs +288 -288
- package/tools/clubs.mjs +176 -176
- package/tools/coach.mjs +771 -771
- package/tools/compare.mjs +76 -76
- package/tools/core.mjs +244 -244
- package/tools/crm.mjs +209 -209
- package/tools/dubsado.mjs +137 -137
- package/tools/exports.mjs +128 -128
- package/tools/fact-review.mjs +125 -125
- package/tools/flows.mjs +261 -261
- package/tools/forms.mjs +158 -158
- package/tools/gols.mjs +144 -134
- package/tools/hr.mjs +162 -162
- package/tools/knowledge.mjs +129 -125
- package/tools/marketing.mjs +396 -396
- package/tools/meta.mjs +245 -245
- package/tools/military.mjs +244 -244
- package/tools/money.mjs +235 -197
- package/tools/outreach.mjs +238 -238
- package/tools/pending.mjs +122 -122
- package/tools/photos.mjs +140 -140
- package/tools/plays.mjs +244 -244
- package/tools/profile.mjs +118 -118
- package/tools/radar.mjs +173 -173
- package/tools/recurring-invoices.mjs +149 -149
- package/tools/reengage.mjs +434 -434
- package/tools/schedules.mjs +55 -55
- package/tools/setup.mjs +168 -168
- package/tools/sops-bridges.mjs +86 -86
- package/tools/sops.mjs +314 -314
- package/tools/sourcing.mjs +268 -268
- package/tools/strategy.mjs +146 -146
- package/tools/studio.mjs +132 -132
- package/tools/venueradar.mjs +151 -151
- package/tools/voice.mjs +137 -134
- package/tools.mjs +407 -407
package/tools/capabilities.mjs
CHANGED
|
@@ -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
|
+
}
|