runwork 0.20.0 → 0.21.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/bundled-types/core-workspace.d.ts +2 -2
- package/dist/index.js +243 -207
- package/package.json +1 -1
|
@@ -370,8 +370,8 @@ export declare class WorkspaceContext {
|
|
|
370
370
|
/**
|
|
371
371
|
* Send a notification to a workspace user
|
|
372
372
|
* Can only send to verified workspace members
|
|
373
|
-
* The platform wraps your content in a standard notification template
|
|
374
|
-
*
|
|
373
|
+
* The platform wraps your content in a standard notification template and
|
|
374
|
+
* sends it from your app's name, using `title` as the subject line
|
|
375
375
|
*
|
|
376
376
|
* @example
|
|
377
377
|
* ```typescript
|
package/dist/index.js
CHANGED
|
@@ -4804,8 +4804,8 @@ export declare class WorkspaceContext {
|
|
|
4804
4804
|
/**
|
|
4805
4805
|
* Send a notification to a workspace user
|
|
4806
4806
|
* Can only send to verified workspace members
|
|
4807
|
-
* The platform wraps your content in a standard notification template
|
|
4808
|
-
*
|
|
4807
|
+
* The platform wraps your content in a standard notification template and
|
|
4808
|
+
* sends it from your app's name, using \`title\` as the subject line
|
|
4809
4809
|
*
|
|
4810
4810
|
* @example
|
|
4811
4811
|
* \`\`\`typescript
|
|
@@ -7645,7 +7645,7 @@ function createKeyboardListener() {
|
|
|
7645
7645
|
}
|
|
7646
7646
|
|
|
7647
7647
|
// src/generated/version.ts
|
|
7648
|
-
var VERSION = "0.
|
|
7648
|
+
var VERSION = "0.21.0";
|
|
7649
7649
|
|
|
7650
7650
|
// src/commands/dev.ts
|
|
7651
7651
|
var exports_dev = {};
|
|
@@ -10742,6 +10742,110 @@ init_store();
|
|
|
10742
10742
|
init_client();
|
|
10743
10743
|
import { Command as Command13 } from "commander";
|
|
10744
10744
|
import { readFileSync as readFileSync22, existsSync as existsSync25 } from "fs";
|
|
10745
|
+
// ../../shared/skill/skill-canonical.ts
|
|
10746
|
+
function toSkillSlug(value) {
|
|
10747
|
+
return value.trim().replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
10748
|
+
}
|
|
10749
|
+
function quoteYamlValue(value) {
|
|
10750
|
+
if (value === "")
|
|
10751
|
+
return '""';
|
|
10752
|
+
if (/[:#[\]{}|>&*!,?'"]/.test(value) || value !== value.trim()) {
|
|
10753
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
10754
|
+
return `"${escaped}"`;
|
|
10755
|
+
}
|
|
10756
|
+
return value;
|
|
10757
|
+
}
|
|
10758
|
+
function unquoteYamlValue(raw) {
|
|
10759
|
+
const value = raw.trim();
|
|
10760
|
+
if (value.length >= 2) {
|
|
10761
|
+
const first = value[0];
|
|
10762
|
+
const last = value[value.length - 1];
|
|
10763
|
+
if (first === '"' && last === '"') {
|
|
10764
|
+
return value.slice(1, -1).replace(/\\(["\\])/g, "$1");
|
|
10765
|
+
}
|
|
10766
|
+
if (first === "'" && last === "'") {
|
|
10767
|
+
return value.slice(1, -1);
|
|
10768
|
+
}
|
|
10769
|
+
}
|
|
10770
|
+
return value;
|
|
10771
|
+
}
|
|
10772
|
+
function parseSkillMd(content) {
|
|
10773
|
+
const lines = content.split(`
|
|
10774
|
+
`);
|
|
10775
|
+
if (lines[0]?.trim() !== "---") {
|
|
10776
|
+
const rawName = lines[0]?.replace(/^#\s*/, "").trim() || "Untitled Skill";
|
|
10777
|
+
return {
|
|
10778
|
+
frontmatter: {
|
|
10779
|
+
name: toSkillSlug(rawName) || "untitled-skill",
|
|
10780
|
+
description: ""
|
|
10781
|
+
},
|
|
10782
|
+
orderedKeys: [],
|
|
10783
|
+
body: content,
|
|
10784
|
+
hadFrontmatter: false
|
|
10785
|
+
};
|
|
10786
|
+
}
|
|
10787
|
+
let closingIndex = -1;
|
|
10788
|
+
for (let i = 1;i < lines.length; i++) {
|
|
10789
|
+
if (lines[i]?.trim() === "---") {
|
|
10790
|
+
closingIndex = i;
|
|
10791
|
+
break;
|
|
10792
|
+
}
|
|
10793
|
+
}
|
|
10794
|
+
if (closingIndex === -1) {
|
|
10795
|
+
return {
|
|
10796
|
+
frontmatter: { name: "untitled-skill", description: "" },
|
|
10797
|
+
orderedKeys: [],
|
|
10798
|
+
body: content,
|
|
10799
|
+
hadFrontmatter: false
|
|
10800
|
+
};
|
|
10801
|
+
}
|
|
10802
|
+
const frontmatterEntries = {};
|
|
10803
|
+
const orderedKeys = [];
|
|
10804
|
+
for (let i = 1;i < closingIndex; i++) {
|
|
10805
|
+
const line = lines[i];
|
|
10806
|
+
if (!line)
|
|
10807
|
+
continue;
|
|
10808
|
+
const colonIndex = line.indexOf(":");
|
|
10809
|
+
if (colonIndex > 0) {
|
|
10810
|
+
const key = line.slice(0, colonIndex).trim();
|
|
10811
|
+
const value = unquoteYamlValue(line.slice(colonIndex + 1));
|
|
10812
|
+
if (!(key in frontmatterEntries))
|
|
10813
|
+
orderedKeys.push(key);
|
|
10814
|
+
frontmatterEntries[key] = value;
|
|
10815
|
+
}
|
|
10816
|
+
}
|
|
10817
|
+
const body = lines.slice(closingIndex + 1).join(`
|
|
10818
|
+
`).trim();
|
|
10819
|
+
return {
|
|
10820
|
+
frontmatter: {
|
|
10821
|
+
...frontmatterEntries,
|
|
10822
|
+
name: toSkillSlug(frontmatterEntries.name || "") || "untitled-skill",
|
|
10823
|
+
description: frontmatterEntries.description || ""
|
|
10824
|
+
},
|
|
10825
|
+
orderedKeys,
|
|
10826
|
+
body,
|
|
10827
|
+
hadFrontmatter: true
|
|
10828
|
+
};
|
|
10829
|
+
}
|
|
10830
|
+
function buildSkillMd(parts) {
|
|
10831
|
+
const lines = [
|
|
10832
|
+
"---",
|
|
10833
|
+
`name: ${toSkillSlug(parts.name) || "untitled-skill"}`,
|
|
10834
|
+
`description: ${quoteYamlValue(parts.description ?? "")}`
|
|
10835
|
+
];
|
|
10836
|
+
if (parts.extra) {
|
|
10837
|
+
for (const [key, value] of Object.entries(parts.extra)) {
|
|
10838
|
+
if (key === "name" || key === "description")
|
|
10839
|
+
continue;
|
|
10840
|
+
lines.push(`${key}: ${quoteYamlValue(value)}`);
|
|
10841
|
+
}
|
|
10842
|
+
}
|
|
10843
|
+
lines.push("---", "", (parts.body ?? "").trim());
|
|
10844
|
+
return lines.join(`
|
|
10845
|
+
`);
|
|
10846
|
+
}
|
|
10847
|
+
|
|
10848
|
+
// src/commands/skills.ts
|
|
10745
10849
|
function truncate(text2, max) {
|
|
10746
10850
|
if (!text2)
|
|
10747
10851
|
return "";
|
|
@@ -10808,6 +10912,23 @@ async function readStdin2() {
|
|
|
10808
10912
|
}
|
|
10809
10913
|
return Buffer.concat(chunks).toString("utf-8");
|
|
10810
10914
|
}
|
|
10915
|
+
function buildSkillPushPayload(fileContent, nameArg) {
|
|
10916
|
+
const parsed = parseSkillMd(fileContent);
|
|
10917
|
+
const docName = parsed.hadFrontmatter && parsed.frontmatter.name !== "untitled-skill" ? parsed.frontmatter.name : "";
|
|
10918
|
+
const name = toSkillSlug(nameArg || "") || docName;
|
|
10919
|
+
if (!name)
|
|
10920
|
+
return null;
|
|
10921
|
+
const description = parsed.hadFrontmatter ? parsed.frontmatter.description : "";
|
|
10922
|
+
const extra = {};
|
|
10923
|
+
for (const key of parsed.orderedKeys) {
|
|
10924
|
+
if (key === "name" || key === "description")
|
|
10925
|
+
continue;
|
|
10926
|
+
const value = parsed.frontmatter[key];
|
|
10927
|
+
if (typeof value === "string")
|
|
10928
|
+
extra[key] = value;
|
|
10929
|
+
}
|
|
10930
|
+
return { name, description, content: buildSkillMd({ name, description, body: parsed.body, extra }) };
|
|
10931
|
+
}
|
|
10811
10932
|
var pushCommand = new Command13("push").description("Upload a local skill file to workspace (upsert by name). Accepts piped content via stdin.").argument("[first]", "Skill name (if piping content) or file path (name from frontmatter)").argument("[second]", "File path (when first arg is the skill name)").option("--workspace <name-or-id>", "Workspace name or ID").action(async (first, second, opts, command) => {
|
|
10812
10933
|
const useJson = shouldOutputJson(command.optsWithGlobals().json);
|
|
10813
10934
|
const credentials = requireAuth();
|
|
@@ -10849,35 +10970,22 @@ var pushCommand = new Command13("push").description("Upload a local skill file t
|
|
|
10849
10970
|
}
|
|
10850
10971
|
}
|
|
10851
10972
|
try {
|
|
10852
|
-
const
|
|
10853
|
-
|
|
10854
|
-
let description = "";
|
|
10855
|
-
let body = content;
|
|
10856
|
-
if (frontmatterMatch) {
|
|
10857
|
-
const fm = frontmatterMatch[1];
|
|
10858
|
-
body = frontmatterMatch[2];
|
|
10859
|
-
const descMatch = fm.match(/^description:\s*(.+)$/m);
|
|
10860
|
-
if (descMatch)
|
|
10861
|
-
description = descMatch[1].trim();
|
|
10862
|
-
if (!name) {
|
|
10863
|
-
const nameMatch = fm.match(/^name:\s*(.+)$/m);
|
|
10864
|
-
if (nameMatch)
|
|
10865
|
-
name = nameMatch[1].trim();
|
|
10866
|
-
}
|
|
10867
|
-
}
|
|
10868
|
-
if (!name) {
|
|
10973
|
+
const payload = buildSkillPushPayload(content, nameArg);
|
|
10974
|
+
if (!payload) {
|
|
10869
10975
|
console.error(`Skill name required. Provide as first argument or in YAML frontmatter:
|
|
10870
10976
|
runwork skills push "My Skill" ./skill.md
|
|
10871
10977
|
cat skill.md | runwork skills push "My Skill"`);
|
|
10872
10978
|
process.exit(1);
|
|
10979
|
+
return;
|
|
10873
10980
|
}
|
|
10981
|
+
const { name, description, content: document } = payload;
|
|
10874
10982
|
const existing = await client.listExternalSkills(workspaceId);
|
|
10875
10983
|
const match = existing.find((s) => s.name === name);
|
|
10876
10984
|
if (match) {
|
|
10877
10985
|
const updated = await client.updateExternalSkill(workspaceId, match.id, {
|
|
10878
10986
|
name,
|
|
10879
10987
|
description,
|
|
10880
|
-
content:
|
|
10988
|
+
content: document
|
|
10881
10989
|
});
|
|
10882
10990
|
if (useJson) {
|
|
10883
10991
|
jsonOut({ skill: updated, action: "updated" });
|
|
@@ -10888,7 +10996,7 @@ var pushCommand = new Command13("push").description("Upload a local skill file t
|
|
|
10888
10996
|
const created = await client.importExternalSkill(workspaceId, {
|
|
10889
10997
|
name,
|
|
10890
10998
|
description,
|
|
10891
|
-
content:
|
|
10999
|
+
content: document,
|
|
10892
11000
|
importedFrom: "cli"
|
|
10893
11001
|
});
|
|
10894
11002
|
if (useJson) {
|
|
@@ -11095,109 +11203,6 @@ import { chmodSync, existsSync as existsSync30, mkdirSync as mkdirSync16, readFi
|
|
|
11095
11203
|
import { join as join23 } from "path";
|
|
11096
11204
|
import { homedir as homedir6, platform as platform2 } from "os";
|
|
11097
11205
|
|
|
11098
|
-
// ../../shared/skill/skill-canonical.ts
|
|
11099
|
-
function toSkillSlug(value) {
|
|
11100
|
-
return value.trim().replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
11101
|
-
}
|
|
11102
|
-
function quoteYamlValue(value) {
|
|
11103
|
-
if (value === "")
|
|
11104
|
-
return '""';
|
|
11105
|
-
if (/[:#[\]{}|>&*!,?'"]/.test(value) || value !== value.trim()) {
|
|
11106
|
-
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
11107
|
-
return `"${escaped}"`;
|
|
11108
|
-
}
|
|
11109
|
-
return value;
|
|
11110
|
-
}
|
|
11111
|
-
function unquoteYamlValue(raw) {
|
|
11112
|
-
const value = raw.trim();
|
|
11113
|
-
if (value.length >= 2) {
|
|
11114
|
-
const first = value[0];
|
|
11115
|
-
const last = value[value.length - 1];
|
|
11116
|
-
if (first === '"' && last === '"') {
|
|
11117
|
-
return value.slice(1, -1).replace(/\\(["\\])/g, "$1");
|
|
11118
|
-
}
|
|
11119
|
-
if (first === "'" && last === "'") {
|
|
11120
|
-
return value.slice(1, -1);
|
|
11121
|
-
}
|
|
11122
|
-
}
|
|
11123
|
-
return value;
|
|
11124
|
-
}
|
|
11125
|
-
function parseSkillMd(content) {
|
|
11126
|
-
const lines = content.split(`
|
|
11127
|
-
`);
|
|
11128
|
-
if (lines[0]?.trim() !== "---") {
|
|
11129
|
-
const rawName = lines[0]?.replace(/^#\s*/, "").trim() || "Untitled Skill";
|
|
11130
|
-
return {
|
|
11131
|
-
frontmatter: {
|
|
11132
|
-
name: toSkillSlug(rawName) || "untitled-skill",
|
|
11133
|
-
description: ""
|
|
11134
|
-
},
|
|
11135
|
-
orderedKeys: [],
|
|
11136
|
-
body: content,
|
|
11137
|
-
hadFrontmatter: false
|
|
11138
|
-
};
|
|
11139
|
-
}
|
|
11140
|
-
let closingIndex = -1;
|
|
11141
|
-
for (let i = 1;i < lines.length; i++) {
|
|
11142
|
-
if (lines[i]?.trim() === "---") {
|
|
11143
|
-
closingIndex = i;
|
|
11144
|
-
break;
|
|
11145
|
-
}
|
|
11146
|
-
}
|
|
11147
|
-
if (closingIndex === -1) {
|
|
11148
|
-
return {
|
|
11149
|
-
frontmatter: { name: "untitled-skill", description: "" },
|
|
11150
|
-
orderedKeys: [],
|
|
11151
|
-
body: content,
|
|
11152
|
-
hadFrontmatter: false
|
|
11153
|
-
};
|
|
11154
|
-
}
|
|
11155
|
-
const frontmatterEntries = {};
|
|
11156
|
-
const orderedKeys = [];
|
|
11157
|
-
for (let i = 1;i < closingIndex; i++) {
|
|
11158
|
-
const line = lines[i];
|
|
11159
|
-
if (!line)
|
|
11160
|
-
continue;
|
|
11161
|
-
const colonIndex = line.indexOf(":");
|
|
11162
|
-
if (colonIndex > 0) {
|
|
11163
|
-
const key = line.slice(0, colonIndex).trim();
|
|
11164
|
-
const value = unquoteYamlValue(line.slice(colonIndex + 1));
|
|
11165
|
-
if (!(key in frontmatterEntries))
|
|
11166
|
-
orderedKeys.push(key);
|
|
11167
|
-
frontmatterEntries[key] = value;
|
|
11168
|
-
}
|
|
11169
|
-
}
|
|
11170
|
-
const body = lines.slice(closingIndex + 1).join(`
|
|
11171
|
-
`).trim();
|
|
11172
|
-
return {
|
|
11173
|
-
frontmatter: {
|
|
11174
|
-
...frontmatterEntries,
|
|
11175
|
-
name: toSkillSlug(frontmatterEntries.name || "") || "untitled-skill",
|
|
11176
|
-
description: frontmatterEntries.description || ""
|
|
11177
|
-
},
|
|
11178
|
-
orderedKeys,
|
|
11179
|
-
body,
|
|
11180
|
-
hadFrontmatter: true
|
|
11181
|
-
};
|
|
11182
|
-
}
|
|
11183
|
-
function buildSkillMd(parts) {
|
|
11184
|
-
const lines = [
|
|
11185
|
-
"---",
|
|
11186
|
-
`name: ${toSkillSlug(parts.name) || "untitled-skill"}`,
|
|
11187
|
-
`description: ${quoteYamlValue(parts.description ?? "")}`
|
|
11188
|
-
];
|
|
11189
|
-
if (parts.extra) {
|
|
11190
|
-
for (const [key, value] of Object.entries(parts.extra)) {
|
|
11191
|
-
if (key === "name" || key === "description")
|
|
11192
|
-
continue;
|
|
11193
|
-
lines.push(`${key}: ${quoteYamlValue(value)}`);
|
|
11194
|
-
}
|
|
11195
|
-
}
|
|
11196
|
-
lines.push("---", "", (parts.body ?? "").trim());
|
|
11197
|
-
return lines.join(`
|
|
11198
|
-
`);
|
|
11199
|
-
}
|
|
11200
|
-
|
|
11201
11206
|
// src/agents/types.ts
|
|
11202
11207
|
var RUNWORK_MCP_PREFIX = "Runwork: ";
|
|
11203
11208
|
var RUNWORK_MCP_PREFIX_LEGACY = "runwork-";
|
|
@@ -18051,7 +18056,52 @@ function summarizeTelemetryForDryRun(result) {
|
|
|
18051
18056
|
return `Telemetry preview: ${plural(result.events.length, "event")} would be sent (${plural(activeCount, "adapter")} with activity). Use --verbose for details.`;
|
|
18052
18057
|
}
|
|
18053
18058
|
|
|
18054
|
-
//
|
|
18059
|
+
// ../../shared/agent-instructions/runwork-instructions.ts
|
|
18060
|
+
function formatList(items, max = 8) {
|
|
18061
|
+
if (items.length <= max)
|
|
18062
|
+
return items.join(", ");
|
|
18063
|
+
return `${items.slice(0, max).join(", ")} + ${items.length - max} more`;
|
|
18064
|
+
}
|
|
18065
|
+
function plural(n, one, many) {
|
|
18066
|
+
return n > 1 ? many : one;
|
|
18067
|
+
}
|
|
18068
|
+
function pushGroup(parts, group, one, many) {
|
|
18069
|
+
if (group.count <= 0)
|
|
18070
|
+
return;
|
|
18071
|
+
const label = `${group.count} ${plural(group.count, one, many)}`;
|
|
18072
|
+
parts.push(group.names.length > 0 ? `${label} (${formatList(group.names, 3)})` : label);
|
|
18073
|
+
}
|
|
18074
|
+
function buildInventoryLine(inv) {
|
|
18075
|
+
const parts = [];
|
|
18076
|
+
if (inv.appCount > 0)
|
|
18077
|
+
parts.push(`${inv.appCount} ${plural(inv.appCount, "app", "apps")}`);
|
|
18078
|
+
if (inv.entityNames.length > 0) {
|
|
18079
|
+
const n = inv.entityNames.length;
|
|
18080
|
+
parts.push(`${n} ${plural(n, "entity", "entities")} (${formatList(inv.entityNames, 5)})`);
|
|
18081
|
+
}
|
|
18082
|
+
if (inv.fileStorageCount > 0) {
|
|
18083
|
+
parts.push(`${inv.fileStorageCount} ${plural(inv.fileStorageCount, "file storage", "file storages")}`);
|
|
18084
|
+
}
|
|
18085
|
+
pushGroup(parts, inv.schedules, "schedule", "schedules");
|
|
18086
|
+
pushGroup(parts, inv.workflows, "workflow", "workflows");
|
|
18087
|
+
pushGroup(parts, inv.agents, "agent", "agents");
|
|
18088
|
+
if (inv.endpointCount > 0) {
|
|
18089
|
+
parts.push(`${inv.endpointCount} ${plural(inv.endpointCount, "endpoint", "endpoints")}`);
|
|
18090
|
+
}
|
|
18091
|
+
if (inv.componentCount > 0) {
|
|
18092
|
+
parts.push(`${inv.componentCount} ${plural(inv.componentCount, "component", "components")}`);
|
|
18093
|
+
}
|
|
18094
|
+
if (inv.integrationIds.length > 0) {
|
|
18095
|
+
const n = inv.integrationIds.length;
|
|
18096
|
+
parts.push(`${n} ${plural(n, "integration", "integrations")} (${formatList(inv.integrationIds, 5)})`);
|
|
18097
|
+
}
|
|
18098
|
+
if (inv.skillCount > 0)
|
|
18099
|
+
parts.push(`${inv.skillCount} ${plural(inv.skillCount, "skill", "skills")}`);
|
|
18100
|
+
if (inv.mcpServerCount > 0) {
|
|
18101
|
+
parts.push(`${inv.mcpServerCount} MCP ${plural(inv.mcpServerCount, "server", "servers")}`);
|
|
18102
|
+
}
|
|
18103
|
+
return parts.length > 0 ? parts.join(", ") : "no resources deployed yet";
|
|
18104
|
+
}
|
|
18055
18105
|
function buildPersonaBlock(persona) {
|
|
18056
18106
|
if (!persona || persona.level === 3)
|
|
18057
18107
|
return [];
|
|
@@ -18080,57 +18130,65 @@ function buildPersonaBlock(persona) {
|
|
|
18080
18130
|
"- Keep explanations concise and outcome-focused."
|
|
18081
18131
|
];
|
|
18082
18132
|
}
|
|
18083
|
-
|
|
18084
|
-
|
|
18085
|
-
|
|
18086
|
-
|
|
18133
|
+
var ROUTING_TABLE = [
|
|
18134
|
+
"| User asks for | Use Runwork | Don't |",
|
|
18135
|
+
"|---|---|---|",
|
|
18136
|
+
"| save / create / share / update a skill | `save_skill` MCP tool | local SKILL.md files, prompt snippets |",
|
|
18137
|
+
"| store, persist, or remember structured data | `entity_{Name}` MCP tools | local SQLite, JSON files, in-memory |",
|
|
18138
|
+
"| schedule a recurring or cron job | Runwork schedules (`schedule_{name}`, `runwork schedules`) | OS cron, `setInterval`, Celery |",
|
|
18139
|
+
"| multi-step process, pipeline, or durable workflow | Runwork workflows (`workflow_{name}`) | ad-hoc scripts, shell pipelines |",
|
|
18140
|
+
"| call Slack / HubSpot / Gmail / Google / any third-party API | `{id}_api` MCP tools (e.g. `hubspot_api`) | raw `fetch`, hand-built auth, SDK installs |",
|
|
18141
|
+
"| upload, save, or serve a file | Runwork file storage (`list_bucket_files`, `generate_user_presigned_url`) | local filesystem, `fs.writeFile` |",
|
|
18142
|
+
"| expose a public API / webhook endpoint | Runwork endpoints (`endpoint_*`) | standalone Express/Flask servers |",
|
|
18143
|
+
"| share AI prompts, instructions, or expertise with the team | `save_skill` MCP tool | local prompt files, copy-paste |",
|
|
18144
|
+
"| connect an AI tool or MCP server | Runwork MCP servers | manual `settings.json` edits per machine |",
|
|
18145
|
+
"| build a new internal tool, team app, or automation | `runwork init` -> `runwork dev` -> `runwork deploy` | one-off scripts, notebooks |"
|
|
18146
|
+
];
|
|
18147
|
+
var PRODUCT_DEV_EXCEPTION = "**Exception -- product development:** Editing your current codebase, running tests, git operations, and building features in the product you're working on are NOT Runwork territory. Runwork is for infrastructure and team tooling, not the product itself.";
|
|
18148
|
+
function buildRunworkInstructions(ctx) {
|
|
18149
|
+
const { workspaceName, workspaceId, workspaceSlug, inventory, surface = "machine" } = ctx;
|
|
18150
|
+
const dashboardUrl = workspaceSlug ? `https://${workspaceSlug}.runwork.ai` : "https://runwork.ai";
|
|
18151
|
+
const workspaceIdLine = `Workspace ID: \`${workspaceId}\`${workspaceSlug ? ` | Dashboard: ${dashboardUrl}` : ""}`;
|
|
18152
|
+
const connection = surface === "mcp" ? `You are connected to the **${workspaceName}** Runwork workspace` : `This machine is connected to the **${workspaceName}** Runwork workspace`;
|
|
18153
|
+
const integrationToolHint = inventory.integrationIds.length > 0 ? ` Connected integrations are callable via MCP tools (${inventory.integrationIds.map((id) => `\`${id}_api\``).join(", ")}) or CLI (\`runwork integrations call <id> <method> <path>\`).` : "";
|
|
18154
|
+
const lines = [
|
|
18155
|
+
"## Runwork",
|
|
18156
|
+
"",
|
|
18157
|
+
`${connection} -- your team's shared platform for building and running infrastructure: apps, data & file storage, scheduled jobs, workflows, integrations, AI agents, skills, MCP servers, public APIs, and reusable components.`,
|
|
18158
|
+
"",
|
|
18159
|
+
workspaceIdLine,
|
|
18160
|
+
""
|
|
18161
|
+
];
|
|
18162
|
+
lines.push("**Default to Runwork -- team sync beats local files.** When the user asks for any of the following, use the Runwork tool shown. These operations sync across every teammate's AI agents; local alternatives only work for the current user.", "", ...ROUTING_TABLE, "", PRODUCT_DEV_EXCEPTION, "", `**Available now:** ${buildInventoryLine(inventory)}`, "", `Runwork MCP tools are always connected -- use them for data access, integration API calls, skill management, and resource discovery.${integrationToolHint} Use \`runwork\` CLI to create (\`runwork init\`), develop (\`runwork dev\`), and deploy (\`runwork deploy\`) apps. Install CLI: \`curl -fsSL https://runwork.ai/install.sh | sh\`. Web dashboard: ${dashboardUrl}. Invoke the \`runwork\` skill for full capability reference.`, ...buildPersonaBlock(ctx.persona));
|
|
18163
|
+
return lines.join(`
|
|
18164
|
+
`);
|
|
18087
18165
|
}
|
|
18088
|
-
|
|
18089
|
-
|
|
18090
|
-
|
|
18091
|
-
|
|
18092
|
-
|
|
18093
|
-
|
|
18094
|
-
|
|
18095
|
-
|
|
18096
|
-
|
|
18097
|
-
|
|
18098
|
-
|
|
18099
|
-
|
|
18100
|
-
|
|
18101
|
-
|
|
18102
|
-
|
|
18103
|
-
|
|
18104
|
-
|
|
18105
|
-
|
|
18106
|
-
|
|
18107
|
-
|
|
18108
|
-
|
|
18109
|
-
|
|
18110
|
-
|
|
18111
|
-
|
|
18112
|
-
|
|
18113
|
-
|
|
18114
|
-
if (reg.agents.length > 0) {
|
|
18115
|
-
const names = reg.agents.map((a) => a.name).filter(Boolean);
|
|
18116
|
-
if (names.length > 0)
|
|
18117
|
-
parts.push(`${reg.agents.length} agent${reg.agents.length > 1 ? "s" : ""} (${formatList(names, 3)})`);
|
|
18118
|
-
else
|
|
18119
|
-
parts.push(`${reg.agents.length} agent${reg.agents.length > 1 ? "s" : ""}`);
|
|
18120
|
-
}
|
|
18121
|
-
if (reg.endpoints.length > 0)
|
|
18122
|
-
parts.push(`${reg.endpoints.length} endpoint${reg.endpoints.length > 1 ? "s" : ""}`);
|
|
18123
|
-
if (reg.components.length > 0)
|
|
18124
|
-
parts.push(`${reg.components.length} component${reg.components.length > 1 ? "s" : ""}`);
|
|
18125
|
-
}
|
|
18126
|
-
if (ctx.connectedIntegrations.length > 0) {
|
|
18127
|
-
parts.push(`${ctx.connectedIntegrations.length} integration${ctx.connectedIntegrations.length > 1 ? "s" : ""} (${formatList(ctx.connectedIntegrations, 5)})`);
|
|
18128
|
-
}
|
|
18129
|
-
if (ctx.skillCount > 0)
|
|
18130
|
-
parts.push(`${ctx.skillCount} skill${ctx.skillCount > 1 ? "s" : ""}`);
|
|
18131
|
-
if (ctx.mcpServerCount > 0)
|
|
18132
|
-
parts.push(`${ctx.mcpServerCount} MCP server${ctx.mcpServerCount > 1 ? "s" : ""}`);
|
|
18133
|
-
return parts.length > 0 ? parts.join(", ") : "no resources deployed yet";
|
|
18166
|
+
|
|
18167
|
+
// src/agents/intro-skill.ts
|
|
18168
|
+
function toRunworkInventory(ctx) {
|
|
18169
|
+
const reg = ctx.registries;
|
|
18170
|
+
return {
|
|
18171
|
+
appCount: ctx.appCount,
|
|
18172
|
+
entityNames: reg ? [...new Set(reg.entities.map((e) => e.entityName))] : [],
|
|
18173
|
+
fileStorageCount: reg?.fileStorages.length ?? 0,
|
|
18174
|
+
schedules: {
|
|
18175
|
+
count: reg?.schedules.length ?? 0,
|
|
18176
|
+
names: reg?.schedules.map((s) => s.name).filter(Boolean) ?? []
|
|
18177
|
+
},
|
|
18178
|
+
workflows: {
|
|
18179
|
+
count: reg?.workflows.length ?? 0,
|
|
18180
|
+
names: reg?.workflows.map((w) => w.name).filter(Boolean) ?? []
|
|
18181
|
+
},
|
|
18182
|
+
agents: {
|
|
18183
|
+
count: reg?.agents.length ?? 0,
|
|
18184
|
+
names: reg?.agents.map((a) => a.name).filter(Boolean) ?? []
|
|
18185
|
+
},
|
|
18186
|
+
endpointCount: reg?.endpoints.length ?? 0,
|
|
18187
|
+
componentCount: reg?.components.length ?? 0,
|
|
18188
|
+
integrationIds: ctx.connectedIntegrations,
|
|
18189
|
+
skillCount: ctx.skillCount,
|
|
18190
|
+
mcpServerCount: ctx.mcpServerCount
|
|
18191
|
+
};
|
|
18134
18192
|
}
|
|
18135
18193
|
function buildAppSkillDescription(appName, registries) {
|
|
18136
18194
|
if (!registries)
|
|
@@ -18423,39 +18481,17 @@ function generateIntroSkill(ctx) {
|
|
|
18423
18481
|
};
|
|
18424
18482
|
}
|
|
18425
18483
|
function generateInstructionHint(ctx) {
|
|
18426
|
-
const
|
|
18427
|
-
|
|
18428
|
-
|
|
18429
|
-
|
|
18484
|
+
const body = buildRunworkInstructions({
|
|
18485
|
+
workspaceName: ctx.workspaceName,
|
|
18486
|
+
workspaceId: ctx.workspaceId,
|
|
18487
|
+
workspaceSlug: ctx.workspaceSlug,
|
|
18488
|
+
inventory: toRunworkInventory(ctx),
|
|
18489
|
+
persona: ctx.persona,
|
|
18490
|
+
surface: "machine"
|
|
18491
|
+
});
|
|
18430
18492
|
return [
|
|
18431
18493
|
"<!-- runwork:start -->",
|
|
18432
|
-
|
|
18433
|
-
"",
|
|
18434
|
-
`This machine is connected to the **${ctx.workspaceName}** Runwork workspace -- your team's shared platform for building and running infrastructure: apps, data & file storage, scheduled jobs, workflows, integrations, AI agents, skills, MCP servers, public APIs, and reusable components.`,
|
|
18435
|
-
"",
|
|
18436
|
-
workspaceIdLine,
|
|
18437
|
-
"",
|
|
18438
|
-
"**Default to Runwork -- team sync beats local files.** When the user asks for any of the following, use the Runwork tool shown. These operations sync across every teammate's AI agents; local alternatives only work for the current user.",
|
|
18439
|
-
"",
|
|
18440
|
-
"| User asks for | Use Runwork | Don't |",
|
|
18441
|
-
"|---|---|---|",
|
|
18442
|
-
"| save / create / share / update a skill | `save_skill` MCP tool | local SKILL.md files, prompt snippets |",
|
|
18443
|
-
"| store, persist, or remember structured data | `entity_{Name}` MCP tools | local SQLite, JSON files, in-memory |",
|
|
18444
|
-
"| schedule a recurring or cron job | Runwork schedules (`schedule_{name}`, `runwork schedules`) | OS cron, `setInterval`, Celery |",
|
|
18445
|
-
"| multi-step process, pipeline, or durable workflow | Runwork workflows (`workflow_{name}`) | ad-hoc scripts, shell pipelines |",
|
|
18446
|
-
"| call Slack / HubSpot / Gmail / Google / any third-party API | `{id}_api` MCP tools (e.g. `hubspot_api`) | raw `fetch`, hand-built auth, SDK installs |",
|
|
18447
|
-
"| upload, save, or serve a file | Runwork file storage (`list_bucket_files`, `generate_user_presigned_url`) | local filesystem, `fs.writeFile` |",
|
|
18448
|
-
"| expose a public API / webhook endpoint | Runwork endpoints (`endpoint_*`) | standalone Express/Flask servers |",
|
|
18449
|
-
"| share AI prompts, instructions, or expertise with the team | `save_skill` MCP tool | local prompt files, copy-paste |",
|
|
18450
|
-
"| connect an AI tool or MCP server | Runwork MCP servers | manual `settings.json` edits per machine |",
|
|
18451
|
-
"| build a new internal tool, team app, or automation | `runwork init` -> `runwork dev` -> `runwork deploy` | one-off scripts, notebooks |",
|
|
18452
|
-
"",
|
|
18453
|
-
"**Exception -- product development:** Editing your current codebase, running tests, git operations, and building features in the product you're working on are NOT Runwork territory. Runwork is for infrastructure and team tooling, not the product itself.",
|
|
18454
|
-
"",
|
|
18455
|
-
`**Available now:** ${inventory}`,
|
|
18456
|
-
"",
|
|
18457
|
-
`Runwork MCP tools are always connected -- use them for data access, integration API calls, skill management, and resource discovery.${integrationToolHint} Use \`runwork\` CLI to create (\`runwork init\`), develop (\`runwork dev\`), and deploy (\`runwork deploy\`) apps. Install CLI: \`curl -fsSL https://runwork.ai/install.sh | sh\`. Web dashboard: ${dashboardUrl}. Invoke the \`runwork\` skill for full capability reference.`,
|
|
18458
|
-
...buildPersonaBlock(ctx.persona),
|
|
18494
|
+
body,
|
|
18459
18495
|
"<!-- runwork:end -->"
|
|
18460
18496
|
].join(`
|
|
18461
18497
|
`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runwork",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "CLI for Runwork: develop, preview, and deploy Runwork apps from your local machine.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "Runwork, Inc. <info@runwork.ai> (https://www.runwork.ai)",
|