shortcutxl 0.3.49 → 0.3.50

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 (46) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/app/index.d.ts +1 -1
  3. package/dist/app/index.js +1 -1
  4. package/dist/app/modes/action/agent.js +6 -1
  5. package/dist/app/prompts/com-api-reference.json +26 -95
  6. package/dist/app/resources/package-manager.js +2 -41
  7. package/dist/app/settings-manager.d.ts +1 -1
  8. package/dist/app/settings-manager.js +1 -1
  9. package/dist/app/skill-proposals/catalog.d.ts +5 -1
  10. package/dist/app/skill-proposals/catalog.js +7 -1
  11. package/dist/app/skill-proposals/review.d.ts +1 -1
  12. package/dist/app/skill-proposals/review.js +2 -1
  13. package/dist/app/subagents/general/agent.js +6 -4
  14. package/dist/app/subagents/index.js +1 -1
  15. package/dist/app/sync/skills-download.d.ts +3 -12
  16. package/dist/app/sync/skills-download.js +32 -57
  17. package/dist/app/sync/skills-upload.d.ts +4 -4
  18. package/dist/app/sync/skills-upload.js +15 -40
  19. package/dist/app/tools/task/task.js +3 -2
  20. package/dist/cli.js +806 -803
  21. package/dist/config.d.ts +0 -2
  22. package/dist/config.js +0 -4
  23. package/dist/main.js +1 -3
  24. package/dist/shell/approvals/file-access-approval.js +2 -2
  25. package/dist/shell/interactive/interactive-actions.js +0 -1
  26. package/dist/shell/interactive/interactive-mode-options.d.ts +0 -5
  27. package/dist/shell/interactive/interactive-mode.js +1 -4
  28. package/dist/shell/interactive/proposals-workflow.js +33 -13
  29. package/dist/shell/interactive/skills-workflow.d.ts +1 -5
  30. package/dist/shell/interactive/skills-workflow.js +25 -24
  31. package/dist/shell/keybindings.d.ts +1 -1
  32. package/dist/shell/keybindings.js +0 -2
  33. package/dist/startup/interactive-commands.js +0 -2
  34. package/package.json +1 -1
  35. package/user-docs/dist/index.html +1 -1
  36. package/user-docs/dist/shortcutxl-docs.pdf +0 -0
  37. package/xll/python/Lib/site-packages/httpx-0.28.1.dist-info/RECORD +1 -1
  38. package/xll/python/Lib/site-packages/pip-26.1.1.dist-info/RECORD +3 -3
  39. package/xll/python/Scripts/httpx.exe +0 -0
  40. package/xll/python/Scripts/pip.exe +0 -0
  41. package/xll/python/Scripts/pip3.12.exe +0 -0
  42. package/xll/python/Scripts/pip3.exe +0 -0
  43. package/dist/app/sync/skills-sync-state.d.ts +0 -14
  44. package/dist/app/sync/skills-sync-state.js +0 -46
  45. package/dist/startup/skills-sync.d.ts +0 -10
  46. package/dist/startup/skills-sync.js +0 -41
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.50]
4
+
5
+ - **MCP-enabled task agents** - General task agents can now use connected MCP servers, giving delegated work access to the same external tools and resources as the main CLI.
6
+ - **Safer skill uploads** - Uploading skills no longer treats missing local skills as cloud deletions, so remote-only skills stay untouched unless explicitly replaced.
7
+ - **Misc bug fixes** - Shift+A for agent panel, wording, clarity.
8
+
3
9
  ## [0.3.49]
4
10
 
5
11
  - **Transcript scrolling & rendering** - Long conversations now scroll more cleanly with mouse wheel, PageUp/PageDown, and Ctrl+Home/Ctrl+End support while the editor and footer stay pinned.
@@ -46,7 +46,7 @@ export { default as skillProposalsExtension } from './skill-proposals/extension.
46
46
  export { getSkillProposalRoots, type ProposalRoots } from './skill-proposals/proposal-fs.js';
47
47
  export { readSkillProposalReviewSnapshot, type SkillProposalReviewSnapshot } from './skill-proposals/review.js';
48
48
  export type { StartupInfoEntry } from './startup-info.js';
49
- export { downloadSkillsWithApproval, syncSkillsConservatively } from './sync/skills-download.js';
49
+ export { downloadSkillsWithApproval } from './sync/skills-download.js';
50
50
  export type { SkillDownloadApprovalHandler, SkillDownloadApprovalRequest, SkillsDownloadResult } from './sync/skills-download.js';
51
51
  export { uploadSkills } from './sync/skills-upload.js';
52
52
  export type { SkillUploadApprovalRequest, SkillsUploadResult } from './sync/skills-upload.js';
package/dist/app/index.js CHANGED
@@ -36,7 +36,7 @@ export { countSkillProposals, listSkillProposals } from './skill-proposals/catal
36
36
  export { default as skillProposalsExtension } from './skill-proposals/extension.js';
37
37
  export { getSkillProposalRoots } from './skill-proposals/proposal-fs.js';
38
38
  export { readSkillProposalReviewSnapshot } from './skill-proposals/review.js';
39
- export { downloadSkillsWithApproval, syncSkillsConservatively } from './sync/skills-download.js';
39
+ export { downloadSkillsWithApproval } from './sync/skills-download.js';
40
40
  export { uploadSkills } from './sync/skills-upload.js';
41
41
  export * from './tools/index.js';
42
42
  export { buildWorkbookPromptContext, fetchWorkbookConnectionState, formatWorkbookScopePromptContext } from './workbook-context/workbook-summary.js';
@@ -5,12 +5,17 @@
5
5
  * for making changes to the spreadsheet. Can switch to plan or installation mode.
6
6
  */
7
7
  import { MODE } from '../../../mode-names.js';
8
- import { ACTION_SWITCH_MODE, BASH, EXECUTE_CODE, EXECUTE_TOOL, GET_TOOL_INFO, MCP_TOOL_NAMES, SEND_MESSAGE, TASK, TODO_LIST, WRITE } from '../../../tool-names.js';
8
+ import { ACTION_SWITCH_MODE, BASH, EDIT, EXECUTE_CODE, EXECUTE_TOOL, FIND, GET_TOOL_INFO, GREP, LS, MCP_TOOL_NAMES, READ, SEND_MESSAGE, TASK, TODO_LIST, WRITE } from '../../../tool-names.js';
9
9
  import { buildActionPrompt } from './prompt.js';
10
10
  const DESCRIPTION = 'Execute changes to the spreadsheet.';
11
11
  const TOOLS = [
12
+ READ,
12
13
  BASH,
14
+ EDIT,
13
15
  WRITE,
16
+ GREP,
17
+ FIND,
18
+ LS,
14
19
  EXECUTE_CODE,
15
20
  TASK,
16
21
  SEND_MESSAGE,
@@ -6,70 +6,47 @@
6
6
  "getSheetNames": {
7
7
  "signature": "def getSheetNames() -> list[str]",
8
8
  "docstring": "Get the names of all sheets in the workbook.",
9
- "tags": [
10
- "action",
11
- "ask"
12
- ]
9
+ "tags": ["action", "ask"]
13
10
  },
14
11
  "getWorkbookSummary": {
15
12
  "signature": "def getWorkbookSummary() -> str",
16
13
  "docstring": "Get sheet names and used ranges for this workbook.\nReturns lines like:\n Sheets and their used ranges:\n Sheet1: A1:D10 (active)\n Inputs: (empty)",
17
- "tags": [
18
- "action",
19
- "ask"
20
- ]
14
+ "tags": ["action", "ask"]
21
15
  },
22
16
  "getSheet": {
23
17
  "signature": "def getSheet(name: str) -> 'Worksheet'",
24
18
  "docstring": "Get a sheet by name, returned as a wrapped Worksheet.",
25
- "tags": [
26
- "action",
27
- "ask"
28
- ]
19
+ "tags": ["action", "ask"]
29
20
  },
30
21
  "addSheet": {
31
22
  "signature": "def addSheet(name: str, index: int | None = None) -> 'Worksheet'",
32
23
  "docstring": "Add a new sheet at 0-based index. index=0 inserts before the first sheet.\nOmit index for Excel's default position.\nExample: wb.addSheet(\"NewSheet\", 0) # insert as first sheet",
33
- "tags": [
34
- "action"
35
- ]
24
+ "tags": ["action"]
36
25
  },
37
26
  "moveSheet": {
38
27
  "signature": "def moveSheet(name: str, index: int) -> None",
39
28
  "docstring": "Move a sheet to 0-based index. index=0 moves to first, index=Count-1 moves to last.\nExample: wb.moveSheet(\"Data\", 0) # move to first position",
40
- "tags": [
41
- "action"
42
- ]
29
+ "tags": ["action"]
43
30
  },
44
31
  "calculate": {
45
32
  "signature": "def calculate() -> None",
46
33
  "docstring": "Recalculate the entire workbook.\nDependent formulas don't update until after code block completes \u2014 read in a follow-up block.",
47
- "tags": [
48
- "action"
49
- ]
34
+ "tags": ["action"]
50
35
  },
51
36
  "errorCheck": {
52
37
  "signature": "def errorCheck(ranges: list[str] | None = None) -> str",
53
38
  "docstring": "Scan for errors (#REF!, #DIV/0!, #NAME?, #VALUE!, #N/A, #NULL!, #NUM!). Ranges MUST include a sheet name (\"Sheet1\" or \"Sheet1!A1:D50\") \u2014 bare ranges like \"A1:D50\" will fail.\n Args:\n ranges: Optional sheet names (\"Sheet1\") or ranges (\"Sheet1!A1:D50\"). Omit to check all.\n Returns:\n \"No issues found.\" or \"Errors (N):\nSheet1!A1: #REF!\n...\"",
54
- "tags": [
55
- "action",
56
- "ask"
57
- ]
39
+ "tags": ["action", "ask"]
58
40
  },
59
41
  "copyPasteRange": {
60
42
  "signature": "def copyPasteRange(from_range: str, to_address: str, paste_type: str = 'all', ) -> None",
61
43
  "docstring": "Copy range within same workbook via range.Copy + PasteSpecial.\nAddresses must include sheet name. Do NOT use Sheet.Copy (broken via pywin32).\nExample: wb.copyPasteRange(\"Sheet1!A1:D10\", \"Sheet2!A1\", paste_type=\"values\")",
62
- "tags": [
63
- "action"
64
- ]
44
+ "tags": ["action"]
65
45
  },
66
46
  "getNamedRangeInfo": {
67
47
  "signature": "def getNamedRangeInfo(max_count: int = 50) -> str",
68
48
  "docstring": "Get named ranges. Returns formatted string with name, reference, scope, and comment.\nExample output: \"Named Ranges (2):\\n Name: TaxRate | Ref: =Settings!$B$2 | Scope: workbook | Comment: ...\"\nReturns \"No named ranges found.\" if none exist.",
69
- "tags": [
70
- "action",
71
- "ask"
72
- ]
49
+ "tags": ["action", "ask"]
73
50
  }
74
51
  }
75
52
  },
@@ -79,93 +56,62 @@
79
56
  "getSheetSummary": {
80
57
  "signature": "def getSheetSummary() -> str",
81
58
  "docstring": "Overview: name, used range, tables (with style/banded/header/total props),\npivots (with range), conditional formats (type/range/priority/stopIfTrue),\nautofilter (with filtering status), charts (with type).",
82
- "tags": [
83
- "action",
84
- "ask"
85
- ]
59
+ "tags": ["action", "ask"]
86
60
  },
87
61
  "getUsedRange": {
88
62
  "signature": "def getUsedRange() -> str",
89
63
  "docstring": "Get the used range address like \"A1:F20\", or \"\" if empty.",
90
- "tags": [
91
- "action",
92
- "ask"
93
- ]
64
+ "tags": ["action", "ask"]
94
65
  },
95
66
  "getCell": {
96
67
  "signature": "def getCell(address: str, include_style: bool = True) -> str",
97
68
  "docstring": "Formatted cell string: \"value(=FORMULA) (style_json)\". Formula always included.\nZero with custom format appends [0]: \"$ - [0]\".\nExample:\n print(sheet.getCell(\"A1\"))\n print(sheet.getCell(\"A1\", include_style=False))",
98
- "tags": [
99
- "action",
100
- "ask"
101
- ]
69
+ "tags": ["action", "ask"]
102
70
  },
103
71
  "getCellRange": {
104
72
  "signature": "def getCellRange(range_addr: str, include_style: bool = True) -> str",
105
73
  "docstring": "Markdown-like range: \"A1:Name | B1:Value\\nA2:Alice | B2:100\". Uses display text. Max 3000 cells; read larger areas in smaller chunks.\nAppends \"--- Style patterns ---\" section when include_style=True.",
106
- "tags": [
107
- "action",
108
- "ask"
109
- ]
74
+ "tags": ["action", "ask"]
110
75
  },
111
76
  "getRawCellData": {
112
77
  "signature": "def getRawCellData(address: str, formula: bool = False) -> Any",
113
78
  "docstring": "Get raw data value from a cell, preserving data type. Set formula=True to get the formula string.",
114
- "tags": [
115
- "action",
116
- "ask"
117
- ]
79
+ "tags": ["action", "ask"]
118
80
  },
119
81
  "getRawRangeData": {
120
82
  "signature": "def getRawRangeData(range_addr: str, formula: bool = False) -> list[list[Any]]",
121
83
  "docstring": "Get raw data for a range as 2D list. Always returns 2D list even for single row/column.",
122
- "tags": [
123
- "action",
124
- "ask"
125
- ]
84
+ "tags": ["action", "ask"]
126
85
  },
127
86
  "regexSearch": {
128
87
  "signature": "def regexSearch(patterns: list[str], match_case: bool = False) -> list[dict]",
129
88
  "docstring": "Regex search across used range using display text. Returns [{\"address\": \"A1\", \"value\": \"display text\"}, ...].\nExample: sheet.regexSearch([\"revenue\", \"cost\"])",
130
- "tags": [
131
- "action",
132
- "ask"
133
- ]
89
+ "tags": ["action", "ask"]
134
90
  },
135
91
  "setCell": {
136
92
  "signature": "def setCell(address: str, value: Any, number_format: str | None = None, note: str | None = None, ) -> None",
137
93
  "docstring": "Write value or formula. \"=\" prefix = formula. None or \"\" clears the cell.\nExamples:\n sheet.setCell(\"A1\", 8000)\n sheet.setCell(\"C1\", 0.15, number_format=\"0%\", note=\"Tax rate\")\n sheet.setCell(\"B1\", \"=SUM(A1:A10)\")",
138
- "tags": [
139
- "action"
140
- ]
94
+ "tags": ["action"]
141
95
  },
142
96
  "setCellRange": {
143
97
  "signature": "def setCellRange(range_addr: str, values: list[list[Any]], number_format: str | None = None, ) -> None",
144
98
  "docstring": "Bulk write \u2014 ALWAYS use for >100 cells. \"=\" prefix = formula.\nExamples:\n sheet.setCellRange(\"A1:B2\", [[1, 2], [3, 4]])\n sheet.setCellRange(\"A1:B2\", [[\"=SUM(C1)\", \"=SUM(D1)\"], [5, 6]], number_format=\"$#,##0\")",
145
- "tags": [
146
- "action"
147
- ]
99
+ "tags": ["action"]
148
100
  },
149
101
  "autoFill": {
150
102
  "signature": "def autoFill(source_range: str, target_range: str, fill_mode: str = 'auto', ) -> None",
151
103
  "docstring": "Drag-fill. Source must be contained within target. Default \"auto\" increments hardcoded numeric literals; use fill_mode=\"constant\" to copy literal values, then read back destination cells and check for empty/NaN values.\nUse fill_mode=\"constant\" when copying a single value without incrementing.\nWARNING: Single numeric value with \"auto\" creates incrementing sequence (0,1,2,3...).\nExample: sheet.autoFill(\"A1:A2\", \"A1:A10\")",
152
- "tags": [
153
- "action"
154
- ]
104
+ "tags": ["action"]
155
105
  },
156
106
  "addPicture": {
157
107
  "signature": "def addPicture(name: str, base64_data: str, anchor_cell: str) -> None",
158
108
  "docstring": "Add a picture from base64-encoded PNG or JPEG data. No data URI prefix.\nExample: sheet.addPicture(\"chart1\", base64_string, \"F2\")",
159
- "tags": [
160
- "action"
161
- ]
109
+ "tags": ["action"]
162
110
  },
163
111
  "setIBTextColors": {
164
112
  "signature": "def setIBTextColors(range_addr: str, ignored_constants: list[int] | None = None, colors: dict | None = None, ) -> None",
165
113
  "docstring": "IB text colors for numeric inputs only. Text strings and booleans are left untouched.\n- Blue/16711680: hard-coded numeric constants (int/float)\n- Black/0: formulas (same-sheet references)\n- Green/32768: formulas with cross-sheet references (contains '!')\nColors are BGR integers (native COM format).\nExample: sheet.setIBTextColors(\"A1:D10\", ignored_constants=[0])",
166
- "tags": [
167
- "action"
168
- ]
114
+ "tags": ["action"]
169
115
  }
170
116
  }
171
117
  }
@@ -174,42 +120,27 @@
174
120
  "hex_to_bgr": {
175
121
  "signature": "def hex_to_bgr(hex_str: str) -> int",
176
122
  "docstring": "Convert \"#RRGGBB\" hex string to BGR integer for Excel COM.\nExample: ws.Range(\"A1\").Font.Color = hex_to_bgr(\"#FF0000\") # red",
177
- "tags": [
178
- "action",
179
- "ask"
180
- ]
123
+ "tags": ["action", "ask"]
181
124
  },
182
125
  "index_to_address": {
183
126
  "signature": "def index_to_address(row: int, col: int) -> str",
184
127
  "docstring": "Convert 0-based (row, col) to Excel address: index_to_address(0, 0) \u2192 \"A1\".",
185
- "tags": [
186
- "action",
187
- "ask"
188
- ]
128
+ "tags": ["action", "ask"]
189
129
  },
190
130
  "address_to_index": {
191
131
  "signature": "def address_to_index(address: str) -> tuple[int, int]",
192
132
  "docstring": "Parse \"A1\" into (row, col) 0-based tuple: address_to_index(\"B3\") \u2192 (2, 1).",
193
- "tags": [
194
- "action",
195
- "ask"
196
- ]
133
+ "tags": ["action", "ask"]
197
134
  },
198
135
  "col_letter": {
199
136
  "signature": "def col_letter(index: int) -> str",
200
137
  "docstring": "Convert 0-based column index to letter: col_letter(26) \u2192 \"AA\".",
201
- "tags": [
202
- "action",
203
- "ask"
204
- ]
138
+ "tags": ["action", "ask"]
205
139
  },
206
140
  "col_index": {
207
141
  "signature": "def col_index(letter: str) -> int",
208
142
  "docstring": "Convert column letter to 0-based index: col_index(\"AA\") \u2192 26.",
209
- "tags": [
210
- "action",
211
- "ask"
212
- ]
143
+ "tags": ["action", "ask"]
213
144
  }
214
145
  }
215
146
  }
@@ -191,37 +191,6 @@ function collectSkillEntries(dir, includeRootFiles = true, ignoreMatcher, rootDi
191
191
  function collectAutoSkillEntries(dir, includeRootFiles = true) {
192
192
  return collectSkillEntries(dir, includeRootFiles);
193
193
  }
194
- function findGitRepoRoot(startDir) {
195
- let dir = resolve(startDir);
196
- while (true) {
197
- if (existsSync(join(dir, '.git'))) {
198
- return dir;
199
- }
200
- const parent = dirname(dir);
201
- if (parent === dir) {
202
- return null;
203
- }
204
- dir = parent;
205
- }
206
- }
207
- function collectAncestorAgentsSkillDirs(startDir) {
208
- const skillDirs = [];
209
- const resolvedStartDir = resolve(startDir);
210
- const gitRepoRoot = findGitRepoRoot(resolvedStartDir);
211
- let dir = resolvedStartDir;
212
- while (true) {
213
- skillDirs.push(join(dir, '.agents', 'skills'));
214
- if (gitRepoRoot && dir === gitRepoRoot) {
215
- break;
216
- }
217
- const parent = dirname(dir);
218
- if (parent === dir) {
219
- break;
220
- }
221
- dir = parent;
222
- }
223
- return skillDirs;
224
- }
225
194
  function collectAutoPromptEntries(dir) {
226
195
  const entries = [];
227
196
  if (!existsSync(dir))
@@ -1370,8 +1339,6 @@ export class DefaultPackageManager {
1370
1339
  prompts: join(projectBaseDir, 'prompts'),
1371
1340
  themes: join(projectBaseDir, 'themes')
1372
1341
  };
1373
- const userAgentsSkillsDir = join(homedir(), '.agents', 'skills');
1374
- const projectAgentsSkillDirs = collectAncestorAgentsSkillDirs(this.cwd);
1375
1342
  const addResources = (resourceType, paths, metadata, overrides, baseDir) => {
1376
1343
  const target = this.getTargetMap(accumulator, resourceType);
1377
1344
  for (const path of paths) {
@@ -1380,17 +1347,11 @@ export class DefaultPackageManager {
1380
1347
  }
1381
1348
  };
1382
1349
  addResources('extensions', collectAutoExtensionEntries(projectDirs.extensions), projectMetadata, projectOverrides.extensions, projectBaseDir);
1383
- addResources('skills', [
1384
- ...collectAutoSkillEntries(projectDirs.skills),
1385
- ...projectAgentsSkillDirs.flatMap((dir) => collectAutoSkillEntries(dir))
1386
- ], projectMetadata, projectOverrides.skills, projectBaseDir);
1350
+ addResources('skills', collectAutoSkillEntries(projectDirs.skills), projectMetadata, projectOverrides.skills, projectBaseDir);
1387
1351
  addResources('prompts', collectAutoPromptEntries(projectDirs.prompts), projectMetadata, projectOverrides.prompts, projectBaseDir);
1388
1352
  addResources('themes', collectAutoThemeEntries(projectDirs.themes), projectMetadata, projectOverrides.themes, projectBaseDir);
1389
1353
  addResources('extensions', collectAutoExtensionEntries(userDirs.extensions), userMetadata, userOverrides.extensions, globalBaseDir);
1390
- addResources('skills', [
1391
- ...collectAutoSkillEntries(userDirs.skills),
1392
- ...collectAutoSkillEntries(userAgentsSkillsDir)
1393
- ], userMetadata, userOverrides.skills, globalBaseDir);
1354
+ addResources('skills', collectAutoSkillEntries(userDirs.skills), userMetadata, userOverrides.skills, globalBaseDir);
1394
1355
  // Bundled default skills shipped with the package
1395
1356
  const bundledMetadata = {
1396
1357
  source: 'auto',
@@ -336,7 +336,7 @@ export declare class SettingsManager {
336
336
  /** Whether session traces and ClickHouse logs are uploaded. Default: true. */
337
337
  getTelemetry(): boolean;
338
338
  setTelemetry(enabled: boolean): void;
339
- /** Whether dev mode is enabled (file tools, AGENTS.md, verbose logging). Default: false. */
339
+ /** Whether dev mode is enabled (AGENTS.md, dev resources, verbose logging). Default: false. */
340
340
  getDevMode(): boolean;
341
341
  setDevMode(enabled: boolean): void;
342
342
  /** Get all connection metadata (names, types, field names — no secrets). */
@@ -955,7 +955,7 @@ export class SettingsManager {
955
955
  this.markModified('telemetry');
956
956
  this.save();
957
957
  }
958
- /** Whether dev mode is enabled (file tools, AGENTS.md, verbose logging). Default: false. */
958
+ /** Whether dev mode is enabled (AGENTS.md, dev resources, verbose logging). Default: false. */
959
959
  getDevMode() {
960
960
  return this.settings.devMode ?? false;
961
961
  }
@@ -1,5 +1,9 @@
1
1
  import { type ProposalRoots } from './proposal-fs.js';
2
- export type ProposalKind = 'create' | 'update';
2
+ export declare const PROPOSAL_KIND: {
3
+ readonly CREATE: "create";
4
+ readonly UPDATE: "update";
5
+ };
6
+ export type ProposalKind = (typeof PROPOSAL_KIND)[keyof typeof PROPOSAL_KIND];
3
7
  export interface SkillProposalSummary {
4
8
  name: string;
5
9
  proposalDir: string;
@@ -8,6 +8,10 @@ import { lstatSync, readdirSync } from 'node:fs';
8
8
  import { join } from 'node:path';
9
9
  import { initializeSkillProposalRoots, liveSkillDir, proposalDir } from './proposal-fs.js';
10
10
  const SKILL_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
11
+ export const PROPOSAL_KIND = {
12
+ CREATE: 'create',
13
+ UPDATE: 'update'
14
+ };
11
15
  /** Passive discovery for status/prompt/reviewer; it must not mutate proposal folders. */
12
16
  export function listSkillProposals(roots) {
13
17
  initializeSkillProposalRoots(roots);
@@ -15,7 +19,9 @@ export function listSkillProposals(roots) {
15
19
  .filter((entry) => isValidProposalDirectoryEntry(roots, entry))
16
20
  .map((entry) => {
17
21
  const targetLiveSkillDir = liveSkillDir(roots, entry.name);
18
- const kind = pathExistsForLstat(targetLiveSkillDir) ? 'update' : 'create';
22
+ const kind = pathExistsForLstat(targetLiveSkillDir)
23
+ ? PROPOSAL_KIND.UPDATE
24
+ : PROPOSAL_KIND.CREATE;
19
25
  return {
20
26
  name: entry.name,
21
27
  proposalDir: proposalDir(roots, entry.name),
@@ -1,4 +1,4 @@
1
- import type { ProposalKind } from './catalog.js';
1
+ import { type ProposalKind } from './catalog.js';
2
2
  import { type ProposalRoots } from './proposal-fs.js';
3
3
  export interface SkillProposalReviewSnapshot {
4
4
  name: string;
@@ -7,6 +7,7 @@
7
7
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
8
8
  import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
9
9
  import { join } from 'node:path';
10
+ import { PROPOSAL_KIND } from './catalog.js';
10
11
  import { initializeSkillProposalRoots, liveSkillDir, proposalDir, validateSkillName } from './proposal-fs.js';
11
12
  /** Read a proposal's name, kind (create/update), and rationale into a single review-time snapshot. */
12
13
  export function readSkillProposalReviewSnapshot(roots, name) {
@@ -16,7 +17,7 @@ export function readSkillProposalReviewSnapshot(roots, name) {
16
17
  throw new Error(`Proposal does not exist: ${name}`);
17
18
  }
18
19
  const liveDir = liveSkillDir(roots, name);
19
- const kind = existsSync(liveDir) ? 'update' : 'create';
20
+ const kind = existsSync(liveDir) ? PROPOSAL_KIND.UPDATE : PROPOSAL_KIND.CREATE;
20
21
  return { name, kind, rationale: readProposalRationale(roots, name) };
21
22
  }
22
23
  /** Read RATIONALE.md from the proposal folder, or return an empty string if absent or unreadable. */
@@ -4,7 +4,7 @@
4
4
  * System prompt is built dynamically so it picks up the correct engine
5
5
  * (COM vs new-sheet) and the same shared guidelines the action agent uses.
6
6
  */
7
- import { BASH, EXECUTE_CODE, LLM_ANALYSIS, WRITE } from '../../../tool-names.js';
7
+ import { BASH, EXECUTE_CODE, LLM_ANALYSIS, MCP_TOOL_NAMES, WRITE } from '../../../tool-names.js';
8
8
  import { buildCurrentSkillsSection, getEnginePrompts, pushActionSharedGuidelines } from '../../prompts/shared-guidelines.js';
9
9
  import { DEFAULT_SUBAGENT_MODEL, DEFAULT_SUBAGENT_TIMEOUT_SECONDS } from '../defaults.js';
10
10
  const NAME = 'general';
@@ -12,7 +12,7 @@ const DESCRIPTION = `\
12
12
  General-purpose agent for research, computation, and web tasks. Uses include:
13
13
  - parallelizable classification tasks. Each agent can handle approximately <50 natural language queries (LLM calls / tool uses) and <10 web search queries per task, and should be launched concurrently to optimize performance.
14
14
  - when the task involves reading or classifying spreadsheet data, the agent has full execute_code access to read the workbook directly — never transcribe cell values into the query.
15
- Tools: write, bash (includes curl/wget for URLs), execute_code (Excel COM API via Python), llm_analysis (offload classification batches).`;
15
+ Tools: write, bash (includes curl/wget for URLs), execute_code (Excel COM API via Python), llm_analysis (offload classification batches), mcp/mcp_describe (connected MCP servers).`;
16
16
  // ---------------------------------------------------------------------------
17
17
  // System prompt builder
18
18
  // ---------------------------------------------------------------------------
@@ -28,7 +28,7 @@ GENERAL AGENT
28
28
  ## YOUR ROLE: GENERAL-PURPOSE AGENT
29
29
 
30
30
  You handle research, computation, data processing, and web tasks.
31
- You have access to bash, execute_code, and llm_analysis.
31
+ You have access to bash, execute_code, llm_analysis, and connected MCP tools.
32
32
 
33
33
  ## Tools
34
34
  execute_code: use it for all spreadsheet interactions
@@ -36,6 +36,8 @@ ${engine.execTool}
36
36
 
37
37
  llm_analysis: offload classification/extraction work to parallel LLM calls
38
38
 
39
+ mcp/mcp_describe: use connected MCP servers when the task needs external systems or MCP-backed data. Use mcp_describe before calling unfamiliar server tools.
40
+
39
41
  Attachments, Read-Only Operations
40
42
  - Avoid cluttering the user's Excel with files they don't need to see or edit. Read one file at a time
41
43
 
@@ -68,7 +70,7 @@ export function generalAgent() {
68
70
  name: NAME,
69
71
  description: DESCRIPTION,
70
72
  systemPrompt: buildGeneralAgentPrompt(),
71
- tools: [WRITE, BASH, EXECUTE_CODE, LLM_ANALYSIS],
73
+ tools: [WRITE, BASH, EXECUTE_CODE, LLM_ANALYSIS, ...MCP_TOOL_NAMES],
72
74
  model: DEFAULT_SUBAGENT_MODEL,
73
75
  thinkingLevel: 'low',
74
76
  timeoutSeconds: DEFAULT_SUBAGENT_TIMEOUT_SECONDS
@@ -66,7 +66,7 @@ Choose foreground vs background deliberately:
66
66
 
67
67
  Choose the subagent_type carefully based on the task:
68
68
  - document_reader: PDFs, images, document extraction (has llm_analysis for multimodal + bash for programmatic extraction)
69
- - general: research, computation, Excel tasks (has write + execute_code + bash + llm_analysis)
69
+ - general: research, computation, Excel tasks (has write + execute_code + bash + llm_analysis + MCP)
70
70
  - verification: read-only adversarial auditor for review, evidence gathering, and formula/value checks`;
71
71
  const lines = [preamble];
72
72
  for (const agent of subagents) {
@@ -1,23 +1,18 @@
1
1
  /**
2
2
  * Skills download — fetches skill folders from the storage API and writes them locally.
3
3
  *
4
- * Interactive downloads can overwrite a local skill folder after approval so the
5
- * local copy matches the cloud version. Non-interactive downloads stay
6
- * conservative: they add missing remote files, keep differing local files, and
7
- * report conflicts instead of overwriting or deleting local content.
4
+ * Downloads can create or overwrite a local skill folder after approval so the
5
+ * local copy matches the cloud version.
8
6
  */
9
7
  import { type StorageContext } from './storage-client.js';
10
8
  export interface SkillDownloadApprovalRequest {
11
9
  skillName: string;
12
10
  source: StorageContext;
11
+ kind: 'create' | 'update';
13
12
  modifiedFiles: number;
14
13
  localOnlyFiles: number;
15
14
  remoteOnlyFiles: number;
16
15
  }
17
- interface SkillsDownloadConflict {
18
- file: string;
19
- source: StorageContext;
20
- }
21
16
  export interface SkillsDownloadResult {
22
17
  /** Skill names that had at least one file written */
23
18
  downloaded: string[];
@@ -27,12 +22,8 @@ export interface SkillsDownloadResult {
27
22
  skippedByUser: string[];
28
23
  /** Total files written to disk */
29
24
  filesDownloaded: number;
30
- /** Files left unresolved because download would overwrite or delete local content */
31
- conflicts: SkillsDownloadConflict[];
32
25
  errors: string[];
33
26
  }
34
27
  export type SkillDownloadApprovalHandler = (request: SkillDownloadApprovalRequest) => Promise<boolean>;
35
- export declare function syncSkillsConservatively(accessToken: string): Promise<SkillsDownloadResult>;
36
28
  export declare function downloadSkillsWithApproval(accessToken: string, approve: SkillDownloadApprovalHandler): Promise<SkillsDownloadResult>;
37
- export {};
38
29
  //# sourceMappingURL=skills-download.d.ts.map