@vibe-hero/server 0.1.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/LICENSE +190 -0
- package/README.md +151 -0
- package/dist/catalog/bundled/claude-code/.gitkeep +0 -0
- package/dist/catalog/bundled/claude-code/context-management.yaml +302 -0
- package/dist/catalog/bundled/claude-code/planning.yaml +313 -0
- package/dist/catalog/bundled/claude-code/subagents.yaml +357 -0
- package/dist/catalog/bundled/general/.gitkeep +0 -0
- package/dist/catalog/bundled/general/_placeholder.yaml +39 -0
- package/dist/catalog/bundled/general/task-decomposition.yaml +390 -0
- package/dist/catalog/bundled/index.d.ts +39 -0
- package/dist/catalog/bundled/index.d.ts.map +1 -0
- package/dist/catalog/bundled/index.js +41 -0
- package/dist/catalog/bundled/index.js.map +1 -0
- package/dist/catalog/fetcher.d.ts +201 -0
- package/dist/catalog/fetcher.d.ts.map +1 -0
- package/dist/catalog/fetcher.js +452 -0
- package/dist/catalog/fetcher.js.map +1 -0
- package/dist/catalog/loader.d.ts +165 -0
- package/dist/catalog/loader.d.ts.map +1 -0
- package/dist/catalog/loader.js +241 -0
- package/dist/catalog/loader.js.map +1 -0
- package/dist/catalog/resolve.d.ts +85 -0
- package/dist/catalog/resolve.d.ts.map +1 -0
- package/dist/catalog/resolve.js +103 -0
- package/dist/catalog/resolve.js.map +1 -0
- package/dist/cli/getOffer.d.ts +38 -0
- package/dist/cli/getOffer.d.ts.map +1 -0
- package/dist/cli/getOffer.js +150 -0
- package/dist/cli/getOffer.js.map +1 -0
- package/dist/cli/index.d.ts +46 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +88 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config.d.ts +34 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +63 -0
- package/dist/config.js.map +1 -0
- package/dist/engine/elo.d.ts +76 -0
- package/dist/engine/elo.d.ts.map +1 -0
- package/dist/engine/elo.js +79 -0
- package/dist/engine/elo.js.map +1 -0
- package/dist/engine/graduation.d.ts +108 -0
- package/dist/engine/graduation.d.ts.map +1 -0
- package/dist/engine/graduation.js +161 -0
- package/dist/engine/graduation.js.map +1 -0
- package/dist/engine/lapse.d.ts +80 -0
- package/dist/engine/lapse.d.ts.map +1 -0
- package/dist/engine/lapse.js +125 -0
- package/dist/engine/lapse.js.map +1 -0
- package/dist/engine/selection.d.ts +84 -0
- package/dist/engine/selection.d.ts.map +1 -0
- package/dist/engine/selection.js +119 -0
- package/dist/engine/selection.js.map +1 -0
- package/dist/grading/deterministic.d.ts +102 -0
- package/dist/grading/deterministic.d.ts.map +1 -0
- package/dist/grading/deterministic.js +118 -0
- package/dist/grading/deterministic.js.map +1 -0
- package/dist/grading/freeform.d.ts +64 -0
- package/dist/grading/freeform.d.ts.map +1 -0
- package/dist/grading/freeform.js +85 -0
- package/dist/grading/freeform.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +91 -0
- package/dist/index.js.map +1 -0
- package/dist/observation/hookEvents.d.ts +113 -0
- package/dist/observation/hookEvents.d.ts.map +1 -0
- package/dist/observation/hookEvents.js +170 -0
- package/dist/observation/hookEvents.js.map +1 -0
- package/dist/observation/offers.d.ts +215 -0
- package/dist/observation/offers.d.ts.map +1 -0
- package/dist/observation/offers.js +327 -0
- package/dist/observation/offers.js.map +1 -0
- package/dist/observation/source.d.ts +133 -0
- package/dist/observation/source.d.ts.map +1 -0
- package/dist/observation/source.js +105 -0
- package/dist/observation/source.js.map +1 -0
- package/dist/profile/migrate.d.ts +122 -0
- package/dist/profile/migrate.d.ts.map +1 -0
- package/dist/profile/migrate.js +147 -0
- package/dist/profile/migrate.js.map +1 -0
- package/dist/profile/store.d.ts +84 -0
- package/dist/profile/store.d.ts.map +1 -0
- package/dist/profile/store.js +267 -0
- package/dist/profile/store.js.map +1 -0
- package/dist/schemas/common.d.ts +95 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +106 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/content.d.ts +828 -0
- package/dist/schemas/content.d.ts.map +1 -0
- package/dist/schemas/content.js +219 -0
- package/dist/schemas/content.js.map +1 -0
- package/dist/schemas/profile.d.ts +599 -0
- package/dist/schemas/profile.d.ts.map +1 -0
- package/dist/schemas/profile.js +177 -0
- package/dist/schemas/profile.js.map +1 -0
- package/dist/schemas/tools.d.ts +1581 -0
- package/dist/schemas/tools.d.ts.map +1 -0
- package/dist/schemas/tools.js +286 -0
- package/dist/schemas/tools.js.map +1 -0
- package/dist/tools/config.d.ts +51 -0
- package/dist/tools/config.d.ts.map +1 -0
- package/dist/tools/config.js +104 -0
- package/dist/tools/config.js.map +1 -0
- package/dist/tools/gate.d.ts +50 -0
- package/dist/tools/gate.d.ts.map +1 -0
- package/dist/tools/gate.js +67 -0
- package/dist/tools/gate.js.map +1 -0
- package/dist/tools/guidance.d.ts +36 -0
- package/dist/tools/guidance.d.ts.map +1 -0
- package/dist/tools/guidance.js +117 -0
- package/dist/tools/guidance.js.map +1 -0
- package/dist/tools/listTopics.d.ts +55 -0
- package/dist/tools/listTopics.d.ts.map +1 -0
- package/dist/tools/listTopics.js +78 -0
- package/dist/tools/listTopics.js.map +1 -0
- package/dist/tools/offers.d.ts +60 -0
- package/dist/tools/offers.d.ts.map +1 -0
- package/dist/tools/offers.js +152 -0
- package/dist/tools/offers.js.map +1 -0
- package/dist/tools/placeholders.d.ts +27 -0
- package/dist/tools/placeholders.d.ts.map +1 -0
- package/dist/tools/placeholders.js +49 -0
- package/dist/tools/placeholders.js.map +1 -0
- package/dist/tools/recordObservation.d.ts +52 -0
- package/dist/tools/recordObservation.d.ts.map +1 -0
- package/dist/tools/recordObservation.js +87 -0
- package/dist/tools/recordObservation.js.map +1 -0
- package/dist/tools/startQuiz.d.ts +82 -0
- package/dist/tools/startQuiz.d.ts.map +1 -0
- package/dist/tools/startQuiz.js +180 -0
- package/dist/tools/startQuiz.js.map +1 -0
- package/dist/tools/status.d.ts +59 -0
- package/dist/tools/status.d.ts.map +1 -0
- package/dist/tools/status.js +133 -0
- package/dist/tools/status.js.map +1 -0
- package/dist/tools/submitAnswer.d.ts +156 -0
- package/dist/tools/submitAnswer.d.ts.map +1 -0
- package/dist/tools/submitAnswer.js +402 -0
- package/dist/tools/submitAnswer.js.map +1 -0
- package/dist/tools/types.d.ts +82 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +48 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/us2/standing.d.ts +111 -0
- package/dist/tools/us2/standing.d.ts.map +1 -0
- package/dist/tools/us2/standing.js +143 -0
- package/dist/tools/us2/standing.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Real `get_guidance` tool module (T027, US-2).
|
|
3
|
+
*
|
|
4
|
+
* Returns teaching guidance plus a concrete next step for a topic. Given a `key`
|
|
5
|
+
* it guides on that exact `(topic × class)`; with NO `key` it auto-selects the
|
|
6
|
+
* learner's weakest/stale in-scope topic (the same weakness ranking
|
|
7
|
+
* `get_status` uses for `suggestions`) so "what should I learn next?" resolves
|
|
8
|
+
* without the user naming a topic.
|
|
9
|
+
*
|
|
10
|
+
* Guidance text is pulled from the topic's authored content: the `guidance` of
|
|
11
|
+
* an item at — or, failing that, nearest above — the learner's current tier,
|
|
12
|
+
* falling back to the topic `summary` when the topic has no items. The next step
|
|
13
|
+
* is a `quiz` when the topic has gradeable items (the practice path, FR-021),
|
|
14
|
+
* else a `read` nudge toward the summary.
|
|
15
|
+
*
|
|
16
|
+
* Telemetry-free (SC-011): everything is derived from the bundled catalog + the
|
|
17
|
+
* profile's `abilities`/`graduations`; no observation/offer state is read.
|
|
18
|
+
* Gated (FR-032) — `index.ts` returns SETUP_REQUIRED before the handler runs
|
|
19
|
+
* when unconfigured, so `config.toolsLearning` is available for the default tool.
|
|
20
|
+
*
|
|
21
|
+
* Exposed as a `dirOverride`-closing factory mirroring `config.ts`.
|
|
22
|
+
*
|
|
23
|
+
* Source of truth: specs/001-vibe-hero-mvp/contracts/mcp-tools.md
|
|
24
|
+
* (`get_guidance`), spec.md US-2 / FR-021 / SC-011.
|
|
25
|
+
*/
|
|
26
|
+
import { loadBundledCatalog } from "../catalog/bundled/index.js";
|
|
27
|
+
import { loadProfile } from "../profile/store.js";
|
|
28
|
+
import { abilityKey, parseAbilityKey, } from "../schemas/common.js";
|
|
29
|
+
import { GetGuidanceInputSchema, } from "../schemas/tools.js";
|
|
30
|
+
import { defineTool } from "./types.js";
|
|
31
|
+
import { computeStandings, rankByWeakness, standingFor, } from "./us2/standing.js";
|
|
32
|
+
/** Resolve the default tool (explicit → first learning → claude-code). */
|
|
33
|
+
const resolveTool = (requested, toolsLearning) => requested ?? toolsLearning[0] ?? "claude-code";
|
|
34
|
+
/** Find the catalog topic whose `(class, id)` serializes to `key`. */
|
|
35
|
+
const findTopicByKey = (topics, key) => topics.find((topic) => abilityKey(topic.class, topic.id) === key);
|
|
36
|
+
/**
|
|
37
|
+
* Pick the authored item whose `guidance` best fits `currentTier`: prefer an
|
|
38
|
+
* item AT the current tier (or tier 100 when the learner has not graduated, so
|
|
39
|
+
* tier is 0), else the nearest item ABOVE it, else any item. Returns `undefined`
|
|
40
|
+
* only when the topic has no items at all.
|
|
41
|
+
*/
|
|
42
|
+
const guidanceItemFor = (topic, currentTier) => {
|
|
43
|
+
if (topic.items.length === 0)
|
|
44
|
+
return undefined;
|
|
45
|
+
const floor = currentTier === 0 ? 100 : currentTier;
|
|
46
|
+
const atTier = topic.items.find((item) => item.tier === floor);
|
|
47
|
+
if (atTier !== undefined)
|
|
48
|
+
return atTier;
|
|
49
|
+
const above = [...topic.items]
|
|
50
|
+
.filter((item) => item.tier >= floor)
|
|
51
|
+
.sort((a, b) => a.tier - b.tier)[0];
|
|
52
|
+
if (above !== undefined)
|
|
53
|
+
return above;
|
|
54
|
+
// Below the floor (learner graduated past authored content): take the highest.
|
|
55
|
+
return [...topic.items].sort((a, b) => b.tier - a.tier)[0];
|
|
56
|
+
};
|
|
57
|
+
/** Build the contract result for one resolved topic standing. */
|
|
58
|
+
const guidanceResultFor = (standing) => {
|
|
59
|
+
const { topic, key, row } = standing;
|
|
60
|
+
const item = guidanceItemFor(topic, row.tier);
|
|
61
|
+
const guidance = item?.guidance ?? topic.summary;
|
|
62
|
+
const nextStep = topic.items.length > 0
|
|
63
|
+
? {
|
|
64
|
+
action: "quiz",
|
|
65
|
+
detail: `Try a short quiz on "${topic.title}" to ${row.status === "not_started" ? "establish a baseline" : "reinforce and advance"}.`,
|
|
66
|
+
}
|
|
67
|
+
: {
|
|
68
|
+
action: "read",
|
|
69
|
+
detail: `Review "${topic.title}": ${topic.summary}`,
|
|
70
|
+
};
|
|
71
|
+
return { key, title: topic.title, currentTier: row.tier, guidance, nextStep };
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Resolve the standing `get_guidance` should report on. With a `key`, guide on
|
|
75
|
+
* that exact topic (scoped via its own class). With no `key`, auto-select the
|
|
76
|
+
* weakest/stale in-scope topic for the resolved tool.
|
|
77
|
+
*
|
|
78
|
+
* @throws {Error} if a supplied `key` matches no catalog topic.
|
|
79
|
+
*/
|
|
80
|
+
const resolveStanding = (input, topics, profile) => {
|
|
81
|
+
if (input.key !== undefined) {
|
|
82
|
+
// Validate the key shape (throws on malformed) before lookup.
|
|
83
|
+
parseAbilityKey(input.key);
|
|
84
|
+
const topic = findTopicByKey(topics, input.key);
|
|
85
|
+
if (topic === undefined) {
|
|
86
|
+
throw new Error(`get_guidance: no catalog topic matches key ${JSON.stringify(input.key)}`);
|
|
87
|
+
}
|
|
88
|
+
return standingFor(topic, profile);
|
|
89
|
+
}
|
|
90
|
+
const tool = resolveTool(input.tool, profile.config?.toolsLearning ?? []);
|
|
91
|
+
const ranked = rankByWeakness(computeStandings(topics, profile, tool));
|
|
92
|
+
const weakest = ranked[0];
|
|
93
|
+
if (weakest === undefined) {
|
|
94
|
+
throw new Error(`get_guidance: no topics available for tool ${JSON.stringify(tool)}`);
|
|
95
|
+
}
|
|
96
|
+
return weakest;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Build the `get_guidance` tool module (US-2).
|
|
100
|
+
*
|
|
101
|
+
* @param dirOverride - Profile-directory override (test seam); see `profileDir`.
|
|
102
|
+
* @returns The erased registry entry for `get_guidance`.
|
|
103
|
+
*/
|
|
104
|
+
export const makeGetGuidanceTool = (dirOverride) => defineTool({
|
|
105
|
+
name: "get_guidance",
|
|
106
|
+
description: "Return teaching guidance and what to learn next for a topic or the weakest area. Read-only.",
|
|
107
|
+
inputSchema: GetGuidanceInputSchema,
|
|
108
|
+
handler: async (input) => {
|
|
109
|
+
const profile = await loadProfile(dirOverride);
|
|
110
|
+
const { topics } = loadBundledCatalog();
|
|
111
|
+
const standing = resolveStanding(input, topics, profile);
|
|
112
|
+
return guidanceResultFor(standing);
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
/** Default `get_guidance` module (env / `~/.vibe-hero`), used by the registry. */
|
|
116
|
+
export const getGuidanceTool = makeGetGuidanceTool();
|
|
117
|
+
//# sourceMappingURL=guidance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guidance.js","sourceRoot":"","sources":["../../src/tools/guidance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Real `list_topics` tool module (T026, US-2).
|
|
3
|
+
*
|
|
4
|
+
* Enumerates the catalog's topics, optionally filtered by `tool` and/or `class`,
|
|
5
|
+
* returning one row per topic (`{ key, id, class, title, tiers, itemCount }`)
|
|
6
|
+
* plus the catalog `version`. Works offline against the bundled snapshot
|
|
7
|
+
* (FR-025); `catalogVersion` comes from the manifest derived over the loaded
|
|
8
|
+
* topics (the placeholder `0.0.0-bundled` until the fetch layer supplies a real
|
|
9
|
+
* semver — see `buildManifest`).
|
|
10
|
+
*
|
|
11
|
+
* Telemetry-free (SC-011): enumeration reads only the catalog, never the
|
|
12
|
+
* profile's observation/offer state. Gated (FR-032) — `index.ts` returns
|
|
13
|
+
* SETUP_REQUIRED before the handler runs when unconfigured.
|
|
14
|
+
*
|
|
15
|
+
* Catalog source: resolves via {@link resolveCatalog} (fresh-fetch → cache →
|
|
16
|
+
* bundled, T054). With no `VIBE_HERO_CONTENT_URL` configured this is exactly the
|
|
17
|
+
* prior bundled behavior; when a source is configured the reported
|
|
18
|
+
* `catalogVersion` advances after a published update (SC-007). All remote
|
|
19
|
+
* failures degrade silently to cache/bundled (FR-027).
|
|
20
|
+
*
|
|
21
|
+
* Filters:
|
|
22
|
+
* - `class` ("general" | "tool") matches a topic's `class.kind`.
|
|
23
|
+
* - `tool` keeps `general` topics (they apply to every tool) plus tool-scoped
|
|
24
|
+
* topics whose tool matches — the same scoping rule `get_status` uses.
|
|
25
|
+
*
|
|
26
|
+
* Exposed as a `(dirOverride, resolver)` factory: `dirOverride` flows to the
|
|
27
|
+
* content-cache dir (sibling of the profile home), and `resolver` is the catalog
|
|
28
|
+
* source seam (defaults to {@link resolveCatalog}) so tests can inject a fake
|
|
29
|
+
* fetch/cache without touching the network.
|
|
30
|
+
*
|
|
31
|
+
* Source of truth: specs/001-vibe-hero-mvp/contracts/mcp-tools.md (`list_topics`),
|
|
32
|
+
* spec.md US-2 / FR-025 / FR-026 / FR-027 / SC-006 / SC-007.
|
|
33
|
+
*/
|
|
34
|
+
import { type ResolvedCatalog } from "../catalog/resolve.js";
|
|
35
|
+
import { type AnyToolModule } from "./types.js";
|
|
36
|
+
/**
|
|
37
|
+
* Catalog-resolution seam for {@link makeListTopicsTool}. Mirrors
|
|
38
|
+
* {@link resolveCatalog}'s signature so tests can inject a fake fetch/cache while
|
|
39
|
+
* production uses the real fresh-fetch → cache → bundled resolution.
|
|
40
|
+
*/
|
|
41
|
+
export type CatalogResolver = (dirOverride?: string) => Promise<ResolvedCatalog>;
|
|
42
|
+
/**
|
|
43
|
+
* Build the `list_topics` tool module (US-2).
|
|
44
|
+
*
|
|
45
|
+
* @param dirOverride - Profile-home override (test seam); flows to the content
|
|
46
|
+
* cache dir used by {@link resolveCatalog}.
|
|
47
|
+
* @param resolver - Catalog-resolution seam (test seam); defaults to
|
|
48
|
+
* {@link resolveCatalog} (fresh-fetch → cache → bundled). Tests inject a fake
|
|
49
|
+
* resolver/fetch so no real network is hit.
|
|
50
|
+
* @returns The erased registry entry for `list_topics`.
|
|
51
|
+
*/
|
|
52
|
+
export declare const makeListTopicsTool: (dirOverride?: string, resolver?: CatalogResolver) => AnyToolModule;
|
|
53
|
+
/** Default `list_topics` module (env / `~/.vibe-hero`), used by the registry. */
|
|
54
|
+
export declare const listTopicsTool: AnyToolModule;
|
|
55
|
+
//# sourceMappingURL=listTopics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listTopics.d.ts","sourceRoot":"","sources":["../../src/tools/listTopics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAS7E,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAG5D;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,CAC5B,WAAW,CAAC,EAAE,MAAM,KACjB,OAAO,CAAC,eAAe,CAAC,CAAC;AAS9B;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,GAC7B,cAAc,MAAM,EACpB,WAAU,eAAgC,KACzC,aA6BC,CAAC;AAEL,iFAAiF;AACjF,eAAO,MAAM,cAAc,EAAE,aAAoC,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Real `list_topics` tool module (T026, US-2).
|
|
3
|
+
*
|
|
4
|
+
* Enumerates the catalog's topics, optionally filtered by `tool` and/or `class`,
|
|
5
|
+
* returning one row per topic (`{ key, id, class, title, tiers, itemCount }`)
|
|
6
|
+
* plus the catalog `version`. Works offline against the bundled snapshot
|
|
7
|
+
* (FR-025); `catalogVersion` comes from the manifest derived over the loaded
|
|
8
|
+
* topics (the placeholder `0.0.0-bundled` until the fetch layer supplies a real
|
|
9
|
+
* semver — see `buildManifest`).
|
|
10
|
+
*
|
|
11
|
+
* Telemetry-free (SC-011): enumeration reads only the catalog, never the
|
|
12
|
+
* profile's observation/offer state. Gated (FR-032) — `index.ts` returns
|
|
13
|
+
* SETUP_REQUIRED before the handler runs when unconfigured.
|
|
14
|
+
*
|
|
15
|
+
* Catalog source: resolves via {@link resolveCatalog} (fresh-fetch → cache →
|
|
16
|
+
* bundled, T054). With no `VIBE_HERO_CONTENT_URL` configured this is exactly the
|
|
17
|
+
* prior bundled behavior; when a source is configured the reported
|
|
18
|
+
* `catalogVersion` advances after a published update (SC-007). All remote
|
|
19
|
+
* failures degrade silently to cache/bundled (FR-027).
|
|
20
|
+
*
|
|
21
|
+
* Filters:
|
|
22
|
+
* - `class` ("general" | "tool") matches a topic's `class.kind`.
|
|
23
|
+
* - `tool` keeps `general` topics (they apply to every tool) plus tool-scoped
|
|
24
|
+
* topics whose tool matches — the same scoping rule `get_status` uses.
|
|
25
|
+
*
|
|
26
|
+
* Exposed as a `(dirOverride, resolver)` factory: `dirOverride` flows to the
|
|
27
|
+
* content-cache dir (sibling of the profile home), and `resolver` is the catalog
|
|
28
|
+
* source seam (defaults to {@link resolveCatalog}) so tests can inject a fake
|
|
29
|
+
* fetch/cache without touching the network.
|
|
30
|
+
*
|
|
31
|
+
* Source of truth: specs/001-vibe-hero-mvp/contracts/mcp-tools.md (`list_topics`),
|
|
32
|
+
* spec.md US-2 / FR-025 / FR-026 / FR-027 / SC-006 / SC-007.
|
|
33
|
+
*/
|
|
34
|
+
import { resolveCatalog } from "../catalog/resolve.js";
|
|
35
|
+
import { abilityKey } from "../schemas/common.js";
|
|
36
|
+
import { ListTopicsInputSchema, } from "../schemas/tools.js";
|
|
37
|
+
import { defineTool } from "./types.js";
|
|
38
|
+
import { topicInScope } from "./us2/standing.js";
|
|
39
|
+
/** The narrowed `class` discriminator surfaced in a {@link ListTopicsRow}. */
|
|
40
|
+
const classKind = (topic) => topic.class.kind;
|
|
41
|
+
/** Distinct tiers present in a topic's items, ascending (manifest convention). */
|
|
42
|
+
const topicTiers = (topic) => [...new Set(topic.items.map((item) => item.tier))].sort((a, b) => a - b);
|
|
43
|
+
/**
|
|
44
|
+
* Build the `list_topics` tool module (US-2).
|
|
45
|
+
*
|
|
46
|
+
* @param dirOverride - Profile-home override (test seam); flows to the content
|
|
47
|
+
* cache dir used by {@link resolveCatalog}.
|
|
48
|
+
* @param resolver - Catalog-resolution seam (test seam); defaults to
|
|
49
|
+
* {@link resolveCatalog} (fresh-fetch → cache → bundled). Tests inject a fake
|
|
50
|
+
* resolver/fetch so no real network is hit.
|
|
51
|
+
* @returns The erased registry entry for `list_topics`.
|
|
52
|
+
*/
|
|
53
|
+
export const makeListTopicsTool = (dirOverride, resolver = resolveCatalog) => defineTool({
|
|
54
|
+
name: "list_topics",
|
|
55
|
+
description: "Enumerate catalog topics, optionally filtered by tool or class. Read-only.",
|
|
56
|
+
inputSchema: ListTopicsInputSchema,
|
|
57
|
+
handler: async (input) => {
|
|
58
|
+
// Resolve via fresh-fetch → cache → bundled (T054). `catalogVersion` is
|
|
59
|
+
// the winning source's version, independent of the caller's filter, so it
|
|
60
|
+
// stays stable across queries and advances after a published update.
|
|
61
|
+
const { topics, catalogVersion } = await resolver(dirOverride);
|
|
62
|
+
const filtered = topics
|
|
63
|
+
.filter((topic) => topicInScope(topic, input.tool))
|
|
64
|
+
.filter((topic) => input.class === undefined || classKind(topic) === input.class);
|
|
65
|
+
const rows = filtered.map((topic) => ({
|
|
66
|
+
key: abilityKey(topic.class, topic.id),
|
|
67
|
+
id: topic.id,
|
|
68
|
+
class: classKind(topic),
|
|
69
|
+
title: topic.title,
|
|
70
|
+
tiers: topicTiers(topic),
|
|
71
|
+
itemCount: topic.items.length,
|
|
72
|
+
}));
|
|
73
|
+
return { topics: rows, catalogVersion };
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
/** Default `list_topics` module (env / `~/.vibe-hero`), used by the registry. */
|
|
77
|
+
export const listTopicsTool = makeListTopicsTool();
|
|
78
|
+
//# sourceMappingURL=listTopics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listTopics.js","sourceRoot":"","sources":["../../src/tools/listTopics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,cAAc,EAAwB,MAAM,uBAAuB,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,OAAO,EACL,qBAAqB,GAItB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAsB,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAWjD,8EAA8E;AAC9E,MAAM,SAAS,GAAG,CAAC,KAAY,EAAsB,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;AAEzE,kFAAkF;AAClF,MAAM,UAAU,GAAG,CAAC,KAAY,EAA0B,EAAE,CAC1D,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAE3E;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,WAAoB,EACpB,WAA4B,cAAc,EAC3B,EAAE,CACjB,UAAU,CAAC;IACT,IAAI,EAAE,aAAa;IACnB,WAAW,EACT,4EAA4E;IAC9E,WAAW,EAAE,qBAAqB;IAClC,OAAO,EAAE,KAAK,EAAE,KAAsB,EAA6B,EAAE;QACnE,wEAAwE;QACxE,0EAA0E;QAC1E,qEAAqE;QACrE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC;QAE/D,MAAM,QAAQ,GAAG,MAAM;aACpB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;aAClD,MAAM,CACL,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,KAAK,CACzE,CAAC;QAEJ,MAAM,IAAI,GAAoB,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACrD,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;YACtC,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC;YACvB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC;YACxB,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM;SAC9B,CAAC,CAAC,CAAC;QAEJ,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IAC1C,CAAC;CACF,CAAC,CAAC;AAEL,iFAAiF;AACjF,MAAM,CAAC,MAAM,cAAc,GAAkB,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Real `get_offer` / `record_offer_response` tool modules (T036, US-1).
|
|
3
|
+
*
|
|
4
|
+
* These two tools are the end-of-work offer surface, thin wrappers over the pure
|
|
5
|
+
* offer engine (`../observation/offers.js`):
|
|
6
|
+
*
|
|
7
|
+
* - `get_offer` ({sessionId, tool}) resolves whether an offer may surface at an
|
|
8
|
+
* end-of-work breakpoint and for which key, honoring cadence + anti-fatigue
|
|
9
|
+
* via {@link resolveOffer}. The candidate pool is the per-session
|
|
10
|
+
* `OfferLedger.candidateKeys` accumulated by `record_observation` (get_offer
|
|
11
|
+
* receives no signals of its own). When an offer surfaces it is recorded via
|
|
12
|
+
* {@link markOffered} so the cadence caps apply on the next call, and the
|
|
13
|
+
* offer's `prompt` is built from the catalog topic's summary.
|
|
14
|
+
*
|
|
15
|
+
* - `record_offer_response` ({sessionId, key, response}) persists the
|
|
16
|
+
* accept/decline/defer outcome: a decline applies within-session suppression
|
|
17
|
+
* (FR-020) AND cross-session backoff + eventual global mute (FR-020b) via
|
|
18
|
+
* {@link applyDecline}; an accept resets the consecutive-decline counter and
|
|
19
|
+
* clears the mute via {@link applyAccept}; a defer leaves state unchanged.
|
|
20
|
+
*
|
|
21
|
+
* Both delegate the decision/state math to the pure engine and own only the IO:
|
|
22
|
+
* clock, catalog load, and the atomic {@link updateProfile} write. Neither tool
|
|
23
|
+
* EVER touches abilities/graduations/quizHistory — offers never score (FR-005,
|
|
24
|
+
* SC-003).
|
|
25
|
+
*
|
|
26
|
+
* Gated (FR-032): NOT exempt — `index.ts`/`withSetupGate` returns SETUP_REQUIRED
|
|
27
|
+
* when `profile.config` is absent. The handlers assume a configured profile.
|
|
28
|
+
*
|
|
29
|
+
* Each tool is a `(dirOverride, catalogLoader)` factory mirroring
|
|
30
|
+
* `startQuiz.ts`/`recordObservation.ts`.
|
|
31
|
+
*
|
|
32
|
+
* Source of truth: specs/001-vibe-hero-mvp/contracts/mcp-tools.md (`get_offer`,
|
|
33
|
+
* `record_offer_response`), spec.md FR-019/020/020a/020b, data-model.md
|
|
34
|
+
* (§ OfferLedger), src/config.ts (ASSESSMENT_CONFIG).
|
|
35
|
+
*/
|
|
36
|
+
import type { CatalogLoadResult } from "../catalog/loader.js";
|
|
37
|
+
import { type AnyToolModule } from "./types.js";
|
|
38
|
+
/** Catalog source override (test seam); defaults to the bundled snapshot. */
|
|
39
|
+
export type CatalogLoader = () => CatalogLoadResult;
|
|
40
|
+
/**
|
|
41
|
+
* Build the `get_offer` tool module (US-1).
|
|
42
|
+
*
|
|
43
|
+
* @param dirOverride - Profile-directory override (test seam); see `profileDir`.
|
|
44
|
+
* @param catalogLoader - Catalog source override (test seam); defaults to the
|
|
45
|
+
* bundled snapshot {@link loadBundledCatalog}.
|
|
46
|
+
* @returns The erased registry entry for `get_offer`.
|
|
47
|
+
*/
|
|
48
|
+
export declare const makeGetOfferTool: (dirOverride?: string, catalogLoader?: CatalogLoader) => AnyToolModule;
|
|
49
|
+
/**
|
|
50
|
+
* Build the `record_offer_response` tool module (US-1).
|
|
51
|
+
*
|
|
52
|
+
* @param dirOverride - Profile-directory override (test seam); see `profileDir`.
|
|
53
|
+
* @returns The erased registry entry for `record_offer_response`.
|
|
54
|
+
*/
|
|
55
|
+
export declare const makeRecordOfferResponseTool: (dirOverride?: string) => AnyToolModule;
|
|
56
|
+
/** Default `get_offer` module (env / `~/.vibe-hero` + bundled catalog). */
|
|
57
|
+
export declare const getOfferTool: AnyToolModule;
|
|
58
|
+
/** Default `record_offer_response` module (env / `~/.vibe-hero`). */
|
|
59
|
+
export declare const recordOfferResponseTool: AnyToolModule;
|
|
60
|
+
//# sourceMappingURL=offers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offers.d.ts","sourceRoot":"","sources":["../../src/tools/offers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAqB9D,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D,6EAA6E;AAC7E,MAAM,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAAC;AAapD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,GAC3B,cAAc,MAAM,EACpB,gBAAe,aAAkC,KAChD,aA0DC,CAAC;AAiBL;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GACtC,cAAc,MAAM,KACnB,aA8CC,CAAC;AAEL,2EAA2E;AAC3E,eAAO,MAAM,YAAY,EAAE,aAAkC,CAAC;AAE9D,qEAAqE;AACrE,eAAO,MAAM,uBAAuB,EAAE,aACP,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Real `get_offer` / `record_offer_response` tool modules (T036, US-1).
|
|
3
|
+
*
|
|
4
|
+
* These two tools are the end-of-work offer surface, thin wrappers over the pure
|
|
5
|
+
* offer engine (`../observation/offers.js`):
|
|
6
|
+
*
|
|
7
|
+
* - `get_offer` ({sessionId, tool}) resolves whether an offer may surface at an
|
|
8
|
+
* end-of-work breakpoint and for which key, honoring cadence + anti-fatigue
|
|
9
|
+
* via {@link resolveOffer}. The candidate pool is the per-session
|
|
10
|
+
* `OfferLedger.candidateKeys` accumulated by `record_observation` (get_offer
|
|
11
|
+
* receives no signals of its own). When an offer surfaces it is recorded via
|
|
12
|
+
* {@link markOffered} so the cadence caps apply on the next call, and the
|
|
13
|
+
* offer's `prompt` is built from the catalog topic's summary.
|
|
14
|
+
*
|
|
15
|
+
* - `record_offer_response` ({sessionId, key, response}) persists the
|
|
16
|
+
* accept/decline/defer outcome: a decline applies within-session suppression
|
|
17
|
+
* (FR-020) AND cross-session backoff + eventual global mute (FR-020b) via
|
|
18
|
+
* {@link applyDecline}; an accept resets the consecutive-decline counter and
|
|
19
|
+
* clears the mute via {@link applyAccept}; a defer leaves state unchanged.
|
|
20
|
+
*
|
|
21
|
+
* Both delegate the decision/state math to the pure engine and own only the IO:
|
|
22
|
+
* clock, catalog load, and the atomic {@link updateProfile} write. Neither tool
|
|
23
|
+
* EVER touches abilities/graduations/quizHistory — offers never score (FR-005,
|
|
24
|
+
* SC-003).
|
|
25
|
+
*
|
|
26
|
+
* Gated (FR-032): NOT exempt — `index.ts`/`withSetupGate` returns SETUP_REQUIRED
|
|
27
|
+
* when `profile.config` is absent. The handlers assume a configured profile.
|
|
28
|
+
*
|
|
29
|
+
* Each tool is a `(dirOverride, catalogLoader)` factory mirroring
|
|
30
|
+
* `startQuiz.ts`/`recordObservation.ts`.
|
|
31
|
+
*
|
|
32
|
+
* Source of truth: specs/001-vibe-hero-mvp/contracts/mcp-tools.md (`get_offer`,
|
|
33
|
+
* `record_offer_response`), spec.md FR-019/020/020a/020b, data-model.md
|
|
34
|
+
* (§ OfferLedger), src/config.ts (ASSESSMENT_CONFIG).
|
|
35
|
+
*/
|
|
36
|
+
import { ASSESSMENT_CONFIG } from "../config.js";
|
|
37
|
+
import { loadBundledCatalog } from "../catalog/bundled/index.js";
|
|
38
|
+
import { loadProfile, updateProfile } from "../profile/store.js";
|
|
39
|
+
import { abilityKey } from "../schemas/common.js";
|
|
40
|
+
import { GetOfferInputSchema, RecordOfferResponseInputSchema, } from "../schemas/tools.js";
|
|
41
|
+
import { applyAccept, applyDecline, applyDefer, ledgerForSession, markOffered, resolveOffer, } from "../observation/offers.js";
|
|
42
|
+
import { defineTool } from "./types.js";
|
|
43
|
+
/** Find the catalog topic whose `(class, id)` serializes to `key`. */
|
|
44
|
+
const findTopicByKey = (topics, key) => topics.find((topic) => abilityKey(topic.class, topic.id) === key);
|
|
45
|
+
/** Build the user-facing offer prompt for a topic (privacy-safe, no scoring). */
|
|
46
|
+
const offerPrompt = (topic) => `You just exercised "${topic.title}". Want a quick quiz to check your grasp? (${topic.summary})`;
|
|
47
|
+
/**
|
|
48
|
+
* Build the `get_offer` tool module (US-1).
|
|
49
|
+
*
|
|
50
|
+
* @param dirOverride - Profile-directory override (test seam); see `profileDir`.
|
|
51
|
+
* @param catalogLoader - Catalog source override (test seam); defaults to the
|
|
52
|
+
* bundled snapshot {@link loadBundledCatalog}.
|
|
53
|
+
* @returns The erased registry entry for `get_offer`.
|
|
54
|
+
*/
|
|
55
|
+
export const makeGetOfferTool = (dirOverride, catalogLoader = loadBundledCatalog) => defineTool({
|
|
56
|
+
name: "get_offer",
|
|
57
|
+
description: "Resolve whether to surface an end-of-work learning offer for the session.",
|
|
58
|
+
inputSchema: GetOfferInputSchema,
|
|
59
|
+
handler: async (input) => {
|
|
60
|
+
const now = new Date();
|
|
61
|
+
const profile = await loadProfile(dirOverride);
|
|
62
|
+
const config = profile.config;
|
|
63
|
+
// Gated: config is present. Reconcile the ledger to THIS session (a new
|
|
64
|
+
// session id rolls over the per-session accounting) before deciding.
|
|
65
|
+
const ledger = ledgerForSession(profile.offers, input.sessionId);
|
|
66
|
+
const decision = resolveOffer({
|
|
67
|
+
proactiveOffers: config?.proactiveOffers ?? false,
|
|
68
|
+
offerCadence: config?.offerCadence ?? "off",
|
|
69
|
+
ledger,
|
|
70
|
+
backoff: profile.backoff,
|
|
71
|
+
candidates: ledger.candidateKeys,
|
|
72
|
+
}, now);
|
|
73
|
+
if (decision.kind === "suppressed") {
|
|
74
|
+
// Persist any session rollover (so a fresh session id is recorded) but
|
|
75
|
+
// never touch scoring state.
|
|
76
|
+
if (ledger.sessionId !== profile.offers.sessionId) {
|
|
77
|
+
await persistLedger(ledger, dirOverride);
|
|
78
|
+
}
|
|
79
|
+
return { suppressed: decision.reason };
|
|
80
|
+
}
|
|
81
|
+
const { topics } = catalogLoader();
|
|
82
|
+
const topic = findTopicByKey(topics, decision.key);
|
|
83
|
+
if (topic === undefined) {
|
|
84
|
+
// The candidate key has no resolvable topic (catalog drift): treat as
|
|
85
|
+
// no candidate rather than throwing — offers must never crash the path.
|
|
86
|
+
if (ledger.sessionId !== profile.offers.sessionId) {
|
|
87
|
+
await persistLedger(ledger, dirOverride);
|
|
88
|
+
}
|
|
89
|
+
return { suppressed: "no_candidate" };
|
|
90
|
+
}
|
|
91
|
+
// Record that the offer surfaced so the cadence caps apply next time.
|
|
92
|
+
const offered = markOffered(ledger, decision.key);
|
|
93
|
+
await persistLedger(offered, dirOverride);
|
|
94
|
+
return {
|
|
95
|
+
offer: {
|
|
96
|
+
key: decision.key,
|
|
97
|
+
title: topic.title,
|
|
98
|
+
prompt: offerPrompt(topic),
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
/**
|
|
104
|
+
* Persist ONLY the `offers` ledger block (atomic). Never touches abilities,
|
|
105
|
+
* graduations, quizHistory, reviewSchedule, or backoff — keeping the offer path
|
|
106
|
+
* score-free (FR-005 / SC-003).
|
|
107
|
+
*/
|
|
108
|
+
const persistLedger = async (ledger, dirOverride) => {
|
|
109
|
+
await updateProfile((current) => ({ ...current, offers: ledger }), dirOverride);
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Build the `record_offer_response` tool module (US-1).
|
|
113
|
+
*
|
|
114
|
+
* @param dirOverride - Profile-directory override (test seam); see `profileDir`.
|
|
115
|
+
* @returns The erased registry entry for `record_offer_response`.
|
|
116
|
+
*/
|
|
117
|
+
export const makeRecordOfferResponseTool = (dirOverride) => defineTool({
|
|
118
|
+
name: "record_offer_response",
|
|
119
|
+
description: "Record an accept/decline/defer offer response so cadence and anti-nag are honored.",
|
|
120
|
+
inputSchema: RecordOfferResponseInputSchema,
|
|
121
|
+
handler: async (input) => {
|
|
122
|
+
const now = new Date();
|
|
123
|
+
// Single atomic read-modify-write so concurrent sessions serialize. Only
|
|
124
|
+
// the `offers` ledger + `backoff` change — scoring state is untouched.
|
|
125
|
+
await updateProfile((current) => {
|
|
126
|
+
const ledger = ledgerForSession(current.offers, input.sessionId);
|
|
127
|
+
switch (input.response) {
|
|
128
|
+
case "decline": {
|
|
129
|
+
const { ledger: nextLedger, backoff } = applyDecline(ledger, current.backoff, input.key, now, ASSESSMENT_CONFIG);
|
|
130
|
+
return { ...current, offers: nextLedger, backoff };
|
|
131
|
+
}
|
|
132
|
+
case "accept": {
|
|
133
|
+
return {
|
|
134
|
+
...current,
|
|
135
|
+
offers: ledger,
|
|
136
|
+
backoff: applyAccept(current.backoff),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
case "defer": {
|
|
140
|
+
const { ledger: nextLedger, backoff } = applyDefer(ledger, current.backoff);
|
|
141
|
+
return { ...current, offers: nextLedger, backoff };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}, dirOverride);
|
|
145
|
+
return { ok: true };
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
/** Default `get_offer` module (env / `~/.vibe-hero` + bundled catalog). */
|
|
149
|
+
export const getOfferTool = makeGetOfferTool();
|
|
150
|
+
/** Default `record_offer_response` module (env / `~/.vibe-hero`). */
|
|
151
|
+
export const recordOfferResponseTool = makeRecordOfferResponseTool();
|
|
152
|
+
//# sourceMappingURL=offers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offers.js","sourceRoot":"","sources":["../../src/tools/offers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Tool registry (T020).
|
|
3
|
+
*
|
|
4
|
+
* This wires all 10 MCP tools into a single {@link TOOL_REGISTRY} that
|
|
5
|
+
* `index.ts` iterates over to register them. Each module carries its real input
|
|
6
|
+
* schema (from `schemas/tools.ts`) so the host-facing tool signatures are
|
|
7
|
+
* correct. `save_config` / `get_config` are the real US-0 implementations
|
|
8
|
+
* (`./config.js`, T022); `get_status` / `list_topics` / `get_guidance` are the
|
|
9
|
+
* real US-2 read tools (`./status.js`, `./listTopics.js`, `./guidance.js`,
|
|
10
|
+
* T025–T027); `start_quiz` / `submit_answer` are the real US-1 core-loop tools
|
|
11
|
+
* (`./startQuiz.js`, `./submitAnswer.js`, T031/T032); `record_observation` /
|
|
12
|
+
* `get_offer` / `record_offer_response` are the real US-1 offer tools
|
|
13
|
+
* (`./recordObservation.js`, `./offers.js`, T034/T036). All 10 are now real —
|
|
14
|
+
* no placeholders remain.
|
|
15
|
+
*
|
|
16
|
+
* The registration architecture and gate wiring delivered here stay unchanged.
|
|
17
|
+
*
|
|
18
|
+
* Source of truth: specs/001-vibe-hero-mvp/contracts/mcp-tools.md.
|
|
19
|
+
*/
|
|
20
|
+
import { type AnyToolModule } from "./types.js";
|
|
21
|
+
/**
|
|
22
|
+
* All 10 vibe-hero MCP tools, in contract order. `index.ts` registers each one
|
|
23
|
+
* (wrapping the handler with the setup gate). This list is the single source of
|
|
24
|
+
* which tools exist.
|
|
25
|
+
*/
|
|
26
|
+
export declare const TOOL_REGISTRY: readonly AnyToolModule[];
|
|
27
|
+
//# sourceMappingURL=placeholders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"placeholders.d.ts","sourceRoot":"","sources":["../../src/tools/placeholders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAUhD;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,aAAa,EAejD,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Tool registry (T020).
|
|
3
|
+
*
|
|
4
|
+
* This wires all 10 MCP tools into a single {@link TOOL_REGISTRY} that
|
|
5
|
+
* `index.ts` iterates over to register them. Each module carries its real input
|
|
6
|
+
* schema (from `schemas/tools.ts`) so the host-facing tool signatures are
|
|
7
|
+
* correct. `save_config` / `get_config` are the real US-0 implementations
|
|
8
|
+
* (`./config.js`, T022); `get_status` / `list_topics` / `get_guidance` are the
|
|
9
|
+
* real US-2 read tools (`./status.js`, `./listTopics.js`, `./guidance.js`,
|
|
10
|
+
* T025–T027); `start_quiz` / `submit_answer` are the real US-1 core-loop tools
|
|
11
|
+
* (`./startQuiz.js`, `./submitAnswer.js`, T031/T032); `record_observation` /
|
|
12
|
+
* `get_offer` / `record_offer_response` are the real US-1 offer tools
|
|
13
|
+
* (`./recordObservation.js`, `./offers.js`, T034/T036). All 10 are now real —
|
|
14
|
+
* no placeholders remain.
|
|
15
|
+
*
|
|
16
|
+
* The registration architecture and gate wiring delivered here stay unchanged.
|
|
17
|
+
*
|
|
18
|
+
* Source of truth: specs/001-vibe-hero-mvp/contracts/mcp-tools.md.
|
|
19
|
+
*/
|
|
20
|
+
import { saveConfigTool, getConfigTool } from "./config.js";
|
|
21
|
+
import { getStatusTool } from "./status.js";
|
|
22
|
+
import { listTopicsTool } from "./listTopics.js";
|
|
23
|
+
import { getGuidanceTool } from "./guidance.js";
|
|
24
|
+
import { startQuizTool } from "./startQuiz.js";
|
|
25
|
+
import { submitAnswerTool } from "./submitAnswer.js";
|
|
26
|
+
import { recordObservationTool } from "./recordObservation.js";
|
|
27
|
+
import { getOfferTool, recordOfferResponseTool } from "./offers.js";
|
|
28
|
+
/**
|
|
29
|
+
* All 10 vibe-hero MCP tools, in contract order. `index.ts` registers each one
|
|
30
|
+
* (wrapping the handler with the setup gate). This list is the single source of
|
|
31
|
+
* which tools exist.
|
|
32
|
+
*/
|
|
33
|
+
export const TOOL_REGISTRY = [
|
|
34
|
+
// Real US-2 read tools (T025–T027).
|
|
35
|
+
getStatusTool,
|
|
36
|
+
listTopicsTool,
|
|
37
|
+
getGuidanceTool,
|
|
38
|
+
// Real US-1 core-loop tools (T031/T032).
|
|
39
|
+
startQuizTool,
|
|
40
|
+
submitAnswerTool,
|
|
41
|
+
// Real US-0 implementations (T022).
|
|
42
|
+
saveConfigTool,
|
|
43
|
+
getConfigTool,
|
|
44
|
+
// Real US-1 observation/offer tools (T034/T036).
|
|
45
|
+
recordObservationTool,
|
|
46
|
+
getOfferTool,
|
|
47
|
+
recordOfferResponseTool,
|
|
48
|
+
];
|
|
49
|
+
//# sourceMappingURL=placeholders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"placeholders.js","sourceRoot":"","sources":["../../src/tools/placeholders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEpE;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAA6B;IACrD,oCAAoC;IACpC,aAAa;IACb,cAAc;IACd,eAAe;IACf,yCAAyC;IACzC,aAAa;IACb,gBAAgB;IAChB,oCAAoC;IACpC,cAAc;IACd,aAAa;IACb,iDAAiD;IACjD,qBAAqB;IACrB,YAAY;IACZ,uBAAuB;CACxB,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Real `record_observation` tool module (T034, US-1).
|
|
3
|
+
*
|
|
4
|
+
* Intake for the observation source (the real-time Claude Code hook in v1). It
|
|
5
|
+
* accepts derived, privacy-safe signals (`toolName` / `mcpTool` / `success` /
|
|
6
|
+
* `toolUseId`) plus a `sessionId`, matches them to candidate topic keys against
|
|
7
|
+
* the catalog's {@link TriggerSignal}s, accumulates the candidates into the
|
|
8
|
+
* per-session {@link OfferLedger}, and returns `{ offerCandidates }`.
|
|
9
|
+
*
|
|
10
|
+
* Trigger-only — AWARDS NOTHING (FR-005 / SC-003). It deliberately touches ONLY
|
|
11
|
+
* the profile's `offers` ledger (its `sessionId` / candidate accounting). It
|
|
12
|
+
* never reads or writes `abilities`, `graduations`, `quizHistory`, or
|
|
13
|
+
* `reviewSchedule`, so observed usage with no answered quiz produces exactly 0
|
|
14
|
+
* change to points or graduation state (SC-003) — the integration test asserts
|
|
15
|
+
* those fields are byte-identical before/after.
|
|
16
|
+
*
|
|
17
|
+
* v1 scope note: a SINGLE real-time hook source feeds this tool. FR-017's
|
|
18
|
+
* two-source (hook + transcript backfill) correlation by `tool_use_id` is
|
|
19
|
+
* architecture-ready (signals carry `toolUseId`; the {@link ObservationSource}
|
|
20
|
+
* seam exists) but is NOT built here, per tasks.md.
|
|
21
|
+
*
|
|
22
|
+
* Gated (FR-032): NOT exempt — `index.ts`/`withSetupGate` returns SETUP_REQUIRED
|
|
23
|
+
* before this handler runs when `profile.config` is absent. The handler assumes
|
|
24
|
+
* a configured profile and reads `config.proactiveOffers` / `config.offerCadence`
|
|
25
|
+
* only to short-circuit candidate work when offers are disabled (so a disabled
|
|
26
|
+
* user never accrues candidate state needlessly).
|
|
27
|
+
*
|
|
28
|
+
* Exposed as a `(dirOverride, catalogLoader)` factory mirroring
|
|
29
|
+
* `startQuiz.ts` / `status.ts`: the registry uses the default instance
|
|
30
|
+
* (env / `~/.vibe-hero` + bundled catalog); tests inject a temp home + fixture
|
|
31
|
+
* catalog.
|
|
32
|
+
*
|
|
33
|
+
* Source of truth: specs/001-vibe-hero-mvp/contracts/mcp-tools.md
|
|
34
|
+
* (`record_observation`), spec.md FR-005 / FR-015..017 / FR-018 / SC-003,
|
|
35
|
+
* data-model.md (§ OfferLedger).
|
|
36
|
+
*/
|
|
37
|
+
import type { CatalogLoadResult } from "../catalog/loader.js";
|
|
38
|
+
import { type AnyToolModule } from "./types.js";
|
|
39
|
+
/** Catalog source override (test seam); defaults to the bundled snapshot. */
|
|
40
|
+
export type CatalogLoader = () => CatalogLoadResult;
|
|
41
|
+
/**
|
|
42
|
+
* Build the `record_observation` tool module (US-1).
|
|
43
|
+
*
|
|
44
|
+
* @param dirOverride - Profile-directory override (test seam); see `profileDir`.
|
|
45
|
+
* @param catalogLoader - Catalog source override (test seam); defaults to the
|
|
46
|
+
* bundled snapshot {@link loadBundledCatalog}.
|
|
47
|
+
* @returns The erased registry entry for `record_observation`.
|
|
48
|
+
*/
|
|
49
|
+
export declare const makeRecordObservationTool: (dirOverride?: string, catalogLoader?: CatalogLoader) => AnyToolModule;
|
|
50
|
+
/** Default `record_observation` module (env / `~/.vibe-hero` + bundled catalog). */
|
|
51
|
+
export declare const recordObservationTool: AnyToolModule;
|
|
52
|
+
//# sourceMappingURL=recordObservation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recordObservation.d.ts","sourceRoot":"","sources":["../../src/tools/recordObservation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAe9D,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D,6EAA6E;AAC7E,MAAM,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAAC;AAEpD;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,GACpC,cAAc,MAAM,EACpB,gBAAe,aAAkC,KAChD,aAuDC,CAAC;AAEL,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,EAAE,aAA2C,CAAC"}
|