shortcutxl 0.2.12 → 0.2.13
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/README.md +26 -26
- package/agent-docs/README.md +397 -397
- package/agent-docs/docs/compaction.md +390 -390
- package/agent-docs/docs/custom-provider.md +580 -580
- package/agent-docs/docs/extensions.md +1971 -1971
- package/agent-docs/docs/packages.md +209 -209
- package/agent-docs/docs/rpc.md +1317 -1317
- package/agent-docs/docs/sdk.md +962 -962
- package/agent-docs/docs/session.md +412 -412
- package/agent-docs/docs/termux.md +127 -127
- package/agent-docs/docs/tui.md +887 -887
- package/agent-docs/examples/README.md +25 -25
- package/agent-docs/examples/extensions/README.md +205 -205
- package/agent-docs/examples/extensions/antigravity-image-gen.ts +447 -447
- package/agent-docs/examples/extensions/auto-commit-on-exit.ts +49 -49
- package/agent-docs/examples/extensions/bash-spawn-hook.ts +30 -30
- package/agent-docs/examples/extensions/bookmark.ts +50 -50
- package/agent-docs/examples/extensions/built-in-tool-renderer.ts +256 -256
- package/agent-docs/examples/extensions/claude-rules.ts +86 -86
- package/agent-docs/examples/extensions/commands.ts +75 -75
- package/agent-docs/examples/extensions/confirm-destructive.ts +59 -59
- package/agent-docs/examples/extensions/custom-compaction.ts +126 -126
- package/agent-docs/examples/extensions/custom-footer.ts +63 -63
- package/agent-docs/examples/extensions/custom-header.ts +73 -73
- package/agent-docs/examples/extensions/custom-provider-anthropic/index.ts +660 -660
- package/agent-docs/examples/extensions/custom-provider-gitlab-duo/index.ts +362 -362
- package/agent-docs/examples/extensions/custom-provider-gitlab-duo/test.ts +88 -88
- package/agent-docs/examples/extensions/custom-provider-qwen-cli/index.ts +349 -349
- package/agent-docs/examples/extensions/dirty-repo-guard.ts +56 -56
- package/agent-docs/examples/extensions/doom-overlay/doom-component.ts +133 -133
- package/agent-docs/examples/extensions/doom-overlay/doom-keys.ts +108 -108
- package/agent-docs/examples/extensions/doom-overlay/index.ts +74 -74
- package/agent-docs/examples/extensions/dynamic-resources/index.ts +15 -15
- package/agent-docs/examples/extensions/dynamic-tools.ts +77 -77
- package/agent-docs/examples/extensions/event-bus.ts +43 -43
- package/agent-docs/examples/extensions/file-trigger.ts +41 -41
- package/agent-docs/examples/extensions/git-checkpoint.ts +53 -53
- package/agent-docs/examples/extensions/handoff.ts +155 -155
- package/agent-docs/examples/extensions/hello.ts +25 -25
- package/agent-docs/examples/extensions/inline-bash.ts +94 -94
- package/agent-docs/examples/extensions/input-transform.ts +43 -43
- package/agent-docs/examples/extensions/interactive-shell.ts +209 -209
- package/agent-docs/examples/extensions/mac-system-theme.ts +47 -47
- package/agent-docs/examples/extensions/message-renderer.ts +59 -59
- package/agent-docs/examples/extensions/minimal-mode.ts +430 -430
- package/agent-docs/examples/extensions/modal-editor.ts +90 -90
- package/agent-docs/examples/extensions/model-status.ts +31 -31
- package/agent-docs/examples/extensions/notify.ts +55 -55
- package/agent-docs/examples/extensions/overlay-qa-tests.ts +936 -936
- package/agent-docs/examples/extensions/overlay-test.ts +159 -159
- package/agent-docs/examples/extensions/permission-gate.ts +37 -37
- package/agent-docs/examples/extensions/pirate.ts +47 -47
- package/agent-docs/examples/extensions/plan-mode/index.ts +363 -363
- package/agent-docs/examples/extensions/preset.ts +418 -418
- package/agent-docs/examples/extensions/protected-paths.ts +30 -30
- package/agent-docs/examples/extensions/qna.ts +122 -122
- package/agent-docs/examples/extensions/question.ts +278 -278
- package/agent-docs/examples/extensions/questionnaire.ts +440 -440
- package/agent-docs/examples/extensions/rainbow-editor.ts +90 -90
- package/agent-docs/examples/extensions/reload-runtime.ts +37 -37
- package/agent-docs/examples/extensions/rpc-demo.ts +124 -124
- package/agent-docs/examples/extensions/sandbox/index.ts +324 -324
- package/agent-docs/examples/extensions/send-user-message.ts +97 -97
- package/agent-docs/examples/extensions/session-name.ts +27 -27
- package/agent-docs/examples/extensions/shutdown-command.ts +69 -69
- package/agent-docs/examples/extensions/snake.ts +343 -343
- package/agent-docs/examples/extensions/space-invaders.ts +566 -566
- package/agent-docs/examples/extensions/ssh.ts +233 -233
- package/agent-docs/examples/extensions/status-line.ts +40 -40
- package/agent-docs/examples/extensions/subagent/agents.ts +130 -130
- package/agent-docs/examples/extensions/subagent/index.ts +1068 -1068
- package/agent-docs/examples/extensions/summarize.ts +206 -206
- package/agent-docs/examples/extensions/system-prompt-header.ts +17 -17
- package/agent-docs/examples/extensions/timed-confirm.ts +72 -72
- package/agent-docs/examples/extensions/titlebar-spinner.ts +58 -58
- package/agent-docs/examples/extensions/todo.ts +314 -314
- package/agent-docs/examples/extensions/tool-override.ts +146 -146
- package/agent-docs/examples/extensions/tools.ts +145 -145
- package/agent-docs/examples/extensions/trigger-compact.ts +40 -40
- package/agent-docs/examples/extensions/truncated-tool.ts +194 -194
- package/agent-docs/examples/extensions/widget-placement.ts +17 -17
- package/agent-docs/examples/extensions/with-deps/index.ts +37 -37
- package/agent-docs/examples/rpc-extension-ui.ts +654 -654
- package/agent-docs/examples/sdk/01-minimal.ts +22 -22
- package/agent-docs/examples/sdk/02-custom-model.ts +48 -48
- package/agent-docs/examples/sdk/03-custom-prompt.ts +55 -55
- package/agent-docs/examples/sdk/04-skills.ts +53 -53
- package/agent-docs/examples/sdk/05-tools.ts +56 -56
- package/agent-docs/examples/sdk/06-extensions.ts +88 -88
- package/agent-docs/examples/sdk/07-context-files.ts +40 -40
- package/agent-docs/examples/sdk/08-prompt-templates.ts +47 -47
- package/agent-docs/examples/sdk/09-api-keys-and-oauth.ts +48 -48
- package/agent-docs/examples/sdk/10-settings.ts +54 -54
- package/agent-docs/examples/sdk/11-sessions.ts +48 -48
- package/agent-docs/examples/sdk/12-full-control.ts +82 -82
- package/agent-docs/examples/sdk/README.md +144 -144
- package/agent-docs/xll-spec.md +110 -110
- package/dist/core/auth-storage.js +21 -2
- package/package.json +1 -1
- package/xll/ShortcutXL.xll +0 -0
- package/xll/modules/debug_render.py +272 -272
- package/xll/modules/gameboy.py +241 -241
- package/xll/modules/pong.py +188 -188
- package/xll/modules/shortcut_xl/_diff_highlight.py +176 -0
- package/xll/modules/shortcut_xl/_log.py +12 -12
- package/xll/modules/shortcut_xl/_registry.py +44 -44
- package/xll/modules/stocks.py +100 -100
- /package/skills/{com-advanced-api → COM-advanced-api}/SKILL.md +0 -0
- /package/skills/{com-advanced-api → COM-advanced-api}/excel-type-library.py +0 -0
- /package/skills/{com-advanced-api → COM-advanced-api}/office-type-library.py +0 -0
|
@@ -1,144 +1,144 @@
|
|
|
1
|
-
# SDK Examples
|
|
2
|
-
|
|
3
|
-
Programmatic usage of shortcutxl via `createAgentSession()`.
|
|
4
|
-
|
|
5
|
-
## Examples
|
|
6
|
-
|
|
7
|
-
| File | Description |
|
|
8
|
-
|------|-------------|
|
|
9
|
-
| `01-minimal.ts` | Simplest usage with all defaults |
|
|
10
|
-
| `02-custom-model.ts` | Select model and thinking level |
|
|
11
|
-
| `03-custom-prompt.ts` | Replace or modify system prompt |
|
|
12
|
-
| `04-skills.ts` | Discover, filter, or replace skills |
|
|
13
|
-
| `05-tools.ts` | Built-in tools, custom tools |
|
|
14
|
-
| `06-extensions.ts` | Logging, blocking, result modification |
|
|
15
|
-
| `07-context-files.ts` | AGENTS.md context files |
|
|
16
|
-
| `08-slash-commands.ts` | File-based slash commands |
|
|
17
|
-
| `09-api-keys-and-oauth.ts` | API key resolution, OAuth config |
|
|
18
|
-
| `10-settings.ts` | Override compaction, retry, terminal settings |
|
|
19
|
-
| `11-sessions.ts` | In-memory, persistent, continue, list sessions |
|
|
20
|
-
| `12-full-control.ts` | Replace everything, no discovery |
|
|
21
|
-
|
|
22
|
-
## Running
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
cd packages/coding-agent
|
|
26
|
-
npx tsx examples/sdk/01-minimal.ts
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Quick Reference
|
|
30
|
-
|
|
31
|
-
```typescript
|
|
32
|
-
import { getModel } from "shortcutxl";
|
|
33
|
-
import {
|
|
34
|
-
AuthStorage,
|
|
35
|
-
createAgentSession,
|
|
36
|
-
DefaultResourceLoader,
|
|
37
|
-
ModelRegistry,
|
|
38
|
-
SessionManager,
|
|
39
|
-
SettingsManager,
|
|
40
|
-
codingTools,
|
|
41
|
-
readOnlyTools,
|
|
42
|
-
readTool, bashTool, editTool, writeTool,
|
|
43
|
-
} from "shortcutxl";
|
|
44
|
-
|
|
45
|
-
// Auth and models setup
|
|
46
|
-
const authStorage = AuthStorage.create();
|
|
47
|
-
const modelRegistry = new ModelRegistry(authStorage);
|
|
48
|
-
|
|
49
|
-
// Minimal
|
|
50
|
-
const { session } = await createAgentSession({ authStorage, modelRegistry });
|
|
51
|
-
|
|
52
|
-
// Custom model
|
|
53
|
-
const model = getModel("anthropic", "claude-opus-4-5");
|
|
54
|
-
const { session } = await createAgentSession({ model, thinkingLevel: "high", authStorage, modelRegistry });
|
|
55
|
-
|
|
56
|
-
// Modify prompt
|
|
57
|
-
const loader = new DefaultResourceLoader({
|
|
58
|
-
systemPromptOverride: (base) => `${base}\n\nBe concise.`,
|
|
59
|
-
});
|
|
60
|
-
await loader.reload();
|
|
61
|
-
const { session } = await createAgentSession({ resourceLoader: loader, authStorage, modelRegistry });
|
|
62
|
-
|
|
63
|
-
// Read-only
|
|
64
|
-
const { session } = await createAgentSession({ tools: readOnlyTools, authStorage, modelRegistry });
|
|
65
|
-
|
|
66
|
-
// In-memory
|
|
67
|
-
const { session } = await createAgentSession({
|
|
68
|
-
sessionManager: SessionManager.inMemory(),
|
|
69
|
-
authStorage,
|
|
70
|
-
modelRegistry,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Full control
|
|
74
|
-
const customAuth = AuthStorage.create("/my/app/auth.json");
|
|
75
|
-
customAuth.setRuntimeApiKey("anthropic", process.env.MY_KEY!);
|
|
76
|
-
const customRegistry = new ModelRegistry(customAuth);
|
|
77
|
-
|
|
78
|
-
const resourceLoader = new DefaultResourceLoader({
|
|
79
|
-
systemPromptOverride: () => "You are helpful.",
|
|
80
|
-
extensionFactories: [myExtension],
|
|
81
|
-
skillsOverride: () => ({ skills: [], diagnostics: [] }),
|
|
82
|
-
agentsFilesOverride: () => ({ agentsFiles: [] }),
|
|
83
|
-
promptsOverride: () => ({ prompts: [], diagnostics: [] }),
|
|
84
|
-
});
|
|
85
|
-
await resourceLoader.reload();
|
|
86
|
-
|
|
87
|
-
const { session } = await createAgentSession({
|
|
88
|
-
model,
|
|
89
|
-
authStorage: customAuth,
|
|
90
|
-
modelRegistry: customRegistry,
|
|
91
|
-
resourceLoader,
|
|
92
|
-
tools: [readTool, bashTool],
|
|
93
|
-
customTools: [{ tool: myTool }],
|
|
94
|
-
sessionManager: SessionManager.inMemory(),
|
|
95
|
-
settingsManager: SettingsManager.inMemory(),
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
// Run prompts
|
|
99
|
-
session.subscribe((event) => {
|
|
100
|
-
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
|
101
|
-
process.stdout.write(event.assistantMessageEvent.delta);
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
await session.prompt("Hello");
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## Options
|
|
108
|
-
|
|
109
|
-
| Option | Default | Description |
|
|
110
|
-
|--------|---------|-------------|
|
|
111
|
-
| `authStorage` | `AuthStorage.create()` | Credential storage |
|
|
112
|
-
| `modelRegistry` | `new ModelRegistry(authStorage)` | Model registry |
|
|
113
|
-
| `cwd` | `process.cwd()` | Working directory |
|
|
114
|
-
| `agentDir` | `~/.shortcut/agent` | Config directory |
|
|
115
|
-
| `model` | From settings/first available | Model to use |
|
|
116
|
-
| `thinkingLevel` | From settings/"off" | off, low, medium, high |
|
|
117
|
-
| `tools` | `codingTools` | Built-in tools |
|
|
118
|
-
| `customTools` | `[]` | Additional tool definitions |
|
|
119
|
-
| `resourceLoader` | DefaultResourceLoader | Resource loader for extensions, skills, prompts, themes |
|
|
120
|
-
| `sessionManager` | `SessionManager.create(cwd)` | Persistence |
|
|
121
|
-
| `settingsManager` | `SettingsManager.create(cwd, agentDir)` | Settings overrides |
|
|
122
|
-
|
|
123
|
-
## Events
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
session.subscribe((event) => {
|
|
127
|
-
switch (event.type) {
|
|
128
|
-
case "message_update":
|
|
129
|
-
if (event.assistantMessageEvent.type === "text_delta") {
|
|
130
|
-
process.stdout.write(event.assistantMessageEvent.delta);
|
|
131
|
-
}
|
|
132
|
-
break;
|
|
133
|
-
case "tool_execution_start":
|
|
134
|
-
console.log(`Tool: ${event.toolName}`);
|
|
135
|
-
break;
|
|
136
|
-
case "tool_execution_end":
|
|
137
|
-
console.log(`Result: ${event.result}`);
|
|
138
|
-
break;
|
|
139
|
-
case "agent_end":
|
|
140
|
-
console.log("Done");
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
```
|
|
1
|
+
# SDK Examples
|
|
2
|
+
|
|
3
|
+
Programmatic usage of shortcutxl via `createAgentSession()`.
|
|
4
|
+
|
|
5
|
+
## Examples
|
|
6
|
+
|
|
7
|
+
| File | Description |
|
|
8
|
+
|------|-------------|
|
|
9
|
+
| `01-minimal.ts` | Simplest usage with all defaults |
|
|
10
|
+
| `02-custom-model.ts` | Select model and thinking level |
|
|
11
|
+
| `03-custom-prompt.ts` | Replace or modify system prompt |
|
|
12
|
+
| `04-skills.ts` | Discover, filter, or replace skills |
|
|
13
|
+
| `05-tools.ts` | Built-in tools, custom tools |
|
|
14
|
+
| `06-extensions.ts` | Logging, blocking, result modification |
|
|
15
|
+
| `07-context-files.ts` | AGENTS.md context files |
|
|
16
|
+
| `08-slash-commands.ts` | File-based slash commands |
|
|
17
|
+
| `09-api-keys-and-oauth.ts` | API key resolution, OAuth config |
|
|
18
|
+
| `10-settings.ts` | Override compaction, retry, terminal settings |
|
|
19
|
+
| `11-sessions.ts` | In-memory, persistent, continue, list sessions |
|
|
20
|
+
| `12-full-control.ts` | Replace everything, no discovery |
|
|
21
|
+
|
|
22
|
+
## Running
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
cd packages/coding-agent
|
|
26
|
+
npx tsx examples/sdk/01-minimal.ts
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Reference
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { getModel } from "shortcutxl";
|
|
33
|
+
import {
|
|
34
|
+
AuthStorage,
|
|
35
|
+
createAgentSession,
|
|
36
|
+
DefaultResourceLoader,
|
|
37
|
+
ModelRegistry,
|
|
38
|
+
SessionManager,
|
|
39
|
+
SettingsManager,
|
|
40
|
+
codingTools,
|
|
41
|
+
readOnlyTools,
|
|
42
|
+
readTool, bashTool, editTool, writeTool,
|
|
43
|
+
} from "shortcutxl";
|
|
44
|
+
|
|
45
|
+
// Auth and models setup
|
|
46
|
+
const authStorage = AuthStorage.create();
|
|
47
|
+
const modelRegistry = new ModelRegistry(authStorage);
|
|
48
|
+
|
|
49
|
+
// Minimal
|
|
50
|
+
const { session } = await createAgentSession({ authStorage, modelRegistry });
|
|
51
|
+
|
|
52
|
+
// Custom model
|
|
53
|
+
const model = getModel("anthropic", "claude-opus-4-5");
|
|
54
|
+
const { session } = await createAgentSession({ model, thinkingLevel: "high", authStorage, modelRegistry });
|
|
55
|
+
|
|
56
|
+
// Modify prompt
|
|
57
|
+
const loader = new DefaultResourceLoader({
|
|
58
|
+
systemPromptOverride: (base) => `${base}\n\nBe concise.`,
|
|
59
|
+
});
|
|
60
|
+
await loader.reload();
|
|
61
|
+
const { session } = await createAgentSession({ resourceLoader: loader, authStorage, modelRegistry });
|
|
62
|
+
|
|
63
|
+
// Read-only
|
|
64
|
+
const { session } = await createAgentSession({ tools: readOnlyTools, authStorage, modelRegistry });
|
|
65
|
+
|
|
66
|
+
// In-memory
|
|
67
|
+
const { session } = await createAgentSession({
|
|
68
|
+
sessionManager: SessionManager.inMemory(),
|
|
69
|
+
authStorage,
|
|
70
|
+
modelRegistry,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Full control
|
|
74
|
+
const customAuth = AuthStorage.create("/my/app/auth.json");
|
|
75
|
+
customAuth.setRuntimeApiKey("anthropic", process.env.MY_KEY!);
|
|
76
|
+
const customRegistry = new ModelRegistry(customAuth);
|
|
77
|
+
|
|
78
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
79
|
+
systemPromptOverride: () => "You are helpful.",
|
|
80
|
+
extensionFactories: [myExtension],
|
|
81
|
+
skillsOverride: () => ({ skills: [], diagnostics: [] }),
|
|
82
|
+
agentsFilesOverride: () => ({ agentsFiles: [] }),
|
|
83
|
+
promptsOverride: () => ({ prompts: [], diagnostics: [] }),
|
|
84
|
+
});
|
|
85
|
+
await resourceLoader.reload();
|
|
86
|
+
|
|
87
|
+
const { session } = await createAgentSession({
|
|
88
|
+
model,
|
|
89
|
+
authStorage: customAuth,
|
|
90
|
+
modelRegistry: customRegistry,
|
|
91
|
+
resourceLoader,
|
|
92
|
+
tools: [readTool, bashTool],
|
|
93
|
+
customTools: [{ tool: myTool }],
|
|
94
|
+
sessionManager: SessionManager.inMemory(),
|
|
95
|
+
settingsManager: SettingsManager.inMemory(),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Run prompts
|
|
99
|
+
session.subscribe((event) => {
|
|
100
|
+
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
|
101
|
+
process.stdout.write(event.assistantMessageEvent.delta);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
await session.prompt("Hello");
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Options
|
|
108
|
+
|
|
109
|
+
| Option | Default | Description |
|
|
110
|
+
|--------|---------|-------------|
|
|
111
|
+
| `authStorage` | `AuthStorage.create()` | Credential storage |
|
|
112
|
+
| `modelRegistry` | `new ModelRegistry(authStorage)` | Model registry |
|
|
113
|
+
| `cwd` | `process.cwd()` | Working directory |
|
|
114
|
+
| `agentDir` | `~/.shortcut/agent` | Config directory |
|
|
115
|
+
| `model` | From settings/first available | Model to use |
|
|
116
|
+
| `thinkingLevel` | From settings/"off" | off, low, medium, high |
|
|
117
|
+
| `tools` | `codingTools` | Built-in tools |
|
|
118
|
+
| `customTools` | `[]` | Additional tool definitions |
|
|
119
|
+
| `resourceLoader` | DefaultResourceLoader | Resource loader for extensions, skills, prompts, themes |
|
|
120
|
+
| `sessionManager` | `SessionManager.create(cwd)` | Persistence |
|
|
121
|
+
| `settingsManager` | `SettingsManager.create(cwd, agentDir)` | Settings overrides |
|
|
122
|
+
|
|
123
|
+
## Events
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
session.subscribe((event) => {
|
|
127
|
+
switch (event.type) {
|
|
128
|
+
case "message_update":
|
|
129
|
+
if (event.assistantMessageEvent.type === "text_delta") {
|
|
130
|
+
process.stdout.write(event.assistantMessageEvent.delta);
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
case "tool_execution_start":
|
|
134
|
+
console.log(`Tool: ${event.toolName}`);
|
|
135
|
+
break;
|
|
136
|
+
case "tool_execution_end":
|
|
137
|
+
console.log(`Result: ${event.result}`);
|
|
138
|
+
break;
|
|
139
|
+
case "agent_end":
|
|
140
|
+
console.log("Done");
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
```
|
package/agent-docs/xll-spec.md
CHANGED
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
# ShortcutXL
|
|
2
|
-
|
|
3
|
-
A native Excel add-in (`.xll`) written in C that turns Excel into a programmable surface for an AI agent. The agent runs locally on Windows as a TypeScript/Node.js CLI with a terminal TUI. It controls Excel through two channels: writing Python files for custom functions, and sending code blocks over HTTP for direct manipulation.
|
|
4
|
-
|
|
5
|
-
See `../agent/PLAN.md` for the agent architecture (loop, multiagent, session management).
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Architecture
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
┌─ Windows ──────────────────────────────────────────┐
|
|
13
|
-
│ │
|
|
14
|
-
│ Terminal (PowerShell / Windows Terminal) │
|
|
15
|
-
│ └─ Agent CLI (Node.js, TypeScript) │
|
|
16
|
-
│ ├─ TUI (shortcutxl) │
|
|
17
|
-
│ ├─ Supervisor agent loop │
|
|
18
|
-
│ │ ├─ POST /exec → localhost:8080 ────────────┼──► HTTP
|
|
19
|
-
│ │ ├─ writes .py → modules/ ──────────────────┼──► filesystem
|
|
20
|
-
│ │ └─ Task tool → spawns worker agents │
|
|
21
|
-
│ └─ shortcutxl (Anthropic, OpenAI, Google) │
|
|
22
|
-
│ │
|
|
23
|
-
│ Excel.exe │
|
|
24
|
-
│ └─ ShortcutXL (in-process) │
|
|
25
|
-
│ ├─ HTTP server (:8080) │
|
|
26
|
-
│ │ ├─ GET /config (paths) │
|
|
27
|
-
│ │ └─ POST /exec (run Python) │
|
|
28
|
-
│ ├─ fs_watcher (hot-reload) │
|
|
29
|
-
│ ├─ CPython 3.12 (embedded) │
|
|
30
|
-
│ └─ COM → all open workbooks │
|
|
31
|
-
│ │
|
|
32
|
-
└─────────────────────────────────────────────────────┘
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
On startup the agent calls `GET /config` to discover the `modules/` path and is ready to go. Both agent and Excel run natively on Windows — no WSL boundary, no filesystem translation.
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
## Two Channels
|
|
40
|
-
|
|
41
|
-
### 1. `POST /exec` — imperative actions
|
|
42
|
-
|
|
43
|
-
Agent sends a Python code block. The XLL runs it in the embedded runtime with full COM access to Excel. The result (stdout + stderr) is returned.
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
POST http://localhost:8080/exec
|
|
47
|
-
{"code": "from shortcut_xl import xl_app\napp = xl_app()\napp.Range('A1:D1').Value = [['Name','Revenue','Growth','Status']]\napp.Range('A1:D1').Font.Bold = True"}
|
|
48
|
-
|
|
49
|
-
→ {"ok": true, "output": ""}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Use this for: writing cells, reading data, formatting, creating charts, any one-shot action.
|
|
53
|
-
|
|
54
|
-
### 2. Filesystem — custom functions
|
|
55
|
-
|
|
56
|
-
Agent writes `.py` files with `@xl_func` decorators to `modules/`. The XLL watches the directory, hot-reloads within ~200ms, and registers them as native Excel formulas.
|
|
57
|
-
|
|
58
|
-
```python
|
|
59
|
-
# modules/dashboard.py
|
|
60
|
-
from shortcut_xl import xl_func
|
|
61
|
-
|
|
62
|
-
@xl_func
|
|
63
|
-
def revenue_ytd(region):
|
|
64
|
-
return {"EMEA": 1.2e6, "NA": 3.4e6}.get(region, 0)
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
In Excel: `=revenue_ytd("EMEA")` → `1200000`
|
|
68
|
-
|
|
69
|
-
Use this for: custom worksheet functions that Excel's calc engine calls repeatedly, tick loops, animations.
|
|
70
|
-
|
|
71
|
-
### Why two channels
|
|
72
|
-
|
|
73
|
-
| Need | Channel | Reason |
|
|
74
|
-
|---|---|---|
|
|
75
|
-
| Write cells, format, charts | `/exec` | One-shot, result discarded after execution |
|
|
76
|
-
| Custom formulas (`=foo()`) | filesystem | Excel calls them on every recalculation |
|
|
77
|
-
| Tick loops, animations | filesystem | Persistent code running on a timer |
|
|
78
|
-
|
|
79
|
-
HTTP gives the agent a simple request/response interface for one-shot operations. The filesystem channel handles the only thing HTTP can't: registering functions with Excel's calc engine via `xlfRegister`, which requires in-process code that persists between calls.
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
## What Runs Where
|
|
84
|
-
|
|
85
|
-
| Component | Where | Why |
|
|
86
|
-
|---|---|---|
|
|
87
|
-
| Agent CLI (LLM calls, orchestration, TUI) | Windows, Node.js process | Native filesystem + HTTP to Excel, no cross-OS friction |
|
|
88
|
-
| ShortcutXL (HTTP, fs_watcher, Python, COM) | Windows, inside excel.exe | In-process access to Excel's internals |
|
|
89
|
-
| `modules/*.py` | Windows filesystem | Agent writes directly, XLL watches for changes |
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
## Installation
|
|
94
|
-
|
|
95
|
-
**Installer (recommended):** MSI drops `ShortcutXL.xll` + embedded Python runtime. Writes registry key:
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
HKCU\Software\Microsoft\Office\<version>\Excel\Options
|
|
99
|
-
OPEN = /R "C:\Program Files\Shortcut\ShortcutXL.xll"
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
Excel loads it automatically on every startup.
|
|
103
|
-
|
|
104
|
-
**Manual:** Excel → File → Options → Add-ins → Manage: Excel Add-ins → Browse → select `ShortcutXL.xll`.
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## Platform
|
|
109
|
-
|
|
110
|
-
Windows only. The XLL API is a Windows DLL interface. The custom spreadsheet engine (see `new-sheet-spec.md`) is the cross-platform path.
|
|
1
|
+
# ShortcutXL
|
|
2
|
+
|
|
3
|
+
A native Excel add-in (`.xll`) written in C that turns Excel into a programmable surface for an AI agent. The agent runs locally on Windows as a TypeScript/Node.js CLI with a terminal TUI. It controls Excel through two channels: writing Python files for custom functions, and sending code blocks over HTTP for direct manipulation.
|
|
4
|
+
|
|
5
|
+
See `../agent/PLAN.md` for the agent architecture (loop, multiagent, session management).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
┌─ Windows ──────────────────────────────────────────┐
|
|
13
|
+
│ │
|
|
14
|
+
│ Terminal (PowerShell / Windows Terminal) │
|
|
15
|
+
│ └─ Agent CLI (Node.js, TypeScript) │
|
|
16
|
+
│ ├─ TUI (shortcutxl) │
|
|
17
|
+
│ ├─ Supervisor agent loop │
|
|
18
|
+
│ │ ├─ POST /exec → localhost:8080 ────────────┼──► HTTP
|
|
19
|
+
│ │ ├─ writes .py → modules/ ──────────────────┼──► filesystem
|
|
20
|
+
│ │ └─ Task tool → spawns worker agents │
|
|
21
|
+
│ └─ shortcutxl (Anthropic, OpenAI, Google) │
|
|
22
|
+
│ │
|
|
23
|
+
│ Excel.exe │
|
|
24
|
+
│ └─ ShortcutXL (in-process) │
|
|
25
|
+
│ ├─ HTTP server (:8080) │
|
|
26
|
+
│ │ ├─ GET /config (paths) │
|
|
27
|
+
│ │ └─ POST /exec (run Python) │
|
|
28
|
+
│ ├─ fs_watcher (hot-reload) │
|
|
29
|
+
│ ├─ CPython 3.12 (embedded) │
|
|
30
|
+
│ └─ COM → all open workbooks │
|
|
31
|
+
│ │
|
|
32
|
+
└─────────────────────────────────────────────────────┘
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
On startup the agent calls `GET /config` to discover the `modules/` path and is ready to go. Both agent and Excel run natively on Windows — no WSL boundary, no filesystem translation.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Two Channels
|
|
40
|
+
|
|
41
|
+
### 1. `POST /exec` — imperative actions
|
|
42
|
+
|
|
43
|
+
Agent sends a Python code block. The XLL runs it in the embedded runtime with full COM access to Excel. The result (stdout + stderr) is returned.
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
POST http://localhost:8080/exec
|
|
47
|
+
{"code": "from shortcut_xl import xl_app\napp = xl_app()\napp.Range('A1:D1').Value = [['Name','Revenue','Growth','Status']]\napp.Range('A1:D1').Font.Bold = True"}
|
|
48
|
+
|
|
49
|
+
→ {"ok": true, "output": ""}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Use this for: writing cells, reading data, formatting, creating charts, any one-shot action.
|
|
53
|
+
|
|
54
|
+
### 2. Filesystem — custom functions
|
|
55
|
+
|
|
56
|
+
Agent writes `.py` files with `@xl_func` decorators to `modules/`. The XLL watches the directory, hot-reloads within ~200ms, and registers them as native Excel formulas.
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
# modules/dashboard.py
|
|
60
|
+
from shortcut_xl import xl_func
|
|
61
|
+
|
|
62
|
+
@xl_func
|
|
63
|
+
def revenue_ytd(region):
|
|
64
|
+
return {"EMEA": 1.2e6, "NA": 3.4e6}.get(region, 0)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
In Excel: `=revenue_ytd("EMEA")` → `1200000`
|
|
68
|
+
|
|
69
|
+
Use this for: custom worksheet functions that Excel's calc engine calls repeatedly, tick loops, animations.
|
|
70
|
+
|
|
71
|
+
### Why two channels
|
|
72
|
+
|
|
73
|
+
| Need | Channel | Reason |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| Write cells, format, charts | `/exec` | One-shot, result discarded after execution |
|
|
76
|
+
| Custom formulas (`=foo()`) | filesystem | Excel calls them on every recalculation |
|
|
77
|
+
| Tick loops, animations | filesystem | Persistent code running on a timer |
|
|
78
|
+
|
|
79
|
+
HTTP gives the agent a simple request/response interface for one-shot operations. The filesystem channel handles the only thing HTTP can't: registering functions with Excel's calc engine via `xlfRegister`, which requires in-process code that persists between calls.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## What Runs Where
|
|
84
|
+
|
|
85
|
+
| Component | Where | Why |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| Agent CLI (LLM calls, orchestration, TUI) | Windows, Node.js process | Native filesystem + HTTP to Excel, no cross-OS friction |
|
|
88
|
+
| ShortcutXL (HTTP, fs_watcher, Python, COM) | Windows, inside excel.exe | In-process access to Excel's internals |
|
|
89
|
+
| `modules/*.py` | Windows filesystem | Agent writes directly, XLL watches for changes |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Installation
|
|
94
|
+
|
|
95
|
+
**Installer (recommended):** MSI drops `ShortcutXL.xll` + embedded Python runtime. Writes registry key:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
HKCU\Software\Microsoft\Office\<version>\Excel\Options
|
|
99
|
+
OPEN = /R "C:\Program Files\Shortcut\ShortcutXL.xll"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Excel loads it automatically on every startup.
|
|
103
|
+
|
|
104
|
+
**Manual:** Excel → File → Options → Add-ins → Manage: Excel Add-ins → Browse → select `ShortcutXL.xll`.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Platform
|
|
109
|
+
|
|
110
|
+
Windows only. The XLL API is a Windows DLL interface. The custom spreadsheet engine (see `new-sheet-spec.md`) is the cross-platform path.
|
|
@@ -11,6 +11,12 @@ import { dirname, join } from 'path';
|
|
|
11
11
|
import lockfile from 'proper-lockfile';
|
|
12
12
|
import { getAgentDir } from '../config.js';
|
|
13
13
|
import { resolveConfigValue } from './resolve-config-value.js';
|
|
14
|
+
/**
|
|
15
|
+
* Shared lock options so sync and async locks always use the same lock path.
|
|
16
|
+
* realpath: false ensures both create the same .lock directory on Windows,
|
|
17
|
+
* where fs.realpath can normalize paths differently.
|
|
18
|
+
*/
|
|
19
|
+
const LOCK_BASE_OPTIONS = { realpath: false };
|
|
14
20
|
export class FileAuthStorageBackend {
|
|
15
21
|
authPath;
|
|
16
22
|
constructor(authPath = join(getAgentDir(), 'auth.json')) {
|
|
@@ -33,7 +39,7 @@ export class FileAuthStorageBackend {
|
|
|
33
39
|
this.ensureFileExists();
|
|
34
40
|
let release;
|
|
35
41
|
try {
|
|
36
|
-
release = lockfile.lockSync(this.authPath,
|
|
42
|
+
release = lockfile.lockSync(this.authPath, LOCK_BASE_OPTIONS);
|
|
37
43
|
const current = existsSync(this.authPath) ? readFileSync(this.authPath, 'utf-8') : undefined;
|
|
38
44
|
const { result, next } = fn(current);
|
|
39
45
|
if (next !== undefined) {
|
|
@@ -61,6 +67,7 @@ export class FileAuthStorageBackend {
|
|
|
61
67
|
};
|
|
62
68
|
try {
|
|
63
69
|
release = await lockfile.lock(this.authPath, {
|
|
70
|
+
...LOCK_BASE_OPTIONS,
|
|
64
71
|
retries: {
|
|
65
72
|
retries: 10,
|
|
66
73
|
factor: 2,
|
|
@@ -245,7 +252,15 @@ export class AuthStorage {
|
|
|
245
252
|
}
|
|
246
253
|
try {
|
|
247
254
|
this.storage.withLock((current) => {
|
|
248
|
-
|
|
255
|
+
// If the existing file is corrupt, start from empty rather than failing.
|
|
256
|
+
// This recovers from partial writes (e.g., process killed mid-save).
|
|
257
|
+
let currentData;
|
|
258
|
+
try {
|
|
259
|
+
currentData = this.parseStorageData(current);
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
currentData = {};
|
|
263
|
+
}
|
|
249
264
|
const merged = { ...currentData };
|
|
250
265
|
if (credential) {
|
|
251
266
|
merged[provider] = credential;
|
|
@@ -327,6 +342,10 @@ export class AuthStorage {
|
|
|
327
342
|
throw new Error(`Unknown OAuth provider: ${providerId}`);
|
|
328
343
|
}
|
|
329
344
|
const credentials = await provider.login(callbacks);
|
|
345
|
+
// Clear loadError so persistProviderChange doesn't silently skip the write.
|
|
346
|
+
// A successful login produces known-good credentials — safe to persist even
|
|
347
|
+
// if the initial file read failed (corrupt file, lock contention, etc.).
|
|
348
|
+
this.loadError = null;
|
|
330
349
|
this.set(providerId, { type: 'oauth', ...credentials });
|
|
331
350
|
}
|
|
332
351
|
/**
|
package/package.json
CHANGED
package/xll/ShortcutXL.xll
CHANGED
|
Binary file
|