runwork 0.12.0 → 0.13.1
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/agents/claude-code.d.ts +21 -0
- package/dist/agents/claude-code.js +58 -2
- package/dist/agents/claude-desktop-plugin-tree.d.ts +15 -0
- package/dist/agents/claude-desktop-plugin-tree.js +37 -1
- package/dist/agents/conversation-skills.d.ts +18 -0
- package/dist/agents/conversation-skills.js +229 -0
- package/dist/agents/registry-data.d.ts +35 -0
- package/dist/agents/registry-data.js +58 -1
- package/dist/agents/runtime-detection.d.ts +82 -0
- package/dist/agents/runtime-detection.js +271 -0
- package/dist/agents/session-start-hook.d.ts +37 -0
- package/dist/agents/session-start-hook.js +159 -0
- package/dist/agents/types.d.ts +12 -0
- package/dist/api/client.d.ts +47 -0
- package/dist/api/client.js +32 -0
- package/dist/commands/doctor.js +89 -2
- package/dist/commands/inbox.d.ts +11 -0
- package/dist/commands/inbox.js +60 -0
- package/dist/commands/resume.d.ts +2 -0
- package/dist/commands/resume.js +265 -0
- package/dist/commands/save-convo.d.ts +8 -0
- package/dist/commands/save-convo.js +20 -0
- package/dist/commands/share-convo.d.ts +24 -0
- package/dist/commands/share-convo.js +167 -0
- package/dist/commands/sync.js +95 -10
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/index.js +8 -0
- package/package.json +1 -1
|
@@ -7,7 +7,28 @@ export declare class ClaudeCodeAdapter implements AgentAdapter {
|
|
|
7
7
|
supportsMcpScope(_scope: 'project' | 'user'): boolean;
|
|
8
8
|
supportsSkills(): boolean;
|
|
9
9
|
writeMcpServers(servers: McpServerEntry[], scope: 'project' | 'user'): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Install the Runwork SessionStart hook into the plugin tree (user scope).
|
|
12
|
+
* The hook writes ~/.runwork/sessions/<sessionId>.json with the active
|
|
13
|
+
* conversation's transcript path + metadata, used by `runwork share-convo`
|
|
14
|
+
* for high-fidelity capture and by `runwork resume` for placement.
|
|
15
|
+
*
|
|
16
|
+
* Called as part of user-scope writeSkills() to keep sync flow consistent.
|
|
17
|
+
* Emits a one-line confirmation matching the format other adapter steps
|
|
18
|
+
* use (` [Claude Code] Installed ... (user)`) so `runwork sync` output
|
|
19
|
+
* shows whether the hook actually landed.
|
|
20
|
+
*/
|
|
21
|
+
private installSessionStartHook;
|
|
10
22
|
writeSkills(skills: SkillFile[], scope: 'project' | 'user'): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Install the runwork SessionStart hook for Claude Code (user scope only).
|
|
25
|
+
* Called once per sync by the main sync loop AFTER writeSkills has
|
|
26
|
+
* populated the plugin directories. Kept separate from writeSkills because
|
|
27
|
+
* writeSkills is also invoked per-skill by the diff executor during
|
|
28
|
+
* push/pull, and the hook install + log line must not repeat for every
|
|
29
|
+
* skill change.
|
|
30
|
+
*/
|
|
31
|
+
writeBuiltInHooks(scope: 'project' | 'user'): Promise<void>;
|
|
11
32
|
writeInstructionHint(hint: string, scope: 'project' | 'user'): Promise<void>;
|
|
12
33
|
writeTeamInstructions(instructions: string, scope: 'project' | 'user'): Promise<void>;
|
|
13
34
|
writeAgentConfig(config: AgentConfigOverride, scope: 'project' | 'user'): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'fs';
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { homedir, platform } from 'os';
|
|
4
4
|
import { execFileSync } from '../utils/subprocess.js';
|
|
@@ -6,6 +6,7 @@ import { whichBinary } from '../utils/which.js';
|
|
|
6
6
|
import { buildSkillMd } from './types.js';
|
|
7
7
|
import { mergeJsonMcpServers, readJsonConfig, writeJsonConfig, removeRunworkMcpServers } from './utils/json-config.js';
|
|
8
8
|
import { writeHintToFile, writeTeamInstructionsToFile, removeHintFromFile, removeTeamInstructionsFromFile } from './utils/instruction-hint.js';
|
|
9
|
+
import { SESSION_START_HOOK_SCRIPT } from './session-start-hook.js';
|
|
9
10
|
const PLUGIN_NAME = 'runwork';
|
|
10
11
|
const PLUGIN_VERSION = '1.0.0';
|
|
11
12
|
function getPluginJson() {
|
|
@@ -96,6 +97,37 @@ export class ClaudeCodeAdapter {
|
|
|
96
97
|
writeJsonConfig(marketplaceMcpPath, pluginEntries);
|
|
97
98
|
}
|
|
98
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Install the Runwork SessionStart hook into the plugin tree (user scope).
|
|
102
|
+
* The hook writes ~/.runwork/sessions/<sessionId>.json with the active
|
|
103
|
+
* conversation's transcript path + metadata, used by `runwork share-convo`
|
|
104
|
+
* for high-fidelity capture and by `runwork resume` for placement.
|
|
105
|
+
*
|
|
106
|
+
* Called as part of user-scope writeSkills() to keep sync flow consistent.
|
|
107
|
+
* Emits a one-line confirmation matching the format other adapter steps
|
|
108
|
+
* use (` [Claude Code] Installed ... (user)`) so `runwork sync` output
|
|
109
|
+
* shows whether the hook actually landed.
|
|
110
|
+
*/
|
|
111
|
+
installSessionStartHook(pluginDir, label) {
|
|
112
|
+
const hooksDir = join(pluginDir, 'hooks');
|
|
113
|
+
mkdirSync(hooksDir, { recursive: true });
|
|
114
|
+
const scriptPath = join(hooksDir, 'on-session-start.sh');
|
|
115
|
+
writeFileSync(scriptPath, SESSION_START_HOOK_SCRIPT);
|
|
116
|
+
try {
|
|
117
|
+
// Best-effort executable bit; chmod is a no-op on Windows.
|
|
118
|
+
chmodSync(scriptPath, 0o755);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
/* not fatal */
|
|
122
|
+
}
|
|
123
|
+
const hooksManifest = {
|
|
124
|
+
SessionStart: [
|
|
125
|
+
{ command: '${CLAUDE_PLUGIN_ROOT}/hooks/on-session-start.sh' },
|
|
126
|
+
],
|
|
127
|
+
};
|
|
128
|
+
writeFileSync(join(hooksDir, 'hooks.json'), JSON.stringify(hooksManifest, null, 2));
|
|
129
|
+
console.log(` [Claude Code] Installed SessionStart hook (${label}) -> ${scriptPath}`);
|
|
130
|
+
}
|
|
99
131
|
async writeSkills(skills, scope) {
|
|
100
132
|
// Always write to the standard skills directory (guaranteed to work)
|
|
101
133
|
const baseDir = scope === 'project'
|
|
@@ -136,6 +168,26 @@ export class ClaudeCodeAdapter {
|
|
|
136
168
|
this.registerPlugin(pluginDir);
|
|
137
169
|
}
|
|
138
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Install the runwork SessionStart hook for Claude Code (user scope only).
|
|
173
|
+
* Called once per sync by the main sync loop AFTER writeSkills has
|
|
174
|
+
* populated the plugin directories. Kept separate from writeSkills because
|
|
175
|
+
* writeSkills is also invoked per-skill by the diff executor during
|
|
176
|
+
* push/pull, and the hook install + log line must not repeat for every
|
|
177
|
+
* skill change.
|
|
178
|
+
*/
|
|
179
|
+
async writeBuiltInHooks(scope) {
|
|
180
|
+
if (scope !== 'user')
|
|
181
|
+
return;
|
|
182
|
+
const pluginDir = this.getPluginDir();
|
|
183
|
+
const marketDir = this.getMarketplaceDir();
|
|
184
|
+
if (existsSync(pluginDir)) {
|
|
185
|
+
this.installSessionStartHook(pluginDir, 'plugin cache');
|
|
186
|
+
}
|
|
187
|
+
if (existsSync(marketDir)) {
|
|
188
|
+
this.installSessionStartHook(marketDir, 'marketplace');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
139
191
|
async writeInstructionHint(hint, scope) {
|
|
140
192
|
const filePath = scope === 'project'
|
|
141
193
|
? join(process.cwd(), '.claude', 'CLAUDE.md')
|
|
@@ -646,7 +698,9 @@ export class ClaudeCodeAdapter {
|
|
|
646
698
|
installed.version = 2;
|
|
647
699
|
if (!installed.plugins)
|
|
648
700
|
installed.plugins = {};
|
|
649
|
-
|
|
701
|
+
const pluginKey = `${PLUGIN_NAME}@runwork`;
|
|
702
|
+
const wasRegistered = !!installed.plugins[pluginKey];
|
|
703
|
+
installed.plugins[pluginKey] = [{
|
|
650
704
|
scope: 'user',
|
|
651
705
|
installPath: pluginDir,
|
|
652
706
|
version: PLUGIN_VERSION,
|
|
@@ -668,6 +722,8 @@ export class ClaudeCodeAdapter {
|
|
|
668
722
|
lastUpdated: new Date().toISOString(),
|
|
669
723
|
};
|
|
670
724
|
writeJsonConfig(marketplacesPath, marketplaces);
|
|
725
|
+
console.log(` [Claude Code] ${wasRegistered ? 'Refreshed' : 'Registered'} plugin ` +
|
|
726
|
+
`${pluginKey} v${PLUGIN_VERSION} -> ${installedPath}`);
|
|
671
727
|
// Enable the plugin in settings.json
|
|
672
728
|
const settingsPath = join(homedir(), '.claude', 'settings.json');
|
|
673
729
|
const settings = readJsonConfig(settingsPath);
|
|
@@ -32,6 +32,19 @@ export declare function writePluginTeamInstructions(destDir: string, instruction
|
|
|
32
32
|
* Email" instead of kebab-cased "write-cold-email").
|
|
33
33
|
*/
|
|
34
34
|
export declare function writePluginSkills(destDir: string, skills: SkillFile[]): void;
|
|
35
|
+
/**
|
|
36
|
+
* Write the SessionStart hook script + hooks.json into <destDir>.
|
|
37
|
+
*
|
|
38
|
+
* Layout:
|
|
39
|
+
* <destDir>/hooks/on-session-start.sh (executable bash script)
|
|
40
|
+
* <destDir>/hooks/hooks.json (Claude hooks manifest)
|
|
41
|
+
*
|
|
42
|
+
* When the Cowork plugin is installed and the user opens a session, this
|
|
43
|
+
* hook writes ~/.runwork/sessions/<sessionId>.json with the authoritative
|
|
44
|
+
* transcript path + metadata, used by `runwork share-convo` and
|
|
45
|
+
* `runwork resume` for high-fidelity capture / placement.
|
|
46
|
+
*/
|
|
47
|
+
export declare function writePluginSessionStartHook(destDir: string): void;
|
|
35
48
|
/**
|
|
36
49
|
* Convenience wrapper that writes the entire plugin tree in one shot.
|
|
37
50
|
* Used by the build-plugin command to materialise a zip-ready directory.
|
|
@@ -40,5 +53,7 @@ export declare function writePluginSkills(destDir: string, skills: SkillFile[]):
|
|
|
40
53
|
* <destDir>/.claude-plugin/plugin.json
|
|
41
54
|
* <destDir>/.mcp.json
|
|
42
55
|
* <destDir>/skills/<filename>/SKILL.md
|
|
56
|
+
* <destDir>/hooks/on-session-start.sh
|
|
57
|
+
* <destDir>/hooks/hooks.json
|
|
43
58
|
*/
|
|
44
59
|
export declare function writePluginTree(destDir: string, input: PluginTreeInput): void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { mkdirSync, rmSync, existsSync, writeFileSync } from 'fs';
|
|
1
|
+
import { chmodSync, mkdirSync, rmSync, existsSync, writeFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { appendTokenToUrl, buildSkillMd } from './types.js';
|
|
4
|
+
import { SESSION_START_HOOK_SCRIPT } from './session-start-hook.js';
|
|
4
5
|
/**
|
|
5
6
|
* Write <destDir>/.claude-plugin/plugin.json.
|
|
6
7
|
*/
|
|
@@ -70,6 +71,38 @@ export function writePluginSkills(destDir, skills) {
|
|
|
70
71
|
writeFileSync(join(skillDir, 'SKILL.md'), buildSkillMd(skill));
|
|
71
72
|
}
|
|
72
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Write the SessionStart hook script + hooks.json into <destDir>.
|
|
76
|
+
*
|
|
77
|
+
* Layout:
|
|
78
|
+
* <destDir>/hooks/on-session-start.sh (executable bash script)
|
|
79
|
+
* <destDir>/hooks/hooks.json (Claude hooks manifest)
|
|
80
|
+
*
|
|
81
|
+
* When the Cowork plugin is installed and the user opens a session, this
|
|
82
|
+
* hook writes ~/.runwork/sessions/<sessionId>.json with the authoritative
|
|
83
|
+
* transcript path + metadata, used by `runwork share-convo` and
|
|
84
|
+
* `runwork resume` for high-fidelity capture / placement.
|
|
85
|
+
*/
|
|
86
|
+
export function writePluginSessionStartHook(destDir) {
|
|
87
|
+
const hooksDir = join(destDir, 'hooks');
|
|
88
|
+
mkdirSync(hooksDir, { recursive: true });
|
|
89
|
+
const scriptPath = join(hooksDir, 'on-session-start.sh');
|
|
90
|
+
writeFileSync(scriptPath, SESSION_START_HOOK_SCRIPT);
|
|
91
|
+
try {
|
|
92
|
+
chmodSync(scriptPath, 0o755);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// chmod may fail on Windows; the script still ships and works wherever
|
|
96
|
+
// bash can execute it.
|
|
97
|
+
}
|
|
98
|
+
const hooksManifest = {
|
|
99
|
+
SessionStart: [
|
|
100
|
+
{ command: '${CLAUDE_PLUGIN_ROOT}/hooks/on-session-start.sh' },
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
writeFileSync(join(hooksDir, 'hooks.json'), JSON.stringify(hooksManifest, null, 2));
|
|
104
|
+
console.log(` [Claude Desktop (Cowork)] Bundled SessionStart hook -> ${scriptPath}`);
|
|
105
|
+
}
|
|
73
106
|
/**
|
|
74
107
|
* Convenience wrapper that writes the entire plugin tree in one shot.
|
|
75
108
|
* Used by the build-plugin command to materialise a zip-ready directory.
|
|
@@ -78,6 +111,8 @@ export function writePluginSkills(destDir, skills) {
|
|
|
78
111
|
* <destDir>/.claude-plugin/plugin.json
|
|
79
112
|
* <destDir>/.mcp.json
|
|
80
113
|
* <destDir>/skills/<filename>/SKILL.md
|
|
114
|
+
* <destDir>/hooks/on-session-start.sh
|
|
115
|
+
* <destDir>/hooks/hooks.json
|
|
81
116
|
*/
|
|
82
117
|
export function writePluginTree(destDir, input) {
|
|
83
118
|
mkdirSync(destDir, { recursive: true });
|
|
@@ -89,4 +124,5 @@ export function writePluginTree(destDir, input) {
|
|
|
89
124
|
});
|
|
90
125
|
writePluginMcpConfig(destDir, input.mcpServers);
|
|
91
126
|
writePluginSkills(destDir, input.skills);
|
|
127
|
+
writePluginSessionStartHook(destDir);
|
|
92
128
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SkillFile } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Names of every skill the CLI ships as a built-in (written to user machines
|
|
4
|
+
* on each sync from code, never pulled from or pushed to the workspace).
|
|
5
|
+
*
|
|
6
|
+
* Sync uses this list to:
|
|
7
|
+
* 1. Skip these names when computing the local-vs-remote diff so they
|
|
8
|
+
* never appear in pull/push/conflict plans.
|
|
9
|
+
* 2. Warn the user (and offer cleanup) if a workspace skill with the same
|
|
10
|
+
* name exists -- it would be shadowed by the built-in locally and is
|
|
11
|
+
* probably a stale duplicate.
|
|
12
|
+
*
|
|
13
|
+
* Keep this list in sync with: intro-skill.ts (`runwork`) and the two
|
|
14
|
+
* `buildXConversationSkill` functions below.
|
|
15
|
+
*/
|
|
16
|
+
export declare const BUILT_IN_SKILL_NAMES: ReadonlyArray<string>;
|
|
17
|
+
export declare function buildShareConversationSkill(): SkillFile;
|
|
18
|
+
export declare function buildSaveConversationSkill(): SkillFile;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Names of every skill the CLI ships as a built-in (written to user machines
|
|
3
|
+
* on each sync from code, never pulled from or pushed to the workspace).
|
|
4
|
+
*
|
|
5
|
+
* Sync uses this list to:
|
|
6
|
+
* 1. Skip these names when computing the local-vs-remote diff so they
|
|
7
|
+
* never appear in pull/push/conflict plans.
|
|
8
|
+
* 2. Warn the user (and offer cleanup) if a workspace skill with the same
|
|
9
|
+
* name exists -- it would be shadowed by the built-in locally and is
|
|
10
|
+
* probably a stale duplicate.
|
|
11
|
+
*
|
|
12
|
+
* Keep this list in sync with: intro-skill.ts (`runwork`) and the two
|
|
13
|
+
* `buildXConversationSkill` functions below.
|
|
14
|
+
*/
|
|
15
|
+
export const BUILT_IN_SKILL_NAMES = [
|
|
16
|
+
'runwork',
|
|
17
|
+
'share-conversation',
|
|
18
|
+
'save-conversation',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Built-in skills shipped by the runwork CLI on every sync. These are
|
|
22
|
+
* fundamental capabilities every user should have regardless of which
|
|
23
|
+
* workspace they belong to, so they live in the CLI codebase (like
|
|
24
|
+
* the dynamic `runwork` intro skill in ./intro-skill.ts) rather than
|
|
25
|
+
* as workspace-managed skills uploaded via the `save_skill` MCP tool.
|
|
26
|
+
*
|
|
27
|
+
* share-conversation: hand off the current AI conversation to a teammate
|
|
28
|
+
* save-conversation: personal checkpoint of the current AI conversation
|
|
29
|
+
*
|
|
30
|
+
* Both are paired with the CLI commands `runwork share-convo` and
|
|
31
|
+
* `runwork save-convo`. The skill body teaches the host LLM how to compose
|
|
32
|
+
* the verbatim transcript + metadata and invoke the CLI.
|
|
33
|
+
*/
|
|
34
|
+
const SHARE_CONVERSATION_CONTENT = `# Share Conversation Skill
|
|
35
|
+
|
|
36
|
+
This skill hands off the user's CURRENT AI conversation (the live chat you and the user are in) to a teammate so they can resume it natively in their own AI agent.
|
|
37
|
+
|
|
38
|
+
## When to trigger
|
|
39
|
+
|
|
40
|
+
The user said something like:
|
|
41
|
+
- "share this convo with X"
|
|
42
|
+
- "share this conversation with X@example.com"
|
|
43
|
+
- "send this to Mehmet"
|
|
44
|
+
- "hand off this thread to Alice"
|
|
45
|
+
- "forward this conversation to user@motaword.com"
|
|
46
|
+
|
|
47
|
+
X may be a workspace member name, an email, or a list. ASK if ambiguous.
|
|
48
|
+
|
|
49
|
+
## What you must do
|
|
50
|
+
|
|
51
|
+
**Step 1: Confirm the recipients.** Read the user's message, extract the email(s) or name(s). Confirm them back to the user before sending. Multiple recipients are allowed (comma-separated or multiple --to flags).
|
|
52
|
+
|
|
53
|
+
**Step 2: Compose a VERBATIM transcript of this conversation.** Write a complete markdown transcription of everything that has happened in this chat session so far. CRITICAL RULES:
|
|
54
|
+
|
|
55
|
+
- Transcribe EVERY message: user, assistant, tool calls, tool results.
|
|
56
|
+
- DO NOT summarize. DO NOT abbreviate. DO NOT skip anything you think is unimportant.
|
|
57
|
+
- Preserve the actual content of every assistant message, every Bash command, every Read result, every Edit, every Web search, every reasoning block you can recall, in chronological order.
|
|
58
|
+
- Use this structure:
|
|
59
|
+
|
|
60
|
+
\`\`\`markdown
|
|
61
|
+
# Conversation: <one-line title>
|
|
62
|
+
|
|
63
|
+
## Conversation
|
|
64
|
+
|
|
65
|
+
### Turn 1 - User
|
|
66
|
+
<user's exact message>
|
|
67
|
+
|
|
68
|
+
### Turn 2 - Assistant
|
|
69
|
+
<your exact response text>
|
|
70
|
+
|
|
71
|
+
#### Tool call: Read
|
|
72
|
+
**Input**: { "file_path": "..." }
|
|
73
|
+
**Result**: <full result, truncated only if absolutely necessary>
|
|
74
|
+
|
|
75
|
+
### Turn 3 - User
|
|
76
|
+
...
|
|
77
|
+
\`\`\`
|
|
78
|
+
|
|
79
|
+
- If you literally cannot remember every single tool result word-for-word (e.g., very long files), include as much as you have in context. Note explicitly when content is partial: \`[truncated for brevity; original was N lines]\`.
|
|
80
|
+
- The goal is full fidelity. If the context is too long, FAIL LOUDLY by telling the user "the conversation is too long to transcribe completely; please confirm you want me to proceed with a best-effort transcription" rather than silently dropping content.
|
|
81
|
+
|
|
82
|
+
Write the transcript to a temp file: \`/tmp/runwork-share-transcript-<random>.md\`.
|
|
83
|
+
|
|
84
|
+
**Step 3: Generate metadata.** Compose a JSON object with these fields based on this conversation's actual content:
|
|
85
|
+
|
|
86
|
+
\`\`\`json
|
|
87
|
+
{
|
|
88
|
+
"workMode": "coding | research | writing | planning | ops | communication | general",
|
|
89
|
+
"topicKeywords": ["3-5 short tags"],
|
|
90
|
+
"lastUserIntent": "one sentence describing what the user was trying to do at handoff time",
|
|
91
|
+
"primaryArtifacts": ["files / URLs / docs touched during the conversation"],
|
|
92
|
+
"openQuestions": ["unresolved questions or decisions at handoff time"],
|
|
93
|
+
"suggestedNextStep": "one sentence about what the recipient should do next",
|
|
94
|
+
"language": "programming or natural language used"
|
|
95
|
+
}
|
|
96
|
+
\`\`\`
|
|
97
|
+
|
|
98
|
+
Write this to \`/tmp/runwork-share-metadata-<random>.json\`.
|
|
99
|
+
|
|
100
|
+
**Step 4: Invoke the CLI.** Run:
|
|
101
|
+
|
|
102
|
+
\`\`\`bash
|
|
103
|
+
runwork share-convo \\
|
|
104
|
+
--to <email1> \\
|
|
105
|
+
--to <email2> \\
|
|
106
|
+
--transcript-file /tmp/runwork-share-transcript-<random>.md \\
|
|
107
|
+
--metadata-file /tmp/runwork-share-metadata-<random>.json \\
|
|
108
|
+
--title "<short title derived from the conversation>"
|
|
109
|
+
\`\`\`
|
|
110
|
+
|
|
111
|
+
The CLI auto-detects your host agent (Claude Code, Codex, etc.) and locates the native session file via \`$CLAUDE_CODE_SESSION_ID\` / \`$CODEX_THREAD_ID\` env vars. You typically don't need to pass \`--source-agent\` or \`--native-file\`.
|
|
112
|
+
|
|
113
|
+
If the user provided a personal note for the recipient, also pass \`--note "<message>"\`.
|
|
114
|
+
|
|
115
|
+
**Step 5: Tell the user.** Report the share ID and inbox URL returned by the CLI. Mention any non-workspace recipients that were invited.
|
|
116
|
+
|
|
117
|
+
## Fallback: when the CLI is not available
|
|
118
|
+
|
|
119
|
+
If \`runwork\` is not on PATH (rare; the CLI ships with workspace onboarding), use the Runwork MCP \`share_conversation\` tool instead. Pass the transcript content inline (lossier - no native bundle - but always works).
|
|
120
|
+
|
|
121
|
+
## Safety
|
|
122
|
+
|
|
123
|
+
- Never include credentials, API keys, or secrets in the transcript if you can identify them. Replace with \`[REDACTED]\`.
|
|
124
|
+
- If the conversation contained sensitive customer data, confirm with the user before sharing.
|
|
125
|
+
- Workspace permissions: non-member recipients trigger an invite, which requires the sender to have the \`members.invite\` permission (owner or admin). The CLI will error out clearly if not allowed.
|
|
126
|
+
|
|
127
|
+
## What this skill is NOT for
|
|
128
|
+
|
|
129
|
+
- Sharing files, apps, links, or screenshots. Use \`share-app\` or copy-paste.
|
|
130
|
+
- Continuing a paused conversation yourself. Use \`runwork resume <id>\` after the recipient runs it.
|
|
131
|
+
- Sharing a different conversation that's not the one you're currently in. The transcript is built from your live context.
|
|
132
|
+
`;
|
|
133
|
+
const SAVE_CONVERSATION_CONTENT = `# Save Conversation Skill
|
|
134
|
+
|
|
135
|
+
This skill creates a personal checkpoint of the user's CURRENT AI conversation. The user is the sole recipient; no notifications are sent to anyone else. They can resume it later in any of their AI agents on any of their machines.
|
|
136
|
+
|
|
137
|
+
## When to trigger
|
|
138
|
+
|
|
139
|
+
The user said something like:
|
|
140
|
+
- "save this convo"
|
|
141
|
+
- "checkpoint this"
|
|
142
|
+
- "bookmark this conversation"
|
|
143
|
+
- "save this for later"
|
|
144
|
+
- "save this for tomorrow"
|
|
145
|
+
- "stash this so I can pick it up at home"
|
|
146
|
+
|
|
147
|
+
If the user mentions ANOTHER PERSON or an email, use the \`share-conversation\` skill instead, not this one.
|
|
148
|
+
|
|
149
|
+
## What you must do
|
|
150
|
+
|
|
151
|
+
**Step 1: Confirm the intent.** A simple confirmation is enough: "Saving this conversation as a personal checkpoint."
|
|
152
|
+
|
|
153
|
+
**Step 2: Compose a VERBATIM transcript of this conversation.** Same rules as the share-conversation skill - faithful, complete, no summarization. Write to \`/tmp/runwork-save-transcript-<random>.md\`.
|
|
154
|
+
|
|
155
|
+
\`\`\`markdown
|
|
156
|
+
# Conversation: <one-line title>
|
|
157
|
+
|
|
158
|
+
## Conversation
|
|
159
|
+
|
|
160
|
+
### Turn 1 - User
|
|
161
|
+
<exact user message>
|
|
162
|
+
|
|
163
|
+
### Turn 2 - Assistant
|
|
164
|
+
<exact assistant response>
|
|
165
|
+
|
|
166
|
+
#### Tool call: <name>
|
|
167
|
+
**Input**: ...
|
|
168
|
+
**Result**: ...
|
|
169
|
+
\`\`\`
|
|
170
|
+
|
|
171
|
+
**Step 3: Generate metadata.** Same shape as share-conversation:
|
|
172
|
+
|
|
173
|
+
\`\`\`json
|
|
174
|
+
{
|
|
175
|
+
"workMode": "coding | research | writing | planning | ops | communication | general",
|
|
176
|
+
"topicKeywords": ["3-5 tags"],
|
|
177
|
+
"lastUserIntent": "what the user was trying to do at checkpoint time",
|
|
178
|
+
"primaryArtifacts": ["files / URLs touched"],
|
|
179
|
+
"openQuestions": ["unresolved items"],
|
|
180
|
+
"suggestedNextStep": "what to do when picking back up",
|
|
181
|
+
"language": "programming or natural language"
|
|
182
|
+
}
|
|
183
|
+
\`\`\`
|
|
184
|
+
|
|
185
|
+
Write to \`/tmp/runwork-save-metadata-<random>.json\`.
|
|
186
|
+
|
|
187
|
+
**Step 4: Invoke the CLI.**
|
|
188
|
+
|
|
189
|
+
\`\`\`bash
|
|
190
|
+
runwork save-convo \\
|
|
191
|
+
--transcript-file /tmp/runwork-save-transcript-<random>.md \\
|
|
192
|
+
--metadata-file /tmp/runwork-save-metadata-<random>.json \\
|
|
193
|
+
--title "<short title>"
|
|
194
|
+
\`\`\`
|
|
195
|
+
|
|
196
|
+
If the user provided a personal note ("save this with note: pick up after lunch"), pass \`--note "<message>"\`.
|
|
197
|
+
|
|
198
|
+
**Step 5: Tell the user.** Report the share ID. Mention they can resume from any machine with:
|
|
199
|
+
|
|
200
|
+
\`\`\`
|
|
201
|
+
runwork resume <share-id>
|
|
202
|
+
\`\`\`
|
|
203
|
+
|
|
204
|
+
## Fallback: when the CLI is not available
|
|
205
|
+
|
|
206
|
+
Use the Runwork MCP \`save_conversation\` tool with the transcript inline.
|
|
207
|
+
|
|
208
|
+
## What this skill is NOT for
|
|
209
|
+
|
|
210
|
+
- Sharing with another person. Use \`share-conversation\`.
|
|
211
|
+
- Saving files or code. Use the file system or git.
|
|
212
|
+
- Note-taking. Use a notes app or write to disk directly.
|
|
213
|
+
`;
|
|
214
|
+
export function buildShareConversationSkill() {
|
|
215
|
+
return {
|
|
216
|
+
name: 'share-conversation',
|
|
217
|
+
filename: 'share-conversation',
|
|
218
|
+
content: SHARE_CONVERSATION_CONTENT,
|
|
219
|
+
description: 'TRIGGERS when the user asks to share, send, hand off, or forward the CURRENT AI conversation (the live chat you are in right now) to a teammate by name or email. Examples: "share this convo with oytun@motaword.com", "send this conversation to Mehmet", "hand off this thread to Alice". Use ONLY when the user wants the OTHER PERSON to be able to continue this exact conversation in their own AI agent. Do NOT trigger for sharing files, apps, links, or unrelated content. For personal checkpoint (sharing to yourself), use the `save-conversation` skill instead.',
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
export function buildSaveConversationSkill() {
|
|
223
|
+
return {
|
|
224
|
+
name: 'save-conversation',
|
|
225
|
+
filename: 'save-conversation',
|
|
226
|
+
content: SAVE_CONVERSATION_CONTENT,
|
|
227
|
+
description: 'TRIGGERS when the user wants to save, checkpoint, bookmark, or stash the CURRENT AI conversation for themselves (not for a teammate). Examples: "save this convo", "checkpoint this", "bookmark this conversation", "save this for later", "save this so I can pick it up tomorrow", "save this for my other machine". Use ONLY when the user is the sole intended recipient. For sharing with a teammate (different person), use the `share-conversation` skill instead.',
|
|
228
|
+
};
|
|
229
|
+
}
|
|
@@ -52,6 +52,39 @@ export interface AgentManualSetup {
|
|
|
52
52
|
showAfter?: ManualSetupSlot;
|
|
53
53
|
downloadArtifact?: AgentManualSetupArtifact;
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* How a recipient can resume a shared conversation locally in this agent.
|
|
57
|
+
*
|
|
58
|
+
* Consumed by `runwork resume <share-id>` and by the desktop UI's
|
|
59
|
+
* "Resume in <agent>" button. The CLI's resume command branches on `mode`:
|
|
60
|
+
*
|
|
61
|
+
* - 'cli-resume' Place the bundle on disk, then exec `cliResumeCommand`
|
|
62
|
+
* with {uuid} substituted. The agent opens directly
|
|
63
|
+
* into the resumed session (Claude Code, Codex CLI,
|
|
64
|
+
* Gemini CLI, Cline CLI).
|
|
65
|
+
*
|
|
66
|
+
* - 'file-drop-only' Place the bundle on disk in a location the agent's
|
|
67
|
+
* UI will surface (e.g., Codex Desktop's recent threads).
|
|
68
|
+
* The CLI prints `manualOpenHint` so the user knows how
|
|
69
|
+
* to pick up the session inside the agent.
|
|
70
|
+
*
|
|
71
|
+
* - 'unsupported' No native resume. The CLI falls back to the universal
|
|
72
|
+
* paste-prompt path. Used for walled-garden agents like
|
|
73
|
+
* Microsoft Copilot or Claude Desktop Chat mode.
|
|
74
|
+
*
|
|
75
|
+
* Bundle placement logic (which directory, which filename) is per-agent and
|
|
76
|
+
* lives in the resume command implementation, not here. This data field stays
|
|
77
|
+
* declarative.
|
|
78
|
+
*/
|
|
79
|
+
export interface AgentResumeCapability {
|
|
80
|
+
mode: 'cli-resume' | 'file-drop-only' | 'unsupported';
|
|
81
|
+
/** Bundle format this agent reads natively (must match a bundle's `format`). */
|
|
82
|
+
nativeBundleFormat?: 'claude-jsonl' | 'codex-rollout' | string;
|
|
83
|
+
/** Template for the resume command (cli-resume only). `{uuid}` is substituted with the share's session id. */
|
|
84
|
+
cliResumeCommand?: string;
|
|
85
|
+
/** Hint text shown to the user when the bundle was dropped but the agent must be opened manually. */
|
|
86
|
+
manualOpenHint?: string;
|
|
87
|
+
}
|
|
55
88
|
export interface AgentDefinition extends InstallableTool {
|
|
56
89
|
aliases?: string[];
|
|
57
90
|
category: AgentCategory;
|
|
@@ -81,6 +114,8 @@ export interface AgentDefinition extends InstallableTool {
|
|
|
81
114
|
quickStart?: AgentQuickStart;
|
|
82
115
|
/** Manual setup steps required after installation (desktop onboarding) */
|
|
83
116
|
manualSetup?: AgentManualSetup;
|
|
117
|
+
/** How `runwork resume` should hand this agent a shared conversation. Undefined = paste-prompt fallback only. */
|
|
118
|
+
resumeCapability?: AgentResumeCapability;
|
|
84
119
|
}
|
|
85
120
|
export declare function getAgent(slug: string): AgentDefinition | undefined;
|
|
86
121
|
export declare function getAgentByName(name: string): AgentDefinition | undefined;
|
|
@@ -31,6 +31,11 @@ const AGENT_REGISTRY = [
|
|
|
31
31
|
instructionFile: { global: '.claude/CLAUDE.md', project: '.claude/CLAUDE.md' },
|
|
32
32
|
mcpConfigKey: 'mcpServers',
|
|
33
33
|
firstClass: true,
|
|
34
|
+
resumeCapability: {
|
|
35
|
+
mode: 'cli-resume',
|
|
36
|
+
nativeBundleFormat: 'claude-jsonl',
|
|
37
|
+
cliResumeCommand: 'claude --resume {uuid}',
|
|
38
|
+
},
|
|
34
39
|
quickStart: {
|
|
35
40
|
launchHint: 'Open your terminal and type `claude`',
|
|
36
41
|
skillCommand: '/skills',
|
|
@@ -64,6 +69,11 @@ const AGENT_REGISTRY = [
|
|
|
64
69
|
},
|
|
65
70
|
mcpConfigKey: 'mcpServers',
|
|
66
71
|
firstClass: true,
|
|
72
|
+
resumeCapability: {
|
|
73
|
+
mode: 'file-drop-only',
|
|
74
|
+
nativeBundleFormat: 'claude-jsonl',
|
|
75
|
+
manualOpenHint: 'Open Claude Desktop, switch to the Cowork tab, and the conversation will appear in your session history.',
|
|
76
|
+
},
|
|
67
77
|
quickStart: {
|
|
68
78
|
launchHint: 'Open Claude Desktop',
|
|
69
79
|
examplePrompts: [
|
|
@@ -197,6 +207,11 @@ const AGENT_REGISTRY = [
|
|
|
197
207
|
skillsPaths: { global: '.codex/skills', project: '.agents/skills' },
|
|
198
208
|
instructionFile: { global: '.codex/instructions.md', project: 'AGENTS.md' },
|
|
199
209
|
firstClass: true,
|
|
210
|
+
resumeCapability: {
|
|
211
|
+
mode: 'file-drop-only',
|
|
212
|
+
nativeBundleFormat: 'codex-rollout',
|
|
213
|
+
manualOpenHint: 'Open Codex Desktop and the conversation will appear in your recent threads list.',
|
|
214
|
+
},
|
|
200
215
|
quickStart: {
|
|
201
216
|
launchHint: 'Open Codex',
|
|
202
217
|
examplePrompts: [
|
|
@@ -217,6 +232,11 @@ const AGENT_REGISTRY = [
|
|
|
217
232
|
skillsPaths: { global: '.codex/skills', project: '.agents/skills' },
|
|
218
233
|
instructionFile: { global: '.codex/instructions.md', project: 'AGENTS.md' },
|
|
219
234
|
firstClass: true,
|
|
235
|
+
resumeCapability: {
|
|
236
|
+
mode: 'cli-resume',
|
|
237
|
+
nativeBundleFormat: 'codex-rollout',
|
|
238
|
+
cliResumeCommand: 'codex resume {uuid}',
|
|
239
|
+
},
|
|
220
240
|
quickStart: {
|
|
221
241
|
launchHint: 'Open your terminal and type `codex`',
|
|
222
242
|
skillCommand: 'Check AGENTS.md for workspace skills',
|
|
@@ -280,6 +300,44 @@ const AGENT_REGISTRY = [
|
|
|
280
300
|
],
|
|
281
301
|
},
|
|
282
302
|
},
|
|
303
|
+
{
|
|
304
|
+
// Google Antigravity - agent-first IDE built as a VS Code fork (Electron).
|
|
305
|
+
// Ships with Gemini 3 Pro; also supports Claude Sonnet/Opus. Skill and MCP
|
|
306
|
+
// config paths are under ~/.gemini/antigravity/ (shared Google AI namespace
|
|
307
|
+
// with Gemini CLI, but a distinct product line). The `antigravity` binary
|
|
308
|
+
// on PATH is the Electron launcher; on macOS we also detect via the
|
|
309
|
+
// installed app bundle so the registry recognizes installs that did not
|
|
310
|
+
// expose a CLI launcher.
|
|
311
|
+
slug: 'antigravity',
|
|
312
|
+
name: 'Antigravity',
|
|
313
|
+
aliases: ['Antigravity (Google)'],
|
|
314
|
+
description: "Google's agent-first IDE for building with AI -- Editor view for code, Manager view to orchestrate multiple agents in parallel",
|
|
315
|
+
category: 'ide',
|
|
316
|
+
detection: {
|
|
317
|
+
method: 'any',
|
|
318
|
+
target: [
|
|
319
|
+
{ method: 'binary', target: 'antigravity' },
|
|
320
|
+
{ method: 'path', target: { macos: '/Applications/Antigravity.app' } },
|
|
321
|
+
],
|
|
322
|
+
},
|
|
323
|
+
launch: {
|
|
324
|
+
app: { macos: 'Antigravity', windows: 'Antigravity' },
|
|
325
|
+
cli: 'antigravity',
|
|
326
|
+
},
|
|
327
|
+
logo: 'antigravity',
|
|
328
|
+
downloadUrl: 'https://antigravity.google',
|
|
329
|
+
skillsPaths: { global: '.gemini/antigravity/skills', project: '.agent/skills' },
|
|
330
|
+
mcpConfigPath: '.gemini/antigravity/mcp_config.json',
|
|
331
|
+
mcpConfigKey: 'mcpServers',
|
|
332
|
+
firstClass: true,
|
|
333
|
+
quickStart: {
|
|
334
|
+
launchHint: 'Open Antigravity',
|
|
335
|
+
examplePrompts: [
|
|
336
|
+
'What workspace skills do I have available?',
|
|
337
|
+
'Use Runwork tools to check team data',
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
},
|
|
283
341
|
// === Additional agents with some custom handling ===
|
|
284
342
|
{
|
|
285
343
|
slug: 'copilot-vscode',
|
|
@@ -316,7 +374,6 @@ const AGENT_REGISTRY = [
|
|
|
316
374
|
mcpConfigKey: 'mcpServers',
|
|
317
375
|
},
|
|
318
376
|
// === Community agents (from skillshare targets.yaml) ===
|
|
319
|
-
{ slug: 'antigravity', name: 'Antigravity', aliases: ['Antigravity (Google)'], description: "Google's Antigravity AI agent", category: 'cli', detection: { method: 'binary', target: 'antigravity' }, logo: 'antigravity', launch: { cli: 'antigravity' }, skillsPaths: { global: '.gemini/antigravity/skills', project: '.agent/skills' } },
|
|
320
377
|
{ slug: 'amp', name: 'Amp', description: 'AI coding agent by Sourcegraph', category: 'cli', detection: { method: 'binary', target: 'amp' }, skillsPaths: { global: '.config/agents/skills', project: '.agents/skills' } },
|
|
321
378
|
{ slug: 'adal', name: 'AdaL', description: 'AI coding agent', category: 'cli', detection: { method: 'binary', target: 'adal' }, skillsPaths: { global: '.adal/skills', project: '.adal/skills' } },
|
|
322
379
|
{ slug: 'astrbot', name: 'AstrBot', description: 'AI coding agent', category: 'cli', detection: { method: 'binary', target: 'astrbot' }, skillsPaths: { global: '.astrbot/data/skills', project: 'data/skills' } },
|