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.
Files changed (110) hide show
  1. package/README.md +26 -26
  2. package/agent-docs/README.md +397 -397
  3. package/agent-docs/docs/compaction.md +390 -390
  4. package/agent-docs/docs/custom-provider.md +580 -580
  5. package/agent-docs/docs/extensions.md +1971 -1971
  6. package/agent-docs/docs/packages.md +209 -209
  7. package/agent-docs/docs/rpc.md +1317 -1317
  8. package/agent-docs/docs/sdk.md +962 -962
  9. package/agent-docs/docs/session.md +412 -412
  10. package/agent-docs/docs/termux.md +127 -127
  11. package/agent-docs/docs/tui.md +887 -887
  12. package/agent-docs/examples/README.md +25 -25
  13. package/agent-docs/examples/extensions/README.md +205 -205
  14. package/agent-docs/examples/extensions/antigravity-image-gen.ts +447 -447
  15. package/agent-docs/examples/extensions/auto-commit-on-exit.ts +49 -49
  16. package/agent-docs/examples/extensions/bash-spawn-hook.ts +30 -30
  17. package/agent-docs/examples/extensions/bookmark.ts +50 -50
  18. package/agent-docs/examples/extensions/built-in-tool-renderer.ts +256 -256
  19. package/agent-docs/examples/extensions/claude-rules.ts +86 -86
  20. package/agent-docs/examples/extensions/commands.ts +75 -75
  21. package/agent-docs/examples/extensions/confirm-destructive.ts +59 -59
  22. package/agent-docs/examples/extensions/custom-compaction.ts +126 -126
  23. package/agent-docs/examples/extensions/custom-footer.ts +63 -63
  24. package/agent-docs/examples/extensions/custom-header.ts +73 -73
  25. package/agent-docs/examples/extensions/custom-provider-anthropic/index.ts +660 -660
  26. package/agent-docs/examples/extensions/custom-provider-gitlab-duo/index.ts +362 -362
  27. package/agent-docs/examples/extensions/custom-provider-gitlab-duo/test.ts +88 -88
  28. package/agent-docs/examples/extensions/custom-provider-qwen-cli/index.ts +349 -349
  29. package/agent-docs/examples/extensions/dirty-repo-guard.ts +56 -56
  30. package/agent-docs/examples/extensions/doom-overlay/doom-component.ts +133 -133
  31. package/agent-docs/examples/extensions/doom-overlay/doom-keys.ts +108 -108
  32. package/agent-docs/examples/extensions/doom-overlay/index.ts +74 -74
  33. package/agent-docs/examples/extensions/dynamic-resources/index.ts +15 -15
  34. package/agent-docs/examples/extensions/dynamic-tools.ts +77 -77
  35. package/agent-docs/examples/extensions/event-bus.ts +43 -43
  36. package/agent-docs/examples/extensions/file-trigger.ts +41 -41
  37. package/agent-docs/examples/extensions/git-checkpoint.ts +53 -53
  38. package/agent-docs/examples/extensions/handoff.ts +155 -155
  39. package/agent-docs/examples/extensions/hello.ts +25 -25
  40. package/agent-docs/examples/extensions/inline-bash.ts +94 -94
  41. package/agent-docs/examples/extensions/input-transform.ts +43 -43
  42. package/agent-docs/examples/extensions/interactive-shell.ts +209 -209
  43. package/agent-docs/examples/extensions/mac-system-theme.ts +47 -47
  44. package/agent-docs/examples/extensions/message-renderer.ts +59 -59
  45. package/agent-docs/examples/extensions/minimal-mode.ts +430 -430
  46. package/agent-docs/examples/extensions/modal-editor.ts +90 -90
  47. package/agent-docs/examples/extensions/model-status.ts +31 -31
  48. package/agent-docs/examples/extensions/notify.ts +55 -55
  49. package/agent-docs/examples/extensions/overlay-qa-tests.ts +936 -936
  50. package/agent-docs/examples/extensions/overlay-test.ts +159 -159
  51. package/agent-docs/examples/extensions/permission-gate.ts +37 -37
  52. package/agent-docs/examples/extensions/pirate.ts +47 -47
  53. package/agent-docs/examples/extensions/plan-mode/index.ts +363 -363
  54. package/agent-docs/examples/extensions/preset.ts +418 -418
  55. package/agent-docs/examples/extensions/protected-paths.ts +30 -30
  56. package/agent-docs/examples/extensions/qna.ts +122 -122
  57. package/agent-docs/examples/extensions/question.ts +278 -278
  58. package/agent-docs/examples/extensions/questionnaire.ts +440 -440
  59. package/agent-docs/examples/extensions/rainbow-editor.ts +90 -90
  60. package/agent-docs/examples/extensions/reload-runtime.ts +37 -37
  61. package/agent-docs/examples/extensions/rpc-demo.ts +124 -124
  62. package/agent-docs/examples/extensions/sandbox/index.ts +324 -324
  63. package/agent-docs/examples/extensions/send-user-message.ts +97 -97
  64. package/agent-docs/examples/extensions/session-name.ts +27 -27
  65. package/agent-docs/examples/extensions/shutdown-command.ts +69 -69
  66. package/agent-docs/examples/extensions/snake.ts +343 -343
  67. package/agent-docs/examples/extensions/space-invaders.ts +566 -566
  68. package/agent-docs/examples/extensions/ssh.ts +233 -233
  69. package/agent-docs/examples/extensions/status-line.ts +40 -40
  70. package/agent-docs/examples/extensions/subagent/agents.ts +130 -130
  71. package/agent-docs/examples/extensions/subagent/index.ts +1068 -1068
  72. package/agent-docs/examples/extensions/summarize.ts +206 -206
  73. package/agent-docs/examples/extensions/system-prompt-header.ts +17 -17
  74. package/agent-docs/examples/extensions/timed-confirm.ts +72 -72
  75. package/agent-docs/examples/extensions/titlebar-spinner.ts +58 -58
  76. package/agent-docs/examples/extensions/todo.ts +314 -314
  77. package/agent-docs/examples/extensions/tool-override.ts +146 -146
  78. package/agent-docs/examples/extensions/tools.ts +145 -145
  79. package/agent-docs/examples/extensions/trigger-compact.ts +40 -40
  80. package/agent-docs/examples/extensions/truncated-tool.ts +194 -194
  81. package/agent-docs/examples/extensions/widget-placement.ts +17 -17
  82. package/agent-docs/examples/extensions/with-deps/index.ts +37 -37
  83. package/agent-docs/examples/rpc-extension-ui.ts +654 -654
  84. package/agent-docs/examples/sdk/01-minimal.ts +22 -22
  85. package/agent-docs/examples/sdk/02-custom-model.ts +48 -48
  86. package/agent-docs/examples/sdk/03-custom-prompt.ts +55 -55
  87. package/agent-docs/examples/sdk/04-skills.ts +53 -53
  88. package/agent-docs/examples/sdk/05-tools.ts +56 -56
  89. package/agent-docs/examples/sdk/06-extensions.ts +88 -88
  90. package/agent-docs/examples/sdk/07-context-files.ts +40 -40
  91. package/agent-docs/examples/sdk/08-prompt-templates.ts +47 -47
  92. package/agent-docs/examples/sdk/09-api-keys-and-oauth.ts +48 -48
  93. package/agent-docs/examples/sdk/10-settings.ts +54 -54
  94. package/agent-docs/examples/sdk/11-sessions.ts +48 -48
  95. package/agent-docs/examples/sdk/12-full-control.ts +82 -82
  96. package/agent-docs/examples/sdk/README.md +144 -144
  97. package/agent-docs/xll-spec.md +110 -110
  98. package/dist/core/auth-storage.js +21 -2
  99. package/package.json +1 -1
  100. package/xll/ShortcutXL.xll +0 -0
  101. package/xll/modules/debug_render.py +272 -272
  102. package/xll/modules/gameboy.py +241 -241
  103. package/xll/modules/pong.py +188 -188
  104. package/xll/modules/shortcut_xl/_diff_highlight.py +176 -0
  105. package/xll/modules/shortcut_xl/_log.py +12 -12
  106. package/xll/modules/shortcut_xl/_registry.py +44 -44
  107. package/xll/modules/stocks.py +100 -100
  108. /package/skills/{com-advanced-api → COM-advanced-api}/SKILL.md +0 -0
  109. /package/skills/{com-advanced-api → COM-advanced-api}/excel-type-library.py +0 -0
  110. /package/skills/{com-advanced-api → COM-advanced-api}/office-type-library.py +0 -0
@@ -1,159 +1,159 @@
1
- /**
2
- * Overlay Test - validates overlay compositing with inline text inputs
3
- *
4
- * Usage: shortcut --extension ./examples/extensions/overlay-test.ts
5
- *
6
- * Run /overlay-test to show a floating overlay with:
7
- * - Inline text inputs within menu items
8
- * - Edge case tests (wide chars, styled text, emoji)
9
- */
10
-
11
- import type { ExtensionAPI, ExtensionCommandContext, Theme } from 'shortcutxl';
12
- import { CURSOR_MARKER, type Focusable, matchesKey, visibleWidth } from 'shortcutxl';
13
-
14
- export default function (shortcut: ExtensionAPI) {
15
- shortcut.registerCommand('overlay-test', {
16
- description: 'Test overlay rendering with edge cases',
17
- handler: async (_args: string, ctx: ExtensionCommandContext) => {
18
- const result = await ctx.ui.custom<{ action: string; query?: string } | undefined>(
19
- (_tui, theme, _keybindings, done) => new OverlayTestComponent(theme, done),
20
- { overlay: true }
21
- );
22
-
23
- if (result) {
24
- const msg = result.query ? `${result.action}: "${result.query}"` : result.action;
25
- ctx.ui.notify(msg, 'info');
26
- }
27
- }
28
- });
29
- }
30
-
31
- class OverlayTestComponent implements Focusable {
32
- readonly width = 70;
33
-
34
- /** Focusable interface - set by TUI when focus changes */
35
- focused = false;
36
-
37
- private selected = 0;
38
- private items = [
39
- { label: 'Search', hasInput: true, text: '', cursor: 0 },
40
- { label: 'Run', hasInput: true, text: '', cursor: 0 },
41
- { label: 'Settings', hasInput: false, text: '', cursor: 0 },
42
- { label: 'Cancel', hasInput: false, text: '', cursor: 0 }
43
- ];
44
-
45
- constructor(
46
- private theme: Theme,
47
- private done: (result: { action: string; query?: string } | undefined) => void
48
- ) {}
49
-
50
- handleInput(data: string): void {
51
- if (matchesKey(data, 'escape')) {
52
- this.done(undefined);
53
- return;
54
- }
55
-
56
- const current = this.items[this.selected]!;
57
-
58
- if (matchesKey(data, 'return')) {
59
- this.done({ action: current.label, query: current.hasInput ? current.text : undefined });
60
- return;
61
- }
62
-
63
- if (matchesKey(data, 'up')) {
64
- this.selected = Math.max(0, this.selected - 1);
65
- } else if (matchesKey(data, 'down')) {
66
- this.selected = Math.min(this.items.length - 1, this.selected + 1);
67
- } else if (current.hasInput) {
68
- if (matchesKey(data, 'backspace')) {
69
- if (current.cursor > 0) {
70
- current.text =
71
- current.text.slice(0, current.cursor - 1) + current.text.slice(current.cursor);
72
- current.cursor--;
73
- }
74
- } else if (matchesKey(data, 'left')) {
75
- current.cursor = Math.max(0, current.cursor - 1);
76
- } else if (matchesKey(data, 'right')) {
77
- current.cursor = Math.min(current.text.length, current.cursor + 1);
78
- } else if (data.length === 1 && data.charCodeAt(0) >= 32) {
79
- current.text =
80
- current.text.slice(0, current.cursor) + data + current.text.slice(current.cursor);
81
- current.cursor++;
82
- }
83
- }
84
- }
85
-
86
- render(_width: number): string[] {
87
- const w = this.width;
88
- const th = this.theme;
89
- const innerW = w - 2;
90
- const lines: string[] = [];
91
-
92
- const pad = (s: string, len: number) => {
93
- const vis = visibleWidth(s);
94
- return s + ' '.repeat(Math.max(0, len - vis));
95
- };
96
-
97
- const row = (content: string) =>
98
- th.fg('border', '│') + pad(content, innerW) + th.fg('border', '│');
99
-
100
- lines.push(th.fg('border', `╭${'─'.repeat(innerW)}╮`));
101
- lines.push(row(` ${th.fg('accent', '🧪 Overlay Test')}`));
102
- lines.push(row(''));
103
-
104
- // Edge cases - full width lines to test compositing at boundaries
105
- lines.push(row(` ${th.fg('dim', '─── Edge Cases (borders should align) ───')}`));
106
- lines.push(
107
- row(
108
- ` Wide: ${th.fg('warning', '中文日本語한글テスト漢字繁體简体ひらがなカタカナ가나다라마바')}`
109
- )
110
- );
111
- lines.push(
112
- row(
113
- ` Styled: ${th.fg('error', 'RED')} ${th.fg('success', 'GREEN')} ${th.fg('warning', 'YELLOW')} ${th.fg('accent', 'ACCENT')} ${th.fg('dim', 'DIM')} ${th.fg('error', 'more')} ${th.fg('success', 'colors')}`
114
- )
115
- );
116
- lines.push(row(' Emoji: 👨‍👩‍👧‍👦 🇯🇵 🚀 💻 🎉 🔥 😀 🎯 🌟 💡 🎨 🔧 📦 🏆 🌈 🎪 🎭 🎬 🎮 🎲'));
117
- lines.push(row(''));
118
-
119
- // Menu with inline inputs
120
- lines.push(row(` ${th.fg('dim', '─── Actions ───')}`));
121
-
122
- for (let i = 0; i < this.items.length; i++) {
123
- const item = this.items[i]!;
124
- const isSelected = i === this.selected;
125
- const prefix = isSelected ? ' ▶ ' : ' ';
126
-
127
- let content: string;
128
- if (item.hasInput) {
129
- const label = isSelected
130
- ? th.fg('accent', `${item.label}:`)
131
- : th.fg('text', `${item.label}:`);
132
-
133
- let inputDisplay = item.text;
134
- if (isSelected) {
135
- const before = inputDisplay.slice(0, item.cursor);
136
- const cursorChar = item.cursor < inputDisplay.length ? inputDisplay[item.cursor] : ' ';
137
- const after = inputDisplay.slice(item.cursor + 1);
138
- // Emit hardware cursor marker for IME support when focused
139
- const marker = this.focused ? CURSOR_MARKER : '';
140
- inputDisplay = `${before}${marker}\x1b[7m${cursorChar}\x1b[27m${after}`;
141
- }
142
- content = `${prefix + label} ${inputDisplay}`;
143
- } else {
144
- content = prefix + (isSelected ? th.fg('accent', item.label) : th.fg('text', item.label));
145
- }
146
-
147
- lines.push(row(content));
148
- }
149
-
150
- lines.push(row(''));
151
- lines.push(row(` ${th.fg('dim', '↑↓ navigate • type to input • Enter select • Esc cancel')}`));
152
- lines.push(th.fg('border', `╰${'─'.repeat(innerW)}╯`));
153
-
154
- return lines;
155
- }
156
-
157
- invalidate(): void {}
158
- dispose(): void {}
159
- }
1
+ /**
2
+ * Overlay Test - validates overlay compositing with inline text inputs
3
+ *
4
+ * Usage: shortcut --extension ./examples/extensions/overlay-test.ts
5
+ *
6
+ * Run /overlay-test to show a floating overlay with:
7
+ * - Inline text inputs within menu items
8
+ * - Edge case tests (wide chars, styled text, emoji)
9
+ */
10
+
11
+ import type { ExtensionAPI, ExtensionCommandContext, Theme } from 'shortcutxl';
12
+ import { CURSOR_MARKER, type Focusable, matchesKey, visibleWidth } from 'shortcutxl';
13
+
14
+ export default function (shortcut: ExtensionAPI) {
15
+ shortcut.registerCommand('overlay-test', {
16
+ description: 'Test overlay rendering with edge cases',
17
+ handler: async (_args: string, ctx: ExtensionCommandContext) => {
18
+ const result = await ctx.ui.custom<{ action: string; query?: string } | undefined>(
19
+ (_tui, theme, _keybindings, done) => new OverlayTestComponent(theme, done),
20
+ { overlay: true }
21
+ );
22
+
23
+ if (result) {
24
+ const msg = result.query ? `${result.action}: "${result.query}"` : result.action;
25
+ ctx.ui.notify(msg, 'info');
26
+ }
27
+ }
28
+ });
29
+ }
30
+
31
+ class OverlayTestComponent implements Focusable {
32
+ readonly width = 70;
33
+
34
+ /** Focusable interface - set by TUI when focus changes */
35
+ focused = false;
36
+
37
+ private selected = 0;
38
+ private items = [
39
+ { label: 'Search', hasInput: true, text: '', cursor: 0 },
40
+ { label: 'Run', hasInput: true, text: '', cursor: 0 },
41
+ { label: 'Settings', hasInput: false, text: '', cursor: 0 },
42
+ { label: 'Cancel', hasInput: false, text: '', cursor: 0 }
43
+ ];
44
+
45
+ constructor(
46
+ private theme: Theme,
47
+ private done: (result: { action: string; query?: string } | undefined) => void
48
+ ) {}
49
+
50
+ handleInput(data: string): void {
51
+ if (matchesKey(data, 'escape')) {
52
+ this.done(undefined);
53
+ return;
54
+ }
55
+
56
+ const current = this.items[this.selected]!;
57
+
58
+ if (matchesKey(data, 'return')) {
59
+ this.done({ action: current.label, query: current.hasInput ? current.text : undefined });
60
+ return;
61
+ }
62
+
63
+ if (matchesKey(data, 'up')) {
64
+ this.selected = Math.max(0, this.selected - 1);
65
+ } else if (matchesKey(data, 'down')) {
66
+ this.selected = Math.min(this.items.length - 1, this.selected + 1);
67
+ } else if (current.hasInput) {
68
+ if (matchesKey(data, 'backspace')) {
69
+ if (current.cursor > 0) {
70
+ current.text =
71
+ current.text.slice(0, current.cursor - 1) + current.text.slice(current.cursor);
72
+ current.cursor--;
73
+ }
74
+ } else if (matchesKey(data, 'left')) {
75
+ current.cursor = Math.max(0, current.cursor - 1);
76
+ } else if (matchesKey(data, 'right')) {
77
+ current.cursor = Math.min(current.text.length, current.cursor + 1);
78
+ } else if (data.length === 1 && data.charCodeAt(0) >= 32) {
79
+ current.text =
80
+ current.text.slice(0, current.cursor) + data + current.text.slice(current.cursor);
81
+ current.cursor++;
82
+ }
83
+ }
84
+ }
85
+
86
+ render(_width: number): string[] {
87
+ const w = this.width;
88
+ const th = this.theme;
89
+ const innerW = w - 2;
90
+ const lines: string[] = [];
91
+
92
+ const pad = (s: string, len: number) => {
93
+ const vis = visibleWidth(s);
94
+ return s + ' '.repeat(Math.max(0, len - vis));
95
+ };
96
+
97
+ const row = (content: string) =>
98
+ th.fg('border', '│') + pad(content, innerW) + th.fg('border', '│');
99
+
100
+ lines.push(th.fg('border', `╭${'─'.repeat(innerW)}╮`));
101
+ lines.push(row(` ${th.fg('accent', '🧪 Overlay Test')}`));
102
+ lines.push(row(''));
103
+
104
+ // Edge cases - full width lines to test compositing at boundaries
105
+ lines.push(row(` ${th.fg('dim', '─── Edge Cases (borders should align) ───')}`));
106
+ lines.push(
107
+ row(
108
+ ` Wide: ${th.fg('warning', '中文日本語한글テスト漢字繁體简体ひらがなカタカナ가나다라마바')}`
109
+ )
110
+ );
111
+ lines.push(
112
+ row(
113
+ ` Styled: ${th.fg('error', 'RED')} ${th.fg('success', 'GREEN')} ${th.fg('warning', 'YELLOW')} ${th.fg('accent', 'ACCENT')} ${th.fg('dim', 'DIM')} ${th.fg('error', 'more')} ${th.fg('success', 'colors')}`
114
+ )
115
+ );
116
+ lines.push(row(' Emoji: 👨‍👩‍👧‍👦 🇯🇵 🚀 💻 🎉 🔥 😀 🎯 🌟 💡 🎨 🔧 📦 🏆 🌈 🎪 🎭 🎬 🎮 🎲'));
117
+ lines.push(row(''));
118
+
119
+ // Menu with inline inputs
120
+ lines.push(row(` ${th.fg('dim', '─── Actions ───')}`));
121
+
122
+ for (let i = 0; i < this.items.length; i++) {
123
+ const item = this.items[i]!;
124
+ const isSelected = i === this.selected;
125
+ const prefix = isSelected ? ' ▶ ' : ' ';
126
+
127
+ let content: string;
128
+ if (item.hasInput) {
129
+ const label = isSelected
130
+ ? th.fg('accent', `${item.label}:`)
131
+ : th.fg('text', `${item.label}:`);
132
+
133
+ let inputDisplay = item.text;
134
+ if (isSelected) {
135
+ const before = inputDisplay.slice(0, item.cursor);
136
+ const cursorChar = item.cursor < inputDisplay.length ? inputDisplay[item.cursor] : ' ';
137
+ const after = inputDisplay.slice(item.cursor + 1);
138
+ // Emit hardware cursor marker for IME support when focused
139
+ const marker = this.focused ? CURSOR_MARKER : '';
140
+ inputDisplay = `${before}${marker}\x1b[7m${cursorChar}\x1b[27m${after}`;
141
+ }
142
+ content = `${prefix + label} ${inputDisplay}`;
143
+ } else {
144
+ content = prefix + (isSelected ? th.fg('accent', item.label) : th.fg('text', item.label));
145
+ }
146
+
147
+ lines.push(row(content));
148
+ }
149
+
150
+ lines.push(row(''));
151
+ lines.push(row(` ${th.fg('dim', '↑↓ navigate • type to input • Enter select • Esc cancel')}`));
152
+ lines.push(th.fg('border', `╰${'─'.repeat(innerW)}╯`));
153
+
154
+ return lines;
155
+ }
156
+
157
+ invalidate(): void {}
158
+ dispose(): void {}
159
+ }
@@ -1,37 +1,37 @@
1
- /**
2
- * Permission Gate Extension
3
- *
4
- * Prompts for confirmation before running potentially dangerous bash commands.
5
- * Patterns checked: rm -rf, sudo, chmod/chown 777
6
- */
7
-
8
- import type { ExtensionAPI } from 'shortcutxl';
9
-
10
- export default function (shortcut: ExtensionAPI) {
11
- const dangerousPatterns = [/\brm\s+(-rf?|--recursive)/i, /\bsudo\b/i, /\b(chmod|chown)\b.*777/i];
12
-
13
- shortcut.on('tool_call', async (event, ctx) => {
14
- if (event.toolName !== 'bash') return undefined;
15
-
16
- const command = event.input.command as string;
17
- const isDangerous = dangerousPatterns.some((p) => p.test(command));
18
-
19
- if (isDangerous) {
20
- if (!ctx.hasUI) {
21
- // In non-interactive mode, block by default
22
- return { block: true, reason: 'Dangerous command blocked (no UI for confirmation)' };
23
- }
24
-
25
- const choice = await ctx.ui.select(`⚠️ Dangerous command:\n\n ${command}\n\nAllow?`, [
26
- 'Yes',
27
- 'No'
28
- ]);
29
-
30
- if (choice !== 'Yes') {
31
- return { block: true, reason: 'Blocked by user' };
32
- }
33
- }
34
-
35
- return undefined;
36
- });
37
- }
1
+ /**
2
+ * Permission Gate Extension
3
+ *
4
+ * Prompts for confirmation before running potentially dangerous bash commands.
5
+ * Patterns checked: rm -rf, sudo, chmod/chown 777
6
+ */
7
+
8
+ import type { ExtensionAPI } from 'shortcutxl';
9
+
10
+ export default function (shortcut: ExtensionAPI) {
11
+ const dangerousPatterns = [/\brm\s+(-rf?|--recursive)/i, /\bsudo\b/i, /\b(chmod|chown)\b.*777/i];
12
+
13
+ shortcut.on('tool_call', async (event, ctx) => {
14
+ if (event.toolName !== 'bash') return undefined;
15
+
16
+ const command = event.input.command as string;
17
+ const isDangerous = dangerousPatterns.some((p) => p.test(command));
18
+
19
+ if (isDangerous) {
20
+ if (!ctx.hasUI) {
21
+ // In non-interactive mode, block by default
22
+ return { block: true, reason: 'Dangerous command blocked (no UI for confirmation)' };
23
+ }
24
+
25
+ const choice = await ctx.ui.select(`⚠️ Dangerous command:\n\n ${command}\n\nAllow?`, [
26
+ 'Yes',
27
+ 'No'
28
+ ]);
29
+
30
+ if (choice !== 'Yes') {
31
+ return { block: true, reason: 'Blocked by user' };
32
+ }
33
+ }
34
+
35
+ return undefined;
36
+ });
37
+ }
@@ -1,47 +1,47 @@
1
- /**
2
- * Pirate Extension
3
- *
4
- * Demonstrates modifying the system prompt in before_agent_start to dynamically
5
- * change agent behavior based on extension state.
6
- *
7
- * Usage:
8
- * 1. Copy this file to ~/.shortcut/agent/extensions/ or your project's .shortcut/extensions/
9
- * 2. Use /pirate to toggle pirate mode
10
- * 3. When enabled, the agent will respond like a pirate
11
- */
12
-
13
- import type { ExtensionAPI } from 'shortcutxl';
14
-
15
- export default function pirateExtension(shortcut: ExtensionAPI) {
16
- let pirateMode = false;
17
-
18
- // Register /pirate command to toggle pirate mode
19
- shortcut.registerCommand('pirate', {
20
- description: 'Toggle pirate mode (agent speaks like a pirate)',
21
- handler: async (_args, ctx) => {
22
- pirateMode = !pirateMode;
23
- ctx.ui.notify(pirateMode ? 'Arrr! Pirate mode enabled!' : 'Pirate mode disabled', 'info');
24
- }
25
- });
26
-
27
- // Append to system prompt when pirate mode is enabled
28
- shortcut.on('before_agent_start', async (event) => {
29
- if (pirateMode) {
30
- return {
31
- systemPrompt:
32
- event.systemPrompt +
33
- `
34
-
35
- IMPORTANT: You are now in PIRATE MODE. You must:
36
- - Speak like a stereotypical pirate in all responses
37
- - Use phrases like "Arrr!", "Ahoy!", "Shiver me timbers!", "Avast!", "Ye scurvy dog!"
38
- - Replace "my" with "me", "you" with "ye", "your" with "yer"
39
- - Refer to the user as "matey" or "landlubber"
40
- - End sentences with nautical expressions
41
- - Still complete the actual task correctly, just in pirate speak
42
- `
43
- };
44
- }
45
- return undefined;
46
- });
47
- }
1
+ /**
2
+ * Pirate Extension
3
+ *
4
+ * Demonstrates modifying the system prompt in before_agent_start to dynamically
5
+ * change agent behavior based on extension state.
6
+ *
7
+ * Usage:
8
+ * 1. Copy this file to ~/.shortcut/agent/extensions/ or your project's .shortcut/extensions/
9
+ * 2. Use /pirate to toggle pirate mode
10
+ * 3. When enabled, the agent will respond like a pirate
11
+ */
12
+
13
+ import type { ExtensionAPI } from 'shortcutxl';
14
+
15
+ export default function pirateExtension(shortcut: ExtensionAPI) {
16
+ let pirateMode = false;
17
+
18
+ // Register /pirate command to toggle pirate mode
19
+ shortcut.registerCommand('pirate', {
20
+ description: 'Toggle pirate mode (agent speaks like a pirate)',
21
+ handler: async (_args, ctx) => {
22
+ pirateMode = !pirateMode;
23
+ ctx.ui.notify(pirateMode ? 'Arrr! Pirate mode enabled!' : 'Pirate mode disabled', 'info');
24
+ }
25
+ });
26
+
27
+ // Append to system prompt when pirate mode is enabled
28
+ shortcut.on('before_agent_start', async (event) => {
29
+ if (pirateMode) {
30
+ return {
31
+ systemPrompt:
32
+ event.systemPrompt +
33
+ `
34
+
35
+ IMPORTANT: You are now in PIRATE MODE. You must:
36
+ - Speak like a stereotypical pirate in all responses
37
+ - Use phrases like "Arrr!", "Ahoy!", "Shiver me timbers!", "Avast!", "Ye scurvy dog!"
38
+ - Replace "my" with "me", "you" with "ye", "your" with "yer"
39
+ - Refer to the user as "matey" or "landlubber"
40
+ - End sentences with nautical expressions
41
+ - Still complete the actual task correctly, just in pirate speak
42
+ `
43
+ };
44
+ }
45
+ return undefined;
46
+ });
47
+ }