terse-cli 0.1.0 → 0.1.2
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/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +358 -333
- package/dist/codegen.js.map +1 -1
- package/dist/deploy.d.ts +1 -1
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +35 -58
- package/dist/deploy.js.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +7 -0
- package/dist/run.js.map +1 -1
- package/dist/shared/ApiRoutes.d.ts +55 -0
- package/dist/shared/ApiRoutes.d.ts.map +1 -1
- package/dist/shared/ApiRoutes.js +40 -0
- package/dist/shared/ApiRoutes.js.map +1 -1
- package/dist/shared/ApprovalTypes.d.ts +29 -0
- package/dist/shared/ApprovalTypes.d.ts.map +1 -0
- package/dist/shared/ApprovalTypes.js +8 -0
- package/dist/shared/ApprovalTypes.js.map +1 -0
- package/dist/shared/Configs.d.ts +50 -9
- package/dist/shared/Configs.d.ts.map +1 -1
- package/dist/shared/Configs.js +62 -10
- package/dist/shared/Configs.js.map +1 -1
- package/dist/shared/FrontendRoutes.d.ts +2 -0
- package/dist/shared/FrontendRoutes.d.ts.map +1 -1
- package/dist/shared/FrontendRoutes.js +2 -0
- package/dist/shared/FrontendRoutes.js.map +1 -1
- package/dist/shared/InvalidationKeys.d.ts +7 -0
- package/dist/shared/InvalidationKeys.d.ts.map +1 -1
- package/dist/shared/InvalidationKeys.js +13 -0
- package/dist/shared/InvalidationKeys.js.map +1 -1
- package/dist/shared/ModelEvents.d.ts +1 -0
- package/dist/shared/ModelEvents.d.ts.map +1 -1
- package/dist/shared/Notifications.d.ts +11 -0
- package/dist/shared/Notifications.d.ts.map +1 -1
- package/dist/shared/Notifications.js.map +1 -1
- package/dist/shared/RunHistoryTypes.d.ts +2 -1
- package/dist/shared/RunHistoryTypes.d.ts.map +1 -1
- package/dist/shared/RunHistoryTypes.js +1 -0
- package/dist/shared/RunHistoryTypes.js.map +1 -1
- package/dist/shared/SentNotifications.d.ts +30 -0
- package/dist/shared/SentNotifications.d.ts.map +1 -0
- package/dist/shared/SentNotifications.js +13 -0
- package/dist/shared/SentNotifications.js.map +1 -0
- package/dist/shared/ToolDisplayUtils.d.ts +26 -0
- package/dist/shared/ToolDisplayUtils.d.ts.map +1 -0
- package/dist/shared/ToolDisplayUtils.js +1024 -0
- package/dist/shared/ToolDisplayUtils.js.map +1 -0
- package/dist/shared/types.d.ts +84 -1
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js +1 -0
- package/dist/shared/types.js.map +1 -1
- package/dist/test.js +2 -2
- package/dist/test.js.map +1 -1
- package/dist/util.d.ts +1 -9
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +2 -17
- package/dist/util.js.map +1 -1
- package/package.json +1 -6
- package/templates/package.json.tmpl +1 -1
- package/templates/src/index.ts.tmpl +24 -24
package/dist/codegen.js
CHANGED
|
@@ -19,9 +19,6 @@ function sectionHeader(name) {
|
|
|
19
19
|
function escapeString(s) {
|
|
20
20
|
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
21
21
|
}
|
|
22
|
-
function suffix(instances, i) {
|
|
23
|
-
return instances.length > 1 ? toPascalCase(instances[i].displayName || `Instance${i + 1}`) : "";
|
|
24
|
-
}
|
|
25
22
|
/** Map a tool's integration key back to the IntegrationType enum value used by ConfigInstance. */
|
|
26
23
|
function toolIntegrationToIntegrationType(toolIntegration) {
|
|
27
24
|
switch (toolIntegration) {
|
|
@@ -63,90 +60,106 @@ function generateResourceClass(className, fields, staticNameField, items) {
|
|
|
63
60
|
function generateGitHubSection(instances) {
|
|
64
61
|
if (instances.length === 0)
|
|
65
62
|
return EMPTY_SECTION;
|
|
63
|
+
const inst = instances[0];
|
|
66
64
|
const imports = new Set(["GitHubConfig"]);
|
|
67
65
|
const parts = [sectionHeader("GitHub"), ""];
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
for (const repo of repositoriesWithFullName) {
|
|
92
|
-
if (!ownerEntries.has(repo.owner)) {
|
|
93
|
-
let ownerStaticName = toStaticName(repo.owner, "UnknownOwner");
|
|
94
|
-
while (usedOwnerNames.has(ownerStaticName))
|
|
95
|
-
ownerStaticName += "_";
|
|
96
|
-
usedOwnerNames.add(ownerStaticName);
|
|
97
|
-
ownerEntries.set(repo.owner, { staticName: ownerStaticName, repos: [] });
|
|
98
|
-
}
|
|
99
|
-
ownerEntries.get(repo.owner).repos.push(repo);
|
|
100
|
-
}
|
|
101
|
-
parts.push(`export class ${ownerClass} {`);
|
|
102
|
-
parts.push(` constructor(public readonly name: string) {}`);
|
|
103
|
-
if (ownerEntries.size > 0) {
|
|
104
|
-
parts.push("");
|
|
105
|
-
for (const [owner, data] of ownerEntries) {
|
|
106
|
-
parts.push(` static ${data.staticName} = new ${ownerClass}("${escapeString(owner)}")`);
|
|
107
|
-
}
|
|
66
|
+
const id = inst.integration.id;
|
|
67
|
+
const toStaticName = (raw, fallback) => {
|
|
68
|
+
let name = toPascalCase(raw || fallback);
|
|
69
|
+
if (!name)
|
|
70
|
+
name = fallback;
|
|
71
|
+
if (/^\d/.test(name))
|
|
72
|
+
name = `_${name}`;
|
|
73
|
+
return name;
|
|
74
|
+
};
|
|
75
|
+
const repositoriesWithFullName = inst.repositories.map(repo => {
|
|
76
|
+
const owner = repo.owner || "UnknownOwner";
|
|
77
|
+
const fullName = repo.owner && repo.name ? `${repo.owner}/${repo.name}` : repo.name;
|
|
78
|
+
return { ...repo, owner, fullName };
|
|
79
|
+
});
|
|
80
|
+
const ownerEntries = new Map();
|
|
81
|
+
const usedOwnerNames = new Set();
|
|
82
|
+
for (const repo of repositoriesWithFullName) {
|
|
83
|
+
if (!ownerEntries.has(repo.owner)) {
|
|
84
|
+
let ownerStaticName = toStaticName(repo.owner, "UnknownOwner");
|
|
85
|
+
while (usedOwnerNames.has(ownerStaticName))
|
|
86
|
+
ownerStaticName += "_";
|
|
87
|
+
usedOwnerNames.add(ownerStaticName);
|
|
88
|
+
ownerEntries.set(repo.owner, { staticName: ownerStaticName, repos: [] });
|
|
108
89
|
}
|
|
109
|
-
|
|
90
|
+
ownerEntries.get(repo.owner).repos.push(repo);
|
|
91
|
+
}
|
|
92
|
+
parts.push("export class GithubOwner {");
|
|
93
|
+
parts.push(" constructor(public readonly name: string) {}");
|
|
94
|
+
if (ownerEntries.size > 0) {
|
|
110
95
|
parts.push("");
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
96
|
+
for (const [owner, data] of ownerEntries) {
|
|
97
|
+
parts.push(` static ${data.staticName} = new GithubOwner("${escapeString(owner)}")`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
parts.push("}");
|
|
101
|
+
parts.push("");
|
|
102
|
+
parts.push("export class Repos {");
|
|
103
|
+
parts.push(" constructor(");
|
|
104
|
+
parts.push(" public readonly repositoryId: number,");
|
|
105
|
+
parts.push(" public readonly name: string,");
|
|
106
|
+
parts.push(" public readonly owner: GithubOwner,");
|
|
107
|
+
parts.push(" public readonly fullName: string");
|
|
108
|
+
parts.push(" ) {}");
|
|
109
|
+
if (ownerEntries.size > 0) {
|
|
110
|
+
parts.push("");
|
|
111
|
+
for (const [, data] of ownerEntries) {
|
|
112
|
+
const usedRepoNames = new Set();
|
|
113
|
+
parts.push(` static ${data.staticName} = {`);
|
|
114
|
+
for (const repo of data.repos) {
|
|
115
|
+
let repoStaticName = toStaticName(repo.name, "Repos");
|
|
116
|
+
while (usedRepoNames.has(repoStaticName))
|
|
117
|
+
repoStaticName += "_";
|
|
118
|
+
usedRepoNames.add(repoStaticName);
|
|
119
|
+
parts.push(` ${repoStaticName}: new Repos(${repo.id}, "${escapeString(repo.name)}", GithubOwner.${data.staticName}, "${escapeString(repo.fullName)}"),`);
|
|
131
120
|
}
|
|
121
|
+
parts.push(" } as const");
|
|
132
122
|
}
|
|
133
|
-
parts.push("}");
|
|
134
|
-
parts.push("");
|
|
135
|
-
parts.push(`/** Use in \`triggers[]\` */`);
|
|
136
|
-
parts.push(`export class GithubTrigger${sfx} extends GitHubConfig {`);
|
|
137
|
-
parts.push(` constructor(repositories: ${repoClass}[]) {`);
|
|
138
|
-
parts.push(` super("${id}", repositories.map(r => r.repositoryId))`);
|
|
139
|
-
parts.push(" }");
|
|
140
|
-
parts.push("}");
|
|
141
|
-
parts.push("");
|
|
142
|
-
parts.push(`/** Use in \`skills[]\` */`);
|
|
143
|
-
parts.push(`export class GithubSkill${sfx} extends GitHubConfig {`);
|
|
144
|
-
parts.push(` constructor(repositories: ${repoClass}[]) {`);
|
|
145
|
-
parts.push(` super("${id}", repositories.map(r => r.repositoryId))`);
|
|
146
|
-
parts.push(" }");
|
|
147
|
-
parts.push("}");
|
|
148
|
-
parts.push("");
|
|
149
123
|
}
|
|
124
|
+
parts.push("}");
|
|
125
|
+
parts.push("");
|
|
126
|
+
imports.add("GitHubEventType");
|
|
127
|
+
imports.add("TypedTrigger");
|
|
128
|
+
imports.add("GithubPRInputEvent");
|
|
129
|
+
imports.add("GithubPushInputEvent");
|
|
130
|
+
imports.add("GithubInputEvent");
|
|
131
|
+
// Namespace object with typed event triggers
|
|
132
|
+
parts.push("export const GitHub = {");
|
|
133
|
+
parts.push(` /** Trigger on push to a repository */`);
|
|
134
|
+
parts.push(` onPush(opts: { repo: Repos }): TypedTrigger<GithubPushInputEvent> {`);
|
|
135
|
+
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PUSH]) as TypedTrigger<GithubPushInputEvent>`);
|
|
136
|
+
parts.push(" },");
|
|
137
|
+
parts.push(` /** Trigger when a pull request is opened */`);
|
|
138
|
+
parts.push(` onPROpened(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {`);
|
|
139
|
+
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PR_OPENED]) as TypedTrigger<GithubPRInputEvent>`);
|
|
140
|
+
parts.push(" },");
|
|
141
|
+
parts.push(` /** Trigger when a pull request is merged */`);
|
|
142
|
+
parts.push(` onPRMerged(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {`);
|
|
143
|
+
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PR_MERGED]) as TypedTrigger<GithubPRInputEvent>`);
|
|
144
|
+
parts.push(" },");
|
|
145
|
+
parts.push(` /** Trigger when a pull request is closed */`);
|
|
146
|
+
parts.push(` onPRClosed(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {`);
|
|
147
|
+
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PR_CLOSED]) as TypedTrigger<GithubPRInputEvent>`);
|
|
148
|
+
parts.push(" },");
|
|
149
|
+
parts.push(` /** Trigger on any pull request event */`);
|
|
150
|
+
parts.push(` onPR(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {`);
|
|
151
|
+
parts.push(` return new GitHubConfig("${id}", [opts.repo.repositoryId], [GitHubEventType.PR_OPENED, GitHubEventType.PR_MERGED, GitHubEventType.PR_CLOSED, GitHubEventType.PR_SYNCHRONIZE]) as TypedTrigger<GithubPRInputEvent>`);
|
|
152
|
+
parts.push(" },");
|
|
153
|
+
parts.push(` /** Trigger on specific GitHub events for the given repositories */`);
|
|
154
|
+
parts.push(` trigger(opts: { repos: Repos[]; eventTypes?: GitHubEventType[] }): TypedTrigger<GithubInputEvent> {`);
|
|
155
|
+
parts.push(` return new GitHubConfig("${id}", opts.repos.map(r => r.repositoryId), opts.eventTypes) as TypedTrigger<GithubInputEvent>`);
|
|
156
|
+
parts.push(" },");
|
|
157
|
+
parts.push(` /** Use in \`skills[]\` */`);
|
|
158
|
+
parts.push(` skill(opts: { repos: Repos[] }): GitHubConfig {`);
|
|
159
|
+
parts.push(` return new GitHubConfig("${id}", opts.repos.map(r => r.repositoryId))`);
|
|
160
|
+
parts.push(" },");
|
|
161
|
+
parts.push("}");
|
|
162
|
+
parts.push("");
|
|
150
163
|
return { code: parts.join("\n"), imports };
|
|
151
164
|
}
|
|
152
165
|
// ── Gmail ─────────────────────────────────────────────────────────────
|
|
@@ -155,57 +168,63 @@ function generateGmailSection(instances) {
|
|
|
155
168
|
return EMPTY_SECTION;
|
|
156
169
|
const imports = new Set(["GmailConfig", "GmailOutputConfig", "GmailDraftOutputConfig"]);
|
|
157
170
|
const parts = [sectionHeader("Gmail"), ""];
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
171
|
+
const id = instances[0].id;
|
|
172
|
+
imports.add("GmailEventType");
|
|
173
|
+
parts.push("export const Gmail = {");
|
|
174
|
+
parts.push(` /** Trigger when a new email is received */`);
|
|
175
|
+
parts.push(` onEmail(): GmailConfig {`);
|
|
176
|
+
parts.push(` return new GmailConfig("${id}", [GmailEventType.EMAIL_RECEIVED])`);
|
|
177
|
+
parts.push(" },");
|
|
178
|
+
parts.push(` /** Trigger on all Gmail events */`);
|
|
179
|
+
parts.push(` trigger(opts?: { eventTypes?: GmailEventType[] }): GmailConfig {`);
|
|
180
|
+
parts.push(` return new GmailConfig("${id}", opts?.eventTypes)`);
|
|
181
|
+
parts.push(" },");
|
|
182
|
+
parts.push(` /** Use in \`skills[]\` */`);
|
|
183
|
+
parts.push(` skill(): GmailOutputConfig {`);
|
|
184
|
+
parts.push(` return new GmailOutputConfig("${id}")`);
|
|
185
|
+
parts.push(" },");
|
|
186
|
+
parts.push(` /** Use in \`skills[]\` — creates draft emails */`);
|
|
187
|
+
parts.push(` draftSkill(): GmailDraftOutputConfig {`);
|
|
188
|
+
parts.push(` return new GmailDraftOutputConfig("${id}")`);
|
|
189
|
+
parts.push(" },");
|
|
190
|
+
parts.push("}");
|
|
191
|
+
parts.push("");
|
|
177
192
|
return { code: parts.join("\n"), imports };
|
|
178
193
|
}
|
|
179
194
|
// ── Slack ──────────────────────────────────────────────────────────────
|
|
180
195
|
function generateSlackSection(instances) {
|
|
181
196
|
if (instances.length === 0)
|
|
182
197
|
return EMPTY_SECTION;
|
|
198
|
+
const inst = instances[0];
|
|
183
199
|
const imports = new Set(["SlackConfig", "SlackOutputConfig"]);
|
|
184
200
|
const parts = [sectionHeader("Slack"), ""];
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
201
|
+
const id = inst.id;
|
|
202
|
+
parts.push(generateResourceClass("SlackChannel", [
|
|
203
|
+
{ classField: "channelId", type: "string", sourceField: "id" },
|
|
204
|
+
{ classField: "name", type: "string", sourceField: "name" },
|
|
205
|
+
], "name", inst.channels));
|
|
206
|
+
parts.push("");
|
|
207
|
+
imports.add("SlackEventType");
|
|
208
|
+
// Namespace object with typed event triggers
|
|
209
|
+
parts.push("export const Slack = {");
|
|
210
|
+
parts.push(` /** Trigger on any message in a channel */`);
|
|
211
|
+
parts.push(` onMessage(opts: { channel: SlackChannel; userIds?: string[] }): SlackConfig {`);
|
|
212
|
+
parts.push(` return new SlackConfig("${id}", opts.channel.channelId, opts.channel.name, false, opts.userIds, [SlackEventType.MESSAGE])`);
|
|
213
|
+
parts.push(" },");
|
|
214
|
+
parts.push(` /** Trigger on direct messages to the bot */`);
|
|
215
|
+
parts.push(` onDm(opts?: { userIds?: string[] }): SlackConfig {`);
|
|
216
|
+
parts.push(` return new SlackConfig("${id}", undefined, undefined, true, opts?.userIds, [SlackEventType.MESSAGE])`);
|
|
217
|
+
parts.push(" },");
|
|
218
|
+
parts.push(` /** Trigger on all Slack events (messages + DMs) for a channel */`);
|
|
219
|
+
parts.push(` trigger(opts?: { channel?: SlackChannel; listenToUserDms?: boolean; userIds?: string[]; eventTypes?: SlackEventType[] }): SlackConfig {`);
|
|
220
|
+
parts.push(` return new SlackConfig("${id}", opts?.channel?.channelId, opts?.channel?.name, opts?.listenToUserDms, opts?.userIds, opts?.eventTypes)`);
|
|
221
|
+
parts.push(" },");
|
|
222
|
+
parts.push(` /** Use in \`skills[]\` */`);
|
|
223
|
+
parts.push(` skill(opts?: { channel?: SlackChannel; userIds?: string[]; userNames?: string[]; listenToUserDms?: boolean }): SlackOutputConfig {`);
|
|
224
|
+
parts.push(` return new SlackOutputConfig("${id}", opts?.channel?.channelId, opts?.channel?.name, opts?.userIds, opts?.userNames, opts?.listenToUserDms)`);
|
|
225
|
+
parts.push(" },");
|
|
226
|
+
parts.push("}");
|
|
227
|
+
parts.push("");
|
|
209
228
|
return { code: parts.join("\n"), imports };
|
|
210
229
|
}
|
|
211
230
|
// ── Figma ─────────────────────────────────────────────────────────────
|
|
@@ -214,255 +233,265 @@ function generateFigmaSection(instances) {
|
|
|
214
233
|
return EMPTY_SECTION;
|
|
215
234
|
const imports = new Set(["FigmaConfig"]);
|
|
216
235
|
const parts = [sectionHeader("Figma"), ""];
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
236
|
+
const id = instances[0].id;
|
|
237
|
+
imports.add("FigmaEventType");
|
|
238
|
+
parts.push("export const Figma = {");
|
|
239
|
+
parts.push(` /** Trigger when a comment is added to a Figma file */`);
|
|
240
|
+
parts.push(` onComment(opts: { fileKey: string; fileName: string; teamId: string }): FigmaConfig {`);
|
|
241
|
+
parts.push(` return new FigmaConfig("${id}", opts.fileKey, opts.fileName, opts.teamId, [FigmaEventType.FILE_COMMENT])`);
|
|
242
|
+
parts.push(" },");
|
|
243
|
+
parts.push(` /** Trigger on all Figma events */`);
|
|
244
|
+
parts.push(` trigger(opts: { fileKey: string; fileName: string; teamId: string; eventTypes?: FigmaEventType[] }): FigmaConfig {`);
|
|
245
|
+
parts.push(` return new FigmaConfig("${id}", opts.fileKey, opts.fileName, opts.teamId, opts.eventTypes)`);
|
|
246
|
+
parts.push(" },");
|
|
247
|
+
parts.push("}");
|
|
248
|
+
parts.push("");
|
|
227
249
|
return { code: parts.join("\n"), imports };
|
|
228
250
|
}
|
|
229
251
|
// ── Linear ────────────────────────────────────────────────────────────
|
|
230
252
|
function generateLinearSection(instances) {
|
|
231
253
|
if (instances.length === 0)
|
|
232
254
|
return EMPTY_SECTION;
|
|
255
|
+
const inst = instances[0];
|
|
233
256
|
const imports = new Set(["LinearInputConfig", "LinearOutputConfig"]);
|
|
234
257
|
const parts = [sectionHeader("Linear"), ""];
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
258
|
+
const id = inst.id;
|
|
259
|
+
parts.push(generateResourceClass("LinearTeam", [
|
|
260
|
+
{ classField: "teamId", type: "string", sourceField: "id" },
|
|
261
|
+
{ classField: "name", type: "string", sourceField: "name" },
|
|
262
|
+
{ classField: "key", type: "string", sourceField: "key" },
|
|
263
|
+
], "name", inst.teams));
|
|
264
|
+
parts.push("");
|
|
265
|
+
imports.add("LinearEventType");
|
|
266
|
+
parts.push("export const Linear = {");
|
|
267
|
+
parts.push(` /** Trigger when a new issue is created */`);
|
|
268
|
+
parts.push(` onIssueCreated(opts?: { projectId?: string; projectName?: string }): LinearInputConfig {`);
|
|
269
|
+
parts.push(` return new LinearInputConfig("${id}", opts?.projectId, opts?.projectName, [LinearEventType.ISSUE_CREATED])`);
|
|
270
|
+
parts.push(" },");
|
|
271
|
+
parts.push(` /** Trigger when an issue is updated */`);
|
|
272
|
+
parts.push(` onIssueUpdated(opts?: { projectId?: string; projectName?: string }): LinearInputConfig {`);
|
|
273
|
+
parts.push(` return new LinearInputConfig("${id}", opts?.projectId, opts?.projectName, [LinearEventType.ISSUE_UPDATED])`);
|
|
274
|
+
parts.push(" },");
|
|
275
|
+
parts.push(` /** Trigger when a comment is added to an issue */`);
|
|
276
|
+
parts.push(` onComment(opts?: { projectId?: string; projectName?: string }): LinearInputConfig {`);
|
|
277
|
+
parts.push(` return new LinearInputConfig("${id}", opts?.projectId, opts?.projectName, [LinearEventType.COMMENT_CREATED])`);
|
|
278
|
+
parts.push(" },");
|
|
279
|
+
parts.push(` /** Trigger on all Linear events */`);
|
|
280
|
+
parts.push(` trigger(opts?: { projectId?: string; projectName?: string; eventTypes?: LinearEventType[] }): LinearInputConfig {`);
|
|
281
|
+
parts.push(` return new LinearInputConfig("${id}", opts?.projectId, opts?.projectName, opts?.eventTypes)`);
|
|
282
|
+
parts.push(" },");
|
|
283
|
+
parts.push(` /** Use in \`skills[]\` */`);
|
|
284
|
+
parts.push(` skill(opts?: { team?: LinearTeam; projectId?: string; projectName?: string }): LinearOutputConfig {`);
|
|
285
|
+
parts.push(` return new LinearOutputConfig("${id}", opts?.team?.teamId, opts?.team?.name, opts?.projectId, opts?.projectName)`);
|
|
286
|
+
parts.push(" },");
|
|
287
|
+
parts.push("}");
|
|
288
|
+
parts.push("");
|
|
260
289
|
return { code: parts.join("\n"), imports };
|
|
261
290
|
}
|
|
262
291
|
// ── Jira & Confluence (Atlassian) ─────────────────────────────────────
|
|
263
292
|
function generateAtlassianSection(instances) {
|
|
264
293
|
if (instances.length === 0)
|
|
265
294
|
return EMPTY_SECTION;
|
|
295
|
+
const inst = instances[0];
|
|
266
296
|
const imports = new Set(["JiraConfig", "ConfluenceConfig"]);
|
|
267
297
|
const parts = [sectionHeader("Jira & Confluence"), ""];
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
298
|
+
const id = inst.id;
|
|
299
|
+
parts.push(generateResourceClass("JiraProject", [
|
|
300
|
+
{ classField: "projectKey", type: "string", sourceField: "key" },
|
|
301
|
+
{ classField: "projectId", type: "string", sourceField: "id" },
|
|
302
|
+
{ classField: "name", type: "string", sourceField: "name" },
|
|
303
|
+
], "name", inst.jiraProjects));
|
|
304
|
+
parts.push("");
|
|
305
|
+
parts.push(generateResourceClass("ConfluencePage", [
|
|
306
|
+
{ classField: "pageId", type: "string", sourceField: "id" },
|
|
307
|
+
{ classField: "title", type: "string", sourceField: "title" },
|
|
308
|
+
{ classField: "spaceId", type: "string", sourceField: "spaceId" },
|
|
309
|
+
{ classField: "spaceName", type: "string", sourceField: "spaceName" },
|
|
310
|
+
], "title", inst.confluencePages));
|
|
311
|
+
parts.push("");
|
|
312
|
+
imports.add("JiraEventType");
|
|
313
|
+
parts.push("export const Jira = {");
|
|
314
|
+
parts.push(` /** Trigger when a Jira issue is created */`);
|
|
315
|
+
parts.push(` onIssueCreated(opts?: { project?: JiraProject }): JiraConfig {`);
|
|
316
|
+
parts.push(` return new JiraConfig("${id}", opts?.project?.projectKey, opts?.project?.projectId, [JiraEventType.ISSUE_CREATED])`);
|
|
317
|
+
parts.push(" },");
|
|
318
|
+
parts.push(` /** Trigger when a Jira issue is updated */`);
|
|
319
|
+
parts.push(` onIssueUpdated(opts?: { project?: JiraProject }): JiraConfig {`);
|
|
320
|
+
parts.push(` return new JiraConfig("${id}", opts?.project?.projectKey, opts?.project?.projectId, [JiraEventType.ISSUE_UPDATED])`);
|
|
321
|
+
parts.push(" },");
|
|
322
|
+
parts.push(` /** Trigger on all Jira events */`);
|
|
323
|
+
parts.push(` trigger(opts?: { project?: JiraProject; eventTypes?: JiraEventType[] }): JiraConfig {`);
|
|
324
|
+
parts.push(` return new JiraConfig("${id}", opts?.project?.projectKey, opts?.project?.projectId, opts?.eventTypes)`);
|
|
325
|
+
parts.push(" },");
|
|
326
|
+
parts.push(` /** Use in \`skills[]\` */`);
|
|
327
|
+
parts.push(` skill(opts?: { project?: JiraProject }): JiraConfig {`);
|
|
328
|
+
parts.push(` return new JiraConfig("${id}", opts?.project?.projectKey, opts?.project?.projectId)`);
|
|
329
|
+
parts.push(" },");
|
|
330
|
+
parts.push("}");
|
|
331
|
+
parts.push("");
|
|
332
|
+
parts.push("export const Confluence = {");
|
|
333
|
+
parts.push(` /** Use in \`skills[]\` */`);
|
|
334
|
+
parts.push(` skill(opts: { page: ConfluencePage }): ConfluenceConfig {`);
|
|
335
|
+
parts.push(` return new ConfluenceConfig("${id}", opts.page.spaceName, opts.page.spaceId, opts.page.pageId, opts.page.title)`);
|
|
336
|
+
parts.push(" },");
|
|
337
|
+
parts.push("}");
|
|
338
|
+
parts.push("");
|
|
310
339
|
return { code: parts.join("\n"), imports };
|
|
311
340
|
}
|
|
312
341
|
// ── Notion ────────────────────────────────────────────────────────────
|
|
313
342
|
function generateNotionSection(instances) {
|
|
314
343
|
if (instances.length === 0)
|
|
315
344
|
return EMPTY_SECTION;
|
|
345
|
+
const inst = instances[0];
|
|
316
346
|
const imports = new Set(["NotionConfig"]);
|
|
317
347
|
const parts = [sectionHeader("Notion"), ""];
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
parts.push(` pages?.map(p => p.pageId), pages?.map(p => p.title))`);
|
|
339
|
-
parts.push(" }");
|
|
340
|
-
parts.push("}");
|
|
341
|
-
parts.push("");
|
|
342
|
-
}
|
|
348
|
+
const id = inst.id;
|
|
349
|
+
parts.push(generateResourceClass("NotionDatabase", [
|
|
350
|
+
{ classField: "databaseId", type: "string", sourceField: "id" },
|
|
351
|
+
{ classField: "title", type: "string", sourceField: "title" },
|
|
352
|
+
], "title", inst.databases));
|
|
353
|
+
parts.push("");
|
|
354
|
+
parts.push(generateResourceClass("NotionPage", [
|
|
355
|
+
{ classField: "pageId", type: "string", sourceField: "id" },
|
|
356
|
+
{ classField: "title", type: "string", sourceField: "title" },
|
|
357
|
+
], "title", inst.pages));
|
|
358
|
+
parts.push("");
|
|
359
|
+
parts.push("export const Notion = {");
|
|
360
|
+
parts.push(` /** Use in \`skills[]\` */`);
|
|
361
|
+
parts.push(` skill(opts?: { databases?: NotionDatabase[]; pages?: NotionPage[] }): NotionConfig {`);
|
|
362
|
+
parts.push(` return new NotionConfig("${id}",`);
|
|
363
|
+
parts.push(` opts?.databases?.map(d => d.databaseId), opts?.databases?.map(d => d.title),`);
|
|
364
|
+
parts.push(` opts?.pages?.map(p => p.pageId), opts?.pages?.map(p => p.title))`);
|
|
365
|
+
parts.push(" },");
|
|
366
|
+
parts.push("}");
|
|
367
|
+
parts.push("");
|
|
343
368
|
return { code: parts.join("\n"), imports };
|
|
344
369
|
}
|
|
345
370
|
// ── PostHog ───────────────────────────────────────────────────────────
|
|
346
371
|
function generatePosthogSection(instances) {
|
|
347
372
|
if (instances.length === 0)
|
|
348
373
|
return EMPTY_SECTION;
|
|
374
|
+
const inst = instances[0];
|
|
349
375
|
const imports = new Set(["PosthogConfig"]);
|
|
350
376
|
const parts = [sectionHeader("PostHog"), ""];
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
parts.push(" }");
|
|
365
|
-
parts.push("}");
|
|
366
|
-
parts.push("");
|
|
367
|
-
}
|
|
377
|
+
const id = inst.id;
|
|
378
|
+
parts.push(generateResourceClass("PosthogProject", [
|
|
379
|
+
{ classField: "projectId", type: "string", sourceField: "id" },
|
|
380
|
+
{ classField: "name", type: "string", sourceField: "name" },
|
|
381
|
+
], "name", inst.projects));
|
|
382
|
+
parts.push("");
|
|
383
|
+
parts.push("export const Posthog = {");
|
|
384
|
+
parts.push(` /** Use in \`skills[]\` */`);
|
|
385
|
+
parts.push(` skill(opts: { project: PosthogProject }): PosthogConfig {`);
|
|
386
|
+
parts.push(` return new PosthogConfig("${id}", opts.project.projectId, opts.project.name)`);
|
|
387
|
+
parts.push(" },");
|
|
388
|
+
parts.push("}");
|
|
389
|
+
parts.push("");
|
|
368
390
|
return { code: parts.join("\n"), imports };
|
|
369
391
|
}
|
|
370
392
|
// ── Datadog ───────────────────────────────────────────────────────────
|
|
371
393
|
function generateDatadogSection(instances) {
|
|
372
394
|
if (instances.length === 0)
|
|
373
395
|
return EMPTY_SECTION;
|
|
396
|
+
const inst = instances[0];
|
|
374
397
|
const imports = new Set(["DatadogConfig"]);
|
|
375
398
|
const parts = [sectionHeader("Datadog"), ""];
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
parts.push(" }");
|
|
389
|
-
parts.push("}");
|
|
390
|
-
parts.push("");
|
|
391
|
-
}
|
|
399
|
+
const id = inst.id;
|
|
400
|
+
parts.push(generateResourceClass("DatadogIndex", [
|
|
401
|
+
{ classField: "name", type: "string", sourceField: "name" },
|
|
402
|
+
], "name", inst.indexes));
|
|
403
|
+
parts.push("");
|
|
404
|
+
parts.push("export const Datadog = {");
|
|
405
|
+
parts.push(` /** Use in \`skills[]\` */`);
|
|
406
|
+
parts.push(` skill(opts?: { indexes?: DatadogIndex[] }): DatadogConfig {`);
|
|
407
|
+
parts.push(` return new DatadogConfig("${id}", opts?.indexes?.map(i => i.name))`);
|
|
408
|
+
parts.push(" },");
|
|
409
|
+
parts.push("}");
|
|
410
|
+
parts.push("");
|
|
392
411
|
return { code: parts.join("\n"), imports };
|
|
393
412
|
}
|
|
394
413
|
// ── LaunchDarkly ──────────────────────────────────────────────────────
|
|
395
414
|
function generateLaunchDarklySection(instances) {
|
|
396
415
|
if (instances.length === 0)
|
|
397
416
|
return EMPTY_SECTION;
|
|
417
|
+
const inst = instances[0];
|
|
398
418
|
const imports = new Set(["LaunchDarklyConfig"]);
|
|
399
419
|
const parts = [sectionHeader("LaunchDarkly"), ""];
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
parts.push(" }");
|
|
414
|
-
parts.push("}");
|
|
415
|
-
parts.push("");
|
|
416
|
-
}
|
|
420
|
+
const id = inst.id;
|
|
421
|
+
parts.push(generateResourceClass("LaunchDarklyProject", [
|
|
422
|
+
{ classField: "projectKey", type: "string", sourceField: "key" },
|
|
423
|
+
{ classField: "name", type: "string", sourceField: "name" },
|
|
424
|
+
], "name", inst.projects));
|
|
425
|
+
parts.push("");
|
|
426
|
+
parts.push("export const LaunchDarkly = {");
|
|
427
|
+
parts.push(` /** Use in \`skills[]\` */`);
|
|
428
|
+
parts.push(` skill(opts: { project: LaunchDarklyProject; environmentKeys: string[] }): LaunchDarklyConfig {`);
|
|
429
|
+
parts.push(` return new LaunchDarklyConfig("${id}", opts.project.projectKey, opts.environmentKeys)`);
|
|
430
|
+
parts.push(" },");
|
|
431
|
+
parts.push("}");
|
|
432
|
+
parts.push("");
|
|
417
433
|
return { code: parts.join("\n"), imports };
|
|
418
434
|
}
|
|
419
435
|
// ── WorkOS ────────────────────────────────────────────────────────────
|
|
420
436
|
function generateWorkOSSection(instances) {
|
|
421
437
|
if (instances.length === 0)
|
|
422
438
|
return EMPTY_SECTION;
|
|
423
|
-
const imports = new Set(["WorkOSInputConfig", "WorkOSOutputConfig"]);
|
|
439
|
+
const imports = new Set(["WorkOSInputConfig", "WorkOSOutputConfig", "WorkOSEventType"]);
|
|
424
440
|
const parts = [sectionHeader("WorkOS"), ""];
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
+
const id = instances[0].id;
|
|
442
|
+
parts.push("export const WorkOS = {");
|
|
443
|
+
parts.push(` /** Trigger on user creation */`);
|
|
444
|
+
parts.push(` onUserCreated(): WorkOSInputConfig {`);
|
|
445
|
+
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.USER_CREATED])`);
|
|
446
|
+
parts.push(" },");
|
|
447
|
+
parts.push(` /** Trigger on user update */`);
|
|
448
|
+
parts.push(` onUserUpdated(): WorkOSInputConfig {`);
|
|
449
|
+
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.USER_UPDATED])`);
|
|
450
|
+
parts.push(" },");
|
|
451
|
+
parts.push(` /** Trigger on user deletion */`);
|
|
452
|
+
parts.push(` onUserDeleted(): WorkOSInputConfig {`);
|
|
453
|
+
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.USER_DELETED])`);
|
|
454
|
+
parts.push(" },");
|
|
455
|
+
parts.push(` /** Trigger on organization membership change */`);
|
|
456
|
+
parts.push(` onMembershipChanged(): WorkOSInputConfig {`);
|
|
457
|
+
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.ORGANIZATION_MEMBERSHIP_CREATED, WorkOSEventType.ORGANIZATION_MEMBERSHIP_UPDATED, WorkOSEventType.ORGANIZATION_MEMBERSHIP_DELETED])`);
|
|
458
|
+
parts.push(" },");
|
|
459
|
+
parts.push(` /** Trigger on invitation accepted */`);
|
|
460
|
+
parts.push(` onInvitationAccepted(): WorkOSInputConfig {`);
|
|
461
|
+
parts.push(` return new WorkOSInputConfig("${id}", [WorkOSEventType.INVITATION_ACCEPTED])`);
|
|
462
|
+
parts.push(" },");
|
|
463
|
+
parts.push(` /** Trigger on specific WorkOS event types */`);
|
|
464
|
+
parts.push(` trigger(opts?: { eventTypes?: WorkOSEventType[] }): WorkOSInputConfig {`);
|
|
465
|
+
parts.push(` return new WorkOSInputConfig("${id}", opts?.eventTypes)`);
|
|
466
|
+
parts.push(" },");
|
|
467
|
+
parts.push(` /** Use in \`skills[]\` */`);
|
|
468
|
+
parts.push(` skill(): WorkOSOutputConfig {`);
|
|
469
|
+
parts.push(` return new WorkOSOutputConfig("${id}")`);
|
|
470
|
+
parts.push(" },");
|
|
471
|
+
parts.push("}");
|
|
472
|
+
parts.push("");
|
|
441
473
|
return { code: parts.join("\n"), imports };
|
|
442
474
|
}
|
|
443
475
|
// ── Attio ─────────────────────────────────────────────────────────────
|
|
444
476
|
function generateAttioSection(instances) {
|
|
445
477
|
if (instances.length === 0)
|
|
446
478
|
return EMPTY_SECTION;
|
|
479
|
+
const inst = instances[0];
|
|
447
480
|
const imports = new Set(["AttioOutputConfig"]);
|
|
448
481
|
const parts = [sectionHeader("Attio"), ""];
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
parts.push(" }");
|
|
463
|
-
parts.push("}");
|
|
464
|
-
parts.push("");
|
|
465
|
-
}
|
|
482
|
+
const id = inst.id;
|
|
483
|
+
parts.push(generateResourceClass("AttioObject", [
|
|
484
|
+
{ classField: "apiSlug", type: "string", sourceField: "api_slug" },
|
|
485
|
+
{ classField: "name", type: "string", sourceField: "singular_noun" },
|
|
486
|
+
], "singular_noun", inst.objects));
|
|
487
|
+
parts.push("");
|
|
488
|
+
parts.push("export const Attio = {");
|
|
489
|
+
parts.push(` /** Use in \`skills[]\` */`);
|
|
490
|
+
parts.push(` skill(opts?: { object?: AttioObject }): AttioOutputConfig {`);
|
|
491
|
+
parts.push(` return new AttioOutputConfig("${id}", opts?.object?.apiSlug)`);
|
|
492
|
+
parts.push(" },");
|
|
493
|
+
parts.push("}");
|
|
494
|
+
parts.push("");
|
|
466
495
|
return { code: parts.join("\n"), imports };
|
|
467
496
|
}
|
|
468
497
|
// ── Typed Tools ───────────────────────────────────────────────────────
|
|
@@ -578,20 +607,20 @@ function generateToolsSection(tools, input) {
|
|
|
578
607
|
};
|
|
579
608
|
// Ensure GitHub repository params are full "owner/repo".
|
|
580
609
|
// Supports:
|
|
581
|
-
// -
|
|
610
|
+
// - Repos objects (uses .fullName)
|
|
582
611
|
// - full string "owner/repo" (passthrough)
|
|
583
612
|
// - short repo name string "repo" (resolved via known generated repositories when unique)
|
|
584
|
-
const
|
|
613
|
+
const normalizeGitHubReposParams = (toolName) => {
|
|
585
614
|
switch (toolName) {
|
|
586
615
|
case "readGitHubFile":
|
|
587
616
|
case "listGitHubPullRequests":
|
|
588
617
|
case "listGitHubDirectory":
|
|
589
618
|
case "listGitHubCommits":
|
|
590
619
|
case "summarizeGitHubPullRequestDiff":
|
|
591
|
-
return "{ ...params, repository:
|
|
620
|
+
return "{ ...params, repository: __normalizeGitHubRepos((params as any).repository) }";
|
|
592
621
|
case "searchGitHubCode":
|
|
593
622
|
case "grepGitHubCode":
|
|
594
|
-
return "{ ...params, repositoryNames:
|
|
623
|
+
return "{ ...params, repositoryNames: __normalizeGitHubReposNames((params as any).repositoryNames) }";
|
|
595
624
|
default:
|
|
596
625
|
return "params";
|
|
597
626
|
}
|
|
@@ -608,18 +637,14 @@ function generateToolsSection(tools, input) {
|
|
|
608
637
|
parts.push(`export type ${typeName} = ${tsType}`);
|
|
609
638
|
parts.push("");
|
|
610
639
|
}
|
|
611
|
-
// Build tool groups: each group has a key, raw integration, tools, and optional baked-in integrationId
|
|
612
640
|
const groups = [];
|
|
613
641
|
for (const [integration, integrationTools] of byIntegration) {
|
|
614
642
|
const needsAutoFill = integrationTools.some(hasAutoFillId);
|
|
615
643
|
if (needsAutoFill) {
|
|
616
644
|
const instances = instanceMap.get(integration) || [];
|
|
617
645
|
if (instances.length === 0)
|
|
618
|
-
continue;
|
|
619
|
-
|
|
620
|
-
const sfx = suffix(instances, i);
|
|
621
|
-
groups.push({ key: integration + sfx, integration, tools: integrationTools, integrationId: instances[i].id });
|
|
622
|
-
}
|
|
646
|
+
continue;
|
|
647
|
+
groups.push({ key: integration, integration, tools: integrationTools, integrationId: instances[0].id });
|
|
623
648
|
}
|
|
624
649
|
else {
|
|
625
650
|
groups.push({ key: integration, integration, tools: integrationTools });
|
|
@@ -647,7 +672,7 @@ function generateToolsSection(tools, input) {
|
|
|
647
672
|
}
|
|
648
673
|
parts.push(`])`);
|
|
649
674
|
parts.push("");
|
|
650
|
-
parts.push(`function
|
|
675
|
+
parts.push(`function __normalizeGitHubRepos(repo: unknown): string {`);
|
|
651
676
|
parts.push(` if (repo && typeof repo === "object" && "fullName" in (repo as Record<string, unknown>)) {`);
|
|
652
677
|
parts.push(` const fullName = (repo as { fullName?: unknown }).fullName`);
|
|
653
678
|
parts.push(` if (typeof fullName === "string" && fullName.length > 0) return fullName`);
|
|
@@ -659,9 +684,9 @@ function generateToolsSection(tools, input) {
|
|
|
659
684
|
parts.push(` return String(repo ?? "")`);
|
|
660
685
|
parts.push(`}`);
|
|
661
686
|
parts.push("");
|
|
662
|
-
parts.push(`function
|
|
687
|
+
parts.push(`function __normalizeGitHubReposNames(repositories: unknown): string[] {`);
|
|
663
688
|
parts.push(` if (!Array.isArray(repositories)) return []`);
|
|
664
|
-
parts.push(` return repositories.map(repo =>
|
|
689
|
+
parts.push(` return repositories.map(repo => __normalizeGitHubRepos(repo))`);
|
|
665
690
|
parts.push(`}`);
|
|
666
691
|
parts.push("");
|
|
667
692
|
}
|
|
@@ -699,7 +724,7 @@ function generateToolsSection(tools, input) {
|
|
|
699
724
|
for (const tool of group.tools) {
|
|
700
725
|
const methodName = toCamelCase(tool.displayName);
|
|
701
726
|
const paramsType = toolNameToInterfaceName(tool.name);
|
|
702
|
-
const normalizedParamsExpr = group.integration === "github" ?
|
|
727
|
+
const normalizedParamsExpr = group.integration === "github" ? normalizeGitHubReposParams(tool.name) : "params";
|
|
703
728
|
if (group.integrationId && hasAutoFillId(tool)) {
|
|
704
729
|
parts.push(` ${methodName}: (params: ${paramsType}) =>`);
|
|
705
730
|
parts.push(` agent.executeTool<ToolOutputByName["${escapeString(tool.name)}"]>("${escapeString(tool.name)}", { ...(${normalizedParamsExpr}), integrationId: "${escapeString(group.integrationId)}" }),`);
|
|
@@ -725,20 +750,20 @@ function generateSystemSection() {
|
|
|
725
750
|
const parts = [];
|
|
726
751
|
parts.push(sectionHeader("Schedule"));
|
|
727
752
|
parts.push("");
|
|
728
|
-
parts.push(
|
|
729
|
-
parts.push(
|
|
730
|
-
parts.push("
|
|
731
|
-
parts.push("
|
|
732
|
-
parts.push(" }");
|
|
753
|
+
parts.push("export const Schedule = {");
|
|
754
|
+
parts.push(` /** Use in \`triggers[]\` — run on a cron schedule */`);
|
|
755
|
+
parts.push(" cron(opts: { expression: string }): TimeTriggerConfig {");
|
|
756
|
+
parts.push(" return new TimeTriggerConfig(opts.expression)");
|
|
757
|
+
parts.push(" },");
|
|
733
758
|
parts.push("}");
|
|
734
759
|
parts.push("");
|
|
735
760
|
parts.push(sectionHeader("Terse"));
|
|
736
761
|
parts.push("");
|
|
737
|
-
parts.push(
|
|
738
|
-
parts.push(
|
|
739
|
-
parts.push("
|
|
740
|
-
parts.push("
|
|
741
|
-
parts.push(" }");
|
|
762
|
+
parts.push("export const Terse = {");
|
|
763
|
+
parts.push(` /** Use in \`skills[]\` — built-in web search */`);
|
|
764
|
+
parts.push(" skill(): TerseConfig {");
|
|
765
|
+
parts.push(" return new TerseConfig()");
|
|
766
|
+
parts.push(" },");
|
|
742
767
|
parts.push("}");
|
|
743
768
|
parts.push("");
|
|
744
769
|
return { code: parts.join("\n"), imports };
|