knosky 0.6.3 → 0.7.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 (64) hide show
  1. package/CHANGELOG.md +149 -93
  2. package/CREDITS.md +14 -14
  3. package/LICENSE.md +76 -76
  4. package/LIMITATIONS.md +33 -23
  5. package/PRIVACY.md +30 -30
  6. package/README.md +170 -117
  7. package/SECURITY.md +78 -46
  8. package/action/post-comment.mjs +94 -89
  9. package/action.yml +62 -62
  10. package/bin/knosky.mjs +279 -105
  11. package/core/CONTRACT.md +70 -70
  12. package/core/append-only-checkpoint.mjs +215 -0
  13. package/core/audit-writer.mjs +317 -0
  14. package/core/benchmark-results.mjs +225 -225
  15. package/core/bundle.mjs +178 -178
  16. package/core/churn.mjs +23 -23
  17. package/core/ci.mjs +268 -268
  18. package/core/comparison.mjs +189 -189
  19. package/core/config.mjs +189 -189
  20. package/core/constants.mjs +13 -13
  21. package/core/contract.mjs +123 -123
  22. package/core/cross-repo.mjs +111 -111
  23. package/core/decision-codes.mjs +92 -0
  24. package/core/destination.mjs +161 -161
  25. package/core/district-classification.mjs +111 -0
  26. package/core/doctor-scorecard.mjs +369 -0
  27. package/core/domain-store.mjs +347 -0
  28. package/core/edges.mjs +43 -43
  29. package/core/escalate.mjs +68 -68
  30. package/core/freshness.mjs +198 -194
  31. package/core/fs-indexer.mjs +218 -218
  32. package/core/key-store.mjs +348 -348
  33. package/core/layout.mjs +46 -46
  34. package/core/ledger.mjs +176 -141
  35. package/core/local-ipc-identity.mjs +500 -0
  36. package/core/lod.mjs +155 -155
  37. package/core/mode-b.mjs +410 -0
  38. package/core/multi-model-benchmark.mjs +405 -405
  39. package/core/net-lockdown.mjs +421 -0
  40. package/core/onboarding.mjs +223 -223
  41. package/core/operator-auth.mjs +317 -0
  42. package/core/overlays.mjs +45 -45
  43. package/core/policy-lattice.mjs +142 -0
  44. package/core/pr-comment.mjs +198 -198
  45. package/core/protocol-spec.mjs +460 -460
  46. package/core/provenance.mjs +320 -0
  47. package/core/retrieve.mjs +63 -63
  48. package/core/route.mjs +304 -304
  49. package/core/schema.mjs +275 -275
  50. package/core/signing-tiers.mjs +1265 -0
  51. package/core/swarm-bench.mjs +106 -0
  52. package/core/swarm-coordinator.mjs +867 -0
  53. package/core/trust-root-rekey.mjs +410 -0
  54. package/mcp/server.mjs +264 -108
  55. package/package.json +56 -46
  56. package/renderer/art/kenney/buildingTiles_sheet.xml +130 -130
  57. package/renderer/art/kenney/cityDetails_sheet.xml +12 -12
  58. package/renderer/art/kenney/landscapeTiles_sheet.xml +129 -129
  59. package/renderer/art/kenney/sheet_allCars.xml +545 -545
  60. package/renderer/build-rich.mjs +43 -43
  61. package/renderer/city.template.html +808 -808
  62. package/ssot/decision-codes.json +133 -0
  63. package/ssot/ladder-l0-l3.md +232 -0
  64. package/ssot/tool-menu.json +130 -0
package/mcp/server.mjs CHANGED
@@ -1,108 +1,264 @@
1
- // Knowledge City — local stdio MCP server (Phase 2a).
2
- // Read-only retrieval over a local contract-v2 index. No network; data never leaves the machine.
3
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
- import { z } from "zod";
6
- import { load, search, getNode, listCategories, getProvenance, getRelated } from "../core/retrieve.mjs";
7
- import { kcRoute } from "../core/route.mjs";
8
-
9
- const CITY = process.env.KC_CITY || process.argv[2];
10
- if (!CITY) { console.error("Knowledge City MCP: set KC_CITY (or pass a path) to a city-data.v2.json"); process.exit(1); }
11
- const ctx = load(CITY);
12
-
13
- const server = new McpServer({ name: "knowledge-city", version: "0.4.1" });
14
-
15
- server.registerTool("kc_search", {
16
- title: "Search the Knowledge City",
17
- description: "Search the indexed knowledge base by keywords. Returns ranked items (title, summary, category) each with a provenance citation (source path + revision) that links back to the live file. Use for 'where does X live / what was decided about Y / how does this connect'. Navigation, not full-text code search.",
18
- inputSchema: { query: z.string().max(500).describe("keywords"), limit: z.number().int().min(1).max(50).optional().describe("max results (default 10, max 50)"), category: z.string().max(200).optional().describe("restrict to one category id") },
19
- annotations: { readOnlyHint: true, openWorldHint: false },
20
- }, async ({ query, limit, category }) => {
21
- const hits = search(ctx, String(query).slice(0, 500), { limit: Math.min(Math.max(1, limit || 10), 50), category: category ? String(category).slice(0, 200) : null });
22
- const text = hits.length
23
- ? hits.map(h => `- [${h.category}] ${h.title} — ${h.summary || ""}\n source: ${h.provenance.ref} @ ${h.provenance.source_rev} (id: ${h.id})`).join("\n")
24
- : "No matches.";
25
- return { content: [{ type: "text", text }], structuredContent: { results: hits } };
26
- });
27
-
28
- server.registerTool("kc_get_node", {
29
- title: "Get one item",
30
- description: "Fetch a single indexed item by id (title, summary, category, kind) with its provenance citation.",
31
- inputSchema: { id: z.string().max(400).describe("node id, e.g. fs:src/index.ts") },
32
- annotations: { readOnlyHint: true },
33
- }, async ({ id }) => {
34
- const n = getNode(ctx, id);
35
- return { content: [{ type: "text", text: n ? JSON.stringify(n, null, 2) : `No item with id ${id}` }], structuredContent: n || {} };
36
- });
37
-
38
- server.registerTool("kc_list_categories", {
39
- title: "List categories",
40
- description: "List the knowledge categories (city districts) with item counts.",
41
- inputSchema: {},
42
- annotations: { readOnlyHint: true },
43
- }, async () => {
44
- const cats = listCategories(ctx);
45
- return { content: [{ type: "text", text: cats.map(c => `${c.label} (${c.id}): ${c.count}`).join("\n") }], structuredContent: { categories: cats } };
46
- });
47
-
48
- server.registerTool("kc_get_provenance", {
49
- title: "Get provenance (citation)",
50
- description: "Get the citation for an item: the live source ref + revision, plus its links to related items.",
51
- inputSchema: { id: z.string().max(400) },
52
- annotations: { readOnlyHint: true },
53
- }, async ({ id }) => {
54
- const p = getProvenance(ctx, id);
55
- return { content: [{ type: "text", text: p ? JSON.stringify(p, null, 2) : `No item with id ${id}` }], structuredContent: p || {} };
56
- });
57
-
58
- server.registerTool("kc_related", {
59
- title: "Related files (connections)",
60
- description: "How a file connects to others in this project: which files it imports (out), which files import it (in), and its recent-change (churn) signal. File-level structure with citations, not code analysis.",
61
- inputSchema: { id: z.string().max(400).describe("node id, e.g. fs:src/auth.js") },
62
- annotations: { readOnlyHint: true },
63
- }, async ({ id }) => {
64
- const r = getRelated(ctx, id);
65
- if (!r) return { content: [{ type: "text", text: "No item with id " + id }], structuredContent: {} };
66
- const NL = String.fromCharCode(10);
67
- const lines = [r.title + " (" + id + ")"];
68
- if (r.churn) lines.push("recent changes: " + r.churn.c + " commit(s), heat " + r.churn.b);
69
- lines.push("imports (" + r.imports.length + "): " + (r.imports.map(x => x.source || x.id).join(", ") || "none"));
70
- lines.push("imported by (" + r.importedBy.length + "): " + (r.importedBy.map(x => x.source || x.id).join(", ") || "none"));
71
- return { content: [{ type: "text", text: lines.join(NL) }], structuredContent: r };
72
- });
73
-
74
- server.registerTool("kc_route", {
75
- title: "Route to a destination (agent GPS)",
76
- description: "ADVISORY, metadata-only route through the repo towards a destination. Returns where-to-look-first (ranked waypoints), alternates, related tests, related docs, caveats, and a confidence score. Does NOT read or analyse code — structural navigation only, local, no network. Verify findings before acting.",
77
- inputSchema: {
78
- destination: z.string().max(400).describe("navigation target — e.g. file:src/auth.js, folder:src/auth, or keywords"),
79
- limit: z.number().int().min(1).max(20).optional().describe("max route entries (default 8, max 20)"),
80
- },
81
- annotations: { readOnlyHint: true, openWorldHint: false },
82
- }, async ({ destination, limit }) => {
83
- const doc = kcRoute(ctx, String(destination).slice(0, 400), { limit: limit ? Math.min(Math.max(1, limit), 20) : 8 });
84
- const lines = [
85
- `Route to: ${doc.destination} (confidence ${(doc.confidence * 100).toFixed(0)}%)`,
86
- '',
87
- 'Route:',
88
- ...(doc.route.map((e, i) => ` ${i + 1}. ${e.path} [${e.reason}]`)),
89
- ];
90
- if (doc.alternates && doc.alternates.length > 0) {
91
- lines.push('', 'Alternates:');
92
- for (const e of doc.alternates) lines.push(` - ${e.path} [${e.reason}]`);
93
- }
94
- if (doc.tests && doc.tests.length > 0) {
95
- lines.push('', 'Tests: ' + doc.tests.map(e => e.path).join(', '));
96
- }
97
- if (doc.docs && doc.docs.length > 0) {
98
- lines.push('', 'Docs: ' + doc.docs.map(e => e.path).join(', '));
99
- }
100
- if (doc.caveats && doc.caveats.length > 0) {
101
- lines.push('', 'Caveats:');
102
- for (const c of doc.caveats) lines.push(` ⚠ ${c}`);
103
- }
104
- return { content: [{ type: "text", text: lines.join('\n') }], structuredContent: doc };
105
- });
106
-
107
- await server.connect(new StdioServerTransport());
108
- console.error(`knowledge-city MCP ready — ${ctx.city.node_count} nodes, ${(ctx.city.categories||[]).length} categories`);
1
+ // Knowledge City — local stdio MCP server (Mode B + DEC-108 menu freeze).
2
+ // Read-mostly local index. No network; data never leaves the machine by default.
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { z } from "zod";
6
+ import { load, search, getNode, listCategories, getProvenance, getRelated } from "../core/retrieve.mjs";
7
+ import { createModeBDoor } from "../core/mode-b.mjs";
8
+ import { queryAudit, verifyAuditChain } from "../core/audit-writer.mjs";
9
+ import { closedSet } from "../core/decision-codes.mjs";
10
+ import { readFileSync } from "node:fs";
11
+ import { dirname, join } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+
14
+ const CITY = process.env.KC_CITY || process.argv[2];
15
+ if (!CITY) {
16
+ console.error("Knowledge City MCP: set KC_CITY (or pass a path) to a city-data.v2.json");
17
+ process.exit(1);
18
+ }
19
+
20
+ const PROFILE = (process.env.KC_PROFILE || "coding").toLowerCase(); // coding | security | advisory
21
+ const DOMAIN = process.env.KC_DOMAIN || undefined;
22
+
23
+ let ctx;
24
+ try {
25
+ ctx = load(CITY);
26
+ } catch (err) {
27
+ console.error("Knowledge City MCP: failed to load city:", err && err.message ? err.message : err);
28
+ process.exit(1);
29
+ }
30
+
31
+ const door = createModeBDoor({
32
+ cityCtx: ctx,
33
+ cityPath: CITY,
34
+ domainRoot: DOMAIN,
35
+ profile: PROFILE,
36
+ });
37
+
38
+ const HERE = dirname(fileURLToPath(import.meta.url));
39
+ const SSOT_MENU = join(HERE, "..", "ssot", "tool-menu.json");
40
+ let menuVersion = "1.0.0";
41
+ try {
42
+ menuVersion = JSON.parse(readFileSync(SSOT_MENU, "utf8")).version || menuVersion;
43
+ } catch {
44
+ /* optional */
45
+ }
46
+
47
+ const server = new McpServer({ name: "knosky", version: "0.7.0-modeb" });
48
+
49
+ function textResult(obj) {
50
+ const text = typeof obj === "string" ? obj : JSON.stringify(obj, null, 2);
51
+ return { content: [{ type: "text", text }], structuredContent: typeof obj === "object" ? obj : { text } };
52
+ }
53
+
54
+ function modeALabel() {
55
+ return PROFILE === "advisory"
56
+ ? " [Mode A advisory — non-authorizing]"
57
+ : " [Tier 0 map — non-authorizing unless combined with Mode B tools]";
58
+ }
59
+
60
+ // --- Tier 0 map tools (non-authorizing) ---
61
+
62
+ server.registerTool("kc_search", {
63
+ title: "Search the Knowledge City",
64
+ description:
65
+ "Search the indexed knowledge base by keywords. Returns ranked items with provenance citations." +
66
+ modeALabel() +
67
+ " Navigation, not full-text code search. Not a Mode B authorization.",
68
+ inputSchema: {
69
+ query: z.string().max(500).describe("keywords"),
70
+ limit: z.number().int().min(1).max(50).optional().describe("max results (default 10, max 50)"),
71
+ category: z.string().max(200).optional().describe("restrict to one category id"),
72
+ },
73
+ annotations: { readOnlyHint: true, openWorldHint: false },
74
+ }, async ({ query, limit, category }) => {
75
+ const hits = search(ctx, String(query).slice(0, 500), {
76
+ limit: Math.min(Math.max(1, limit || 10), 50),
77
+ category: category ? String(category).slice(0, 200) : null,
78
+ });
79
+ const text = hits.length
80
+ ? hits
81
+ .map(
82
+ (h) =>
83
+ `- [${h.category}] ${h.title} ${h.summary || ""}\n source: ${h.provenance.ref} @ ${h.provenance.source_rev} (id: ${h.id})`,
84
+ )
85
+ .join("\n")
86
+ : "No matches.";
87
+ return {
88
+ content: [{ type: "text", text: text + "\n\n(decision_code hint: ADVISORY_UNAUTH / non-authorizing map)" }],
89
+ structuredContent: { results: hits, decision_code: "ADVISORY_UNAUTH", mode: "A", authorizing: false },
90
+ };
91
+ });
92
+
93
+ server.registerTool("kc_get_node", {
94
+ title: "Get one item",
95
+ description: "Fetch a single indexed item by id with provenance." + modeALabel(),
96
+ inputSchema: { id: z.string().max(400).describe("node id, e.g. fs:src/index.ts") },
97
+ annotations: { readOnlyHint: true },
98
+ }, async ({ id }) => {
99
+ const n = getNode(ctx, id);
100
+ return textResult(
101
+ n
102
+ ? { ...n, decision_code: "ADVISORY_UNAUTH", mode: "A", authorizing: false }
103
+ : { error: `No item with id ${id}`, decision_code: "ERROR_INVALID_INPUT" },
104
+ );
105
+ });
106
+
107
+ server.registerTool("kc_list_categories", {
108
+ title: "List categories",
109
+ description: "List knowledge categories (city districts) with item counts." + modeALabel(),
110
+ inputSchema: {},
111
+ annotations: { readOnlyHint: true },
112
+ }, async () => {
113
+ const cats = listCategories(ctx);
114
+ return {
115
+ content: [{ type: "text", text: cats.map((c) => `${c.label} (${c.id}): ${c.count}`).join("\n") }],
116
+ structuredContent: { categories: cats, decision_code: "ADVISORY_UNAUTH", mode: "A", authorizing: false },
117
+ };
118
+ });
119
+
120
+ server.registerTool("kc_get_provenance", {
121
+ title: "Get provenance (citation)",
122
+ description: "Citation for an item: live source ref + revision + links." + modeALabel(),
123
+ inputSchema: { id: z.string().max(400) },
124
+ annotations: { readOnlyHint: true },
125
+ }, async ({ id }) => {
126
+ const p = getProvenance(ctx, id);
127
+ return textResult(
128
+ p
129
+ ? { ...p, decision_code: "ADVISORY_UNAUTH", mode: "A", authorizing: false }
130
+ : { error: `No item with id ${id}`, decision_code: "ERROR_INVALID_INPUT" },
131
+ );
132
+ });
133
+
134
+ server.registerTool("kc_related", {
135
+ title: "Related files (connections)",
136
+ description: "Imports / importers / churn for a file." + modeALabel(),
137
+ inputSchema: { id: z.string().max(400).describe("node id, e.g. fs:src/auth.js") },
138
+ annotations: { readOnlyHint: true },
139
+ }, async ({ id }) => {
140
+ const r = getRelated(ctx, id);
141
+ if (!r) return textResult({ error: "No item with id " + id, decision_code: "ERROR_INVALID_INPUT" });
142
+ const NL = String.fromCharCode(10);
143
+ const lines = [r.title + " (" + id + ")"];
144
+ if (r.churn) lines.push("recent changes: " + r.churn.c + " commit(s), heat " + r.churn.b);
145
+ lines.push("imports (" + r.imports.length + "): " + (r.imports.map((x) => x.source || x.id).join(", ") || "none"));
146
+ lines.push(
147
+ "imported by (" + r.importedBy.length + "): " + (r.importedBy.map((x) => x.source || x.id).join(", ") || "none"),
148
+ );
149
+ return {
150
+ content: [{ type: "text", text: lines.join(NL) }],
151
+ structuredContent: { ...r, decision_code: "ADVISORY_UNAUTH", mode: "A", authorizing: false },
152
+ };
153
+ });
154
+
155
+ // --- Tier 1 governed tools ---
156
+
157
+ const leaseFields = {
158
+ leaseId: z.string().max(200).optional().describe("Server-issued lease id (Mode B). Required when KC_PROFILE!=advisory."),
159
+ agentId: z
160
+ .string()
161
+ .max(200)
162
+ .optional()
163
+ .describe("Optional agent id claim — NEVER trusted alone; must match lease record"),
164
+ advisory: z.boolean().optional().describe("Force Mode A ADVISORY_UNAUTH path"),
165
+ };
166
+
167
+ server.registerTool("kc_route", {
168
+ title: "Route to a destination (agent GPS)",
169
+ description:
170
+ "Mode B (default coding profile): identity + policy + audit receipt then ALLOW authorized subgraph or DENY_*. " +
171
+ "Mode A when profile=advisory or advisory=true: ADVISORY_UNAUTH labeled tips only. " +
172
+ "Structural navigation with citations; does not replace reading live files. Decision codes: " +
173
+ closedSet().join(", "),
174
+ inputSchema: {
175
+ destination: z.string().max(400).describe("navigation target — file:, folder:, or keywords"),
176
+ limit: z.number().int().min(1).max(20).optional().describe("max route entries (default 8)"),
177
+ ...leaseFields,
178
+ },
179
+ annotations: { readOnlyHint: true, openWorldHint: false },
180
+ }, async ({ destination, limit, leaseId, agentId, advisory }) => {
181
+ const env = door.handle({
182
+ tool: "route",
183
+ destination,
184
+ limit,
185
+ leaseId,
186
+ agentId: agentId ?? null,
187
+ advisory: !!advisory,
188
+ });
189
+ return textResult(env);
190
+ });
191
+
192
+ server.registerTool("kc_bundle", {
193
+ title: "Build intent bundle (share-safe pointers)",
194
+ description:
195
+ "Mode B: package a small pointer bundle (intent-manifest) under identity+policy+audit; fail-closed secret scan. " +
196
+ "Not available as authorizing in pure advisory profile.",
197
+ inputSchema: {
198
+ destination: z.string().max(400).optional().describe("optional GPS destination used to pick node ids"),
199
+ nodeIds: z.array(z.string().max(400)).optional().describe("explicit node ids to include"),
200
+ root: z.string().max(1000).optional().describe("repo root for secret scan / sha256"),
201
+ ...leaseFields,
202
+ },
203
+ annotations: { readOnlyHint: true, openWorldHint: false },
204
+ }, async ({ destination, nodeIds, root, leaseId, agentId, advisory }) => {
205
+ const env = door.handle({
206
+ tool: "bundle",
207
+ destination,
208
+ nodeIds,
209
+ root: root || process.env.KC_ROOT || null,
210
+ leaseId,
211
+ agentId: agentId ?? null,
212
+ advisory: !!advisory,
213
+ });
214
+ return textResult(env);
215
+ });
216
+
217
+ server.registerTool("kc_policy_check", {
218
+ title: "Dry-run policy check",
219
+ description: "Mode B: would this destination be allowed for the bound agent? Returns decision codes without full route body preference.",
220
+ inputSchema: {
221
+ destination: z.string().max(400),
222
+ ...leaseFields,
223
+ },
224
+ annotations: { readOnlyHint: true, openWorldHint: false },
225
+ }, async ({ destination, leaseId, agentId, advisory }) => {
226
+ const env = door.handle({
227
+ tool: "policy_check",
228
+ destination,
229
+ leaseId,
230
+ agentId: agentId ?? null,
231
+ advisory: !!advisory,
232
+ });
233
+ return textResult(env);
234
+ });
235
+
236
+ // --- Security profile only ---
237
+ if (PROFILE === "security") {
238
+ server.registerTool("kc_audit_query", {
239
+ title: "Query local audit receipts",
240
+ description: "Security profile only. Metadata-only audit events for this local trust domain.",
241
+ inputSchema: {
242
+ limit: z.number().int().min(1).max(200).optional(),
243
+ agent_id: z.string().max(200).optional(),
244
+ },
245
+ annotations: { readOnlyHint: true },
246
+ }, async ({ limit, agent_id }) => {
247
+ const rows = queryAudit(door.domainRoot, { limit, agent_id });
248
+ return textResult({ events: rows, domain: door.domainRoot });
249
+ });
250
+
251
+ server.registerTool("kc_audit_verify", {
252
+ title: "Verify audit chain",
253
+ description: "Security profile only. Verify hash chain of local audit ledger.",
254
+ inputSchema: {},
255
+ annotations: { readOnlyHint: true },
256
+ }, async () => {
257
+ return textResult(verifyAuditChain(door.domainRoot));
258
+ });
259
+ }
260
+
261
+ await server.connect(new StdioServerTransport());
262
+ console.error(
263
+ `knosky MCP ready — profile=${PROFILE} ssot=${menuVersion} nodes=${ctx.city.node_count} domain=${door.domainRoot}`,
264
+ );
package/package.json CHANGED
@@ -1,46 +1,56 @@
1
- {
2
- "name": "knosky",
3
- "version": "0.6.3",
4
- "description": "Turn any repo or docs folder into an explorable city, plus a local MCP that grounds your AI in your own source with citations. Local-first, free, $0 tokens.",
5
- "type": "module",
6
- "bin": {
7
- "knosky": "bin/knosky.mjs"
8
- },
9
- "files": [
10
- "bin",
11
- "core",
12
- "renderer",
13
- "action",
14
- "action.yml",
15
- "mcp/server.mjs",
16
- "README.md",
17
- "LICENSE.md",
18
- "SECURITY.md",
19
- "PRIVACY.md",
20
- "LIMITATIONS.md",
21
- "CREDITS.md",
22
- "CHANGELOG.md"
23
- ],
24
- "engines": {
25
- "node": ">=18"
26
- },
27
- "dependencies": {
28
- "@modelcontextprotocol/sdk": "^1.12.0",
29
- "zod": "^3.23.8"
30
- },
31
- "keywords": [
32
- "repo-map",
33
- "codebase-visualization",
34
- "mcp",
35
- "ai",
36
- "citations",
37
- "local-first",
38
- "developer-tools"
39
- ],
40
- "license": "SEE LICENSE IN LICENSE.md",
41
- "homepage": "https://knosky.com",
42
- "repository": {
43
- "type": "git",
44
- "url": "git+https://github.com/SathiaAI/knosky.git"
45
- }
46
- }
1
+ {
2
+ "name": "knosky",
3
+ "version": "0.7.0",
4
+ "description": "Local-first agentic GPS for repos: map + Mode B governed MCP routes (identity, policy, audit). Free protocol.",
5
+ "type": "module",
6
+ "bin": {
7
+ "knosky": "bin/knosky.mjs"
8
+ },
9
+ "files": [
10
+ "CHANGELOG.md",
11
+ "CREDITS.md",
12
+ "LICENSE.md",
13
+ "LIMITATIONS.md",
14
+ "PRIVACY.md",
15
+ "README.md",
16
+ "SECURITY.md",
17
+ "action",
18
+ "action.yml",
19
+ "bin",
20
+ "core",
21
+ "mcp/server.mjs",
22
+ "renderer",
23
+ "ssot"
24
+ ],
25
+ "engines": {
26
+ "node": ">=20"
27
+ },
28
+ "dependencies": {
29
+ "@modelcontextprotocol/sdk": "^1.12.0",
30
+ "zod": "^3.23.8",
31
+ "@simplewebauthn/server": "^13.3.2",
32
+ "otplib": "^13.4.1",
33
+ "@otplib/plugin-crypto-node": "^13.4.1",
34
+ "@levischuck/tiny-cbor": "^0.2.2",
35
+ "@peculiar/x509": "^1.14.3"
36
+ },
37
+ "keywords": [
38
+ "repo-map",
39
+ "codebase-visualization",
40
+ "mcp",
41
+ "ai",
42
+ "citations",
43
+ "local-first",
44
+ "developer-tools"
45
+ ],
46
+ "license": "SEE LICENSE IN LICENSE.md",
47
+ "homepage": "https://knosky.com",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/SathiaAI/knosky.git"
51
+ },
52
+ "devDependencies": {
53
+ "@peculiar/asn1-ecc": "^2.6.1",
54
+ "@peculiar/asn1-schema": "^2.6.0"
55
+ }
56
+ }