@atheory-ai/ce-plugin-sdk 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.
Files changed (46) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +86 -0
  3. package/dist/abi.d.ts +33 -0
  4. package/dist/abi.d.ts.map +1 -0
  5. package/dist/abi.js +162 -0
  6. package/dist/abi.js.map +7 -0
  7. package/dist/build/ce-abi.d.ts +7 -0
  8. package/dist/build/ce-abi.d.ts.map +1 -0
  9. package/dist/build/ce-abi.js +65 -0
  10. package/dist/build/ce-abi.js.map +7 -0
  11. package/dist/define.d.ts +18 -0
  12. package/dist/define.d.ts.map +1 -0
  13. package/dist/effects.d.ts +21 -0
  14. package/dist/effects.d.ts.map +1 -0
  15. package/dist/eslint/index.d.ts +29 -0
  16. package/dist/eslint/index.d.ts.map +1 -0
  17. package/dist/eslint/index.js +309 -0
  18. package/dist/eslint/index.js.map +7 -0
  19. package/dist/eslint/rules/concept-term-format.d.ts +4 -0
  20. package/dist/eslint/rules/concept-term-format.d.ts.map +1 -0
  21. package/dist/eslint/rules/extract-return-type.d.ts +4 -0
  22. package/dist/eslint/rules/extract-return-type.d.ts.map +1 -0
  23. package/dist/eslint/rules/id-helpers-required.d.ts +4 -0
  24. package/dist/eslint/rules/id-helpers-required.d.ts.map +1 -0
  25. package/dist/eslint/rules/no-node-apis.d.ts +4 -0
  26. package/dist/eslint/rules/no-node-apis.d.ts.map +1 -0
  27. package/dist/eslint/rules/pure-activate.d.ts +4 -0
  28. package/dist/eslint/rules/pure-activate.d.ts.map +1 -0
  29. package/dist/eslint/rules/tool-description-length.d.ts +4 -0
  30. package/dist/eslint/rules/tool-description-length.d.ts.map +1 -0
  31. package/dist/host.d.ts +13 -0
  32. package/dist/host.d.ts.map +1 -0
  33. package/dist/iir.d.ts +83 -0
  34. package/dist/iir.d.ts.map +1 -0
  35. package/dist/index.cjs +267 -0
  36. package/dist/index.cjs.map +7 -0
  37. package/dist/index.d.ts +9 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +244 -0
  40. package/dist/index.js.map +7 -0
  41. package/dist/tree.d.ts +27 -0
  42. package/dist/tree.d.ts.map +1 -0
  43. package/dist/types.d.ts +175 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/package.json +84 -0
  46. package/tsconfig.plugin.json +7 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ IIRTypeUnknown: () => IIRTypeUnknown,
24
+ childByField: () => childByField,
25
+ childrenByType: () => childrenByType,
26
+ classifyEffect: () => classifyEffect,
27
+ createSubstrateClient: () => createSubstrateClient,
28
+ definePlugin: () => definePlugin,
29
+ edgeID: () => edgeID,
30
+ emit: () => emit,
31
+ fieldText: () => fieldText,
32
+ firstByType: () => firstByType,
33
+ firstDescendantByType: () => firstDescendantByType,
34
+ getConfig: () => getConfig,
35
+ hasChildType: () => hasChildType,
36
+ log: () => log,
37
+ nodeID: () => nodeID,
38
+ setPluginDefinition: () => setPluginDefinition,
39
+ walk: () => walk,
40
+ walkTopLevel: () => walkTopLevel
41
+ });
42
+ module.exports = __toCommonJS(src_exports);
43
+
44
+ // src/define.ts
45
+ function definePlugin(definition) {
46
+ if (!definition.id || !definition.id.includes(".")) {
47
+ throw new Error(
48
+ `Plugin id must be reverse-domain format (e.g., "com.example.my-plugin"). Got: "${definition.id}"`
49
+ );
50
+ }
51
+ if (!definition.name) {
52
+ throw new Error("Plugin name is required");
53
+ }
54
+ if (!definition.version || !isValidSemver(definition.version)) {
55
+ throw new Error(
56
+ `Plugin version must be valid semver (e.g., "1.0.0"). Got: "${definition.version}"`
57
+ );
58
+ }
59
+ if (definition.language) {
60
+ if (typeof definition.language.match !== "function") {
61
+ throw new Error("language.match must be a function");
62
+ }
63
+ if (typeof definition.language.extract !== "function") {
64
+ throw new Error("language.extract must be a function");
65
+ }
66
+ if (definition.language.concepts) {
67
+ for (const seed of definition.language.concepts) {
68
+ if (seed.term !== seed.term.toLowerCase()) {
69
+ throw new Error(`Concept terms must be lowercase. Got: "${seed.term}"`);
70
+ }
71
+ if (!seed.term.match(/^[a-z][a-z0-9-]*$/)) {
72
+ throw new Error(
73
+ `Concept terms must be lowercase-hyphenated. Got: "${seed.term}"`
74
+ );
75
+ }
76
+ }
77
+ }
78
+ }
79
+ if (definition.tools) {
80
+ for (const tool of definition.tools) {
81
+ if (!tool.name) {
82
+ throw new Error("Tool name is required");
83
+ }
84
+ if (!tool.description) {
85
+ throw new Error(`Tool "${tool.name}" description is required`);
86
+ }
87
+ if (tool.description.length > 100) {
88
+ throw new Error(
89
+ `Tool "${tool.name}" description exceeds 100 characters (${tool.description.length}). The Strategizer receives this in its prompt \u2014 keep it concise.`
90
+ );
91
+ }
92
+ if (typeof tool.activate !== "function") {
93
+ throw new Error(`Tool "${tool.name}" activate must be a function`);
94
+ }
95
+ if (typeof tool.execute !== "function") {
96
+ throw new Error(`Tool "${tool.name}" execute must be a function`);
97
+ }
98
+ }
99
+ }
100
+ if (definition.analyzers) {
101
+ for (const analyzer of definition.analyzers) {
102
+ if (!analyzer.name) {
103
+ throw new Error("Analyzer name is required");
104
+ }
105
+ if (typeof analyzer.analyze !== "function") {
106
+ throw new Error(`Analyzer "${analyzer.name}" analyze must be a function`);
107
+ }
108
+ }
109
+ }
110
+ if (definition.iirRules) {
111
+ if (!Array.isArray(definition.iirRules.rules) || definition.iirRules.rules.length === 0) {
112
+ throw new Error("iirRules.rules must be a non-empty array");
113
+ }
114
+ for (const rule of definition.iirRules.rules) {
115
+ if (!rule.id) {
116
+ throw new Error("Each IIR rule requires an id");
117
+ }
118
+ if (!rule.target || !rule.severity) {
119
+ throw new Error(`IIR rule "${rule.id}" requires target and severity`);
120
+ }
121
+ }
122
+ }
123
+ setPluginDefinition(definition);
124
+ return definition;
125
+ }
126
+ function setPluginDefinition(definition) {
127
+ const globalScope = globalThis;
128
+ globalScope.__ce_plugin_definition = definition;
129
+ }
130
+ function isValidSemver(version) {
131
+ return /^\d+\.\d+\.\d+(-[\w.]+)?(\+[\w.]+)?$/.test(version);
132
+ }
133
+
134
+ // src/host.ts
135
+ var log = {
136
+ debug: (message) => __ce_log("debug", message),
137
+ info: (message) => __ce_log("info", message),
138
+ warn: (message) => __ce_log("warn", message),
139
+ error: (message) => __ce_log("error", message)
140
+ };
141
+ function emit(channel, content) {
142
+ __ce_emit(channel, content);
143
+ }
144
+ function createSubstrateClient() {
145
+ return {
146
+ query(q) {
147
+ const result = __ce_substrate_query(JSON.stringify(q));
148
+ return JSON.parse(result);
149
+ }
150
+ };
151
+ }
152
+ function getConfig(key) {
153
+ const result = __ce_get_config(key);
154
+ if (!result)
155
+ return void 0;
156
+ return JSON.parse(result);
157
+ }
158
+ function nodeID(projectID, type, canonicalID) {
159
+ return __ce_node_id(projectID, type, canonicalID);
160
+ }
161
+ function edgeID(sourceID, type, targetID) {
162
+ return __ce_edge_id(sourceID, type, targetID);
163
+ }
164
+
165
+ // src/tree.ts
166
+ function kids(node) {
167
+ return node.children ?? [];
168
+ }
169
+ function walk(node, visit) {
170
+ if (!node)
171
+ return;
172
+ const stack = [node];
173
+ while (stack.length > 0) {
174
+ const n = stack.pop();
175
+ visit(n);
176
+ const cs = kids(n);
177
+ for (let i = cs.length - 1; i >= 0; i--)
178
+ stack.push(cs[i]);
179
+ }
180
+ }
181
+ function walkTopLevel(node, visit) {
182
+ if (!node)
183
+ return;
184
+ const stack = [{ n: node, descend: true }];
185
+ while (stack.length > 0) {
186
+ const { n, descend } = stack.pop();
187
+ visit(n);
188
+ if (!descend)
189
+ continue;
190
+ const cs = kids(n);
191
+ for (let i = cs.length - 1; i >= 0; i--) {
192
+ stack.push({ n: cs[i], descend: !SCOPE_BOUNDARY.has(cs[i].type) });
193
+ }
194
+ }
195
+ }
196
+ var SCOPE_BOUNDARY = /* @__PURE__ */ new Set([
197
+ "function_declaration",
198
+ "function_expression",
199
+ "arrow_function",
200
+ "method_definition",
201
+ "generator_function",
202
+ "generator_function_declaration"
203
+ ]);
204
+ function childByField(node, field) {
205
+ for (const child of kids(node)) {
206
+ if (child.fieldName === field)
207
+ return child;
208
+ }
209
+ return null;
210
+ }
211
+ function childrenByType(node, type) {
212
+ return kids(node).filter((c) => c.type === type);
213
+ }
214
+ function firstByType(node, type) {
215
+ return kids(node).find((c) => c.type === type) ?? null;
216
+ }
217
+ function hasChildType(node, type) {
218
+ return kids(node).some((c) => c.type === type);
219
+ }
220
+ function fieldText(node, field) {
221
+ return childByField(node, field)?.text ?? "";
222
+ }
223
+ function firstDescendantByType(node, type) {
224
+ let found = null;
225
+ walk(node, (n) => {
226
+ if (found === null && n.type === type)
227
+ found = n;
228
+ });
229
+ return found;
230
+ }
231
+
232
+ // src/iir.ts
233
+ var IIRTypeUnknown = "unknown";
234
+
235
+ // src/effects.ts
236
+ var pathCategories = [
237
+ ["network", ["net/http", "net/url", "net", "google.golang.org/grpc", "github.com/gorilla/websocket"]],
238
+ ["db", ["database/sql", "gorm.io", "go.mongodb.org", "github.com/redis", "github.com/jmoiron/sqlx"]],
239
+ ["io", ["os", "io", "io/ioutil", "bufio", "path/filepath"]],
240
+ // fmt's pure formatters (Sprintf, Errorf) are excluded before classification;
241
+ // what reaches here (Println/Printf/Fprint…) writes output.
242
+ ["log", ["log", "log/slog", "fmt"]]
243
+ ];
244
+ var rootCategories = [
245
+ ["network", ["http", "https", "fetch", "axios", "request", "requests", "grpc", "ws", "socket", "urllib"]],
246
+ ["db", ["db", "sql", "database", "redis", "mongo", "session", "repository", "datastore"]],
247
+ ["io", ["os", "io", "fs", "ioutil", "file", "pathlib"]],
248
+ ["log", ["log", "logger", "logging", "console", "slog", "fmt"]]
249
+ ];
250
+ var mutationVerbs = ["track", "send", "emit", "publish", "save", "create", "update", "delete", "write"];
251
+ function classifyEffect(input) {
252
+ const path = (input.importPath ?? "").toLowerCase();
253
+ const root = (input.root ?? "").toLowerCase();
254
+ const method = (input.method ?? "").toLowerCase();
255
+ for (const [kind, prefixes] of pathCategories) {
256
+ if (prefixes.some((p) => path === p || path.startsWith(p + "/")))
257
+ return { kind, basis: "resolved" };
258
+ }
259
+ for (const [kind, roots] of rootCategories) {
260
+ if (roots.includes(root))
261
+ return { kind, basis: "resolved" };
262
+ }
263
+ if (mutationVerbs.some((v) => method.includes(v)))
264
+ return { kind: "mutation", basis: "heuristic" };
265
+ return { kind: "unclassified", basis: "heuristic" };
266
+ }
267
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/define.ts", "../src/host.ts", "../src/tree.ts", "../src/iir.ts", "../src/effects.ts"],
4
+ "sourcesContent": ["export { definePlugin, setPluginDefinition } from \"./define.js\"\nexport { log, emit, createSubstrateClient, getConfig, nodeID, edgeID } from \"./host.js\"\nexport {\n walk,\n walkTopLevel,\n childByField,\n childrenByType,\n firstByType,\n hasChildType,\n fieldText,\n firstDescendantByType,\n} from \"./tree.js\"\nexport type {\n SyntaxNode,\n Position,\n Node,\n Edge,\n NodeType,\n EdgeType,\n SourceClass,\n ExtractionResult,\n ConceptSeed,\n AnchorRef,\n Anchor,\n IR,\n Emission,\n ToolRequest,\n ToolResult,\n SubstrateQuery,\n SubstrateClient,\n LanguageDefinition,\n RoleDefinition,\n AnalyzerDefinition,\n ToolDefinition,\n PluginDefinition,\n IIRSeverity,\n IIRTarget,\n IIRExprPattern,\n IIRRuleWhen,\n IIRRuleRequire,\n IIRRule,\n IIRRulePack,\n} from \"./types.js\"\nexport { IIRTypeUnknown } from \"./iir.js\"\nexport type {\n IIRVisibility,\n IIRParam,\n IIRReturn,\n IIRExpr,\n IIRBehaviorClause,\n IIRConsequence,\n IIRSideEffect,\n IIRFailureMode,\n FunctionIntent,\n ExtractedFunction,\n} from \"./iir.js\"\nexport { classifyEffect } from \"./effects.js\"\nexport type { EffectKind, EffectBasis, EffectClassifierInput } from \"./effects.js\"\n", "import type { PluginDefinition } from \"./types.js\"\n\n/**\n * definePlugin is the single entry point for all plugin authors.\n *\n * Usage:\n * import { definePlugin } from \"@atheory-ai/ce-plugin-sdk\"\n *\n * export default definePlugin({\n * id: \"com.example.my-plugin\",\n * name: \"My Plugin\",\n * version: \"1.0.0\",\n * language: { match, extract, concepts },\n * tools: [{ name, description, activate, execute }],\n * })\n */\nexport function definePlugin(definition: PluginDefinition): PluginDefinition {\n if (!definition.id || !definition.id.includes(\".\")) {\n throw new Error(\n `Plugin id must be reverse-domain format (e.g., \"com.example.my-plugin\"). Got: \"${definition.id}\"`\n )\n }\n\n if (!definition.name) {\n throw new Error(\"Plugin name is required\")\n }\n\n if (!definition.version || !isValidSemver(definition.version)) {\n throw new Error(\n `Plugin version must be valid semver (e.g., \"1.0.0\"). Got: \"${definition.version}\"`\n )\n }\n\n if (definition.language) {\n if (typeof definition.language.match !== \"function\") {\n throw new Error(\"language.match must be a function\")\n }\n if (typeof definition.language.extract !== \"function\") {\n throw new Error(\"language.extract must be a function\")\n }\n if (definition.language.concepts) {\n for (const seed of definition.language.concepts) {\n if (seed.term !== seed.term.toLowerCase()) {\n throw new Error(`Concept terms must be lowercase. Got: \"${seed.term}\"`)\n }\n if (!seed.term.match(/^[a-z][a-z0-9-]*$/)) {\n throw new Error(\n `Concept terms must be lowercase-hyphenated. Got: \"${seed.term}\"`\n )\n }\n }\n }\n }\n\n if (definition.tools) {\n for (const tool of definition.tools) {\n if (!tool.name) {\n throw new Error(\"Tool name is required\")\n }\n if (!tool.description) {\n throw new Error(`Tool \"${tool.name}\" description is required`)\n }\n if (tool.description.length > 100) {\n throw new Error(\n `Tool \"${tool.name}\" description exceeds 100 characters (${tool.description.length}). ` +\n `The Strategizer receives this in its prompt \u2014 keep it concise.`\n )\n }\n if (typeof tool.activate !== \"function\") {\n throw new Error(`Tool \"${tool.name}\" activate must be a function`)\n }\n if (typeof tool.execute !== \"function\") {\n throw new Error(`Tool \"${tool.name}\" execute must be a function`)\n }\n }\n }\n\n if (definition.analyzers) {\n for (const analyzer of definition.analyzers) {\n if (!analyzer.name) {\n throw new Error(\"Analyzer name is required\")\n }\n if (typeof analyzer.analyze !== \"function\") {\n throw new Error(`Analyzer \"${analyzer.name}\" analyze must be a function`)\n }\n }\n }\n\n if (definition.iirRules) {\n // The host is the authoritative validator (internal/iir). Keep this to a\n // shape sanity check so an obvious mistake fails at author time rather than\n // silently shipping an empty pack.\n if (!Array.isArray(definition.iirRules.rules) || definition.iirRules.rules.length === 0) {\n throw new Error(\"iirRules.rules must be a non-empty array\")\n }\n for (const rule of definition.iirRules.rules) {\n if (!rule.id) {\n throw new Error(\"Each IIR rule requires an id\")\n }\n if (!rule.target || !rule.severity) {\n throw new Error(`IIR rule \"${rule.id}\" requires target and severity`)\n }\n }\n }\n\n setPluginDefinition(definition)\n return definition\n}\n\nexport function setPluginDefinition(definition: PluginDefinition): void {\n const globalScope = globalThis as typeof globalThis & {\n __ce_plugin_definition?: PluginDefinition\n }\n globalScope.__ce_plugin_definition = definition\n}\n\nfunction isValidSemver(version: string): boolean {\n return /^\\d+\\.\\d+\\.\\d+(-[\\w.]+)?(\\+[\\w.]+)?$/.test(version)\n}\n", "import type { SubstrateQuery, Node, SubstrateClient, NodeType, EdgeType } from \"./types.js\"\n\n/**\n * Low-level host function declarations.\n * These are provided by the engine's wazero runtime.\n */\ndeclare function __ce_log(level: string, message: string): void\ndeclare function __ce_emit(channel: string, content: string): void\ndeclare function __ce_substrate_query(queryJSON: string): string\ndeclare function __ce_get_config(key: string): string\ndeclare function __ce_node_id(projectID: string, type: string, canonicalID: string): string\ndeclare function __ce_edge_id(sourceID: string, type: string, targetID: string): string\n\n// \u2500\u2500 Logging \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const log = {\n debug: (message: string) => __ce_log(\"debug\", message),\n info: (message: string) => __ce_log(\"info\", message),\n warn: (message: string) => __ce_log(\"warn\", message),\n error: (message: string) => __ce_log(\"error\", message),\n}\n\n// \u2500\u2500 Emit \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function emit(channel: \"thinking\" | \"action\" | \"debug\" | \"warning\", content: string): void {\n __ce_emit(channel, content)\n}\n\n// \u2500\u2500 Substrate \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function createSubstrateClient(): SubstrateClient {\n return {\n query(q: SubstrateQuery): Node[] {\n const result = __ce_substrate_query(JSON.stringify(q))\n return JSON.parse(result) as Node[]\n }\n }\n}\n\n// \u2500\u2500 Config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function getConfig<T = unknown>(key: string): T | undefined {\n const result = __ce_get_config(key)\n if (!result) return undefined\n return JSON.parse(result) as T\n}\n\n// \u2500\u2500 ID generation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function nodeID(projectID: string, type: NodeType, canonicalID: string): string {\n return __ce_node_id(projectID, type as string, canonicalID)\n}\n\nexport function edgeID(sourceID: string, type: EdgeType, targetID: string): string {\n return __ce_edge_id(sourceID, type as string, targetID)\n}\n", "// CST-walking helpers for language extractors. The host hands plugins a parsed\n// tree-sitter tree (SyntaxNode); these make walking it ergonomic so extractors\n// never fall back to regex.\n\nimport type { SyntaxNode } from \"./types.js\"\n\n// The host omits `children` on leaf nodes (serialized as null), so every access\n// goes through this \u2014 helpers must never assume `children` is an array.\nfunction kids(node: SyntaxNode): SyntaxNode[] {\n return node.children ?? []\n}\n\n/**\n * walk visits node and every descendant in pre-order. Iterative (explicit\n * stack) so a deeply nested CST \u2014 long expression chains, minified/generated\n * code \u2014 cannot overflow the call stack.\n */\nexport function walk(node: SyntaxNode | null, visit: (n: SyntaxNode) => void): void {\n if (!node) return\n const stack: SyntaxNode[] = [node]\n while (stack.length > 0) {\n const n = stack.pop()!\n visit(n)\n const cs = kids(n)\n for (let i = cs.length - 1; i >= 0; i--) stack.push(cs[i]) // reversed \u2192 pop in order\n }\n}\n\n/**\n * walkTopLevel visits node and its descendants, but does NOT descend into\n * nested function/closure scopes \u2014 a boundary node itself is visited, its body\n * is not. Use it to collect a scope's own declarations without pulling in ones\n * nested inside inner functions.\n */\nexport function walkTopLevel(node: SyntaxNode | null, visit: (n: SyntaxNode) => void): void {\n if (!node) return\n // Stack entries carry whether to descend into that node's children \u2014 a\n // boundary node is visited but its subtree is not walked. Iterative to avoid\n // call-stack overflow on deep trees.\n const stack: Array<{ n: SyntaxNode; descend: boolean }> = [{ n: node, descend: true }]\n while (stack.length > 0) {\n const { n, descend } = stack.pop()!\n visit(n)\n if (!descend) continue\n const cs = kids(n)\n for (let i = cs.length - 1; i >= 0; i--) {\n stack.push({ n: cs[i], descend: !SCOPE_BOUNDARY.has(cs[i].type) })\n }\n }\n}\n\nconst SCOPE_BOUNDARY = new Set([\n \"function_declaration\",\n \"function_expression\",\n \"arrow_function\",\n \"method_definition\",\n \"generator_function\",\n \"generator_function_declaration\",\n])\n\n/** childByField returns the child occupying the given field, or null. */\nexport function childByField(node: SyntaxNode, field: string): SyntaxNode | null {\n for (const child of kids(node)) {\n if (child.fieldName === field) return child\n }\n return null\n}\n\n/** childrenByType returns direct children of a given type. */\nexport function childrenByType(node: SyntaxNode, type: string): SyntaxNode[] {\n return kids(node).filter(c => c.type === type)\n}\n\n/** firstByType returns the first direct child of a given type, or null. */\nexport function firstByType(node: SyntaxNode, type: string): SyntaxNode | null {\n return kids(node).find(c => c.type === type) ?? null\n}\n\n/** hasChildType reports whether node has a direct child (named or token) of the given type. */\nexport function hasChildType(node: SyntaxNode, type: string): boolean {\n return kids(node).some(c => c.type === type)\n}\n\n/** fieldText returns the text of the child in the given field, or \"\". */\nexport function fieldText(node: SyntaxNode, field: string): string {\n return childByField(node, field)?.text ?? \"\"\n}\n\n/** firstDescendantByType returns the first descendant of a given type (pre-order), or null. */\nexport function firstDescendantByType(node: SyntaxNode, type: string): SyntaxNode | null {\n let found: SyntaxNode | null = null\n walk(node, n => {\n if (found === null && n.type === type) found = n\n })\n return found\n}\n", "// The IIR (Intermediate Intent Representation) model, mirrored from Context\n// Engine's internal/iir. A language plugin may *lift* source into these types\n// during extraction; the host validates and stores them (it remains the\n// authoritative parser \u2014 these are the authoring/contract surface). Field names\n// match the host's JSON tags so a host-side ParseIntentJSON accepts them.\n\nexport type IIRVisibility = \"public\" | \"private\"\n\n// TypeUnknown marks a parameter type that could not be determined from source \u2014\n// represented explicitly rather than dropped, so comparison can tell \"unknown\"\n// from \"absent\".\nexport const IIRTypeUnknown = \"unknown\"\n\nexport interface IIRParam {\n name: string\n type: string\n}\n\nexport interface IIRReturn {\n // Empty string means the return type was absent in source (distinct from the\n // explicit type \"void\").\n type: string\n explicit: boolean\n}\n\n// IIRExpr is a normalized condition expression node (mirrors iir.Expr): `op`\n// names the node (\"<\", \"&&\", \"!\", \"path\", \"lit\"), `args` are operands in source\n// order, `text` carries a leaf payload (a literal value or a dotted access path).\nexport interface IIRExpr {\n op: string\n args?: IIRExpr[]\n text?: string\n}\n\n/**\n * A normalized behavior consequence \u2014 the `then` action of a clause, structured\n * just enough to compare across languages. `op` is the action: \"return\", \"throw\"\n * (Go panic / JS-TS throw / Python raise, folded), or \"invoke\" (a call). `value`\n * is an opaque canonical payload (the returned expression, the thrown failure's\n * identity, or the invoked callee), omitted when the action carries none.\n */\nexport interface IIRConsequence {\n op: \"return\" | \"throw\" | \"invoke\"\n value?: string\n}\n\nexport interface IIRBehaviorClause {\n when: string\n then: string\n whenExpr?: IIRExpr\n thenExpr?: IIRConsequence\n}\n\n/**\n * An observable side effect. Either a bare name (\"analytics.track\") or an object\n * carrying an optional kind (network | db | io | log | mutation | unclassified)\n * and basis \u2014 \"resolved\" when the kind came from a known effectful API (an\n * import path or recognized client), \"heuristic\" when it was guessed from a\n * method-name verb. The host accepts both forms; a plugin that can categorize an\n * effect emits the object form, otherwise a bare string.\n */\nexport type IIRSideEffect = string | {\n name: string\n kind?: \"network\" | \"db\" | \"io\" | \"log\" | \"mutation\" | \"unclassified\"\n basis?: \"resolved\" | \"heuristic\"\n}\n\n/**\n * An expected failure outcome. Either a bare code (\"amount_below_minimum\") or an\n * object carrying an optional kind and source. The kind names *how* the function\n * signals the failure: \"constructed\" (created inline, e.g. throw new Error(\"msg\")\n * / errors.New), \"sentinel\" (a named error value/type, e.g. ErrClosed or a custom\n * error class), or \"propagated\" (an upstream failure forwarded on \u2014 a re-throw or\n * `return nil, err`). For a propagated failure, `source` names the forwarded\n * identifier. The host accepts both forms; a code-only failure round-trips as a\n * bare string.\n */\nexport type IIRFailureMode = string | {\n code: string\n kind?: \"constructed\" | \"sentinel\" | \"propagated\"\n source?: string\n}\n\nexport interface FunctionIntent {\n kind: \"FunctionIntent\"\n name: string\n language: string\n /**\n * The epistemic layer this intent comes from. A plugin lift emits \"observed\"\n * (ground truth about what the code does); a hand-authored spec is \"declared\";\n * the shaper emits \"inferred\". The host defaults an absent origin to declared.\n */\n origin?: \"observed\" | \"declared\" | \"inferred\"\n visibility?: IIRVisibility\n inputs: IIRParam[]\n returns: IIRReturn\n behavior: IIRBehaviorClause[]\n sideEffects: IIRSideEffect[]\n failureModes: IIRFailureMode[]\n constraints: string[]\n}\n\n// ExtractedFunction pairs a lifted FunctionIntent with the id of the symbol node\n// it was lifted from. The plugin knows this id directly (it created the node),\n// so the host can attach the IIR to the exact node without the (name,start_byte)\n// correlation the Go-side lift needs.\nexport interface ExtractedFunction {\n nodeId: string\n intent: FunctionIntent\n}\n", "// Shared side-effect classifier for language plugins. Given the structured parts\n// of a detected call \u2014 the receiver root, the method, and (when the root is an\n// imported package) its full import path \u2014 it returns the effect kind and a\n// confidence. Plugins emit these on each SideEffect so the host doesn't have to\n// re-derive them from the name string at compare time.\n//\n// Matching is structural (exact root, method substring, import-path prefix)\n// rather than substring-on-the-whole-name, which avoids false hits like\n// \"catalog.save\" being read as a log effect because the name contains \"log\".\n\nexport type EffectKind = \"network\" | \"db\" | \"io\" | \"log\" | \"mutation\" | \"unclassified\"\n// How an effect's kind was established. \"resolved\" means it matched a known\n// effectful API (an import path or a recognized client root) \u2014 deterministic\n// knowledge, not a probabilistic guess. \"heuristic\" means it was inferred from a\n// method-name verb or is uncategorized. The comparator treats an undeclared\n// resolved effect as an error and an undeclared heuristic one as a warning: it\n// should not fail verification on a guess.\nexport type EffectBasis = \"resolved\" | \"heuristic\"\n\nexport interface EffectClassifierInput {\n /** The called method / function name, e.g. \"Get\", \"track\". */\n method?: string\n /** The receiver root, e.g. \"http\", \"analytics\". */\n root?: string\n /** Full import path of the root when it is an imported package, e.g. \"net/http\". */\n importPath?: string\n}\n\n// Import-path prefixes are the strongest signal (a real, categorizable package).\nconst pathCategories: [EffectKind, string[]][] = [\n [\"network\", [\"net/http\", \"net/url\", \"net\", \"google.golang.org/grpc\", \"github.com/gorilla/websocket\"]],\n [\"db\", [\"database/sql\", \"gorm.io\", \"go.mongodb.org\", \"github.com/redis\", \"github.com/jmoiron/sqlx\"]],\n [\"io\", [\"os\", \"io\", \"io/ioutil\", \"bufio\", \"path/filepath\"]],\n // fmt's pure formatters (Sprintf, Errorf) are excluded before classification;\n // what reaches here (Println/Printf/Fprint\u2026) writes output.\n [\"log\", [\"log\", \"log/slog\", \"fmt\"]],\n]\n\n// Receiver roots that categorize a call even without a resolved import path\n// (e.g. member calls on a well-known client name).\nconst rootCategories: [EffectKind, string[]][] = [\n [\"network\", [\"http\", \"https\", \"fetch\", \"axios\", \"request\", \"requests\", \"grpc\", \"ws\", \"socket\", \"urllib\"]],\n [\"db\", [\"db\", \"sql\", \"database\", \"redis\", \"mongo\", \"session\", \"repository\", \"datastore\"]],\n [\"io\", [\"os\", \"io\", \"fs\", \"ioutil\", \"file\", \"pathlib\"]],\n [\"log\", [\"log\", \"logger\", \"logging\", \"console\", \"slog\", \"fmt\"]],\n]\n\n// Method names that signal an observable mutation/effect.\nconst mutationVerbs = [\"track\", \"send\", \"emit\", \"publish\", \"save\", \"create\", \"update\", \"delete\", \"write\"]\n\n/**\n * classifyEffect returns the (kind, basis) for a detected effectful call. A call\n * matched against a known effectful API \u2014 by import path or a recognized client\n * root \u2014 is \"resolved\" (deterministic). A call categorized only from a\n * method-name verb, or left uncategorized, is \"heuristic\" (a guess).\n */\nexport function classifyEffect(input: EffectClassifierInput): { kind: EffectKind; basis: EffectBasis } {\n const path = (input.importPath ?? \"\").toLowerCase()\n const root = (input.root ?? \"\").toLowerCase()\n const method = (input.method ?? \"\").toLowerCase()\n\n for (const [kind, prefixes] of pathCategories) {\n if (prefixes.some(p => path === p || path.startsWith(p + \"/\"))) return { kind, basis: \"resolved\" }\n }\n for (const [kind, roots] of rootCategories) {\n if (roots.includes(root)) return { kind, basis: \"resolved\" }\n }\n if (mutationVerbs.some(v => method.includes(v))) return { kind: \"mutation\", basis: \"heuristic\" }\n return { kind: \"unclassified\", basis: \"heuristic\" }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,9 @@
1
+ export { definePlugin, setPluginDefinition } from "./define.js";
2
+ export { log, emit, createSubstrateClient, getConfig, nodeID, edgeID } from "./host.js";
3
+ export { walk, walkTopLevel, childByField, childrenByType, firstByType, hasChildType, fieldText, firstDescendantByType, } from "./tree.js";
4
+ export type { SyntaxNode, Position, Node, Edge, NodeType, EdgeType, SourceClass, ExtractionResult, ConceptSeed, AnchorRef, Anchor, IR, Emission, ToolRequest, ToolResult, SubstrateQuery, SubstrateClient, LanguageDefinition, RoleDefinition, AnalyzerDefinition, ToolDefinition, PluginDefinition, IIRSeverity, IIRTarget, IIRExprPattern, IIRRuleWhen, IIRRuleRequire, IIRRule, IIRRulePack, } from "./types.js";
5
+ export { IIRTypeUnknown } from "./iir.js";
6
+ export type { IIRVisibility, IIRParam, IIRReturn, IIRExpr, IIRBehaviorClause, IIRConsequence, IIRSideEffect, IIRFailureMode, FunctionIntent, ExtractedFunction, } from "./iir.js";
7
+ export { classifyEffect } from "./effects.js";
8
+ export type { EffectKind, EffectBasis, EffectClassifierInput } from "./effects.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAC/D,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AACvF,OAAO,EACL,IAAI,EACJ,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,WAAW,EACX,YAAY,EACZ,SAAS,EACT,qBAAqB,GACtB,MAAM,WAAW,CAAA;AAClB,YAAY,EACV,UAAU,EACV,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,MAAM,EACN,EAAE,EACF,QAAQ,EACR,WAAW,EACX,UAAU,EACV,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,cAAc,EACd,WAAW,EACX,cAAc,EACd,OAAO,EACP,WAAW,GACZ,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACzC,YAAY,EACV,aAAa,EACb,QAAQ,EACR,SAAS,EACT,OAAO,EACP,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,cAAc,EACd,cAAc,EACd,iBAAiB,GAClB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,244 @@
1
+ // src/define.ts
2
+ function definePlugin(definition) {
3
+ if (!definition.id || !definition.id.includes(".")) {
4
+ throw new Error(
5
+ `Plugin id must be reverse-domain format (e.g., "com.example.my-plugin"). Got: "${definition.id}"`
6
+ );
7
+ }
8
+ if (!definition.name) {
9
+ throw new Error("Plugin name is required");
10
+ }
11
+ if (!definition.version || !isValidSemver(definition.version)) {
12
+ throw new Error(
13
+ `Plugin version must be valid semver (e.g., "1.0.0"). Got: "${definition.version}"`
14
+ );
15
+ }
16
+ if (definition.language) {
17
+ if (typeof definition.language.match !== "function") {
18
+ throw new Error("language.match must be a function");
19
+ }
20
+ if (typeof definition.language.extract !== "function") {
21
+ throw new Error("language.extract must be a function");
22
+ }
23
+ if (definition.language.concepts) {
24
+ for (const seed of definition.language.concepts) {
25
+ if (seed.term !== seed.term.toLowerCase()) {
26
+ throw new Error(`Concept terms must be lowercase. Got: "${seed.term}"`);
27
+ }
28
+ if (!seed.term.match(/^[a-z][a-z0-9-]*$/)) {
29
+ throw new Error(
30
+ `Concept terms must be lowercase-hyphenated. Got: "${seed.term}"`
31
+ );
32
+ }
33
+ }
34
+ }
35
+ }
36
+ if (definition.tools) {
37
+ for (const tool of definition.tools) {
38
+ if (!tool.name) {
39
+ throw new Error("Tool name is required");
40
+ }
41
+ if (!tool.description) {
42
+ throw new Error(`Tool "${tool.name}" description is required`);
43
+ }
44
+ if (tool.description.length > 100) {
45
+ throw new Error(
46
+ `Tool "${tool.name}" description exceeds 100 characters (${tool.description.length}). The Strategizer receives this in its prompt \u2014 keep it concise.`
47
+ );
48
+ }
49
+ if (typeof tool.activate !== "function") {
50
+ throw new Error(`Tool "${tool.name}" activate must be a function`);
51
+ }
52
+ if (typeof tool.execute !== "function") {
53
+ throw new Error(`Tool "${tool.name}" execute must be a function`);
54
+ }
55
+ }
56
+ }
57
+ if (definition.analyzers) {
58
+ for (const analyzer of definition.analyzers) {
59
+ if (!analyzer.name) {
60
+ throw new Error("Analyzer name is required");
61
+ }
62
+ if (typeof analyzer.analyze !== "function") {
63
+ throw new Error(`Analyzer "${analyzer.name}" analyze must be a function`);
64
+ }
65
+ }
66
+ }
67
+ if (definition.iirRules) {
68
+ if (!Array.isArray(definition.iirRules.rules) || definition.iirRules.rules.length === 0) {
69
+ throw new Error("iirRules.rules must be a non-empty array");
70
+ }
71
+ for (const rule of definition.iirRules.rules) {
72
+ if (!rule.id) {
73
+ throw new Error("Each IIR rule requires an id");
74
+ }
75
+ if (!rule.target || !rule.severity) {
76
+ throw new Error(`IIR rule "${rule.id}" requires target and severity`);
77
+ }
78
+ }
79
+ }
80
+ setPluginDefinition(definition);
81
+ return definition;
82
+ }
83
+ function setPluginDefinition(definition) {
84
+ const globalScope = globalThis;
85
+ globalScope.__ce_plugin_definition = definition;
86
+ }
87
+ function isValidSemver(version) {
88
+ return /^\d+\.\d+\.\d+(-[\w.]+)?(\+[\w.]+)?$/.test(version);
89
+ }
90
+
91
+ // src/host.ts
92
+ var log = {
93
+ debug: (message) => __ce_log("debug", message),
94
+ info: (message) => __ce_log("info", message),
95
+ warn: (message) => __ce_log("warn", message),
96
+ error: (message) => __ce_log("error", message)
97
+ };
98
+ function emit(channel, content) {
99
+ __ce_emit(channel, content);
100
+ }
101
+ function createSubstrateClient() {
102
+ return {
103
+ query(q) {
104
+ const result = __ce_substrate_query(JSON.stringify(q));
105
+ return JSON.parse(result);
106
+ }
107
+ };
108
+ }
109
+ function getConfig(key) {
110
+ const result = __ce_get_config(key);
111
+ if (!result)
112
+ return void 0;
113
+ return JSON.parse(result);
114
+ }
115
+ function nodeID(projectID, type, canonicalID) {
116
+ return __ce_node_id(projectID, type, canonicalID);
117
+ }
118
+ function edgeID(sourceID, type, targetID) {
119
+ return __ce_edge_id(sourceID, type, targetID);
120
+ }
121
+
122
+ // src/tree.ts
123
+ function kids(node) {
124
+ return node.children ?? [];
125
+ }
126
+ function walk(node, visit) {
127
+ if (!node)
128
+ return;
129
+ const stack = [node];
130
+ while (stack.length > 0) {
131
+ const n = stack.pop();
132
+ visit(n);
133
+ const cs = kids(n);
134
+ for (let i = cs.length - 1; i >= 0; i--)
135
+ stack.push(cs[i]);
136
+ }
137
+ }
138
+ function walkTopLevel(node, visit) {
139
+ if (!node)
140
+ return;
141
+ const stack = [{ n: node, descend: true }];
142
+ while (stack.length > 0) {
143
+ const { n, descend } = stack.pop();
144
+ visit(n);
145
+ if (!descend)
146
+ continue;
147
+ const cs = kids(n);
148
+ for (let i = cs.length - 1; i >= 0; i--) {
149
+ stack.push({ n: cs[i], descend: !SCOPE_BOUNDARY.has(cs[i].type) });
150
+ }
151
+ }
152
+ }
153
+ var SCOPE_BOUNDARY = /* @__PURE__ */ new Set([
154
+ "function_declaration",
155
+ "function_expression",
156
+ "arrow_function",
157
+ "method_definition",
158
+ "generator_function",
159
+ "generator_function_declaration"
160
+ ]);
161
+ function childByField(node, field) {
162
+ for (const child of kids(node)) {
163
+ if (child.fieldName === field)
164
+ return child;
165
+ }
166
+ return null;
167
+ }
168
+ function childrenByType(node, type) {
169
+ return kids(node).filter((c) => c.type === type);
170
+ }
171
+ function firstByType(node, type) {
172
+ return kids(node).find((c) => c.type === type) ?? null;
173
+ }
174
+ function hasChildType(node, type) {
175
+ return kids(node).some((c) => c.type === type);
176
+ }
177
+ function fieldText(node, field) {
178
+ return childByField(node, field)?.text ?? "";
179
+ }
180
+ function firstDescendantByType(node, type) {
181
+ let found = null;
182
+ walk(node, (n) => {
183
+ if (found === null && n.type === type)
184
+ found = n;
185
+ });
186
+ return found;
187
+ }
188
+
189
+ // src/iir.ts
190
+ var IIRTypeUnknown = "unknown";
191
+
192
+ // src/effects.ts
193
+ var pathCategories = [
194
+ ["network", ["net/http", "net/url", "net", "google.golang.org/grpc", "github.com/gorilla/websocket"]],
195
+ ["db", ["database/sql", "gorm.io", "go.mongodb.org", "github.com/redis", "github.com/jmoiron/sqlx"]],
196
+ ["io", ["os", "io", "io/ioutil", "bufio", "path/filepath"]],
197
+ // fmt's pure formatters (Sprintf, Errorf) are excluded before classification;
198
+ // what reaches here (Println/Printf/Fprint…) writes output.
199
+ ["log", ["log", "log/slog", "fmt"]]
200
+ ];
201
+ var rootCategories = [
202
+ ["network", ["http", "https", "fetch", "axios", "request", "requests", "grpc", "ws", "socket", "urllib"]],
203
+ ["db", ["db", "sql", "database", "redis", "mongo", "session", "repository", "datastore"]],
204
+ ["io", ["os", "io", "fs", "ioutil", "file", "pathlib"]],
205
+ ["log", ["log", "logger", "logging", "console", "slog", "fmt"]]
206
+ ];
207
+ var mutationVerbs = ["track", "send", "emit", "publish", "save", "create", "update", "delete", "write"];
208
+ function classifyEffect(input) {
209
+ const path = (input.importPath ?? "").toLowerCase();
210
+ const root = (input.root ?? "").toLowerCase();
211
+ const method = (input.method ?? "").toLowerCase();
212
+ for (const [kind, prefixes] of pathCategories) {
213
+ if (prefixes.some((p) => path === p || path.startsWith(p + "/")))
214
+ return { kind, basis: "resolved" };
215
+ }
216
+ for (const [kind, roots] of rootCategories) {
217
+ if (roots.includes(root))
218
+ return { kind, basis: "resolved" };
219
+ }
220
+ if (mutationVerbs.some((v) => method.includes(v)))
221
+ return { kind: "mutation", basis: "heuristic" };
222
+ return { kind: "unclassified", basis: "heuristic" };
223
+ }
224
+ export {
225
+ IIRTypeUnknown,
226
+ childByField,
227
+ childrenByType,
228
+ classifyEffect,
229
+ createSubstrateClient,
230
+ definePlugin,
231
+ edgeID,
232
+ emit,
233
+ fieldText,
234
+ firstByType,
235
+ firstDescendantByType,
236
+ getConfig,
237
+ hasChildType,
238
+ log,
239
+ nodeID,
240
+ setPluginDefinition,
241
+ walk,
242
+ walkTopLevel
243
+ };
244
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/define.ts", "../src/host.ts", "../src/tree.ts", "../src/iir.ts", "../src/effects.ts"],
4
+ "sourcesContent": ["import type { PluginDefinition } from \"./types.js\"\n\n/**\n * definePlugin is the single entry point for all plugin authors.\n *\n * Usage:\n * import { definePlugin } from \"@atheory-ai/ce-plugin-sdk\"\n *\n * export default definePlugin({\n * id: \"com.example.my-plugin\",\n * name: \"My Plugin\",\n * version: \"1.0.0\",\n * language: { match, extract, concepts },\n * tools: [{ name, description, activate, execute }],\n * })\n */\nexport function definePlugin(definition: PluginDefinition): PluginDefinition {\n if (!definition.id || !definition.id.includes(\".\")) {\n throw new Error(\n `Plugin id must be reverse-domain format (e.g., \"com.example.my-plugin\"). Got: \"${definition.id}\"`\n )\n }\n\n if (!definition.name) {\n throw new Error(\"Plugin name is required\")\n }\n\n if (!definition.version || !isValidSemver(definition.version)) {\n throw new Error(\n `Plugin version must be valid semver (e.g., \"1.0.0\"). Got: \"${definition.version}\"`\n )\n }\n\n if (definition.language) {\n if (typeof definition.language.match !== \"function\") {\n throw new Error(\"language.match must be a function\")\n }\n if (typeof definition.language.extract !== \"function\") {\n throw new Error(\"language.extract must be a function\")\n }\n if (definition.language.concepts) {\n for (const seed of definition.language.concepts) {\n if (seed.term !== seed.term.toLowerCase()) {\n throw new Error(`Concept terms must be lowercase. Got: \"${seed.term}\"`)\n }\n if (!seed.term.match(/^[a-z][a-z0-9-]*$/)) {\n throw new Error(\n `Concept terms must be lowercase-hyphenated. Got: \"${seed.term}\"`\n )\n }\n }\n }\n }\n\n if (definition.tools) {\n for (const tool of definition.tools) {\n if (!tool.name) {\n throw new Error(\"Tool name is required\")\n }\n if (!tool.description) {\n throw new Error(`Tool \"${tool.name}\" description is required`)\n }\n if (tool.description.length > 100) {\n throw new Error(\n `Tool \"${tool.name}\" description exceeds 100 characters (${tool.description.length}). ` +\n `The Strategizer receives this in its prompt \u2014 keep it concise.`\n )\n }\n if (typeof tool.activate !== \"function\") {\n throw new Error(`Tool \"${tool.name}\" activate must be a function`)\n }\n if (typeof tool.execute !== \"function\") {\n throw new Error(`Tool \"${tool.name}\" execute must be a function`)\n }\n }\n }\n\n if (definition.analyzers) {\n for (const analyzer of definition.analyzers) {\n if (!analyzer.name) {\n throw new Error(\"Analyzer name is required\")\n }\n if (typeof analyzer.analyze !== \"function\") {\n throw new Error(`Analyzer \"${analyzer.name}\" analyze must be a function`)\n }\n }\n }\n\n if (definition.iirRules) {\n // The host is the authoritative validator (internal/iir). Keep this to a\n // shape sanity check so an obvious mistake fails at author time rather than\n // silently shipping an empty pack.\n if (!Array.isArray(definition.iirRules.rules) || definition.iirRules.rules.length === 0) {\n throw new Error(\"iirRules.rules must be a non-empty array\")\n }\n for (const rule of definition.iirRules.rules) {\n if (!rule.id) {\n throw new Error(\"Each IIR rule requires an id\")\n }\n if (!rule.target || !rule.severity) {\n throw new Error(`IIR rule \"${rule.id}\" requires target and severity`)\n }\n }\n }\n\n setPluginDefinition(definition)\n return definition\n}\n\nexport function setPluginDefinition(definition: PluginDefinition): void {\n const globalScope = globalThis as typeof globalThis & {\n __ce_plugin_definition?: PluginDefinition\n }\n globalScope.__ce_plugin_definition = definition\n}\n\nfunction isValidSemver(version: string): boolean {\n return /^\\d+\\.\\d+\\.\\d+(-[\\w.]+)?(\\+[\\w.]+)?$/.test(version)\n}\n", "import type { SubstrateQuery, Node, SubstrateClient, NodeType, EdgeType } from \"./types.js\"\n\n/**\n * Low-level host function declarations.\n * These are provided by the engine's wazero runtime.\n */\ndeclare function __ce_log(level: string, message: string): void\ndeclare function __ce_emit(channel: string, content: string): void\ndeclare function __ce_substrate_query(queryJSON: string): string\ndeclare function __ce_get_config(key: string): string\ndeclare function __ce_node_id(projectID: string, type: string, canonicalID: string): string\ndeclare function __ce_edge_id(sourceID: string, type: string, targetID: string): string\n\n// \u2500\u2500 Logging \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const log = {\n debug: (message: string) => __ce_log(\"debug\", message),\n info: (message: string) => __ce_log(\"info\", message),\n warn: (message: string) => __ce_log(\"warn\", message),\n error: (message: string) => __ce_log(\"error\", message),\n}\n\n// \u2500\u2500 Emit \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function emit(channel: \"thinking\" | \"action\" | \"debug\" | \"warning\", content: string): void {\n __ce_emit(channel, content)\n}\n\n// \u2500\u2500 Substrate \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function createSubstrateClient(): SubstrateClient {\n return {\n query(q: SubstrateQuery): Node[] {\n const result = __ce_substrate_query(JSON.stringify(q))\n return JSON.parse(result) as Node[]\n }\n }\n}\n\n// \u2500\u2500 Config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function getConfig<T = unknown>(key: string): T | undefined {\n const result = __ce_get_config(key)\n if (!result) return undefined\n return JSON.parse(result) as T\n}\n\n// \u2500\u2500 ID generation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function nodeID(projectID: string, type: NodeType, canonicalID: string): string {\n return __ce_node_id(projectID, type as string, canonicalID)\n}\n\nexport function edgeID(sourceID: string, type: EdgeType, targetID: string): string {\n return __ce_edge_id(sourceID, type as string, targetID)\n}\n", "// CST-walking helpers for language extractors. The host hands plugins a parsed\n// tree-sitter tree (SyntaxNode); these make walking it ergonomic so extractors\n// never fall back to regex.\n\nimport type { SyntaxNode } from \"./types.js\"\n\n// The host omits `children` on leaf nodes (serialized as null), so every access\n// goes through this \u2014 helpers must never assume `children` is an array.\nfunction kids(node: SyntaxNode): SyntaxNode[] {\n return node.children ?? []\n}\n\n/**\n * walk visits node and every descendant in pre-order. Iterative (explicit\n * stack) so a deeply nested CST \u2014 long expression chains, minified/generated\n * code \u2014 cannot overflow the call stack.\n */\nexport function walk(node: SyntaxNode | null, visit: (n: SyntaxNode) => void): void {\n if (!node) return\n const stack: SyntaxNode[] = [node]\n while (stack.length > 0) {\n const n = stack.pop()!\n visit(n)\n const cs = kids(n)\n for (let i = cs.length - 1; i >= 0; i--) stack.push(cs[i]) // reversed \u2192 pop in order\n }\n}\n\n/**\n * walkTopLevel visits node and its descendants, but does NOT descend into\n * nested function/closure scopes \u2014 a boundary node itself is visited, its body\n * is not. Use it to collect a scope's own declarations without pulling in ones\n * nested inside inner functions.\n */\nexport function walkTopLevel(node: SyntaxNode | null, visit: (n: SyntaxNode) => void): void {\n if (!node) return\n // Stack entries carry whether to descend into that node's children \u2014 a\n // boundary node is visited but its subtree is not walked. Iterative to avoid\n // call-stack overflow on deep trees.\n const stack: Array<{ n: SyntaxNode; descend: boolean }> = [{ n: node, descend: true }]\n while (stack.length > 0) {\n const { n, descend } = stack.pop()!\n visit(n)\n if (!descend) continue\n const cs = kids(n)\n for (let i = cs.length - 1; i >= 0; i--) {\n stack.push({ n: cs[i], descend: !SCOPE_BOUNDARY.has(cs[i].type) })\n }\n }\n}\n\nconst SCOPE_BOUNDARY = new Set([\n \"function_declaration\",\n \"function_expression\",\n \"arrow_function\",\n \"method_definition\",\n \"generator_function\",\n \"generator_function_declaration\",\n])\n\n/** childByField returns the child occupying the given field, or null. */\nexport function childByField(node: SyntaxNode, field: string): SyntaxNode | null {\n for (const child of kids(node)) {\n if (child.fieldName === field) return child\n }\n return null\n}\n\n/** childrenByType returns direct children of a given type. */\nexport function childrenByType(node: SyntaxNode, type: string): SyntaxNode[] {\n return kids(node).filter(c => c.type === type)\n}\n\n/** firstByType returns the first direct child of a given type, or null. */\nexport function firstByType(node: SyntaxNode, type: string): SyntaxNode | null {\n return kids(node).find(c => c.type === type) ?? null\n}\n\n/** hasChildType reports whether node has a direct child (named or token) of the given type. */\nexport function hasChildType(node: SyntaxNode, type: string): boolean {\n return kids(node).some(c => c.type === type)\n}\n\n/** fieldText returns the text of the child in the given field, or \"\". */\nexport function fieldText(node: SyntaxNode, field: string): string {\n return childByField(node, field)?.text ?? \"\"\n}\n\n/** firstDescendantByType returns the first descendant of a given type (pre-order), or null. */\nexport function firstDescendantByType(node: SyntaxNode, type: string): SyntaxNode | null {\n let found: SyntaxNode | null = null\n walk(node, n => {\n if (found === null && n.type === type) found = n\n })\n return found\n}\n", "// The IIR (Intermediate Intent Representation) model, mirrored from Context\n// Engine's internal/iir. A language plugin may *lift* source into these types\n// during extraction; the host validates and stores them (it remains the\n// authoritative parser \u2014 these are the authoring/contract surface). Field names\n// match the host's JSON tags so a host-side ParseIntentJSON accepts them.\n\nexport type IIRVisibility = \"public\" | \"private\"\n\n// TypeUnknown marks a parameter type that could not be determined from source \u2014\n// represented explicitly rather than dropped, so comparison can tell \"unknown\"\n// from \"absent\".\nexport const IIRTypeUnknown = \"unknown\"\n\nexport interface IIRParam {\n name: string\n type: string\n}\n\nexport interface IIRReturn {\n // Empty string means the return type was absent in source (distinct from the\n // explicit type \"void\").\n type: string\n explicit: boolean\n}\n\n// IIRExpr is a normalized condition expression node (mirrors iir.Expr): `op`\n// names the node (\"<\", \"&&\", \"!\", \"path\", \"lit\"), `args` are operands in source\n// order, `text` carries a leaf payload (a literal value or a dotted access path).\nexport interface IIRExpr {\n op: string\n args?: IIRExpr[]\n text?: string\n}\n\n/**\n * A normalized behavior consequence \u2014 the `then` action of a clause, structured\n * just enough to compare across languages. `op` is the action: \"return\", \"throw\"\n * (Go panic / JS-TS throw / Python raise, folded), or \"invoke\" (a call). `value`\n * is an opaque canonical payload (the returned expression, the thrown failure's\n * identity, or the invoked callee), omitted when the action carries none.\n */\nexport interface IIRConsequence {\n op: \"return\" | \"throw\" | \"invoke\"\n value?: string\n}\n\nexport interface IIRBehaviorClause {\n when: string\n then: string\n whenExpr?: IIRExpr\n thenExpr?: IIRConsequence\n}\n\n/**\n * An observable side effect. Either a bare name (\"analytics.track\") or an object\n * carrying an optional kind (network | db | io | log | mutation | unclassified)\n * and basis \u2014 \"resolved\" when the kind came from a known effectful API (an\n * import path or recognized client), \"heuristic\" when it was guessed from a\n * method-name verb. The host accepts both forms; a plugin that can categorize an\n * effect emits the object form, otherwise a bare string.\n */\nexport type IIRSideEffect = string | {\n name: string\n kind?: \"network\" | \"db\" | \"io\" | \"log\" | \"mutation\" | \"unclassified\"\n basis?: \"resolved\" | \"heuristic\"\n}\n\n/**\n * An expected failure outcome. Either a bare code (\"amount_below_minimum\") or an\n * object carrying an optional kind and source. The kind names *how* the function\n * signals the failure: \"constructed\" (created inline, e.g. throw new Error(\"msg\")\n * / errors.New), \"sentinel\" (a named error value/type, e.g. ErrClosed or a custom\n * error class), or \"propagated\" (an upstream failure forwarded on \u2014 a re-throw or\n * `return nil, err`). For a propagated failure, `source` names the forwarded\n * identifier. The host accepts both forms; a code-only failure round-trips as a\n * bare string.\n */\nexport type IIRFailureMode = string | {\n code: string\n kind?: \"constructed\" | \"sentinel\" | \"propagated\"\n source?: string\n}\n\nexport interface FunctionIntent {\n kind: \"FunctionIntent\"\n name: string\n language: string\n /**\n * The epistemic layer this intent comes from. A plugin lift emits \"observed\"\n * (ground truth about what the code does); a hand-authored spec is \"declared\";\n * the shaper emits \"inferred\". The host defaults an absent origin to declared.\n */\n origin?: \"observed\" | \"declared\" | \"inferred\"\n visibility?: IIRVisibility\n inputs: IIRParam[]\n returns: IIRReturn\n behavior: IIRBehaviorClause[]\n sideEffects: IIRSideEffect[]\n failureModes: IIRFailureMode[]\n constraints: string[]\n}\n\n// ExtractedFunction pairs a lifted FunctionIntent with the id of the symbol node\n// it was lifted from. The plugin knows this id directly (it created the node),\n// so the host can attach the IIR to the exact node without the (name,start_byte)\n// correlation the Go-side lift needs.\nexport interface ExtractedFunction {\n nodeId: string\n intent: FunctionIntent\n}\n", "// Shared side-effect classifier for language plugins. Given the structured parts\n// of a detected call \u2014 the receiver root, the method, and (when the root is an\n// imported package) its full import path \u2014 it returns the effect kind and a\n// confidence. Plugins emit these on each SideEffect so the host doesn't have to\n// re-derive them from the name string at compare time.\n//\n// Matching is structural (exact root, method substring, import-path prefix)\n// rather than substring-on-the-whole-name, which avoids false hits like\n// \"catalog.save\" being read as a log effect because the name contains \"log\".\n\nexport type EffectKind = \"network\" | \"db\" | \"io\" | \"log\" | \"mutation\" | \"unclassified\"\n// How an effect's kind was established. \"resolved\" means it matched a known\n// effectful API (an import path or a recognized client root) \u2014 deterministic\n// knowledge, not a probabilistic guess. \"heuristic\" means it was inferred from a\n// method-name verb or is uncategorized. The comparator treats an undeclared\n// resolved effect as an error and an undeclared heuristic one as a warning: it\n// should not fail verification on a guess.\nexport type EffectBasis = \"resolved\" | \"heuristic\"\n\nexport interface EffectClassifierInput {\n /** The called method / function name, e.g. \"Get\", \"track\". */\n method?: string\n /** The receiver root, e.g. \"http\", \"analytics\". */\n root?: string\n /** Full import path of the root when it is an imported package, e.g. \"net/http\". */\n importPath?: string\n}\n\n// Import-path prefixes are the strongest signal (a real, categorizable package).\nconst pathCategories: [EffectKind, string[]][] = [\n [\"network\", [\"net/http\", \"net/url\", \"net\", \"google.golang.org/grpc\", \"github.com/gorilla/websocket\"]],\n [\"db\", [\"database/sql\", \"gorm.io\", \"go.mongodb.org\", \"github.com/redis\", \"github.com/jmoiron/sqlx\"]],\n [\"io\", [\"os\", \"io\", \"io/ioutil\", \"bufio\", \"path/filepath\"]],\n // fmt's pure formatters (Sprintf, Errorf) are excluded before classification;\n // what reaches here (Println/Printf/Fprint\u2026) writes output.\n [\"log\", [\"log\", \"log/slog\", \"fmt\"]],\n]\n\n// Receiver roots that categorize a call even without a resolved import path\n// (e.g. member calls on a well-known client name).\nconst rootCategories: [EffectKind, string[]][] = [\n [\"network\", [\"http\", \"https\", \"fetch\", \"axios\", \"request\", \"requests\", \"grpc\", \"ws\", \"socket\", \"urllib\"]],\n [\"db\", [\"db\", \"sql\", \"database\", \"redis\", \"mongo\", \"session\", \"repository\", \"datastore\"]],\n [\"io\", [\"os\", \"io\", \"fs\", \"ioutil\", \"file\", \"pathlib\"]],\n [\"log\", [\"log\", \"logger\", \"logging\", \"console\", \"slog\", \"fmt\"]],\n]\n\n// Method names that signal an observable mutation/effect.\nconst mutationVerbs = [\"track\", \"send\", \"emit\", \"publish\", \"save\", \"create\", \"update\", \"delete\", \"write\"]\n\n/**\n * classifyEffect returns the (kind, basis) for a detected effectful call. A call\n * matched against a known effectful API \u2014 by import path or a recognized client\n * root \u2014 is \"resolved\" (deterministic). A call categorized only from a\n * method-name verb, or left uncategorized, is \"heuristic\" (a guess).\n */\nexport function classifyEffect(input: EffectClassifierInput): { kind: EffectKind; basis: EffectBasis } {\n const path = (input.importPath ?? \"\").toLowerCase()\n const root = (input.root ?? \"\").toLowerCase()\n const method = (input.method ?? \"\").toLowerCase()\n\n for (const [kind, prefixes] of pathCategories) {\n if (prefixes.some(p => path === p || path.startsWith(p + \"/\"))) return { kind, basis: \"resolved\" }\n }\n for (const [kind, roots] of rootCategories) {\n if (roots.includes(root)) return { kind, basis: \"resolved\" }\n }\n if (mutationVerbs.some(v => method.includes(v))) return { kind: \"mutation\", basis: \"heuristic\" }\n return { kind: \"unclassified\", basis: \"heuristic\" }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }