@serviceme/devtools-core 0.1.6 → 0.1.7

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/index.d.mts CHANGED
@@ -2,6 +2,36 @@ import * as _serviceme_devtools_protocol from '@serviceme/devtools-protocol';
2
2
  import { AgentMarketplaceEntry, AgentMutationRequest, AgentPermissionSummary, CopilotDoctorResult, CopilotPromptOptions, CopilotPromptResult, EnvironmentCheckResult, KnownEnvironmentTool, ToolCheckResult, ServiceMeImageValidationResult, ServiceMeImageInfo, ServiceMeImageCompressOptions, ServiceMeImageCompressResult, JsonSortOptions, JsonValidationResult, AgentToolRiskLevel, AgentToolPermission, ServiceMeProjectExtractTemplateInput, ServiceMeProjectExtractTemplateResult, ServiceMeProjectInstallDepsResult, ServiceMeProjectMakeScriptsExecutableResult, ServiceMeProjectGitInitResult, ServiceMeProjectScaffoldPruneResult, TaskExecutionStatus, ScheduledTaskType, TaskPayload, ScheduledTasksConfig, ScheduledTask, TaskStartedEventParams, TaskOutputEventParams, TaskCompletedEventParams, TaskFailedEventParams, TaskCancelledEventParams, TaskExecutionSnapshot, RunningTaskInfo, TaskExecutionLog, SkillMarketplaceEntry, SkillMutationRequest } from '@serviceme/devtools-protocol';
3
3
  import * as fs from 'node:fs/promises';
4
4
 
5
+ type AgentInstallScope = "workspace" | "user";
6
+ interface InstalledAgent {
7
+ id: string;
8
+ name: string;
9
+ scope: AgentInstallScope;
10
+ installedAt: string;
11
+ }
12
+ interface AgentsStateFile {
13
+ schemaVersion: number;
14
+ installedAgents: InstalledAgent[];
15
+ }
16
+ interface AgentDownloadFile {
17
+ path: string;
18
+ content: string;
19
+ executable?: boolean;
20
+ }
21
+ interface AgentStoreFileSystem {
22
+ readdir: typeof fs.readdir;
23
+ readFile: typeof fs.readFile;
24
+ writeFile: typeof fs.writeFile;
25
+ mkdir: typeof fs.mkdir;
26
+ chmod: typeof fs.chmod;
27
+ }
28
+ interface AgentStoreOptions {
29
+ workspacePath: string;
30
+ userAgentsRoot: string;
31
+ fileSystem?: AgentStoreFileSystem;
32
+ schemaVersion?: number;
33
+ }
34
+
5
35
  interface AgentCatalog {
6
36
  agents: AgentMarketplaceEntry[];
7
37
  fetchedAt: string;
@@ -15,6 +45,7 @@ declare class AgentCatalogClient {
15
45
  private readonly baseUrl?;
16
46
  constructor(options?: AgentCatalogClientOptions);
17
47
  getCatalog(): Promise<AgentCatalog>;
48
+ downloadAgent(remoteId: string): Promise<AgentDownloadFile[]>;
18
49
  }
19
50
 
20
51
  interface AgentStoreLike {
@@ -47,36 +78,6 @@ declare class AgentReconciler {
47
78
  private hasHighRiskTool;
48
79
  }
49
80
 
50
- type AgentInstallScope = "workspace" | "user";
51
- interface InstalledAgent {
52
- id: string;
53
- name: string;
54
- scope: AgentInstallScope;
55
- installedAt: string;
56
- }
57
- interface AgentsStateFile {
58
- schemaVersion: number;
59
- installedAgents: InstalledAgent[];
60
- }
61
- interface AgentDownloadFile {
62
- path: string;
63
- content: string;
64
- executable?: boolean;
65
- }
66
- interface AgentStoreFileSystem {
67
- readdir: typeof fs.readdir;
68
- readFile: typeof fs.readFile;
69
- writeFile: typeof fs.writeFile;
70
- mkdir: typeof fs.mkdir;
71
- chmod: typeof fs.chmod;
72
- }
73
- interface AgentStoreOptions {
74
- workspacePath: string;
75
- userAgentsRoot: string;
76
- fileSystem?: AgentStoreFileSystem;
77
- schemaVersion?: number;
78
- }
79
-
80
81
  declare class AgentStore {
81
82
  private readonly workspacePath;
82
83
  private readonly userAgentsRoot;
@@ -166,6 +167,7 @@ declare class ProjectTools {
166
167
  makeScriptsExecutable(workspacePath: string): Promise<ServiceMeProjectMakeScriptsExecutableResult>;
167
168
  initializeGit(workspacePath: string): Promise<ServiceMeProjectGitInitResult>;
168
169
  runScaffoldPrune(workspacePath: string, preset?: string): Promise<ServiceMeProjectScaffoldPruneResult>;
170
+ private ensurePresetManifest;
169
171
  private findScripts;
170
172
  private pathExists;
171
173
  private selectExtractedDirectory;
@@ -340,6 +342,24 @@ declare class TaskLogManager {
340
342
  clearLogs(taskId?: string): number;
341
343
  }
342
344
 
345
+ interface SkillDownloadFile {
346
+ path: string;
347
+ content: string;
348
+ executable: boolean;
349
+ }
350
+ interface SkillStoreFileSystem {
351
+ readdir: typeof fs.readdir;
352
+ readFile: typeof fs.readFile;
353
+ writeFile: typeof fs.writeFile;
354
+ mkdir: typeof fs.mkdir;
355
+ chmod: typeof fs.chmod;
356
+ }
357
+ interface SkillStoreOptions {
358
+ workspacePath: string;
359
+ userSkillsRoot: string;
360
+ fileSystem?: SkillStoreFileSystem;
361
+ }
362
+
343
363
  interface SkillCatalog {
344
364
  skills: SkillMarketplaceEntry[];
345
365
  fetchedAt: string;
@@ -353,6 +373,7 @@ declare class SkillCatalogClient {
353
373
  private readonly baseUrl?;
354
374
  constructor(options?: SkillCatalogClientOptions);
355
375
  getCatalog(): Promise<SkillCatalog>;
376
+ downloadSkill(remoteId: string): Promise<SkillDownloadFile[]>;
356
377
  }
357
378
 
358
379
  interface CatalogSkillLike {
@@ -389,18 +410,6 @@ declare class SkillReconciler {
389
410
  mutate(request: SkillMutationRequest): Promise<SkillMutateResult>;
390
411
  }
391
412
 
392
- interface SkillStoreFileSystem {
393
- readdir: typeof fs.readdir;
394
- readFile: typeof fs.readFile;
395
- writeFile: typeof fs.writeFile;
396
- mkdir: typeof fs.mkdir;
397
- }
398
- interface SkillStoreOptions {
399
- workspacePath: string;
400
- userSkillsRoot: string;
401
- fileSystem?: SkillStoreFileSystem;
402
- }
403
-
404
413
  declare class SkillStore {
405
414
  private readonly workspacePath;
406
415
  private readonly userSkillsRoot;
@@ -414,9 +423,10 @@ declare class SkillStore {
414
423
  listUserSkillIds(): Promise<string[]>;
415
424
  writeManagedUserSkillMarker(skillId: string): Promise<void>;
416
425
  isManagedUserSkill(skillId: string): Promise<boolean>;
426
+ writeSkillFiles(skillId: string, scope: "workspace" | "user", files: SkillDownloadFile[]): Promise<void>;
417
427
  }
418
428
 
419
429
  declare const unzipFile: (zipPath: string, dest: string) => Promise<void>;
420
430
  declare const moveFiles: (sourceDir: string, destDir: string, overwrite?: boolean) => Promise<void>;
421
431
 
422
- export { type AgentCatalog, AgentCatalogClient, type AgentCatalogClientOptions, type AgentDownloadFile, type AgentInstallScope, type AgentMutateResult, AgentReconciler, type AgentReconcilerDependencies, AgentStore, type AgentStoreFileSystem, type AgentStoreOptions, type AgentsStateFile, type AppendLogInput, type CreateTaskInput, DaemonLogger, type EditTaskInput, EnvironmentInspector, type ExecutorResult, GithubCopilotCliExecutor, HttpRequestExecutor, ImageTools, type InstalledAgent, type JsonTools, type OutputEventCallback, PidManager, ProjectTools, SchedulerDaemon, type ServiceMeLogger, ShellExecutor, type SkillCatalog, SkillCatalogClient, type SkillCatalogClientOptions, type SkillMutateResult, SkillReconciler, type SkillReconcilerDependencies, SkillStore, type SkillStoreFileSystem, type SkillStoreOptions, type StreamingExecutorHandle, type StreamingTaskExecutor, TOOL_RISK_MAP, TaskConfigManager, type TaskEventListener, TaskExecutionEngine, type TaskExecutor, TaskLogManager, copilotDoctor, copilotPrompt, createConsoleLogger, createCopilotAuthRequiredError, createCopilotNotInstalledError, createImageTools, createJsonTools, createProjectTools, getExecutor, isCopilotAuthenticated, isStreamingTaskExecutor, matchesCron, moveFiles, noopLogger, parseAgentToolPermissions, parseIntervalMs, resolveTaskExecutionPayload, unzipFile, validateTaskPayload };
432
+ export { type AgentCatalog, AgentCatalogClient, type AgentCatalogClientOptions, type AgentDownloadFile, type AgentInstallScope, type AgentMutateResult, AgentReconciler, type AgentReconcilerDependencies, AgentStore, type AgentStoreFileSystem, type AgentStoreOptions, type AgentsStateFile, type AppendLogInput, type CreateTaskInput, DaemonLogger, type EditTaskInput, EnvironmentInspector, type ExecutorResult, GithubCopilotCliExecutor, HttpRequestExecutor, ImageTools, type InstalledAgent, type JsonTools, type OutputEventCallback, PidManager, ProjectTools, SchedulerDaemon, type ServiceMeLogger, ShellExecutor, type SkillCatalog, SkillCatalogClient, type SkillCatalogClientOptions, type SkillDownloadFile, type SkillMutateResult, SkillReconciler, type SkillReconcilerDependencies, SkillStore, type SkillStoreFileSystem, type SkillStoreOptions, type StreamingExecutorHandle, type StreamingTaskExecutor, TOOL_RISK_MAP, TaskConfigManager, type TaskEventListener, TaskExecutionEngine, type TaskExecutor, TaskLogManager, copilotDoctor, copilotPrompt, createConsoleLogger, createCopilotAuthRequiredError, createCopilotNotInstalledError, createImageTools, createJsonTools, createProjectTools, getExecutor, isCopilotAuthenticated, isStreamingTaskExecutor, matchesCron, moveFiles, noopLogger, parseAgentToolPermissions, parseIntervalMs, resolveTaskExecutionPayload, unzipFile, validateTaskPayload };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,36 @@ import * as _serviceme_devtools_protocol from '@serviceme/devtools-protocol';
2
2
  import { AgentMarketplaceEntry, AgentMutationRequest, AgentPermissionSummary, CopilotDoctorResult, CopilotPromptOptions, CopilotPromptResult, EnvironmentCheckResult, KnownEnvironmentTool, ToolCheckResult, ServiceMeImageValidationResult, ServiceMeImageInfo, ServiceMeImageCompressOptions, ServiceMeImageCompressResult, JsonSortOptions, JsonValidationResult, AgentToolRiskLevel, AgentToolPermission, ServiceMeProjectExtractTemplateInput, ServiceMeProjectExtractTemplateResult, ServiceMeProjectInstallDepsResult, ServiceMeProjectMakeScriptsExecutableResult, ServiceMeProjectGitInitResult, ServiceMeProjectScaffoldPruneResult, TaskExecutionStatus, ScheduledTaskType, TaskPayload, ScheduledTasksConfig, ScheduledTask, TaskStartedEventParams, TaskOutputEventParams, TaskCompletedEventParams, TaskFailedEventParams, TaskCancelledEventParams, TaskExecutionSnapshot, RunningTaskInfo, TaskExecutionLog, SkillMarketplaceEntry, SkillMutationRequest } from '@serviceme/devtools-protocol';
3
3
  import * as fs from 'node:fs/promises';
4
4
 
5
+ type AgentInstallScope = "workspace" | "user";
6
+ interface InstalledAgent {
7
+ id: string;
8
+ name: string;
9
+ scope: AgentInstallScope;
10
+ installedAt: string;
11
+ }
12
+ interface AgentsStateFile {
13
+ schemaVersion: number;
14
+ installedAgents: InstalledAgent[];
15
+ }
16
+ interface AgentDownloadFile {
17
+ path: string;
18
+ content: string;
19
+ executable?: boolean;
20
+ }
21
+ interface AgentStoreFileSystem {
22
+ readdir: typeof fs.readdir;
23
+ readFile: typeof fs.readFile;
24
+ writeFile: typeof fs.writeFile;
25
+ mkdir: typeof fs.mkdir;
26
+ chmod: typeof fs.chmod;
27
+ }
28
+ interface AgentStoreOptions {
29
+ workspacePath: string;
30
+ userAgentsRoot: string;
31
+ fileSystem?: AgentStoreFileSystem;
32
+ schemaVersion?: number;
33
+ }
34
+
5
35
  interface AgentCatalog {
6
36
  agents: AgentMarketplaceEntry[];
7
37
  fetchedAt: string;
@@ -15,6 +45,7 @@ declare class AgentCatalogClient {
15
45
  private readonly baseUrl?;
16
46
  constructor(options?: AgentCatalogClientOptions);
17
47
  getCatalog(): Promise<AgentCatalog>;
48
+ downloadAgent(remoteId: string): Promise<AgentDownloadFile[]>;
18
49
  }
19
50
 
20
51
  interface AgentStoreLike {
@@ -47,36 +78,6 @@ declare class AgentReconciler {
47
78
  private hasHighRiskTool;
48
79
  }
49
80
 
50
- type AgentInstallScope = "workspace" | "user";
51
- interface InstalledAgent {
52
- id: string;
53
- name: string;
54
- scope: AgentInstallScope;
55
- installedAt: string;
56
- }
57
- interface AgentsStateFile {
58
- schemaVersion: number;
59
- installedAgents: InstalledAgent[];
60
- }
61
- interface AgentDownloadFile {
62
- path: string;
63
- content: string;
64
- executable?: boolean;
65
- }
66
- interface AgentStoreFileSystem {
67
- readdir: typeof fs.readdir;
68
- readFile: typeof fs.readFile;
69
- writeFile: typeof fs.writeFile;
70
- mkdir: typeof fs.mkdir;
71
- chmod: typeof fs.chmod;
72
- }
73
- interface AgentStoreOptions {
74
- workspacePath: string;
75
- userAgentsRoot: string;
76
- fileSystem?: AgentStoreFileSystem;
77
- schemaVersion?: number;
78
- }
79
-
80
81
  declare class AgentStore {
81
82
  private readonly workspacePath;
82
83
  private readonly userAgentsRoot;
@@ -166,6 +167,7 @@ declare class ProjectTools {
166
167
  makeScriptsExecutable(workspacePath: string): Promise<ServiceMeProjectMakeScriptsExecutableResult>;
167
168
  initializeGit(workspacePath: string): Promise<ServiceMeProjectGitInitResult>;
168
169
  runScaffoldPrune(workspacePath: string, preset?: string): Promise<ServiceMeProjectScaffoldPruneResult>;
170
+ private ensurePresetManifest;
169
171
  private findScripts;
170
172
  private pathExists;
171
173
  private selectExtractedDirectory;
@@ -340,6 +342,24 @@ declare class TaskLogManager {
340
342
  clearLogs(taskId?: string): number;
341
343
  }
342
344
 
345
+ interface SkillDownloadFile {
346
+ path: string;
347
+ content: string;
348
+ executable: boolean;
349
+ }
350
+ interface SkillStoreFileSystem {
351
+ readdir: typeof fs.readdir;
352
+ readFile: typeof fs.readFile;
353
+ writeFile: typeof fs.writeFile;
354
+ mkdir: typeof fs.mkdir;
355
+ chmod: typeof fs.chmod;
356
+ }
357
+ interface SkillStoreOptions {
358
+ workspacePath: string;
359
+ userSkillsRoot: string;
360
+ fileSystem?: SkillStoreFileSystem;
361
+ }
362
+
343
363
  interface SkillCatalog {
344
364
  skills: SkillMarketplaceEntry[];
345
365
  fetchedAt: string;
@@ -353,6 +373,7 @@ declare class SkillCatalogClient {
353
373
  private readonly baseUrl?;
354
374
  constructor(options?: SkillCatalogClientOptions);
355
375
  getCatalog(): Promise<SkillCatalog>;
376
+ downloadSkill(remoteId: string): Promise<SkillDownloadFile[]>;
356
377
  }
357
378
 
358
379
  interface CatalogSkillLike {
@@ -389,18 +410,6 @@ declare class SkillReconciler {
389
410
  mutate(request: SkillMutationRequest): Promise<SkillMutateResult>;
390
411
  }
391
412
 
392
- interface SkillStoreFileSystem {
393
- readdir: typeof fs.readdir;
394
- readFile: typeof fs.readFile;
395
- writeFile: typeof fs.writeFile;
396
- mkdir: typeof fs.mkdir;
397
- }
398
- interface SkillStoreOptions {
399
- workspacePath: string;
400
- userSkillsRoot: string;
401
- fileSystem?: SkillStoreFileSystem;
402
- }
403
-
404
413
  declare class SkillStore {
405
414
  private readonly workspacePath;
406
415
  private readonly userSkillsRoot;
@@ -414,9 +423,10 @@ declare class SkillStore {
414
423
  listUserSkillIds(): Promise<string[]>;
415
424
  writeManagedUserSkillMarker(skillId: string): Promise<void>;
416
425
  isManagedUserSkill(skillId: string): Promise<boolean>;
426
+ writeSkillFiles(skillId: string, scope: "workspace" | "user", files: SkillDownloadFile[]): Promise<void>;
417
427
  }
418
428
 
419
429
  declare const unzipFile: (zipPath: string, dest: string) => Promise<void>;
420
430
  declare const moveFiles: (sourceDir: string, destDir: string, overwrite?: boolean) => Promise<void>;
421
431
 
422
- export { type AgentCatalog, AgentCatalogClient, type AgentCatalogClientOptions, type AgentDownloadFile, type AgentInstallScope, type AgentMutateResult, AgentReconciler, type AgentReconcilerDependencies, AgentStore, type AgentStoreFileSystem, type AgentStoreOptions, type AgentsStateFile, type AppendLogInput, type CreateTaskInput, DaemonLogger, type EditTaskInput, EnvironmentInspector, type ExecutorResult, GithubCopilotCliExecutor, HttpRequestExecutor, ImageTools, type InstalledAgent, type JsonTools, type OutputEventCallback, PidManager, ProjectTools, SchedulerDaemon, type ServiceMeLogger, ShellExecutor, type SkillCatalog, SkillCatalogClient, type SkillCatalogClientOptions, type SkillMutateResult, SkillReconciler, type SkillReconcilerDependencies, SkillStore, type SkillStoreFileSystem, type SkillStoreOptions, type StreamingExecutorHandle, type StreamingTaskExecutor, TOOL_RISK_MAP, TaskConfigManager, type TaskEventListener, TaskExecutionEngine, type TaskExecutor, TaskLogManager, copilotDoctor, copilotPrompt, createConsoleLogger, createCopilotAuthRequiredError, createCopilotNotInstalledError, createImageTools, createJsonTools, createProjectTools, getExecutor, isCopilotAuthenticated, isStreamingTaskExecutor, matchesCron, moveFiles, noopLogger, parseAgentToolPermissions, parseIntervalMs, resolveTaskExecutionPayload, unzipFile, validateTaskPayload };
432
+ export { type AgentCatalog, AgentCatalogClient, type AgentCatalogClientOptions, type AgentDownloadFile, type AgentInstallScope, type AgentMutateResult, AgentReconciler, type AgentReconcilerDependencies, AgentStore, type AgentStoreFileSystem, type AgentStoreOptions, type AgentsStateFile, type AppendLogInput, type CreateTaskInput, DaemonLogger, type EditTaskInput, EnvironmentInspector, type ExecutorResult, GithubCopilotCliExecutor, HttpRequestExecutor, ImageTools, type InstalledAgent, type JsonTools, type OutputEventCallback, PidManager, ProjectTools, SchedulerDaemon, type ServiceMeLogger, ShellExecutor, type SkillCatalog, SkillCatalogClient, type SkillCatalogClientOptions, type SkillDownloadFile, type SkillMutateResult, SkillReconciler, type SkillReconcilerDependencies, SkillStore, type SkillStoreFileSystem, type SkillStoreOptions, type StreamingExecutorHandle, type StreamingTaskExecutor, TOOL_RISK_MAP, TaskConfigManager, type TaskEventListener, TaskExecutionEngine, type TaskExecutor, TaskLogManager, copilotDoctor, copilotPrompt, createConsoleLogger, createCopilotAuthRequiredError, createCopilotNotInstalledError, createImageTools, createJsonTools, createProjectTools, getExecutor, isCopilotAuthenticated, isStreamingTaskExecutor, matchesCron, moveFiles, noopLogger, parseAgentToolPermissions, parseIntervalMs, resolveTaskExecutionPayload, unzipFile, validateTaskPayload };
package/dist/index.js CHANGED
@@ -72,6 +72,7 @@ __export(index_exports, {
72
72
  module.exports = __toCommonJS(index_exports);
73
73
 
74
74
  // src/agents/AgentCatalogClient.ts
75
+ var import_devtools_protocol = require("@serviceme/devtools-protocol");
75
76
  var AgentCatalogClient = class {
76
77
  constructor(options = {}) {
77
78
  this.fetchImpl = options.fetchImpl ?? fetch;
@@ -96,6 +97,30 @@ var AgentCatalogClient = class {
96
97
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
97
98
  };
98
99
  }
100
+ async downloadAgent(remoteId) {
101
+ if (!this.baseUrl) {
102
+ throw (0, import_devtools_protocol.createServicemeError)(
103
+ "workspace_not_found",
104
+ "Agent catalog baseUrl is not configured."
105
+ );
106
+ }
107
+ const response = await this.fetchImpl(
108
+ `${this.baseUrl}/api/v1/marketplace/agents/download/${remoteId}`
109
+ );
110
+ if (!response.ok) {
111
+ if (response.status === 404) {
112
+ throw (0, import_devtools_protocol.createServicemeError)(
113
+ "not_found",
114
+ `Agent '${remoteId}' not found`
115
+ );
116
+ }
117
+ throw new Error(
118
+ `Failed to download agent ${remoteId}: ${response.status}`
119
+ );
120
+ }
121
+ const payload = await response.json();
122
+ return payload.data?.files ?? [];
123
+ }
99
124
  };
100
125
 
101
126
  // src/permissions/agent-permissions.ts
@@ -353,7 +378,7 @@ var AgentStore = class {
353
378
  };
354
379
 
355
380
  // src/copilot/doctor.ts
356
- var import_devtools_protocol = require("@serviceme/devtools-protocol");
381
+ var import_devtools_protocol2 = require("@serviceme/devtools-protocol");
357
382
 
358
383
  // src/process/runCommand.ts
359
384
  var import_node_child_process = require("child_process");
@@ -497,20 +522,20 @@ async function copilotDoctor() {
497
522
  return { installed: true, version, authenticated };
498
523
  }
499
524
  function createCopilotNotInstalledError() {
500
- return (0, import_devtools_protocol.createServicemeError)(
501
- import_devtools_protocol.COPILOT_ERROR_CODES.NOT_INSTALLED,
525
+ return (0, import_devtools_protocol2.createServicemeError)(
526
+ import_devtools_protocol2.COPILOT_ERROR_CODES.NOT_INSTALLED,
502
527
  "GitHub Copilot CLI is not installed. Visit https://docs.github.com/en/copilot/using-github-copilot/using-github-copilot-in-the-command-line to install."
503
528
  );
504
529
  }
505
530
  function createCopilotAuthRequiredError() {
506
- return (0, import_devtools_protocol.createServicemeError)(
507
- import_devtools_protocol.COPILOT_ERROR_CODES.AUTH_REQUIRED,
531
+ return (0, import_devtools_protocol2.createServicemeError)(
532
+ import_devtools_protocol2.COPILOT_ERROR_CODES.AUTH_REQUIRED,
508
533
  "GitHub Copilot CLI requires authentication. Run `copilot auth login` to sign in."
509
534
  );
510
535
  }
511
536
 
512
537
  // src/copilot/prompt.ts
513
- var import_devtools_protocol2 = require("@serviceme/devtools-protocol");
538
+ var import_devtools_protocol3 = require("@serviceme/devtools-protocol");
514
539
  var COPILOT_COMMAND2 = "copilot";
515
540
  var DEFAULT_TIMEOUT_MS = 12e4;
516
541
  function stripAnsi(text) {
@@ -544,8 +569,8 @@ async function copilotPrompt(options) {
544
569
  });
545
570
  const output = stripAnsi(result.stdout);
546
571
  if (output.includes("I'm sorry, but I cannot assist") && result.stderr?.includes("Stream completed without a response")) {
547
- throw (0, import_devtools_protocol2.createServicemeError)(
548
- import_devtools_protocol2.COPILOT_ERROR_CODES.AUTH_REQUIRED,
572
+ throw (0, import_devtools_protocol3.createServicemeError)(
573
+ import_devtools_protocol3.COPILOT_ERROR_CODES.AUTH_REQUIRED,
549
574
  "Copilot CLI returned a refusal response. This typically indicates an expired authentication token. Run `gh auth login` to re-authenticate.",
550
575
  { exitCode: 0, output, stderr: result.stderr }
551
576
  );
@@ -554,8 +579,8 @@ async function copilotPrompt(options) {
554
579
  } catch (error) {
555
580
  const err = error;
556
581
  if (err.code === "ETIMEDOUT") {
557
- throw (0, import_devtools_protocol2.createServicemeError)(
558
- import_devtools_protocol2.COPILOT_ERROR_CODES.TIMEOUT,
582
+ throw (0, import_devtools_protocol3.createServicemeError)(
583
+ import_devtools_protocol3.COPILOT_ERROR_CODES.TIMEOUT,
559
584
  `Copilot prompt timed out after ${String(timeout)}ms.`
560
585
  );
561
586
  }
@@ -563,8 +588,8 @@ async function copilotPrompt(options) {
563
588
  const output = stripAnsi(err.stdout ?? "");
564
589
  const stderr = err.stderr ?? err.message ?? "";
565
590
  if (output.includes("I'm sorry, but I cannot assist") || stderr.includes("Stream completed without a response")) {
566
- throw (0, import_devtools_protocol2.createServicemeError)(
567
- import_devtools_protocol2.COPILOT_ERROR_CODES.AUTH_REQUIRED,
591
+ throw (0, import_devtools_protocol3.createServicemeError)(
592
+ import_devtools_protocol3.COPILOT_ERROR_CODES.AUTH_REQUIRED,
568
593
  "Copilot CLI returned a refusal response. This typically indicates an expired authentication token. Run `gh auth login` to re-authenticate.",
569
594
  { exitCode, output, stderr }
570
595
  );
@@ -572,8 +597,8 @@ async function copilotPrompt(options) {
572
597
  if (exitCode !== 0 && output) {
573
598
  return { output, exitCode };
574
599
  }
575
- throw (0, import_devtools_protocol2.createServicemeError)(
576
- import_devtools_protocol2.COPILOT_ERROR_CODES.EXECUTION_FAILED,
600
+ throw (0, import_devtools_protocol3.createServicemeError)(
601
+ import_devtools_protocol3.COPILOT_ERROR_CODES.EXECUTION_FAILED,
577
602
  stderr || `Copilot exited with code ${String(exitCode)}.`,
578
603
  { exitCode, stderr }
579
604
  );
@@ -581,7 +606,7 @@ async function copilotPrompt(options) {
581
606
  }
582
607
 
583
608
  // src/env/environmentInspector.ts
584
- var import_devtools_protocol3 = require("@serviceme/devtools-protocol");
609
+ var import_devtools_protocol4 = require("@serviceme/devtools-protocol");
585
610
  var DEFAULT_TOOL_CHECK_TIMEOUT_MS = 5e3;
586
611
  var TOOL_CHECK_TIMEOUT_MS = {
587
612
  nuget: 12e3,
@@ -599,7 +624,7 @@ var ERROR_CODE_TIMEOUT = "ETIMEDOUT";
599
624
  var EnvironmentInspector = class {
600
625
  async checkEnvironment() {
601
626
  const results = await Promise.all(
602
- import_devtools_protocol3.KNOWN_ENVIRONMENT_TOOLS.map(
627
+ import_devtools_protocol4.KNOWN_ENVIRONMENT_TOOLS.map(
603
628
  async (tool) => [tool, await this.checkTool(tool)]
604
629
  )
605
630
  );
@@ -607,7 +632,7 @@ var EnvironmentInspector = class {
607
632
  }
608
633
  async checkTool(toolName) {
609
634
  if (!/^[a-zA-Z0-9-]+$/.test(toolName)) {
610
- throw (0, import_devtools_protocol3.createServicemeError)("invalid_params", "Invalid tool name.");
635
+ throw (0, import_devtools_protocol4.createServicemeError)("invalid_params", "Invalid tool name.");
611
636
  }
612
637
  try {
613
638
  if (toolName === "nvm") {
@@ -791,7 +816,7 @@ var EnvironmentInspector = class {
791
816
  const execError = error;
792
817
  const message = execError.message || String(error);
793
818
  const code = execError.code;
794
- const isNotFound = message.includes("command not found") || message.includes("not recognized") || message.includes("ENOENT") || code === "ENOENT" || code === ERROR_CODE_NOT_FOUND;
819
+ const isNotFound = message.includes("command not found") || message.includes("not recognized") || message.includes("ENOENT") || message.includes("EACCES") || code === "ENOENT" || code === "EACCES" || code === ERROR_CODE_NOT_FOUND;
795
820
  const isTimeout = code === ERROR_CODE_TIMEOUT || message.toLowerCase().includes("timed out");
796
821
  return {
797
822
  installed: false,
@@ -803,7 +828,7 @@ var EnvironmentInspector = class {
803
828
  // src/image/imageTools.ts
804
829
  var fs2 = __toESM(require("fs/promises"));
805
830
  var path2 = __toESM(require("path"));
806
- var import_devtools_protocol4 = require("@serviceme/devtools-protocol");
831
+ var import_devtools_protocol5 = require("@serviceme/devtools-protocol");
807
832
  var SUPPORTED_FORMATS = [
808
833
  ".jpg",
809
834
  ".jpeg",
@@ -847,7 +872,7 @@ var ImageTools = class {
847
872
  async compress(imagePath, options) {
848
873
  const validation = await this.validate(imagePath, options.sharpModulePath);
849
874
  if (!validation.valid) {
850
- throw (0, import_devtools_protocol4.createServicemeError)(
875
+ throw (0, import_devtools_protocol5.createServicemeError)(
851
876
  "invalid_params",
852
877
  `Invalid image file: ${imagePath}`
853
878
  );
@@ -921,7 +946,7 @@ var ImageTools = class {
921
946
  try {
922
947
  return sharpModulePath ? require(sharpModulePath) : require("sharp");
923
948
  } catch (error) {
924
- throw (0, import_devtools_protocol4.createServicemeError)(
949
+ throw (0, import_devtools_protocol5.createServicemeError)(
925
950
  "internal_error",
926
951
  `sharp is required for image operations: ${error instanceof Error ? error.message : String(error)}`
927
952
  );
@@ -933,13 +958,13 @@ function createImageTools() {
933
958
  }
934
959
 
935
960
  // src/json/jsonTools.ts
936
- var import_devtools_protocol5 = require("@serviceme/devtools-protocol");
961
+ var import_devtools_protocol6 = require("@serviceme/devtools-protocol");
937
962
  var import_comment_json = require("comment-json");
938
963
  var import_json5 = __toESM(require("json5"));
939
964
  function createJsonTools() {
940
965
  return {
941
966
  sort(text, options) {
942
- const finalOptions = { ...import_devtools_protocol5.DEFAULT_JSON_SORT_OPTIONS, ...options };
967
+ const finalOptions = { ...import_devtools_protocol6.DEFAULT_JSON_SORT_OPTIONS, ...options };
943
968
  const json = parseJson(text);
944
969
  const sorted = sortObject(json, finalOptions);
945
970
  return JSON.stringify(sorted, null, detectIndent(text));
@@ -975,7 +1000,7 @@ function parseJson(text) {
975
1000
  } catch {
976
1001
  }
977
1002
  }
978
- throw (0, import_devtools_protocol5.createServicemeError)("json_invalid_input", "Invalid JSON format.");
1003
+ throw (0, import_devtools_protocol6.createServicemeError)("json_invalid_input", "Invalid JSON format.");
979
1004
  }
980
1005
  function sortObject(obj, options) {
981
1006
  if (Array.isArray(obj)) {
@@ -1069,7 +1094,7 @@ function createConsoleLogger(prefix = "serviceme") {
1069
1094
  // src/project/projectTools.ts
1070
1095
  var fs3 = __toESM(require("fs/promises"));
1071
1096
  var path3 = __toESM(require("path"));
1072
- var import_devtools_protocol6 = require("@serviceme/devtools-protocol");
1097
+ var import_devtools_protocol7 = require("@serviceme/devtools-protocol");
1073
1098
 
1074
1099
  // src/utils/fileUtils.ts
1075
1100
  var import_node_fs = require("fs");
@@ -1225,7 +1250,7 @@ var ProjectTools = class {
1225
1250
  }
1226
1251
  async installDependencies(workspacePath, command) {
1227
1252
  if (!command) {
1228
- throw (0, import_devtools_protocol6.createServicemeError)("invalid_params", "Expected install command.");
1253
+ throw (0, import_devtools_protocol7.createServicemeError)("invalid_params", "Expected install command.");
1229
1254
  }
1230
1255
  await runCommand(command, {
1231
1256
  cwd: workspacePath,
@@ -1288,13 +1313,16 @@ var ProjectTools = class {
1288
1313
  }
1289
1314
  async runScaffoldPrune(workspacePath, preset) {
1290
1315
  if (!preset) {
1291
- throw (0, import_devtools_protocol6.createServicemeError)("invalid_params", "Expected scaffold preset.");
1316
+ throw (0, import_devtools_protocol7.createServicemeError)("invalid_params", "Expected scaffold preset.");
1292
1317
  }
1293
- const commands = [
1294
- `pnpm run scaffold:prune -- --preset ${preset} --project-root .`,
1295
- `pnpm run scaffold:init-metadata -- --preset ${preset} --project-root . --force`
1296
- ];
1318
+ await this.ensurePresetManifest(workspacePath, preset);
1319
+ const pruneCommand = `pnpm run scaffold:prune -- --preset ${preset} --project-root .`;
1320
+ const initMetadataCommand = `pnpm run scaffold:init-metadata -- --preset ${preset} --project-root . --force`;
1321
+ const commands = [pruneCommand, initMetadataCommand];
1297
1322
  for (const command of commands) {
1323
+ if (command === initMetadataCommand) {
1324
+ await this.ensurePresetManifest(workspacePath, preset);
1325
+ }
1298
1326
  await runCommand(command, {
1299
1327
  cwd: workspacePath,
1300
1328
  shell: true
@@ -1306,6 +1334,44 @@ var ProjectTools = class {
1306
1334
  commands
1307
1335
  };
1308
1336
  }
1337
+ async ensurePresetManifest(workspacePath, preset) {
1338
+ const presetManifestPath = path3.join(
1339
+ workspacePath,
1340
+ ".ms-scaffold",
1341
+ "presets",
1342
+ `${preset}.json`
1343
+ );
1344
+ if (await this.pathExists(presetManifestPath)) {
1345
+ return;
1346
+ }
1347
+ const projectModePath = path3.join(
1348
+ workspacePath,
1349
+ ".ms-scaffold",
1350
+ "project-mode.json"
1351
+ );
1352
+ if (!await this.pathExists(projectModePath)) {
1353
+ return;
1354
+ }
1355
+ const projectModeRaw = await fs3.readFile(projectModePath, "utf8");
1356
+ const projectMode = JSON.parse(projectModeRaw);
1357
+ const synthesizedPreset = {
1358
+ preset,
1359
+ selectedModules: Array.isArray(projectMode.selectedModules) ? projectMode.selectedModules : [],
1360
+ prunedModules: Array.isArray(projectMode.prunedModules) ? projectMode.prunedModules : [],
1361
+ exclude: [],
1362
+ recommendedFollowUps: [],
1363
+ managedFiles: [],
1364
+ mergeManagedFiles: [],
1365
+ userOwnedPaths: []
1366
+ };
1367
+ await fs3.mkdir(path3.dirname(presetManifestPath), { recursive: true });
1368
+ await fs3.writeFile(
1369
+ presetManifestPath,
1370
+ `${JSON.stringify(synthesizedPreset, null, 2)}
1371
+ `,
1372
+ "utf8"
1373
+ );
1374
+ }
1309
1375
  async findScripts(dir, extensions) {
1310
1376
  const results = [];
1311
1377
  let entries;
@@ -1926,7 +1992,7 @@ function getExecutor(taskType) {
1926
1992
  var import_node_crypto = require("crypto");
1927
1993
  var fs8 = __toESM(require("fs"));
1928
1994
  var path7 = __toESM(require("path"));
1929
- var import_devtools_protocol7 = require("@serviceme/devtools-protocol");
1995
+ var import_devtools_protocol8 = require("@serviceme/devtools-protocol");
1930
1996
  var CONFIG_DIR3 = ".serviceme";
1931
1997
  var CONFIG_FILE = "scheduled-tasks.json";
1932
1998
  function emptyConfig() {
@@ -1937,7 +2003,7 @@ function isRecord(value) {
1937
2003
  }
1938
2004
  function requireNonEmptyString(payload, field, taskType) {
1939
2005
  if (!isRecord(payload) || typeof payload[field] !== "string" || !payload[field].trim()) {
1940
- throw (0, import_devtools_protocol7.createServicemeError)(
2006
+ throw (0, import_devtools_protocol8.createServicemeError)(
1941
2007
  "invalid_payload",
1942
2008
  `${taskType} payload ${field} is required`
1943
2009
  );
@@ -2585,6 +2651,7 @@ function matchCronField(field, value) {
2585
2651
  }
2586
2652
 
2587
2653
  // src/skills/SkillCatalogClient.ts
2654
+ var import_devtools_protocol9 = require("@serviceme/devtools-protocol");
2588
2655
  var SkillCatalogClient = class {
2589
2656
  constructor(options = {}) {
2590
2657
  this.fetchImpl = options.fetchImpl ?? fetch;
@@ -2609,6 +2676,30 @@ var SkillCatalogClient = class {
2609
2676
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
2610
2677
  };
2611
2678
  }
2679
+ async downloadSkill(remoteId) {
2680
+ if (!this.baseUrl) {
2681
+ throw (0, import_devtools_protocol9.createServicemeError)(
2682
+ "workspace_not_found",
2683
+ "Skill catalog baseUrl is not configured."
2684
+ );
2685
+ }
2686
+ const response = await this.fetchImpl(
2687
+ `${this.baseUrl}/api/v1/marketplace/skills/download/${remoteId}`
2688
+ );
2689
+ if (!response.ok) {
2690
+ if (response.status === 404) {
2691
+ throw (0, import_devtools_protocol9.createServicemeError)(
2692
+ "not_found",
2693
+ `Skill '${remoteId}' not found`
2694
+ );
2695
+ }
2696
+ throw new Error(
2697
+ `Failed to download skill ${remoteId}: ${response.status}`
2698
+ );
2699
+ }
2700
+ const payload = await response.json();
2701
+ return payload.data?.files ?? [];
2702
+ }
2612
2703
  };
2613
2704
 
2614
2705
  // src/skills/SkillReconciler.ts
@@ -2745,6 +2836,22 @@ var SkillStore = class {
2745
2836
  return false;
2746
2837
  }
2747
2838
  }
2839
+ async writeSkillFiles(skillId, scope, files) {
2840
+ const root = scope === "workspace" ? path9.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE) : this.userSkillsRoot;
2841
+ const targetDir = path9.join(root, skillId);
2842
+ await this.fileSystem.mkdir(targetDir, { recursive: true });
2843
+ for (const file of files) {
2844
+ const filePath = path9.join(targetDir, file.path);
2845
+ await this.fileSystem.mkdir(path9.dirname(filePath), { recursive: true });
2846
+ await this.fileSystem.writeFile(filePath, file.content, "utf-8");
2847
+ if (file.executable) {
2848
+ try {
2849
+ await this.fileSystem.chmod(filePath, 493);
2850
+ } catch {
2851
+ }
2852
+ }
2853
+ }
2854
+ }
2748
2855
  };
2749
2856
  // Annotate the CommonJS export names for ESM import in node:
2750
2857
  0 && (module.exports = {
package/dist/index.mjs CHANGED
@@ -6,6 +6,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  });
7
7
 
8
8
  // src/agents/AgentCatalogClient.ts
9
+ import { createServicemeError } from "@serviceme/devtools-protocol";
9
10
  var AgentCatalogClient = class {
10
11
  constructor(options = {}) {
11
12
  this.fetchImpl = options.fetchImpl ?? fetch;
@@ -30,6 +31,30 @@ var AgentCatalogClient = class {
30
31
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
31
32
  };
32
33
  }
34
+ async downloadAgent(remoteId) {
35
+ if (!this.baseUrl) {
36
+ throw createServicemeError(
37
+ "workspace_not_found",
38
+ "Agent catalog baseUrl is not configured."
39
+ );
40
+ }
41
+ const response = await this.fetchImpl(
42
+ `${this.baseUrl}/api/v1/marketplace/agents/download/${remoteId}`
43
+ );
44
+ if (!response.ok) {
45
+ if (response.status === 404) {
46
+ throw createServicemeError(
47
+ "not_found",
48
+ `Agent '${remoteId}' not found`
49
+ );
50
+ }
51
+ throw new Error(
52
+ `Failed to download agent ${remoteId}: ${response.status}`
53
+ );
54
+ }
55
+ const payload = await response.json();
56
+ return payload.data?.files ?? [];
57
+ }
33
58
  };
34
59
 
35
60
  // src/permissions/agent-permissions.ts
@@ -289,7 +314,7 @@ var AgentStore = class {
289
314
  // src/copilot/doctor.ts
290
315
  import {
291
316
  COPILOT_ERROR_CODES,
292
- createServicemeError
317
+ createServicemeError as createServicemeError2
293
318
  } from "@serviceme/devtools-protocol";
294
319
 
295
320
  // src/process/runCommand.ts
@@ -434,13 +459,13 @@ async function copilotDoctor() {
434
459
  return { installed: true, version, authenticated };
435
460
  }
436
461
  function createCopilotNotInstalledError() {
437
- return createServicemeError(
462
+ return createServicemeError2(
438
463
  COPILOT_ERROR_CODES.NOT_INSTALLED,
439
464
  "GitHub Copilot CLI is not installed. Visit https://docs.github.com/en/copilot/using-github-copilot/using-github-copilot-in-the-command-line to install."
440
465
  );
441
466
  }
442
467
  function createCopilotAuthRequiredError() {
443
- return createServicemeError(
468
+ return createServicemeError2(
444
469
  COPILOT_ERROR_CODES.AUTH_REQUIRED,
445
470
  "GitHub Copilot CLI requires authentication. Run `copilot auth login` to sign in."
446
471
  );
@@ -449,7 +474,7 @@ function createCopilotAuthRequiredError() {
449
474
  // src/copilot/prompt.ts
450
475
  import {
451
476
  COPILOT_ERROR_CODES as COPILOT_ERROR_CODES2,
452
- createServicemeError as createServicemeError2
477
+ createServicemeError as createServicemeError3
453
478
  } from "@serviceme/devtools-protocol";
454
479
  var COPILOT_COMMAND2 = "copilot";
455
480
  var DEFAULT_TIMEOUT_MS = 12e4;
@@ -484,7 +509,7 @@ async function copilotPrompt(options) {
484
509
  });
485
510
  const output = stripAnsi(result.stdout);
486
511
  if (output.includes("I'm sorry, but I cannot assist") && result.stderr?.includes("Stream completed without a response")) {
487
- throw createServicemeError2(
512
+ throw createServicemeError3(
488
513
  COPILOT_ERROR_CODES2.AUTH_REQUIRED,
489
514
  "Copilot CLI returned a refusal response. This typically indicates an expired authentication token. Run `gh auth login` to re-authenticate.",
490
515
  { exitCode: 0, output, stderr: result.stderr }
@@ -494,7 +519,7 @@ async function copilotPrompt(options) {
494
519
  } catch (error) {
495
520
  const err = error;
496
521
  if (err.code === "ETIMEDOUT") {
497
- throw createServicemeError2(
522
+ throw createServicemeError3(
498
523
  COPILOT_ERROR_CODES2.TIMEOUT,
499
524
  `Copilot prompt timed out after ${String(timeout)}ms.`
500
525
  );
@@ -503,7 +528,7 @@ async function copilotPrompt(options) {
503
528
  const output = stripAnsi(err.stdout ?? "");
504
529
  const stderr = err.stderr ?? err.message ?? "";
505
530
  if (output.includes("I'm sorry, but I cannot assist") || stderr.includes("Stream completed without a response")) {
506
- throw createServicemeError2(
531
+ throw createServicemeError3(
507
532
  COPILOT_ERROR_CODES2.AUTH_REQUIRED,
508
533
  "Copilot CLI returned a refusal response. This typically indicates an expired authentication token. Run `gh auth login` to re-authenticate.",
509
534
  { exitCode, output, stderr }
@@ -512,7 +537,7 @@ async function copilotPrompt(options) {
512
537
  if (exitCode !== 0 && output) {
513
538
  return { output, exitCode };
514
539
  }
515
- throw createServicemeError2(
540
+ throw createServicemeError3(
516
541
  COPILOT_ERROR_CODES2.EXECUTION_FAILED,
517
542
  stderr || `Copilot exited with code ${String(exitCode)}.`,
518
543
  { exitCode, stderr }
@@ -522,7 +547,7 @@ async function copilotPrompt(options) {
522
547
 
523
548
  // src/env/environmentInspector.ts
524
549
  import {
525
- createServicemeError as createServicemeError3,
550
+ createServicemeError as createServicemeError4,
526
551
  KNOWN_ENVIRONMENT_TOOLS
527
552
  } from "@serviceme/devtools-protocol";
528
553
  var DEFAULT_TOOL_CHECK_TIMEOUT_MS = 5e3;
@@ -550,7 +575,7 @@ var EnvironmentInspector = class {
550
575
  }
551
576
  async checkTool(toolName) {
552
577
  if (!/^[a-zA-Z0-9-]+$/.test(toolName)) {
553
- throw createServicemeError3("invalid_params", "Invalid tool name.");
578
+ throw createServicemeError4("invalid_params", "Invalid tool name.");
554
579
  }
555
580
  try {
556
581
  if (toolName === "nvm") {
@@ -734,7 +759,7 @@ var EnvironmentInspector = class {
734
759
  const execError = error;
735
760
  const message = execError.message || String(error);
736
761
  const code = execError.code;
737
- const isNotFound = message.includes("command not found") || message.includes("not recognized") || message.includes("ENOENT") || code === "ENOENT" || code === ERROR_CODE_NOT_FOUND;
762
+ const isNotFound = message.includes("command not found") || message.includes("not recognized") || message.includes("ENOENT") || message.includes("EACCES") || code === "ENOENT" || code === "EACCES" || code === ERROR_CODE_NOT_FOUND;
738
763
  const isTimeout = code === ERROR_CODE_TIMEOUT || message.toLowerCase().includes("timed out");
739
764
  return {
740
765
  installed: false,
@@ -747,7 +772,7 @@ var EnvironmentInspector = class {
747
772
  import * as fs2 from "fs/promises";
748
773
  import * as path2 from "path";
749
774
  import {
750
- createServicemeError as createServicemeError4
775
+ createServicemeError as createServicemeError5
751
776
  } from "@serviceme/devtools-protocol";
752
777
  var SUPPORTED_FORMATS = [
753
778
  ".jpg",
@@ -792,7 +817,7 @@ var ImageTools = class {
792
817
  async compress(imagePath, options) {
793
818
  const validation = await this.validate(imagePath, options.sharpModulePath);
794
819
  if (!validation.valid) {
795
- throw createServicemeError4(
820
+ throw createServicemeError5(
796
821
  "invalid_params",
797
822
  `Invalid image file: ${imagePath}`
798
823
  );
@@ -866,7 +891,7 @@ var ImageTools = class {
866
891
  try {
867
892
  return sharpModulePath ? __require(sharpModulePath) : __require("sharp");
868
893
  } catch (error) {
869
- throw createServicemeError4(
894
+ throw createServicemeError5(
870
895
  "internal_error",
871
896
  `sharp is required for image operations: ${error instanceof Error ? error.message : String(error)}`
872
897
  );
@@ -879,7 +904,7 @@ function createImageTools() {
879
904
 
880
905
  // src/json/jsonTools.ts
881
906
  import {
882
- createServicemeError as createServicemeError5,
907
+ createServicemeError as createServicemeError6,
883
908
  DEFAULT_JSON_SORT_OPTIONS
884
909
  } from "@serviceme/devtools-protocol";
885
910
  import { parse as parseCommentJson } from "comment-json";
@@ -923,7 +948,7 @@ function parseJson(text) {
923
948
  } catch {
924
949
  }
925
950
  }
926
- throw createServicemeError5("json_invalid_input", "Invalid JSON format.");
951
+ throw createServicemeError6("json_invalid_input", "Invalid JSON format.");
927
952
  }
928
953
  function sortObject(obj, options) {
929
954
  if (Array.isArray(obj)) {
@@ -1018,7 +1043,7 @@ function createConsoleLogger(prefix = "serviceme") {
1018
1043
  import * as fs3 from "fs/promises";
1019
1044
  import * as path3 from "path";
1020
1045
  import {
1021
- createServicemeError as createServicemeError6
1046
+ createServicemeError as createServicemeError7
1022
1047
  } from "@serviceme/devtools-protocol";
1023
1048
 
1024
1049
  // src/utils/fileUtils.ts
@@ -1183,7 +1208,7 @@ var ProjectTools = class {
1183
1208
  }
1184
1209
  async installDependencies(workspacePath, command) {
1185
1210
  if (!command) {
1186
- throw createServicemeError6("invalid_params", "Expected install command.");
1211
+ throw createServicemeError7("invalid_params", "Expected install command.");
1187
1212
  }
1188
1213
  await runCommand(command, {
1189
1214
  cwd: workspacePath,
@@ -1246,13 +1271,16 @@ var ProjectTools = class {
1246
1271
  }
1247
1272
  async runScaffoldPrune(workspacePath, preset) {
1248
1273
  if (!preset) {
1249
- throw createServicemeError6("invalid_params", "Expected scaffold preset.");
1274
+ throw createServicemeError7("invalid_params", "Expected scaffold preset.");
1250
1275
  }
1251
- const commands = [
1252
- `pnpm run scaffold:prune -- --preset ${preset} --project-root .`,
1253
- `pnpm run scaffold:init-metadata -- --preset ${preset} --project-root . --force`
1254
- ];
1276
+ await this.ensurePresetManifest(workspacePath, preset);
1277
+ const pruneCommand = `pnpm run scaffold:prune -- --preset ${preset} --project-root .`;
1278
+ const initMetadataCommand = `pnpm run scaffold:init-metadata -- --preset ${preset} --project-root . --force`;
1279
+ const commands = [pruneCommand, initMetadataCommand];
1255
1280
  for (const command of commands) {
1281
+ if (command === initMetadataCommand) {
1282
+ await this.ensurePresetManifest(workspacePath, preset);
1283
+ }
1256
1284
  await runCommand(command, {
1257
1285
  cwd: workspacePath,
1258
1286
  shell: true
@@ -1264,6 +1292,44 @@ var ProjectTools = class {
1264
1292
  commands
1265
1293
  };
1266
1294
  }
1295
+ async ensurePresetManifest(workspacePath, preset) {
1296
+ const presetManifestPath = path3.join(
1297
+ workspacePath,
1298
+ ".ms-scaffold",
1299
+ "presets",
1300
+ `${preset}.json`
1301
+ );
1302
+ if (await this.pathExists(presetManifestPath)) {
1303
+ return;
1304
+ }
1305
+ const projectModePath = path3.join(
1306
+ workspacePath,
1307
+ ".ms-scaffold",
1308
+ "project-mode.json"
1309
+ );
1310
+ if (!await this.pathExists(projectModePath)) {
1311
+ return;
1312
+ }
1313
+ const projectModeRaw = await fs3.readFile(projectModePath, "utf8");
1314
+ const projectMode = JSON.parse(projectModeRaw);
1315
+ const synthesizedPreset = {
1316
+ preset,
1317
+ selectedModules: Array.isArray(projectMode.selectedModules) ? projectMode.selectedModules : [],
1318
+ prunedModules: Array.isArray(projectMode.prunedModules) ? projectMode.prunedModules : [],
1319
+ exclude: [],
1320
+ recommendedFollowUps: [],
1321
+ managedFiles: [],
1322
+ mergeManagedFiles: [],
1323
+ userOwnedPaths: []
1324
+ };
1325
+ await fs3.mkdir(path3.dirname(presetManifestPath), { recursive: true });
1326
+ await fs3.writeFile(
1327
+ presetManifestPath,
1328
+ `${JSON.stringify(synthesizedPreset, null, 2)}
1329
+ `,
1330
+ "utf8"
1331
+ );
1332
+ }
1267
1333
  async findScripts(dir, extensions) {
1268
1334
  const results = [];
1269
1335
  let entries;
@@ -1884,7 +1950,7 @@ function getExecutor(taskType) {
1884
1950
  import { randomUUID } from "crypto";
1885
1951
  import * as fs8 from "fs";
1886
1952
  import * as path7 from "path";
1887
- import { createServicemeError as createServicemeError7 } from "@serviceme/devtools-protocol";
1953
+ import { createServicemeError as createServicemeError8 } from "@serviceme/devtools-protocol";
1888
1954
  var CONFIG_DIR3 = ".serviceme";
1889
1955
  var CONFIG_FILE = "scheduled-tasks.json";
1890
1956
  function emptyConfig() {
@@ -1895,7 +1961,7 @@ function isRecord(value) {
1895
1961
  }
1896
1962
  function requireNonEmptyString(payload, field, taskType) {
1897
1963
  if (!isRecord(payload) || typeof payload[field] !== "string" || !payload[field].trim()) {
1898
- throw createServicemeError7(
1964
+ throw createServicemeError8(
1899
1965
  "invalid_payload",
1900
1966
  `${taskType} payload ${field} is required`
1901
1967
  );
@@ -2543,6 +2609,7 @@ function matchCronField(field, value) {
2543
2609
  }
2544
2610
 
2545
2611
  // src/skills/SkillCatalogClient.ts
2612
+ import { createServicemeError as createServicemeError9 } from "@serviceme/devtools-protocol";
2546
2613
  var SkillCatalogClient = class {
2547
2614
  constructor(options = {}) {
2548
2615
  this.fetchImpl = options.fetchImpl ?? fetch;
@@ -2567,6 +2634,30 @@ var SkillCatalogClient = class {
2567
2634
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
2568
2635
  };
2569
2636
  }
2637
+ async downloadSkill(remoteId) {
2638
+ if (!this.baseUrl) {
2639
+ throw createServicemeError9(
2640
+ "workspace_not_found",
2641
+ "Skill catalog baseUrl is not configured."
2642
+ );
2643
+ }
2644
+ const response = await this.fetchImpl(
2645
+ `${this.baseUrl}/api/v1/marketplace/skills/download/${remoteId}`
2646
+ );
2647
+ if (!response.ok) {
2648
+ if (response.status === 404) {
2649
+ throw createServicemeError9(
2650
+ "not_found",
2651
+ `Skill '${remoteId}' not found`
2652
+ );
2653
+ }
2654
+ throw new Error(
2655
+ `Failed to download skill ${remoteId}: ${response.status}`
2656
+ );
2657
+ }
2658
+ const payload = await response.json();
2659
+ return payload.data?.files ?? [];
2660
+ }
2570
2661
  };
2571
2662
 
2572
2663
  // src/skills/SkillReconciler.ts
@@ -2703,6 +2794,22 @@ var SkillStore = class {
2703
2794
  return false;
2704
2795
  }
2705
2796
  }
2797
+ async writeSkillFiles(skillId, scope, files) {
2798
+ const root = scope === "workspace" ? path9.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE) : this.userSkillsRoot;
2799
+ const targetDir = path9.join(root, skillId);
2800
+ await this.fileSystem.mkdir(targetDir, { recursive: true });
2801
+ for (const file of files) {
2802
+ const filePath = path9.join(targetDir, file.path);
2803
+ await this.fileSystem.mkdir(path9.dirname(filePath), { recursive: true });
2804
+ await this.fileSystem.writeFile(filePath, file.content, "utf-8");
2805
+ if (file.executable) {
2806
+ try {
2807
+ await this.fileSystem.chmod(filePath, 493);
2808
+ } catch {
2809
+ }
2810
+ }
2811
+ }
2812
+ }
2706
2813
  };
2707
2814
  export {
2708
2815
  AgentCatalogClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serviceme/devtools-core",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Reusable Node.js core capabilities powering the SERVICEME CLI and integrations.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "repository": {
@@ -36,7 +36,7 @@
36
36
  "comment-json": "^4.5.1",
37
37
  "json5": "^2.2.3",
38
38
  "yauzl": "^3.2.0",
39
- "@serviceme/devtools-protocol": "0.1.6"
39
+ "@serviceme/devtools-protocol": "0.1.7"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^24",
@@ -48,6 +48,7 @@
48
48
  "scripts": {
49
49
  "watch": "tsup src/index.ts --format cjs,esm --dts --watch",
50
50
  "build": "tsup src/index.ts --format cjs,esm --dts",
51
+ "typecheck": "tsc --noEmit",
51
52
  "test": "pnpm run build && node --test test/**/*.test.js && node --import tsx --test src/**/__tests__/**/*.test.ts",
52
53
  "release:check": "pnpm run build && npm pack --dry-run",
53
54
  "pack": "npm pack",