terse-cli 0.3.8 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/dist/_tmp_create_project.d.ts +0 -2
- package/dist/_tmp_create_project.d.ts.map +0 -1
- package/dist/_tmp_create_project.js +0 -17
- package/dist/_tmp_create_project.js.map +0 -1
- package/dist/_tmp_delete_agent.d.ts +0 -2
- package/dist/_tmp_delete_agent.d.ts.map +0 -1
- package/dist/_tmp_delete_agent.js +0 -14
- package/dist/_tmp_delete_agent.js.map +0 -1
- package/dist/_tmp_manual_trigger.d.ts +0 -2
- package/dist/_tmp_manual_trigger.d.ts.map +0 -1
- package/dist/_tmp_manual_trigger.js +0 -17
- package/dist/_tmp_manual_trigger.js.map +0 -1
- package/dist/_tmp_patch_active.d.ts +0 -2
- package/dist/_tmp_patch_active.d.ts.map +0 -1
- package/dist/_tmp_patch_active.js +0 -21
- package/dist/_tmp_patch_active.js.map +0 -1
- package/dist/attach.d.ts +0 -3
- package/dist/attach.d.ts.map +0 -1
- package/dist/attach.js +0 -61
- package/dist/attach.js.map +0 -1
- package/dist/auth.d.ts +0 -15
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js +0 -169
- package/dist/auth.js.map +0 -1
- package/dist/codegen.d.ts +0 -135
- package/dist/codegen.d.ts.map +0 -1
- package/dist/codegen.js +0 -1196
- package/dist/codegen.js.map +0 -1
- package/dist/commands/deployTheme.d.ts +0 -28
- package/dist/commands/deployTheme.d.ts.map +0 -1
- package/dist/commands/deployTheme.js +0 -68
- package/dist/commands/deployTheme.js.map +0 -1
- package/dist/commands/dev.d.ts +0 -3
- package/dist/commands/dev.d.ts.map +0 -1
- package/dist/commands/dev.js +0 -47
- package/dist/commands/dev.js.map +0 -1
- package/dist/commands/fsExec.d.ts +0 -8
- package/dist/commands/fsExec.d.ts.map +0 -1
- package/dist/commands/fsExec.js +0 -46
- package/dist/commands/fsExec.js.map +0 -1
- package/dist/commands/metaAdsScope.d.ts +0 -12
- package/dist/commands/metaAdsScope.d.ts.map +0 -1
- package/dist/commands/metaAdsScope.js +0 -120
- package/dist/commands/metaAdsScope.js.map +0 -1
- package/dist/commands/serve.d.ts +0 -1
- package/dist/commands/serve.d.ts.map +0 -1
- package/dist/commands/serve.js +0 -2
- package/dist/commands/serve.js.map +0 -1
- package/dist/commands/terseCliTheme.d.ts +0 -28
- package/dist/commands/terseCliTheme.d.ts.map +0 -1
- package/dist/commands/terseCliTheme.js +0 -68
- package/dist/commands/terseCliTheme.js.map +0 -1
- package/dist/commands/tool.d.ts +0 -6
- package/dist/commands/tool.d.ts.map +0 -1
- package/dist/commands/tool.js +0 -34
- package/dist/commands/tool.js.map +0 -1
- package/dist/data/standardSlackEmoji.d.ts +0 -2
- package/dist/data/standardSlackEmoji.d.ts.map +0 -1
- package/dist/data/standardSlackEmoji.js +0 -1978
- package/dist/data/standardSlackEmoji.js.map +0 -1
- package/dist/data/standardSlackEmoji.json +0 -1974
- package/dist/deploy.d.ts +0 -3
- package/dist/deploy.d.ts.map +0 -1
- package/dist/deploy.js +0 -124
- package/dist/deploy.js.map +0 -1
- package/dist/generate.d.ts +0 -3
- package/dist/generate.d.ts.map +0 -1
- package/dist/generate.js +0 -282
- package/dist/generate.js.map +0 -1
- package/dist/init.d.ts +0 -3
- package/dist/init.d.ts.map +0 -1
- package/dist/init.js +0 -98
- package/dist/init.js.map +0 -1
- package/dist/integrate.d.ts +0 -3
- package/dist/integrate.d.ts.map +0 -1
- package/dist/integrate.js +0 -214
- package/dist/integrate.js.map +0 -1
- package/dist/nonInteractive.d.ts +0 -20
- package/dist/nonInteractive.d.ts.map +0 -1
- package/dist/nonInteractive.js +0 -60
- package/dist/nonInteractive.js.map +0 -1
- package/dist/perf.d.ts +0 -9
- package/dist/perf.d.ts.map +0 -1
- package/dist/perf.js +0 -59
- package/dist/perf.js.map +0 -1
- package/dist/providers/codegen.test.d.ts +0 -2
- package/dist/providers/codegen.test.d.ts.map +0 -1
- package/dist/providers/codegen.test.js +0 -45
- package/dist/providers/codegen.test.js.map +0 -1
- package/dist/providers/python/PythonProvider.d.ts +0 -51
- package/dist/providers/python/PythonProvider.d.ts.map +0 -1
- package/dist/providers/python/PythonProvider.js +0 -414
- package/dist/providers/python/PythonProvider.js.map +0 -1
- package/dist/providers/python/PythonProvider.test.d.ts +0 -2
- package/dist/providers/python/PythonProvider.test.d.ts.map +0 -1
- package/dist/providers/python/PythonProvider.test.js +0 -169
- package/dist/providers/python/PythonProvider.test.js.map +0 -1
- package/dist/providers/python/preparePythonCodegenData.d.ts +0 -139
- package/dist/providers/python/preparePythonCodegenData.d.ts.map +0 -1
- package/dist/providers/python/preparePythonCodegenData.js +0 -452
- package/dist/providers/python/preparePythonCodegenData.js.map +0 -1
- package/dist/providers/python/pythonTemplateEngine.d.ts +0 -3
- package/dist/providers/python/pythonTemplateEngine.d.ts.map +0 -1
- package/dist/providers/python/pythonTemplateEngine.js +0 -30
- package/dist/providers/python/pythonTemplateEngine.js.map +0 -1
- package/dist/providers/shared/shellUtils.d.ts +0 -2
- package/dist/providers/shared/shellUtils.d.ts.map +0 -1
- package/dist/providers/shared/shellUtils.js +0 -25
- package/dist/providers/shared/shellUtils.js.map +0 -1
- package/dist/providers/typescript/attioProjection.d.ts +0 -50
- package/dist/providers/typescript/attioProjection.d.ts.map +0 -1
- package/dist/providers/typescript/attioProjection.js +0 -358
- package/dist/providers/typescript/attioProjection.js.map +0 -1
- package/dist/providers/typescript/modules/cursor/CursorModule.d.ts +0 -20
- package/dist/providers/typescript/modules/cursor/CursorModule.d.ts.map +0 -1
- package/dist/providers/typescript/modules/cursor/CursorModule.js +0 -27
- package/dist/providers/typescript/modules/cursor/CursorModule.js.map +0 -1
- package/dist/providers/typescript/prepareCodegenData.d.ts +0 -166
- package/dist/providers/typescript/prepareCodegenData.d.ts.map +0 -1
- package/dist/providers/typescript/prepareCodegenData.js +0 -1431
- package/dist/providers/typescript/prepareCodegenData.js.map +0 -1
- package/dist/providers/typescript/prepareCodegenData.test.d.ts +0 -2
- package/dist/providers/typescript/prepareCodegenData.test.d.ts.map +0 -1
- package/dist/providers/typescript/prepareCodegenData.test.js +0 -54
- package/dist/providers/typescript/prepareCodegenData.test.js.map +0 -1
- package/dist/providers/typescript/projectedTypes.d.ts +0 -11
- package/dist/providers/typescript/projectedTypes.d.ts.map +0 -1
- package/dist/providers/typescript/projectedTypes.js +0 -53
- package/dist/providers/typescript/projectedTypes.js.map +0 -1
- package/dist/providers/typescript/projectionPrinter.d.ts +0 -26
- package/dist/providers/typescript/projectionPrinter.d.ts.map +0 -1
- package/dist/providers/typescript/projectionPrinter.js +0 -110
- package/dist/providers/typescript/projectionPrinter.js.map +0 -1
- package/dist/providers/typescript/replayLogDedupe.d.ts +0 -6
- package/dist/providers/typescript/replayLogDedupe.d.ts.map +0 -1
- package/dist/providers/typescript/replayLogDedupe.js +0 -156
- package/dist/providers/typescript/replayLogDedupe.js.map +0 -1
- package/dist/providers/typescript/triggerPayloads.d.ts +0 -7
- package/dist/providers/typescript/triggerPayloads.d.ts.map +0 -1
- package/dist/providers/typescript/triggerPayloads.js +0 -60
- package/dist/providers/typescript/triggerPayloads.js.map +0 -1
- package/dist/providers/typescript/triggerTypeRegistry.d.ts +0 -7
- package/dist/providers/typescript/triggerTypeRegistry.d.ts.map +0 -1
- package/dist/providers/typescript/triggerTypeRegistry.js +0 -198
- package/dist/providers/typescript/triggerTypeRegistry.js.map +0 -1
- package/dist/run.d.ts +0 -3
- package/dist/run.d.ts.map +0 -1
- package/dist/run.js +0 -72
- package/dist/run.js.map +0 -1
- package/dist/sampleEventCache.d.ts +0 -9
- package/dist/sampleEventCache.d.ts.map +0 -1
- package/dist/sampleEventCache.js +0 -47
- package/dist/sampleEventCache.js.map +0 -1
- package/dist/sampleEventId.d.ts +0 -9
- package/dist/sampleEventId.d.ts.map +0 -1
- package/dist/sampleEventId.js +0 -16
- package/dist/sampleEventId.js.map +0 -1
- package/dist/shared/ApiRoutes.d.ts +0 -348
- package/dist/shared/ApiRoutes.d.ts.map +0 -1
- package/dist/shared/ApiRoutes.js +0 -325
- package/dist/shared/ApiRoutes.js.map +0 -1
- package/dist/shared/ApprovalTypes.d.ts +0 -29
- package/dist/shared/ApprovalTypes.d.ts.map +0 -1
- package/dist/shared/ApprovalTypes.js +0 -8
- package/dist/shared/ApprovalTypes.js.map +0 -1
- package/dist/shared/ClientBoundTools.d.ts +0 -37
- package/dist/shared/ClientBoundTools.d.ts.map +0 -1
- package/dist/shared/ClientBoundTools.js +0 -37
- package/dist/shared/ClientBoundTools.js.map +0 -1
- package/dist/shared/Configs.d.ts +0 -488
- package/dist/shared/Configs.d.ts.map +0 -1
- package/dist/shared/Configs.js +0 -785
- package/dist/shared/Configs.js.map +0 -1
- package/dist/shared/DonatedState.d.ts +0 -5
- package/dist/shared/DonatedState.d.ts.map +0 -1
- package/dist/shared/DonatedState.js +0 -13
- package/dist/shared/DonatedState.js.map +0 -1
- package/dist/shared/Entities.d.ts +0 -8
- package/dist/shared/Entities.d.ts.map +0 -1
- package/dist/shared/Entities.js +0 -9
- package/dist/shared/Entities.js.map +0 -1
- package/dist/shared/FrontendRoutes.d.ts +0 -54
- package/dist/shared/FrontendRoutes.d.ts.map +0 -1
- package/dist/shared/FrontendRoutes.js +0 -54
- package/dist/shared/FrontendRoutes.js.map +0 -1
- package/dist/shared/Integrations.d.ts +0 -235
- package/dist/shared/Integrations.d.ts.map +0 -1
- package/dist/shared/Integrations.js +0 -143
- package/dist/shared/Integrations.js.map +0 -1
- package/dist/shared/InvalidationKeys.d.ts +0 -53
- package/dist/shared/InvalidationKeys.d.ts.map +0 -1
- package/dist/shared/InvalidationKeys.js +0 -166
- package/dist/shared/InvalidationKeys.js.map +0 -1
- package/dist/shared/ModelEvents.d.ts +0 -176
- package/dist/shared/ModelEvents.d.ts.map +0 -1
- package/dist/shared/ModelEvents.js +0 -29
- package/dist/shared/ModelEvents.js.map +0 -1
- package/dist/shared/Notifications.d.ts +0 -43
- package/dist/shared/Notifications.d.ts.map +0 -1
- package/dist/shared/Notifications.js +0 -6
- package/dist/shared/Notifications.js.map +0 -1
- package/dist/shared/RunHistoryTypes.d.ts +0 -95
- package/dist/shared/RunHistoryTypes.d.ts.map +0 -1
- package/dist/shared/RunHistoryTypes.js +0 -11
- package/dist/shared/RunHistoryTypes.js.map +0 -1
- package/dist/shared/SentNotifications.d.ts +0 -30
- package/dist/shared/SentNotifications.d.ts.map +0 -1
- package/dist/shared/SentNotifications.js +0 -13
- package/dist/shared/SentNotifications.js.map +0 -1
- package/dist/shared/SocketEvents.d.ts +0 -49
- package/dist/shared/SocketEvents.d.ts.map +0 -1
- package/dist/shared/SocketEvents.js +0 -53
- package/dist/shared/SocketEvents.js.map +0 -1
- package/dist/shared/Survey.d.ts +0 -10
- package/dist/shared/Survey.d.ts.map +0 -1
- package/dist/shared/Survey.js +0 -2
- package/dist/shared/Survey.js.map +0 -1
- package/dist/shared/TicketSystem.d.ts +0 -154
- package/dist/shared/TicketSystem.d.ts.map +0 -1
- package/dist/shared/TicketSystem.js +0 -21
- package/dist/shared/TicketSystem.js.map +0 -1
- package/dist/shared/ToolDisplayUtils.d.ts +0 -30
- package/dist/shared/ToolDisplayUtils.d.ts.map +0 -1
- package/dist/shared/ToolDisplayUtils.js +0 -1163
- package/dist/shared/ToolDisplayUtils.js.map +0 -1
- package/dist/shared/ToolsTypes.d.ts +0 -18
- package/dist/shared/ToolsTypes.d.ts.map +0 -1
- package/dist/shared/ToolsTypes.js +0 -2
- package/dist/shared/ToolsTypes.js.map +0 -1
- package/dist/shared/types.d.ts +0 -1732
- package/dist/shared/types.d.ts.map +0 -1
- package/dist/shared/types.js +0 -17
- package/dist/shared/types.js.map +0 -1
- package/dist/test.d.ts +0 -3
- package/dist/test.d.ts.map +0 -1
- package/dist/test.js +0 -82
- package/dist/test.js.map +0 -1
- package/dist/triggerParseDetail.d.ts +0 -8
- package/dist/triggerParseDetail.d.ts.map +0 -1
- package/dist/triggerParseDetail.js +0 -30
- package/dist/triggerParseDetail.js.map +0 -1
- package/dist/util.d.ts +0 -3
- package/dist/util.d.ts.map +0 -1
- package/dist/util.js +0 -5
- package/dist/util.js.map +0 -1
- package/templates/typescript/codegen/.impeccable/hook.cache.json +0 -1
package/dist/codegen.js
DELETED
|
@@ -1,1196 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure codegen function: integration data → TypeScript source text.
|
|
3
|
-
* No I/O — all side effects live in generate.ts.
|
|
4
|
-
*/
|
|
5
|
-
const EMPTY_SECTION = { code: "", imports: new Set() };
|
|
6
|
-
// ── Helpers ───────────────────────────────────────────────────────────
|
|
7
|
-
function toPascalCase(s) {
|
|
8
|
-
return s
|
|
9
|
-
.replace(/[^a-zA-Z0-9]+/g, " ")
|
|
10
|
-
.trim()
|
|
11
|
-
.split(/\s+/)
|
|
12
|
-
.map(w => w.charAt(0).toUpperCase() + w.slice(1))
|
|
13
|
-
.join("");
|
|
14
|
-
}
|
|
15
|
-
function sectionHeader(name) {
|
|
16
|
-
const dashes = "─".repeat(Math.max(1, 58 - name.length));
|
|
17
|
-
return `// ── ${name} ${dashes}`;
|
|
18
|
-
}
|
|
19
|
-
function escapeString(s) {
|
|
20
|
-
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
21
|
-
}
|
|
22
|
-
function toGeneratedIdentifier(raw, fallback) {
|
|
23
|
-
let name = toPascalCase(raw || fallback);
|
|
24
|
-
if (!name)
|
|
25
|
-
name = fallback;
|
|
26
|
-
if (/^\d/.test(name))
|
|
27
|
-
name = `_${name}`;
|
|
28
|
-
return name;
|
|
29
|
-
}
|
|
30
|
-
function isProbablyAttioMultiValue(attr) {
|
|
31
|
-
const slug = (attr.api_slug || "").toLowerCase();
|
|
32
|
-
const type = (attr.type || "").toLowerCase();
|
|
33
|
-
return (type.includes("multi") ||
|
|
34
|
-
type.includes("array") ||
|
|
35
|
-
type.includes("list") ||
|
|
36
|
-
slug === "email_addresses" ||
|
|
37
|
-
slug === "domains" ||
|
|
38
|
-
slug === "phone_numbers" ||
|
|
39
|
-
slug === "social_profiles" ||
|
|
40
|
-
slug === "links" ||
|
|
41
|
-
slug === "tags" ||
|
|
42
|
-
slug.endsWith("_addresses") ||
|
|
43
|
-
slug.endsWith("_ids"));
|
|
44
|
-
}
|
|
45
|
-
function attioAttributeBaseType(attr) {
|
|
46
|
-
const slug = (attr.api_slug || "").toLowerCase();
|
|
47
|
-
const type = (attr.type || "").toLowerCase();
|
|
48
|
-
if (type.includes("checkbox") || type.includes("boolean"))
|
|
49
|
-
return "boolean";
|
|
50
|
-
if (type.includes("number") || type.includes("currency") || type.includes("rating") || type.includes("percent"))
|
|
51
|
-
return "number";
|
|
52
|
-
if (type.includes("date") || type.includes("time"))
|
|
53
|
-
return "string";
|
|
54
|
-
if (type.includes("email") ||
|
|
55
|
-
type.includes("domain") ||
|
|
56
|
-
type.includes("phone") ||
|
|
57
|
-
type.includes("url") ||
|
|
58
|
-
type.includes("select") ||
|
|
59
|
-
type.includes("status") ||
|
|
60
|
-
type.includes("text") ||
|
|
61
|
-
type.includes("string") ||
|
|
62
|
-
type.includes("name")) {
|
|
63
|
-
return "string";
|
|
64
|
-
}
|
|
65
|
-
if (type.includes("location") ||
|
|
66
|
-
type.includes("address") ||
|
|
67
|
-
type.includes("reference") ||
|
|
68
|
-
type.includes("record") ||
|
|
69
|
-
type.includes("actor")) {
|
|
70
|
-
return "Record<string, unknown>";
|
|
71
|
-
}
|
|
72
|
-
if (slug === "email_addresses" || slug === "domains" || slug === "phone_numbers" || slug === "name") {
|
|
73
|
-
return "string";
|
|
74
|
-
}
|
|
75
|
-
return "unknown";
|
|
76
|
-
}
|
|
77
|
-
function attioAttributeInputTsType(attr) {
|
|
78
|
-
const baseType = attioAttributeBaseType(attr);
|
|
79
|
-
if (!isProbablyAttioMultiValue(attr))
|
|
80
|
-
return baseType;
|
|
81
|
-
return baseType.includes("|") ? `(${baseType})[]` : `${baseType}[]`;
|
|
82
|
-
}
|
|
83
|
-
function attioAttributeRecordTsType(attr) {
|
|
84
|
-
const baseType = attioAttributeBaseType(attr);
|
|
85
|
-
if (!isProbablyAttioMultiValue(attr))
|
|
86
|
-
return baseType;
|
|
87
|
-
return baseType.includes("|") ? `(${baseType})[]` : `${baseType}[]`;
|
|
88
|
-
}
|
|
89
|
-
function renderAttioObjectValueShape(attributes, mode) {
|
|
90
|
-
if (attributes.length === 0)
|
|
91
|
-
return "Record<string, unknown>";
|
|
92
|
-
const lines = attributes.map(attr => {
|
|
93
|
-
const valueType = mode === "input" ? attioAttributeInputTsType(attr) : attioAttributeRecordTsType(attr);
|
|
94
|
-
return ` "${escapeString(attr.api_slug || "")}"?: ${valueType}`;
|
|
95
|
-
});
|
|
96
|
-
return `{\n${lines.join("\n")}\n}`;
|
|
97
|
-
}
|
|
98
|
-
/** Map a tool's integration key back to the IntegrationType enum value used by ConfigInstance. */
|
|
99
|
-
function toolIntegrationToIntegrationType(toolIntegration) {
|
|
100
|
-
switch (toolIntegration) {
|
|
101
|
-
case "jira":
|
|
102
|
-
case "confluence":
|
|
103
|
-
return "atlassian";
|
|
104
|
-
default:
|
|
105
|
-
return toolIntegration;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
function renderStringLiteralUnion(values) {
|
|
109
|
-
if (values.length === 0)
|
|
110
|
-
return "never";
|
|
111
|
-
return values.map(value => `"${escapeString(value)}"`).join(" | ");
|
|
112
|
-
}
|
|
113
|
-
function buildSkillToolTypeForIntegration(tools, integrationType) {
|
|
114
|
-
const toolNames = tools
|
|
115
|
-
.filter(tool => toolIntegrationToIntegrationType(tool.integration.toLowerCase()) === integrationType)
|
|
116
|
-
.filter(tool => !tool.isReadOnly || tool.supportsApproval)
|
|
117
|
-
.map(tool => tool.name)
|
|
118
|
-
.sort();
|
|
119
|
-
return renderStringLiteralUnion(toolNames);
|
|
120
|
-
}
|
|
121
|
-
function generateResourceClass(className, fields, staticNameField, items) {
|
|
122
|
-
const lines = [];
|
|
123
|
-
lines.push(`export class ${className} {`);
|
|
124
|
-
const ctorParams = fields.map(f => `public readonly ${f.classField}: ${f.type}`).join(", ");
|
|
125
|
-
lines.push(` constructor(${ctorParams}) {}`);
|
|
126
|
-
if (items.length > 0) {
|
|
127
|
-
lines.push("");
|
|
128
|
-
const usedNames = new Set();
|
|
129
|
-
for (const item of items) {
|
|
130
|
-
let name = toPascalCase(String(item[staticNameField] || "Unknown"));
|
|
131
|
-
if (!name || /^\d/.test(name))
|
|
132
|
-
name = `_${name}`;
|
|
133
|
-
while (usedNames.has(name))
|
|
134
|
-
name += "_";
|
|
135
|
-
usedNames.add(name);
|
|
136
|
-
const args = fields.map(f => {
|
|
137
|
-
const val = item[f.sourceField];
|
|
138
|
-
if (typeof val === "number")
|
|
139
|
-
return String(val);
|
|
140
|
-
return `"${escapeString(String(val ?? ""))}"`;
|
|
141
|
-
}).join(", ");
|
|
142
|
-
lines.push(` static ${name} = new ${className}(${args})`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
lines.push("}");
|
|
146
|
-
return lines.join("\n");
|
|
147
|
-
}
|
|
148
|
-
// ── GitHub ────────────────────────────────────────────────────────────
|
|
149
|
-
function generateGitHubSection(instances, tools) {
|
|
150
|
-
if (instances.length === 0)
|
|
151
|
-
return EMPTY_SECTION;
|
|
152
|
-
const inst = instances[0];
|
|
153
|
-
const imports = new Set(["GitHubConfig"]);
|
|
154
|
-
const parts = [sectionHeader("GitHub"), ""];
|
|
155
|
-
const id = inst.integration.id;
|
|
156
|
-
const skillToolType = buildSkillToolTypeForIntegration(tools, "github");
|
|
157
|
-
const toStaticName = (raw, fallback) => {
|
|
158
|
-
let name = toPascalCase(raw || fallback);
|
|
159
|
-
if (!name)
|
|
160
|
-
name = fallback;
|
|
161
|
-
if (/^\d/.test(name))
|
|
162
|
-
name = `_${name}`;
|
|
163
|
-
return name;
|
|
164
|
-
};
|
|
165
|
-
const repositoriesWithFullName = inst.repositories.map(repo => {
|
|
166
|
-
const owner = repo.owner || "UnknownOwner";
|
|
167
|
-
const fullName = repo.owner && repo.name ? `${repo.owner}/${repo.name}` : repo.name;
|
|
168
|
-
return { ...repo, owner, fullName };
|
|
169
|
-
});
|
|
170
|
-
const ownerEntries = new Map();
|
|
171
|
-
const usedOwnerNames = new Set();
|
|
172
|
-
for (const repo of repositoriesWithFullName) {
|
|
173
|
-
if (!ownerEntries.has(repo.owner)) {
|
|
174
|
-
let ownerStaticName = toStaticName(repo.owner, "UnknownOwner");
|
|
175
|
-
while (usedOwnerNames.has(ownerStaticName))
|
|
176
|
-
ownerStaticName += "_";
|
|
177
|
-
usedOwnerNames.add(ownerStaticName);
|
|
178
|
-
ownerEntries.set(repo.owner, { staticName: ownerStaticName, repos: [] });
|
|
179
|
-
}
|
|
180
|
-
ownerEntries.get(repo.owner).repos.push(repo);
|
|
181
|
-
}
|
|
182
|
-
parts.push("export class GithubOwner {");
|
|
183
|
-
parts.push(" constructor(public readonly name: string) {}");
|
|
184
|
-
if (ownerEntries.size > 0) {
|
|
185
|
-
parts.push("");
|
|
186
|
-
for (const [owner, data] of ownerEntries) {
|
|
187
|
-
parts.push(` static ${data.staticName} = new GithubOwner("${escapeString(owner)}")`);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
parts.push("}");
|
|
191
|
-
parts.push("");
|
|
192
|
-
parts.push("export class Repos {");
|
|
193
|
-
parts.push(" constructor(");
|
|
194
|
-
parts.push(" public readonly repositoryId: number,");
|
|
195
|
-
parts.push(" public readonly name: string,");
|
|
196
|
-
parts.push(" public readonly owner: GithubOwner,");
|
|
197
|
-
parts.push(" public readonly fullName: string");
|
|
198
|
-
parts.push(" ) {}");
|
|
199
|
-
if (ownerEntries.size > 0) {
|
|
200
|
-
parts.push("");
|
|
201
|
-
for (const [, data] of ownerEntries) {
|
|
202
|
-
const usedRepoNames = new Set();
|
|
203
|
-
parts.push(` static ${data.staticName} = {`);
|
|
204
|
-
for (const repo of data.repos) {
|
|
205
|
-
let repoStaticName = toStaticName(repo.name, "Repos");
|
|
206
|
-
while (usedRepoNames.has(repoStaticName))
|
|
207
|
-
repoStaticName += "_";
|
|
208
|
-
usedRepoNames.add(repoStaticName);
|
|
209
|
-
parts.push(` ${repoStaticName}: new Repos(${repo.id}, "${escapeString(repo.name)}", GithubOwner.${data.staticName}, "${escapeString(repo.fullName)}"),`);
|
|
210
|
-
}
|
|
211
|
-
parts.push(" } as const");
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
parts.push("}");
|
|
215
|
-
parts.push("");
|
|
216
|
-
imports.add("GitHubEventType");
|
|
217
|
-
imports.add("TypedTrigger");
|
|
218
|
-
imports.add("TypedSkill");
|
|
219
|
-
imports.add("GithubPRInputEvent");
|
|
220
|
-
imports.add("GithubPushInputEvent");
|
|
221
|
-
imports.add("GithubInputEvent");
|
|
222
|
-
// Namespace object with typed event triggers
|
|
223
|
-
parts.push("export const GitHub = {");
|
|
224
|
-
parts.push(` /** Trigger on push to a repository */`);
|
|
225
|
-
parts.push(` onPush(opts: { repo: Repos }): TypedTrigger<GithubPushInputEvent> {`);
|
|
226
|
-
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PUSH]) as TypedTrigger<GithubPushInputEvent>`);
|
|
227
|
-
parts.push(" },");
|
|
228
|
-
parts.push(` /** Trigger when a pull request is opened */`);
|
|
229
|
-
parts.push(` onPROpened(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {`);
|
|
230
|
-
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PR_OPENED]) as TypedTrigger<GithubPRInputEvent>`);
|
|
231
|
-
parts.push(" },");
|
|
232
|
-
parts.push(` /** Trigger when a pull request is merged */`);
|
|
233
|
-
parts.push(` onPRMerged(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {`);
|
|
234
|
-
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PR_MERGED]) as TypedTrigger<GithubPRInputEvent>`);
|
|
235
|
-
parts.push(" },");
|
|
236
|
-
parts.push(` /** Trigger when a pull request is closed */`);
|
|
237
|
-
parts.push(` onPRClosed(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {`);
|
|
238
|
-
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PR_CLOSED]) as TypedTrigger<GithubPRInputEvent>`);
|
|
239
|
-
parts.push(" },");
|
|
240
|
-
parts.push(` /** Trigger on any pull request event */`);
|
|
241
|
-
parts.push(` onPR(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {`);
|
|
242
|
-
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PR_OPENED, GitHubEventType.PR_MERGED, GitHubEventType.PR_CLOSED, GitHubEventType.PR_SYNCHRONIZE]) as TypedTrigger<GithubPRInputEvent>`);
|
|
243
|
-
parts.push(" },");
|
|
244
|
-
parts.push(` /** Trigger on specific GitHub events for the given repositories */`);
|
|
245
|
-
parts.push(` trigger(opts: { repos: Repos[]; eventTypes?: GitHubEventType[] }): TypedTrigger<GithubInputEvent> {`);
|
|
246
|
-
parts.push(` return new GitHubConfig("${id}", opts.repos.map(r => r.repositoryId), opts.eventTypes) as TypedTrigger<GithubInputEvent>`);
|
|
247
|
-
parts.push(" },");
|
|
248
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
249
|
-
parts.push(` skill(opts: { repos: Repos[] }): TypedSkill<${skillToolType}> {`);
|
|
250
|
-
parts.push(` return new GitHubConfig("${id}", opts.repos.map(r => r.repositoryId)) as TypedSkill<${skillToolType}>`);
|
|
251
|
-
parts.push(" },");
|
|
252
|
-
parts.push("}");
|
|
253
|
-
parts.push("");
|
|
254
|
-
return { code: parts.join("\n"), imports };
|
|
255
|
-
}
|
|
256
|
-
// ── Gmail ─────────────────────────────────────────────────────────────
|
|
257
|
-
function generateGmailSection(instances, tools) {
|
|
258
|
-
if (instances.length === 0)
|
|
259
|
-
return EMPTY_SECTION;
|
|
260
|
-
const imports = new Set(["GmailConfig", "GmailOutputConfig", "GmailDraftOutputConfig", "TypedSkill"]);
|
|
261
|
-
const parts = [sectionHeader("Gmail"), ""];
|
|
262
|
-
const id = instances[0].id;
|
|
263
|
-
const skillToolType = buildSkillToolTypeForIntegration(tools, "gmail");
|
|
264
|
-
imports.add("GmailEventType");
|
|
265
|
-
parts.push("export const Gmail = {");
|
|
266
|
-
parts.push(` /** Trigger when a new email is received */`);
|
|
267
|
-
parts.push(` onEmail(): GmailConfig {`);
|
|
268
|
-
parts.push(` return new GmailConfig("${id}", [GmailEventType.EMAIL_RECEIVED])`);
|
|
269
|
-
parts.push(" },");
|
|
270
|
-
parts.push(` /** Trigger on all Gmail events */`);
|
|
271
|
-
parts.push(` trigger(opts?: { eventTypes?: GmailEventType[] }): GmailConfig {`);
|
|
272
|
-
parts.push(` return new GmailConfig("${id}", opts?.eventTypes)`);
|
|
273
|
-
parts.push(" },");
|
|
274
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
275
|
-
parts.push(` skill(): TypedSkill<${skillToolType}> {`);
|
|
276
|
-
parts.push(` return new GmailOutputConfig("${id}") as TypedSkill<${skillToolType}>`);
|
|
277
|
-
parts.push(" },");
|
|
278
|
-
parts.push(` /** Use in \`skills[]\` — creates draft emails */`);
|
|
279
|
-
parts.push(` draftSkill(): TypedSkill<${skillToolType}> {`);
|
|
280
|
-
parts.push(` return new GmailDraftOutputConfig("${id}") as TypedSkill<${skillToolType}>`);
|
|
281
|
-
parts.push(" },");
|
|
282
|
-
parts.push("}");
|
|
283
|
-
parts.push("");
|
|
284
|
-
return { code: parts.join("\n"), imports };
|
|
285
|
-
}
|
|
286
|
-
// ── Slack ──────────────────────────────────────────────────────────────
|
|
287
|
-
function generateSlackSection(instances, tools) {
|
|
288
|
-
if (instances.length === 0)
|
|
289
|
-
return EMPTY_SECTION;
|
|
290
|
-
const inst = instances[0];
|
|
291
|
-
const imports = new Set(["SlackConfig", "SlackOutputConfig", "TypedSkill"]);
|
|
292
|
-
const parts = [sectionHeader("Slack"), ""];
|
|
293
|
-
const id = inst.id;
|
|
294
|
-
const skillToolType = buildSkillToolTypeForIntegration(tools, "slack");
|
|
295
|
-
parts.push(generateResourceClass("SlackChannel", [
|
|
296
|
-
{ classField: "channelId", type: "string", sourceField: "id" },
|
|
297
|
-
{ classField: "name", type: "string", sourceField: "name" },
|
|
298
|
-
], "name", inst.channels));
|
|
299
|
-
parts.push("");
|
|
300
|
-
imports.add("SlackEventType");
|
|
301
|
-
// Namespace object with typed event triggers
|
|
302
|
-
parts.push("export const Slack = {");
|
|
303
|
-
parts.push(` /** Trigger on any message in a channel */`);
|
|
304
|
-
parts.push(` onMessage(opts: { channel: SlackChannel; userIds?: string[] }): SlackConfig {`);
|
|
305
|
-
parts.push(` return new SlackConfig("${id}", opts.channel.channelId, opts.channel.name, false, opts.userIds, [SlackEventType.MESSAGE])`);
|
|
306
|
-
parts.push(" },");
|
|
307
|
-
parts.push(` /** Trigger on direct messages to the bot */`);
|
|
308
|
-
parts.push(` onDm(opts?: { userIds?: string[] }): SlackConfig {`);
|
|
309
|
-
parts.push(` return new SlackConfig("${id}", undefined, undefined, true, opts?.userIds, [SlackEventType.MESSAGE])`);
|
|
310
|
-
parts.push(" },");
|
|
311
|
-
parts.push(` /** Trigger on all Slack events (messages + DMs) for a channel */`);
|
|
312
|
-
parts.push(` trigger(opts?: { channel?: SlackChannel; listenToUserDms?: boolean; userIds?: string[]; eventTypes?: SlackEventType[] }): SlackConfig {`);
|
|
313
|
-
parts.push(` return new SlackConfig("${id}", opts?.channel?.channelId, opts?.channel?.name, opts?.listenToUserDms, opts?.userIds, opts?.eventTypes)`);
|
|
314
|
-
parts.push(" },");
|
|
315
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
316
|
-
parts.push(` skill(opts?: { channel?: SlackChannel; userIds?: string[]; userNames?: string[]; listenToUserDms?: boolean }): TypedSkill<${skillToolType}> {`);
|
|
317
|
-
parts.push(` return new SlackOutputConfig("${id}", opts?.channel?.channelId, opts?.channel?.name, opts?.userIds, opts?.userNames, opts?.listenToUserDms) as TypedSkill<${skillToolType}>`);
|
|
318
|
-
parts.push(" },");
|
|
319
|
-
parts.push("}");
|
|
320
|
-
parts.push("");
|
|
321
|
-
return { code: parts.join("\n"), imports };
|
|
322
|
-
}
|
|
323
|
-
// ── Figma ─────────────────────────────────────────────────────────────
|
|
324
|
-
function generateFigmaSection(instances) {
|
|
325
|
-
if (instances.length === 0)
|
|
326
|
-
return EMPTY_SECTION;
|
|
327
|
-
const imports = new Set(["FigmaConfig"]);
|
|
328
|
-
const parts = [sectionHeader("Figma"), ""];
|
|
329
|
-
const id = instances[0].id;
|
|
330
|
-
imports.add("FigmaEventType");
|
|
331
|
-
parts.push("export const Figma = {");
|
|
332
|
-
parts.push(` /** Trigger when a comment is added to a Figma file */`);
|
|
333
|
-
parts.push(` onComment(opts: { fileKey: string; fileName: string; teamId: string }): FigmaConfig {`);
|
|
334
|
-
parts.push(` return new FigmaConfig("${id}", opts.fileKey, opts.fileName, opts.teamId, [FigmaEventType.FILE_COMMENT])`);
|
|
335
|
-
parts.push(" },");
|
|
336
|
-
parts.push(` /** Trigger on all Figma events */`);
|
|
337
|
-
parts.push(` trigger(opts: { fileKey: string; fileName: string; teamId: string; eventTypes?: FigmaEventType[] }): FigmaConfig {`);
|
|
338
|
-
parts.push(` return new FigmaConfig("${id}", opts.fileKey, opts.fileName, opts.teamId, opts.eventTypes)`);
|
|
339
|
-
parts.push(" },");
|
|
340
|
-
parts.push("}");
|
|
341
|
-
parts.push("");
|
|
342
|
-
return { code: parts.join("\n"), imports };
|
|
343
|
-
}
|
|
344
|
-
// ── Linear ────────────────────────────────────────────────────────────
|
|
345
|
-
function generateLinearSection(instances, tools) {
|
|
346
|
-
if (instances.length === 0)
|
|
347
|
-
return EMPTY_SECTION;
|
|
348
|
-
const inst = instances[0];
|
|
349
|
-
const imports = new Set(["LinearInputConfig", "LinearOutputConfig", "TypedSkill"]);
|
|
350
|
-
const parts = [sectionHeader("Linear"), ""];
|
|
351
|
-
const id = inst.id;
|
|
352
|
-
const skillToolType = buildSkillToolTypeForIntegration(tools, "linear");
|
|
353
|
-
parts.push(generateResourceClass("LinearTeam", [
|
|
354
|
-
{ classField: "teamId", type: "string", sourceField: "id" },
|
|
355
|
-
{ classField: "name", type: "string", sourceField: "name" },
|
|
356
|
-
{ classField: "key", type: "string", sourceField: "key" },
|
|
357
|
-
], "name", inst.teams));
|
|
358
|
-
parts.push("");
|
|
359
|
-
imports.add("LinearEventType");
|
|
360
|
-
parts.push("export const Linear = {");
|
|
361
|
-
parts.push(` /** Trigger when a new issue is created */`);
|
|
362
|
-
parts.push(` onIssueCreated(opts?: { projectId?: string; projectName?: string }): LinearInputConfig {`);
|
|
363
|
-
parts.push(` return new LinearInputConfig("${id}", opts?.projectId, opts?.projectName, [LinearEventType.ISSUE_CREATED])`);
|
|
364
|
-
parts.push(" },");
|
|
365
|
-
parts.push(` /** Trigger when an issue is updated */`);
|
|
366
|
-
parts.push(` onIssueUpdated(opts?: { projectId?: string; projectName?: string }): LinearInputConfig {`);
|
|
367
|
-
parts.push(` return new LinearInputConfig("${id}", opts?.projectId, opts?.projectName, [LinearEventType.ISSUE_UPDATED])`);
|
|
368
|
-
parts.push(" },");
|
|
369
|
-
parts.push(` /** Trigger when a comment is added to an issue */`);
|
|
370
|
-
parts.push(` onComment(opts?: { projectId?: string; projectName?: string }): LinearInputConfig {`);
|
|
371
|
-
parts.push(` return new LinearInputConfig("${id}", opts?.projectId, opts?.projectName, [LinearEventType.COMMENT_CREATED])`);
|
|
372
|
-
parts.push(" },");
|
|
373
|
-
parts.push(` /** Trigger on all Linear events */`);
|
|
374
|
-
parts.push(` trigger(opts?: { projectId?: string; projectName?: string; eventTypes?: LinearEventType[] }): LinearInputConfig {`);
|
|
375
|
-
parts.push(` return new LinearInputConfig("${id}", opts?.projectId, opts?.projectName, opts?.eventTypes)`);
|
|
376
|
-
parts.push(" },");
|
|
377
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
378
|
-
parts.push(` skill(opts?: { team?: LinearTeam; projectId?: string; projectName?: string }): TypedSkill<${skillToolType}> {`);
|
|
379
|
-
parts.push(` return new LinearOutputConfig("${id}", opts?.team?.teamId, opts?.team?.name, opts?.projectId, opts?.projectName) as TypedSkill<${skillToolType}>`);
|
|
380
|
-
parts.push(" },");
|
|
381
|
-
parts.push("}");
|
|
382
|
-
parts.push("");
|
|
383
|
-
return { code: parts.join("\n"), imports };
|
|
384
|
-
}
|
|
385
|
-
// ── Jira & Confluence (Atlassian) ─────────────────────────────────────
|
|
386
|
-
function generateAtlassianSection(instances, tools) {
|
|
387
|
-
if (instances.length === 0)
|
|
388
|
-
return EMPTY_SECTION;
|
|
389
|
-
const inst = instances[0];
|
|
390
|
-
const imports = new Set(["JiraConfig", "ConfluenceConfig", "TypedSkill"]);
|
|
391
|
-
const parts = [sectionHeader("Jira & Confluence"), ""];
|
|
392
|
-
const id = inst.id;
|
|
393
|
-
const atlassianSkillToolType = buildSkillToolTypeForIntegration(tools, "atlassian");
|
|
394
|
-
parts.push(generateResourceClass("JiraProject", [
|
|
395
|
-
{ classField: "projectKey", type: "string", sourceField: "key" },
|
|
396
|
-
{ classField: "projectId", type: "string", sourceField: "id" },
|
|
397
|
-
{ classField: "name", type: "string", sourceField: "name" },
|
|
398
|
-
], "name", inst.jiraProjects));
|
|
399
|
-
parts.push("");
|
|
400
|
-
parts.push(generateResourceClass("ConfluencePage", [
|
|
401
|
-
{ classField: "pageId", type: "string", sourceField: "id" },
|
|
402
|
-
{ classField: "title", type: "string", sourceField: "title" },
|
|
403
|
-
{ classField: "spaceId", type: "string", sourceField: "spaceId" },
|
|
404
|
-
{ classField: "spaceName", type: "string", sourceField: "spaceName" },
|
|
405
|
-
], "title", inst.confluencePages));
|
|
406
|
-
parts.push("");
|
|
407
|
-
imports.add("JiraEventType");
|
|
408
|
-
parts.push("export const Jira = {");
|
|
409
|
-
parts.push(` /** Trigger when a Jira issue is created */`);
|
|
410
|
-
parts.push(` onIssueCreated(opts?: { project?: JiraProject }): JiraConfig {`);
|
|
411
|
-
parts.push(` return new JiraConfig("${id}", opts?.project?.projectKey, opts?.project?.projectId, [JiraEventType.ISSUE_CREATED])`);
|
|
412
|
-
parts.push(" },");
|
|
413
|
-
parts.push(` /** Trigger when a Jira issue is updated */`);
|
|
414
|
-
parts.push(` onIssueUpdated(opts?: { project?: JiraProject }): JiraConfig {`);
|
|
415
|
-
parts.push(` return new JiraConfig("${id}", opts?.project?.projectKey, opts?.project?.projectId, [JiraEventType.ISSUE_UPDATED])`);
|
|
416
|
-
parts.push(" },");
|
|
417
|
-
parts.push(` /** Trigger on all Jira events */`);
|
|
418
|
-
parts.push(` trigger(opts?: { project?: JiraProject; eventTypes?: JiraEventType[] }): JiraConfig {`);
|
|
419
|
-
parts.push(` return new JiraConfig("${id}", opts?.project?.projectKey, opts?.project?.projectId, opts?.eventTypes)`);
|
|
420
|
-
parts.push(" },");
|
|
421
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
422
|
-
parts.push(` skill(opts?: { project?: JiraProject }): TypedSkill<${atlassianSkillToolType}> {`);
|
|
423
|
-
parts.push(` return new JiraConfig("${id}", opts?.project?.projectKey, opts?.project?.projectId) as TypedSkill<${atlassianSkillToolType}>`);
|
|
424
|
-
parts.push(" },");
|
|
425
|
-
parts.push("}");
|
|
426
|
-
parts.push("");
|
|
427
|
-
parts.push("export const Confluence = {");
|
|
428
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
429
|
-
parts.push(` skill(opts: { page: ConfluencePage }): TypedSkill<${atlassianSkillToolType}> {`);
|
|
430
|
-
parts.push(` return new ConfluenceConfig("${id}", opts.page.spaceName, opts.page.spaceId, opts.page.pageId, opts.page.title) as TypedSkill<${atlassianSkillToolType}>`);
|
|
431
|
-
parts.push(" },");
|
|
432
|
-
parts.push("}");
|
|
433
|
-
parts.push("");
|
|
434
|
-
return { code: parts.join("\n"), imports };
|
|
435
|
-
}
|
|
436
|
-
// ── Notion ────────────────────────────────────────────────────────────
|
|
437
|
-
function generateNotionSection(instances, tools) {
|
|
438
|
-
if (instances.length === 0)
|
|
439
|
-
return EMPTY_SECTION;
|
|
440
|
-
const inst = instances[0];
|
|
441
|
-
const imports = new Set(["NotionConfig", "TypedSkill"]);
|
|
442
|
-
const parts = [sectionHeader("Notion"), ""];
|
|
443
|
-
const id = inst.id;
|
|
444
|
-
const skillToolType = buildSkillToolTypeForIntegration(tools, "notion");
|
|
445
|
-
parts.push(generateResourceClass("NotionDatabase", [
|
|
446
|
-
{ classField: "databaseId", type: "string", sourceField: "id" },
|
|
447
|
-
{ classField: "title", type: "string", sourceField: "title" },
|
|
448
|
-
], "title", inst.databases));
|
|
449
|
-
parts.push("");
|
|
450
|
-
parts.push(generateResourceClass("NotionPage", [
|
|
451
|
-
{ classField: "pageId", type: "string", sourceField: "id" },
|
|
452
|
-
{ classField: "title", type: "string", sourceField: "title" },
|
|
453
|
-
], "title", inst.pages));
|
|
454
|
-
parts.push("");
|
|
455
|
-
parts.push("export const Notion = {");
|
|
456
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
457
|
-
parts.push(` skill(opts?: { databases?: NotionDatabase[]; pages?: NotionPage[] }): TypedSkill<${skillToolType}> {`);
|
|
458
|
-
parts.push(` return new NotionConfig("${id}",`);
|
|
459
|
-
parts.push(` opts?.databases?.map(d => d.databaseId), opts?.databases?.map(d => d.title),`);
|
|
460
|
-
parts.push(` opts?.pages?.map(p => p.pageId), opts?.pages?.map(p => p.title)) as TypedSkill<${skillToolType}>`);
|
|
461
|
-
parts.push(" },");
|
|
462
|
-
parts.push("}");
|
|
463
|
-
parts.push("");
|
|
464
|
-
return { code: parts.join("\n"), imports };
|
|
465
|
-
}
|
|
466
|
-
// ── PostHog ───────────────────────────────────────────────────────────
|
|
467
|
-
function generatePosthogSection(instances, tools) {
|
|
468
|
-
if (instances.length === 0)
|
|
469
|
-
return EMPTY_SECTION;
|
|
470
|
-
const inst = instances[0];
|
|
471
|
-
const imports = new Set(["PosthogConfig", "TypedSkill"]);
|
|
472
|
-
const parts = [sectionHeader("PostHog"), ""];
|
|
473
|
-
const id = inst.id;
|
|
474
|
-
const skillToolType = buildSkillToolTypeForIntegration(tools, "posthog");
|
|
475
|
-
parts.push(generateResourceClass("PosthogProject", [
|
|
476
|
-
{ classField: "projectId", type: "string", sourceField: "id" },
|
|
477
|
-
{ classField: "name", type: "string", sourceField: "name" },
|
|
478
|
-
], "name", inst.projects));
|
|
479
|
-
parts.push("");
|
|
480
|
-
parts.push("export const Posthog = {");
|
|
481
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
482
|
-
parts.push(` skill(opts: { project: PosthogProject }): TypedSkill<${skillToolType}> {`);
|
|
483
|
-
parts.push(` return new PosthogConfig("${id}", opts.project.projectId, opts.project.name) as TypedSkill<${skillToolType}>`);
|
|
484
|
-
parts.push(" },");
|
|
485
|
-
parts.push("}");
|
|
486
|
-
parts.push("");
|
|
487
|
-
return { code: parts.join("\n"), imports };
|
|
488
|
-
}
|
|
489
|
-
// ── Datadog ───────────────────────────────────────────────────────────
|
|
490
|
-
function generateDatadogSection(instances, tools) {
|
|
491
|
-
if (instances.length === 0)
|
|
492
|
-
return EMPTY_SECTION;
|
|
493
|
-
const inst = instances[0];
|
|
494
|
-
const imports = new Set(["DatadogConfig", "TypedSkill"]);
|
|
495
|
-
const parts = [sectionHeader("Datadog"), ""];
|
|
496
|
-
const id = inst.id;
|
|
497
|
-
const skillToolType = buildSkillToolTypeForIntegration(tools, "datadog");
|
|
498
|
-
parts.push(generateResourceClass("DatadogIndex", [
|
|
499
|
-
{ classField: "name", type: "string", sourceField: "name" },
|
|
500
|
-
], "name", inst.indexes));
|
|
501
|
-
parts.push("");
|
|
502
|
-
parts.push("export const Datadog = {");
|
|
503
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
504
|
-
parts.push(` skill(opts?: { indexes?: DatadogIndex[] }): TypedSkill<${skillToolType}> {`);
|
|
505
|
-
parts.push(` return new DatadogConfig("${id}", opts?.indexes?.map(i => i.name)) as TypedSkill<${skillToolType}>`);
|
|
506
|
-
parts.push(" },");
|
|
507
|
-
parts.push("}");
|
|
508
|
-
parts.push("");
|
|
509
|
-
return { code: parts.join("\n"), imports };
|
|
510
|
-
}
|
|
511
|
-
// ── LaunchDarkly ──────────────────────────────────────────────────────
|
|
512
|
-
function generateLaunchDarklySection(instances, tools) {
|
|
513
|
-
if (instances.length === 0)
|
|
514
|
-
return EMPTY_SECTION;
|
|
515
|
-
const inst = instances[0];
|
|
516
|
-
const imports = new Set(["LaunchDarklyConfig", "TypedSkill"]);
|
|
517
|
-
const parts = [sectionHeader("LaunchDarkly"), ""];
|
|
518
|
-
const id = inst.id;
|
|
519
|
-
const skillToolType = buildSkillToolTypeForIntegration(tools, "launchdarkly");
|
|
520
|
-
parts.push(generateResourceClass("LaunchDarklyProject", [
|
|
521
|
-
{ classField: "projectKey", type: "string", sourceField: "key" },
|
|
522
|
-
{ classField: "name", type: "string", sourceField: "name" },
|
|
523
|
-
], "name", inst.projects));
|
|
524
|
-
parts.push("");
|
|
525
|
-
parts.push("export const LaunchDarkly = {");
|
|
526
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
527
|
-
parts.push(` skill(opts: { project: LaunchDarklyProject; environmentKeys: string[] }): TypedSkill<${skillToolType}> {`);
|
|
528
|
-
parts.push(` return new LaunchDarklyConfig("${id}", opts.project.projectKey, opts.environmentKeys) as TypedSkill<${skillToolType}>`);
|
|
529
|
-
parts.push(" },");
|
|
530
|
-
parts.push("}");
|
|
531
|
-
parts.push("");
|
|
532
|
-
return { code: parts.join("\n"), imports };
|
|
533
|
-
}
|
|
534
|
-
// ── WorkOS ────────────────────────────────────────────────────────────
|
|
535
|
-
function generateWorkOSSection(instances, tools) {
|
|
536
|
-
if (instances.length === 0)
|
|
537
|
-
return EMPTY_SECTION;
|
|
538
|
-
const imports = new Set(["WorkOSInputConfig", "WorkOSOutputConfig", "WorkOSEventType", "TypedSkill"]);
|
|
539
|
-
const parts = [sectionHeader("WorkOS"), ""];
|
|
540
|
-
const id = instances[0].id;
|
|
541
|
-
const skillToolType = buildSkillToolTypeForIntegration(tools, "workos");
|
|
542
|
-
imports.add("TypedTrigger");
|
|
543
|
-
imports.add("WorkOSInputEvent");
|
|
544
|
-
imports.add("WorkOSUserInputEvent");
|
|
545
|
-
imports.add("WorkOSMembershipInputEvent");
|
|
546
|
-
imports.add("WorkOSInvitationInputEvent");
|
|
547
|
-
imports.add("WorkOSOrganizationInputEvent");
|
|
548
|
-
parts.push("export const WorkOS = {");
|
|
549
|
-
parts.push(` /** Trigger on user creation */`);
|
|
550
|
-
parts.push(` onUserCreated(): TypedTrigger<WorkOSUserInputEvent> {`);
|
|
551
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.USER_CREATED]) as TypedTrigger<WorkOSUserInputEvent>`);
|
|
552
|
-
parts.push(" },");
|
|
553
|
-
parts.push(` /** Trigger on user update */`);
|
|
554
|
-
parts.push(` onUserUpdated(): TypedTrigger<WorkOSUserInputEvent> {`);
|
|
555
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.USER_UPDATED]) as TypedTrigger<WorkOSUserInputEvent>`);
|
|
556
|
-
parts.push(" },");
|
|
557
|
-
parts.push(` /** Trigger on user deletion */`);
|
|
558
|
-
parts.push(` onUserDeleted(): TypedTrigger<WorkOSUserInputEvent> {`);
|
|
559
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.USER_DELETED]) as TypedTrigger<WorkOSUserInputEvent>`);
|
|
560
|
-
parts.push(" },");
|
|
561
|
-
parts.push(` /** Trigger on organization membership change */`);
|
|
562
|
-
parts.push(` onMembershipChanged(): TypedTrigger<WorkOSMembershipInputEvent> {`);
|
|
563
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.ORGANIZATION_MEMBERSHIP_CREATED, WorkOSEventType.ORGANIZATION_MEMBERSHIP_UPDATED, WorkOSEventType.ORGANIZATION_MEMBERSHIP_DELETED]) as TypedTrigger<WorkOSMembershipInputEvent>`);
|
|
564
|
-
parts.push(" },");
|
|
565
|
-
parts.push(` /** Trigger when an invitation is sent */`);
|
|
566
|
-
parts.push(` onInvitationSent(): TypedTrigger<WorkOSInvitationInputEvent> {`);
|
|
567
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.INVITATION_CREATED, WorkOSEventType.INVITATION_RESENT]) as TypedTrigger<WorkOSInvitationInputEvent>`);
|
|
568
|
-
parts.push(" },");
|
|
569
|
-
parts.push(` /** Trigger when an invitation is first created */`);
|
|
570
|
-
parts.push(` onInvitationCreated(): TypedTrigger<WorkOSInvitationInputEvent> {`);
|
|
571
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.INVITATION_CREATED]) as TypedTrigger<WorkOSInvitationInputEvent>`);
|
|
572
|
-
parts.push(" },");
|
|
573
|
-
parts.push(` /** Trigger when an invitation is resent */`);
|
|
574
|
-
parts.push(` onInvitationResent(): TypedTrigger<WorkOSInvitationInputEvent> {`);
|
|
575
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.INVITATION_RESENT]) as TypedTrigger<WorkOSInvitationInputEvent>`);
|
|
576
|
-
parts.push(" },");
|
|
577
|
-
parts.push(` /** Trigger on invitation accepted */`);
|
|
578
|
-
parts.push(` onInvitationAccepted(): TypedTrigger<WorkOSInvitationInputEvent> {`);
|
|
579
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.INVITATION_ACCEPTED]) as TypedTrigger<WorkOSInvitationInputEvent>`);
|
|
580
|
-
parts.push(" },");
|
|
581
|
-
parts.push(` /** Trigger when an invitation is revoked */`);
|
|
582
|
-
parts.push(` onInvitationRevoked(): TypedTrigger<WorkOSInvitationInputEvent> {`);
|
|
583
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.INVITATION_REVOKED]) as TypedTrigger<WorkOSInvitationInputEvent>`);
|
|
584
|
-
parts.push(" },");
|
|
585
|
-
parts.push(` /** Trigger on organization creation */`);
|
|
586
|
-
parts.push(` onOrganizationCreated(): TypedTrigger<WorkOSOrganizationInputEvent> {`);
|
|
587
|
-
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.ORGANIZATION_CREATED]) as TypedTrigger<WorkOSOrganizationInputEvent>`);
|
|
588
|
-
parts.push(" },");
|
|
589
|
-
parts.push(` /** Trigger on specific WorkOS event types */`);
|
|
590
|
-
parts.push(` trigger(opts?: { eventTypes?: WorkOSEventType[] }): TypedTrigger<WorkOSInputEvent | WorkOSUserInputEvent | WorkOSMembershipInputEvent | WorkOSInvitationInputEvent | WorkOSOrganizationInputEvent> {`);
|
|
591
|
-
parts.push(` return new WorkOSInputConfig("${id}", opts?.eventTypes) as TypedTrigger<WorkOSInputEvent | WorkOSUserInputEvent | WorkOSMembershipInputEvent | WorkOSInvitationInputEvent | WorkOSOrganizationInputEvent>`);
|
|
592
|
-
parts.push(" },");
|
|
593
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
594
|
-
parts.push(` skill(): TypedSkill<${skillToolType}> {`);
|
|
595
|
-
parts.push(` return new WorkOSOutputConfig("${id}") as TypedSkill<${skillToolType}>`);
|
|
596
|
-
parts.push(" },");
|
|
597
|
-
parts.push("}");
|
|
598
|
-
parts.push("");
|
|
599
|
-
return { code: parts.join("\n"), imports };
|
|
600
|
-
}
|
|
601
|
-
// ── Attio ─────────────────────────────────────────────────────────────
|
|
602
|
-
function generateAttioSection(instances, tools) {
|
|
603
|
-
if (instances.length === 0)
|
|
604
|
-
return EMPTY_SECTION;
|
|
605
|
-
const inst = instances[0];
|
|
606
|
-
const imports = new Set(["AttioOutputConfig", "TypedSkill"]);
|
|
607
|
-
const parts = [sectionHeader("Attio"), ""];
|
|
608
|
-
const id = inst.id;
|
|
609
|
-
const skillToolType = buildSkillToolTypeForIntegration(tools, "attio");
|
|
610
|
-
parts.push("export type AttioAttributeDefinition<TSlug extends string = string, TType extends string = string> = {");
|
|
611
|
-
parts.push(" apiSlug: TSlug");
|
|
612
|
-
parts.push(" title?: string");
|
|
613
|
-
parts.push(" type?: TType");
|
|
614
|
-
parts.push(" isRequired?: boolean");
|
|
615
|
-
parts.push(" isUnique?: boolean");
|
|
616
|
-
parts.push("}");
|
|
617
|
-
parts.push("");
|
|
618
|
-
parts.push("export class AttioObject<");
|
|
619
|
-
parts.push(" TSlug extends string = string,");
|
|
620
|
-
parts.push(" TRecordValues extends Record<string, unknown> = Record<string, unknown>,");
|
|
621
|
-
parts.push(" TInputValues extends Record<string, unknown> = TRecordValues");
|
|
622
|
-
parts.push("> {");
|
|
623
|
-
parts.push(" constructor(");
|
|
624
|
-
parts.push(" public readonly apiSlug: TSlug,");
|
|
625
|
-
parts.push(" public readonly name: string,");
|
|
626
|
-
parts.push(" public readonly attributes: readonly AttioAttributeDefinition[] = []");
|
|
627
|
-
parts.push(" ) {}");
|
|
628
|
-
parts.push("");
|
|
629
|
-
parts.push(" declare readonly __recordValues: TRecordValues");
|
|
630
|
-
parts.push(" declare readonly __inputValues: TInputValues");
|
|
631
|
-
if (inst.objects.length > 0) {
|
|
632
|
-
parts.push("");
|
|
633
|
-
const usedNames = new Set();
|
|
634
|
-
for (const object of inst.objects) {
|
|
635
|
-
let staticName = toGeneratedIdentifier(object.singular_noun || object.api_slug || "Object", "AttioObject");
|
|
636
|
-
while (usedNames.has(staticName))
|
|
637
|
-
staticName += "_";
|
|
638
|
-
usedNames.add(staticName);
|
|
639
|
-
const attributes = (object.attributes || []).filter((attr) => !!attr.api_slug);
|
|
640
|
-
const attributeSource = attributes.length === 0
|
|
641
|
-
? "[]"
|
|
642
|
-
: `[\n${attributes.map(attr => {
|
|
643
|
-
const fields = [
|
|
644
|
-
`apiSlug: "${escapeString(attr.api_slug)}"`,
|
|
645
|
-
attr.title ? `title: "${escapeString(attr.title)}"` : undefined,
|
|
646
|
-
attr.type ? `type: "${escapeString(attr.type)}"` : undefined,
|
|
647
|
-
attr.is_required !== undefined ? `isRequired: ${attr.is_required ? "true" : "false"}` : undefined,
|
|
648
|
-
attr.is_unique !== undefined ? `isUnique: ${attr.is_unique ? "true" : "false"}` : undefined,
|
|
649
|
-
].filter(Boolean).join(", ");
|
|
650
|
-
return ` { ${fields} }`;
|
|
651
|
-
}).join(",\n")}\n ]`;
|
|
652
|
-
const recordValuesType = renderAttioObjectValueShape(attributes, "record");
|
|
653
|
-
const inputValuesType = renderAttioObjectValueShape(attributes, "input");
|
|
654
|
-
parts.push(` static ${staticName} = new AttioObject<"${escapeString(object.api_slug)}", ${recordValuesType}, ${inputValuesType}>("${escapeString(object.api_slug)}", "${escapeString(object.singular_noun)}", ${attributeSource})`);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
parts.push("}");
|
|
658
|
-
parts.push("");
|
|
659
|
-
parts.push("export const Attio = {");
|
|
660
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
661
|
-
parts.push(` skill(opts: { object: AttioObject<any> }): TypedSkill<${skillToolType}> {`);
|
|
662
|
-
parts.push(` return new AttioOutputConfig("${id}", opts.object.apiSlug) as TypedSkill<${skillToolType}>`);
|
|
663
|
-
parts.push(" },");
|
|
664
|
-
parts.push("}");
|
|
665
|
-
parts.push("");
|
|
666
|
-
return { code: parts.join("\n"), imports };
|
|
667
|
-
}
|
|
668
|
-
// ── Snowflake ─────────────────────────────────────────────────────────
|
|
669
|
-
function generateSnowflakeSection(instances, tools) {
|
|
670
|
-
if (instances.length === 0)
|
|
671
|
-
return EMPTY_SECTION;
|
|
672
|
-
const inst = instances[0];
|
|
673
|
-
const imports = new Set(["SnowflakeOutputConfig", "TypedSkill"]);
|
|
674
|
-
const parts = [sectionHeader("Snowflake"), ""];
|
|
675
|
-
const id = inst.id;
|
|
676
|
-
const skillToolType = buildSkillToolTypeForIntegration(tools, "snowflake");
|
|
677
|
-
parts.push("export const Snowflake = {");
|
|
678
|
-
parts.push(` /** Use in \`skills[]\` */`);
|
|
679
|
-
parts.push(` skill(): TypedSkill<${skillToolType}> {`);
|
|
680
|
-
parts.push(` return new SnowflakeOutputConfig("${id}") as TypedSkill<${skillToolType}>`);
|
|
681
|
-
parts.push(" },");
|
|
682
|
-
parts.push("}");
|
|
683
|
-
parts.push("");
|
|
684
|
-
return { code: parts.join("\n"), imports };
|
|
685
|
-
}
|
|
686
|
-
// ── Typed Tools ───────────────────────────────────────────────────────
|
|
687
|
-
/**
|
|
688
|
-
* Converts a displayName like "Create ticket" to camelCase like "createTicket".
|
|
689
|
-
*/
|
|
690
|
-
function toCamelCase(s) {
|
|
691
|
-
const pascal = toPascalCase(s);
|
|
692
|
-
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
693
|
-
}
|
|
694
|
-
/**
|
|
695
|
-
* Converts a tool name like "linear_create_ticket" to a PascalCase interface name
|
|
696
|
-
* like "LinearCreateTicketParams".
|
|
697
|
-
*/
|
|
698
|
-
function toolNameToInterfaceName(name) {
|
|
699
|
-
return name.split("_").map(w => w.charAt(0).toUpperCase() + w.slice(1)).join("") + "Params";
|
|
700
|
-
}
|
|
701
|
-
/**
|
|
702
|
-
* Converts a JSON Schema type to a TypeScript type string.
|
|
703
|
-
* Handles nested objects, arrays, enums, anyOf (nullable), and primitives.
|
|
704
|
-
*/
|
|
705
|
-
function jsonSchemaToTs(schema, indent) {
|
|
706
|
-
if (!schema)
|
|
707
|
-
return "unknown";
|
|
708
|
-
// Handle enums
|
|
709
|
-
if (schema.enum) {
|
|
710
|
-
return schema.enum.map(v => typeof v === "string" ? `"${escapeString(v)}"` : String(v)).join(" | ");
|
|
711
|
-
}
|
|
712
|
-
// Handle anyOf (commonly used for nullable: [X, { type: "null" }])
|
|
713
|
-
if (schema.anyOf) {
|
|
714
|
-
const nonNull = schema.anyOf.filter(s => s.type !== "null");
|
|
715
|
-
const hasNull = schema.anyOf.some(s => s.type === "null");
|
|
716
|
-
if (nonNull.length === 1 && hasNull) {
|
|
717
|
-
return `${jsonSchemaToTs(nonNull[0], indent)} | null`;
|
|
718
|
-
}
|
|
719
|
-
return schema.anyOf.map(s => jsonSchemaToTs(s, indent)).join(" | ");
|
|
720
|
-
}
|
|
721
|
-
switch (schema.type) {
|
|
722
|
-
case "string":
|
|
723
|
-
return "string";
|
|
724
|
-
case "number":
|
|
725
|
-
case "integer":
|
|
726
|
-
return "number";
|
|
727
|
-
case "boolean":
|
|
728
|
-
return "boolean";
|
|
729
|
-
case "array":
|
|
730
|
-
if (schema.items) {
|
|
731
|
-
const itemType = jsonSchemaToTs(schema.items, indent);
|
|
732
|
-
// Wrap union types in parens for array
|
|
733
|
-
return itemType.includes("|") ? `(${itemType})[]` : `${itemType}[]`;
|
|
734
|
-
}
|
|
735
|
-
return "unknown[]";
|
|
736
|
-
case "object": {
|
|
737
|
-
if (!schema.properties || Object.keys(schema.properties).length === 0) {
|
|
738
|
-
return "Record<string, unknown>";
|
|
739
|
-
}
|
|
740
|
-
const requiredSet = new Set(schema.required || []);
|
|
741
|
-
const pad = " ".repeat(indent);
|
|
742
|
-
const innerPad = " ".repeat(indent + 4);
|
|
743
|
-
const entries = Object.entries(schema.properties).map(([key, prop]) => {
|
|
744
|
-
const optional = requiredSet.has(key) ? "" : "?";
|
|
745
|
-
const tsType = jsonSchemaToTs(prop, indent + 4);
|
|
746
|
-
const desc = prop.description ? ` /** ${prop.description} */\n${innerPad}` : "";
|
|
747
|
-
return `${desc}${key}${optional}: ${tsType}`;
|
|
748
|
-
});
|
|
749
|
-
return `{\n${innerPad}${entries.join(`\n${innerPad}`)}\n${pad}}`;
|
|
750
|
-
}
|
|
751
|
-
default:
|
|
752
|
-
return "unknown";
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
function generateToolsSection(tools, input) {
|
|
756
|
-
if (tools.length === 0)
|
|
757
|
-
return EMPTY_SECTION;
|
|
758
|
-
const imports = new Set(["TerseAgent", "ToolOutputByName"]);
|
|
759
|
-
const parts = [sectionHeader("Typed Tools"), ""];
|
|
760
|
-
// Build instance map: integration type → instances with id & displayName
|
|
761
|
-
const instanceMap = new Map();
|
|
762
|
-
instanceMap.set("slack", input.slack.map(s => ({ id: s.id, displayName: s.displayName })));
|
|
763
|
-
instanceMap.set("github", input.github.map(g => ({ id: g.integration.id, displayName: g.integration.account_name || "" })));
|
|
764
|
-
instanceMap.set("gmail", input.gmail.map(g => ({ id: g.id, displayName: g.displayName })));
|
|
765
|
-
instanceMap.set("figma", input.figma.map(f => ({ id: f.id, displayName: f.displayName })));
|
|
766
|
-
instanceMap.set("linear", input.linear.map(l => ({ id: l.id, displayName: l.displayName })));
|
|
767
|
-
instanceMap.set("jira", input.atlassian.map(a => ({ id: a.id, displayName: a.displayName })));
|
|
768
|
-
instanceMap.set("confluence", input.atlassian.map(a => ({ id: a.id, displayName: a.displayName })));
|
|
769
|
-
instanceMap.set("atlassian", input.atlassian.map(a => ({ id: a.id, displayName: a.displayName })));
|
|
770
|
-
instanceMap.set("notion", input.notion.map(n => ({ id: n.id, displayName: n.displayName })));
|
|
771
|
-
instanceMap.set("posthog", input.posthog.map(p => ({ id: p.id, displayName: p.displayName })));
|
|
772
|
-
instanceMap.set("datadog", input.datadog.map(d => ({ id: d.id, displayName: d.displayName })));
|
|
773
|
-
instanceMap.set("launchdarkly", input.launchdarkly.map(l => ({ id: l.id, displayName: l.displayName })));
|
|
774
|
-
instanceMap.set("workos", input.workos.map(w => ({ id: w.id, displayName: w.displayName })));
|
|
775
|
-
instanceMap.set("attio", input.attio.map(a => ({ id: a.id, displayName: a.displayName })));
|
|
776
|
-
instanceMap.set("snowflake", input.snowflake.map(s => ({ id: s.id, displayName: s.name })));
|
|
777
|
-
// Group tools by integration
|
|
778
|
-
const byIntegration = new Map();
|
|
779
|
-
for (const tool of tools) {
|
|
780
|
-
const key = tool.integration.toLowerCase();
|
|
781
|
-
if (!byIntegration.has(key))
|
|
782
|
-
byIntegration.set(key, []);
|
|
783
|
-
byIntegration.get(key).push(tool);
|
|
784
|
-
}
|
|
785
|
-
// Check if a tool has integrationId that should be auto-filled
|
|
786
|
-
const hasAutoFillId = (tool) => tool.parameters.properties?.integrationId !== undefined;
|
|
787
|
-
// Clone a schema with integrationId removed from properties and required
|
|
788
|
-
const omitIntegrationId = (schema) => {
|
|
789
|
-
const cloned = { ...schema };
|
|
790
|
-
if (cloned.properties) {
|
|
791
|
-
cloned.properties = Object.fromEntries(Object.entries(cloned.properties).filter(([key]) => key !== "integrationId"));
|
|
792
|
-
}
|
|
793
|
-
if (cloned.required) {
|
|
794
|
-
cloned.required = cloned.required.filter(r => r !== "integrationId");
|
|
795
|
-
}
|
|
796
|
-
return cloned;
|
|
797
|
-
};
|
|
798
|
-
// Ensure GitHub repository params are full "owner/repo".
|
|
799
|
-
// Supports:
|
|
800
|
-
// - Repos objects (uses .fullName)
|
|
801
|
-
// - full string "owner/repo" (passthrough)
|
|
802
|
-
// - short repo name string "repo" (resolved via known generated repositories when unique)
|
|
803
|
-
const normalizeGitHubReposParams = (toolName) => {
|
|
804
|
-
switch (toolName) {
|
|
805
|
-
case "readGitHubFile":
|
|
806
|
-
case "listGitHubPullRequests":
|
|
807
|
-
case "listGitHubDirectory":
|
|
808
|
-
case "listGitHubCommits":
|
|
809
|
-
case "summarizeGitHubPullRequestDiff":
|
|
810
|
-
return "{ ...params, repository: __normalizeGitHubRepos((params as any).repository) }";
|
|
811
|
-
case "searchGitHubCode":
|
|
812
|
-
case "grepGitHubCode":
|
|
813
|
-
return "{ ...params, repositoryNames: __normalizeGitHubReposNames((params as any).repositoryNames) }";
|
|
814
|
-
default:
|
|
815
|
-
return "params";
|
|
816
|
-
}
|
|
817
|
-
};
|
|
818
|
-
const isAttioTool = (tool) => tool.integration.toLowerCase() === "attio";
|
|
819
|
-
const attioObjects = input.attio[0]?.objects ?? [];
|
|
820
|
-
const attioGeneratedObjects = (() => {
|
|
821
|
-
const usedNames = new Set();
|
|
822
|
-
return attioObjects.map(object => {
|
|
823
|
-
let staticName = toGeneratedIdentifier(object.singular_noun || object.api_slug || "Object", "AttioObject");
|
|
824
|
-
while (usedNames.has(staticName))
|
|
825
|
-
staticName += "_";
|
|
826
|
-
usedNames.add(staticName);
|
|
827
|
-
return {
|
|
828
|
-
...object,
|
|
829
|
-
staticName,
|
|
830
|
-
attributes: (object.attributes || []).filter((attr) => !!attr.api_slug),
|
|
831
|
-
};
|
|
832
|
-
});
|
|
833
|
-
})();
|
|
834
|
-
if (tools.some(isAttioTool)) {
|
|
835
|
-
parts.push("type __AttioPrimitive = string | number | boolean | null");
|
|
836
|
-
parts.push("type __AttioStructuredValue = Record<string, unknown>");
|
|
837
|
-
parts.push("type __AttioValue = __AttioPrimitive | __AttioStructuredValue | (__AttioPrimitive | __AttioStructuredValue)[]");
|
|
838
|
-
parts.push("type __AttioFilterShorthand<T> = T extends (infer U)[] ? U | T : T");
|
|
839
|
-
parts.push('type __AttioFilterValue<T> = __AttioFilterShorthand<T> | { $eq?: __AttioFilterShorthand<T>; $contains?: string; $starts_with?: string; $ends_with?: string } | Record<string, unknown>');
|
|
840
|
-
parts.push('type __AttioFilterExpression<TValues extends Record<string, unknown>> = Partial<{ [K in keyof TValues]: __AttioFilterValue<TValues[K]> }> & { $and?: Array<__AttioFilterExpression<TValues>>; $or?: Array<__AttioFilterExpression<TValues>> }');
|
|
841
|
-
parts.push('type __AttioRecordBase = NonNullable<NonNullable<ToolOutputByName["attio_upsert_record"]["records"]>[number]>');
|
|
842
|
-
parts.push('type __AttioRecordWithValues<TValues extends Record<string, unknown>> = Omit<__AttioRecordBase, "values"> & TValues & { values: TValues; attributes: TValues }');
|
|
843
|
-
parts.push("");
|
|
844
|
-
if (attioGeneratedObjects.length > 0) {
|
|
845
|
-
parts.push("export type GeneratedAttioObject =");
|
|
846
|
-
for (const object of attioGeneratedObjects) {
|
|
847
|
-
parts.push(` | typeof AttioObject.${object.staticName}`);
|
|
848
|
-
}
|
|
849
|
-
parts.push("");
|
|
850
|
-
parts.push("export type AttioInputValuesByObject = {");
|
|
851
|
-
for (const object of attioGeneratedObjects) {
|
|
852
|
-
const inputShape = object.attributes.length === 0 ? "Record<string, __AttioValue>" : renderAttioObjectValueShape(object.attributes, "input");
|
|
853
|
-
parts.push(` "${escapeString(object.api_slug)}": ${inputShape}`);
|
|
854
|
-
}
|
|
855
|
-
parts.push("}");
|
|
856
|
-
parts.push("");
|
|
857
|
-
parts.push("export type AttioRecordValuesByObject = {");
|
|
858
|
-
for (const object of attioGeneratedObjects) {
|
|
859
|
-
const recordShape = object.attributes.length === 0 ? "Record<string, __AttioValue>" : renderAttioObjectValueShape(object.attributes, "record");
|
|
860
|
-
parts.push(` "${escapeString(object.api_slug)}": ${recordShape}`);
|
|
861
|
-
}
|
|
862
|
-
parts.push("}");
|
|
863
|
-
parts.push("");
|
|
864
|
-
parts.push("export type AttioFilterByObject = {");
|
|
865
|
-
for (const object of attioGeneratedObjects) {
|
|
866
|
-
parts.push(` "${escapeString(object.api_slug)}": __AttioFilterExpression<AttioRecordValuesByObject["${escapeString(object.api_slug)}"]>`);
|
|
867
|
-
}
|
|
868
|
-
parts.push("}");
|
|
869
|
-
parts.push("");
|
|
870
|
-
parts.push("export type AttioRecordByObject = {");
|
|
871
|
-
for (const object of attioGeneratedObjects) {
|
|
872
|
-
parts.push(` "${escapeString(object.api_slug)}": __AttioRecordWithValues<AttioRecordValuesByObject["${escapeString(object.api_slug)}"]>`);
|
|
873
|
-
}
|
|
874
|
-
parts.push("}");
|
|
875
|
-
}
|
|
876
|
-
else {
|
|
877
|
-
parts.push("export type GeneratedAttioObject = AttioObject<string, Record<string, __AttioValue>, Record<string, __AttioValue>>");
|
|
878
|
-
parts.push('export type AttioInputValuesByObject = Record<string, Record<string, __AttioValue>>');
|
|
879
|
-
parts.push('export type AttioRecordValuesByObject = Record<string, Record<string, __AttioValue>>');
|
|
880
|
-
parts.push('export type AttioFilterByObject = Record<string, __AttioFilterExpression<Record<string, __AttioValue>>>');
|
|
881
|
-
parts.push('export type AttioRecordByObject = Record<string, __AttioRecordWithValues<Record<string, __AttioValue>>>');
|
|
882
|
-
}
|
|
883
|
-
parts.push("");
|
|
884
|
-
parts.push('export type AttioValuesFor<TObject extends GeneratedAttioObject = GeneratedAttioObject> = TObject extends { __inputValues: infer TInputValues } ? TInputValues : AttioInputValuesByObject[TObject["apiSlug"]]');
|
|
885
|
-
parts.push('export type AttioRecordValuesFor<TObject extends GeneratedAttioObject = GeneratedAttioObject> = TObject extends { __recordValues: infer TRecordValues } ? TRecordValues : AttioRecordValuesByObject[TObject["apiSlug"]]');
|
|
886
|
-
parts.push('export type AttioAttributeSlug<TObject extends GeneratedAttioObject = GeneratedAttioObject> = Extract<keyof AttioValuesFor<TObject>, string>');
|
|
887
|
-
parts.push('export type AttioFilterFor<TObject extends GeneratedAttioObject = GeneratedAttioObject> = __AttioFilterExpression<AttioRecordValuesFor<TObject>>');
|
|
888
|
-
parts.push('export type AttioRecordFor<TObject extends GeneratedAttioObject = GeneratedAttioObject> = __AttioRecordWithValues<AttioRecordValuesFor<TObject>>');
|
|
889
|
-
parts.push('export type AttioQueryRecordsParams<TObject extends GeneratedAttioObject = GeneratedAttioObject> = { object: TObject; filter?: AttioFilterFor<TObject> | null; limit?: number | null }');
|
|
890
|
-
parts.push('export type AttioUpsertRecordParams<TObject extends GeneratedAttioObject = GeneratedAttioObject> = { object: TObject; matchingAttribute: AttioAttributeSlug<TObject>; records: AttioValuesFor<TObject>[] }');
|
|
891
|
-
parts.push("export type AttioListObjectsParams = Record<string, never>");
|
|
892
|
-
parts.push('export type AttioQueryRecordsResult<TObject extends GeneratedAttioObject = GeneratedAttioObject> = Omit<ToolOutputByName["attio_query_records"], "records"> & { records: Array<AttioRecordFor<TObject>> }');
|
|
893
|
-
parts.push('export type AttioUpsertRecordResult<TObject extends GeneratedAttioObject = GeneratedAttioObject> = Omit<ToolOutputByName["attio_upsert_record"], "records"> & { records?: Array<AttioRecordFor<TObject>> }');
|
|
894
|
-
parts.push("");
|
|
895
|
-
parts.push("const __attioMetadataKeys = new Set([");
|
|
896
|
-
parts.push(' "active_from",');
|
|
897
|
-
parts.push(' "active_until",');
|
|
898
|
-
parts.push(' "attribute_type",');
|
|
899
|
-
parts.push(' "created_by_actor",');
|
|
900
|
-
parts.push("])");
|
|
901
|
-
parts.push("");
|
|
902
|
-
parts.push("const __attioMultiValueAttributeSlugsByObject: Record<string, readonly string[]> = {");
|
|
903
|
-
for (const object of attioGeneratedObjects) {
|
|
904
|
-
const multiValueSlugs = object.attributes.filter(isProbablyAttioMultiValue).map(attr => `"${escapeString(attr.api_slug)}"`).join(", ");
|
|
905
|
-
parts.push(` "${escapeString(object.api_slug)}": [${multiValueSlugs}],`);
|
|
906
|
-
}
|
|
907
|
-
parts.push("}");
|
|
908
|
-
parts.push("");
|
|
909
|
-
parts.push("function __normalizeAttioObjectSlug(object: unknown): string {");
|
|
910
|
-
parts.push(' if (object && typeof object === "object" && "apiSlug" in (object as Record<string, unknown>)) {');
|
|
911
|
-
parts.push(' const apiSlug = (object as { apiSlug?: unknown }).apiSlug');
|
|
912
|
-
parts.push(' if (typeof apiSlug === "string" && apiSlug.length > 0) return apiSlug');
|
|
913
|
-
parts.push(" }");
|
|
914
|
-
parts.push(' return typeof object === "string" ? object : ""');
|
|
915
|
-
parts.push("}");
|
|
916
|
-
parts.push("");
|
|
917
|
-
parts.push("function __serializeAttioFilter(filter: unknown): string | null {");
|
|
918
|
-
parts.push(" if (filter === undefined || filter === null) return null");
|
|
919
|
-
parts.push(" return JSON.stringify(filter)");
|
|
920
|
-
parts.push("}");
|
|
921
|
-
parts.push("");
|
|
922
|
-
parts.push("function __serializeAttioRecords(records: unknown): string {");
|
|
923
|
-
parts.push(" if (!Array.isArray(records)) return JSON.stringify([])");
|
|
924
|
-
parts.push(' return JSON.stringify(records.filter((record): record is Record<string, unknown> => typeof record === "object" && record !== null && !Array.isArray(record)))');
|
|
925
|
-
parts.push("}");
|
|
926
|
-
parts.push("");
|
|
927
|
-
parts.push("function __isAttioMultiValueAttribute(objectSlug: string, attributeSlug: string): boolean {");
|
|
928
|
-
parts.push(" return (__attioMultiValueAttributeSlugsByObject[objectSlug] || []).includes(attributeSlug)");
|
|
929
|
-
parts.push("}");
|
|
930
|
-
parts.push("");
|
|
931
|
-
parts.push("function __flattenAttioLeafValue(value: unknown): unknown {");
|
|
932
|
-
parts.push(" if (value === null || value === undefined) return value");
|
|
933
|
-
parts.push(" if (Array.isArray(value)) return value.map(entry => __flattenAttioLeafValue(entry))");
|
|
934
|
-
parts.push(' if (typeof value !== "object") return value');
|
|
935
|
-
parts.push(" const rawObject = value as Record<string, unknown>");
|
|
936
|
-
parts.push(' if (typeof rawObject.full_name === "string") return rawObject.full_name');
|
|
937
|
-
parts.push(' if (typeof rawObject.email_address === "string") return rawObject.email_address');
|
|
938
|
-
parts.push(' if (typeof rawObject.domain === "string") return rawObject.domain');
|
|
939
|
-
parts.push(' if (typeof rawObject.phone_number === "string") return rawObject.phone_number');
|
|
940
|
-
parts.push(' if ("value" in rawObject) return __flattenAttioLeafValue(rawObject.value)');
|
|
941
|
-
parts.push(" const dataEntries = Object.entries(rawObject).filter(([key]) => !__attioMetadataKeys.has(key))");
|
|
942
|
-
parts.push(" if (dataEntries.length === 0) return rawObject");
|
|
943
|
-
parts.push(" if (dataEntries.length === 1) return __flattenAttioLeafValue(dataEntries[0][1])");
|
|
944
|
-
parts.push(" return Object.fromEntries(dataEntries.map(([key, entryValue]) => [key, __flattenAttioLeafValue(entryValue)]))");
|
|
945
|
-
parts.push("}");
|
|
946
|
-
parts.push("");
|
|
947
|
-
parts.push("function __flattenAttioAttributeValue(rawValue: unknown, preferArray: boolean): unknown {");
|
|
948
|
-
parts.push(" if (!Array.isArray(rawValue)) return __flattenAttioLeafValue(rawValue)");
|
|
949
|
-
parts.push(" const flattened = rawValue");
|
|
950
|
-
parts.push(" .map(entry => __flattenAttioLeafValue(entry))");
|
|
951
|
-
parts.push(" .filter(entry => entry !== undefined)");
|
|
952
|
-
parts.push(" return preferArray ? flattened : flattened[0]");
|
|
953
|
-
parts.push("}");
|
|
954
|
-
parts.push("");
|
|
955
|
-
parts.push("function __getAttioRecordValues<TValues extends Record<string, unknown>>(objectSlug: string, record: unknown): TValues {");
|
|
956
|
-
parts.push(' const rawValues = record && typeof record === "object" ? (record as { values?: unknown }).values : undefined');
|
|
957
|
-
parts.push(' if (!rawValues || typeof rawValues !== "object" || Array.isArray(rawValues)) return {} as TValues');
|
|
958
|
-
parts.push(" const flattenedValues: Record<string, unknown> = {}");
|
|
959
|
-
parts.push(" for (const [attributeSlug, rawValue] of Object.entries(rawValues)) {");
|
|
960
|
-
parts.push(" flattenedValues[attributeSlug] = __flattenAttioAttributeValue(rawValue, __isAttioMultiValueAttribute(objectSlug, attributeSlug))");
|
|
961
|
-
parts.push(" }");
|
|
962
|
-
parts.push(" return flattenedValues as TValues");
|
|
963
|
-
parts.push("}");
|
|
964
|
-
parts.push("");
|
|
965
|
-
parts.push("function __enhanceAttioRecord<TObject extends GeneratedAttioObject>(object: TObject, record: unknown): __AttioRecordWithValues<AttioRecordValuesFor<TObject>> | undefined {");
|
|
966
|
-
parts.push(' if (!record || typeof record !== "object") return undefined');
|
|
967
|
-
parts.push(" const values = __getAttioRecordValues<AttioRecordValuesFor<TObject>>(__normalizeAttioObjectSlug(object), record)");
|
|
968
|
-
parts.push(' return { ...values, ...(record as __AttioRecordBase), values, attributes: values } as __AttioRecordWithValues<AttioRecordValuesFor<TObject>>');
|
|
969
|
-
parts.push("}");
|
|
970
|
-
parts.push("");
|
|
971
|
-
parts.push('function __enhanceAttioQueryResult<TObject extends GeneratedAttioObject>(object: TObject, result: ToolOutputByName["attio_query_records"]): AttioQueryRecordsResult<TObject> {');
|
|
972
|
-
parts.push(" return {");
|
|
973
|
-
parts.push(" ...result,");
|
|
974
|
-
parts.push(" records: (result.records || []).map(record => __enhanceAttioRecord(object, record)).filter(Boolean) as Array<AttioRecordFor<TObject>>,");
|
|
975
|
-
parts.push(" }");
|
|
976
|
-
parts.push("}");
|
|
977
|
-
parts.push("");
|
|
978
|
-
parts.push('function __enhanceAttioUpsertResult<TObject extends GeneratedAttioObject>(object: TObject, result: ToolOutputByName["attio_upsert_record"]): AttioUpsertRecordResult<TObject> {');
|
|
979
|
-
parts.push(" return {");
|
|
980
|
-
parts.push(" ...result,");
|
|
981
|
-
parts.push(" records: (result.records || []).map(record => __enhanceAttioRecord(object, record)).filter(Boolean) as Array<AttioRecordFor<TObject>>,");
|
|
982
|
-
parts.push(" }");
|
|
983
|
-
parts.push("}");
|
|
984
|
-
parts.push("");
|
|
985
|
-
}
|
|
986
|
-
// Generate per-tool param types (with integrationId omitted where applicable)
|
|
987
|
-
for (const tool of tools) {
|
|
988
|
-
if (isAttioTool(tool))
|
|
989
|
-
continue;
|
|
990
|
-
const typeName = toolNameToInterfaceName(tool.name);
|
|
991
|
-
const schema = hasAutoFillId(tool) ? omitIntegrationId(tool.parameters) : tool.parameters;
|
|
992
|
-
const tsType = jsonSchemaToTs(schema, 0);
|
|
993
|
-
if (tool.description) {
|
|
994
|
-
parts.push(`/** ${tool.description} */`);
|
|
995
|
-
}
|
|
996
|
-
// Always use `type` (not `interface`) so params are assignable to Record<string, unknown>
|
|
997
|
-
parts.push(`export type ${typeName} = ${tsType}`);
|
|
998
|
-
parts.push("");
|
|
999
|
-
}
|
|
1000
|
-
const groups = [];
|
|
1001
|
-
for (const [integration, integrationTools] of byIntegration) {
|
|
1002
|
-
const needsAutoFill = integrationTools.some(hasAutoFillId);
|
|
1003
|
-
if (needsAutoFill) {
|
|
1004
|
-
const instances = instanceMap.get(integration) || [];
|
|
1005
|
-
if (instances.length === 0)
|
|
1006
|
-
continue;
|
|
1007
|
-
groups.push({ key: integration, integration, tools: integrationTools, integrationId: instances[0].id });
|
|
1008
|
-
}
|
|
1009
|
-
else {
|
|
1010
|
-
groups.push({ key: integration, integration, tools: integrationTools });
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
groups.sort((a, b) => a.key.localeCompare(b.key));
|
|
1014
|
-
const githubToolsExist = groups.some(g => g.integration === "github");
|
|
1015
|
-
if (githubToolsExist) {
|
|
1016
|
-
const githubRepoFullNames = Array.from(new Set(input.github.flatMap(g => g.repositories.map(r => (r.owner && r.name ? `${r.owner}/${r.name}` : r.name).trim())).filter(Boolean)));
|
|
1017
|
-
const nameToFullName = new Map();
|
|
1018
|
-
const ambiguousNames = new Set();
|
|
1019
|
-
for (const fullName of githubRepoFullNames) {
|
|
1020
|
-
const [, repoName = fullName] = fullName.split("/", 2);
|
|
1021
|
-
if (nameToFullName.has(repoName) && nameToFullName.get(repoName) !== fullName) {
|
|
1022
|
-
ambiguousNames.add(repoName);
|
|
1023
|
-
nameToFullName.delete(repoName);
|
|
1024
|
-
}
|
|
1025
|
-
else if (!ambiguousNames.has(repoName)) {
|
|
1026
|
-
nameToFullName.set(repoName, fullName);
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
parts.push(`const __githubRepoNameToFullName = new Map<string, string>([`);
|
|
1030
|
-
for (const [name, fullName] of Array.from(nameToFullName.entries()).sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
1031
|
-
parts.push(` ["${escapeString(name)}", "${escapeString(fullName)}"],`);
|
|
1032
|
-
}
|
|
1033
|
-
parts.push(`])`);
|
|
1034
|
-
parts.push("");
|
|
1035
|
-
parts.push(`function __normalizeGitHubRepos(repo: unknown): string {`);
|
|
1036
|
-
parts.push(` if (repo && typeof repo === "object" && "fullName" in (repo as Record<string, unknown>)) {`);
|
|
1037
|
-
parts.push(` const fullName = (repo as { fullName?: unknown }).fullName`);
|
|
1038
|
-
parts.push(` if (typeof fullName === "string" && fullName.length > 0) return fullName`);
|
|
1039
|
-
parts.push(` }`);
|
|
1040
|
-
parts.push(` if (typeof repo === "string") {`);
|
|
1041
|
-
parts.push(` if (repo.includes("/")) return repo`);
|
|
1042
|
-
parts.push(` return __githubRepoNameToFullName.get(repo) ?? repo`);
|
|
1043
|
-
parts.push(` }`);
|
|
1044
|
-
parts.push(` return String(repo ?? "")`);
|
|
1045
|
-
parts.push(`}`);
|
|
1046
|
-
parts.push("");
|
|
1047
|
-
parts.push(`function __normalizeGitHubReposNames(repositories: unknown): string[] {`);
|
|
1048
|
-
parts.push(` if (!Array.isArray(repositories)) return []`);
|
|
1049
|
-
parts.push(` return repositories.map(repo => __normalizeGitHubRepos(repo))`);
|
|
1050
|
-
parts.push(`}`);
|
|
1051
|
-
parts.push("");
|
|
1052
|
-
}
|
|
1053
|
-
// Generate the GeneratedTools type
|
|
1054
|
-
parts.push("export type GeneratedTools = {");
|
|
1055
|
-
for (const group of groups) {
|
|
1056
|
-
parts.push(` ${group.key}: {`);
|
|
1057
|
-
for (const tool of group.tools) {
|
|
1058
|
-
const methodName = toCamelCase(tool.displayName);
|
|
1059
|
-
const paramsType = toolNameToInterfaceName(tool.name);
|
|
1060
|
-
if (tool.description) {
|
|
1061
|
-
parts.push(` /** ${tool.description} */`);
|
|
1062
|
-
}
|
|
1063
|
-
if (group.integration === "attio" && tool.name === "attio_query_records") {
|
|
1064
|
-
parts.push(` ${methodName}<TObject extends GeneratedAttioObject>(params: AttioQueryRecordsParams<TObject>): Promise<AttioQueryRecordsResult<TObject>>`);
|
|
1065
|
-
}
|
|
1066
|
-
else if (group.integration === "attio" && tool.name === "attio_upsert_record") {
|
|
1067
|
-
parts.push(` ${methodName}<TObject extends GeneratedAttioObject>(params: AttioUpsertRecordParams<TObject>): Promise<AttioUpsertRecordResult<TObject>>`);
|
|
1068
|
-
}
|
|
1069
|
-
else if (group.integration === "attio" && tool.name === "attio_list_objects") {
|
|
1070
|
-
parts.push(` ${methodName}(params?: AttioListObjectsParams): Promise<ToolOutputByName["${escapeString(tool.name)}"]>`);
|
|
1071
|
-
}
|
|
1072
|
-
else {
|
|
1073
|
-
parts.push(` ${methodName}(params: ${paramsType}): Promise<ToolOutputByName["${escapeString(tool.name)}"]>`);
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
parts.push(" }");
|
|
1077
|
-
}
|
|
1078
|
-
parts.push("}");
|
|
1079
|
-
parts.push("");
|
|
1080
|
-
// Module augmentation — adds `tools` to TerseAgent's type
|
|
1081
|
-
parts.push('declare module "terse-sdk" {');
|
|
1082
|
-
parts.push(" interface TerseAgent {");
|
|
1083
|
-
parts.push(" readonly tools: GeneratedTools");
|
|
1084
|
-
parts.push(" }");
|
|
1085
|
-
parts.push("}");
|
|
1086
|
-
parts.push("");
|
|
1087
|
-
// Runtime: factory that builds the tools object for a given agent instance,
|
|
1088
|
-
// filtered to only include tools whose integration matches the agent's skills.
|
|
1089
|
-
parts.push("function createTools(agent: TerseAgent): GeneratedTools {");
|
|
1090
|
-
parts.push(" const allowed: Set<string> = new Set(agent.skills.map(s => s.integrationType))");
|
|
1091
|
-
parts.push(" return {");
|
|
1092
|
-
for (const group of groups) {
|
|
1093
|
-
const intType = toolIntegrationToIntegrationType(group.integration);
|
|
1094
|
-
parts.push(` ...(allowed.has("${escapeString(intType)}") ? { ${group.key}: {`);
|
|
1095
|
-
for (const tool of group.tools) {
|
|
1096
|
-
const methodName = toCamelCase(tool.displayName);
|
|
1097
|
-
const paramsType = toolNameToInterfaceName(tool.name);
|
|
1098
|
-
const normalizedParamsExpr = group.integration === "github" ? normalizeGitHubReposParams(tool.name) : "params";
|
|
1099
|
-
if (group.integration === "attio" && tool.name === "attio_query_records" && group.integrationId) {
|
|
1100
|
-
parts.push(` ${methodName}: <TObject extends GeneratedAttioObject>(params: AttioQueryRecordsParams<TObject>) =>`);
|
|
1101
|
-
parts.push(` agent.executeTool<ToolOutputByName["${escapeString(tool.name)}"]>("${escapeString(tool.name)}", { objectSlug: __normalizeAttioObjectSlug(params.object), filter: __serializeAttioFilter(params.filter), limit: params.limit ?? null, integrationId: "${escapeString(group.integrationId)}" }).then(result => __enhanceAttioQueryResult(params.object, result)),`);
|
|
1102
|
-
}
|
|
1103
|
-
else if (group.integration === "attio" && tool.name === "attio_upsert_record" && group.integrationId) {
|
|
1104
|
-
parts.push(` ${methodName}: <TObject extends GeneratedAttioObject>(params: AttioUpsertRecordParams<TObject>) =>`);
|
|
1105
|
-
parts.push(` agent.executeTool<ToolOutputByName["${escapeString(tool.name)}"]>("${escapeString(tool.name)}", { objectSlug: __normalizeAttioObjectSlug(params.object), matchingAttribute: params.matchingAttribute, records: __serializeAttioRecords(params.records), integrationId: "${escapeString(group.integrationId)}" }).then(result => __enhanceAttioUpsertResult(params.object, result)),`);
|
|
1106
|
-
}
|
|
1107
|
-
else if (group.integration === "attio" && tool.name === "attio_list_objects" && group.integrationId) {
|
|
1108
|
-
parts.push(` ${methodName}: (params: AttioListObjectsParams = {}) =>`);
|
|
1109
|
-
parts.push(` agent.executeTool<ToolOutputByName["${escapeString(tool.name)}"]>("${escapeString(tool.name)}", { ...params, integrationId: "${escapeString(group.integrationId)}" }),`);
|
|
1110
|
-
}
|
|
1111
|
-
else if (group.integrationId && hasAutoFillId(tool)) {
|
|
1112
|
-
parts.push(` ${methodName}: (params: ${paramsType}) =>`);
|
|
1113
|
-
parts.push(` agent.executeTool<ToolOutputByName["${escapeString(tool.name)}"]>("${escapeString(tool.name)}", { ...(${normalizedParamsExpr}), integrationId: "${escapeString(group.integrationId)}" }),`);
|
|
1114
|
-
}
|
|
1115
|
-
else {
|
|
1116
|
-
parts.push(` ${methodName}: (params: ${paramsType}) =>`);
|
|
1117
|
-
parts.push(` agent.executeTool<ToolOutputByName["${escapeString(tool.name)}"]>("${escapeString(tool.name)}", ${normalizedParamsExpr}),`);
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
parts.push(` } } : {}),`);
|
|
1121
|
-
}
|
|
1122
|
-
parts.push(" } as GeneratedTools");
|
|
1123
|
-
parts.push("}");
|
|
1124
|
-
parts.push("");
|
|
1125
|
-
// Expose factory on globalThis for CLI (tsx may load a separate module instance)
|
|
1126
|
-
parts.push(";(globalThis as any).__terse_createTools = createTools");
|
|
1127
|
-
parts.push("");
|
|
1128
|
-
return { code: parts.join("\n"), imports };
|
|
1129
|
-
}
|
|
1130
|
-
// ── System (always included) ──────────────────────────────────────────
|
|
1131
|
-
function generateSystemSection(tools) {
|
|
1132
|
-
const imports = new Set(["TimeTriggerConfig", "TerseConfig", "TypedSkill"]);
|
|
1133
|
-
const parts = [];
|
|
1134
|
-
const skillToolType = buildSkillToolTypeForIntegration(tools, "terse");
|
|
1135
|
-
parts.push(sectionHeader("Schedule"));
|
|
1136
|
-
parts.push("");
|
|
1137
|
-
parts.push("export const Schedule = {");
|
|
1138
|
-
parts.push(` /** Use in \`triggers[]\` — run on a cron schedule */`);
|
|
1139
|
-
parts.push(" cron(opts: { expression: string }): TimeTriggerConfig {");
|
|
1140
|
-
parts.push(" return new TimeTriggerConfig(opts.expression)");
|
|
1141
|
-
parts.push(" },");
|
|
1142
|
-
parts.push("}");
|
|
1143
|
-
parts.push("");
|
|
1144
|
-
parts.push(sectionHeader("Terse"));
|
|
1145
|
-
parts.push("");
|
|
1146
|
-
parts.push("export const Terse = {");
|
|
1147
|
-
parts.push(` /** Use in \`skills[]\` — built-in web search */`);
|
|
1148
|
-
parts.push(` skill(): TypedSkill<${skillToolType}> {`);
|
|
1149
|
-
parts.push(` return new TerseConfig() as TypedSkill<${skillToolType}>`);
|
|
1150
|
-
parts.push(" },");
|
|
1151
|
-
parts.push("}");
|
|
1152
|
-
parts.push("");
|
|
1153
|
-
return { code: parts.join("\n"), imports };
|
|
1154
|
-
}
|
|
1155
|
-
// ── Main Codegen Entry Point ──────────────────────────────────────────
|
|
1156
|
-
export function generateCode(input) {
|
|
1157
|
-
const allImports = new Set();
|
|
1158
|
-
const sections = [];
|
|
1159
|
-
const sectionGenerators = [
|
|
1160
|
-
generateGitHubSection(input.github, input.tools),
|
|
1161
|
-
generateGmailSection(input.gmail, input.tools),
|
|
1162
|
-
generateSlackSection(input.slack, input.tools),
|
|
1163
|
-
generateFigmaSection(input.figma),
|
|
1164
|
-
generateLinearSection(input.linear, input.tools),
|
|
1165
|
-
generateAtlassianSection(input.atlassian, input.tools),
|
|
1166
|
-
generateNotionSection(input.notion, input.tools),
|
|
1167
|
-
generatePosthogSection(input.posthog, input.tools),
|
|
1168
|
-
generateDatadogSection(input.datadog, input.tools),
|
|
1169
|
-
generateLaunchDarklySection(input.launchdarkly, input.tools),
|
|
1170
|
-
generateWorkOSSection(input.workos, input.tools),
|
|
1171
|
-
generateAttioSection(input.attio, input.tools),
|
|
1172
|
-
generateSnowflakeSection(input.snowflake, input.tools),
|
|
1173
|
-
generateToolsSection(input.tools, input),
|
|
1174
|
-
generateSystemSection(input.tools),
|
|
1175
|
-
];
|
|
1176
|
-
for (const section of sectionGenerators) {
|
|
1177
|
-
if (section.code) {
|
|
1178
|
-
sections.push(section.code);
|
|
1179
|
-
section.imports.forEach(i => allImports.add(i));
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
// Build import statement
|
|
1183
|
-
const sortedImports = [...allImports].sort();
|
|
1184
|
-
const importLine = sortedImports.length <= 3
|
|
1185
|
-
? `import { ${sortedImports.join(", ")} } from "terse-sdk"`
|
|
1186
|
-
: `import {\n ${sortedImports.join(",\n ")},\n} from "terse-sdk"`;
|
|
1187
|
-
const header = [
|
|
1188
|
-
"// Auto-generated by `terse generate` — do not edit manually",
|
|
1189
|
-
"// Re-run `terse generate` to update.",
|
|
1190
|
-
"",
|
|
1191
|
-
importLine,
|
|
1192
|
-
"",
|
|
1193
|
-
];
|
|
1194
|
-
return [...header, ...sections].join("\n");
|
|
1195
|
-
}
|
|
1196
|
-
//# sourceMappingURL=codegen.js.map
|