@davesheffer/hunch 1.38.0 → 1.39.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 (48) hide show
  1. package/dist/cli/index.js +255 -9
  2. package/dist/cli/serve.js +1 -0
  3. package/dist/client/readOrCompute.d.ts +77 -0
  4. package/dist/client/readOrCompute.js +85 -0
  5. package/dist/client/state.d.ts +1 -0
  6. package/dist/client/state.js +1 -0
  7. package/dist/constitution/g2.d.ts +1 -0
  8. package/dist/constitution/service.js +8 -0
  9. package/dist/constitution/sourceMutation.js +23 -18
  10. package/dist/core/config.d.ts +16 -0
  11. package/dist/core/config.js +13 -0
  12. package/dist/core/machine.d.ts +20 -0
  13. package/dist/core/machine.js +101 -0
  14. package/dist/core/taskRecord.js +6 -3
  15. package/dist/core/taskReport.d.ts +25 -2
  16. package/dist/core/taskReport.js +92 -18
  17. package/dist/core/taskReportEvidence.d.ts +4 -1
  18. package/dist/core/taskReportEvidence.js +5 -2
  19. package/dist/core/taskReportHook.d.ts +16 -3
  20. package/dist/core/taskReportHook.js +65 -13
  21. package/dist/core/taskTouched.d.ts +4 -0
  22. package/dist/core/taskTouched.js +30 -10
  23. package/dist/core/types.d.ts +66 -1
  24. package/dist/core/types.js +3 -0
  25. package/dist/core/workspace.d.ts +234 -0
  26. package/dist/core/workspace.js +335 -0
  27. package/dist/extractors/helm.d.ts +17 -28
  28. package/dist/extractors/helm.js +12 -12
  29. package/dist/extractors/indexer.js +171 -7
  30. package/dist/extractors/k8sManifest.d.ts +59 -0
  31. package/dist/extractors/k8sManifest.js +507 -0
  32. package/dist/extractors/workspaces.d.ts +18 -0
  33. package/dist/extractors/workspaces.js +350 -0
  34. package/dist/integrations/claudemd.js +1 -0
  35. package/dist/integrations/hooks.d.ts +2 -0
  36. package/dist/integrations/hooks.js +25 -0
  37. package/dist/integrations/scaffold.js +11 -0
  38. package/dist/integrations/workspaceLedger.d.ts +73 -0
  39. package/dist/integrations/workspaceLedger.js +201 -0
  40. package/dist/mcp/server.js +54 -0
  41. package/dist/mcp/taskReportTools.d.ts +9 -0
  42. package/dist/mcp/taskReportTools.js +13 -5
  43. package/dist/serve/app.d.ts +2 -0
  44. package/dist/serve/app.js +107 -92
  45. package/dist/serve/mcpHttp.d.ts +27 -0
  46. package/dist/serve/mcpHttp.js +95 -0
  47. package/package.json +1 -1
  48. package/server.json +2 -2
@@ -0,0 +1,95 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ // The web-standard transport, not the Node one: the Node wrapper imports @hono/node-server,
3
+ // which the production dependency audit keeps unreachable (tooling/production-dependency-audit.mjs).
4
+ // Bridging IncomingMessage → Request → ServerResponse here is a few lines and keeps that boundary.
5
+ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
6
+ import { CaptureBatchRequestSchema, CaptureRequestSchema, ReadRequestSchema, RecordsRequestSchema, ScopeSchema, SubscribeRequestSchema, WriteRequestSchema } from "../core/stateContract.js";
7
+ export const MCP_PATH = "/nuryel/v1/mcp";
8
+ const TOOLS = [
9
+ {
10
+ name: "nuryel_capabilities", route: "capabilities",
11
+ title: "nuryel.state/1 — what this state server supports",
12
+ description: "Negotiate before depending on anything: the contract version, capability list (verbs + record schemas) and your principal's grants. Optional scope {kind, id} selects a granted partition; default is your first grant.",
13
+ input: { scope: ScopeSchema.optional() },
14
+ },
15
+ {
16
+ name: "nuryel_read", route: "read",
17
+ title: "nuryel.state/1 read — the system-of-record answer for a subject",
18
+ description: "Read current state for a scope under a delivery receipt. Pass a subject (an entity id, a decision topic, or an external object_type:object_key such as transaction:TX-1) to get state_of_record — what is current, in force, done, what it depends on and what invalidates it — with the referenced records. Read before redoing work: if a current record already answers the subject and nothing it depends on changed, reuse it. Your identity comes from your credential; scopes outside your grants are named in denied_scopes.",
19
+ input: ReadRequestSchema.omit({ schema: true, principal: true }).shape,
20
+ },
21
+ {
22
+ name: "nuryel_write", route: "write",
23
+ title: "nuryel.state/1 write — provenance + idempotency in, durability out",
24
+ description: "Write one record into a facet (receipts, commitments, derived, entities, relationships, decisions, constraints, bugs, findings). The record must carry provenance and the request an idempotency_key: replaying the same request returns the original; the same key with a different payload is refused and the refusal names what differs. Ids are derived from the record's facts. A second live decision on a topic is refused with the incumbent named; pass supersedes to replace it explicitly. The result reports durability (local, committed or pushed).",
25
+ input: WriteRequestSchema.omit({ schema: true, principal: true }).shape,
26
+ },
27
+ {
28
+ name: "nuryel_capture", route: "capture",
29
+ title: "nuryel.state/1 capture — one relevant assertion with exact source excerpts",
30
+ description: "Save ONE relevant atomic assertion learned during the task, with a concrete future-use reason and exact excerpts from the source text. Whole source text is never stored. Saved observations are not verified current summaries or receipts.",
31
+ input: CaptureRequestSchema.omit({ schema: true, principal: true }).shape,
32
+ },
33
+ {
34
+ name: "nuryel_capture_batch", route: "capture-batch",
35
+ title: "nuryel.state/1 capture batch — save relevant atomic observations",
36
+ description: "Save up to 32 independent assertions from up to 8 sources in one call, each with exact supporting excerpts and a future-use reason. Results preserve input indexes; inspect every refusal.",
37
+ input: CaptureBatchRequestSchema.omit({ schema: true, principal: true }).shape,
38
+ },
39
+ {
40
+ name: "nuryel_subscribe", route: "subscribe",
41
+ title: "nuryel.state/1 subscribe — the scope's ordered change stream after a cursor",
42
+ description: "Return change events for a scope with seq > after_seq, strictly ordered. Keep head_seq as your next cursor; if resync is true, rebuild held state with reads.",
43
+ input: SubscribeRequestSchema.omit({ schema: true, principal: true }).shape,
44
+ },
45
+ {
46
+ name: "nuryel_records", route: "records",
47
+ title: "nuryel.state/1 records — fetch records by id, grants first",
48
+ description: "Fetch state records by id (from a subscribe event, a read ref or a write result). Every id is accounted for: found, denied (outside your grants) or missing.",
49
+ input: RecordsRequestSchema.omit({ schema: true, principal: true }).shape,
50
+ },
51
+ ];
52
+ function buildServer(dispatch, problemOf, version) {
53
+ const server = new McpServer({ name: "hunch-serve", version });
54
+ for (const tool of TOOLS) {
55
+ server.registerTool(tool.name, { title: tool.title, description: tool.description, inputSchema: tool.input }, async (input) => {
56
+ try {
57
+ const { payload } = await dispatch(tool.route, { ...input });
58
+ return { content: [{ type: "text", text: JSON.stringify(payload) }], structuredContent: payload };
59
+ }
60
+ catch (error) {
61
+ const p = problemOf(error);
62
+ return { isError: true, content: [{ type: "text", text: `nuryel.state/1 refused [${p.title}] (${p.status}): ${p.detail}` }], structuredContent: p };
63
+ }
64
+ });
65
+ }
66
+ return server;
67
+ }
68
+ /** Serve one authenticated MCP POST. The caller has already authenticated and parsed the body. */
69
+ export async function handleMcpRequest(req, res, body, dispatch, problemOf, version) {
70
+ const server = buildServer(dispatch, problemOf, version);
71
+ const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true });
72
+ try {
73
+ await server.connect(transport);
74
+ // Only the headers the transport negotiates on travel; the body was already read and bounded.
75
+ const headers = new Headers();
76
+ for (const name of ["accept", "content-type", "mcp-protocol-version", "mcp-session-id", "last-event-id"]) {
77
+ const value = req.headers[name];
78
+ if (typeof value === "string")
79
+ headers.set(name, value);
80
+ }
81
+ const request = new Request(new URL(req.url ?? MCP_PATH, "http://127.0.0.1"), { method: "POST", headers, body: JSON.stringify(body) });
82
+ const response = await transport.handleRequest(request, { parsedBody: body });
83
+ const text = await response.text();
84
+ const out = { "cache-control": "no-store", "x-hunch-version": version, "content-length": String(Buffer.byteLength(text)) };
85
+ response.headers.forEach((value, name) => { if (name !== "content-length")
86
+ out[name] = value; });
87
+ res.writeHead(response.status, out);
88
+ res.end(text);
89
+ }
90
+ finally {
91
+ await transport.close();
92
+ await server.close();
93
+ }
94
+ }
95
+ //# sourceMappingURL=mcpHttp.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@davesheffer/hunch",
3
- "version": "1.38.0",
3
+ "version": "1.39.0",
4
4
  "mcpName": "io.github.davesheffer/hunch",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Dave Sheffer <dave.sheffer1@gmail.com>",
package/server.json CHANGED
@@ -7,13 +7,13 @@
7
7
  "source": "github"
8
8
  },
9
9
  "websiteUrl": "https://www.hunchmemory.com",
10
- "version": "1.38.0",
10
+ "version": "1.39.0",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "registryBaseUrl": "https://registry.npmjs.org",
15
15
  "identifier": "@davesheffer/hunch",
16
- "version": "1.38.0",
16
+ "version": "1.39.0",
17
17
  "runtimeHint": "npx",
18
18
  "packageArguments": [
19
19
  {