@serviceme/devtools-core 1.0.0 → 2.0.0

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/agents/AgentCatalogClient.ts","../src/permissions/agent-permissions.ts","../src/agents/AgentReconciler.ts","../src/agents/AgentStore.ts","../src/auth/AccessControl.ts","../src/logger.ts","../src/auth/AuthStateManager.ts","../src/auth/ProviderRegistry.ts","../src/auth/AuthCore.ts","../src/auth/KeychainAuthTokenStore.ts","../src/auth/utils/githubUserEmail.ts","../src/auth/providers/GitHubAuthProvider.ts","../src/auth/providers/MicrosoftAuthProvider.ts","../src/copilot/doctor.ts","../src/process/runCommand.ts","../src/copilot/prompt.ts","../src/device/deviceAuth.ts","../src/device/Enroller.ts","../src/device/InstallationId.ts","../src/device/IdentityStore.ts","../src/paths/userHome.ts","../src/device/types.ts","../src/device/DeviceCore.ts","../src/drafts/index.ts","../src/skill-store/index.ts","../src/repo-layout/index.ts","../src/env/environmentInspector.ts","../src/git-client/index.ts","../src/git-client/types.ts","../src/image/imageTools.ts","../src/json/jsonTools.ts","../src/paths/serverProxyGlobal.ts","../src/phase5/bootstrap.ts","../src/project/projectTools.ts","../src/utils/fileUtils.ts","../src/repo-manager/index.ts","../src/repos/types.ts","../src/repos/default-repos.ts","../src/repos/loader.ts","../src/repos/store.ts","../src/scheduled-tasks/daemon/DaemonLogger.ts","../src/scheduled-tasks/daemon/PidManager.ts","../src/scheduled-tasks/daemon/SchedulerDaemonV2.ts","../src/scheduled-tasks/executors/GithubCopilotCliExecutor.ts","../src/scheduled-tasks/executors/timeout.ts","../src/scheduled-tasks/executors/HttpRequestExecutor.ts","../src/scheduled-tasks/executors/ShellExecutor.ts","../src/scheduled-tasks/executors/types.ts","../src/scheduled-tasks/executors/index.ts","../src/scheduled-tasks/TaskConfigManager.ts","../src/scheduled-tasks/TaskExecutionEngine.ts","../src/scheduled-tasks/TaskLogManager.ts","../src/scheduled-tasks/migration/MigrateToGlobal.ts","../src/scheduled-tasks/workspace-probe/WorkspaceProbe.ts","../src/skills/SkillCatalogClient.ts","../src/skills/SkillReconciler.ts","../src/skills/SkillStore.ts","../src/submit/index.ts","../src/submit/types.ts","../src/toolbox/sort.ts","../src/toolbox/ToolboxStore.ts","../src/toolbox/types.ts","../src/toolbox/ToolboxCore.ts"],"sourcesContent":["import type { AgentMarketplaceEntry } from \"@serviceme/devtools-protocol\";\nimport { createServicemeError } from \"@serviceme/devtools-protocol\";\nimport type { AgentDownloadFile } from \"./types\";\n\nexport interface AgentCatalog {\n\tagents: AgentMarketplaceEntry[];\n\tfetchedAt: string;\n}\n\nexport interface AgentCatalogClientOptions {\n\tfetchImpl?: typeof fetch;\n\tbaseUrl?: string;\n}\n\nexport class AgentCatalogClient {\n\tprivate readonly fetchImpl: typeof fetch;\n\tprivate readonly baseUrl?: string;\n\n\tconstructor(options: AgentCatalogClientOptions = {}) {\n\t\tthis.fetchImpl = options.fetchImpl ?? fetch;\n\t\tthis.baseUrl = options.baseUrl;\n\t}\n\n\tasync getCatalog(): Promise<AgentCatalog> {\n\t\tif (!this.baseUrl) {\n\t\t\tthrow createServicemeError(\"workspace_not_found\", \"Agent catalog baseUrl is not configured.\");\n\t\t}\n\n\t\tconst response = await this.fetchImpl(`${this.baseUrl}/api/v1/marketplace/agents`);\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Failed to fetch agents catalog: ${response.status}`);\n\t\t}\n\n\t\tconst data = (await response.json()) as {\n\t\t\tagents?: AgentMarketplaceEntry[];\n\t\t};\n\t\treturn {\n\t\t\tagents: data.agents ?? [],\n\t\t\tfetchedAt: new Date().toISOString(),\n\t\t};\n\t}\n\n\tasync downloadAgent(remoteId: string): Promise<AgentDownloadFile[]> {\n\t\tif (!this.baseUrl) {\n\t\t\tthrow createServicemeError(\"workspace_not_found\", \"Agent catalog baseUrl is not configured.\");\n\t\t}\n\n\t\tconst response = await this.fetchImpl(\n\t\t\t`${this.baseUrl}/api/v1/marketplace/agents/download/${remoteId}`\n\t\t);\n\t\tif (!response.ok) {\n\t\t\tif (response.status === 404) {\n\t\t\t\tthrow createServicemeError(\"not_found\", `Agent '${remoteId}' not found`);\n\t\t\t}\n\t\t\tthrow new Error(`Failed to download agent ${remoteId}: ${response.status}`);\n\t\t}\n\n\t\tconst payload = (await response.json()) as {\n\t\t\tdata?: { files?: AgentDownloadFile[] };\n\t\t};\n\t\treturn payload.data?.files ?? [];\n\t}\n}\n","import type { AgentToolPermission, AgentToolRiskLevel } from \"@serviceme/devtools-protocol\";\n\nexport const TOOL_RISK_MAP: Record<string, AgentToolRiskLevel> = {\n\tshell: \"high\",\n\tterminal: \"high\",\n\trun_in_terminal: \"high\",\n\texecution_subagent: \"high\",\n\tfilesystem: \"medium\",\n\tfetch: \"medium\",\n\tfetch_webpage: \"medium\",\n\tcreate_file: \"medium\",\n\treplace_string_in_file: \"medium\",\n\tmulti_replace_string_in_file: \"medium\",\n\tread_file: \"low\",\n\tsearch: \"low\",\n\tgrep_search: \"low\",\n\tfile_search: \"low\",\n\tsemantic_search: \"low\",\n\tlist_dir: \"low\",\n};\n\nconst FRONTMATTER_REGEX = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/;\nconst TOOLS_LINE_REGEX = /^tools:\\s*$/m;\nconst TOOLS_INLINE_REGEX = /^tools:\\s*\\[([^\\]]*)\\]/m;\nconst LIST_ITEM_REGEX = /^\\s*-\\s+(.+)$/;\n\nexport function parseAgentToolPermissions(content: string): AgentToolPermission[] {\n\tconst fmMatch = content.match(FRONTMATTER_REGEX);\n\tif (!fmMatch?.[1]) return [];\n\n\tconst frontmatter = fmMatch[1];\n\n\tconst inlineMatch = frontmatter.match(TOOLS_INLINE_REGEX);\n\tif (inlineMatch?.[1] != null) {\n\t\tconst raw = inlineMatch[1];\n\t\treturn raw\n\t\t\t.split(\",\")\n\t\t\t.map((t) => t.trim())\n\t\t\t.filter(Boolean)\n\t\t\t.map((tool) => ({\n\t\t\t\ttool,\n\t\t\t\triskLevel: TOOL_RISK_MAP[tool] ?? \"medium\",\n\t\t\t}));\n\t}\n\n\tconst blockMatch = frontmatter.match(TOOLS_LINE_REGEX);\n\tif (!blockMatch?.[0]) return [];\n\n\tconst toolsStartIndex = frontmatter.indexOf(blockMatch[0]) + blockMatch[0].length;\n\tconst remaining = frontmatter.slice(toolsStartIndex);\n\tconst lines = remaining.split(/\\r?\\n/);\n\tconst tools: AgentToolPermission[] = [];\n\n\tfor (const line of lines) {\n\t\tconst itemMatch = line.match(LIST_ITEM_REGEX);\n\t\tif (itemMatch?.[1]) {\n\t\t\tconst tool = itemMatch[1].trim();\n\t\t\ttools.push({ tool, riskLevel: TOOL_RISK_MAP[tool] ?? \"medium\" });\n\t\t} else if (line.trim() !== \"\" && !line.startsWith(\" \") && !line.startsWith(\"\\t\")) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn tools;\n}\n","import type {\n\tAgentMarketplaceEntry,\n\tAgentMutationRequest,\n\tAgentPermissionSummary,\n} from \"@serviceme/devtools-protocol\";\nimport { parseAgentToolPermissions, TOOL_RISK_MAP } from \"../permissions\";\n\ninterface AgentStoreLike {\n\tnormalizeRemoteAgentId(remoteId: string): string;\n\tlistWorkspaceAgentIds(): Promise<string[]>;\n\tlistUserAgentIds(): Promise<string[]>;\n}\n\ninterface AgentCatalogLike {\n\tagents: AgentMarketplaceEntry[];\n\tfetchedAt: string;\n}\n\ninterface AgentCatalogClientLike {\n\tgetCatalog(): Promise<AgentCatalogLike>;\n}\n\nexport interface AgentReconcilerDependencies {\n\tagentStore: AgentStoreLike;\n\tcatalogClient: AgentCatalogClientLike;\n}\n\nexport interface AgentMutateResult {\n\tstatus: \"success\" | \"blocked\" | \"requires_confirmation\";\n\tchanged: boolean;\n\tmessage?: string;\n\ttools?: string[];\n}\n\nexport class AgentReconciler {\n\tconstructor(private readonly deps: AgentReconcilerDependencies) {}\n\n\tasync mutate(request: AgentMutationRequest): Promise<AgentMutateResult> {\n\t\tif (request.targetScope !== \"workspace\" && request.targetScope !== \"user\") {\n\t\t\tthrow new Error(`Invalid target scope: ${String(request.targetScope)}`);\n\t\t}\n\n\t\tif (\n\t\t\trequest.action === \"uninstall\" ||\n\t\t\trequest.action === \"move\" ||\n\t\t\trequest.action === \"removeExternal\"\n\t\t) {\n\t\t\treturn {\n\t\t\t\tstatus: \"success\",\n\t\t\t\tchanged: true,\n\t\t\t\tmessage: `Agent ${request.action} completed.`,\n\t\t\t};\n\t\t}\n\n\t\tif (request.action !== \"install\") {\n\t\t\treturn {\n\t\t\t\tstatus: \"blocked\",\n\t\t\t\tchanged: false,\n\t\t\t\tmessage: `Agent action is not supported by bridge reconciler: ${request.action}`,\n\t\t\t};\n\t\t}\n\n\t\tconst catalog = await this.deps.catalogClient.getCatalog();\n\t\tconst remoteAgent = catalog.agents.find(\n\t\t\t(agent) => this.deps.agentStore.normalizeRemoteAgentId(agent.id) === request.agentId\n\t\t);\n\n\t\tif (!remoteAgent) {\n\t\t\treturn {\n\t\t\t\tstatus: \"blocked\",\n\t\t\t\tchanged: false,\n\t\t\t\tmessage: \"Agent not found in catalog.\",\n\t\t\t};\n\t\t}\n\n\t\tif (!request.confirmed && this.hasHighRiskTool(remoteAgent.tools)) {\n\t\t\treturn {\n\t\t\t\tstatus: \"requires_confirmation\",\n\t\t\t\tchanged: false,\n\t\t\t\tmessage: \"This agent uses high-risk tools that require confirmation.\",\n\t\t\t\ttools: remoteAgent.tools,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tchanged: true,\n\t\t\tmessage: \"Agent installed.\",\n\t\t};\n\t}\n\n\tgetPermissionSummary(\n\t\tagentId: string,\n\t\tagentName: string,\n\t\tcontent: string\n\t): AgentPermissionSummary {\n\t\tconst tools = parseAgentToolPermissions(content);\n\t\treturn {\n\t\t\tagentId,\n\t\t\tagentName,\n\t\t\ttools,\n\t\t\thighRiskCount: tools.filter((tool) => tool.riskLevel === \"high\").length,\n\t\t\tmediumRiskCount: tools.filter((tool) => tool.riskLevel === \"medium\").length,\n\t\t\tlowRiskCount: tools.filter((tool) => tool.riskLevel === \"low\").length,\n\t\t};\n\t}\n\n\tprivate hasHighRiskTool(tools: string[]): boolean {\n\t\treturn tools.some((tool) => TOOL_RISK_MAP[tool] === \"high\");\n\t}\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type {\n\tAgentDownloadFile,\n\tAgentStoreFileSystem,\n\tAgentStoreOptions,\n\tAgentsStateFile,\n\tInstalledAgent,\n} from \"./types\";\n\nconst WORKSPACE_AGENTS_ROOT_RELATIVE = \".github/agents\";\nconst WORKSPACE_AGENTS_STATE_RELATIVE = \".github/.serviceme-agents.yml\";\nconst LEGACY_WORKSPACE_AGENTS_STATE_RELATIVE = \".github/.ms-devtools-agents.yml\";\nconst SAFE_LOCAL_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;\n\nfunction assertSafeLocalAgentId(agentId: string): string {\n\tif (\n\t\ttypeof agentId !== \"string\" ||\n\t\tagentId.length === 0 ||\n\t\tagentId === \".\" ||\n\t\tagentId === \"..\" ||\n\t\tagentId.includes(\"/\") ||\n\t\tagentId.includes(\"\\\\\") ||\n\t\t!SAFE_LOCAL_ID_PATTERN.test(agentId)\n\t) {\n\t\tthrow new Error(`Invalid agent id: ${agentId}`);\n\t}\n\n\treturn agentId;\n}\n\nexport class AgentStore {\n\tprivate readonly workspacePath: string;\n\tprivate readonly userAgentsRoot: string;\n\tprivate readonly fileSystem: AgentStoreFileSystem;\n\tprivate readonly schemaVersion: number;\n\n\tconstructor(options: AgentStoreOptions) {\n\t\tthis.workspacePath = options.workspacePath;\n\t\tthis.userAgentsRoot = options.userAgentsRoot;\n\t\tthis.fileSystem = options.fileSystem ?? fs;\n\t\tthis.schemaVersion = options.schemaVersion ?? 1;\n\t}\n\n\tnormalizeRemoteAgentId(remoteId: string): string {\n\t\tif (remoteId.startsWith(\"official/\")) {\n\t\t\treturn assertSafeLocalAgentId(remoteId.slice(\"official/\".length));\n\t\t}\n\t\tif (remoteId.startsWith(\"community/\")) {\n\t\t\tconst lastSlash = remoteId.lastIndexOf(\"/\");\n\t\t\treturn assertSafeLocalAgentId(remoteId.slice(lastSlash + 1));\n\t\t}\n\t\treturn assertSafeLocalAgentId(remoteId);\n\t}\n\n\tgetWorkspaceAgentsRootPath(): string {\n\t\treturn WORKSPACE_AGENTS_ROOT_RELATIVE;\n\t}\n\n\tgetWorkspaceStateFilePath(): string {\n\t\treturn WORKSPACE_AGENTS_STATE_RELATIVE;\n\t}\n\n\tgetUserAgentsRootPath(): string {\n\t\treturn this.userAgentsRoot;\n\t}\n\n\tasync listWorkspaceAgentIds(): Promise<string[]> {\n\t\treturn this.listAgentIds(path.join(this.workspacePath, WORKSPACE_AGENTS_ROOT_RELATIVE));\n\t}\n\n\tasync listUserAgentIds(): Promise<string[]> {\n\t\treturn this.listAgentIds(this.userAgentsRoot);\n\t}\n\n\tasync readState(): Promise<AgentsStateFile | null> {\n\t\tawait this.migrateLegacyState();\n\t\ttry {\n\t\t\tconst raw = await this.fileSystem.readFile(\n\t\t\t\tpath.join(this.workspacePath, WORKSPACE_AGENTS_STATE_RELATIVE),\n\t\t\t\t\"utf-8\"\n\t\t\t);\n\t\t\tconst parsed = JSON.parse(raw) as AgentsStateFile;\n\t\t\tif (typeof parsed.schemaVersion !== \"number\" || !Array.isArray(parsed.installedAgents)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn parsed;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * One-time migration: the workspace agents state file used to be named\n\t * `.github/.ms-devtools-agents.yml`. If the new `.serviceme-agents.yml`\n\t * doesn't exist yet but the legacy file does, copy its content forward\n\t * so existing installed-agent state isn't silently lost.\n\t */\n\tprivate async migrateLegacyState(): Promise<void> {\n\t\tconst newPath = path.join(this.workspacePath, WORKSPACE_AGENTS_STATE_RELATIVE);\n\t\tconst legacyPath = path.join(this.workspacePath, LEGACY_WORKSPACE_AGENTS_STATE_RELATIVE);\n\t\ttry {\n\t\t\tawait this.fileSystem.readFile(newPath, \"utf-8\");\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// new file missing — check the legacy path below\n\t\t}\n\t\ttry {\n\t\t\tconst legacyContent = await this.fileSystem.readFile(legacyPath, \"utf-8\");\n\t\t\tawait this.fileSystem.mkdir(path.dirname(newPath), { recursive: true });\n\t\t\tawait this.fileSystem.writeFile(newPath, legacyContent, \"utf-8\");\n\t\t} catch {\n\t\t\t// legacy file doesn't exist either — nothing to migrate\n\t\t}\n\t}\n\n\tasync writeState(state: AgentsStateFile): Promise<void> {\n\t\tconst statePath = path.join(this.workspacePath, WORKSPACE_AGENTS_STATE_RELATIVE);\n\t\tawait this.fileSystem.mkdir(path.dirname(statePath), { recursive: true });\n\t\tawait this.fileSystem.writeFile(statePath, JSON.stringify(state, null, 2), \"utf-8\");\n\t}\n\n\tasync addInstalledAgent(entry: InstalledAgent): Promise<void> {\n\t\tconst state =\n\t\t\t(await this.readState()) ??\n\t\t\t({\n\t\t\t\tschemaVersion: this.schemaVersion,\n\t\t\t\tinstalledAgents: [],\n\t\t\t} as AgentsStateFile);\n\t\tstate.installedAgents = state.installedAgents.filter((agent) => agent.id !== entry.id);\n\t\tstate.installedAgents.push(entry);\n\t\tawait this.writeState(state);\n\t}\n\n\tasync removeInstalledAgent(agentId: string): Promise<void> {\n\t\tconst state = await this.readState();\n\t\tif (!state) {\n\t\t\treturn;\n\t\t}\n\t\tstate.installedAgents = state.installedAgents.filter((agent) => agent.id !== agentId);\n\t\tawait this.writeState(state);\n\t}\n\n\tasync writeAgentFiles(\n\t\tagentId: string,\n\t\tscope: \"workspace\" | \"user\",\n\t\tfiles: AgentDownloadFile[]\n\t): Promise<void> {\n\t\tconst root =\n\t\t\tscope === \"workspace\"\n\t\t\t\t? path.join(this.workspacePath, WORKSPACE_AGENTS_ROOT_RELATIVE)\n\t\t\t\t: this.userAgentsRoot;\n\t\tconst firstFile = files[0];\n\t\tconst isSingleFlatFile =\n\t\t\tfiles.length === 1 &&\n\t\t\tfirstFile !== undefined &&\n\t\t\tfirstFile.path === `${agentId}.agent.md` &&\n\t\t\t!firstFile.path.includes(\"/\");\n\t\tconst targetDir = isSingleFlatFile ? root : path.join(root, agentId);\n\t\tawait this.fileSystem.mkdir(targetDir, { recursive: true });\n\n\t\tfor (const file of files) {\n\t\t\tconst filePath = path.join(targetDir, file.path);\n\t\t\tawait this.fileSystem.mkdir(path.dirname(filePath), { recursive: true });\n\t\t\tawait this.fileSystem.writeFile(filePath, file.content, \"utf-8\");\n\t\t\tif (file.executable) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.fileSystem.chmod(filePath, 0o755);\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore chmod failures on unsupported environments\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async listAgentIds(dir: string): Promise<string[]> {\n\t\ttry {\n\t\t\tconst entries = await this.fileSystem.readdir(dir, {\n\t\t\t\twithFileTypes: true,\n\t\t\t});\n\t\t\tconst ids: string[] = [];\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (entry.name.startsWith(\".\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (entry.isDirectory()) {\n\t\t\t\t\tids.push(entry.name);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (entry.isFile() && entry.name.endsWith(\".agent.md\")) {\n\t\t\t\t\tids.push(entry.name.replace(/\\.agent\\.md$/, \"\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ids.sort();\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n}\n","/**\n * AccessControl — Pure logic for org-membership gating.\n *\n * Ported from `apps/extension/src/services/auth/AccessControlService.ts`\n * but stripped of all VSCode dependencies:\n * - No `vscode.window.showWarningMessage` — callers render the notice.\n * - No `vscode.context.globalState` — callers inject a `KeyValueStore`.\n * - No `vscode.Event` — uses a plain `onDidChange` callback.\n *\n * The class is generic over the membership-fetcher function so it can\n * run identically with the CLI's `@serviceme/devtools-shared` `getGitHubOrgMembership`\n * or with the Extension's bundled copy. ADL-003 forbids Core from\n * importing `@serviceme/devtools-shared` directly, so the org membership\n * result type is defined locally and the runtime adapter wraps the\n * shared helper at the boundary (Phase 5.3+).\n *\n * Refs:\n * - 4.功能规划.md §2.1 — `AccessControl.ts 从 AccessControlService 改写`\n * - ADL-003 — `@serviceme/devtools-shared` boundary (Core MUST NOT import shared)\n */\n\nimport type { AuthAccountMeta, AuthProvider } from \"@serviceme/devtools-protocol\";\n\n/** Core-local mirror of `GitHubOrgMembershipCheckResult` (defined in `@serviceme/devtools-shared`). */\nexport interface CoreOrgMembershipResult {\n\tstatus: \"active\" | \"pending\" | \"not_member\" | \"unknown\";\n\thttpStatus?: number;\n\trole?: string;\n\tdirectMembership?: boolean;\n}\n\n/**\n * Upstream org-membership fetcher. CLI / Extension adapters wrap\n * `getGitHubOrgMembership` (or the local extension copy) to match\n * this signature.\n */\nexport type OrgMembershipFetcher = (token: string, org: string) => Promise<CoreOrgMembershipResult>;\n\n/** Pluggable key/value store. Extension injects `globalState`-backed impl; CLI injects a file-backed impl. */\nexport interface KeyValueStore {\n\tget<T>(key: string): T | undefined;\n\tupdate<T>(key: string, value: T): Promise<void>;\n}\n\nexport interface AccessCheckResult {\n\tallowed: boolean;\n\treason?: \"not_authenticated\" | \"not_member\";\n\tusername?: string;\n}\n\nexport interface AccessControlOptions {\n\torg: string;\n\t/** Domains that auto-qualify Microsoft users without a GitHub link. */\n\tmicrosoftEmailDomains?: readonly string[];\n\t/** TTL for in-memory membership cache (ms). */\n\tcacheTtlMs?: number;\n\t/** TTL for persisted membership cache (ms). */\n\tpersistentCacheTtlMs?: number;\n\t/** Tokens whose bearer can be supplied to `OrgMembershipFetcher`. */\n\ttokenFetcher: (provider: AuthProvider) => Promise<string | null>;\n\t/** Upstream org-membership fetcher (typically `getGitHubOrgMembership` from `@serviceme/devtools-shared`). */\n\torgFetcher: OrgMembershipFetcher;\n\t/** Clock seam for tests. */\n\tnow?: () => number;\n}\n\nconst DEFAULT_CACHE_TTL_MS = 10 * 60 * 1000;\nconst DEFAULT_PERSISTENT_CACHE_TTL_MS = 24 * 60 * 60 * 1000;\nconst KEY_GRANTED_USERS = \"msDevTools.accessControl.grantedUsers\";\nconst KEY_MEMBERSHIP_CACHE = \"msDevTools.accessControl.membershipCache\";\nconst KEY_SERVER_IN_ORG = \"msDevTools.accessControl.serverInOrg\";\n\ninterface CacheEntry {\n\tisMember: boolean;\n\tat: number;\n}\n\ninterface PersistentCache {\n\t[username: string]: CacheEntry;\n}\n\nexport class AccessControl {\n\tprivate readonly memCache = new Map<string, CacheEntry>();\n\tprivate readonly serverInOrgMem = new Map<string, boolean>();\n\tprivate readonly listeners = new Set<() => void>();\n\tprivate readonly opts: Required<\n\t\tOmit<AccessControlOptions, \"now\" | \"tokenFetcher\" | \"microsoftEmailDomains\" | \"orgFetcher\">\n\t> & {\n\t\ttokenFetcher: AccessControlOptions[\"tokenFetcher\"];\n\t\torgFetcher: OrgMembershipFetcher;\n\t\tmicrosoftEmailDomains: readonly string[];\n\t\tnow: () => number;\n\t};\n\n\tconstructor(\n\t\tprivate readonly kv: KeyValueStore,\n\t\toptions: AccessControlOptions\n\t) {\n\t\tthis.opts = {\n\t\t\torg: options.org,\n\t\t\ttokenFetcher: options.tokenFetcher,\n\t\t\torgFetcher: options.orgFetcher,\n\t\t\tmicrosoftEmailDomains: options.microsoftEmailDomains ?? [],\n\t\t\tcacheTtlMs: options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS,\n\t\t\tpersistentCacheTtlMs: options.persistentCacheTtlMs ?? DEFAULT_PERSISTENT_CACHE_TTL_MS,\n\t\t\tnow: options.now ?? (() => Date.now()),\n\t\t};\n\t\t// Hydrate `serverInOrg` from persistent storage so the gate can run instantly on restart.\n\t\tconst persistedServerInOrg = this.kv.get<Record<string, boolean>>(KEY_SERVER_IN_ORG);\n\t\tif (persistedServerInOrg) {\n\t\t\tfor (const [key, value] of Object.entries(persistedServerInOrg)) {\n\t\t\t\tthis.serverInOrgMem.set(key, value);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Subscribe to inOrg changes (used by extension to refresh access-gated UI). */\n\tonDidChange(listener: () => void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => this.listeners.delete(listener);\n\t}\n\n\t/** Full check: requires authentication + org membership / admin grant. */\n\tasync checkAccess(user: AuthAccountMeta | null): Promise<AccessCheckResult> {\n\t\tif (!user) {\n\t\t\treturn { allowed: false, reason: \"not_authenticated\" };\n\t\t}\n\n\t\t// Microsoft users: server-confirmed inOrg takes priority over GitHub link.\n\t\tif (user.provider === \"microsoft\" && user.login) {\n\t\t\tconst inOrg = this.serverInOrgMem.get(user.login.toLowerCase());\n\t\t\tif (inOrg === true) return { allowed: true };\n\t\t\tif (inOrg === false) {\n\t\t\t\treturn { allowed: false, reason: \"not_member\", username: user.login };\n\t\t\t}\n\t\t}\n\n\t\t// Find a GitHub account if available (linked for Microsoft users).\n\t\tconst githubAccount = user.provider === \"github\" ? user : null;\n\t\tif (!githubAccount && user.provider === \"microsoft\" && user.login) {\n\t\t\tconst [, domain = \"\"] = user.login.split(\"@\");\n\t\t\tconst isDomainMember =\n\t\t\t\tdomain.length > 0 && this.opts.microsoftEmailDomains.includes(domain.toLowerCase());\n\t\t\tif (isDomainMember) return { allowed: true };\n\t\t\t// No GitHub link + unknown domain — deny until the next server-confirmed claim.\n\t\t\treturn { allowed: false, reason: \"not_member\", username: user.login };\n\t\t}\n\t\tif (!githubAccount) {\n\t\t\t// No identity at all — treat as authenticated for now (caller may have other gating).\n\t\t\treturn { allowed: true };\n\t\t}\n\n\t\tconst accessUsername = githubAccount.login ?? user.login ?? \"\";\n\t\tif (accessUsername.length === 0) {\n\t\t\treturn { allowed: false, reason: \"not_member\", username: \"\" };\n\t\t}\n\n\t\t// Admin-granted users bypass the org check.\n\t\tconst granted = this.getGrantedUsers();\n\t\tif (granted.includes(accessUsername.toLowerCase())) {\n\t\t\treturn { allowed: true };\n\t\t}\n\n\t\tconst isMember = await this.checkOrgMembership(accessUsername);\n\t\treturn isMember\n\t\t\t? { allowed: true }\n\t\t\t: { allowed: false, reason: \"not_member\", username: accessUsername };\n\t}\n\n\t/** Admin: grant access to a GitHub username (bypasses org check). */\n\tasync grantAccess(username: string): Promise<void> {\n\t\tconst list = this.getGrantedUsers();\n\t\tconst key = username.toLowerCase();\n\t\tif (!list.includes(key)) {\n\t\t\tlist.push(key);\n\t\t\tawait this.kv.update(KEY_GRANTED_USERS, list);\n\t\t}\n\t}\n\n\t/** Admin: revoke a previously granted access. */\n\tasync revokeAccess(username: string): Promise<void> {\n\t\tconst updated = this.getGrantedUsers().filter((u) => u !== username.toLowerCase());\n\t\tawait this.kv.update(KEY_GRANTED_USERS, updated);\n\t}\n\n\t/** Snapshot of all admin-granted usernames (lowercased). */\n\tgetGrantedUsers(): string[] {\n\t\treturn this.kv.get<string[]>(KEY_GRANTED_USERS) ?? [];\n\t}\n\n\t/** Cached membership lookup with both in-memory + persistent layers. */\n\tasync checkOrgMembership(username: string): Promise<boolean> {\n\t\tconst now = this.opts.now();\n\n\t\t// 1. In-memory cache.\n\t\tconst inMem = this.memCache.get(username);\n\t\tif (inMem && now - inMem.at < this.opts.cacheTtlMs) {\n\t\t\treturn inMem.isMember;\n\t\t}\n\n\t\t// 2. Persistent cache.\n\t\tconst persistent = this.kv.get<PersistentCache>(KEY_MEMBERSHIP_CACHE) ?? {};\n\t\tconst persistentEntry = persistent[username];\n\t\tif (persistentEntry && now - persistentEntry.at < this.opts.persistentCacheTtlMs) {\n\t\t\tthis.memCache.set(username, persistentEntry);\n\t\t\treturn persistentEntry.isMember;\n\t\t}\n\n\t\t// 3. Hit upstream GitHub API via the injected fetcher.\n\t\tconst token = await this.opts.tokenFetcher(\"github\");\n\t\tif (!token) return false;\n\n\t\ttry {\n\t\t\tconst result = await this.opts.orgFetcher(token, this.opts.org);\n\t\t\tconst decision = this.resolveDecision(username, result);\n\t\t\tif (typeof decision === \"boolean\") {\n\t\t\t\tthis.updateCache(username, decision);\n\t\t\t\treturn decision;\n\t\t\t}\n\t\t\t// Indeterminate — fail-open.\n\t\t\treturn true;\n\t\t} catch {\n\t\t\t// Fail-open on transient network errors.\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t/** Server-confirmed inOrg status (set by device claim flow). */\n\tsetServerInOrg(username: string, inOrg: boolean): void {\n\t\tconst key = username.toLowerCase();\n\t\tthis.serverInOrgMem.set(key, inOrg);\n\t\tconst persisted = this.kv.get<Record<string, boolean>>(KEY_SERVER_IN_ORG) ?? {};\n\t\tpersisted[key] = inOrg;\n\t\tvoid this.kv.update(KEY_SERVER_IN_ORG, persisted);\n\t\tthis.notifyListeners();\n\t}\n\n\t/** Inspect server-confirmed inOrg for a username; returns undefined when unknown. */\n\tgetServerInOrg(username: string): boolean | undefined {\n\t\treturn this.serverInOrgMem.get(username.toLowerCase());\n\t}\n\n\tprivate resolveDecision(\n\t\t_username: string,\n\t\tmembership: CoreOrgMembershipResult\n\t): boolean | undefined {\n\t\tif (membership.status === \"active\" || membership.status === \"pending\") return true;\n\t\tif (membership.status === \"not_member\") return false;\n\t\treturn undefined;\n\t}\n\n\tprivate updateCache(username: string, isMember: boolean): void {\n\t\tconst at = this.opts.now();\n\t\tthis.memCache.set(username, { isMember, at });\n\t\tconst persistent = this.kv.get<PersistentCache>(KEY_MEMBERSHIP_CACHE) ?? {};\n\t\tpersistent[username] = { isMember, at };\n\t\tvoid this.kv.update(KEY_MEMBERSHIP_CACHE, persistent);\n\t}\n\n\tprivate notifyListeners(): void {\n\t\tfor (const listener of this.listeners) {\n\t\t\ttry {\n\t\t\t\tlistener();\n\t\t\t} catch {\n\t\t\t\t// listener errors must not propagate; AccessControl is best-effort.\n\t\t\t}\n\t\t}\n\t}\n}\n","export interface ServiceMeLogger {\n\tdebug(message: string, ...args: unknown[]): void;\n\tinfo(message: string, ...args: unknown[]): void;\n\twarn(message: string, ...args: unknown[]): void;\n\terror(message: string, ...args: unknown[]): void;\n}\n\nexport const noopLogger: ServiceMeLogger = {\n\tdebug() {},\n\tinfo() {},\n\twarn() {},\n\terror() {},\n};\n\nfunction formatArgs(args: unknown[]): string {\n\treturn args\n\t\t.map((arg) => {\n\t\t\tif (typeof arg === \"string\") {\n\t\t\t\treturn arg;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\treturn JSON.stringify(arg);\n\t\t\t} catch {\n\t\t\t\treturn String(arg);\n\t\t\t}\n\t\t})\n\t\t.join(\" \");\n}\n\nexport function createConsoleLogger(prefix = \"serviceme\"): ServiceMeLogger {\n\treturn {\n\t\tdebug(message: string, ...args: unknown[]) {\n\t\t\tprocess.stderr.write(`[${prefix}] DEBUG ${message} ${formatArgs(args)}\\n`);\n\t\t},\n\t\tinfo(message: string, ...args: unknown[]) {\n\t\t\tprocess.stderr.write(`[${prefix}] INFO ${message} ${formatArgs(args)}\\n`);\n\t\t},\n\t\twarn(message: string, ...args: unknown[]) {\n\t\t\tprocess.stderr.write(`[${prefix}] WARN ${message} ${formatArgs(args)}\\n`);\n\t\t},\n\t\terror(message: string, ...args: unknown[]) {\n\t\t\tprocess.stderr.write(`[${prefix}] ERROR ${message} ${formatArgs(args)}\\n`);\n\t\t},\n\t};\n}\n","/**\n * AuthStateManager — In-memory mirror of the persisted auth state.\n *\n * Holds the multi-account map (provider + accountId keyed) plus the\n * \"active\" provider/account pair. Emits change events via Node's\n * built-in `events.EventEmitter` so listeners stay decoupled.\n *\n * Important: this class does NOT touch `vscode.SecretStorage` or any\n * file — it only keeps the metadata (`AuthAccountMeta`) and the\n * `activeProvider` selection. Token bytes are looked up via the\n * `KeychainAuthTokenStore` on demand. This separation is what lets\n * `AuthCore` run identically in CLI + Extension.\n *\n * Refs:\n * - 4.功能规划.md §2.1 — `AuthStateManager.ts events.EventEmitter, NO VSCode dep`\n * - ADL-004 — token bytes never enter Core state\n */\n\nimport { EventEmitter } from \"node:events\";\n\nimport type { AuthAccountMeta, AuthProvider, AuthStatus } from \"@serviceme/devtools-protocol\";\n\nexport interface AuthStateManagerEvents {\n\tchange: () => void;\n}\n\nexport interface AuthStateManagerOptions {\n\t/** EventEmitter listener cap; defaults to 32 (Node default) but raised for hot test paths. */\n\tmaxListeners?: number;\n}\n\nexport class AuthStateManager {\n\tprivate readonly emitter = new EventEmitter();\n\tprivate accounts: AuthAccountMeta[] = [];\n\tprivate activeProvider: AuthProvider | null = null;\n\tprivate activeAccountId: string | null = null;\n\tprivate lastError: string | null = null;\n\n\tconstructor(opts: AuthStateManagerOptions = {}) {\n\t\tif (opts.maxListeners !== undefined) {\n\t\t\tthis.emitter.setMaxListeners(opts.maxListeners);\n\t\t}\n\t}\n\n\t/** Subscribe to state-change events. Returns a disposer. */\n\tonDidChange(listener: () => void): () => void {\n\t\tthis.emitter.on(\"change\", listener);\n\t\treturn () => this.emitter.off(\"change\", listener);\n\t}\n\n\t/** Snapshot of all known accounts (immutable copy). */\n\tlistAccounts(): AuthAccountMeta[] {\n\t\treturn [...this.accounts];\n\t}\n\n\t/** Find an account by `(provider, accountId)` tuple; returns `null` when absent. */\n\tfindAccount(provider: AuthProvider, accountId: string): AuthAccountMeta | null {\n\t\treturn this.accounts.find((a) => a.provider === provider && a.id === accountId) ?? null;\n\t}\n\n\t/** First account for the requested provider — used for \"switch to GitHub\" UX. */\n\tfindFirstForProvider(provider: AuthProvider): AuthAccountMeta | null {\n\t\treturn this.accounts.find((a) => a.provider === provider) ?? null;\n\t}\n\n\t/** Insert or update an account entry. New accounts land at the head of the list. */\n\tupsertAccount(meta: AuthAccountMeta): void {\n\t\tconst idx = this.accounts.findIndex((a) => a.provider === meta.provider && a.id === meta.id);\n\t\tif (idx >= 0) {\n\t\t\tthis.accounts[idx] = meta;\n\t\t} else {\n\t\t\tthis.accounts.unshift(meta);\n\t\t}\n\t\tthis.fire();\n\t}\n\n\t/** Remove an account entry. Returns `true` when an entry was removed. */\n\tremoveAccount(provider: AuthProvider, accountId: string): boolean {\n\t\tconst before = this.accounts.length;\n\t\tthis.accounts = this.accounts.filter((a) => !(a.provider === provider && a.id === accountId));\n\t\tconst removed = this.accounts.length !== before;\n\t\t// If we removed the active provider's account, clear the active marker.\n\t\tif (removed && this.activeProvider === provider && this.activeAccountId === accountId) {\n\t\t\tconst stillHasSameProvider = this.accounts.some((a) => a.provider === provider);\n\t\t\tif (!stillHasSameProvider) {\n\t\t\t\tthis.activeProvider = null;\n\t\t\t\tthis.activeAccountId = null;\n\t\t\t} else {\n\t\t\t\tconst next = this.accounts.find((a) => a.provider === provider);\n\t\t\t\tif (next) {\n\t\t\t\t\tthis.activeAccountId = next.id;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (removed) this.fire();\n\t\treturn removed;\n\t}\n\n\t/** Set the active provider. When `accountId` is omitted, picks the first account for that provider. */\n\tsetActive(provider: AuthProvider, accountId?: string): boolean {\n\t\tconst match =\n\t\t\taccountId !== undefined\n\t\t\t\t? this.accounts.find((a) => a.provider === provider && a.id === accountId)\n\t\t\t\t: this.accounts.find((a) => a.provider === provider);\n\t\tif (!match) return false;\n\t\tthis.activeProvider = provider;\n\t\tthis.activeAccountId = match.id;\n\t\tthis.fire();\n\t\treturn true;\n\t}\n\n\t/** Currently active provider — `null` when no session is active. */\n\tgetActiveProvider(): AuthProvider | null {\n\t\treturn this.activeProvider;\n\t}\n\n\t/** Currently active account metadata — `null` when none. */\n\tgetActiveAccount(): AuthAccountMeta | null {\n\t\tif (!this.activeProvider || !this.activeAccountId) return null;\n\t\treturn (\n\t\t\tthis.accounts.find(\n\t\t\t\t(a) => a.provider === this.activeProvider && a.id === this.activeAccountId\n\t\t\t) ?? null\n\t\t);\n\t}\n\n\t/** True when at least one provider has an account entry. */\n\thasAnySession(): boolean {\n\t\treturn this.accounts.length > 0 && this.activeProvider !== null;\n\t}\n\n\t/** Snapshot of the state for `auth.status` and bridge serialization. */\n\tgetStatus(): AuthStatus {\n\t\tconst grouped: Partial<Record<AuthProvider, AuthAccountMeta>> = {};\n\t\tfor (const account of this.accounts) {\n\t\t\tgrouped[account.provider] = account;\n\t\t}\n\t\treturn {\n\t\t\tactiveProvider: this.activeProvider,\n\t\t\taccounts: grouped,\n\t\t\thasAnySession: this.hasAnySession(),\n\t\t\tlastError: this.lastError ?? undefined,\n\t\t};\n\t}\n\n\t/** Record a non-fatal error from the last login/refresh attempt. */\n\trecordError(message: string): void {\n\t\tthis.lastError = message;\n\t\tthis.fire();\n\t}\n\n\t/** Clear the recorded error (e.g. after a successful login). */\n\tclearError(): void {\n\t\tif (this.lastError === null) return;\n\t\tthis.lastError = null;\n\t\tthis.fire();\n\t}\n\n\t/** Drop every account — used by `auth.logout` with no provider. */\n\tclearAll(): void {\n\t\tthis.accounts = [];\n\t\tthis.activeProvider = null;\n\t\tthis.activeAccountId = null;\n\t\tthis.lastError = null;\n\t\tthis.fire();\n\t}\n\n\tprivate fire(): void {\n\t\tthis.emitter.emit(\"change\");\n\t}\n}\n","/**\n * ProviderRegistry — Maps `AuthProvider` ids to `IAuthProvider` instances.\n *\n * `AuthCore` looks up providers by `AuthProvider` enum (string union\n * per the protocol). The registry is mutable so that callers can\n * replace a provider (e.g. swap a real keyring-backed implementation\n * in tests), but providers must be registered before `AuthCore.login()`\n * is called.\n *\n * Refs:\n * - 4.功能规划.md §2.1 — `ProviderRegistry.ts`\n */\n\nimport type { AuthProvider } from \"@serviceme/devtools-protocol\";\n\nimport type { IAuthProvider } from \"./providers/IAuthProvider\";\n\nexport class ProviderRegistry {\n\tprivate readonly providers = new Map<AuthProvider, IAuthProvider>();\n\n\t/** Register or replace a provider implementation. */\n\tregister(provider: IAuthProvider): void {\n\t\tthis.providers.set(provider.providerId, provider);\n\t}\n\n\t/** Look up a registered provider by id; throws when missing. */\n\tget(providerId: AuthProvider): IAuthProvider {\n\t\tconst p = this.providers.get(providerId);\n\t\tif (!p) {\n\t\t\tthrow new Error(`Auth provider not registered: ${providerId}`);\n\t\t}\n\t\treturn p;\n\t}\n\n\t/** Non-throwing lookup; returns `undefined` when the provider is unknown. */\n\ttryGet(providerId: AuthProvider): IAuthProvider | undefined {\n\t\treturn this.providers.get(providerId);\n\t}\n\n\t/** Return all registered provider ids — used by `auth.status` and bridge capability hints. */\n\tlist(): AuthProvider[] {\n\t\treturn [...this.providers.keys()];\n\t}\n\n\t/** True when a provider has been registered for this id. */\n\thas(providerId: AuthProvider): boolean {\n\t\treturn this.providers.has(providerId);\n\t}\n}\n","/**\n * AuthCore — Main entry for the auth domain.\n *\n * Owns the `ProviderRegistry` + `AuthStateManager` + `KeychainAuthTokenStore`.\n * Provides a small, side-effectful surface that CLI / Extension / Bridge\n * handlers can call:\n *\n * - `login(provider)` → drives Device Flow, persists token via store.\n * - `logout(provider?)` → clears the active session (and token bytes).\n * - `status()` → snapshot for `auth.status`.\n * - `whoami()` → minimal identity for `auth.whoami`.\n * - `switchProvider(p)` → flip the active provider (multi-account).\n *\n * Core NEVER holds token bytes past the `KeychainAuthTokenStore.set()`\n * boundary — callers pass the token to the store immediately, then\n * drop the local reference. Per ADL-004 the token bytes only live in\n * (a) SecretStorage / (b) keyring / (c) HTTP Authorization header.\n *\n * Refs:\n * - 4.功能规划.md §2.1 — `AuthCore.ts 主入口`\n * - ADL-002 — Device Flow OAuth\n * - ADL-004 — Auth token storage\n */\n\nimport type {\n\tAuthAccountMeta,\n\tAuthLoginResult,\n\tAuthProvider,\n\tAuthStatus,\n\tAuthSwitchResult,\n\tAuthWhoamiResult,\n} from \"@serviceme/devtools-protocol\";\nimport { noopLogger, type ServiceMeLogger } from \"../logger\";\nimport type { AccessCheckResult, AccessControl } from \"./AccessControl\";\nimport { AuthStateManager } from \"./AuthStateManager\";\nimport type { KeychainAuthTokenStore } from \"./KeychainAuthTokenStore\";\nimport { ProviderRegistry } from \"./ProviderRegistry\";\nimport type { IAuthProvider, IAuthProviderSession } from \"./providers/IAuthProvider\";\n\nexport interface AuthCoreOptions {\n\tproviders: IAuthProvider[];\n\ttokenStore: KeychainAuthTokenStore;\n\taccessControl?: AccessControl;\n\tstateManager?: AuthStateManager;\n\t/** Diagnostic logger for login/logout lifecycle events (never receives token bytes). */\n\tlogger?: ServiceMeLogger;\n}\n\n/**\n * Callback invoked once the user finishes the device-flow handshake but\n * BEFORE the token is written to the keychain. Lets the caller display\n * the `userCode` + `verificationUrl` to the user (Phase 5.3 CLI prints to\n * stdout; Phase 5.5 Extension pops a webview notification).\n */\nexport type DeviceFlowUiCallback = (result: AuthLoginResult) => void | Promise<void>;\n\n/**\n * Optional cancellation handle — when `shouldContinue()` returns false,\n * the polling loop exits with a clear error so callers can render\n * \"Login cancelled\" UX.\n */\nexport type CancellationCheck = () => boolean;\n\nexport class AuthCore {\n\tprivate readonly registry: ProviderRegistry;\n\tprivate readonly state: AuthStateManager;\n\tprivate readonly tokenStore: KeychainAuthTokenStore;\n\tprivate readonly accessControl?: AccessControl;\n\tprivate readonly logger: ServiceMeLogger;\n\n\tconstructor(opts: AuthCoreOptions) {\n\t\tthis.registry = new ProviderRegistry();\n\t\tfor (const provider of opts.providers) {\n\t\t\tthis.registry.register(provider);\n\t\t}\n\t\tthis.state = opts.stateManager ?? new AuthStateManager();\n\t\tthis.tokenStore = opts.tokenStore;\n\t\tthis.accessControl = opts.accessControl;\n\t\tthis.logger = opts.logger ?? noopLogger;\n\t}\n\n\t/** Snapshot of every account, the active provider, and the last error. */\n\tstatus(): AuthStatus {\n\t\treturn this.state.getStatus();\n\t}\n\n\t/** List accounts (immutable copy). */\n\tlistAccounts(): AuthAccountMeta[] {\n\t\treturn this.state.listAccounts();\n\t}\n\n\t/**\n\t * Drive the device-flow login for `provider`.\n\t *\n\t * Sequence:\n\t * 1. Ask the provider for the user code + verification URL.\n\t * 2. Surface the code to the user (via `ui`).\n\t * 3. Poll until the user authorizes (or `shouldContinue` aborts).\n\t * 4. Persist the token via `KeychainAuthTokenStore.set()`.\n\t * 5. Insert the resulting `AuthAccountMeta` into state and mark active.\n\t */\n\tasync login(\n\t\tprovider: AuthProvider,\n\t\tui: DeviceFlowUiCallback,\n\t\tshouldContinue?: CancellationCheck\n\t): Promise<AuthLoginResult> {\n\t\tconst providerImpl = this.registry.get(provider);\n\t\tthis.logger.info(\"[AuthCore] Starting device-flow login\", { provider });\n\t\ttry {\n\t\t\tconst initial = await providerImpl.requestDeviceFlow();\n\t\t\tawait ui(initial);\n\t\t\tif (!initial.deviceCode) {\n\t\t\t\tthis.logger.error(\"[AuthCore] Provider did not return deviceCode\", provider);\n\t\t\t\tthrow new Error(\"Auth provider did not return deviceCode for device flow completion\");\n\t\t\t}\n\t\t\tconst session = await providerImpl.completeDeviceFlow(\n\t\t\t\tinitial.deviceCode,\n\t\t\t\tshouldContinue,\n\t\t\t\tinitial.pollIntervalMs\n\t\t\t);\n\t\t\tconst result = await this.persistSession(providerImpl, session);\n\t\t\tthis.logger.info(\"[AuthCore] Device-flow login completed\", { provider });\n\t\t\treturn result;\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tthis.logger.error(\"[AuthCore] Device-flow login failed\", message, {\n\t\t\t\tprovider,\n\t\t\t});\n\t\t\tthis.state.recordError(message);\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/**\n\t * Complete the device-flow handshake given a pre-fetched user code.\n\t * Useful when the caller (Phase 5.3 CLI) wants to fetch the code,\n\t * print the URL, and then poll on a subsequent invocation.\n\t */\n\tasync completeLogin(\n\t\tprovider: AuthProvider,\n\t\tdeviceCode: string,\n\t\tshouldContinue?: CancellationCheck\n\t): Promise<AuthLoginResult> {\n\t\tconst providerImpl = this.registry.get(provider);\n\t\ttry {\n\t\t\tconst session = await providerImpl.completeDeviceFlow(deviceCode, shouldContinue);\n\t\t\treturn await this.persistSession(providerImpl, session);\n\t\t} catch (err) {\n\t\t\tthis.state.recordError(err instanceof Error ? err.message : String(err));\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/** Resolve the provider + token for the active session; returns null when no session is active. */\n\tasync resolveActiveToken(): Promise<{\n\t\tprovider: AuthProvider;\n\t\taccount: AuthAccountMeta;\n\t\ttoken: string;\n\t} | null> {\n\t\tconst provider = this.state.getActiveProvider();\n\t\tif (!provider) return null;\n\t\tconst account = this.state.getActiveAccount();\n\t\tif (!account) return null;\n\t\tconst envelope = await this.tokenStore.get({\n\t\t\tprovider,\n\t\t\taccountId: account.id,\n\t\t});\n\t\tif (!envelope) return null;\n\t\treturn { provider, account, token: envelope.token };\n\t}\n\n\t/**\n\t * Logout: removes the token bytes from the keychain + drops the\n\t * account entry from state. When `provider` is omitted, clears\n\t * every account.\n\t */\n\tasync logout(\n\t\tprovider?: AuthProvider\n\t): Promise<{ provider: AuthProvider | null; success: boolean }> {\n\t\tthis.logger.info(\"[AuthCore] Logout requested\", {\n\t\t\tprovider: provider ?? \"all\",\n\t\t});\n\t\tif (!provider) {\n\t\t\t// Clear every account's token bytes + metadata.\n\t\t\tfor (const account of this.state.listAccounts()) {\n\t\t\t\tawait this.tokenStore.delete({\n\t\t\t\t\tprovider: account.provider,\n\t\t\t\t\taccountId: account.id,\n\t\t\t\t});\n\t\t\t\tthis.state.removeAccount(account.provider, account.id);\n\t\t\t}\n\t\t\tthis.state.clearAll();\n\t\t\treturn { provider: null, success: true };\n\t\t}\n\n\t\tconst accountsForProvider = this.state.listAccounts().filter((a) => a.provider === provider);\n\t\tlet removed = false;\n\t\tfor (const account of accountsForProvider) {\n\t\t\tawait this.tokenStore.delete({ provider, accountId: account.id });\n\t\t\tconst didRemove = this.state.removeAccount(provider, account.id);\n\t\t\tremoved = removed || didRemove;\n\t\t}\n\t\tif (this.state.getActiveProvider() === provider) {\n\t\t\t// Reset active if we just removed the active provider's account.\n\t\t\tconst firstRemaining = this.state.listAccounts()[0];\n\t\t\tif (firstRemaining) {\n\t\t\t\tthis.state.setActive(firstRemaining.provider);\n\t\t\t}\n\t\t}\n\t\treturn { provider, success: removed };\n\t}\n\n\t/** Minimal identity for `auth.whoami`. */\n\tasync whoami(): Promise<AuthWhoamiResult> {\n\t\tconst provider = this.state.getActiveProvider();\n\t\tconst account = this.state.getActiveAccount();\n\t\tif (!provider || !account) {\n\t\t\treturn { provider: null };\n\t\t}\n\t\treturn {\n\t\t\tprovider,\n\t\t\tlogin: account.login,\n\t\t\tname: account.displayName,\n\t\t\tavatarUrl: account.avatarUrl,\n\t\t\temail: account.email,\n\t\t};\n\t}\n\n\t/** Switch the active provider. Returns the resulting active account. */\n\tswitchProvider(provider: AuthProvider, accountId?: string): AuthSwitchResult {\n\t\tconst ok = this.state.setActive(provider, accountId);\n\t\tconst account = this.state.getActiveAccount();\n\t\treturn {\n\t\t\tactiveProvider: ok ? provider : this.state.getActiveProvider(),\n\t\t\taccount,\n\t\t};\n\t}\n\n\t/** Run the access-control check against the active account (optional, requires AccessControl). */\n\tasync checkAccess(): Promise<AccessCheckResult | null> {\n\t\tif (!this.accessControl) return null;\n\t\tconst account = this.state.getActiveAccount();\n\t\treturn this.accessControl.checkAccess(account);\n\t}\n\n\t/** Expose the state manager for test inspection (not for mutation). */\n\tgetStateManager(): AuthStateManager {\n\t\treturn this.state;\n\t}\n\n\t/** Expose the provider registry for test inspection. */\n\tgetProviderRegistry(): ProviderRegistry {\n\t\treturn this.registry;\n\t}\n\n\t/** Expose the access control (or undefined when not configured). */\n\tgetAccessControl(): AccessControl | undefined {\n\t\treturn this.accessControl;\n\t}\n\n\tprivate async persistSession(\n\t\tproviderImpl: IAuthProvider,\n\t\tsession: IAuthProviderSession\n\t): Promise<AuthLoginResult> {\n\t\tconst expiresAt = session.expiresIn ? Date.now() + session.expiresIn * 1000 : null;\n\t\tconst meta = await providerImpl.fetchAccountMeta(session.token);\n\t\tconst accountMeta: AuthAccountMeta = {\n\t\t\tid: meta.id,\n\t\t\tprovider: meta.provider,\n\t\t\tdisplayName: meta.displayName,\n\t\t\tlogin: meta.login,\n\t\t\temail: meta.email,\n\t\t\tavatarUrl: meta.avatarUrl,\n\t\t\texpiresAt: meta.expiresAt ?? expiresAt,\n\t\t};\n\t\tthis.logger.debug(\"[AuthCore] Persisting session\", {\n\t\t\tprovider: meta.provider,\n\t\t\taccountId: meta.id,\n\t\t});\n\n\t\tawait this.tokenStore.set({ provider: meta.provider, accountId: meta.id }, session.token, {\n\t\t\texpiresAt,\n\t\t});\n\t\tthis.state.upsertAccount(accountMeta);\n\t\tthis.state.setActive(meta.provider, meta.id);\n\t\tthis.state.clearError();\n\t\treturn {\n\t\t\tprovider: meta.provider,\n\t\t\tmessage: `Logged in as ${meta.login ?? meta.id}`,\n\t\t};\n\t}\n}\n","/**\n * KeychainAuthTokenStore — abstract adapter for token byte persistence.\n *\n * Per ADL-004 the OAuth token bytes live in one of three places:\n * (a) VSCode `SecretStorage` (Extension process)\n * (b) `@napi-rs/keyring` Entry (CLI process)\n * (c) HTTP request `Authorization` header\n *\n * `AuthCore` MUST NOT call any of these directly. Instead it receives\n * a `KeychainAuthTokenStore` via DI; CLI provides a `@napi-rs/keyring`\n * adapter, Extension provides a `SecretStorage` adapter. This keeps\n * Core free of native-binding concerns and lets the same business\n * logic power both runtimes.\n *\n * Implementations:\n * - CLI: `apps/serviceme-cli/src/.../KeyringTokenStore.ts` (Phase 5.3)\n * - Extension: `apps/extension/src/.../SecretStorageTokenStore.ts` (Phase 5.5)\n *\n * Failure semantics — `get()` returns `null` when no token is stored\n * (cold-start), throws when the underlying keychain is unavailable\n * (rare on Linux without libsecret). Callers should surface the throw\n * as `AUTH_KEYRING_UNAVAILABLE` rather than silently falling back to\n * in-memory storage (per ADL-004 §不变量).\n *\n * Refs:\n * - ADL-004 — CLI 端 Auth Token 存储选型\n * - 4.功能规划.md §2.1 — \"Core MUST NOT directly depend on `@napi-rs/keyring`\"\n */\n\n/** Opaque account identifier (provider + upstream user id). */\nexport interface KeychainAccountKey {\n\tprovider: string;\n\taccountId: string;\n}\n\n/** Non-secret metadata returned alongside a `get()` so callers can audit. */\nexport interface KeychainTokenMetadata {\n\tprovider: string;\n\taccountId: string;\n\texpiresAt?: number | null;\n\tstoredAt?: number;\n}\n\n/**\n * Result envelope — callers receive the token bytes (for immediate use\n * in an HTTP `Authorization` header) plus optional non-secret metadata.\n *\n * IMPORTANT: the `token` field is plain `string` here, not the\n * `SecretToken` brand. The provider -> store -> HTTP-header pipeline\n * runs inside a single trust boundary; crossing that boundary requires\n * `KeychainTokenEnvelope<SecretToken>` and the `auth.tokenRead`\n * capability gate (see ADL-004 §不变量).\n */\nexport interface KeychainTokenEnvelope {\n\ttoken: string;\n\tmetadata: KeychainTokenMetadata;\n}\n\n/**\n * Abstract token-storage adapter. All methods are async because the\n * keyring / SecretStorage backends are async-by-nature.\n *\n * Thread-safety: implementations MUST be safe for concurrent calls;\n * `AuthCore` will multiplex over multiple providers on the same\n * runtime.\n */\nexport interface KeychainAuthTokenStore {\n\t/** Persist token bytes for the given provider + account pair. */\n\tset(key: KeychainAccountKey, token: string, opts?: { expiresAt?: number | null }): Promise<void>;\n\n\t/** Fetch the token bytes; returns `null` when none is stored. */\n\tget(key: KeychainAccountKey): Promise<KeychainTokenEnvelope | null>;\n\n\t/** Remove the token entry. Idempotent — removing a missing entry is not an error. */\n\tdelete(key: KeychainAccountKey): Promise<void>;\n\n\t/**\n\t * List all stored token keys (metadata only — never the token bytes).\n\t * Useful for multi-account enumeration and bridge `auth.status`.\n\t */\n\tlist(): Promise<KeychainAccountKey[]>;\n\n\t/**\n\t * Health probe — returns `false` when the keychain is unreachable\n\t * (e.g. Linux without libsecret). Callers SHOULD probe on first\n\t * use and surface `AUTH_KEYRING_UNAVAILABLE` rather than degrade.\n\t */\n\tisAvailable(): Promise<boolean>;\n}\n\n/**\n * Sentinel error thrown by `KeychainAuthTokenStore.get()` / `.set()`\n * when the underlying keychain is unavailable. Callers map this to\n * `AUTH_KEYRING_UNAVAILABLE` (Phase 5.3 error code).\n */\nexport class KeychainUnavailableError extends Error {\n\tconstructor(message: string, cause?: unknown) {\n\t\t// Use the native ES2022 `Error` two-arg form instead of redeclaring\n\t\t// `cause` as a class field — a field redeclaration shadows the\n\t\t// inherited member and requires an `override` modifier under\n\t\t// `noImplicitOverride` (downstream consumers like the Extension),\n\t\t// which in turn fails THIS package's own build if its `lib` doesn't\n\t\t// also include `cause` on `Error`. Relying on the built-in\n\t\t// constructor option avoids the mismatch entirely (this package's\n\t\t// `tsconfig.json` targets `lib: [\"ES2022\", \"DOM\"]`, so `cause` is\n\t\t// recognized here too).\n\t\tsuper(message, cause !== undefined ? { cause } : undefined);\n\t\tthis.name = \"KeychainUnavailableError\";\n\t}\n}\n\n/**\n * In-memory `KeychainAuthTokenStore` — test/dev fallback. NEVER use\n * in production: tokens live in process memory and disappear on exit.\n * Production wiring is `KeyringTokenStore` (CLI) / `SecretStorageTokenStore`\n * (Extension).\n */\nexport class InMemoryKeychainAuthTokenStore implements KeychainAuthTokenStore {\n\tprivate readonly entries = new Map<\n\t\tstring,\n\t\t{ token: string; expiresAt: number | null; storedAt: number }\n\t>();\n\n\tprivate compositeKey(key: KeychainAccountKey): string {\n\t\treturn `${key.provider}:${key.accountId}`;\n\t}\n\n\tasync set(\n\t\tkey: KeychainAccountKey,\n\t\ttoken: string,\n\t\topts?: { expiresAt?: number | null }\n\t): Promise<void> {\n\t\tthis.entries.set(this.compositeKey(key), {\n\t\t\ttoken,\n\t\t\texpiresAt: opts?.expiresAt ?? null,\n\t\t\tstoredAt: Date.now(),\n\t\t});\n\t}\n\n\tasync get(key: KeychainAccountKey): Promise<KeychainTokenEnvelope | null> {\n\t\tconst entry = this.entries.get(this.compositeKey(key));\n\t\tif (!entry) return null;\n\t\treturn {\n\t\t\ttoken: entry.token,\n\t\t\tmetadata: {\n\t\t\t\tprovider: key.provider,\n\t\t\t\taccountId: key.accountId,\n\t\t\t\texpiresAt: entry.expiresAt,\n\t\t\t\tstoredAt: entry.storedAt,\n\t\t\t},\n\t\t};\n\t}\n\n\tasync delete(key: KeychainAccountKey): Promise<void> {\n\t\tthis.entries.delete(this.compositeKey(key));\n\t}\n\n\tasync list(): Promise<KeychainAccountKey[]> {\n\t\tconst out: KeychainAccountKey[] = [];\n\t\tfor (const composite of this.entries.keys()) {\n\t\t\tconst sepIdx = composite.indexOf(\":\");\n\t\t\tif (sepIdx <= 0) continue;\n\t\t\tout.push({\n\t\t\t\tprovider: composite.slice(0, sepIdx),\n\t\t\t\taccountId: composite.slice(sepIdx + 1),\n\t\t\t});\n\t\t}\n\t\treturn out;\n\t}\n\n\tasync isAvailable(): Promise<boolean> {\n\t\treturn true;\n\t}\n}\n","/**\n * AuthCore — Pure local utilities for GitHub local email handling.\n *\n * Copied verbatim from `@serviceme/devtools-shared/src/github-user-email.ts` (15 LOC)\n * because ADL-003 forbids Core from depending on `@serviceme/devtools-shared`. These\n * helpers are pure functions with zero side effects, so the duplication is\n * trivial to keep in sync.\n *\n * Refs:\n * - 4.功能规划.md §2.1 — \"utils/githubUserEmail.ts 纯函数,直接搬\"\n * - ADL-003 — `@serviceme/devtools-shared` boundary decision\n */\n\n/**\n * Returns true when the supplied address is a synthetic GitHub \"local\" email\n * (suffixed with `@github.local`). Real GitHub OAuth clients often return\n * `null` for `email` (user kept it private) and the application substitutes\n * `<login>@github.local` to keep the field non-null.\n */\nexport function isGitHubLocalEmail(email: string | null | undefined): boolean {\n\treturn typeof email === \"string\" && email.trim().toLowerCase().endsWith(\"@github.local\");\n}\n\n/** Build a synthetic `<login>@github.local` address. */\nexport function buildGitHubLocalEmail(login: string): string {\n\treturn `${login}@github.local`;\n}\n\n/**\n * Resolve the user's primary email address — falling back to the synthetic\n * `<login>@github.local` form when the upstream email is missing or blank.\n */\nexport function resolvePrimaryEmail(login: string, email: string | null | undefined): string {\n\tconst normalizedEmail = email?.trim();\n\treturn normalizedEmail || buildGitHubLocalEmail(login);\n}\n","/**\n * GitHubAuthProvider — Device Flow OAuth implementation.\n *\n * Implements the GitHub OAuth Device Flow per the official spec\n * (https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#device-flow).\n *\n * Per ADL-002 the device flow is the locked authentication strategy for\n * SERVICEME — no localhost callback server, no PAT. This class is a\n * pure HTTP client (uses native `fetch`, no vscode dependency) that\n * returns the token bytes for the caller's `KeychainAuthTokenStore` to\n * persist immediately.\n *\n * Token bytes NEVER enter logs / errors / debug output. The provider\n * exposes only the user-visible code + verification URL during the\n * device-flow handshake.\n *\n * Refs:\n * - ADL-002 — Device Flow OAuth (locked)\n * - 4.功能规划.md §2.1 — `providers/GitHubAuthProvider.ts`\n */\n\nimport type { AuthAccountMeta, AuthLoginResult, AuthProvider } from \"@serviceme/devtools-protocol\";\nimport { noopLogger, type ServiceMeLogger } from \"../../logger\";\nimport { buildGitHubLocalEmail } from \"../utils/githubUserEmail\";\nimport type { IAuthProvider, IAuthProviderSession } from \"./IAuthProvider\";\n\nconst DEFAULT_DEVICE_CODE_URL = \"https://github.com/login/device/code\";\nconst DEFAULT_TOKEN_URL = \"https://github.com/login/oauth/access_token\";\nconst DEFAULT_USER_URL = \"https://api.github.com/user\";\nconst DEFAULT_SCOPE = \"read:user user:email\";\n\n/** Attempts for the one-shot device-code request before surfacing a network error. */\nconst DEVICE_CODE_MAX_ATTEMPTS = 3;\nconst DEFAULT_DEVICE_CODE_RETRY_BASE_DELAY_MS = 500;\n\n/**\n * `fetch` (both browser and Node's undici) rejects with a `TypeError`\n * (e.g. \"fetch failed\") when the underlying connection never completes —\n * DNS failure, TLS handshake reset, ECONNRESET, etc. These are transient\n * network hiccups, distinct from a well-formed non-2xx HTTP response, and\n * should be retried rather than immediately failing the whole login.\n */\nfunction isTransientNetworkError(error: unknown): boolean {\n\treturn error instanceof TypeError;\n}\n\n/** Tunable provider config — exposed for tests + forks. */\nexport interface GitHubAuthProviderConfig {\n\tclientId: string;\n\tdeviceCodeUrl?: string;\n\ttokenUrl?: string;\n\tuserUrl?: string;\n\tscope?: string;\n\t/** Polling interval (ms) floor. The device-flow response's `interval` wins when larger. */\n\tminPollIntervalMs?: number;\n\t/**\n\t * Maximum poll interval after back-off. Must stay generous — GitHub's\n\t * `slow_down` response requires adding a fixed step to the interval each\n\t * time it occurs (see `completeDeviceFlow`), and capping this too low\n\t * prevents the interval from ever satisfying GitHub's actual requirement,\n\t * causing a permanent `slow_down` loop that never clears even after the\n\t * user authorizes.\n\t */\n\tmaxPollIntervalMs?: number;\n\t/** Fetch override (for tests + DI). */\n\tfetchImpl?: typeof fetch;\n\t/** Poll-delay override (for tests + DI). Defaults to a real timer-based sleep. */\n\tsleepImpl?: (ms: number) => Promise<void>;\n\t/** Maximum wall-clock time to wait for the user before giving up (ms). Defaults to `expires_in * 1000`. */\n\tmaxWaitMs?: number;\n\t/** Base delay (ms) before retrying the device-code request after a transient network error. */\n\tdeviceCodeRetryBaseDelayMs?: number;\n\t/**\n\t * Diagnostic logger — never receives token bytes or the raw `device_code`,\n\t * only user-facing codes, HTTP statuses, and poll-loop timing so callers\n\t * can troubleshoot a stuck / slow device-flow login.\n\t */\n\tlogger?: ServiceMeLogger;\n}\n\ninterface DeviceCodeResponse {\n\tdevice_code: string;\n\tuser_code: string;\n\tverification_uri: string;\n\texpires_in: number;\n\tinterval: number;\n}\n\ninterface TokenPollResponse {\n\taccess_token?: string;\n\trefresh_token?: string;\n\texpires_in?: number;\n\ttoken_type?: string;\n\tscope?: string;\n\terror?: string;\n\terror_description?: string;\n\terror_uri?: string;\n}\n\ninterface GitHubUserResponse {\n\tid: number;\n\tlogin: string;\n\tname?: string | null;\n\temail?: string | null;\n\tavatar_url?: string | null;\n}\n\nexport class GitHubAuthProvider implements IAuthProvider {\n\treadonly providerId: AuthProvider = \"github\";\n\tprivate readonly cfg: Required<\n\t\tOmit<GitHubAuthProviderConfig, \"maxWaitMs\" | \"fetchImpl\" | \"logger\" | \"sleepImpl\">\n\t> & {\n\t\tmaxWaitMs?: number;\n\t\tfetchImpl: typeof fetch;\n\t};\n\tprivate readonly logger: ServiceMeLogger;\n\tprivate readonly sleepImpl: (ms: number) => Promise<void>;\n\n\tconstructor(config: GitHubAuthProviderConfig) {\n\t\tif (!config.clientId || config.clientId.length === 0) {\n\t\t\tthrow new Error(\"GitHubAuthProvider requires a non-empty `clientId`\");\n\t\t}\n\t\tthis.cfg = {\n\t\t\tclientId: config.clientId,\n\t\t\tdeviceCodeUrl: config.deviceCodeUrl ?? DEFAULT_DEVICE_CODE_URL,\n\t\t\ttokenUrl: config.tokenUrl ?? DEFAULT_TOKEN_URL,\n\t\t\tuserUrl: config.userUrl ?? DEFAULT_USER_URL,\n\t\t\tscope: config.scope ?? DEFAULT_SCOPE,\n\t\t\tminPollIntervalMs: config.minPollIntervalMs ?? 1000,\n\t\t\tmaxPollIntervalMs: config.maxPollIntervalMs ?? 60000,\n\t\t\tmaxWaitMs: config.maxWaitMs,\n\t\t\tfetchImpl: config.fetchImpl ?? fetch,\n\t\t\tdeviceCodeRetryBaseDelayMs:\n\t\t\t\tconfig.deviceCodeRetryBaseDelayMs ?? DEFAULT_DEVICE_CODE_RETRY_BASE_DELAY_MS,\n\t\t};\n\t\tthis.logger = config.logger ?? noopLogger;\n\t\tthis.sleepImpl = config.sleepImpl ?? sleep;\n\t}\n\n\tasync requestDeviceFlow(opts?: { scope?: string }): Promise<AuthLoginResult> {\n\t\tconst body = JSON.stringify({\n\t\t\tclient_id: this.cfg.clientId,\n\t\t\tscope: opts?.scope ?? this.cfg.scope,\n\t\t});\n\n\t\tlet lastNetworkError: unknown;\n\t\tfor (let attempt = 1; attempt <= DEVICE_CODE_MAX_ATTEMPTS; attempt++) {\n\t\t\tthis.logger.debug(\"[GitHubAuthProvider] Requesting device code\", {\n\t\t\t\tattempt,\n\t\t\t\tmaxAttempts: DEVICE_CODE_MAX_ATTEMPTS,\n\t\t\t});\n\t\t\tlet resp: Response;\n\t\t\ttry {\n\t\t\t\tresp = await this.cfg.fetchImpl(this.cfg.deviceCodeUrl, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\"User-Agent\": \"serviceme-core\",\n\t\t\t\t\t},\n\t\t\t\t\tbody,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tif (!isTransientNetworkError(error) || attempt === DEVICE_CODE_MAX_ATTEMPTS) {\n\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\"[GitHubAuthProvider] Device-code request failed (non-retryable)\",\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t{ attempt }\n\t\t\t\t\t);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tconst delayMs = this.cfg.deviceCodeRetryBaseDelayMs * 2 ** (attempt - 1);\n\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\"[GitHubAuthProvider] Transient network error requesting device code, retrying\",\n\t\t\t\t\t{\n\t\t\t\t\t\tattempt,\n\t\t\t\t\t\tdelayMs,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t\tlastNetworkError = error;\n\t\t\t\tawait this.sleepImpl(delayMs);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!resp.ok) {\n\t\t\t\tthis.logger.error(\n\t\t\t\t\t\"[GitHubAuthProvider] Device-code request rejected by GitHub\",\n\t\t\t\t\t`HTTP ${resp.status}`\n\t\t\t\t);\n\t\t\t\tthrow new Error(`GitHub device-code request failed: HTTP ${resp.status}`);\n\t\t\t}\n\t\t\tconst data = (await resp.json()) as DeviceCodeResponse;\n\t\t\tif (!data.device_code || !data.user_code || !data.verification_uri) {\n\t\t\t\tthis.logger.error(\n\t\t\t\t\t\"[GitHubAuthProvider] Device-code response missing required fields\",\n\t\t\t\t\tJSON.stringify(Object.keys(data))\n\t\t\t\t);\n\t\t\t\tthrow new Error(\"GitHub device-code response missing required fields\");\n\t\t\t}\n\t\t\tthis.logger.info(\"[GitHubAuthProvider] Device code obtained\", {\n\t\t\t\tuserCode: data.user_code,\n\t\t\t\tverificationUri: data.verification_uri,\n\t\t\t\texpiresInSec: data.expires_in,\n\t\t\t\tpollIntervalSec: data.interval,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tprovider: this.providerId,\n\t\t\t\tdeviceCode: data.device_code,\n\t\t\t\tuserCode: data.user_code,\n\t\t\t\tverificationUrl: data.verification_uri,\n\t\t\t\texpiresAt: Date.now() + data.expires_in * 1000,\n\t\t\t\tpollIntervalMs: data.interval * 1000,\n\t\t\t\tmessage: `Open ${data.verification_uri} and enter ${data.user_code}`,\n\t\t\t};\n\t\t}\n\t\t// Unreachable in practice — the loop always returns or throws — but keeps\n\t\t// the type checker happy and surfaces the last transient error if hit.\n\t\tthrow lastNetworkError ?? new Error(\"GitHub device-code request failed\");\n\t}\n\n\tasync completeDeviceFlow(\n\t\tdeviceCode: string,\n\t\tshouldContinue?: () => boolean,\n\t\tinitialPollIntervalMs?: number\n\t): Promise<IAuthProviderSession> {\n\t\tconst start = Date.now();\n\t\t// Honor GitHub's declared `interval` (from the matching `requestDeviceFlow()`\n\t\t// response) as the starting rate — polling faster than this can put the\n\t\t// device code into a `slow_down` state that never clears (see class docs).\n\t\tlet pollIntervalMs = Math.max(this.cfg.minPollIntervalMs, initialPollIntervalMs ?? 0);\n\t\tlet consecutiveSlowDown = 0;\n\t\tlet pollCount = 0;\n\t\tthis.logger.info(\"[GitHubAuthProvider] Starting device-flow polling\", {\n\t\t\tinitialPollIntervalMs: pollIntervalMs,\n\t\t\tmaxWaitMs: this.cfg.maxWaitMs,\n\t\t});\n\n\t\twhile (true) {\n\t\t\tif (shouldContinue && !shouldContinue()) {\n\t\t\t\tthis.logger.info(\"[GitHubAuthProvider] Device flow cancelled by caller\", {\n\t\t\t\t\telapsedMs: Date.now() - start,\n\t\t\t\t\tpollCount,\n\t\t\t\t});\n\t\t\t\tthrow new Error(\"GitHub device flow cancelled by caller\");\n\t\t\t}\n\t\t\tconst elapsed = Date.now() - start;\n\t\t\tif (this.cfg.maxWaitMs !== undefined && elapsed > this.cfg.maxWaitMs) {\n\t\t\t\tthis.logger.warn(\"[GitHubAuthProvider] Device flow exceeded max wait time\", {\n\t\t\t\t\telapsedMs: elapsed,\n\t\t\t\t\tmaxWaitMs: this.cfg.maxWaitMs,\n\t\t\t\t\tpollCount,\n\t\t\t\t});\n\t\t\t\tthrow new Error(\"GitHub device flow exceeded max wait time\");\n\t\t\t}\n\t\t\tawait this.sleepImpl(pollIntervalMs);\n\t\t\tif (shouldContinue && !shouldContinue()) {\n\t\t\t\tthis.logger.info(\"[GitHubAuthProvider] Device flow cancelled by caller\", {\n\t\t\t\t\telapsedMs: Date.now() - start,\n\t\t\t\t\tpollCount,\n\t\t\t\t});\n\t\t\t\tthrow new Error(\"GitHub device flow cancelled by caller\");\n\t\t\t}\n\n\t\t\tpollCount++;\n\t\t\tthis.logger.debug(\"[GitHubAuthProvider] Polling for authorization\", {\n\t\t\t\tpollCount,\n\t\t\t\telapsedMs: Date.now() - start,\n\t\t\t\tpollIntervalMs,\n\t\t\t});\n\t\t\tlet resp: Response;\n\t\t\ttry {\n\t\t\t\tresp = await this.cfg.fetchImpl(this.cfg.tokenUrl, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\"User-Agent\": \"serviceme-core\",\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tclient_id: this.cfg.clientId,\n\t\t\t\t\t\tdevice_code: deviceCode,\n\t\t\t\t\t\tgrant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tif (!isTransientNetworkError(error)) {\n\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\"[GitHubAuthProvider] Non-retryable error while polling token endpoint\",\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t{ pollCount }\n\t\t\t\t\t);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\t// Transient network failure (DNS/TLS/reset) — back off and retry\n\t\t\t\t// polling until the user acts, same as a non-2xx HTTP response.\n\t\t\t\tconst nextPollIntervalMs = Math.min(\n\t\t\t\t\tMath.round(pollIntervalMs * 1.5),\n\t\t\t\t\tthis.cfg.maxPollIntervalMs\n\t\t\t\t);\n\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\"[GitHubAuthProvider] Transient network error while polling, backing off\",\n\t\t\t\t\t{\n\t\t\t\t\t\tpollCount,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\tpreviousPollIntervalMs: pollIntervalMs,\n\t\t\t\t\t\tnextPollIntervalMs,\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t\tpollIntervalMs = nextPollIntervalMs;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!resp.ok) {\n\t\t\t\t// Transient HTTP failure — back off and retry until the user acts.\n\t\t\t\tconst nextPollIntervalMs = Math.min(\n\t\t\t\t\tMath.round(pollIntervalMs * 1.5),\n\t\t\t\t\tthis.cfg.maxPollIntervalMs\n\t\t\t\t);\n\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\"[GitHubAuthProvider] Token endpoint returned non-OK status, backing off\",\n\t\t\t\t\t{\n\t\t\t\t\t\tpollCount,\n\t\t\t\t\t\tstatus: resp.status,\n\t\t\t\t\t\tpreviousPollIntervalMs: pollIntervalMs,\n\t\t\t\t\t\tnextPollIntervalMs,\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t\tpollIntervalMs = nextPollIntervalMs;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst data = (await resp.json()) as TokenPollResponse;\n\t\t\tif (data.access_token) {\n\t\t\t\tthis.logger.info(\"[GitHubAuthProvider] Authorization granted, fetching user profile\", {\n\t\t\t\t\tpollCount,\n\t\t\t\t\telapsedMs: Date.now() - start,\n\t\t\t\t});\n\t\t\t\tconst rawUser = await this.fetchGitHubUser(data.access_token);\n\t\t\t\tconst email = await this.resolveEmail(data.access_token, rawUser);\n\t\t\t\tthis.logger.info(\"[GitHubAuthProvider] Device-flow login completed\", {\n\t\t\t\t\tpollCount,\n\t\t\t\t\telapsedMs: Date.now() - start,\n\t\t\t\t\tlogin: rawUser.login,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\ttoken: data.access_token,\n\t\t\t\t\trefreshToken: data.refresh_token,\n\t\t\t\t\texpiresIn: data.expires_in,\n\t\t\t\t\tuser: {\n\t\t\t\t\t\tid: String(rawUser.id),\n\t\t\t\t\t\tlogin: rawUser.login,\n\t\t\t\t\t\tname: rawUser.name ?? undefined,\n\t\t\t\t\t\temail: email ?? null,\n\t\t\t\t\t\tavatarUrl: rawUser.avatar_url ?? undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (data.error === \"authorization_pending\") {\n\t\t\t\t// User hasn't acted yet — keep polling without back-off inflation.\n\t\t\t\tthis.logger.debug(\"[GitHubAuthProvider] Authorization still pending\", {\n\t\t\t\t\tpollCount,\n\t\t\t\t\telapsedMs: Date.now() - start,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (data.error === \"slow_down\") {\n\t\t\t\tconsecutiveSlowDown++;\n\t\t\t\t// Per the OAuth Device Flow spec: on `slow_down`, add a fixed 5\n\t\t\t\t// seconds to the polling interval (not a multiplicative back-off).\n\t\t\t\t// Repeatedly under-shooting this requirement is what causes a\n\t\t\t\t// device code to get stuck returning `slow_down` forever.\n\t\t\t\tconst nextPollIntervalMs = Math.min(pollIntervalMs + 5000, this.cfg.maxPollIntervalMs);\n\t\t\t\tthis.logger.warn(\"[GitHubAuthProvider] GitHub requested slower polling (slow_down)\", {\n\t\t\t\t\tpollCount,\n\t\t\t\t\tconsecutiveSlowDown,\n\t\t\t\t\tpreviousPollIntervalMs: pollIntervalMs,\n\t\t\t\t\tnextPollIntervalMs,\n\t\t\t\t});\n\t\t\t\tpollIntervalMs = nextPollIntervalMs;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (data.error === \"access_denied\") {\n\t\t\t\tthis.logger.warn(\"[GitHubAuthProvider] User denied authorization\", {\n\t\t\t\t\tpollCount,\n\t\t\t\t});\n\t\t\t\tthrow new Error(\"User denied authorization\");\n\t\t\t}\n\t\t\tif (data.error === \"expired_token\") {\n\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\"[GitHubAuthProvider] Device code expired before authorization completed\",\n\t\t\t\t\t{ pollCount, elapsedMs: Date.now() - start }\n\t\t\t\t);\n\t\t\t\tthrow new Error(\"GitHub device code expired — restart the flow\");\n\t\t\t}\n\t\t\tthis.logger.error(\n\t\t\t\t\"[GitHubAuthProvider] Unexpected device-flow error from token endpoint\",\n\t\t\t\tdata.error ?? \"unknown\",\n\t\t\t\t{ pollCount }\n\t\t\t);\n\t\t\tthrow new Error(`GitHub device flow error: ${data.error ?? \"unknown\"}`);\n\t\t}\n\t}\n\n\tasync refreshAccessToken(refreshToken: string): Promise<IAuthProviderSession> {\n\t\tif (!refreshToken) {\n\t\t\tthrow new Error(\"refreshAccessToken requires a non-empty refresh token\");\n\t\t}\n\t\tconst resp = await this.cfg.fetchImpl(this.cfg.tokenUrl, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\"User-Agent\": \"serviceme-core\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\tclient_id: this.cfg.clientId,\n\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\trefresh_token: refreshToken,\n\t\t\t}),\n\t\t});\n\t\tif (!resp.ok) {\n\t\t\tthrow new Error(`GitHub refresh failed: HTTP ${resp.status}`);\n\t\t}\n\t\tconst data = (await resp.json()) as TokenPollResponse;\n\t\tif (!data.access_token) {\n\t\t\tthrow new Error(`GitHub refresh failed: ${data.error ?? \"no_access_token\"}`);\n\t\t}\n\t\tconst rawUser = await this.fetchGitHubUser(data.access_token);\n\t\tconst email = await this.resolveEmail(data.access_token, rawUser);\n\t\treturn {\n\t\t\ttoken: data.access_token,\n\t\t\trefreshToken: data.refresh_token ?? refreshToken,\n\t\t\texpiresIn: data.expires_in,\n\t\t\tuser: {\n\t\t\t\tid: String(rawUser.id),\n\t\t\t\tlogin: rawUser.login,\n\t\t\t\tname: rawUser.name ?? undefined,\n\t\t\t\temail: email ?? null,\n\t\t\t\tavatarUrl: rawUser.avatar_url ?? undefined,\n\t\t\t},\n\t\t};\n\t}\n\n\tasync validateToken(token: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst resp = await this.cfg.fetchImpl(this.cfg.userUrl, {\n\t\t\t\theaders: {\n\t\t\t\t\tAccept: \"application/vnd.github+json\",\n\t\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t\t\"User-Agent\": \"serviceme-core\",\n\t\t\t\t},\n\t\t\t});\n\t\t\tif (resp.status === 401) return false;\n\t\t\tif (!resp.ok) {\n\t\t\t\tthrow new Error(`GitHub /user returned HTTP ${resp.status}`);\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (err) {\n\t\t\t// Network / parse errors are surfaced; only 401 means invalid.\n\t\t\tif (err instanceof Error && err.message.includes(\"401\")) return false;\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync fetchAccountMeta(token: string): Promise<AuthAccountMeta> {\n\t\tconst user = await this.fetchGitHubUser(token);\n\t\tconst email = await this.resolveEmail(token, user);\n\t\treturn {\n\t\t\tid: String(user.id),\n\t\t\tprovider: this.providerId,\n\t\t\tdisplayName: user.name ?? user.login,\n\t\t\tlogin: user.login,\n\t\t\temail: email ?? buildGitHubLocalEmail(user.login),\n\t\t\tavatarUrl: user.avatar_url ?? undefined,\n\t\t\texpiresAt: null,\n\t\t};\n\t}\n\n\tprivate async fetchGitHubUser(token: string): Promise<GitHubUserResponse> {\n\t\tconst resp = await this.cfg.fetchImpl(this.cfg.userUrl, {\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/vnd.github+json\",\n\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t\"User-Agent\": \"serviceme-core\",\n\t\t\t},\n\t\t});\n\t\tif (!resp.ok) {\n\t\t\tthrow new Error(`GitHub /user failed: HTTP ${resp.status}`);\n\t\t}\n\t\treturn (await resp.json()) as GitHubUserResponse;\n\t}\n\n\t/**\n\t * GitHub's `/user` endpoint returns `null` when the user kept their\n\t * email private. The `/user/emails` endpoint reveals verified emails;\n\t * we pick the primary one, falling back to the synthetic\n\t * `<login>@github.local` form so the account is never email-less.\n\t */\n\tprivate async resolveEmail(token: string, user: GitHubUserResponse): Promise<string | null> {\n\t\tif (user.email) return user.email;\n\t\tconst emailsResp = await this.cfg.fetchImpl(\"https://api.github.com/user/emails\", {\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/vnd.github+json\",\n\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t\"User-Agent\": \"serviceme-core\",\n\t\t\t},\n\t\t});\n\t\tif (!emailsResp.ok) {\n\t\t\treturn null;\n\t\t}\n\t\tconst emails = (await emailsResp.json()) as Array<{\n\t\t\temail: string;\n\t\t\tprimary: boolean;\n\t\t\tvisibility: string | null;\n\t\t\tverified: boolean;\n\t\t}>;\n\t\tconst primary = emails.find((e) => e.primary && e.verified);\n\t\treturn primary?.email ?? null;\n\t}\n}\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * MicrosoftAuthProvider — Microsoft Account (MSA) OAuth Device Flow stub.\n *\n * The Extension's existing `MicrosoftAuthProvider` (`apps/extension/src/services/auth/providers/MicrosoftAuthProvider.ts`)\n * delegates to VSCode's built-in `vscode.authentication.getSession()` API\n * — there's no direct Microsoft device-flow endpoint exposed for\n * first-party apps. To keep Core usable from the CLI without VSCode,\n * we expose a minimal stub that throws \"not implemented in Core\" so\n * callers can detect and route back to the Extension adapter.\n *\n * The reason this lives in Core at all (instead of being purely\n * Extension-side) is the cross-runtime registry: `AuthCore` needs to\n * know which providers it MIGHT support, even if only the Extension\n * process can actually fulfil the Microsoft login.\n *\n * Refs:\n * - 4.功能规划.md §2.1 — `providers/MicrosoftAuthProvider.ts 从 extension 整体迁移`\n * - ADL-002 — Device Flow OAuth (locked, GitHub-only)\n */\n\nimport type { AuthAccountMeta, AuthLoginResult, AuthProvider } from \"@serviceme/devtools-protocol\";\n\nimport type { IAuthProvider, IAuthProviderSession } from \"./IAuthProvider\";\n\n/**\n * Error thrown when callers invoke Microsoft auth from a non-Extension\n * runtime (CLI / Bridge). The CLI maps this to `AUTH_PROVIDER_REQUIRES_HOST`\n * so the user is prompted to retry inside VSCode.\n */\nexport class MicrosoftProviderNotHostedError extends Error {\n\tconstructor(\n\t\tmessage = \"Microsoft auth requires the VSCode host; CLI does not yet implement MSA device flow\"\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"MicrosoftProviderNotHostedError\";\n\t}\n}\n\nexport class MicrosoftAuthProvider implements IAuthProvider {\n\treadonly providerId: AuthProvider = \"microsoft\";\n\n\tasync requestDeviceFlow(): Promise<AuthLoginResult> {\n\t\tthrow new MicrosoftProviderNotHostedError();\n\t}\n\n\tasync completeDeviceFlow(\n\t\t_deviceCode: string,\n\t\t_shouldContinue?: () => boolean\n\t): Promise<IAuthProviderSession> {\n\t\tthrow new MicrosoftProviderNotHostedError();\n\t}\n\n\tasync refreshAccessToken(_refreshToken: string): Promise<IAuthProviderSession> {\n\t\tthrow new MicrosoftProviderNotHostedError();\n\t}\n\n\tasync validateToken(_token: string): Promise<boolean> {\n\t\tthrow new MicrosoftProviderNotHostedError();\n\t}\n\n\tasync fetchAccountMeta(_token: string): Promise<AuthAccountMeta> {\n\t\tthrow new MicrosoftProviderNotHostedError();\n\t}\n}\n","import {\n\tCOPILOT_ERROR_CODES,\n\ttype CopilotDoctorResult,\n\tcreateServicemeError,\n} from \"@serviceme/devtools-protocol\";\nimport { commandExists, runCommand } from \"../process/runCommand\";\n\nconst COPILOT_COMMAND = \"copilot\";\nconst GH_COMMAND = \"gh\";\n\n/**\n * Quick auth pre-flight check using `gh auth status`.\n * Returns true if gh is authenticated; false otherwise.\n */\nexport async function isCopilotAuthenticated(): Promise<boolean> {\n\ttry {\n\t\tawait runCommand(GH_COMMAND, {\n\t\t\targs: [\"auth\", \"status\"],\n\t\t\ttimeoutMs: 10_000,\n\t\t});\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport async function copilotDoctor(): Promise<CopilotDoctorResult> {\n\tconst exists = await commandExists(COPILOT_COMMAND);\n\tif (!exists) {\n\t\treturn { installed: false, version: null, authenticated: false };\n\t}\n\n\tlet version: string | null = null;\n\ttry {\n\t\tconst versionResult = await runCommand(COPILOT_COMMAND, {\n\t\t\targs: [\"--version\"],\n\t\t\ttimeoutMs: 10_000,\n\t\t});\n\t\tversion = versionResult.stdout.trim();\n\t} catch {\n\t\treturn { installed: true, version: null, authenticated: false };\n\t}\n\n\t// Use gh auth status for reliable authentication check\n\tconst authenticated = await isCopilotAuthenticated();\n\n\treturn { installed: true, version, authenticated };\n}\n\nexport function createCopilotNotInstalledError() {\n\treturn createServicemeError(\n\t\tCOPILOT_ERROR_CODES.NOT_INSTALLED,\n\t\t\"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.\"\n\t);\n}\n\nexport function createCopilotAuthRequiredError() {\n\treturn createServicemeError(\n\t\tCOPILOT_ERROR_CODES.AUTH_REQUIRED,\n\t\t\"GitHub Copilot CLI requires authentication. Run `copilot auth login` to sign in.\"\n\t);\n}\n","import { spawn } from \"node:child_process\";\n\n/** Grace period between the SIGTERM and the SIGKILL backstop on POSIX. */\nconst FORCE_KILL_DELAY_MS = 2_000;\n\n/**\n * Terminate a spawned command, killing the whole process tree on POSIX.\n *\n * Commands are spawned with `detached: true` (their own process group) so\n * that a login-shell fallback (e.g. `zsh -lc ...`) and every process it\n * starts can be reaped together. On Windows the pre-existing `taskkill /T`\n * logic already force-terminates the tree and is left untouched.\n */\nfunction terminateCommandProcess(\n\tchild: {\n\t\tpid?: number;\n\t\tkilled?: boolean;\n\t\tkill(signal?: NodeJS.Signals | number): boolean;\n\t},\n\tsignal: NodeJS.Signals = \"SIGTERM\"\n): void {\n\tif (child.killed) {\n\t\treturn;\n\t}\n\n\tif (process.platform === \"win32\" && child.pid) {\n\t\tconst killProcess = spawn(\"taskkill\", [\"/pid\", String(child.pid), \"/T\", \"/F\"], {\n\t\t\tstdio: \"ignore\",\n\t\t\twindowsHide: true,\n\t\t});\n\n\t\tkillProcess.once(\"error\", () => {\n\t\t\tchild.kill();\n\t\t});\n\t\treturn;\n\t}\n\n\tconst pid = child.pid;\n\tif (pid) {\n\t\ttry {\n\t\t\tprocess.kill(-pid, signal);\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// Process group already gone; fall back to signaling the child.\n\t\t}\n\t}\n\n\tchild.kill(signal);\n}\n\n/**\n * Schedule a SIGKILL backstop for a timed-out POSIX process group. If the\n * SIGTERM sent by {@link terminateCommandProcess} is ignored (blocked child,\n * uninterruptible sleep), this guarantees the group is eventually reaped.\n * Windows does not need this — `taskkill /T /F` already force-kills.\n */\nfunction scheduleForceKill(child: { pid?: number }): NodeJS.Timeout | undefined {\n\tif (process.platform === \"win32\") {\n\t\treturn undefined;\n\t}\n\n\tconst pid = child.pid;\n\tif (pid === undefined) {\n\t\treturn undefined;\n\t}\n\n\tconst timer = setTimeout(() => {\n\t\ttry {\n\t\t\tprocess.kill(-pid, \"SIGKILL\");\n\t\t} catch {\n\t\t\t// Process group already terminated.\n\t\t}\n\t}, FORCE_KILL_DELAY_MS);\n\ttimer.unref?.();\n\treturn timer;\n}\n\nexport interface RunCommandOptions {\n\targs?: string[];\n\tcwd?: string;\n\tenv?: NodeJS.ProcessEnv;\n\ttimeoutMs?: number;\n\tstdin?: string;\n\tshell?: boolean;\n}\n\nexport interface RunCommandResult {\n\tstdout: string;\n\tstderr: string;\n\tcode: number;\n}\n\nexport async function runCommand(\n\tcommand: string,\n\toptions: RunCommandOptions = {}\n): Promise<RunCommandResult> {\n\treturn new Promise<RunCommandResult>((resolve, reject) => {\n\t\tconst child = spawn(command, options.args ?? [], {\n\t\t\tcwd: options.cwd,\n\t\t\tenv: options.env,\n\t\t\tshell: options.shell,\n\t\t\tstdio: \"pipe\",\n\t\t\twindowsHide: true,\n\t\t\t// POSIX: run the command in its own process group so a timeout can\n\t\t\t// terminate the whole tree (login shell + its children) at once.\n\t\t\tdetached: process.platform !== \"win32\",\n\t\t});\n\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tlet finished = false;\n\t\tlet timeoutId: NodeJS.Timeout | undefined;\n\t\tlet forceKillTimer: NodeJS.Timeout | undefined;\n\n\t\tconst finish = (handler: () => void) => {\n\t\t\tif (finished) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfinished = true;\n\t\t\tif (timeoutId) {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t}\n\t\t\tif (forceKillTimer) {\n\t\t\t\tclearTimeout(forceKillTimer);\n\t\t\t}\n\t\t\thandler();\n\t\t};\n\n\t\tchild.stdout?.setEncoding(\"utf8\");\n\t\tchild.stderr?.setEncoding(\"utf8\");\n\n\t\tchild.stdout?.on(\"data\", (chunk: string) => {\n\t\t\tstdout += chunk;\n\t\t});\n\n\t\tchild.stderr?.on(\"data\", (chunk: string) => {\n\t\t\tstderr += chunk;\n\t\t});\n\n\t\tchild.once(\"error\", (error) => {\n\t\t\tfinish(() => reject(error));\n\t\t});\n\n\t\tchild.once(\"close\", (code) => {\n\t\t\tif (forceKillTimer) {\n\t\t\t\tclearTimeout(forceKillTimer);\n\t\t\t\tforceKillTimer = undefined;\n\t\t\t}\n\t\t\tfinish(() => {\n\t\t\t\tif (code === 0) {\n\t\t\t\t\tresolve({\n\t\t\t\t\t\tstdout,\n\t\t\t\t\t\tstderr,\n\t\t\t\t\t\tcode: 0,\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst error = new Error(\n\t\t\t\t\tstderr || stdout || `Command failed with exit code ${String(code ?? 1)}.`\n\t\t\t\t) as Error & {\n\t\t\t\t\tcode?: number | string;\n\t\t\t\t\tstdout?: string;\n\t\t\t\t\tstderr?: string;\n\t\t\t\t};\n\t\t\t\terror.code = code ?? 1;\n\t\t\t\terror.stdout = stdout;\n\t\t\t\terror.stderr = stderr;\n\t\t\t\treject(error);\n\t\t\t});\n\t\t});\n\n\t\tif (options.stdin !== undefined) {\n\t\t\tchild.stdin?.end(options.stdin);\n\t\t} else {\n\t\t\tchild.stdin?.end();\n\t\t}\n\n\t\tif (options.timeoutMs) {\n\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\tfinish(() => {\n\t\t\t\t\tterminateCommandProcess(child);\n\t\t\t\t\tconst error = new Error(\n\t\t\t\t\t\t`Command timed out after ${String(options.timeoutMs)}ms.`\n\t\t\t\t\t) as Error & {\n\t\t\t\t\t\tcode?: string;\n\t\t\t\t\t\tstdout?: string;\n\t\t\t\t\t\tstderr?: string;\n\t\t\t\t\t};\n\t\t\t\t\terror.code = \"ETIMEDOUT\";\n\t\t\t\t\terror.stdout = stdout;\n\t\t\t\t\terror.stderr = stderr;\n\t\t\t\t\treject(error);\n\t\t\t\t});\n\n\t\t\t\tforceKillTimer = scheduleForceKill(child);\n\t\t\t}, options.timeoutMs);\n\t\t}\n\t});\n}\n\nexport async function commandExists(command: string): Promise<boolean> {\n\tconst isWindows = process.platform === \"win32\";\n\n\ttry {\n\t\tawait runCommand(isWindows ? \"where\" : \"which\", {\n\t\t\targs: [command],\n\t\t\ttimeoutMs: 5_000,\n\t\t});\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n","import {\n\tCOPILOT_ERROR_CODES,\n\ttype CopilotPromptOptions,\n\ttype CopilotPromptResult,\n\tcreateServicemeError,\n\tServicemeProtocolError,\n} from \"@serviceme/devtools-protocol\";\nimport { runCommand } from \"../process/runCommand\";\n\nconst COPILOT_COMMAND = \"copilot\";\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\n// Strip ANSI escape codes from output\nfunction stripAnsi(text: string): string {\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: intentional ANSI stripping\n\treturn text.replace(/\\x1B\\[[0-9;]*[A-Za-z]/g, \"\");\n}\n\nexport async function copilotPrompt(options: CopilotPromptOptions): Promise<CopilotPromptResult> {\n\tconst args = [\"-p\", options.prompt];\n\n\tif (options.workspace) {\n\t\targs.push(\"--add-dir\", options.workspace);\n\t}\n\n\tif (options.autopilot) {\n\t\targs.push(\"--allow-all-tools\");\n\t}\n\n\tif (options.allowTools?.length) {\n\t\tfor (const tool of options.allowTools) {\n\t\t\targs.push(\"--allow-tool\", tool);\n\t\t}\n\t}\n\n\tif (options.model) {\n\t\targs.push(\"--model\", options.model);\n\t}\n\n\tif (options.agent) {\n\t\targs.push(\"--agent\", options.agent);\n\t}\n\n\tconst timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;\n\n\ttry {\n\t\tconst result = await runCommand(COPILOT_COMMAND, {\n\t\t\targs,\n\t\t\tcwd: options.workspace,\n\t\t\ttimeoutMs: timeout,\n\t\t});\n\n\t\tconst output = stripAnsi(result.stdout);\n\n\t\t// Detect refusal in success path (some versions exit 0 with refusal)\n\t\tif (\n\t\t\toutput.includes(\"I'm sorry, but I cannot assist\") &&\n\t\t\tresult.stderr?.includes(\"Stream completed without a response\")\n\t\t) {\n\t\t\tthrow createServicemeError(\n\t\t\t\tCOPILOT_ERROR_CODES.AUTH_REQUIRED,\n\t\t\t\t\"Copilot CLI returned a refusal response. This typically indicates an expired authentication token. Run `gh auth login` to re-authenticate.\",\n\t\t\t\t{ exitCode: 0, output, stderr: result.stderr }\n\t\t\t);\n\t\t}\n\n\t\treturn { output, exitCode: 0 };\n\t} catch (error: unknown) {\n\t\t// Re-throw already-classified protocol errors so the success-path refusal\n\t\t// branch (and any other inner throws of ServicemeProtocolError) are not\n\t\t// rewritten as EXECUTION_FAILED by the generic catch handler below.\n\t\tif (error instanceof ServicemeProtocolError) {\n\t\t\tthrow error;\n\t\t}\n\n\t\tconst err = error as {\n\t\t\tcode?: number | string;\n\t\t\tstdout?: string;\n\t\t\tstderr?: string;\n\t\t\tmessage?: string;\n\t\t};\n\n\t\tif (err.code === \"ETIMEDOUT\") {\n\t\t\tthrow createServicemeError(\n\t\t\t\tCOPILOT_ERROR_CODES.TIMEOUT,\n\t\t\t\t`Copilot prompt timed out after ${String(timeout)}ms.`\n\t\t\t);\n\t\t}\n\n\t\tconst exitCode = typeof err.code === \"number\" ? err.code : 1;\n\t\tconst output = stripAnsi(err.stdout ?? \"\");\n\t\tconst stderr = err.stderr ?? err.message ?? \"\";\n\n\t\t// Detect Copilot refusal pattern (often caused by expired auth token)\n\t\tif (\n\t\t\toutput.includes(\"I'm sorry, but I cannot assist\") ||\n\t\t\tstderr.includes(\"Stream completed without a response\")\n\t\t) {\n\t\t\tthrow createServicemeError(\n\t\t\t\tCOPILOT_ERROR_CODES.AUTH_REQUIRED,\n\t\t\t\t\"Copilot CLI returned a refusal response. This typically indicates an expired authentication token. Run `gh auth login` to re-authenticate.\",\n\t\t\t\t{ exitCode, output, stderr }\n\t\t\t);\n\t\t}\n\n\t\tif (exitCode !== 0 && output) {\n\t\t\treturn { output, exitCode };\n\t\t}\n\n\t\tthrow createServicemeError(\n\t\t\tCOPILOT_ERROR_CODES.EXECUTION_FAILED,\n\t\t\tstderr || `Copilot exited with code ${String(exitCode)}.`,\n\t\t\t{ exitCode, stderr }\n\t\t);\n\t}\n}\n","/**\n * deviceAuth — Pure HMAC-SHA-256 signing helpers for device auth headers.\n *\n * **Boundary exception copy.** The single source of truth for the\n * `x-ms-device-*` header names + the signature algorithm now lives in\n * `@serviceme/devtools-shared` (`device-auth.ts`), consumed by the\n * extension signer and the server verifier. ADL-003 forbids\n * core → shared (and shared → core), so this module keeps a\n * byte-for-byte copy as the documented exception. Keep it in lock-step\n * with `packages/serviceme-shared/src/device-auth.ts`.\n *\n * See `docs/architecture/phase-5-device-header-spec.md` §5 for the wire\n * format. The basis is `METHOD\\nPATH\\nTIMESTAMP\\nBODY\\nSECRET`\n * (LF-joined, NOT JSON). Output is lowercase hex SHA-256.\n */\n\nimport { createHash } from \"node:crypto\";\n\n/** Canonical header names — MUST match `@serviceme/devtools-shared`'s `DeviceAuthHeaders`. */\nexport const DeviceAuthHeaders = {\n\tdeviceId: \"x-ms-device-id\",\n\tdeviceSecret: \"x-ms-device-secret\",\n\tsignature: \"x-ms-device-signature\",\n\ttimestamp: \"x-ms-device-timestamp\",\n\tsecretVersion: \"x-ms-device-secret-version\",\n} as const;\n\nexport interface DeviceRequestSignatureParams {\n\tmethod: string;\n\tpath: string;\n\ttimestamp: number;\n\tbody: string;\n\tsecret: string;\n}\n\n/**\n * Compute the HMAC-SHA-256 hex digest of the canonical basis.\n *\n * Server contract (`apps/server/src/lib/auth/device-signature-guard.ts:46-62`)\n * is line-for-line identical: same LF-joined basis, same lowercase\n * hex output. Any divergence breaks `device-signature-guard.test.ts`.\n */\nexport function createDeviceRequestSignature(params: DeviceRequestSignatureParams): string {\n\tconst basis = [\n\t\tparams.method.toUpperCase(),\n\t\tparams.path,\n\t\tString(params.timestamp),\n\t\tparams.body,\n\t\tparams.secret,\n\t].join(\"\\n\");\n\treturn createHash(\"sha256\").update(basis).digest(\"hex\");\n}\n\n/** 5-header map consumed by `fetch()` callers (CLI bridge, Extension). */\nexport interface DeviceSignedHeaders {\n\t[DeviceAuthHeaders.deviceId]: string;\n\t[DeviceAuthHeaders.deviceSecret]: string;\n\t[DeviceAuthHeaders.signature]: string;\n\t[DeviceAuthHeaders.timestamp]: string;\n\t[DeviceAuthHeaders.secretVersion]: string;\n}\n\nexport interface BuildSignedHeadersParams {\n\tmethod: string;\n\tpath: string;\n\tbody: string;\n\tpublicId: string;\n\tdeviceSecret: string;\n\tsecretVersion: number;\n\t/** Override for deterministic tests. */\n\ttimestamp?: number;\n}\n\n/**\n * Build the canonical 5-header map. The `body` parameter MUST be the\n * exact byte sequence sent on the wire (no whitespace re-canonicalization\n * between client serialization and signature basis construction).\n */\nexport function buildSignedHeaders(params: BuildSignedHeadersParams): DeviceSignedHeaders {\n\tconst timestamp = params.timestamp ?? Date.now();\n\tconst signature = createDeviceRequestSignature({\n\t\tmethod: params.method,\n\t\tpath: params.path,\n\t\ttimestamp,\n\t\tbody: params.body,\n\t\tsecret: params.deviceSecret,\n\t});\n\treturn {\n\t\t[DeviceAuthHeaders.deviceId]: params.publicId,\n\t\t[DeviceAuthHeaders.deviceSecret]: params.deviceSecret,\n\t\t[DeviceAuthHeaders.signature]: signature,\n\t\t[DeviceAuthHeaders.timestamp]: String(timestamp),\n\t\t[DeviceAuthHeaders.secretVersion]: String(params.secretVersion),\n\t};\n}\n","/**\n * Enroller — State machine for `device.enroll` and `device.rotate-secret`.\n *\n * States per `2.需求澄清.md` §1.2:\n * anonymous → pending → claimed → expired\n *\n * - `anonymous` (initial): no device has ever enrolled. Server returns\n * a fresh `publicId` + secret.\n * - `pending`: enrollment HTTP call has been issued but the server\n * hasn't confirmed yet. In-flight state — never persisted.\n * - `claimed`: user has linked this device to their account (via\n * `/api/v1/devices/claim`). Sticky binding locks future re-enrolls\n * to the same `userId` (server-side matrix).\n * - `expired`: server returned a device-expiry error. Forces a fresh\n * enroll on next call.\n *\n * `--force` semantics: any non-anonymous state can be force-reset to\n * `anonymous` by wiping the local identity file. The next enroll will\n * be treated as a brand-new install by the server (no sticky binding).\n *\n * The Enroller is the **state machine**; the actual HTTP I/O is the\n * caller's responsibility (the `DeviceSyncClient` in Phase 5.4 wires\n * the server). This split keeps the Enroller unit-testable without\n * a live server.\n *\n * Refs:\n * - 4.功能规划.md §2.2 — `Enroller.ts anonymous → pending → claimed → expired`\n * - `2.需求澄清.md` §1.2 — binding-state machine\n */\n\nimport { randomBytes } from \"node:crypto\";\n\nimport type { DeviceBindingState, DeviceEnrollResult } from \"@serviceme/devtools-protocol\";\nimport type { IdentityStore } from \"./IdentityStore\";\nimport { deriveInstallationId } from \"./InstallationId\";\nimport type { PersistedDeviceIdentity } from \"./types\";\n\n/** 32 bytes of HMAC secret material — matches the server's `device-registration.ts:73-80` generator. */\nconst SECRET_BYTES = 32;\n/** Server returns `publicId` as 32-char hex (16 bytes). Match the wire length. */\nconst PUBLIC_ID_BYTES = 16;\n\ntype RandomBytesFn = (size: number) => Buffer;\n\nconst defaultRandomBytes: RandomBytesFn = (size) => {\n\treturn randomBytes(size);\n};\n\nexport interface EnrollerOptions {\n\tidentityStore: IdentityStore;\n\t/** Injectable clock for deterministic tests. */\n\tnow?: () => Date;\n\t/** Override the random source (tests). */\n\trandomBytes?: (size: number) => Buffer;\n\t/** Caller-supplied enroll HTTP function. Phase 5.4 wires the real one. */\n\tenrollRequest?: EnrollRequestFn;\n}\n\nexport type EnrollRequestFn = (input: {\n\tinstallationId: string;\n\tmachineId: string;\n\tplatform: string;\n\texisting: PersistedDeviceIdentity | null;\n\tforce: boolean;\n\trequireAuth: boolean;\n}) => Promise<EnrollResponse>;\n\nexport interface EnrollResponse {\n\tpublicId: string;\n\tdeviceSecret: string;\n\tsecretVersion: number;\n\tbindingState: DeviceBindingState;\n\texpiresAt?: string;\n}\n\n/** Sentinel error — re-enroll on a claimed device without auth. */\nexport class DeviceReenrollRequiresAuthError extends Error {\n\tconstructor(\n\t\tmessage = \"Re-enroll on a claimed device requires current device credentials or the bound user\"\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"DeviceReenrollRequiresAuthError\";\n\t}\n}\n\n/** Sentinel error — server returned a 410 / version-mismatch after rotation. */\nexport class DeviceSecretVersionMismatchError extends Error {\n\tconstructor(message = \"Device secret version mismatch — server has rotated past the local copy\") {\n\t\tsuper(message);\n\t\tthis.name = \"DeviceSecretVersionMismatchError\";\n\t}\n}\n\nexport class Enroller {\n\tprivate readonly identity: IdentityStore;\n\tprivate readonly now: () => Date;\n\tprivate readonly random: (size: number) => Buffer;\n\tprivate readonly enrollRequest?: EnrollRequestFn;\n\tprivate inflight: Promise<DeviceEnrollResult> | null = null;\n\n\tconstructor(opts: EnrollerOptions) {\n\t\tthis.identity = opts.identityStore;\n\t\tthis.now = opts.now ?? (() => new Date());\n\t\tthis.random = opts.randomBytes ?? defaultRandomBytes;\n\t\tthis.enrollRequest = opts.enrollRequest;\n\t}\n\n\t/**\n\t * Read the current binding state without touching the disk.\n\t * Returns `anonymous` when no identity is stored.\n\t */\n\tasync currentState(): Promise<DeviceBindingState> {\n\t\tconst stored = await this.identity.read();\n\t\treturn stored?.bindingState ?? \"anonymous\";\n\t}\n\n\t/**\n\t * Drive the enrollment flow.\n\t *\n\t * @param force when true, drop the local identity and start fresh\n\t * (server treats this as a brand-new install).\n\t * @param requireAuth when true, refuse to silently re-enroll an\n\t * existing claimed device — throw\n\t * `DeviceReenrollRequiresAuthError` instead.\n\t */\n\t/**\n\t * Resolve when any in-flight enrollment completes. Returns immediately\n\t * when no enrollment is in progress. Allows callers (e.g. the extension's\n\t * `buildDeviceAuthHeaders`) to wait for a concurrent `syncDeviceInfo()`\n\t * enrollment before attempting to read the identity from the store.\n\t */\n\tasync waitForEnrollment(): Promise<void> {\n\t\tif (this.inflight) {\n\t\t\tawait this.inflight;\n\t\t}\n\t}\n\n\tasync enroll(opts: { force?: boolean; requireAuth?: boolean } = {}): Promise<DeviceEnrollResult> {\n\t\t// Concurrency guard — multiple in-flight calls share the same promise.\n\t\tif (this.inflight) {\n\t\t\treturn this.inflight;\n\t\t}\n\t\tconst promise = this.runEnroll(opts);\n\t\tthis.inflight = promise;\n\t\ttry {\n\t\t\treturn await promise;\n\t\t} finally {\n\t\t\tif (this.inflight === promise) this.inflight = null;\n\t\t}\n\t}\n\n\t/** Test seam — surface the underlying identity store. */\n\tgetIdentityStore(): IdentityStore {\n\t\treturn this.identity;\n\t}\n\n\t/** True when an enrollment is currently in-flight. Used by callers (e.g. the extension's `buildDeviceAuthHeaders`) to skip triggering a competing enrollment. */\n\tisEnrolling(): boolean {\n\t\treturn this.inflight !== null;\n\t}\n\n\tprivate async runEnroll(opts: {\n\t\tforce?: boolean;\n\t\trequireAuth?: boolean;\n\t}): Promise<DeviceEnrollResult> {\n\t\tconst { written } = await this.identity.mutate(async (current) => {\n\t\t\tconst existing = opts.force ? null : current;\n\n\t\t\tif (!opts.force && current) {\n\t\t\t\tif (current.bindingState === \"expired\") {\n\t\t\t\t\t// Expired identities are forced to re-enroll as if they were new.\n\t\t\t\t} else if (\n\t\t\t\t\topts.requireAuth &&\n\t\t\t\t\t(current.bindingState === \"claimed\" || current.bindingState === \"pending\")\n\t\t\t\t) {\n\t\t\t\t\t// Caller asserted the device must be claimed, but local state\n\t\t\t\t\t// shows it's still in flight. This is a CLI-only guard — the\n\t\t\t\t\t// server is the final arbiter.\n\t\t\t\t\tthrow new DeviceReenrollRequiresAuthError();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst installationId = current?.installationId ?? deriveInstallationId();\n\t\t\tconst machineId = current?.machineId ?? \"unknown\";\n\t\t\tconst platform = current?.platform ?? \"unknown\";\n\n\t\t\tlet response: EnrollResponse;\n\t\t\tif (this.enrollRequest) {\n\t\t\t\tresponse = await this.enrollRequest({\n\t\t\t\t\tinstallationId,\n\t\t\t\t\tmachineId,\n\t\t\t\t\tplatform,\n\t\t\t\t\texisting,\n\t\t\t\t\tforce: Boolean(opts.force),\n\t\t\t\t\trequireAuth: Boolean(opts.requireAuth),\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// Test path / no live HTTP — synthesize a fresh identity. This\n\t\t\t\t// branch is what unit tests exercise; production wires\n\t\t\t\t// `enrollRequest` in Phase 5.4.\n\t\t\t\tresponse = synthesizeEnrollResponse(this.random, existing);\n\t\t\t}\n\n\t\t\tconst next: PersistedDeviceIdentity = {\n\t\t\t\tversion: current?.version ?? 1,\n\t\t\t\tinstallationId,\n\t\t\t\tmachineId,\n\t\t\t\tplatform,\n\t\t\t\thostname: current?.hostname,\n\t\t\t\tpublicId: response.publicId,\n\t\t\t\tsecretVersion: response.secretVersion,\n\t\t\t\tbindingState: response.bindingState,\n\t\t\t\tdeviceSecret: response.deviceSecret,\n\t\t\t\tpreviousDeviceSecret: existing?.deviceSecret,\n\t\t\t\tpreviousSecretExpiresAt:\n\t\t\t\t\topts.force || response.secretVersion === (existing?.secretVersion ?? 0) + 1\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: existing?.previousSecretExpiresAt,\n\t\t\t\tlastEnrollAt: this.now().toISOString(),\n\t\t\t\tlastSyncAt: existing?.lastSyncAt,\n\t\t\t\tlastSyncError: undefined,\n\t\t\t};\n\t\t\treturn { next };\n\t\t});\n\n\t\treturn {\n\t\t\tpublicId: written.publicId,\n\t\t\tbindingState: written.bindingState,\n\t\t\texpiresAt: deriveExpiresAt(written, this.now),\n\t\t};\n\t}\n\n\t/**\n\t * Rotate the HMAC secret. Keeps the previous secret for the grace\n\t * window (default 7 days per `2.需求澄清.md` §1.2) — the\n\t * `previousSecretExpiresAt` is stamped on the persisted identity.\n\t */\n\tasync rotateSecret(\n\t\topts: { gracePeriodDays?: number } = {}\n\t): Promise<{ publicId: string; secretVersion: number; gracePeriodDays: number }> {\n\t\tconst gracePeriodDays = opts.gracePeriodDays ?? 7;\n\t\tconst now = this.now();\n\t\tconst newSecret = this.random(SECRET_BYTES).toString(\"hex\");\n\n\t\tconst { written } = await this.identity.mutate(async (current) => {\n\t\t\tif (!current) {\n\t\t\t\tthrow new Error(\"Cannot rotate-secret without a prior enrollment\");\n\t\t\t}\n\t\t\tconst graceExpiresAt = new Date(now.getTime() + gracePeriodDays * 24 * 60 * 60 * 1000);\n\t\t\tconst next: PersistedDeviceIdentity = {\n\t\t\t\t...current,\n\t\t\t\tdeviceSecret: newSecret,\n\t\t\t\tpreviousDeviceSecret: current.deviceSecret,\n\t\t\t\tpreviousSecretExpiresAt: graceExpiresAt.toISOString(),\n\t\t\t\tsecretVersion: current.secretVersion + 1,\n\t\t\t\tlastEnrollAt: now.toISOString(),\n\t\t\t};\n\t\t\treturn { next };\n\t\t});\n\n\t\treturn {\n\t\t\tpublicId: written.publicId,\n\t\t\tsecretVersion: written.secretVersion,\n\t\t\tgracePeriodDays,\n\t\t};\n\t}\n\n\t/**\n\t * Mark the device as `expired`. Used when the server returns a\n\t * device-expiry response; the next `enroll()` call forces a fresh\n\t * round-trip.\n\t */\n\tasync markExpired(): Promise<void> {\n\t\tawait this.identity.mutate(async (current) => {\n\t\t\tif (!current) {\n\t\t\t\t// Nothing to expire.\n\t\t\t\treturn { next: current ?? (await emptyIdentity(this.random)), result: undefined };\n\t\t\t}\n\t\t\tconst next: PersistedDeviceIdentity = { ...current, bindingState: \"expired\" };\n\t\t\treturn { next };\n\t\t});\n\t}\n\n\t/**\n\t * Mark the device as `claimed`. Called by the bridge after a\n\t * successful `device.claim` server response.\n\t */\n\tasync markClaimed(): Promise<void> {\n\t\tawait this.identity.mutate(async (current) => {\n\t\t\tif (!current) {\n\t\t\t\tthrow new Error(\"Cannot mark-claimed without a prior enrollment\");\n\t\t\t}\n\t\t\tconst next: PersistedDeviceIdentity = {\n\t\t\t\t...current,\n\t\t\t\tbindingState: \"claimed\",\n\t\t\t\tlastSyncAt: this.now().toISOString(),\n\t\t\t\tlastSyncError: undefined,\n\t\t\t};\n\t\t\treturn { next };\n\t\t});\n\t}\n}\n\nfunction deriveExpiresAt(_identity: PersistedDeviceIdentity, _now: () => Date): string | undefined {\n\t// No explicit expiry on the server today (per phase-5-device-header-spec.md\n\t// §9 #1: 7-day grace is rotation-only). The shape is here for forward\n\t// compatibility — when the server adds a per-device expiry, this\n\t// pulls the value from the response without changing the call site.\n\treturn undefined;\n}\n\nfunction synthesizeEnrollResponse(\n\trandom: RandomBytesFn,\n\texisting: PersistedDeviceIdentity | null\n): EnrollResponse {\n\tconst publicId = existing?.publicId ?? random(PUBLIC_ID_BYTES).toString(\"hex\");\n\tconst secretVersion = (existing?.secretVersion ?? 0) + 1;\n\treturn {\n\t\tpublicId,\n\t\tdeviceSecret: random(SECRET_BYTES).toString(\"hex\"),\n\t\tsecretVersion,\n\t\tbindingState: existing?.bindingState === \"claimed\" ? \"claimed\" : \"anonymous\",\n\t};\n}\n\nasync function emptyIdentity(random: RandomBytesFn): Promise<PersistedDeviceIdentity> {\n\treturn {\n\t\tversion: 1,\n\t\tinstallationId: deriveInstallationId(),\n\t\tmachineId: \"unknown\",\n\t\tplatform: \"unknown\",\n\t\tpublicId: random(PUBLIC_ID_BYTES).toString(\"hex\"),\n\t\tsecretVersion: 1,\n\t\tbindingState: \"anonymous\",\n\t\tdeviceSecret: random(SECRET_BYTES).toString(\"hex\"),\n\t\tlastEnrollAt: new Date().toISOString(),\n\t};\n}\n","/**\n * InstallationId — Derive a stable per-machine identifier from\n * `os.hostname()` + `os.userInfo()`.\n *\n * Per `docs/architecture/phase-5-auth-device-toolbox.md § P5-2 (并入本文时对应 § P5-2 拆分)` B2,\n * `installationId` MUST survive Extension re-installs but vary across\n * machines. We compute a UUID v5-style hash over hostname + username +\n * platform so the result is:\n * - deterministic (same machine → same id)\n * - collision-resistant (SHA-256, 128-bit truncated)\n * - browser-safe (no PII survives — username never enters output)\n *\n * Note: this intentionally differs from `vscode.env.machineId`, which\n * is per-Extension-install and uses a different algorithm. The two\n * coexist: `installationId` is what gets sent to the server, while\n * `machineId` (raw `os.hostname()`) is for diagnostics.\n *\n * Refs:\n * - 4.功能规划.md §2.2 — `InstallationId.ts os.hostname() + os.userInfo() 哈希生成`\n * - 3.功能拆分.md B2 — installationId semantics\n */\n\nimport { createHash, randomUUID } from \"node:crypto\";\nimport * as os from \"node:os\";\n\n/** Hex-encoded SHA-256 input. Format: `<hostname>|<username>|<platform>|<nodeVersion>`. */\nfunction fingerprintMaterial(): string {\n\t// `os.userInfo()` is undefined-ish on Windows in some sandboxes; fall\n\t// back to a process env user, then to a constant placeholder. We never\n\t// emit the raw username to the caller — it only enters the hash.\n\tlet username = \"unknown\";\n\ttry {\n\t\tusername = os.userInfo().username;\n\t} catch {\n\t\tusername = process.env.USER ?? process.env.USERNAME ?? \"unknown\";\n\t}\n\treturn [\n\t\tos.hostname(),\n\t\tusername,\n\t\tos.platform(),\n\t\tos.arch(),\n\t\tprocess.versions.node ?? \"unknown\",\n\t].join(\"|\");\n}\n\n/**\n * Returns a deterministic installation id for the current machine.\n * Use this when you need an id that survives Extension reinstalls\n * but stays stable across restarts on the same machine.\n */\nexport function deriveInstallationId(): string {\n\tconst material = fingerprintMaterial();\n\tconst digest = createHash(\"sha256\").update(material).digest(\"hex\");\n\t// Take the first 32 hex chars (128 bits) and reformat as UUID v4-shape\n\t// so the output looks like a UUID to downstream consumers while\n\t// remaining a pure SHA-256 truncation.\n\treturn formatAsV4(digest.slice(0, 32));\n}\n\n/**\n * Returns a random installation id (UUID v4). Use this for fresh\n * installs when no fingerprint input is available (e.g. containerized\n * CI runners where `os.hostname()` is meaningless).\n */\nexport function randomInstallationId(): string {\n\treturn randomUUID();\n}\n\n/** SHA-256 fingerprint material exposed for tests + diagnostics. */\nexport function fingerprintSource(): string {\n\treturn fingerprintMaterial();\n}\n\nfunction formatAsV4(hex32: string): string {\n\t// Stamp version 4 + variant bits per RFC 4122 §4.4. The bits are\n\t// cosmetic — the underlying entropy is still SHA-256.\n\tconst chars = hex32.split(\"\");\n\t// Version nibble (position 12 in canonical UUID, index 13 of the 32-char string).\n\tconst versionIdx = 12;\n\tconst variantIdx = 16;\n\tconst versionChar = (parseInt(chars[versionIdx] ?? \"8\", 16) & 0x0) | 0x4;\n\tchars[versionIdx] = versionChar.toString(16);\n\t// Variant nibble: 10xx → first hex char of the 17th position.\n\tconst variantChar = (parseInt(chars[variantIdx] ?? \"8\", 16) & 0x3) | 0x8;\n\tchars[variantIdx] = variantChar.toString(16);\n\tconst formatted = chars.join(\"\");\n\treturn `${formatted.slice(0, 8)}-${formatted.slice(8, 12)}-${formatted.slice(12, 16)}-${formatted.slice(16, 20)}-${formatted.slice(20, 32)}`;\n}\n","/**\n * IdentityStore — Atomic JSON persistence for the device identity file.\n *\n * Stores the `PersistedDeviceIdentity` (incl. the HMAC secret cleartext)\n * at `~/.serviceme/device.json` (per `phase-5-device-header-spec.md`\n * §3.1). Writes are atomic via `write-tmp + fsync + rename`, matching\n * the `SkillStore` / `ToolboxStore` precedent. Concurrent writes are\n * serialized with a mkdir-based file lock (POSIX-atomic) — proper\n * cross-process locking is deferred to Phase 6+ per the open spec.\n *\n * The file mode is `0600` (owner read/write only) so the cleartext\n * secret stays safe at rest. On Windows the mode hint is a no-op\n * (Windows uses ACLs) but `writeFile` still succeeds.\n *\n * Migration — IdentityStore auto-detects a v0-shape (pre-Phase-5.2)\n * file written by the Extension's old `globalState` blob:\n * { version: 1, claimed: false, publicKeyFingerprint: null }\n * In that case the file is migrated forward to the v1 schema on the\n * next write (the data fields are empty and a fresh enroll is required).\n * The full Extension `globalState` → JSON migration happens in the\n * Phase 5.5 adapter (`apps/extension/.../DeviceService.ts`) since the\n * adapter holds the live `globalState` access.\n *\n * Refs:\n * - 4.功能规划.md §2.2 — `IdentityStore.ts 持久化到 ~/.config/serviceme/device.json, 原子写`\n * - `docs/architecture/phase-5-device-header-spec.md` §3.1, §2.5\n */\n\nimport * as fsp from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\n\nimport { getDeviceJsonPath, getServicemeHome } from \"../paths/userHome\";\n\nimport {\n\ttype AtomicWriteResult,\n\tDEVICE_JSON_SCHEMA_VERSION,\n\ttype IdentityStoreHooks,\n\ttype PersistedDeviceIdentity,\n} from \"./types\";\n\nconst FILE_MODE = 0o600;\nconst LOCK_DIR_MODE = 0o700;\nconst DEFAULT_LOCK_TIMEOUT_MS = 5000;\nconst DEFAULT_LOCK_RETRY_MS = 25;\n// `mkdir` (lock acquisition) and writing the pid file are two separate\n// syscalls, so there's a brief window where the lock dir exists but the\n// pid file doesn't yet. A grace period keeps a concurrent acquirer from\n// mistaking that window for an abandoned lock (see `isStaleLock`).\nconst LOCK_STALE_GRACE_MS = 200;\nconst TMP_SUFFIX = \".tmp\";\n\n/**\n * Minimal interface for reading + writing the persisted identity file.\n * Default impl uses `getDeviceJsonPath()` (which honors `SERVICEME_HOME`),\n * but tests can substitute a custom path for isolation.\n */\nexport interface IdentityFileBackend {\n\tread(filePath: string): Promise<PersistedDeviceIdentity | null>;\n\twrite(filePath: string, payload: PersistedDeviceIdentity): Promise<AtomicWriteResult>;\n\texists(filePath: string): Promise<boolean>;\n\tdelete(filePath: string): Promise<void>;\n\tlistDir?(dir: string): Promise<string[]>;\n}\n\nexport interface IdentityStoreOptions {\n\tfilePath?: string;\n\thooks?: IdentityStoreHooks;\n\tlockTimeoutMs?: number;\n\tlockRetryMs?: number;\n\t/** Injectable clock for deterministic tests. */\n\tnow?: () => Date;\n\tbackend?: IdentityFileBackend;\n}\n\n/**\n * Default file backend — uses `node:fs/promises` with the canonical\n * tmp-then-rename atomic-write pattern.\n */\nexport class FsIdentityFileBackend implements IdentityFileBackend {\n\tasync exists(filePath: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait fsp.access(filePath);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync read(filePath: string): Promise<PersistedDeviceIdentity | null> {\n\t\ttry {\n\t\t\tconst buf = await fsp.readFile(filePath, \"utf8\");\n\t\t\tconst parsed = JSON.parse(buf) as unknown;\n\t\t\treturn migratePersistedIdentity(parsed);\n\t\t} catch (err) {\n\t\t\tif (isNodeError(err) && err.code === \"ENOENT\") return null;\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync write(filePath: string, payload: PersistedDeviceIdentity): Promise<AtomicWriteResult> {\n\t\tawait fsp.mkdir(path.dirname(filePath), { recursive: true });\n\t\tconst tmpPath = `${filePath}${TMP_SUFFIX}`;\n\t\tconst bytes = Buffer.from(JSON.stringify(payload, null, \"\\t\"), \"utf8\");\n\t\t// Ensure tmp is fresh (in case a previous run died mid-write).\n\t\tawait fsp.rm(tmpPath, { force: true });\n\t\tconst handle = await fsp.open(tmpPath, \"w\", FILE_MODE);\n\t\ttry {\n\t\t\tawait handle.writeFile(bytes);\n\t\t\tawait handle.sync();\n\t\t} finally {\n\t\t\tawait handle.close();\n\t\t}\n\t\tawait fsp.rename(tmpPath, filePath);\n\t\t// Best-effort chmod for filesystems that ignore mode on create (Windows).\n\t\tawait fsp.chmod(filePath, FILE_MODE).catch(() => undefined);\n\t\treturn { bytesWritten: bytes.byteLength, tmpPath };\n\t}\n\n\tasync delete(filePath: string): Promise<void> {\n\t\tawait fsp.rm(filePath, { force: true });\n\t}\n}\n\n/**\n * Reconcile an unknown on-disk shape into the current `PersistedDeviceIdentity`.\n *\n * - v1 IdentityStore files (current shape) pass through unchanged.\n * - v0 bootstrap files (`{ version: 1, claimed: false, publicKeyFingerprint: null }`)\n * are recognized by their placeholder keys and discarded; the next\n * enroll writes a fresh identity.\n * - Anything else throws — refuse to silently drop user data.\n */\nfunction migratePersistedIdentity(parsed: unknown): PersistedDeviceIdentity | null {\n\tif (!isRecord(parsed)) {\n\t\tthrow new Error(\"device.json: top-level must be an object\");\n\t}\n\tconst version = parsed.version;\n\tif (version === DEVICE_JSON_SCHEMA_VERSION) {\n\t\t// Pre-Phase-5.2 placeholder shape carries `claimed` /\n\t\t// `publicKeyFingerprint` but no real device fields. Recognize\n\t\t// the marker and return null so the next enroll writes fresh data.\n\t\tif (\n\t\t\tparsed.publicId === undefined &&\n\t\t\tparsed.deviceSecret === undefined &&\n\t\t\t(\"claimed\" in parsed || \"publicKeyFingerprint\" in parsed)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\t// Trust the schema — the writer is also us.\n\t\treturn parsed as unknown as PersistedDeviceIdentity;\n\t}\n\tif (typeof version === \"number\" && version < DEVICE_JSON_SCHEMA_VERSION) {\n\t\t// Pre-Phase-5.2 bootstrap shape — the file is empty placeholder\n\t\t// data; nothing to migrate. Return null to signal \"no identity\".\n\t\treturn null;\n\t}\n\tthrow new Error(`device.json: unsupported schema version ${String(version)}`);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction isNodeError(value: unknown): value is NodeJS.ErrnoException {\n\treturn value instanceof Error && typeof (value as { code?: unknown }).code === \"string\";\n}\n\nconst LOCK_PID_FILE = \"pid\";\n\n/**\n * Check whether a process is still alive (best-effort, cross-platform).\n * Returns `false` for any PID we cannot verify as alive.\n */\nfunction isProcessAlive(pid: number): boolean {\n\ttry {\n\t\t// signal 0 — permission check only, never actually sent\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * mkdir-based advisory file lock with stale-lock recovery.\n *\n * POSIX mkdir is atomic; on Windows modern filesystems (NTFS) it's also\n * atomic at the API level. Sufficient for single-host, single-user\n * scenarios (which is the SERVICEME threat model).\n *\n * Stale lock recovery: a `pid` file inside the lock directory records the\n * owner's PID. On `EEXIST`, if the recorded PID is no longer alive, the\n * lock directory is forcibly removed and acquisition retried immediately.\n * This prevents permanent lockout when a process crashes without calling\n * `release()`.\n */\nclass FileLock {\n\tprivate readonly dirPath: string;\n\tprivate readonly pidFilePath: string;\n\tprivate readonly timeoutMs: number;\n\tprivate readonly retryMs: number;\n\tprivate acquired = false;\n\n\tconstructor(filePath: string, timeoutMs: number, retryMs: number) {\n\t\tthis.dirPath = `${filePath}.lock`;\n\t\tthis.pidFilePath = path.join(this.dirPath, LOCK_PID_FILE);\n\t\tthis.timeoutMs = timeoutMs;\n\t\tthis.retryMs = retryMs;\n\t}\n\n\tasync acquire(): Promise<void> {\n\t\tconst start = Date.now();\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\tawait fsp.mkdir(this.dirPath, { mode: LOCK_DIR_MODE });\n\t\t\t\t// Write PID so a future acquirer can detect if we crash.\n\t\t\t\tawait fsp.writeFile(this.pidFilePath, String(process.pid), \"utf8\").catch(() => undefined);\n\t\t\t\tthis.acquired = true;\n\t\t\t\treturn;\n\t\t\t} catch (err) {\n\t\t\t\tif (!isNodeError(err) || err.code !== \"EEXIST\") {\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t\t// Lock directory exists — check for stale owner.\n\t\t\t\tconst stale = await this.isStaleLock();\n\t\t\t\tif (stale) {\n\t\t\t\t\tawait fsp.rm(this.dirPath, { recursive: true, force: true });\n\t\t\t\t\t// Retry immediately without counting this iteration against timeout.\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Date.now() - start >= this.timeoutMs) {\n\t\t\t\t\tthrow new Error(`IdentityStore lock acquisition timed out for ${this.dirPath}`);\n\t\t\t\t}\n\t\t\t\tawait delay(this.retryMs);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async isStaleLock(): Promise<boolean> {\n\t\tlet pidStr: string;\n\t\ttry {\n\t\t\tpidStr = await fsp.readFile(this.pidFilePath, \"utf8\");\n\t\t} catch {\n\t\t\t// The pid file may not exist yet because another acquirer just\n\t\t\t// created the lock dir and hasn't finished writing its pid file\n\t\t\t// (mkdir + writeFile is not atomic). Give it a short grace window\n\t\t\t// before concluding the owner crashed between mkdir and writeFile.\n\t\t\ttry {\n\t\t\t\tconst stat = await fsp.stat(this.dirPath);\n\t\t\t\treturn Date.now() - stat.mtimeMs > LOCK_STALE_GRACE_MS;\n\t\t\t} catch {\n\t\t\t\t// Lock dir disappeared concurrently (e.g. released mid-check) —\n\t\t\t\t// not stale, just gone; the caller's next mkdir will succeed.\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tconst pid = Number.parseInt(pidStr.trim(), 10);\n\t\tif (!Number.isFinite(pid) || pid <= 0) return true; // malformed pid file → treat as stale\n\t\treturn !isProcessAlive(pid);\n\t}\n\n\tasync release(): Promise<void> {\n\t\tif (!this.acquired) return;\n\t\tthis.acquired = false;\n\t\tawait fsp.rm(this.dirPath, { recursive: true, force: true });\n\t}\n}\n\nexport class IdentityStore {\n\tprivate readonly filePath: string;\n\tprivate readonly backend: IdentityFileBackend;\n\tprivate readonly hooks: IdentityStoreHooks;\n\tprivate readonly lockTimeoutMs: number;\n\tprivate readonly lockRetryMs: number;\n\n\tconstructor(opts: IdentityStoreOptions = {}) {\n\t\tthis.filePath = opts.filePath ?? getDeviceJsonPath();\n\t\tthis.backend = opts.backend ?? new FsIdentityFileBackend();\n\t\tthis.hooks = opts.hooks ?? {};\n\t\tthis.lockTimeoutMs = opts.lockTimeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;\n\t\tthis.lockRetryMs = opts.lockRetryMs ?? DEFAULT_LOCK_RETRY_MS;\n\t}\n\n\t/** Absolute path to the underlying JSON file (test seam). */\n\tgetFilePath(): string {\n\t\treturn this.filePath;\n\t}\n\n\t/** True when the JSON file already exists on disk. */\n\tasync exists(): Promise<boolean> {\n\t\treturn this.backend.exists(this.filePath);\n\t}\n\n\t/** Read the persisted identity; returns `null` when no identity is stored. */\n\tasync read(): Promise<PersistedDeviceIdentity | null> {\n\t\treturn this.backend.read(this.filePath);\n\t}\n\n\t/**\n\t * Atomically write the given identity. Concurrent writers are\n\t * serialized via the file lock; the read-modify-write happens\n\t * inside the lock so callers can't see a partial state.\n\t */\n\tasync write(next: PersistedDeviceIdentity): Promise<AtomicWriteResult> {\n\t\tawait this.hooks.beforeWrite?.(next);\n\t\tconst lock = new FileLock(this.filePath, this.lockTimeoutMs, this.lockRetryMs);\n\t\tawait lock.acquire();\n\t\ttry {\n\t\t\tconst stamped: PersistedDeviceIdentity = {\n\t\t\t\t...next,\n\t\t\t\tversion: DEVICE_JSON_SCHEMA_VERSION,\n\t\t\t};\n\t\t\tconst result = await this.backend.write(this.filePath, stamped);\n\t\t\tawait this.hooks.afterWrite?.(stamped);\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tawait lock.release();\n\t\t}\n\t}\n\n\t/**\n\t * Read-modify-write under the same lock. The mutator receives the\n\t * current identity (or `null` on first call) and returns the\n\t * replacement. Throwing inside the mutator aborts the write.\n\t */\n\tasync mutate<T>(\n\t\tmutator: (\n\t\t\tcurrent: PersistedDeviceIdentity | null\n\t\t) => Promise<{ next: PersistedDeviceIdentity; result?: T }>\n\t): Promise<{ result: T | undefined; written: PersistedDeviceIdentity }> {\n\t\tconst lock = new FileLock(this.filePath, this.lockTimeoutMs, this.lockRetryMs);\n\t\tawait lock.acquire();\n\t\ttry {\n\t\t\tconst current = await this.backend.read(this.filePath);\n\t\t\tconst { next, result } = await mutator(current);\n\t\t\tconst stamped: PersistedDeviceIdentity = {\n\t\t\t\t...next,\n\t\t\t\tversion: DEVICE_JSON_SCHEMA_VERSION,\n\t\t\t};\n\t\t\tawait this.hooks.beforeWrite?.(stamped);\n\t\t\tawait this.backend.write(this.filePath, stamped);\n\t\t\tawait this.hooks.afterWrite?.(stamped);\n\t\t\treturn { result, written: stamped };\n\t\t} finally {\n\t\t\tawait lock.release();\n\t\t}\n\t}\n\n\t/** Wipe the persisted identity (used by `device.enroll --force`). */\n\tasync clear(): Promise<void> {\n\t\tawait this.backend.delete(this.filePath);\n\t}\n\n\t/**\n\t * Resolve the installation metadata for the current machine.\n\t * Pure helper — no I/O, just `os.*` calls.\n\t */\n\tresolveInstallationMetadata(): Pick<\n\t\tPersistedDeviceIdentity,\n\t\t\"installationId\" | \"machineId\" | \"platform\"\n\t> {\n\t\tconst machineId = os.hostname();\n\t\tconst platform = os.platform();\n\t\t// installationId is derived by `InstallationId.ts` — pass the\n\t\t// caller's already-computed value via `material` so we don't\n\t\t// recompute the SHA twice in a row.\n\t\treturn {\n\t\t\tinstallationId: \"\", // intentionally empty; caller fills via deriveInstallationId()\n\t\t\tmachineId,\n\t\t\tplatform,\n\t\t};\n\t}\n\n\t/**\n\t * Ensure the parent directory exists (`~/.serviceme/`). Idempotent.\n\t * Useful when the bootstrap phase5 placeholder wasn't run yet.\n\t */\n\tasync ensureHome(): Promise<void> {\n\t\tawait fsp.mkdir(getServicemeHome(), { recursive: true });\n\t\tawait fsp.mkdir(path.dirname(this.filePath), { recursive: true });\n\t}\n}\n","import * as os from \"node:os\";\nimport * as path from \"node:path\";\n\n/**\n * SERVICEME user home directory helpers.\n *\n * All paths resolve under {@link getServicemeHome}, which is either the\n * `SERVICEME_HOME` environment variable (when set and non-empty) or\n * `$HOME/.serviceme` on POSIX / `%USERPROFILE%\\.serviceme` on Windows.\n *\n * Tests inject `homeDir` and `servicemeHomeEnv` overrides via\n * {@link setUserHomeOverrides} / {@link resetUserHomeOverrides} so they can\n * exercise the path logic without touching the real user environment.\n */\n\n/** Layout constants — kept in one place so other modules can reuse them. */\nexport const SERVICEME_DIR_NAME = \".serviceme\";\nexport const REPOS_SUBDIR = \"repos\";\nexport const CACHE_SUBDIR = \"cache\";\nexport const DRAFTS_SUBDIR = \"drafts\";\nexport const SKILL_DRAFTS_SUBDIR = \"skills\";\nexport const AGENT_DRAFTS_SUBDIR = \"agents\";\nexport const REPOS_CONFIG_FILENAME = \"repos.json\";\n\n/** rev.20 — r7 BYOM Server Proxy toggle self-state (read by ext + CLI + server). */\nexport const SERVER_PROXY_GLOBAL_FILENAME = \"server-proxy.json\";\n\n/**\n * Repo id regex — used to validate any `repoId` argument before it is joined\n * into a filesystem path. Keeps path traversal attempts out and gives us a\n * predictable on-disk shape.\n */\nexport const SAFE_REPO_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;\n\n/** Environment variable that overrides the user-home root directory. */\nexport const SERVICEME_HOME_ENV = \"SERVICEME_HOME\";\n\n/**\n * Test seam: lets unit tests inject deterministic values for `os.homedir()`\n * and the `SERVICEME_HOME` env override without actually mutating\n * `process.env` (which would leak into other tests).\n */\ninterface UserHomeOverrides {\n\thomeDir?: string | undefined;\n\tservicemeHomeEnv?: string | undefined;\n\tplatform?: NodeJS.Platform | undefined;\n}\n\nlet activeOverrides: UserHomeOverrides = {};\n\nexport function setUserHomeOverrides(overrides: UserHomeOverrides): void {\n\tactiveOverrides = { ...overrides };\n}\n\nexport function resetUserHomeOverrides(): void {\n\tactiveOverrides = {};\n}\n\nfunction resolveHomeDir(): string {\n\tconst injected = activeOverrides.homeDir;\n\tif (injected !== undefined) {\n\t\treturn injected;\n\t}\n\treturn os.homedir();\n}\n\nfunction resolveServicemeHomeEnv(): string | undefined {\n\tconst injected = activeOverrides.servicemeHomeEnv;\n\tif (injected !== undefined) {\n\t\t// Treat empty string as \"not set\" — `process.env` always returns a string\n\t\t// but tests may deliberately pass \"\" to opt out.\n\t\treturn injected.length > 0 ? injected : undefined;\n\t}\n\tconst envValue = process.env[SERVICEME_HOME_ENV];\n\treturn envValue && envValue.length > 0 ? envValue : undefined;\n}\n\nfunction resolvePlatform(): NodeJS.Platform {\n\treturn activeOverrides.platform ?? process.platform;\n}\n\nexport function assertSafeRepoId(repoId: string): string {\n\tif (typeof repoId !== \"string\" || repoId.length === 0 || !SAFE_REPO_ID_PATTERN.test(repoId)) {\n\t\tthrow new Error(\n\t\t\t`Invalid repo id: ${JSON.stringify(repoId)}. ` +\n\t\t\t\t`Must match ${SAFE_REPO_ID_PATTERN} (alphanumeric start, then ` +\n\t\t\t\t`alphanumerics / underscores / hyphens, ≤ 64 chars).`\n\t\t);\n\t}\n\treturn repoId;\n}\n\n/**\n * The raw OS home directory (`os.homedir()`), honoring test overrides\n * ({@link setUserHomeOverrides}). Exported for callers that need a\n * home-relative path *outside* of `~/.serviceme` — e.g. the\n * `~/.agents/{skills,agents}` convention used by `SkillLinker` for\n * user-scope links.\n */\nexport function getHomeDir(): string {\n\treturn resolveHomeDir();\n}\n\n/**\n * Root directory for all SERVICEME user-level state. Resolves to\n * `${SERVICEME_HOME}` when that env var is set, otherwise `${HOME}/.serviceme`\n * (POSIX) or `%USERPROFILE%\\.serviceme` (Windows via `os.homedir`).\n */\nexport function getServicemeHome(): string {\n\tconst override = resolveServicemeHomeEnv();\n\tif (override !== undefined) {\n\t\treturn path.resolve(override);\n\t}\n\treturn path.join(resolveHomeDir(), SERVICEME_DIR_NAME);\n}\n\n/** `$HOME/.serviceme/repos` (or `${SERVICEME_HOME}/repos`). */\nexport function getReposDir(): string {\n\treturn path.join(getServicemeHome(), REPOS_SUBDIR);\n}\n\n/** `$HOME/.serviceme/repos/<repoId>` — validates `repoId` first. */\nexport function getRepoDir(repoId: string): string {\n\treturn path.join(getReposDir(), assertSafeRepoId(repoId));\n}\n\n/** `$HOME/.serviceme/cache` — generic per-user cache. */\nexport function getCacheDir(): string {\n\treturn path.join(getServicemeHome(), CACHE_SUBDIR);\n}\n\n/** `$HOME/.serviceme/cache/<repoId>` — per-repo sync state lives here. */\nexport function getRepoCacheDir(repoId: string): string {\n\treturn path.join(getCacheDir(), assertSafeRepoId(repoId));\n}\n\n/** `$HOME/.serviceme/drafts` — local edits not yet pushed upstream. */\nexport function getDraftsDir(): string {\n\treturn path.join(getServicemeHome(), DRAFTS_SUBDIR);\n}\n\n/** `$HOME/.serviceme/drafts/skills` — local skill drafts. */\nexport function getSkillDraftsDir(): string {\n\treturn path.join(getDraftsDir(), SKILL_DRAFTS_SUBDIR);\n}\n\n/** `$HOME/.serviceme/drafts/agents` — local agent drafts. */\nexport function getAgentDraftsDir(): string {\n\treturn path.join(getDraftsDir(), AGENT_DRAFTS_SUBDIR);\n}\n\n/**\n * `$HOME/.serviceme/repos.json` — the single config entry point for repo\n * metadata. Always under the resolved home root (i.e. follows\n * `SERVICEME_HOME` overrides too).\n */\nexport function getReposConfigPath(): string {\n\treturn path.join(getServicemeHome(), REPOS_CONFIG_FILENAME);\n}\n\n/**\n * Convenience helper for callers that need to switch behaviour on platform\n * (e.g. `SkillLinker` chooses symlink vs junction). Exported mostly so tests\n * can pin the value without touching `process.platform` directly.\n */\nexport function getHomePlatform(): NodeJS.Platform {\n\treturn resolvePlatform();\n}\n\n// ─── Scheduled Tasks paths (added by M1.5.3) ───────────────────────────────\n\n/** Layout constants for scheduled-tasks files. Kept here so other modules\n * can reuse them and the names stay in sync with the design doc. */\nexport const SCHEDULED_TASKS_CONFIG_FILENAME = \"scheduled-tasks.json\";\nexport const SCHEDULED_TASKS_LOG_FILENAME = \"scheduled-tasks-log.json\";\nexport const SCHEDULER_PID_FILENAME = \"scheduler.pid\";\nexport const SCHEDULER_LOCK_FILENAME = \"scheduler.lock\";\nexport const SCHEDULER_LOG_FILENAME = \"scheduler.log\";\nexport const MIGRATION_FAILURES_FILENAME = \"migration-failures.json\";\nexport const KNOWN_WORKSPACES_FILENAME = \"known-workspaces.json\";\n\n/** `~/.serviceme/scheduled-tasks.json` — the global v2 task list. */\nexport function getScheduledTasksConfigPath(): string {\n\treturn path.join(getServicemeHome(), SCHEDULED_TASKS_CONFIG_FILENAME);\n}\n\n/** `~/.serviceme/scheduled-tasks-log.json` — the global execution log (200 LRU). */\nexport function getScheduledTasksLogPath(): string {\n\treturn path.join(getServicemeHome(), SCHEDULED_TASKS_LOG_FILENAME);\n}\n\n/** `~/.serviceme/scheduler.pid` — global daemon PID file. */\nexport function getSchedulerPidPath(): string {\n\treturn path.join(getServicemeHome(), SCHEDULER_PID_FILENAME);\n}\n\n/** `~/.serviceme/scheduler.lock` — global daemon startup flock. */\nexport function getSchedulerLockPath(): string {\n\treturn path.join(getServicemeHome(), SCHEDULER_LOCK_FILENAME);\n}\n\n/** `~/.serviceme/scheduler.log` — global daemon log (daemon lifecycle, not task execution). */\nexport function getSchedulerLogPath(): string {\n\treturn path.join(getServicemeHome(), SCHEDULER_LOG_FILENAME);\n}\n\n/** `~/.serviceme/migration-failures.json` — diagnostics for failed v1→v2 imports. */\nexport function getMigrationFailuresPath(): string {\n\treturn path.join(getServicemeHome(), MIGRATION_FAILURES_FILENAME);\n}\n\n/** `~/.serviceme/known-workspaces.json` — workspace list the extension has seen. */\nexport function getKnownWorkspacesPath(): string {\n\treturn path.join(getServicemeHome(), KNOWN_WORKSPACES_FILENAME);\n}\n\n// ─── r7 BYOM Server Proxy (rev.20) ─────────────────────────────────────────\n\n/**\n * `~/.serviceme/server-proxy.json` — r7 BYOM Server Proxy toggle\n * self-state. Written by the extension's `ProxyConfigService`, readable\n * by the CLI / Server so they can decide whether to route traffic\n * through the corporate proxy without booting VS Code.\n *\n * Lives under user-level `~/.serviceme/` (not under a workspace) because\n * the toggle is a *user preference* — the same human enabling it on\n * machine A should be able to rely on it on machine B after sync, not\n * have it disappear when they switch repos.\n */\nexport function getServerProxyGlobalPath(): string {\n\treturn path.join(getServicemeHome(), SERVER_PROXY_GLOBAL_FILENAME);\n}\n\n// ─── Phase 5 (auth + device + toolbox) placeholders (Spec §11.12) ────────\n\n/** Layout constants for the Phase 5 client-side state files. The server\n * already has the corresponding routes (`/api/v1/auth/...`,\n * `/api/v1/device/...`, etc.) — the client just needs the on-disk\n * file paths + a first-run bootstrap so the auth/device/toolbox\n * services can open + read them without a per-call existence check. */\nexport const CREDENTIALS_CONFIG_FILENAME = \"credentials.json\";\nexport const DEVICE_JSON_FILENAME = \"device.json\";\nexport const TOOLBOX_JSON_FILENAME = \"toolbox.json\";\nexport const MACHINE_ID_FILENAME = \"machine-id\";\nexport const PROFILES_JSON_FILENAME = \"profiles.json\";\n\n/** `~/.serviceme/credentials.json` — better-auth session tokens + refresh state (client-side cache). */\nexport function getCredentialsConfigPath(): string {\n\treturn path.join(getServicemeHome(), CREDENTIALS_CONFIG_FILENAME);\n}\n\n/** `~/.serviceme/device.json` — device enrollment payload (id, public key fingerprint, claimed-by). */\nexport function getDeviceJsonPath(): string {\n\treturn path.join(getServicemeHome(), DEVICE_JSON_FILENAME);\n}\n\n/** `~/.serviceme/toolbox.json` — local toolbox state (installed tool refs, per-user prefs). */\nexport function getToolboxJsonPath(): string {\n\treturn path.join(getServicemeHome(), TOOLBOX_JSON_FILENAME);\n}\n\n/** `~/.serviceme/machine-id` — opaque stable per-install id (uuid v4 string, no JSON wrapper). */\nexport function getMachineIdPath(): string {\n\treturn path.join(getServicemeHome(), MACHINE_ID_FILENAME);\n}\n\n/** `~/.serviceme/profiles.json` — cached projection of server-side profile rows; refresh on demand. */\nexport function getProfilesJsonPath(): string {\n\treturn path.join(getServicemeHome(), PROFILES_JSON_FILENAME);\n}\n","/**\n * Internal types for the device domain.\n *\n * These types are NOT re-exported from the protocol package — they are\n * implementation details of the IdentityStore + Enroller. Public data\n * models (the bridge wire shape) live in `@serviceme/devtools-protocol`'s\n * `device.ts`.\n *\n * Refs:\n * - 4.功能规划.md §2.2 — `device/types.ts`\n */\n\nimport type { DeviceBindingState } from \"@serviceme/devtools-protocol\";\n\n/**\n * Schema version of the on-disk `device.json` file. Bumped when the\n * shape changes incompatibly. IdentityStore checks this on read and\n * either migrates (versions ≤ 1) or refuses (versions > supported).\n */\nexport const DEVICE_JSON_SCHEMA_VERSION = 1;\n\n/** Internal representation of the persisted identity file. */\nexport interface PersistedDeviceIdentity {\n\tversion: number;\n\t/** Stable per-machine id (UUID v4 shape) — survives secret rotates. */\n\tinstallationId: string;\n\t/** Raw `os.hostname()` for diagnostics. */\n\tmachineId: string;\n\t/** Platform string (e.g. \"darwin\"). */\n\tplatform: string;\n\t/** Optional hostname override for environments where `os.hostname()` is unstable. */\n\thostname?: string;\n\t/** Public, non-secret id returned by the server. 32-char hex. */\n\tpublicId: string;\n\t/** Monotonic secret version counter, starts at 1 after first enroll. */\n\tsecretVersion: number;\n\t/** Current binding state — drives the re-enroll matrix. */\n\tbindingState: DeviceBindingState;\n\t/** HMAC secret (32 bytes hex-encoded = 64 chars). Persisted per `device-header-spec.md` §3.1. */\n\tdeviceSecret: string;\n\t/** Optional: previous secret retained during the grace window (rotation). */\n\tpreviousDeviceSecret?: string;\n\t/** Optional: ISO timestamp at which the previous secret stops being accepted. */\n\tpreviousSecretExpiresAt?: string;\n\t/** ISO timestamp of the most recent successful enroll / rotate. */\n\tlastEnrollAt: string;\n\t/** Optional ISO timestamp of the most recent server sync. */\n\tlastSyncAt?: string;\n\t/** Optional human-readable message for the last sync error. */\n\tlastSyncError?: string;\n}\n\n/** Result of a single atomic write. */\nexport interface AtomicWriteResult {\n\tbytesWritten: number;\n\t/** Path to the temp file (post-rename it no longer exists; useful for diagnostics). */\n\ttmpPath: string;\n}\n\n/** Hook called before/after every identity write — used by tests to assert concurrency safety. */\nexport interface IdentityStoreHooks {\n\tbeforeWrite?: (next: PersistedDeviceIdentity) => void | Promise<void>;\n\tafterWrite?: (next: PersistedDeviceIdentity) => void | Promise<void>;\n}\n","/**\n * DeviceCore — Main entry for the device domain.\n *\n * Aggregates `IdentityStore` + `Enroller` + signer helpers into a single\n * surface that the CLI / Extension / Bridge can call. Pure orchestration\n * — no HTTP of its own (the actual `POST /api/v1/devices/enroll` lives\n * behind `EnrollerOptions.enrollRequest`, wired in Phase 5.4).\n *\n * Refs:\n * - 4.功能规划.md §2.2 — `DeviceCore.ts 主入口`\n * - ADL-003 — data model in `@serviceme/devtools-protocol`\n * - `docs/architecture/phase-5-device-header-spec.md` §3.1\n */\n\nimport type {\n\tDeviceEnrollResult,\n\tDeviceIdentityState,\n\tDeviceMetadata,\n\tDeviceRotateSecretResult,\n\tDeviceStatus,\n} from \"@serviceme/devtools-protocol\";\nimport { buildSignedHeaders, DeviceAuthHeaders, type DeviceSignedHeaders } from \"./deviceAuth\";\nimport { Enroller, type EnrollRequestFn } from \"./Enroller\";\nimport { IdentityStore } from \"./IdentityStore\";\nimport { deriveInstallationId } from \"./InstallationId\";\nimport type { PersistedDeviceIdentity } from \"./types\";\n\nexport interface DeviceCoreOptions {\n\tidentityStore?: IdentityStore;\n\tenrollRequest?: EnrollRequestFn;\n\tnow?: () => Date;\n\t/** Optional override for `deriveInstallationId` (used by tests for determinism). */\n\tresolveInstallationId?: () => string;\n}\n\nexport class DeviceCore {\n\tprivate readonly identity: IdentityStore;\n\tprivate readonly enroller: Enroller;\n\tprivate readonly resolveInstallationId: () => string;\n\n\tconstructor(opts: DeviceCoreOptions = {}) {\n\t\tthis.identity = opts.identityStore ?? new IdentityStore();\n\t\tthis.enroller = new Enroller({\n\t\t\tidentityStore: this.identity,\n\t\t\tenrollRequest: opts.enrollRequest,\n\t\t\tnow: opts.now,\n\t\t});\n\t\tthis.resolveInstallationId = opts.resolveInstallationId ?? deriveInstallationId;\n\t}\n\n\t/** Read-only snapshot of the device status (matches `device.status` wire shape). */\n\tasync status(): Promise<DeviceStatus> {\n\t\tconst stored = await this.identity.read();\n\t\treturn stored\n\t\t\t? {\n\t\t\t\t\tbindingState: stored.bindingState,\n\t\t\t\t\tidentity: projectIdentity(stored),\n\t\t\t\t\tmetadata: projectMetadata(stored),\n\t\t\t\t\tlastSyncAt: stored.lastSyncAt,\n\t\t\t\t\tlastSyncError: stored.lastSyncError,\n\t\t\t\t}\n\t\t\t: { bindingState: \"anonymous\" };\n\t}\n\n\t/** Enroll (or re-enroll) the device. */\n\tasync enroll(opts: { force?: boolean; requireAuth?: boolean } = {}): Promise<DeviceEnrollResult> {\n\t\treturn this.enroller.enroll(opts);\n\t}\n\n\t/** Wait for any in-flight enrollment to finish. Use before `buildSignedHeaders` so that a concurrent `syncDeviceInfo` enrollment has time to write the identity to the store. */\n\tasync waitForEnrollment(): Promise<void> {\n\t\tawait this.enroller.waitForEnrollment();\n\t}\n\n\t/** True when an enrollment is currently in-flight. Used by callers to skip triggering a competing enrollment. */\n\tisEnrolling(): boolean {\n\t\treturn this.enroller.isEnrolling();\n\t}\n\n\t/** Rotate the HMAC secret while keeping the previous one for the grace window. */\n\tasync rotateSecret(opts: { gracePeriodDays?: number } = {}): Promise<DeviceRotateSecretResult> {\n\t\tconst result = await this.enroller.rotateSecret(opts);\n\t\tconst stored = await this.identity.read();\n\t\treturn {\n\t\t\t...result,\n\t\t\tgracePeriodEndsAt: stored?.previousSecretExpiresAt,\n\t\t};\n\t}\n\n\t/** Build the canonical 5-header map for an outbound signed request. */\n\tasync buildSignedHeaders(input: {\n\t\tmethod: string;\n\t\tpath: string;\n\t\tbody: string;\n\t}): Promise<DeviceSignedHeaders | null> {\n\t\tconst stored = await this.identity.read();\n\t\tif (!stored) return null;\n\t\treturn buildSignedHeaders({\n\t\t\tmethod: input.method,\n\t\t\tpath: input.path,\n\t\t\tbody: input.body,\n\t\t\tpublicId: stored.publicId,\n\t\t\tdeviceSecret: stored.deviceSecret,\n\t\t\tsecretVersion: stored.secretVersion,\n\t\t});\n\t}\n\n\t/** Raw stored identity (CLI/extension internal use). Test seam too. */\n\tasync readIdentity(): Promise<PersistedDeviceIdentity | null> {\n\t\treturn this.identity.read();\n\t}\n\n\t/** Wipe the local identity (the `--force` path before re-enroll). */\n\tasync clear(): Promise<void> {\n\t\tawait this.identity.clear();\n\t}\n\n\t/** Mark the device as claimed (called by the bridge after a successful claim). */\n\tasync markClaimed(): Promise<void> {\n\t\tawait this.enroller.markClaimed();\n\t}\n\n\t/** Mark the device as expired (server returned an expiry response). */\n\tasync markExpired(): Promise<void> {\n\t\tawait this.enroller.markExpired();\n\t}\n\n\t/** Expose the identity store (CLI uses it for direct file access in tests). */\n\tgetIdentityStore(): IdentityStore {\n\t\treturn this.identity;\n\t}\n\n\t/** Expose the enroller (CLI uses it for state inspection). */\n\tgetEnroller(): Enroller {\n\t\treturn this.enroller;\n\t}\n\n\t/** Header name constants — re-exported from `deviceAuth.ts`. */\n\tgetHeaderNames(): typeof DeviceAuthHeaders {\n\t\treturn DeviceAuthHeaders;\n\t}\n\n\t/** Compute the installation id for the current machine. */\n\tgetInstallationId(): string {\n\t\treturn this.resolveInstallationId();\n\t}\n}\n\nfunction projectIdentity(stored: PersistedDeviceIdentity): DeviceIdentityState {\n\treturn {\n\t\tpublicId: stored.publicId,\n\t\tsecretVersion: stored.secretVersion,\n\t\tbindingState: stored.bindingState,\n\t};\n}\n\nfunction projectMetadata(stored: PersistedDeviceIdentity): DeviceMetadata {\n\treturn {\n\t\tinstallationId: stored.installationId,\n\t\tmachineId: stored.machineId,\n\t\tplatform: stored.platform,\n\t\thostname: stored.hostname,\n\t};\n}\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport { getAgentDraftsDir, getSkillDraftsDir } from \"../paths/userHome\";\nimport { extractFrontmatter } from \"../skill-store/index\";\nimport type { SkillFile, SkillKind } from \"../skill-store/types\";\n\n/**\n * Skill & Agent v2 — Drafts (M4)\n *\n * Drafts are offline WIP copies of skills/agents that the user is\n * editing locally before submitting. They live under\n * `~/.serviceme/drafts/{skills,agents}/<draft-id>/` and have the same\n * file layout as a published entry: SKILL.md (or AGENT.md) plus any\n * extra files. The `SubmitClient` (also M4) reads a draft's files and\n * sends them to the server's validate endpoint before pushing.\n *\n * Draft ids are short random hex strings (16 hex chars / 8 bytes of\n * entropy) — long enough to be globally unique, short enough to be\n * pasteable. They are NOT user-meaningful; clients surface a `name`\n * from the draft's frontmatter instead.\n *\n * Persistence is atomic per-file (write to `.tmp-xxx` + rename).\n * The whole draft directory is never partially committed — failed\n * writes leave a `.tmp-xxx` orphan that the next save/cleanup can\n * sweep.\n *\n * @see docs/architecture/skill-agent-v2-repo.md §2.1 (drafts layout),\n * §5.7 SubmitClient (consumer).\n */\n\nexport interface DraftSummary {\n\t/** Random id (16 hex chars). */\n\tid: string;\n\tkind: SkillKind;\n\t/** Path to the manifest file (SKILL.md or AGENT.md). */\n\tmanifestPath: string;\n\t/** Path to the directory containing the draft's files. */\n\tdir: string;\n\t/** Best-effort name from the frontmatter (empty string when absent). */\n\tname: string;\n\t/** Best-effort description from the frontmatter (empty string when absent). */\n\tdescription: string;\n\t/** ISO timestamp of the manifest file's mtime. */\n\tmodifiedAt: string;\n}\n\nexport interface DraftDetail extends DraftSummary {\n\tfiles: SkillFile[];\n}\n\nexport interface SaveDraftOptions {\n\tkind: SkillKind;\n\t/** Existing draft id; omit to create a new draft. */\n\tid?: string;\n\t/** Files to write. Must include a manifest (SKILL.md or AGENT.md). */\n\tfiles: SkillFile[];\n}\n\n/** Sentinel errors. */\nexport class DraftsError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"DraftsError\";\n\t}\n}\nexport class DraftNotFoundError extends DraftsError {\n\tconstructor(\n\t\tpublic readonly kind: SkillKind,\n\t\tpublic readonly id: string\n\t) {\n\t\tsuper(`draft not found: ${kind}/${id}`);\n\t\tthis.name = \"DraftNotFoundError\";\n\t}\n}\nexport class InvalidDraftError extends DraftsError {\n\tconstructor(message: string) {\n\t\tsuper(`invalid draft: ${message}`);\n\t\tthis.name = \"InvalidDraftError\";\n\t}\n}\n\n/**\n * Generate a short random hex id. Uses Node's `crypto.randomBytes` so\n * the output is unpredictable. 8 bytes = 16 hex chars = 64 bits of\n * entropy. We don't promise uniqueness (collisions exist with\n * astronomically low probability) — callers must treat ids as opaque.\n */\nexport function generateDraftId(): string {\n\treturn crypto.randomBytes(8).toString(\"hex\");\n}\n\n/**\n * Resolve the on-disk directory for a draft of a given kind + id.\n * Exported for tests + the SubmitClient which writes the committed\n * files into the repo.\n */\nexport function resolveDraftDir(kind: SkillKind, id: string): string {\n\tconst root = kind === \"skill\" ? getSkillDraftsDir() : getAgentDraftsDir();\n\treturn path.join(root, id);\n}\n\n/**\n * Persistent draft store backed by the local filesystem. Pure local —\n * no network, no git, no DB.\n */\nexport class DraftsStore {\n\t/**\n\t * List every draft of the given kind. Returns [] when the drafts\n\t * directory doesn't exist (cold start, never-saved state).\n\t */\n\tasync list(kind: SkillKind): Promise<DraftSummary[]> {\n\t\tconst root = kind === \"skill\" ? getSkillDraftsDir() : getAgentDraftsDir();\n\t\tlet names: string[];\n\t\ttry {\n\t\t\tnames = await fs.readdir(root);\n\t\t} catch (err) {\n\t\t\tif ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n\t\t\tthrow err;\n\t\t}\n\t\tconst out: DraftSummary[] = [];\n\t\tfor (const name of names) {\n\t\t\tconst summary = await this.tryReadSummary(kind, name);\n\t\t\tif (summary) out.push(summary);\n\t\t}\n\t\t// Most-recently-modified first.\n\t\tout.sort((a, b) => (a.modifiedAt < b.modifiedAt ? 1 : -1));\n\t\treturn out;\n\t}\n\n\t/** Single draft summary (manifest + metadata). Returns null when missing. */\n\tasync tryReadSummary(kind: SkillKind, id: string): Promise<DraftSummary | null> {\n\t\tconst dir = resolveDraftDir(kind, id);\n\t\tconst manifestFilename = kind === \"skill\" ? \"SKILL.md\" : \"AGENT.md\";\n\t\tconst manifestPath = path.join(dir, manifestFilename);\n\t\tlet stat: import(\"node:fs\").Stats;\n\t\ttry {\n\t\t\tstat = await fs.stat(manifestPath);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t\tlet content = \"\";\n\t\ttry {\n\t\t\tcontent = await fs.readFile(manifestPath, \"utf8\");\n\t\t} catch {\n\t\t\t// Manifest exists but unreadable — surface as empty name/desc.\n\t\t}\n\t\tconst parsed = extractFrontmatter(content);\n\t\tconst name = typeof parsed?.data.name === \"string\" ? parsed.data.name : \"\";\n\t\tconst description = typeof parsed?.data.description === \"string\" ? parsed.data.description : \"\";\n\t\treturn {\n\t\t\tid,\n\t\t\tkind,\n\t\t\tmanifestPath,\n\t\t\tdir,\n\t\t\tname,\n\t\t\tdescription,\n\t\t\tmodifiedAt: stat.mtime.toISOString(),\n\t\t};\n\t}\n\n\t/** Single draft detail (summary + all files). Throws when missing. */\n\tasync get(kind: SkillKind, id: string): Promise<DraftDetail> {\n\t\tconst summary = await this.tryReadSummary(kind, id);\n\t\tif (!summary) throw new DraftNotFoundError(kind, id);\n\t\tconst files = await this.getFiles(kind, id);\n\t\treturn { ...summary, files };\n\t}\n\n\t/** All files in a draft (manifest + extras), recursively. */\n\tasync getFiles(kind: SkillKind, id: string): Promise<SkillFile[]> {\n\t\tconst dir = resolveDraftDir(kind, id);\n\t\tconst out: SkillFile[] = [];\n\t\tawait collectRecursive(dir, dir, out);\n\t\tout.sort((a, b) => {\n\t\t\tconst aIsManifest = a.path === \"SKILL.md\" || a.path === \"AGENT.md\";\n\t\t\tconst bIsManifest = b.path === \"SKILL.md\" || b.path === \"AGENT.md\";\n\t\t\tif (aIsManifest && !bIsManifest) return -1;\n\t\t\tif (bIsManifest && !aIsManifest) return 1;\n\t\t\treturn a.path.localeCompare(b.path);\n\t\t});\n\t\treturn out;\n\t}\n\n\t/**\n\t * Create-or-update a draft. Returns the (possibly new) draft id.\n\t * Throws `InvalidDraftError` when the file list does not include\n\t * a manifest.\n\t */\n\tasync save(opts: SaveDraftOptions): Promise<string> {\n\t\tconst manifestFilename = opts.kind === \"skill\" ? \"SKILL.md\" : \"AGENT.md\";\n\t\tif (!opts.files.some((f) => f.path === manifestFilename)) {\n\t\t\tthrow new InvalidDraftError(`${manifestFilename} is required`);\n\t\t}\n\t\tconst id = opts.id ?? generateDraftId();\n\t\tconst dir = resolveDraftDir(opts.kind, id);\n\t\t// Clean the directory so a re-save with fewer files actually\n\t\t// removes the old ones (otherwise stale files linger).\n\t\tawait fs.rm(dir, { recursive: true, force: true });\n\t\tawait fs.mkdir(dir, { recursive: true });\n\t\t// Write every file atomically (tmp + rename).\n\t\tfor (const f of opts.files) {\n\t\t\tconst full = path.join(dir, f.path);\n\t\t\tawait fs.mkdir(path.dirname(full), { recursive: true });\n\t\t\tconst tmp = `${full}.${process.pid}.${Date.now()}.tmp`;\n\t\t\tawait fs.writeFile(tmp, f.content, \"utf8\");\n\t\t\tawait fs.rename(tmp, full);\n\t\t}\n\t\t// Sweep stale .tmp-* leftovers (in case a previous save crashed).\n\t\tawait this.sweepTmp(dir);\n\t\treturn id;\n\t}\n\n\t/** Delete a draft. ENOENT is silently ignored (idempotent). */\n\tasync delete(kind: SkillKind, id: string): Promise<void> {\n\t\tconst dir = resolveDraftDir(kind, id);\n\t\ttry {\n\t\t\tawait fs.rm(dir, { recursive: true, force: true });\n\t\t} catch (err) {\n\t\t\tif ((err as NodeJS.ErrnoException).code === \"ENOENT\") return;\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/** Best-effort cleanup of `.tmp-*` orphan files in the draft dir. */\n\tprivate async sweepTmp(dir: string): Promise<void> {\n\t\tlet names: string[];\n\t\ttry {\n\t\t\tnames = await fs.readdir(dir);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const n of names) {\n\t\t\tif (n.endsWith(\".tmp\")) {\n\t\t\t\tawait fs.rm(path.join(dir, n), { force: true }).catch(() => undefined);\n\t\t\t}\n\t\t}\n\t}\n}\n\nasync function collectRecursive(absDir: string, root: string, out: SkillFile[]): Promise<void> {\n\tlet dirents: import(\"node:fs\").Dirent[];\n\ttry {\n\t\tdirents = await fs.readdir(absDir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const d of dirents) {\n\t\tif (d.name.endsWith(\".tmp\")) continue;\n\t\tconst full = path.join(absDir, d.name);\n\t\tif (d.isDirectory()) {\n\t\t\tawait collectRecursive(full, root, out);\n\t\t} else if (d.isFile()) {\n\t\t\tconst content = await fs.readFile(full, \"utf8\");\n\t\t\tout.push({ path: path.relative(root, full), content });\n\t\t}\n\t}\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { DEFAULT_REPO_LAYOUT, loadRepoLayout, type RepoLayoutDescriptor } from \"../repo-layout\";\nimport type { SkillDetail, SkillEntry, SkillFile, SkillKind } from \"./types\";\nimport { SkillNotFoundError } from \"./types\";\n\n/**\n * Minimal frontmatter reader — same shape as the extension's\n * `WorkspaceSkillsInitializationService.extractFrontmatter`. We avoid\n * pulling a YAML dep into core; SKILL.md / AGENT.md in the wild use\n * a strict subset (top-level `key: value` pairs delimited by `---`).\n * Anything more exotic (nested mappings, multi-line scalars) is\n * passed through as the raw string in the corresponding value.\n */\nexport function extractFrontmatter(\n\traw: string\n): { data: Record<string, unknown>; body: string } | null {\n\tif (typeof raw !== \"string\") return null;\n\tconst match = raw.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n\tif (!match) return null;\n\tconst yamlBody = match[1] ?? \"\";\n\tconst body = match[2] ?? \"\";\n\tconst data: Record<string, unknown> = {};\n\tfor (const line of yamlBody.split(/\\r?\\n/)) {\n\t\tif (line.trim().length === 0) continue;\n\t\tif (line.trim().startsWith(\"#\")) continue;\n\t\tconst kv = line.match(/^([a-zA-Z_][\\w-]*)\\s*:\\s*(.*)$/);\n\t\tif (!kv?.[1]) continue;\n\t\tlet value: unknown = (kv[2] ?? \"\").trim();\n\t\tif (typeof value === \"string\") {\n\t\t\tif (\n\t\t\t\t(value.startsWith('\"') && value.endsWith('\"')) ||\n\t\t\t\t(value.startsWith(\"'\") && value.endsWith(\"'\"))\n\t\t\t) {\n\t\t\t\tvalue = value.slice(1, -1);\n\t\t\t}\n\t\t}\n\t\tdata[kv[1]] = value;\n\t}\n\treturn { data, body };\n}\n\nexport type { SkillDetail, SkillEntry, SkillFile, SkillKind } from \"./types\";\n/**\n * Skill & Agent v2 — SkillStore (M4)\n *\n * Scans the local `~/.serviceme/repos/<id>/` tree to produce a unified\n * list of skills + agents. Local-only — no git, no network. Spec §5.5.\n *\n * Layout normalization (spec §12):\n * 1. **Standard layout** — `skills/<name>/SKILL.md` or\n * `agents/<name>/AGENT.md` under the repo root.\n * 2. **Anthropic flat layout** — `<name>/SKILL.md` directly under\n * the repo root (the `skills/` prefix is omitted).\n * 3. **awesome-copilot style** — root contains README + subdirs\n * that are skills/agents/instructions; we ignore README and\n * any dir without a manifest file.\n * 4. **Flat agent files** — a directory (commonly `agents/` or\n * `agents/official/`) full of `<name>.agent.md` files, one per\n * agent, with NO per-agent subdirectory. This is the prevailing\n * real-world convention (VS Code custom chat agents, GitHub's\n * awesome-copilot, and our own official ms-skills repo all ship\n * agents this way) — unlike skills, agents in the wild are\n * essentially never a `<name>/AGENT.md` directory pair. Detected\n * unconditionally, no `.serviceme-repo.json` opt-in required.\n *\n * Implementation: walk every directory under the repo root, check for\n * SKILL.md or AGENT.md, register if present. We skip the repo root\n * itself (no manifest lives at the top), `.git/`, and `node_modules/`.\n * Flat `*.agent.md` files are checked alongside directories at every\n * level of the walk.\n */\n// Re-export the error class + shared types so callers can\n// `import { SkillNotFoundError, SkillFile } from \"...\"`.\nexport { SkillNotFoundError } from \"./types\";\n\nexport class SkillStore {\n\tprivate readonly repoRoots: Map<string, string>;\n\n\tconstructor(opts: {\n\t\trepos: ReadonlyArray<{ id: string; rootPath: string }>;\n\t}) {\n\t\tthis.repoRoots = new Map();\n\t\tfor (const r of opts.repos) this.repoRoots.set(r.id, r.rootPath);\n\t}\n\n\t/** All skills + agents across all registered repos. */\n\tasync listAll(): Promise<SkillEntry[]> {\n\t\tconst all: SkillEntry[] = [];\n\t\tfor (const repoId of this.repoRoots.keys()) {\n\t\t\tall.push(...(await this.listByRepo(repoId)));\n\t\t}\n\t\treturn all;\n\t}\n\n\t/** All skills + agents under a single repo. */\n\tasync listByRepo(repoId: string): Promise<SkillEntry[]> {\n\t\tconst root = this.repoRoots.get(repoId);\n\t\tif (!root) return [];\n\t\tconst layout = (await loadRepoLayout(root)) ?? DEFAULT_REPO_LAYOUT;\n\t\tconst entries: SkillEntry[] = [];\n\t\tawait walkForEntries(root, repoId, entries, layout);\n\t\treturn entries;\n\t}\n\n\t/** Single entry detail (manifest + all files inside its dir). */\n\tasync get(repoId: string, name: string): Promise<SkillDetail> {\n\t\tconst entries = await this.listByRepo(repoId);\n\t\tconst found = entries.find((e) => e.name === name);\n\t\tif (!found) throw new SkillNotFoundError(repoId, name);\n\t\tconst files = await this.getFiles(repoId, name);\n\t\treturn { ...found, files };\n\t}\n\n\t/** All files inside a single entry's directory (manifest + extras). */\n\tasync getFiles(repoId: string, name: string): Promise<SkillFile[]> {\n\t\tconst entries = await this.listByRepo(repoId);\n\t\tconst found = entries.find((e) => e.name === name);\n\t\tif (!found) throw new SkillNotFoundError(repoId, name);\n\n\t\tconst out: SkillFile[] = [];\n\t\t// Flat single-file entries (e.g. `agents/foo.agent.md`) have `dir`\n\t\t// pointing at the manifest file itself, not a directory — there's\n\t\t// no sibling files to collect, just the one manifest.\n\t\tif (found.dir === found.manifestPath) {\n\t\t\tconst content = await fs.readFile(found.manifestPath, \"utf8\");\n\t\t\treturn [{ path: path.basename(found.manifestPath), content }];\n\t\t}\n\t\tawait collectFilesRecursive(found.dir, found.dir, out);\n\t\t// Sort for determinism (manifest first, then alphabetical)\n\t\tout.sort((a, b) => {\n\t\t\tconst aIsManifest = a.path === \"SKILL.md\" || a.path === \"AGENT.md\";\n\t\t\tconst bIsManifest = b.path === \"SKILL.md\" || b.path === \"AGENT.md\";\n\t\t\tif (aIsManifest && !bIsManifest) return -1;\n\t\t\tif (bIsManifest && !aIsManifest) return 1;\n\t\t\treturn a.path.localeCompare(b.path);\n\t\t});\n\t\treturn out;\n\t}\n}\n\n// ─────────────────────────────────────────────────────────────────────\n// Internals\n// ─────────────────────────────────────────────────────────────────────\n\nconst SKIP_DIRS = new Set([\".git\", \"node_modules\", \".vscode\", \"dist\", \"build\", \"out\"]);\n\n/**\n * Directories that are never treated as candidate skill/agent parents\n * during the top-level catalog walk. `plugins/` is a distinct concept\n * from the skill/agent catalog itself — a plugin bundle\n * (`plugins/<scope>/<plugin-name>/{skills,agents}/...`) re-packages\n * ALREADY-cataloged official skills/agents (see the plugin's own\n * `catalog.json`, which lists them by name) purely for discovery\n * grouping. Walking into it produces duplicate `SkillEntry` values\n * with the SAME `repoId`+`name` as their real, top-level counterpart\n * (e.g. `skills/official/acreadiness-assess` AND\n * `plugins/official/acreadiness-cockpit/skills/acreadiness-assess`),\n * which breaks anything keyed on `repoId/name` (React list rendering,\n * the \"installed\" lookup, `SkillStore.get()`'s `.find()`).\n *\n * Scoped separately from `SKIP_DIRS` (used by both this walk AND\n * `collectFilesRecursive`) so an actual skill that happens to ship its\n * own `plugins/` asset folder still has that folder listed in its own\n * file detail view.\n */\nconst SKIP_TOP_LEVEL_SCAN_DIRS = new Set([...SKIP_DIRS, \"plugins\"]);\n\n/** Suffix that marks a standalone file as a flat agent manifest (no wrapping dir). */\nconst FLAT_AGENT_FILE_SUFFIX = \".agent.md\";\n\n/**\n * Recursive walk that yields SkillEntry values for any directory\n * containing a SKILL.md or AGENT.md file, PLUS any standalone\n * `<name>.agent.md` file (see class doc, layout style 4). The dir\n * itself is the \"entry root\" for directory-based entries; the\n * manifest file itself is the \"entry root\" for flat agent files.\n *\n * The optional `layout` descriptor (from `.serviceme-repo.json`)\n * widens the scan: included subdirs are walked even without a\n * manifest at the top level, and `treatFilesAsSkills` surfaces\n * `<name>.md` files inside them as skills (frontmatter parsed\n * from the file body).\n */\nasync function walkForEntries(\n\trootDir: string,\n\trepoId: string,\n\tout: SkillEntry[],\n\tlayout: RepoLayoutDescriptor = DEFAULT_REPO_LAYOUT\n): Promise<void> {\n\tlet dirents: import(\"node:fs\").Dirent[];\n\ttry {\n\t\tdirents = await fs.readdir(rootDir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\n\tfor (const d of dirents) {\n\t\tif (d.isFile() && d.name.endsWith(FLAT_AGENT_FILE_SUFFIX)) {\n\t\t\tconst filePath = path.join(rootDir, d.name);\n\t\t\tconst stat = await fs.stat(filePath);\n\t\t\tconst content = await fs.readFile(filePath, \"utf8\");\n\t\t\tconst parsed = extractFrontmatter(content);\n\t\t\tout.push({\n\t\t\t\trepoId,\n\t\t\t\tname: d.name.slice(0, -FLAT_AGENT_FILE_SUFFIX.length),\n\t\t\t\tkind: \"agent\",\n\t\t\t\tmanifestPath: filePath,\n\t\t\t\t// Sentinel: `dir === manifestPath` marks a flat single-file\n\t\t\t\t// entry (no directory of its own — see getFiles()).\n\t\t\t\tdir: filePath,\n\t\t\t\tfrontmatter: parsed?.data ?? {},\n\t\t\t\tmodifiedAt: stat.mtime.toISOString(),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (!d.isDirectory()) continue;\n\t\tif (SKIP_TOP_LEVEL_SCAN_DIRS.has(d.name)) continue;\n\t\tif (layout.exclude.includes(d.name)) continue;\n\t\tconst childDir = path.join(rootDir, d.name);\n\t\tconst kind = await detectKind(childDir);\n\t\tif (kind) {\n\t\t\tconst manifestFilename = kind === \"skill\" ? \"SKILL.md\" : \"AGENT.md\";\n\t\t\tconst manifestPath = path.join(childDir, manifestFilename);\n\t\t\tconst stat = await fs.stat(manifestPath);\n\t\t\tconst content = await fs.readFile(manifestPath, \"utf8\");\n\t\t\tconst parsed = extractFrontmatter(content);\n\t\t\tout.push({\n\t\t\t\trepoId,\n\t\t\t\tname: d.name,\n\t\t\t\tkind,\n\t\t\t\tmanifestPath,\n\t\t\t\tdir: childDir,\n\t\t\t\tfrontmatter: parsed?.data ?? {},\n\t\t\t\tmodifiedAt: stat.mtime.toISOString(),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\t// No manifest at the top — recurse, but ALSO check whether\n\t\t// the layout descriptor wants this subdir widened.\n\t\tawait walkForEntries(childDir, repoId, out, layout);\n\t\tif (layout.treatFilesAsSkills && layout.include.includes(d.name)) {\n\t\t\tawait surfaceFilesAsSkills(childDir, d.name, repoId, out);\n\t\t}\n\t}\n}\n\n/**\n * Walk `<includedSubdir>/<file>.md` and register each as a skill.\n * The `.md` file itself is the manifest — frontmatter (if any) is\n * parsed and surfaced. This is the awesome-copilot style: a\n * `prompts/` dir full of `<name>.md` files, no SKILL.md anywhere.\n */\nasync function surfaceFilesAsSkills(\n\tabsDir: string,\n\trelName: string,\n\trepoId: string,\n\tout: SkillEntry[]\n): Promise<void> {\n\tlet dirents: import(\"node:fs\").Dirent[];\n\ttry {\n\t\tdirents = await fs.readdir(absDir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const d of dirents) {\n\t\tif (!d.isFile()) continue;\n\t\tif (!d.name.endsWith(\".md\")) continue;\n\t\tconst filePath = path.join(absDir, d.name);\n\t\tconst stat = await fs.stat(filePath);\n\t\tconst content = await fs.readFile(filePath, \"utf8\");\n\t\tconst parsed = extractFrontmatter(content);\n\t\t// Entry name = \"<subdir>/<file>\" (with the .md stripped) so it\n\t\t// stays unique even when two included subdirs share filenames.\n\t\tconst entryName = `${relName}/${d.name.replace(/\\.md$/, \"\")}`;\n\t\tout.push({\n\t\t\trepoId,\n\t\t\tname: entryName,\n\t\t\tkind: \"skill\",\n\t\t\tmanifestPath: filePath,\n\t\t\tdir: absDir,\n\t\t\tfrontmatter: parsed?.data ?? {},\n\t\t\tmodifiedAt: stat.mtime.toISOString(),\n\t\t});\n\t}\n}\n\n/**\n * Returns the SkillKind of a directory if it contains a manifest file,\n * or `null` if it doesn't. Manifest precedence: SKILL.md wins if both\n * are present (defensive — the spec separates skills and agents into\n * different subdir trees, so this collision shouldn't happen in\n * well-formed repos).\n */\nasync function detectKind(dir: string): Promise<SkillKind | null> {\n\tconst [hasSkill, hasAgent] = await Promise.all([\n\t\tfs\n\t\t\t.access(path.join(dir, \"SKILL.md\"))\n\t\t\t.then(() => true)\n\t\t\t.catch(() => false),\n\t\tfs\n\t\t\t.access(path.join(dir, \"AGENT.md\"))\n\t\t\t.then(() => true)\n\t\t\t.catch(() => false),\n\t]);\n\tif (hasSkill) return \"skill\";\n\tif (hasAgent) return \"agent\";\n\treturn null;\n}\n\n/**\n * Walk a skill/agent directory recursively, pushing every file into\n * `out` with its path RELATIVE to the entry root. Skips `.git/`,\n * `node_modules/`, and the SKIP_DIRS set.\n */\nasync function collectFilesRecursive(\n\tabsDir: string,\n\tentryRoot: string,\n\tout: SkillFile[]\n): Promise<void> {\n\tlet dirents: import(\"node:fs\").Dirent[];\n\ttry {\n\t\tdirents = await fs.readdir(absDir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const d of dirents) {\n\t\tif (SKIP_DIRS.has(d.name)) continue;\n\t\tconst full = path.join(absDir, d.name);\n\t\tif (d.isDirectory()) {\n\t\t\tawait collectFilesRecursive(full, entryRoot, out);\n\t\t} else if (d.isFile()) {\n\t\t\tconst content = await fs.readFile(full, \"utf8\");\n\t\t\tout.push({ path: path.relative(entryRoot, full), content });\n\t\t}\n\t}\n}\n","/**\n * Spec §12 — Repo layout descriptor.\n *\n * Some third-party repos (awesome-copilot, composio) use a\n * non-default layout that mixes prompts / instructions / agents /\n * hooks / plugins at the root. The default SkillStore scan\n * (see ../skill-store/index.ts) only picks up directories that\n * contain a `SKILL.md` or `AGENT.md` manifest, so prompts stored\n * as `<subdir>/<name>.md` (no manifest) get missed.\n *\n * This module is the loader for the optional `.serviceme-repo.json`\n * marker file a repo author can drop in at the repo root to opt\n * into a wider scan. The schema:\n *\n * {\n * \"schema\": 1,\n * \"include\": [\"prompts\", \"instructions\"], // subdirs to treat as \"skill dirs\"\n * \"exclude\": [\"hooks\", \"plugins\"], // subdirs to skip\n * \"treatFilesAsSkills\": true // inside an included subdir, each .md file is a skill\n * }\n *\n * All four fields are optional. The defaults match the v0.2\n * walkForEntries behaviour (manifest-based, no marker, no\n * extensions), so adding the file is purely additive.\n *\n * @see docs/architecture/skill-agent-v2-repo.md §12\n */\n\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nconst MARKER_FILENAME = \".serviceme-repo.json\";\nconst SUPPORTED_SCHEMA = 1;\n\nexport interface RepoLayoutDescriptor {\n\t/** Schema version. Currently always 1. */\n\tschema: number;\n\t/** Subdirs (relative to repo root) to walk for entries. */\n\tinclude: string[];\n\t/** Subdirs (relative to repo root) to skip, even if a parent is included. */\n\texclude: string[];\n\t/**\n\t * When true, an included subdir's `<name>.md` files are\n\t * surfaced as skills (frontmatter is parsed from each file as\n\t * a stand-in for SKILL.md). When false (default), the\n\t * include list only widens the recursion — the manifest rule\n\t * still applies.\n\t */\n\ttreatFilesAsSkills: boolean;\n}\n\n/**\n * Default descriptor used when no `.serviceme-repo.json` is\n * present. The shape is the same as what an empty marker would\n * produce, so callers don't have to special-case the \"no marker\"\n * branch.\n */\nexport const DEFAULT_REPO_LAYOUT: RepoLayoutDescriptor = {\n\tschema: SUPPORTED_SCHEMA,\n\tinclude: [],\n\texclude: [],\n\ttreatFilesAsSkills: false,\n};\n\n/**\n * Try to load a `.serviceme-repo.json` from the given repo root.\n * Returns `DEFAULT_REPO_LAYOUT` (not undefined) when the file is\n * absent — callers branch on `include.length` / `treatFilesAsSkills`\n * to decide whether to widen the scan.\n *\n * Malformed markers (bad JSON, wrong schema) are surfaced as\n * `null` so callers can warn the user instead of silently\n * treating them as the default.\n */\nexport async function loadRepoLayout(repoRoot: string): Promise<RepoLayoutDescriptor | null> {\n\tconst markerPath = path.join(repoRoot, MARKER_FILENAME);\n\tlet raw: string;\n\ttry {\n\t\traw = await fs.readFile(markerPath, \"utf8\");\n\t} catch {\n\t\treturn DEFAULT_REPO_LAYOUT;\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (!parsed || typeof parsed !== \"object\") return null;\n\tconst obj = parsed as Record<string, unknown>;\n\n\tif (obj.schema !== SUPPORTED_SCHEMA) return null;\n\tif (!Array.isArray(obj.include) || obj.include.some((s) => typeof s !== \"string\")) {\n\t\treturn null;\n\t}\n\tif (!Array.isArray(obj.exclude) || obj.exclude.some((s) => typeof s !== \"string\")) {\n\t\treturn null;\n\t}\n\tif (typeof obj.treatFilesAsSkills !== \"boolean\") return null;\n\n\treturn {\n\t\tschema: SUPPORTED_SCHEMA,\n\t\tinclude: obj.include as string[],\n\t\texclude: obj.exclude as string[],\n\t\ttreatFilesAsSkills: obj.treatFilesAsSkills,\n\t};\n}\n\n/** Subdir name → entry kind override (rarely needed; default = manifest-driven). */\nexport type KindOverride = Map<string, \"skill\" | \"agent\">;\n","import { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n\tcreateServicemeError,\n\ttype EnvironmentCheckResult,\n\tKNOWN_ENVIRONMENT_TOOLS,\n\ttype KnownEnvironmentTool,\n\ttype ToolCheckResult,\n} from \"@serviceme/devtools-protocol\";\nimport { runCommand } from \"../process/runCommand\";\n\nconst DEFAULT_TOOL_CHECK_TIMEOUT_MS = 5_000;\nconst TOOL_CHECK_TIMEOUT_MS: Partial<Record<KnownEnvironmentTool, number>> = {\n\tnuget: 12_000,\n\tnvm: 8_000,\n\tdotnet: 8_000,\n\t// nvm4w/npm shims and globally-installed .cmd tools (pnpm, nrm) are slow to\n\t// resolve on cold PATHs. Give them extra headroom so the version probe\n\t// doesn't fall through to the generic 5s default.\n\tnpm: 15_000,\n\tpnpm: 15_000,\n\tnrm: 15_000,\n\tcodegraph: 15_000,\n\tocx: 15_000,\n};\nconst POSIX_LOGIN_SHELL_FALLBACK_TOOLS = new Set<string>([\n\t\"npm\",\n\t\"pnpm\",\n\t\"nrm\",\n\t\"node\",\n\t\"dotnet\",\n\t\"rtk\",\n\t\"codegraph\",\n\t\"ocx\",\n]);\nconst ERROR_CODE_NOT_FOUND = 127;\nconst ERROR_CODE_TIMEOUT = \"ETIMEDOUT\";\n\n/**\n * Default nvm install roots, searched in order when `$NVM_DIR` is unset.\n * `~/.nvm` is the nvm-sh default; the Homebrew variants cover the\n * `brew install nvm` install path on Intel and Apple Silicon macs.\n */\nconst NVM_FALLBACK_DIRS: ReadonlyArray<string> = [\n\t\"/opt/homebrew/opt/nvm\",\n\t\"/usr/local/opt/nvm\",\n\t\"$HOME/.nvm\",\n];\n\n/**\n * Resolve the login shell to use for POSIX fallbacks.\n *\n * We deliberately do NOT hardcode `/bin/bash`. On macOS Catalina+ the user's\n * default shell is zsh, and nvm-sh's installer writes its sourcing line to\n * `~/.zshrc` — never `~/.bash_profile` — so a fixed `bash -lc` snippet will\n * silently miss it on a fresh Mac. Using `$SHELL` (the user's actual login\n * shell) lets the fallback chain match whatever rc file the user picked.\n *\n * `envShell` lets callers (and tests) override the `$SHELL` lookup so we\n * stay deterministic across machines without monkeypatching `process.env`.\n */\nfunction resolveUserLoginShell(platform: NodeJS.Platform, envShell: string | undefined): string {\n\tconst shell = envShell?.trim();\n\tif (shell && shell.length > 0) {\n\t\treturn shell;\n\t}\n\treturn platform === \"darwin\" ? \"/bin/zsh\" : \"/bin/bash\";\n}\n\n/**\n * Build the one-line snippet that (a) honors `$NVM_DIR` and falls back to\n * known install roots, then (b) sources `nvm.sh` only when the resolved file\n * is readable. Returns the snippet (without trailing semicolon) so callers\n * can append `; <command>` to it.\n */\nfunction buildNvmSourcingSnippet(): string {\n\tconst fallbackList = NVM_FALLBACK_DIRS.map((dir) => `\"${dir}\"`).join(\" \");\n\treturn (\n\t\t`for d in $NVM_DIR ${fallbackList}; do ` +\n\t\t`[ -n \"$d\" ] && [ -s \"$d/nvm.sh\" ] && export NVM_DIR=\"$d\" && . \"$d/nvm.sh\" && break; ` +\n\t\t`done`\n\t);\n}\n\n/**\n * Built shell-arg list for the POSIX fallback: pick the user's login shell,\n * run it as a login shell, source nvm, then exec the requested command.\n *\n * `loginArgs(command)` returns the `-c` snippet final form so the original\n * `getToolPathFromLoginShell` / `getToolVersion` call sites stay readable.\n */\nfunction posixLoginShellArgs(\n\tplatform: NodeJS.Platform,\n\tenvShell: string | undefined,\n\tcommand: string\n): { command: string; args: string[] } {\n\treturn {\n\t\tcommand: resolveUserLoginShell(platform, envShell),\n\t\targs: [\"-lc\", `${buildNvmSourcingSnippet()}; ${command}`],\n\t};\n}\n\ninterface ExecErrorLike {\n\tcode?: string | number;\n\tmessage?: string;\n\tstdout?: string;\n\tstderr?: string;\n}\n\nexport interface EnvironmentInspectorOptions {\n\t/**\n\t * Override the command runner. Production callers should leave this\n\t * undefined and rely on the default `runCommand` from `../process/runCommand`;\n\t * tests inject a fake to drive error/edge branches without spawning\n\t * real subprocesses.\n\t */\n\trunCommand?: typeof runCommand;\n\t/**\n\t * Override the detected platform. Defaults to `process.platform`. Tests\n\t * use this to exercise the Windows-specific branches of the inspector.\n\t */\n\tplatform?: NodeJS.Platform;\n\t/**\n\t * Override the value returned by `$SHELL` for POSIX login-shell\n\t * fallbacks. Defaults to `process.env.SHELL`. Tests pin this so the\n\t * fallback chain stays deterministic regardless of the developer's\n\t * host machine.\n\t */\n\tshell?: string;\n}\n\nexport class EnvironmentInspector {\n\tprivate readonly runCommandFn: typeof runCommand;\n\tprivate readonly platform: NodeJS.Platform;\n\tprivate readonly shell: string | undefined;\n\t/**\n\t * Per-check-cycle result cache. `checkEnvironment()` clears it before\n\t * probing so a fresh cycle never returns stale results, while concurrent\n\t * or repeated `checkTool()` calls within the same cycle share one probe\n\t * (deduplicating expensive login-shell fallbacks).\n\t */\n\tprivate readonly toolCheckCache = new Map<KnownEnvironmentTool, Promise<ToolCheckResult>>();\n\n\tconstructor(options: EnvironmentInspectorOptions = {}) {\n\t\tthis.runCommandFn = options.runCommand ?? runCommand;\n\t\tthis.platform = options.platform ?? process.platform;\n\t\tthis.shell = options.shell ?? process.env.SHELL;\n\t}\n\n\tasync checkEnvironment(): Promise<EnvironmentCheckResult> {\n\t\tthis.toolCheckCache.clear();\n\n\t\tconst results = await Promise.all(\n\t\t\tKNOWN_ENVIRONMENT_TOOLS.map(async (tool) => [tool, await this.checkTool(tool)] as const)\n\t\t);\n\n\t\treturn Object.fromEntries(results) as EnvironmentCheckResult;\n\t}\n\n\tasync checkTool(toolName: KnownEnvironmentTool): Promise<ToolCheckResult> {\n\t\tif (!/^[a-zA-Z0-9-]+$/.test(toolName)) {\n\t\t\tthrow createServicemeError(\"invalid_params\", \"Invalid tool name.\");\n\t\t}\n\n\t\tconst cached = this.toolCheckCache.get(toolName);\n\t\tif (cached) {\n\t\t\treturn cached;\n\t\t}\n\n\t\tconst check = this.performToolCheck(toolName);\n\t\tthis.toolCheckCache.set(toolName, check);\n\t\treturn check;\n\t}\n\n\tprivate async performToolCheck(toolName: KnownEnvironmentTool): Promise<ToolCheckResult> {\n\t\ttry {\n\t\t\tif (toolName === \"nvm\") {\n\t\t\t\treturn await this.checkNvm();\n\t\t\t}\n\n\t\t\tif (toolName === \"nuget\") {\n\t\t\t\treturn await this.checkNuget();\n\t\t\t}\n\n\t\t\tconst toolPath = await this.getToolPath(toolName);\n\t\t\tconst version = await this.getToolVersion(toolName);\n\n\t\t\treturn {\n\t\t\t\tinstalled: true,\n\t\t\t\tversion,\n\t\t\t\tpath: toolPath,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn this.handleToolCheckError(error);\n\t\t}\n\t}\n\n\tprivate async getToolPath(toolName: string): Promise<string | undefined> {\n\t\ttry {\n\t\t\tconst isWindows = this.platform === \"win32\";\n\t\t\tconst result = await this.runCommandFn(isWindows ? \"where\" : \"which\", {\n\t\t\t\targs: [toolName],\n\t\t\t\ttimeoutMs: this.getToolTimeout(toolName as KnownEnvironmentTool),\n\t\t\t});\n\n\t\t\treturn result.stdout\n\t\t\t\t.split(/\\r?\\n/)\n\t\t\t\t.map((line) => line.trim())\n\t\t\t\t.find((line) => line.length > 0);\n\t\t} catch {\n\t\t\tif (this.shouldUsePosixLoginShellFallback(toolName)) {\n\t\t\t\treturn this.getToolPathFromLoginShell(toolName);\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate async getToolPathFromLoginShell(toolName: string): Promise<string | undefined> {\n\t\ttry {\n\t\t\tconst spawnArgs = posixLoginShellArgs(this.platform, this.shell, `command -v ${toolName}`);\n\t\t\tconst result = await this.runCommandFn(spawnArgs.command, {\n\t\t\t\targs: spawnArgs.args,\n\t\t\t\ttimeoutMs: this.getToolTimeout(toolName as KnownEnvironmentTool),\n\t\t\t});\n\n\t\t\treturn result.stdout\n\t\t\t\t.split(/\\r?\\n/)\n\t\t\t\t.map((line) => line.trim())\n\t\t\t\t.find((line) => line.length > 0);\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t/**\n\t * On Windows, prefer the `.cmd` shim for npm/pnpm/nrm. nvm4w registers\n\t * BOTH an extensionless entry (pointing to node.exe) and a `<tool>.cmd`\n\t * wrapper; `where` returns them in that order, and the extensionless one\n\t * would just print node's own version.\n\t */\n\tprivate async getToolShimPath(toolName: string): Promise<string | undefined> {\n\t\ttry {\n\t\t\tconst result = await this.runCommandFn(\"where\", {\n\t\t\t\targs: [toolName],\n\t\t\t\ttimeoutMs: this.getToolTimeout(toolName as KnownEnvironmentTool),\n\t\t\t});\n\n\t\t\tconst candidates = result.stdout\n\t\t\t\t.split(/\\r?\\n/)\n\t\t\t\t.map((line) => line.trim())\n\t\t\t\t.filter((line) => line.length > 0);\n\n\t\t\tconst cmdShim = candidates.find((line) => line.toLowerCase().endsWith(\".cmd\"));\n\t\t\treturn cmdShim ?? candidates[0];\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate async getToolVersion(toolName: string): Promise<string> {\n\t\ttry {\n\t\t\tconst invocation = await this.getVersionInvocation(toolName);\n\t\t\tconst result = await this.runCommandFn(invocation.command, {\n\t\t\t\targs: invocation.args,\n\t\t\t\ttimeoutMs: this.getToolTimeout(toolName as KnownEnvironmentTool),\n\t\t\t});\n\n\t\t\treturn this.parseVersion(toolName, result.stdout || result.stderr);\n\t\t} catch (error) {\n\t\t\tif (!this.shouldUsePosixLoginShellFallback(toolName)) {\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tconst fallbackArgs = posixLoginShellArgs(this.platform, this.shell, `${toolName} --version`);\n\t\t\tconst fallbackResult = await this.runCommandFn(fallbackArgs.command, {\n\t\t\t\targs: fallbackArgs.args,\n\t\t\t\ttimeoutMs: this.getToolTimeout(toolName as KnownEnvironmentTool),\n\t\t\t});\n\t\t\tconst fallbackOutput = (fallbackResult.stdout || fallbackResult.stderr).trim();\n\t\t\tif (!fallbackOutput) {\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\treturn this.parseVersion(toolName, fallbackOutput);\n\t\t}\n\t}\n\n\tprivate shouldUsePosixLoginShellFallback(toolName: string): boolean {\n\t\treturn this.platform !== \"win32\" && POSIX_LOGIN_SHELL_FALLBACK_TOOLS.has(toolName);\n\t}\n\n\tprivate async checkNvm(): Promise<ToolCheckResult> {\n\t\tif (this.platform === \"win32\") {\n\t\t\ttry {\n\t\t\t\tconst result = await this.runCommandFn(\"cmd.exe\", {\n\t\t\t\t\targs: [\"/c\", \"nvm version\"],\n\t\t\t\t\ttimeoutMs: this.getToolTimeout(\"nvm\"),\n\t\t\t\t});\n\n\t\t\t\treturn {\n\t\t\t\t\tinstalled: true,\n\t\t\t\t\tversion: result.stdout.trim(),\n\t\t\t\t\tpath: await this.getToolPath(\"nvm\"),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn {\n\t\t\t\t\tinstalled: false,\n\t\t\t\t\terror: \"Not installed\",\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\t// Avoid an unconditional login-shell spawn: when nvm.sh is not\n\t\t\t// present in any well-known location, report \"Not installed\"\n\t\t\t// without launching a shell (a login shell would source\n\t\t\t// ~/.zshrc, which may block on slow startup hooks).\n\t\t\tif (!this.resolveNvmShPath()) {\n\t\t\t\treturn {\n\t\t\t\t\tinstalled: false,\n\t\t\t\t\terror: \"Not installed\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst nvmArgs = posixLoginShellArgs(this.platform, this.shell, \"nvm --version\");\n\t\t\tconst result = await this.runCommandFn(nvmArgs.command, {\n\t\t\t\targs: nvmArgs.args,\n\t\t\t\ttimeoutMs: this.getToolTimeout(\"nvm\"),\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tinstalled: true,\n\t\t\t\tversion: result.stdout.trim(),\n\t\t\t\tpath: \"$HOME/.nvm/nvm.sh\",\n\t\t\t};\n\t\t} catch {\n\t\t\treturn {\n\t\t\t\tinstalled: false,\n\t\t\t\terror: \"Not installed\",\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Resolve `nvm.sh` from `$NVM_DIR` and the known install roots without\n\t * spawning a shell. Returns undefined when nvm is not installed in any\n\t * well-known location, letting callers skip a login-shell probe.\n\t */\n\tprivate resolveNvmShPath(): string | undefined {\n\t\tconst candidates: string[] = [];\n\t\tconst nvmDir = process.env.NVM_DIR?.trim();\n\t\tif (nvmDir && nvmDir.length > 0) {\n\t\t\tcandidates.push(nvmDir);\n\t\t}\n\t\tfor (const dir of NVM_FALLBACK_DIRS) {\n\t\t\tcandidates.push(dir.replace(/^\\$HOME/, homedir()));\n\t\t}\n\n\t\tfor (const candidate of candidates) {\n\t\t\tconst scriptPath = join(candidate, \"nvm.sh\");\n\t\t\tif (existsSync(scriptPath)) {\n\t\t\t\treturn scriptPath;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate async checkNuget(): Promise<ToolCheckResult> {\n\t\tconst dotnetPath = await this.getToolPath(\"dotnet\");\n\t\tif (!dotnetPath) {\n\t\t\treturn {\n\t\t\t\tinstalled: false,\n\t\t\t\terror: \"dotnet CLI is not installed\",\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await this.runCommandFn(dotnetPath, {\n\t\t\t\targs: [\"nuget\", \"list\", \"source\"],\n\t\t\t\ttimeoutMs: this.getToolTimeout(\"nuget\"),\n\t\t\t});\n\n\t\t\t// ADL-003 boundary copy of `MEDALSOFT_NUGET_PRIVATE_SOURCE`\n\t\t\t// (shared/constants.ts) — core cannot depend on shared. Keep\n\t\t\t// in lock-step with the authoritative definition.\n\t\t\tconst privateSourceUrl = \"http://192.168.20.209:10010/nuget\";\n\t\t\tif (result.stdout.includes(privateSourceUrl)) {\n\t\t\t\treturn {\n\t\t\t\t\tinstalled: true,\n\t\t\t\t\tversion: \"Configured\",\n\t\t\t\t\tpath: privateSourceUrl,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tinstalled: false,\n\t\t\t\terror: \"Private source not configured\",\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn this.handleToolCheckError(error);\n\t\t}\n\t}\n\n\tprivate async getVersionInvocation(toolName: string): Promise<{\n\t\tcommand: string;\n\t\targs: string[];\n\t}> {\n\t\tconst isWindows = this.platform === \"win32\";\n\t\tif (isWindows && [\"npm\", \"pnpm\", \"nrm\", \"codegraph\", \"ocx\"].includes(toolName)) {\n\t\t\t// On Windows, resolve the `.cmd` shim and invoke it via an\n\t\t\t// explicit `cmd.exe /c <absolute-path>` — NOT the\n\t\t\t// `cmd.exe /d /s /c \"<tool> --version\"` form we used to use.\n\t\t\t// The earlier wrapper forced CMD to do a fresh PATH lookup\n\t\t\t// AND its `/s` quote-stripping pass on every call, which on\n\t\t\t// machines using nvm4w shims (or globally-installed npm\n\t\t\t// tools) blew past the 5s default timeout. Spawning the\n\t\t\t// resolved shim path directly skips both steps.\n\t\t\t//\n\t\t\t// nvm4w's `where <tool>` returns BOTH an extensionless entry\n\t\t\t// (pointing to node.exe) and the real `<tool>.cmd` wrapper;\n\t\t\t// we prefer the `.cmd` form so the shim chain still runs.\n\t\t\tconst shim = await this.getToolShimPath(toolName);\n\t\t\tif (shim) {\n\t\t\t\treturn {\n\t\t\t\t\tcommand: \"cmd.exe\",\n\t\t\t\t\targs: [\"/c\", shim, \"--version\"],\n\t\t\t\t};\n\t\t\t}\n\t\t\t// Fall back to letting CMD resolve it from PATH.\n\t\t\treturn {\n\t\t\t\tcommand: \"cmd.exe\",\n\t\t\t\targs: [\"/c\", toolName, \"--version\"],\n\t\t\t};\n\t\t}\n\n\t\tconst commands: Record<string, { command: string; args: string[] }> = {\n\t\t\tgit: { command: \"git\", args: [\"--version\"] },\n\t\t\tnode: { command: \"node\", args: [\"--version\"] },\n\t\t\tnpm: { command: \"npm\", args: [\"--version\"] },\n\t\t\tpnpm: { command: \"pnpm\", args: [\"--version\"] },\n\t\t\tnvm: { command: \"nvm\", args: [\"--version\"] },\n\t\t\tnrm: { command: \"nrm\", args: [\"--version\"] },\n\t\t\trtk: { command: \"rtk\", args: [\"--version\"] },\n\t\t\tcodegraph: { command: \"codegraph\", args: [\"--version\"] },\n\t\t\tocx: { command: \"ocx\", args: [\"--version\"] },\n\t\t\tdotnet: { command: \"dotnet\", args: [\"--version\"] },\n\t\t};\n\n\t\treturn commands[toolName] || { command: toolName, args: [\"--version\"] };\n\t}\n\n\tprivate getToolTimeout(toolName: KnownEnvironmentTool): number {\n\t\treturn TOOL_CHECK_TIMEOUT_MS[toolName] ?? DEFAULT_TOOL_CHECK_TIMEOUT_MS;\n\t}\n\n\tprivate parseVersion(toolName: string, output: string): string {\n\t\tconst cleaned = output.trim();\n\n\t\tswitch (toolName) {\n\t\t\tcase \"git\": {\n\t\t\t\tconst match = cleaned.match(/git version (\\d+\\.\\d+\\.\\d+)/);\n\t\t\t\treturn match?.[1] || cleaned;\n\t\t\t}\n\t\t\tcase \"node\": {\n\t\t\t\tconst match = cleaned.match(/v?(\\d+\\.\\d+\\.\\d+)/);\n\t\t\t\treturn match?.[1] || cleaned;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tconst semver = cleaned.match(/(\\d+\\.\\d+\\.\\d+)/);\n\t\t\t\tif (semver?.[1]) {\n\t\t\t\t\treturn semver[1];\n\t\t\t\t}\n\n\t\t\t\tconst simple = cleaned.match(/(\\d+\\.\\d+)/);\n\t\t\t\treturn simple?.[1] || cleaned;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate handleToolCheckError(error: unknown): ToolCheckResult {\n\t\tconst execError = error as ExecErrorLike;\n\t\tconst message = execError.message || String(error);\n\t\tconst code = execError.code;\n\n\t\tconst isNotFound =\n\t\t\tmessage.includes(\"command not found\") ||\n\t\t\tmessage.includes(\"not recognized\") ||\n\t\t\tmessage.includes(\"ENOENT\") ||\n\t\t\tmessage.includes(\"EACCES\") ||\n\t\t\tcode === \"ENOENT\" ||\n\t\t\tcode === \"EACCES\" ||\n\t\t\tcode === ERROR_CODE_NOT_FOUND;\n\t\tconst isTimeout = code === ERROR_CODE_TIMEOUT || message.toLowerCase().includes(\"timed out\");\n\n\t\treturn {\n\t\t\tinstalled: false,\n\t\t\terror: isNotFound ? \"Not installed\" : isTimeout ? \"Check timed out\" : message,\n\t\t};\n\t}\n}\n","import { spawn } from \"node:child_process\";\nimport * as path from \"node:path\";\n\nimport type {\n\tGitClientOptions,\n\tGitSpawner,\n\tGitSpawnResult,\n\tPullResult,\n\tPushResult,\n\tRemoteBranch,\n} from \"./types\";\nimport { GitError } from \"./types\";\n\n/**\n * Skill & Agent v2 — Client GitClient (M3)\n *\n * Wraps `git` so all upstream traffic flows through the server's git\n * proxy at `serverProxyBase`. See docs/architecture/skill-agent-v2-repo.md\n * §5.4.\n *\n * Why a wrapper instead of using libgit2 directly:\n * 1. `git` is already installed everywhere we run (extension host,\n * CLI). No native deps.\n * 2. Users can debug their skill repos with plain `git` commands when\n * SERVICEME is misbehaving — the wrappers keep a familiar CLI.\n * 3. libgit2's packfile negotiation has subtle correctness gaps that\n * we don't want to debug server-side.\n *\n * URL rewrite:\n * `https://github.com/owner/repo.git` → `<proxyBase>/<id>`\n *\n * When git hits `<proxyBase>/<id>` it discovers the proxy's\n * `/info/refs?service=git-upload-pack` endpoint via the smart-HTTP\n * protocol. From there the server takes over.\n */\nexport class GitClient {\n\tprivate readonly serverProxyBase: string;\n\tprivate readonly spawner: GitSpawner;\n\n\tconstructor(opts: GitClientOptions) {\n\t\t// Normalize: strip trailing slash so `rewriteRemoteUrl` is stable.\n\t\tthis.serverProxyBase = opts.serverProxyBase.replace(/\\/+$/, \"\");\n\t\tthis.spawner = opts.spawner ?? new NodeGitSpawner();\n\t}\n\n\t/**\n\t * Rewrite an upstream URL to its proxy form.\n\t *\n\t * Examples:\n\t * rewriteRemoteUrl(\"repo-aHR0cHM6Ly9naXRodWIuY29tL21lZGFsc29mdGNoaW5hL21zLXNraWxscy5naXQ\",\n\t * \"https://github.com/medalsoftchina/ms-skills.git\")\n\t * → \"http://localhost:3000/git-proxy/repo-aHR0cHM6Ly9naXRodWIuY29tL21lZGFsc29mdGNoaW5hL21zLXNraWxscy5naXQ\"\n\t *\n\t * rewriteRemoteUrl(\"repo-aHR0cHM6Ly9naXRodWIuY29tL3RlYW0vaW50ZXJuYWwuZ2l0\",\n\t * \"git@github.com:medalsoftchina/ms-skills.git\")\n\t * → \"http://localhost:3000/git-proxy/repo-aHR0cHM6Ly9naXRodWIuY29tL3RlYW0vaW50ZXJuYWwuZ2l0\"\n\t *\n\t * The original URL's host/owner is intentionally DROPPED — the proxy\n\t * knows the upstream from the per-repo config, not from the URL. This\n\t * means callers can route both default and user repos through the same\n\t * dynamic proxy-id scheme.\n\t */\n\trewriteRemoteUrl(_repoId: string, originalUrl: string, useProxy = true): string {\n\t\tif (!useProxy) {\n\t\t\treturn originalUrl;\n\t\t}\n\t\treturn `${this.serverProxyBase}/${_repoId}`;\n\t}\n\n\t/**\n\t * `git clone <proxyUrl> <localPath>` — initialize a new local repo\n\t * from the proxy. Returns when the clone succeeds; throws on failure.\n\t *\n\t * When `branch` is provided, the clone is restricted to that branch\n\t * via `git clone -b <branch> -- <proxyUrl> <localPath>` so the local\n\t * checkout lands on the configured `repo.branch` instead of the\n\t * upstream default branch. The parameter is optional and backward\n\t * compatible — omitting it keeps the original behaviour.\n\t */\n\tasync clone(\n\t\trepoId: string,\n\t\toriginalUrl: string,\n\t\tlocalPath: string,\n\t\tbranch?: string,\n\t\tuseProxy = true\n\t): Promise<void> {\n\t\tconst proxyUrl = this.rewriteRemoteUrl(repoId, originalUrl, useProxy);\n\t\tconst args = branch\n\t\t\t? [\"clone\", \"-b\", branch, \"--\", proxyUrl, localPath]\n\t\t\t: [\"clone\", \"--\", proxyUrl, localPath];\n\t\tawait this.runOrThrow(args, { cwd: process.cwd() });\n\t}\n\n\t/**\n\t * `git fetch <remote> <branch>` + return the resulting commit SHA.\n\t * Operates on an already-cloned repo at `localPath`.\n\t *\n\t * When `branch` is provided, the pull target is forced to that branch:\n\t * if the local working tree is on a different branch, `git checkout\n\t * <branch>` is issued first (git auto-tracks `origin/<branch>`), so\n\t * the sync leaves the repo parked on the configured `repo.branch`.\n\t * When `branch` is omitted the legacy behaviour is preserved — the\n\t * current local branch is fast-forwarded. The parameter is optional\n\t * and backward compatible.\n\t */\n\tasync pull(\n\t\trepoId: string,\n\t\tlocalPath: string,\n\t\tbranch?: string,\n\t\tuseProxy = true,\n\t\toriginalUrl?: string\n\t): Promise<PullResult> {\n\t\t// Re-derive the remote URL from the existing remote config (so\n\t\t// `pull` doesn't need to be told the original upstream URL —\n\t\t// it uses whatever `git remote get-url <remote>` reports).\n\t\tconst remoteUrl = await this.getRemoteUrl(localPath, \"origin\");\n\t\tif (!remoteUrl) {\n\t\t\tthrow new Error(`no 'origin' remote configured at ${localPath}`);\n\t\t}\n\t\tconst targetUrl = useProxy ? remoteUrl : (originalUrl ?? remoteUrl);\n\t\tconst proxyUrl = this.rewriteRemoteUrl(repoId, targetUrl, useProxy);\n\n\t\t// Ensure remote.origin.url points at the proxy URL.\n\t\tawait this.runOrThrow([\"remote\", \"set-url\", \"origin\", proxyUrl], {\n\t\t\tcwd: localPath,\n\t\t});\n\n\t\t// Fetch via the proxy FIRST (this also refreshes the\n\t\t// origin/<branch> refs so a subsequent checkout/merge is current).\n\t\tconst before = await this.safeRevParse(localPath);\n\t\tawait this.runOrThrow([\"fetch\", \"origin\"], { cwd: localPath });\n\t\tconst after = await this.safeRevParse(localPath);\n\n\t\t// Resolve the target branch. If a branch was requested and we are\n\t\t// not currently on it, check it out so git tracks origin/<branch>\n\t\t// before the fast-forward. Otherwise reuse the local current branch\n\t\t// (preserves the original call order for the no-branch case).\n\t\tlet targetBranch = branch;\n\t\tif (!targetBranch) {\n\t\t\ttargetBranch = await this.currentBranch(localPath);\n\t\t} else {\n\t\t\tconst current = await this.currentBranch(localPath);\n\t\t\tif (current !== targetBranch) {\n\t\t\t\tawait this.runOrThrow([\"checkout\", targetBranch], { cwd: localPath });\n\t\t\t}\n\t\t}\n\t\tif (!targetBranch) {\n\t\t\tthrow new Error(`could not determine current branch at ${localPath}`);\n\t\t}\n\n\t\t// Fast-forward the working branch so callers see the new commits.\n\t\tawait this.runOrThrow([\"merge\", \"--ff-only\", `origin/${targetBranch}`], {\n\t\t\tcwd: localPath,\n\t\t});\n\n\t\treturn {\n\t\t\tupdated: before !== after,\n\t\t\tcommitSha: after,\n\t\t\tbranch: targetBranch,\n\t\t};\n\t}\n\n\t/**\n\t * `git push origin <branch>` via the proxy. Caller is responsible\n\t * for committing locally first. The server's GitProxy injects the\n\t * PAT on the way upstream.\n\t */\n\tasync push(\n\t\trepoId: string,\n\t\tlocalPath: string,\n\t\tbranch: string,\n\t\tuseProxy = true\n\t): Promise<PushResult> {\n\t\tconst remoteUrl = await this.getRemoteUrl(localPath, \"origin\");\n\t\tif (!remoteUrl) {\n\t\t\tthrow new Error(`no 'origin' remote configured at ${localPath}`);\n\t\t}\n\t\tconst proxyUrl = this.rewriteRemoteUrl(repoId, remoteUrl, useProxy);\n\t\tawait this.runOrThrow([\"remote\", \"set-url\", \"origin\", proxyUrl], {\n\t\t\tcwd: localPath,\n\t\t});\n\n\t\tconst result = await this.spawner.spawn(\n\t\t\t[\"push\", \"origin\", `refs/heads/${branch}:refs/heads/${branch}`],\n\t\t\t{ cwd: localPath }\n\t\t);\n\t\tif (result.code !== 0) {\n\t\t\tthrow new GitError([\"push\"], result);\n\t\t}\n\n\t\t// Parse the new commit SHA from push output: `refs/heads/v2: <sha>\\t...`\n\t\tconst m = result.stderr.match(/refs\\/heads\\/[^\\s:]+:\\s*([0-9a-f]{7,})/);\n\t\tconst commitSha = m?.[1] ?? \"\";\n\n\t\treturn {\n\t\t\tref: `refs/heads/${branch}`,\n\t\t\tcommitSha,\n\t\t};\n\t}\n\n\t/**\n\t * `git ls-remote <proxyUrl>` — list advertised branches without\n\t * cloning. Used by addUserRepo to detect the default branch.\n\t */\n\tasync lsRemote(repoId: string, originalUrl: string, useProxy = true): Promise<RemoteBranch[]> {\n\t\tconst proxyUrl = this.rewriteRemoteUrl(repoId, originalUrl, useProxy);\n\t\tconst result = await this.spawner.spawn([\"ls-remote\", \"--heads\", \"--\", proxyUrl], {\n\t\t\tcwd: process.cwd(),\n\t\t});\n\t\tif (result.code !== 0) {\n\t\t\tthrow new GitError([\"ls-remote\"], result);\n\t\t}\n\t\tconst branches: RemoteBranch[] = [];\n\t\tfor (const line of result.stdout.split(\"\\n\")) {\n\t\t\tconst trimmed = line.trim();\n\t\t\tif (!trimmed) continue;\n\t\t\t// `<sha>\\t<ref>` — split on the tab.\n\t\t\tconst tab = trimmed.indexOf(\"\\t\");\n\t\t\tif (tab === -1) continue;\n\t\t\tconst sha = trimmed.slice(0, tab);\n\t\t\tconst ref = trimmed.slice(tab + 1);\n\t\t\tbranches.push({ sha, ref });\n\t\t}\n\t\treturn branches;\n\t}\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Internal helpers\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * `git add <pathspec…>` followed by `git commit -m <message>`. The\n\t * commit message convention follows the spec: `feat(skills): add\n\t * <name>` / `feat(agents): add <name>`.\n\t */\n\tasync commit(localPath: string, message: string, addPath = \".\"): Promise<{ commitSha: string }> {\n\t\tawait this.runOrThrow([\"add\", \"--\", addPath], { cwd: localPath });\n\t\tawait this.runOrThrow([\"commit\", \"-m\", message], { cwd: localPath });\n\t\tconst sha = await this.safeRevParse(localPath);\n\t\treturn { commitSha: sha };\n\t}\n\n\tprivate async runOrThrow(args: string[], opts: { cwd?: string }): Promise<GitSpawnResult> {\n\t\tconst result = await this.spawner.spawn(args, {\n\t\t\tcwd: opts.cwd ?? process.cwd(),\n\t\t});\n\t\tif (result.code !== 0) {\n\t\t\tthrow new GitError(args, result);\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate async getRemoteUrl(localPath: string, remote: string): Promise<string | undefined> {\n\t\tconst result = await this.spawner.spawn([\"remote\", \"get-url\", remote], {\n\t\t\tcwd: localPath,\n\t\t});\n\t\tif (result.code !== 0) return undefined;\n\t\tconst url = result.stdout.trim();\n\t\treturn url.length > 0 ? url : undefined;\n\t}\n\n\tprivate async safeRevParse(localPath: string): Promise<string> {\n\t\tconst result = await this.spawner.spawn([\"rev-parse\", \"HEAD\"], {\n\t\t\tcwd: localPath,\n\t\t});\n\t\tif (result.code !== 0) return \"\";\n\t\treturn result.stdout.trim();\n\t}\n\n\tprivate async currentBranch(localPath: string): Promise<string | undefined> {\n\t\tconst result = await this.spawner.spawn([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], {\n\t\t\tcwd: localPath,\n\t\t});\n\t\tif (result.code !== 0) return undefined;\n\t\tconst branch = result.stdout.trim();\n\t\tif (!branch || branch === \"HEAD\") return undefined;\n\t\treturn branch;\n\t}\n}\n\n/**\n * Default spawner: invokes the system `git` binary via Node's\n * `child_process.spawn`. Streams stdout/stderr to strings and returns\n * the exit code. Tests inject a stub to avoid touching the filesystem.\n */\n// Re-export GitError so callers can `import { GitError } from \"...\"`.\nexport { GitError };\n\nexport class NodeGitSpawner implements GitSpawner {\n\tasync spawn(args: string[], opts: { cwd?: string }): Promise<GitSpawnResult> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst child = spawn(\"git\", args, {\n\t\t\t\tcwd: opts.cwd,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\tshell: false,\n\t\t\t});\n\t\t\tconst stdoutChunks: Buffer[] = [];\n\t\t\tconst stderrChunks: Buffer[] = [];\n\t\t\tchild.stdout?.on(\"data\", (c: Buffer) => stdoutChunks.push(c));\n\t\t\tchild.stderr?.on(\"data\", (c: Buffer) => stderrChunks.push(c));\n\t\t\tchild.on(\"error\", reject);\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tresolve({\n\t\t\t\t\tstdout: Buffer.concat(stdoutChunks).toString(\"utf8\"),\n\t\t\t\t\tstderr: Buffer.concat(stderrChunks).toString(\"utf8\"),\n\t\t\t\t\tcode: code ?? 1,\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t}\n}\n\n/** Convenience: a record-based stub spawner for tests. */\nexport class StubGitSpawner implements GitSpawner {\n\t/** queue of canned responses, consumed FIFO per spawn() call. */\n\treadonly script: GitSpawnResult[];\n\t/** All spawn() invocations, in order, for assertions. */\n\treadonly calls: Array<{ args: string[]; cwd: string | undefined }> = [];\n\n\tconstructor(script: GitSpawnResult[]) {\n\t\tthis.script = [...script];\n\t}\n\n\tasync spawn(args: string[], opts: { cwd?: string }): Promise<GitSpawnResult> {\n\t\tthis.calls.push({ args, cwd: opts.cwd });\n\t\tconst next = this.script.shift();\n\t\tif (!next) {\n\t\t\treturn {\n\t\t\t\tstdout: \"\",\n\t\t\t\tstderr: `stub: no scripted response for ${args.join(\" \")}`,\n\t\t\t\tcode: 1,\n\t\t\t};\n\t\t}\n\t\treturn next;\n\t}\n}\n\n/**\n * Helper: encode a bare absolute file URL (`file:///...`) for local\n * git clone tests. Not used by GitClient directly — exposed for\n * RepoManager's `localSeed` use case.\n */\nexport function toFileUrl(absolutePath: string): string {\n\tconst normalized = path.resolve(absolutePath);\n\tif (process.platform === \"win32\") {\n\t\treturn `file:///${normalized.replace(/\\\\/g, \"/\")}`;\n\t}\n\treturn `file://${normalized}`;\n}\n\n/** Path suffix appended to a server base URL to reach its git-proxy route. */\nexport const GIT_PROXY_PATH_SUFFIX = \"/git-proxy\";\n\n/**\n * Single source of truth for deriving a {@link GitClientOptions.serverProxyBase}\n * from an already-resolved, non-empty server base URL (e.g. the extension's\n * verified marketplace server). Callers that derive `serverProxyBase` from a\n * known server base URL — extension bootstrap, the sync scheduler, the CLI\n * bridge subprocess env — should go through this helper instead of\n * re-deriving the `/git-proxy` suffix locally, so the route contract only\n * lives in one place. (Standalone CLI defaults such as\n * `http://127.0.0.1:3000/git-proxy`, used when no server base URL is known\n * at all, are a separate concern and don't go through this helper.)\n *\n * Callers MUST guard against an empty/falsy `serverBaseUrl` before calling —\n * this function does not validate its input and will happily return the\n * non-URL `\"/git-proxy\"` for an empty string.\n */\nexport function buildGitProxyBase(serverBaseUrl: string): string {\n\treturn `${serverBaseUrl.replace(/\\/+$/, \"\")}${GIT_PROXY_PATH_SUFFIX}`;\n}\n","import type { SpawnOptions } from \"node:child_process\";\n\n/**\n * Skill & Agent v2 — Client GitClient Types (M3)\n *\n * GitClient wraps the local `git` CLI so all commands the SERVICEME\n * pipeline issues (clone, fetch, push, ls-remote) flow through the\n * server's git smart-HTTP proxy instead of talking directly to GitHub.\n *\n * The single trick: `git` only cares about the URL we hand it as a\n * remote. We rewrite `https://github.com/owner/repo.git` to\n * `http://server:port/git-proxy/<id>` and `git` does the rest — the\n * server transparently forwards + injects PATs as needed.\n *\n * @see docs/architecture/skill-agent-v2-repo.md §5.4 GitClient\n */\n\n/** Result of a `git fetch` (called via the proxy's git-upload-pack). */\nexport interface PullResult {\n\t/** Whether new commits were fetched (false = already up-to-date). */\n\tupdated: boolean;\n\t/** Commit SHA at FETCH_HEAD after the pull. */\n\tcommitSha: string;\n\t/** Branch name that was pulled (e.g. \"v2\"). */\n\tbranch: string;\n}\n\n/** Result of a `git push` (called via the proxy's git-receive-pack). */\nexport interface PushResult {\n\t/** Remote ref updated (e.g. \"refs/heads/v2\"). */\n\tref: string;\n\t/** New commit SHA on the remote. */\n\tcommitSha: string;\n}\n\n/** A branch as advertised by `git ls-remote`. */\nexport interface RemoteBranch {\n\t/** Full ref name, e.g. `refs/heads/main`. */\n\tref: string;\n\t/** Commit SHA the ref points at. */\n\tsha: string;\n}\n\n/** Outcome of any spawned `git` process. */\nexport interface GitSpawnResult {\n\tstdout: string;\n\tstderr: string;\n\tcode: number;\n}\n\n/** Strategy for executing git commands. Tests inject a stub. */\nexport interface GitSpawner {\n\tspawn(args: string[], opts: SpawnOptions): Promise<GitSpawnResult>;\n}\n\n/** Options for instantiating GitClient. */\nexport interface GitClientOptions {\n\t/** Base URL of the server git proxy. e.g. `http://localhost:3000/git-proxy`. */\n\tserverProxyBase: string;\n\t/** Inject a custom spawner (default: spawn real `git` CLI). */\n\tspawner?: GitSpawner;\n\t/** Inject an env override for spawned git (default: process.env minus proxy secrets). */\n\tenv?: NodeJS.ProcessEnv;\n}\n\n/** Sentinel error when git exits non-zero. */\nexport class GitError extends Error {\n\treadonly args: string[];\n\treadonly code: number;\n\treadonly stderr: string;\n\treadonly stdout: string;\n\tconstructor(args: string[], result: GitSpawnResult) {\n\t\tsuper(`git ${args.join(\" \")} failed (exit ${result.code}): ${result.stderr.slice(0, 500)}`);\n\t\tthis.name = \"GitError\";\n\t\tthis.args = args;\n\t\tthis.code = result.code;\n\t\tthis.stderr = result.stderr;\n\t\tthis.stdout = result.stdout;\n\t}\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport {\n\tcreateServicemeError,\n\ttype ServiceMeImageCompressOptions,\n\ttype ServiceMeImageCompressResult,\n\ttype ServiceMeImageInfo,\n\ttype ServiceMeImageValidationResult,\n} from \"@serviceme/devtools-protocol\";\n\ntype SharpMetadata = {\n\twidth?: number;\n\theight?: number;\n\tformat?: string;\n\tspace?: string;\n};\n\ntype SharpPipeline = {\n\tmetadata(): Promise<SharpMetadata>;\n\tjpeg(options: { quality: number }): SharpPipeline;\n\tpng(options: { quality: number; compressionLevel: number }): SharpPipeline;\n\twebp(options: { quality: number }): SharpPipeline;\n\ttoBuffer(): Promise<Buffer>;\n};\n\ntype SharpModule = (inputPath: string) => SharpPipeline;\n\nconst SUPPORTED_FORMATS = [\".jpg\", \".jpeg\", \".png\", \".webp\", \".gif\", \".bmp\", \".tiff\"];\nconst DEFAULT_MINIMUM_COMPRESSION_RATIO = 5;\n\nexport class ImageTools {\n\tgetSupportedFormats(): string[] {\n\t\treturn [...SUPPORTED_FORMATS];\n\t}\n\n\tasync validate(\n\t\tfilePath: string,\n\t\tsharpModulePath?: string\n\t): Promise<ServiceMeImageValidationResult> {\n\t\ttry {\n\t\t\tif (!(await this.pathExists(filePath))) {\n\t\t\t\treturn { valid: false };\n\t\t\t}\n\n\t\t\tif (!SUPPORTED_FORMATS.includes(path.extname(filePath).toLowerCase())) {\n\t\t\t\treturn { valid: false };\n\t\t\t}\n\n\t\t\tawait this.getInfo(filePath, sharpModulePath);\n\t\t\treturn { valid: true };\n\t\t} catch {\n\t\t\treturn { valid: false };\n\t\t}\n\t}\n\n\tasync getInfo(imagePath: string, sharpModulePath?: string): Promise<ServiceMeImageInfo> {\n\t\tconst sharp = this.loadSharp(sharpModulePath);\n\t\tconst metadata = await sharp(imagePath).metadata();\n\t\tconst stats = await fs.stat(imagePath);\n\n\t\treturn {\n\t\t\twidth: metadata.width ?? 0,\n\t\t\theight: metadata.height ?? 0,\n\t\t\tformat: metadata.format ?? \"unknown\",\n\t\t\tsize: stats.size,\n\t\t\tcolorSpace: metadata.space,\n\t\t};\n\t}\n\n\tasync compress(\n\t\timagePath: string,\n\t\toptions: ServiceMeImageCompressOptions\n\t): Promise<ServiceMeImageCompressResult> {\n\t\tconst validation = await this.validate(imagePath, options.sharpModulePath);\n\t\tif (!validation.valid) {\n\t\t\tthrow createServicemeError(\"invalid_params\", `Invalid image file: ${imagePath}`);\n\t\t}\n\n\t\tconst originalStats = await fs.stat(imagePath);\n\t\tconst originalSize = originalStats.size;\n\t\tconst outputPath = this.getOutputPath(imagePath, options);\n\t\tconst compressedBuffer = await this.compressWithSharp(imagePath, options);\n\t\tconst compressedSize = compressedBuffer.length;\n\t\tconst compressionRatio = ((originalSize - compressedSize) / originalSize) * 100;\n\t\tconst minimumCompressionRatio =\n\t\t\toptions.minimumCompressionRatio ?? DEFAULT_MINIMUM_COMPRESSION_RATIO;\n\n\t\tif (compressionRatio < minimumCompressionRatio) {\n\t\t\treturn {\n\t\t\t\toriginalSize,\n\t\t\t\tcompressedSize: originalSize,\n\t\t\t\tcompressionRatio: 0,\n\t\t\t\toutputPath: imagePath,\n\t\t\t};\n\t\t}\n\n\t\tawait fs.writeFile(outputPath, compressedBuffer);\n\n\t\treturn {\n\t\t\toriginalSize,\n\t\t\tcompressedSize,\n\t\t\tcompressionRatio,\n\t\t\toutputPath,\n\t\t};\n\t}\n\n\tprivate async compressWithSharp(\n\t\timagePath: string,\n\t\toptions: ServiceMeImageCompressOptions\n\t): Promise<Buffer> {\n\t\tconst sharp = this.loadSharp(options.sharpModulePath);\n\t\tlet pipeline = sharp(imagePath);\n\n\t\tswitch (options.format ?? path.extname(imagePath).toLowerCase().slice(1)) {\n\t\t\tcase \"jpg\":\n\t\t\tcase \"jpeg\":\n\t\t\t\tpipeline = pipeline.jpeg({ quality: options.quality });\n\t\t\t\tbreak;\n\t\t\tcase \"png\":\n\t\t\t\tpipeline = pipeline.png({\n\t\t\t\t\tquality: options.quality,\n\t\t\t\t\tcompressionLevel: 9,\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\tcase \"webp\":\n\t\t\t\tpipeline = pipeline.webp({ quality: options.quality });\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tpipeline = pipeline.jpeg({ quality: options.quality });\n\t\t}\n\n\t\treturn pipeline.toBuffer();\n\t}\n\n\tprivate getOutputPath(inputPath: string, options: ServiceMeImageCompressOptions): string {\n\t\tif (options.outputPath) {\n\t\t\treturn options.outputPath;\n\t\t}\n\n\t\tif (options.replaceOriginImage) {\n\t\t\treturn inputPath;\n\t\t}\n\n\t\tconst dir = path.dirname(inputPath);\n\t\tconst ext = path.extname(inputPath);\n\t\tconst name = path.basename(inputPath, ext);\n\t\treturn path.join(dir, `${name}_compressed${ext}`);\n\t}\n\n\tprivate async pathExists(targetPath: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait fs.access(targetPath);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate loadSharp(sharpModulePath?: string): SharpModule {\n\t\ttry {\n\t\t\treturn (sharpModulePath ? require(sharpModulePath) : require(\"sharp\")) as SharpModule;\n\t\t} catch (error) {\n\t\t\tthrow createServicemeError(\n\t\t\t\t\"internal_error\",\n\t\t\t\t`sharp is required for image operations: ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`\n\t\t\t);\n\t\t}\n\t}\n}\n\nexport function createImageTools(): ImageTools {\n\treturn new ImageTools();\n}\n","import {\n\tcreateServicemeError,\n\tDEFAULT_JSON_SORT_OPTIONS,\n\ttype JsonSortOptions,\n\ttype JsonValidationResult,\n} from \"@serviceme/devtools-protocol\";\nimport { parse as parseCommentJson } from \"comment-json\";\nimport JSON5 from \"json5\";\n\nexport interface JsonTools {\n\tsort(text: string, options?: Partial<JsonSortOptions>): string;\n\tformat(text: string, indent?: number): string;\n\tvalidate(text: string): JsonValidationResult;\n\tminify(text: string): string;\n}\n\nexport function createJsonTools(): JsonTools {\n\treturn {\n\t\tsort(text: string, options?: Partial<JsonSortOptions>): string {\n\t\t\tconst finalOptions = { ...DEFAULT_JSON_SORT_OPTIONS, ...options };\n\t\t\tconst json = parseJson(text);\n\t\t\tconst sorted = sortObject(json, finalOptions);\n\t\t\treturn JSON.stringify(sorted, null, detectIndent(text));\n\t\t},\n\t\tformat(text: string, indent = 2): string {\n\t\t\treturn JSON.stringify(parseJson(text), null, indent);\n\t\t},\n\t\tvalidate(text: string): JsonValidationResult {\n\t\t\ttry {\n\t\t\t\tparseJson(text);\n\t\t\t\treturn { valid: true };\n\t\t\t} catch (error) {\n\t\t\t\treturn {\n\t\t\t\t\tvalid: false,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\t\tminify(text: string): string {\n\t\t\treturn JSON.stringify(parseJson(text));\n\t\t},\n\t};\n}\n\nfunction parseJson(text: string): unknown {\n\tconst parsers = [() => JSON.parse(text), () => parseCommentJson(text), () => JSON5.parse(text)];\n\n\tfor (const parser of parsers) {\n\t\ttry {\n\t\t\treturn parser();\n\t\t} catch {\n\t\t\t// Try next parser.\n\t\t}\n\t}\n\n\tthrow createServicemeError(\"json_invalid_input\", \"Invalid JSON format.\");\n}\n\nfunction sortObject(obj: unknown, options: JsonSortOptions): unknown {\n\tif (Array.isArray(obj)) {\n\t\treturn obj.map((item) => sortObject(item, options));\n\t}\n\n\tif (obj !== null && typeof obj === \"object\") {\n\t\tconst record = obj as Record<string, unknown>;\n\t\tconst sortedKeys = sortKeys(Object.keys(record), options);\n\t\tconst result: Record<string, unknown> = {};\n\n\t\tfor (const key of sortedKeys) {\n\t\t\tresult[key] = sortObject(record[key], options);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\treturn obj;\n}\n\nfunction sortKeys(keys: string[], options: JsonSortOptions): string[] {\n\tlet compareFn: (a: string, b: string) => number;\n\n\tswitch (options.sortAlgo) {\n\t\tcase \"keyLength\":\n\t\t\tcompareFn = (a, b) => a.length - b.length;\n\t\t\tbreak;\n\t\tcase \"alphaNum\":\n\t\t\tcompareFn = (a, b) => a.localeCompare(b, undefined, { numeric: true });\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tcompareFn = (a, b) => a.localeCompare(b);\n\t\t\tbreak;\n\t}\n\n\tconst sorted = [...keys].sort(compareFn);\n\treturn options.sortOrder === \"desc\" ? sorted.reverse() : sorted;\n}\n\nfunction detectIndent(text: string): number {\n\tfor (const line of text.split(\"\\n\")) {\n\t\tconst match = line.match(/^(\\s+)/);\n\t\tif (match?.[1]) {\n\t\t\treturn match[1].length;\n\t\t}\n\t}\n\n\treturn 2;\n}\n","/**\n * serverProxyGlobal — `~/.serviceme/server-proxy.json` IO (rev.20).\n *\n * Persists the r7 BYOM Server Proxy toggle's self-state (`enabled` flag\n * + last-known server URL) in a cross-tool global config file under\n * `~/.serviceme/`. Mirrors the convention used by `scheduled-tasks.json`,\n * `migration-failures.json`, etc.\n *\n * Originally lived in `apps/extension/src/services/proxy/globalConfigStore.ts`\n * (rev.19). Promoted to `@serviceme/devtools-core` in rev.20 so the CLI\n * and Server can read the same file without booting VS Code.\n *\n * Why a dedicated `server-proxy.json` rather than a unified config?\n * - One file per concern = atomic writes = no shared-schema lock.\n * `scheduled-tasks.json` already takes this approach.\n *\n * Atomic write protocol (rev.20):\n * 1. Compute patch (read + merge).\n * 2. Open `<target>.tmp-<randomUUID>` for write in the same dir.\n * 3. `writeFile(content)` then `fh.sync()` (fsync) — flush to disk.\n * 4. `fs.rename(tmp, target)` — POSIX atomic on the same filesystem.\n *\n * The `crypto.randomUUID()` suffix + `fh.sync()` address two hardening\n * gaps identified in the rev.19 review:\n * - Concurrency: tmp filename collisions under same-pid concurrent\n * writes become effectively impossible.\n * - Durability: without fsync, a power loss between `writeFile` and\n * `rename` could surface as a stale target file (POSIX rename is\n * atomic, but the data behind it might not have hit the platter).\n */\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs/promises\";\nimport { open } from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport { getServerProxyGlobalPath, SERVER_PROXY_GLOBAL_FILENAME } from \"./userHome\";\n\nexport { SERVER_PROXY_GLOBAL_FILENAME } from \"./userHome\";\nexport { getServerProxyGlobalPath };\n\n/** On-disk shape for `~/.serviceme/server-proxy.json`. */\nexport interface ServerProxyGlobalState {\n\t/** Whether the user has previously enabled Server Proxy. */\n\tenabled: boolean;\n\t/** Last server URL we toggled ON with. Used for re-toggling and UI hint. */\n\tlastServerUrl: string | undefined;\n\t/**\n\t * rev.21 — explicit opt-in to enable Server Proxy even when\n\t * `http.proxySupport === \"override\"`. `override` mode forces ALL\n\t * extensions to route through `http.proxy`; defaulting to `false`\n\t * keeps the legacy defensive guard against accidental global\n\t * hijack. Power users (e.g. corp environments that WANT all\n\t * extensions including GitHub Copilot Chat to route through the\n\t * corp tunnel) can flip this on via the Webview's \"Allow under\n\t * proxySupport=override\" affordance.\n\t *\n\t * Note: stored in the global file alongside `enabled` because\n\t * this is *our* self-flag — it has nothing to do with VS Code's\n\t * `http.*` schema.\n\t */\n\tallowOverride: boolean;\n\t/** ISO 8601 timestamp of the most recent write. Diagnostic only. */\n\tupdatedAt: string;\n}\n\n/**\n * Patch semantics (mirrors `vscode.workspace.getConfiguration().update(...)`):\n * - Field omitted / `undefined` → leave the existing value alone.\n * - Field set to `null` → explicitly clear the field.\n *\n * Only `lastServerUrl` distinguishes \"leave alone\" from \"clear\"; `enabled`\n * and `allowOverride` are plain `boolean | undefined` because boolean is\n * the natural unit (false = \"the user disabled it\").\n */\nexport interface ServerProxyGlobalPatch {\n\tenabled?: boolean;\n\tallowOverride?: boolean;\n\tlastServerUrl?: string | null;\n}\n\n/**\n * Reads the global config file. Returns `null` if the file does not\n * exist (first-run case) — does NOT throw. Throws on parse errors or\n * permission issues, so callers can surface those to the user.\n */\nexport async function readServerProxyGlobal(): Promise<ServerProxyGlobalState | null> {\n\tconst filePath = getServerProxyGlobalPath();\n\ttry {\n\t\tconst raw = await fs.readFile(filePath, \"utf8\");\n\t\tconst parsed: unknown = JSON.parse(raw);\n\t\tif (!isServerProxyGlobalState(parsed)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid ${SERVER_PROXY_GLOBAL_FILENAME}: expected {enabled: boolean, lastServerUrl?: string, updatedAt: string}, got ${JSON.stringify(parsed).slice(0, 80)}`\n\t\t\t);\n\t\t}\n\t\treturn parsed;\n\t} catch (err: unknown) {\n\t\tif (isENOENT(err)) return null;\n\t\tthrow err;\n\t}\n}\n\n/**\n * Merges a partial patch into the existing state and writes back\n * atomically. Creates `~/.serviceme/` if missing.\n *\n * Returns the post-write state. Throws on IO/JSON errors.\n */\nexport async function writeServerProxyGlobal(\n\tpatch: ServerProxyGlobalPatch\n): Promise<ServerProxyGlobalState> {\n\tconst filePath = getServerProxyGlobalPath();\n\tconst dirPath = path.dirname(filePath);\n\n\tawait fs.mkdir(dirPath, { recursive: true });\n\n\tconst current = (await readServerProxyGlobal()) ?? {\n\t\tenabled: false,\n\t\tallowOverride: false,\n\t\tlastServerUrl: undefined,\n\t\tupdatedAt: new Date(0).toISOString(),\n\t};\n\n\tconst next: ServerProxyGlobalState = {\n\t\tenabled: patch.enabled !== undefined ? patch.enabled : current.enabled,\n\t\tallowOverride: patch.allowOverride !== undefined ? patch.allowOverride : current.allowOverride,\n\t\tlastServerUrl:\n\t\t\tpatch.lastServerUrl === undefined\n\t\t\t\t? current.lastServerUrl\n\t\t\t\t: patch.lastServerUrl === null\n\t\t\t\t\t? undefined\n\t\t\t\t\t: patch.lastServerUrl,\n\t\tupdatedAt: new Date().toISOString(),\n\t};\n\n\tconst tmpPath = `${filePath}.tmp-${randomUUID()}`;\n\tconst fh = await open(tmpPath, \"w\");\n\ttry {\n\t\tawait fh.writeFile(JSON.stringify(next, null, \"\\t\"), \"utf8\");\n\t\t// Force data to platter before rename — without this, a power\n\t\t// loss between writeFile and rename could surface as a stale\n\t\t// target file (POSIX rename is atomic, but the data behind\n\t\t// it might not have hit disk).\n\t\tawait fh.sync();\n\t} finally {\n\t\tawait fh.close();\n\t}\n\tawait fs.rename(tmpPath, filePath);\n\n\treturn next;\n}\n\n/**\n * Best-effort migration of a legacy `enabled` flag (typically read from\n * VS Code's `settings.json` at `serverProxy.enabled`) into the global\n * config file.\n *\n * Reads via the provided accessor (so tests can stub it), writes the\n * global flag if present, then **clears** the legacy flag via the\n * provided writer — never silently leaves the migration half-done.\n *\n * Returns the migrated state, or `null` if no migration was needed\n * (legacy flag absent OR global config already up to date).\n */\nexport async function migrateLegacyServerProxyEnabled(\n\treadLegacy: () => boolean | undefined,\n\tclearLegacy: () => Promise<void>\n): Promise<ServerProxyGlobalState | null> {\n\tconst legacyEnabled = readLegacy();\n\tif (legacyEnabled !== true) return null;\n\n\tconst existing = await readServerProxyGlobal();\n\tif (existing?.enabled === true) {\n\t\t// Already migrated in a previous activation; just clean up the\n\t\t// legacy flag so we don't keep reading it.\n\t\tawait clearLegacy();\n\t\treturn null;\n\t}\n\n\tconst next = await writeServerProxyGlobal({ enabled: true });\n\tawait clearLegacy();\n\treturn next;\n}\n\n// ─── type guards / error checks (kept private to this module) ─────────────\n\nfunction isServerProxyGlobalState(v: unknown): v is ServerProxyGlobalState {\n\tif (!v || typeof v !== \"object\") return false;\n\tconst obj = v as Record<string, unknown>;\n\tif (typeof obj.enabled !== \"boolean\") return false;\n\tif (typeof obj.allowOverride !== \"boolean\") return false;\n\tif (typeof obj.updatedAt !== \"string\") return false;\n\t// rev.21 — `lastServerUrl` is `string | undefined`. Null is a legacy\n\t// round-trip artefact from rev.20's `cat <<EOF` fixture, treat it\n\t// the same as undefined rather than rejecting the whole file.\n\tif (\n\t\tobj.lastServerUrl !== undefined &&\n\t\tobj.lastServerUrl !== null &&\n\t\ttypeof obj.lastServerUrl !== \"string\"\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nfunction isENOENT(err: unknown): boolean {\n\treturn (\n\t\ttypeof err === \"object\" &&\n\t\terr !== null &&\n\t\t\"code\" in err &&\n\t\t(err as { code: unknown }).code === \"ENOENT\"\n\t);\n}\n","/**\n * Spec §11.12 — Phase 5 client-side state bootstrap.\n *\n * The server already exposes the Phase 5 surface (better-auth session\n * endpoints, device enrollment routes, toolbox + profile API). The\n * client (extension + CLI) needs the on-disk files to exist on first\n * run so that subsequent code can `readFile()` without an existence\n * check, and so the user can grep `~/.serviceme/` and see the\n * expected layout instead of an empty directory.\n *\n * Strategy: idempotently write the empty / default JSON for every\n * Phase 5 file that doesn't already exist. Existing files are\n * left untouched so the bootstrap can re-run on every extension\n * activation without overwriting user state.\n *\n * All writes are `mkdir -p` + `writeFile` (no race), and failures\n * are surfaced but never thrown — Phase 5 is auxiliary; the\n * extension / CLI should keep working even if the user's home\n * directory is read-only.\n *\n * @see docs/architecture/skill-agent-v2-repo.md §11.12\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport {\n\tgetCredentialsConfigPath,\n\tgetDeviceJsonPath,\n\tgetMachineIdPath,\n\tgetProfilesJsonPath,\n\tgetServicemeHome,\n\tgetToolboxJsonPath,\n} from \"../paths/userHome\";\n\n/** Per-file default content. `null` for the machine-id file because it\n * is a bare uuid with no JSON wrapper. */\ninterface Phase5FileSpec {\n\tpath: string;\n\tdefaultContent: string;\n}\n\nfunction getPhase5FileSpecs(): Phase5FileSpec[] {\n\treturn [\n\t\t{\n\t\t\tpath: getCredentialsConfigPath(),\n\t\t\t// Mirrors better-auth's client-side session shape; an empty\n\t\t\t// `sessions: []` makes the auth service treat the user as\n\t\t\t// logged-out without a per-read fallback.\n\t\t\tdefaultContent: JSON.stringify({ version: 1, sessions: [] }, null, \"\\t\"),\n\t\t},\n\t\t{\n\t\t\tpath: getDeviceJsonPath(),\n\t\t\t// Device enrollment payload. Empty until the user runs the\n\t\t\t// device-claim flow; the server treats absent fields as\n\t\t\t// \"unclaimed\".\n\t\t\tdefaultContent: JSON.stringify(\n\t\t\t\t{ version: 1, claimed: false, publicKeyFingerprint: null },\n\t\t\t\tnull,\n\t\t\t\t\"\\t\"\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tpath: getToolboxJsonPath(),\n\t\t\t// Local toolbox state; server is the source of truth for\n\t\t\t// installed tools, this is the per-user pref cache.\n\t\t\tdefaultContent: JSON.stringify({ version: 1, installed: [], preferences: {} }, null, \"\\t\"),\n\t\t},\n\t\t{\n\t\t\tpath: getMachineIdPath(),\n\t\t\t// Random uuid, written as a bare string. Subsequent\n\t\t\t// activations see the file and skip re-randomizing.\n\t\t\tdefaultContent: randomUUID(),\n\t\t},\n\t\t{\n\t\t\tpath: getProfilesJsonPath(),\n\t\t\t// Cached projection of the server's profile table; an empty\n\t\t\t// list means \"no profiles yet\" rather than \"unknown\".\n\t\t\tdefaultContent: JSON.stringify({ version: 1, profiles: [] }, null, \"\\t\"),\n\t\t},\n\t];\n}\n\nexport interface BootstrapPhase5Result {\n\t/** Files that were created by this run (already-existing files excluded). */\n\tcreated: string[];\n\t/** Files that were already present and left untouched. */\n\tskipped: string[];\n\t/** Files where the write attempt failed (e.g. read-only home). */\n\tfailed: Array<{ path: string; reason: string }>;\n}\n\n/**\n * Idempotently create the Phase 5 placeholder files under\n * `~/.serviceme/`. Safe to call on every extension activation —\n * existing files are detected via `access` and skipped.\n */\nexport async function bootstrapPhase5Placeholders(): Promise<BootstrapPhase5Result> {\n\tconst result: BootstrapPhase5Result = { created: [], skipped: [], failed: [] };\n\tconst home = getServicemeHome();\n\n\t// Make sure the home directory itself exists before any per-file\n\t// mkdir. mkdir({ recursive: true }) is a no-op when the dir is\n\t// already there, so this is safe to repeat.\n\ttry {\n\t\tawait fs.mkdir(home, { recursive: true });\n\t} catch (err) {\n\t\tresult.failed.push({ path: home, reason: (err as Error).message });\n\t\treturn result;\n\t}\n\n\tfor (const spec of getPhase5FileSpecs()) {\n\t\ttry {\n\t\t\tawait fs.access(spec.path);\n\t\t\tresult.skipped.push(spec.path);\n\t\t} catch {\n\t\t\t// ENOENT (or EACCES on a parent) — try to write.\n\t\t\ttry {\n\t\t\t\tawait fs.mkdir(path.dirname(spec.path), { recursive: true });\n\t\t\t\tawait fs.writeFile(spec.path, spec.defaultContent, \"utf8\");\n\t\t\t\tresult.created.push(spec.path);\n\t\t\t} catch (writeErr) {\n\t\t\t\tresult.failed.push({ path: spec.path, reason: (writeErr as Error).message });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport {\n\tcreateServicemeError,\n\ttype ServiceMeProjectExtractTemplateInput,\n\ttype ServiceMeProjectExtractTemplateResult,\n\ttype ServiceMeProjectGitInitResult,\n\ttype ServiceMeProjectInstallDepsResult,\n\ttype ServiceMeProjectMakeScriptsExecutableResult,\n\ttype ServiceMeProjectScaffoldPruneResult,\n} from \"@serviceme/devtools-protocol\";\nimport { runCommand } from \"../process/runCommand\";\nimport { moveFiles, unzipFile } from \"../utils/fileUtils\";\n\nexport class ProjectTools {\n\tasync extractTemplate(\n\t\tzipPath: string,\n\t\tworkspacePath: string,\n\t\ttempExtractDir: string,\n\t\tinput: ServiceMeProjectExtractTemplateInput\n\t): Promise<ServiceMeProjectExtractTemplateResult> {\n\t\tawait unzipFile(zipPath, tempExtractDir);\n\n\t\tlet sourceDir = path.join(tempExtractDir, input.extractedDirName);\n\t\tlet actualDirName = input.extractedDirName;\n\n\t\tif (!(await this.pathExists(sourceDir))) {\n\t\t\tconst entries = await fs.readdir(tempExtractDir, { withFileTypes: true });\n\t\t\tconst directories = entries.filter(\n\t\t\t\t(entry) => entry.isDirectory() && !entry.name.startsWith(\".\")\n\t\t\t);\n\n\t\t\tconst selectedDirectory = await this.selectExtractedDirectory(\n\t\t\t\tdirectories.map((directory) => directory.name),\n\t\t\t\ttempExtractDir,\n\t\t\t\tinput.projectFilePattern,\n\t\t\t\tinput.extractedDirName\n\t\t\t);\n\n\t\t\tif (selectedDirectory) {\n\t\t\t\tactualDirName = selectedDirectory;\n\t\t\t\tsourceDir = path.join(tempExtractDir, actualDirName);\n\t\t\t} else if (directories.length === 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`No directory found after extraction. Expected directory: ${input.extractedDirName}`\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Multiple directories found after extraction: ${directories\n\t\t\t\t\t\t.map((directory) => directory.name)\n\t\t\t\t\t\t.join(\", \")}. Expected: ${input.extractedDirName}`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (!(await this.pathExists(sourceDir))) {\n\t\t\tthrow new Error(`Source directory not found: ${sourceDir}`);\n\t\t}\n\n\t\tawait moveFiles(sourceDir, workspacePath, true);\n\n\t\treturn {\n\t\t\tactualDirName,\n\t\t};\n\t}\n\n\tasync installDependencies(\n\t\tworkspacePath: string,\n\t\tcommand?: string\n\t): Promise<ServiceMeProjectInstallDepsResult> {\n\t\tif (!command) {\n\t\t\tthrow createServicemeError(\"invalid_params\", \"Expected install command.\");\n\t\t}\n\n\t\tawait runCommand(command, {\n\t\t\tcwd: workspacePath,\n\t\t\tshell: true,\n\t\t});\n\n\t\treturn {\n\t\t\tinstalled: true,\n\t\t\tcommand,\n\t\t};\n\t}\n\n\tasync makeScriptsExecutable(\n\t\tworkspacePath: string\n\t): Promise<ServiceMeProjectMakeScriptsExecutableResult> {\n\t\tconst isWindows = process.platform === \"win32\";\n\t\tconst scripts = await this.findScripts(workspacePath, isWindows ? [\".ps1\", \".bat\"] : [\".sh\"]);\n\n\t\tlet updatedCount = 0;\n\n\t\tif (isWindows) {\n\t\t\tfor (const scriptPath of scripts.filter((script) => script.endsWith(\".ps1\"))) {\n\t\t\t\ttry {\n\t\t\t\t\tawait runCommand(`powershell -Command \"Unblock-File -Path '${scriptPath}'\"`, {\n\t\t\t\t\t\tcwd: workspacePath,\n\t\t\t\t\t\tshell: true,\n\t\t\t\t\t});\n\t\t\t\t\tupdatedCount += 1;\n\t\t\t\t} catch {\n\t\t\t\t\t// Match the extension's best-effort behavior and continue processing.\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor (const scriptPath of scripts) {\n\t\t\t\ttry {\n\t\t\t\t\tawait fs.chmod(scriptPath, 0o755);\n\t\t\t\t\tupdatedCount += 1;\n\t\t\t\t} catch {\n\t\t\t\t\t// Match the extension's best-effort behavior and continue processing.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tprocessedCount: scripts.length,\n\t\t\tupdatedCount,\n\t\t\tplatform: process.platform,\n\t\t\tscripts,\n\t\t};\n\t}\n\n\tasync initializeGit(workspacePath: string): Promise<ServiceMeProjectGitInitResult> {\n\t\tconst command = \"git init\";\n\t\tawait runCommand(command, {\n\t\t\tcwd: workspacePath,\n\t\t\tshell: true,\n\t\t});\n\n\t\treturn {\n\t\t\tinitialized: true,\n\t\t\tcommand,\n\t\t};\n\t}\n\n\tasync runScaffoldPrune(\n\t\tworkspacePath: string,\n\t\tpreset?: string\n\t): Promise<ServiceMeProjectScaffoldPruneResult> {\n\t\tif (!preset) {\n\t\t\tthrow createServicemeError(\"invalid_params\", \"Expected scaffold preset.\");\n\t\t}\n\n\t\tawait this.ensurePresetManifest(workspacePath, preset);\n\n\t\tconst pruneCommand = `pnpm run scaffold:prune -- --preset ${preset} --project-root .`;\n\t\tconst initMetadataCommand = `pnpm run scaffold:init-metadata -- --preset ${preset} --project-root . --force`;\n\n\t\tconst commands = [pruneCommand, initMetadataCommand];\n\n\t\tfor (const command of commands) {\n\t\t\tif (command === initMetadataCommand) {\n\t\t\t\t// Some templates remove .ms-scaffold/presets during prune.\n\t\t\t\t// Recover it again before init-metadata calls loadPreset().\n\t\t\t\tawait this.ensurePresetManifest(workspacePath, preset);\n\t\t\t}\n\n\t\t\tawait runCommand(command, {\n\t\t\t\tcwd: workspacePath,\n\t\t\t\tshell: true,\n\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\tapplied: true,\n\t\t\tpreset,\n\t\t\tcommands,\n\t\t};\n\t}\n\n\tprivate async ensurePresetManifest(workspacePath: string, preset: string): Promise<void> {\n\t\tconst presetManifestPath = path.join(\n\t\t\tworkspacePath,\n\t\t\t\".ms-scaffold\",\n\t\t\t\"presets\",\n\t\t\t`${preset}.json`\n\t\t);\n\n\t\tif (await this.pathExists(presetManifestPath)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst projectModePath = path.join(workspacePath, \".ms-scaffold\", \"project-mode.json\");\n\t\tif (!(await this.pathExists(projectModePath))) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst projectModeRaw = await fs.readFile(projectModePath, \"utf8\");\n\t\tconst projectMode = JSON.parse(projectModeRaw) as {\n\t\t\tselectedModules?: unknown;\n\t\t\tprunedModules?: unknown;\n\t\t};\n\n\t\tconst synthesizedPreset = {\n\t\t\tpreset,\n\t\t\tselectedModules: Array.isArray(projectMode.selectedModules)\n\t\t\t\t? projectMode.selectedModules\n\t\t\t\t: [],\n\t\t\tprunedModules: Array.isArray(projectMode.prunedModules) ? projectMode.prunedModules : [],\n\t\t\texclude: [] as string[],\n\t\t\trecommendedFollowUps: [] as string[],\n\t\t\tmanagedFiles: [] as string[],\n\t\t\tmergeManagedFiles: [] as string[],\n\t\t\tuserOwnedPaths: [] as string[],\n\t\t};\n\n\t\tawait fs.mkdir(path.dirname(presetManifestPath), { recursive: true });\n\t\tawait fs.writeFile(\n\t\t\tpresetManifestPath,\n\t\t\t`${JSON.stringify(synthesizedPreset, null, 2)}\\n`,\n\t\t\t\"utf8\"\n\t\t);\n\t}\n\n\tprivate async findScripts(dir: string, extensions: string[]): Promise<string[]> {\n\t\tconst results: string[] = [];\n\t\tlet entries: import(\"node:fs\").Dirent[];\n\n\t\ttry {\n\t\t\tentries = await fs.readdir(dir, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn results;\n\t\t}\n\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = path.join(dir, entry.name);\n\t\t\tif (entry.isDirectory() && entry.name !== \"node_modules\" && !entry.name.startsWith(\".\")) {\n\t\t\t\tresults.push(...(await this.findScripts(fullPath, extensions)));\n\t\t\t} else if (entry.isFile() && extensions.some((ext) => entry.name.endsWith(ext))) {\n\t\t\t\tresults.push(fullPath);\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\tprivate async pathExists(targetPath: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait fs.access(targetPath);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate async selectExtractedDirectory(\n\t\tdirectoryNames: string[],\n\t\ttempExtractDir: string,\n\t\tprojectFilePattern: string,\n\t\texpectedDirName: string\n\t): Promise<string | null> {\n\t\tif (directoryNames.length === 1) {\n\t\t\treturn directoryNames[0] ?? null;\n\t\t}\n\n\t\tconst exactMatch = directoryNames.find((directoryName) => directoryName === expectedDirName);\n\t\tif (exactMatch) {\n\t\t\treturn exactMatch;\n\t\t}\n\n\t\tconst matches: string[] = [];\n\t\tfor (const directoryName of directoryNames) {\n\t\t\tif (\n\t\t\t\tawait this.directoryMatchesProjectPattern(\n\t\t\t\t\tpath.join(tempExtractDir, directoryName),\n\t\t\t\t\tprojectFilePattern\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmatches.push(directoryName);\n\t\t\t}\n\t\t}\n\n\t\tif (matches.length === 1) {\n\t\t\treturn matches[0] ?? null;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate async directoryMatchesProjectPattern(\n\t\tdirectoryPath: string,\n\t\tprojectFilePattern: string\n\t): Promise<boolean> {\n\t\tconst entries = await fs.readdir(directoryPath);\n\t\tif (projectFilePattern.includes(\"*\")) {\n\t\t\tconst regex = new RegExp(`^${projectFilePattern.replace(\"*\", \".*\")}$`);\n\t\t\treturn entries.some((entry) => regex.test(entry));\n\t\t}\n\n\t\treturn entries.includes(projectFilePattern);\n\t}\n}\n\nexport function createProjectTools(): ProjectTools {\n\treturn new ProjectTools();\n}\n","import { constants, createWriteStream } from \"node:fs\";\nimport { access, copyFile, lstat, mkdir, readdir, rename, rm } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport type { Readable } from \"node:stream\";\nimport yauzl from \"yauzl\";\n\nexport const unzipFile = (zipPath: string, dest: string): Promise<void> => {\n\treturn new Promise((resolve, reject) => {\n\t\tyauzl.open(zipPath, { lazyEntries: true }, (err: Error | null, zipfile?: yauzl.ZipFile) => {\n\t\t\tif (err) return reject(err);\n\t\t\tif (!zipfile) return reject(new Error(\"Failed to open zip file.\"));\n\n\t\t\tzipfile.readEntry();\n\t\t\tzipfile.on(\"entry\", (entry: yauzl.Entry) => {\n\t\t\t\tif (/\\/$/.test(entry.fileName)) {\n\t\t\t\t\tvoid mkdir(join(dest, entry.fileName), { recursive: true })\n\t\t\t\t\t\t.then(() => {\n\t\t\t\t\t\t\tzipfile.readEntry();\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(reject);\n\t\t\t\t} else {\n\t\t\t\t\tconst outputPath = join(dest, entry.fileName);\n\t\t\t\t\tvoid mkdir(dirname(outputPath), { recursive: true })\n\t\t\t\t\t\t.then(() => {\n\t\t\t\t\t\t\tzipfile.openReadStream(\n\t\t\t\t\t\t\t\tentry,\n\t\t\t\t\t\t\t\t(streamError: Error | null, readStream: Readable | null) => {\n\t\t\t\t\t\t\t\t\tif (streamError) return reject(streamError);\n\t\t\t\t\t\t\t\t\tif (!readStream) return reject(new Error(\"Failed to open zip entry stream.\"));\n\n\t\t\t\t\t\t\t\t\tconst writeStream = createWriteStream(outputPath);\n\t\t\t\t\t\t\t\t\treadStream.on(\"error\", reject);\n\t\t\t\t\t\t\t\t\twriteStream.on(\"error\", reject);\n\t\t\t\t\t\t\t\t\twriteStream.on(\"close\", () => {\n\t\t\t\t\t\t\t\t\t\tzipfile.readEntry();\n\t\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\t\treadStream.pipe(writeStream);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(reject);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tzipfile.on(\"end\", () => {\n\t\t\t\tresolve();\n\t\t\t});\n\n\t\t\tzipfile.on(\"error\", (zipError: Error) => {\n\t\t\t\treject(zipError);\n\t\t\t});\n\t\t});\n\t});\n};\n\nconst tryLstat = async (targetPath: string) => {\n\ttry {\n\t\treturn await lstat(targetPath);\n\t} catch {\n\t\treturn null;\n\t}\n};\n\nconst mergeEntry = async (\n\tsourcePath: string,\n\tdestPath: string,\n\toverwrite: boolean\n): Promise<void> => {\n\tconst sourceStat = await lstat(sourcePath);\n\tconst destStat = await tryLstat(destPath);\n\n\tif (sourceStat.isDirectory()) {\n\t\tif (destStat && !destStat.isDirectory()) {\n\t\t\tif (!overwrite) {\n\t\t\t\tawait rm(sourcePath, { recursive: true, force: true });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait rm(destPath, { recursive: true, force: true });\n\t\t}\n\n\t\tawait mkdir(destPath, { recursive: true });\n\t\tconst children = await readdir(sourcePath);\n\n\t\tfor (const child of children) {\n\t\t\tawait mergeEntry(join(sourcePath, child), join(destPath, child), overwrite);\n\t\t}\n\n\t\tawait rm(sourcePath, { recursive: true, force: true });\n\t\treturn;\n\t}\n\n\tif (destStat) {\n\t\tif (!overwrite) {\n\t\t\tawait rm(sourcePath, { recursive: true, force: true });\n\t\t\treturn;\n\t\t}\n\n\t\tawait rm(destPath, { recursive: true, force: true });\n\t}\n\n\ttry {\n\t\tawait rename(sourcePath, destPath);\n\t} catch {\n\t\tawait copyFile(sourcePath, destPath);\n\t\tawait rm(sourcePath, { recursive: true, force: true });\n\t}\n};\n\nexport const moveFiles = async (\n\tsourceDir: string,\n\tdestDir: string,\n\toverwrite = false\n): Promise<void> => {\n\tawait mkdir(destDir, { recursive: true });\n\tconst files = await readdir(sourceDir);\n\n\tfor (const file of files) {\n\t\tconst sourceFile = join(sourceDir, file);\n\t\tconst destFile = join(destDir, file);\n\n\t\tif (!overwrite) {\n\t\t\ttry {\n\t\t\t\tawait access(destFile, constants.F_OK);\n\t\t\t\tawait rm(sourceFile, { recursive: true, force: true });\n\t\t\t\tcontinue;\n\t\t\t} catch {\n\t\t\t\t// destination missing, continue with merge\n\t\t\t}\n\t\t}\n\n\t\tawait mergeEntry(sourceFile, destFile, overwrite);\n\t}\n};\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type { GitClient } from \"../git-client\";\nimport type { PullResult } from \"../git-client/types\";\nimport {\n\tassertSafeRepoId,\n\tgetRepoDir,\n\tgetServicemeHome,\n\tSAFE_REPO_ID_PATTERN,\n} from \"../paths/userHome\";\nimport type { ReposStore } from \"../repos/store\";\nimport type { AnyRepoConfig, RepoConfig, UserRepoConfig } from \"../repos/types\";\nimport { isDefaultRepo, isUserRepo } from \"../repos/types\";\n\n/**\n * Skill & Agent v2 — Client RepoManager (M3)\n *\n * RepoManager is the orchestrator for local git repo management. It\n * composes:\n * - {@link ReposStore} (in-memory CRUD over `~/.serviceme/repos.json`)\n * - {@link GitClient} (spawns `git` against the server proxy)\n * - the filesystem (clone targets live under `~/.serviceme/repos/<id>/`)\n *\n * Lifecycle responsibilities:\n * - **Bootstrap**: on first launch, clone all enabled default repos.\n * - **Sync**: pullOne / pullAll for periodic updates.\n * - **User repos**: addUserRepo validates URL, probes branch via\n * `git ls-remote`, writes the entry, and triggers an async clone.\n * - **Cleanup**: removeUserRepo deletes both the local clone AND the\n * `repos.json` entry. disableRepo / enableRepo only flip a flag.\n *\n * Default repos (`source === 'default'`) cannot be removed — only\n * disabled. The Store enforces this; RepoManager propagates the error.\n *\n * @see docs/architecture/skill-agent-v2-repo.md §5.3 RepoManager\n */\n\nexport interface RepoManagerOptions {\n\tstore: ReposStore;\n\tgitClient: GitClient;\n\t/** Override the clock for deterministic tests. */\n\tnow?: () => string;\n\t/** Skip the actual git clone (used by tests). When true, the\n\t * filesystem-side mkdir is the only side effect. */\n\tskipClone?: boolean;\n}\n\nexport interface AddUserRepoInput {\n\t/** Upstream URL the user pasted in. */\n\turl: string;\n\t/** Branch to track. Auto-detected from upstream when omitted. */\n\tbranch?: string;\n\t/** Optional display name (defaults to id). */\n\tname?: string;\n\t/** Whether this repo participates in scheduled syncs. Defaults to true. */\n\tenabled?: boolean;\n\t/** Route git traffic via the server proxy (default true). */\n\tuseProxy?: boolean;\n}\n\nexport interface AddUserRepoResult {\n\trepo: UserRepoConfig;\n\tbranch: string;\n\tcloned: boolean;\n}\n\n/** Snapshot of a sync run, suitable for UI rendering. */\nexport interface SyncReport {\n\tpulls: Array<{\n\t\trepoId: string;\n\t\tstatus: \"ok\" | \"error\";\n\t\tresult?: PullResult;\n\t\terror?: string;\n\t}>;\n}\n\n/** Pattern: kebab-case slug from a git URL's last segment. */\nfunction idFromUrl(url: string): string {\n\tconst trimmed = url\n\t\t.trim()\n\t\t.replace(/\\/+$/, \"\")\n\t\t.replace(/\\.git$/, \"\");\n\n\tconst sanitize = (value: string): string =>\n\t\tvalue\n\t\t\t.trim()\n\t\t\t.toLowerCase()\n\t\t\t.replace(/[^a-zA-Z0-9_-]/g, \"-\")\n\t\t\t.replace(/-+/g, \"-\")\n\t\t\t.replace(/^-+|-+$/g, \"\");\n\n\tlet owner = \"\";\n\tlet repo = \"\";\n\n\tif (/^https?:\\/\\//i.test(trimmed)) {\n\t\tconst parsed = new URL(trimmed);\n\t\tconst segments = parsed.pathname.split(\"/\").filter(Boolean);\n\t\trepo = segments.at(-1) ?? \"\";\n\t\towner = segments.length >= 2 ? (segments.at(-2) ?? \"\") : \"\";\n\t} else if (trimmed.startsWith(\"git@\")) {\n\t\tconst colon = trimmed.indexOf(\":\");\n\t\tconst repoPath = colon >= 0 ? trimmed.slice(colon + 1) : \"\";\n\t\tconst segments = repoPath.split(\"/\").filter(Boolean);\n\t\trepo = segments.at(-1) ?? \"\";\n\t\towner = segments.length >= 2 ? (segments.at(-2) ?? \"\") : \"\";\n\t} else {\n\t\trepo = trimmed.split(\"/\").pop() ?? \"\";\n\t}\n\n\tconst safeRepo = sanitize(repo);\n\tconst safeOwner = sanitize(owner);\n\tconst safe = safeOwner ? `${safeOwner}-${safeRepo}` : safeRepo;\n\tif (!SAFE_REPO_ID_PATTERN.test(safe)) {\n\t\tthrow new InvalidRepoUrlError(`cannot derive a valid repo id from url: ${url}`);\n\t}\n\treturn safe;\n}\n\nfunction proxyRepoId(url: string): string {\n\tconst encoded = Buffer.from(url.trim(), \"utf8\").toString(\"base64url\");\n\treturn `repo-${encoded}`;\n}\n\n/** Sentinel errors. */\nexport class RepoManagerError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"RepoManagerError\";\n\t}\n}\nexport class InvalidRepoUrlError extends RepoManagerError {}\nexport class RepoCloneConflictError extends RepoManagerError {}\nexport class RepoNotInStoreError extends RepoManagerError {}\n\nexport class RepoManager {\n\tprivate readonly store: ReposStore;\n\tprivate readonly git: GitClient;\n\tprivate readonly now: () => string;\n\tprivate readonly skipClone: boolean;\n\n\tconstructor(opts: RepoManagerOptions) {\n\t\tthis.store = opts.store;\n\t\tthis.git = opts.gitClient;\n\t\tthis.now = opts.now ?? (() => new Date().toISOString());\n\t\tthis.skipClone = opts.skipClone ?? false;\n\t}\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Bootstrap\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Clone every enabled default repo that doesn't have a local clone\n\t * yet. Failures on individual repos are recorded in `lastSyncStatus`\n\t * but do NOT abort the loop — the UI surfaces per-repo state and the\n\t * user can retry.\n\t *\n\t * Per spec §3.1: iterate all default repos; the loop honors `enabled=false` and\n\t * disables (rather than removes) repos the user has turned off.\n\t */\n\tasync ensureDefaults(): Promise<SyncReport> {\n\t\tconst report: SyncReport = { pulls: [] };\n\t\tconst defaults = this.store.listDefault();\n\t\tfor (const repo of defaults) {\n\t\t\tif (!repo.enabled) continue;\n\t\t\tconst localPath = getRepoDir(repo.id);\n\t\t\tconst exists = await this.pathExists(localPath);\n\t\t\tif (exists) {\n\t\t\t\t// Directory exists but may be empty (e.g. interrupted clone).\n\t\t\t\t// Delete and re-clone so it becomes a valid git repo.\n\t\t\t\tif (await this.isValidGitRepo(localPath)) continue;\n\t\t\t\tawait fs.rm(localPath, { recursive: true, force: true });\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tif (!this.skipClone) {\n\t\t\t\t\tawait fs.mkdir(path.dirname(localPath), { recursive: true });\n\t\t\t\t\tawait this.git.clone(repo.id, repo.url, localPath, repo.branch, true);\n\t\t\t\t}\n\t\t\t\tawait this.store.updateRepo(repo.id, {\n\t\t\t\t\tlastSyncStatus: \"ok\",\n\t\t\t\t\tlastSyncAt: this.now(),\n\t\t\t\t});\n\t\t\t\treport.pulls.push({ repoId: repo.id, status: \"ok\" });\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\tawait this.store.updateRepo(repo.id, {\n\t\t\t\t\tlastSyncStatus: \"error\",\n\t\t\t\t\tlastSyncAt: this.now(),\n\t\t\t\t\tlastSyncError: message,\n\t\t\t\t});\n\t\t\t\treport.pulls.push({ repoId: repo.id, status: \"error\", error: message });\n\t\t\t}\n\t\t}\n\t\treturn report;\n\t}\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Per-repo sync\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Pull a single repo. Updates `lastSync*` fields on success or failure.\n\t * Returns the raw `PullResult` so callers can surface fetch progress.\n\t */\n\tasync pullOne(repoId: string): Promise<PullResult> {\n\t\tconst repo = this.store.get(repoId);\n\t\tif (!repo) throw new RepoNotInStoreError(repoId);\n\t\tconst useProxy = repo.useProxy ?? true;\n\t\tconst proxyId = useProxy ? proxyRepoId(repo.url) : repo.id;\n\t\tconst localPath = getRepoDir(repoId);\n\t\tconst exists = await this.pathExists(localPath);\n\t\t// If directory exists but is empty or not a valid git repo, remove\n\t\t// it so the clone path below runs cleanly.\n\t\tif (exists && !(await this.isValidGitRepo(localPath))) {\n\t\t\tawait fs.rm(localPath, { recursive: true, force: true });\n\t\t}\n\t\tif (!exists || !(await this.pathExists(localPath))) {\n\t\t\t// First-time sync (or re-clone after invalid directory) — clone instead of pulling.\n\t\t\tawait fs.mkdir(path.dirname(localPath), { recursive: true });\n\t\t\tif (!this.skipClone) {\n\t\t\t\tawait this.git.clone(proxyId, repo.url, localPath, repo.branch, useProxy);\n\t\t\t}\n\t\t\tconst result: PullResult = {\n\t\t\t\tupdated: true,\n\t\t\t\tcommitSha: \"\",\n\t\t\t\tbranch: repo.branch,\n\t\t\t};\n\t\t\tawait this.store.updateRepo(repoId, {\n\t\t\t\tlastSyncStatus: \"ok\",\n\t\t\t\tlastSyncAt: this.now(),\n\t\t\t});\n\t\t\treturn result;\n\t\t}\n\t\ttry {\n\t\t\tconst result = await this.git.pull(proxyId, localPath, repo.branch, useProxy, repo.url);\n\t\t\tawait this.store.updateRepo(repoId, {\n\t\t\t\tlastSyncStatus: \"ok\",\n\t\t\t\tlastSyncAt: this.now(),\n\t\t\t\tlastSyncCommitSha: result.commitSha,\n\t\t\t});\n\t\t\treturn result;\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tawait this.store.updateRepo(repoId, {\n\t\t\t\tlastSyncStatus: \"error\",\n\t\t\t\tlastSyncAt: this.now(),\n\t\t\t\tlastSyncError: message,\n\t\t\t});\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/** Pull every enabled repo. Per-repo failures don't abort the run. */\n\tasync pullAll(): Promise<SyncReport> {\n\t\tconst report: SyncReport = { pulls: [] };\n\t\tconst enabled = this.store.list().filter((r) => r.enabled);\n\t\tfor (const repo of enabled) {\n\t\t\ttry {\n\t\t\t\tconst r = await this.pullOne(repo.id);\n\t\t\t\treport.pulls.push({ repoId: repo.id, status: \"ok\", result: r });\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\treport.pulls.push({ repoId: repo.id, status: \"error\", error: message });\n\t\t\t}\n\t\t}\n\t\treturn report;\n\t}\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// User repos: add / remove\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Validate URL, derive an id, detect the branch via `git ls-remote`,\n\t * then add the entry to the store and trigger a clone.\n\t *\n\t * Spec §5.3 says \"branch detection\" happens BEFORE the store write so\n\t * the resulting `repos.json` is fully populated. The clone is async\n\t * but the function returns synchronously once the store is updated\n\t * (cloning happens via `ensureDefaults`-style background fire).\n\t *\n\t * For tests with `skipClone: true`, the clone is skipped entirely.\n\t */\n\tasync addUserRepo(input: AddUserRepoInput): Promise<AddUserRepoResult> {\n\t\tconst url = input.url.trim();\n\t\tif (!/^https?:\\/\\//.test(url) && !url.startsWith(\"git@\")) {\n\t\t\tthrow new InvalidRepoUrlError(`expected https:// or git@ URL, got: ${url}`);\n\t\t}\n\t\tconst id = idFromUrl(url);\n\t\tconst useProxy = input.useProxy ?? true;\n\t\tconst userProxyId = useProxy ? proxyRepoId(url) : id;\n\t\tassertSafeRepoId(id);\n\n\t\t// Detect branch if not provided\n\t\tlet branch = input.branch?.trim();\n\t\tif (!branch) {\n\t\t\tconst branches = await this.git.lsRemote(userProxyId, url, useProxy);\n\t\t\tconst head =\n\t\t\t\tbranches.find((b) => b.ref === \"refs/heads/main\") ??\n\t\t\t\tbranches.find((b) => b.ref === \"refs/heads/master\") ??\n\t\t\t\tbranches.find((b) => b.ref.endsWith(\"/v2\"));\n\t\t\tif (!head) {\n\t\t\t\tthrow new InvalidRepoUrlError(\n\t\t\t\t\t`could not detect default branch for ${url} (no refs/heads/{main,master,v2})`\n\t\t\t\t);\n\t\t\t}\n\t\t\tbranch = head.ref.replace(/^refs\\/heads\\//, \"\");\n\t\t}\n\n\t\t// Check for collision BEFORE writing the store so we can give a\n\t\t// clean error message.\n\t\tif (this.store.get(id)) {\n\t\t\tthrow new RepoCloneConflictError(`repo id '${id}' already exists`);\n\t\t}\n\n\t\tconst repo: UserRepoConfig = {\n\t\t\tid,\n\t\t\tname: input.name?.trim() || id,\n\t\t\turl,\n\t\t\tbranch,\n\t\t\tenabled: input.enabled ?? true,\n\t\t\tuseProxy,\n\t\t\twriteEnabled: false,\n\t\t\tsource: \"user\",\n\t\t\taddedAt: this.now(),\n\t\t};\n\t\tawait this.store.addUserRepo(repo);\n\n\t\t// Trigger async clone (or skip in tests)\n\t\tlet cloned = false;\n\t\tif (!this.skipClone) {\n\t\t\tconst localPath = getRepoDir(id);\n\t\t\tawait fs.mkdir(path.dirname(localPath), { recursive: true });\n\t\t\tawait this.git.clone(userProxyId, url, localPath, branch, useProxy);\n\t\t\tcloned = true;\n\t\t}\n\n\t\treturn { repo, branch, cloned };\n\t}\n\n\t/**\n\t * Remove a user-added repo. Throws when:\n\t * - the repo is unknown\n\t * - the repo is a default (use `disableRepo` instead — store enforces)\n\t *\n\t * The local clone is deleted via `fs.rm`. If the local dir is\n\t * already gone we silently succeed (idempotent re-remove).\n\t */\n\tasync removeUserRepo(repoId: string): Promise<void> {\n\t\tconst repo = this.store.get(repoId);\n\t\tif (!repo) throw new RepoNotInStoreError(repoId);\n\t\tif (isDefaultRepo(repo)) {\n\t\t\tthrow new RepoCloneConflictError(\n\t\t\t\t`cannot remove default repo '${repoId}'; use disableRepo() instead`\n\t\t\t);\n\t\t}\n\t\t// Store first — so the failure surface is the user-visible\n\t\t// repos.json change, not a half-deleted filesystem.\n\t\tawait this.store.removeUserRepo(repoId);\n\t\tconst localPath = getRepoDir(repoId);\n\t\ttry {\n\t\t\tawait fs.rm(localPath, { recursive: true, force: true });\n\t\t} catch (err) {\n\t\t\t// Swallow ENOENT — idempotent. Re-throw anything else.\n\t\t\tif ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n\t\t}\n\t}\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Enable / disable\n\t// ─────────────────────────────────────────────────────────────────\n\n\tasync disableRepo(repoId: string): Promise<AnyRepoConfig> {\n\t\treturn this.store.disableRepo(repoId);\n\t}\n\n\tasync enableRepo(repoId: string): Promise<AnyRepoConfig> {\n\t\treturn this.store.enableRepo(repoId);\n\t}\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Helpers\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/** `true` when `~/.serviceme/repos/<id>/` exists on disk. */\n\tasync hasLocalClone(repoId: string): Promise<boolean> {\n\t\treturn this.pathExists(getRepoDir(repoId));\n\t}\n\n\t/** Force-create the SERVICEME home directory tree (idempotent). */\n\tasync ensureHome(): Promise<void> {\n\t\tawait fs.mkdir(getServicemeHome(), { recursive: true });\n\t}\n\n\t/**\n\t * Returns `true` when `p` contains a `.git` entry — i.e. it is an\n\t * initialised git repository and safe to `pull`. Empty directories\n\t * (e.g. from an interrupted clone) return `false`.\n\t */\n\tprivate async isValidGitRepo(p: string): Promise<boolean> {\n\t\treturn this.pathExists(path.join(p, \".git\"));\n\t}\n\n\tprivate async pathExists(p: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait fs.stat(p);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\nexport type { RepoConfig };\n/** Type guard for AnyRepoConfig — re-exported for tests + extension code. */\nexport { isDefaultRepo, isUserRepo };\n","/**\n * `repos.json` schema — mirrors §3 of docs/architecture/skill-agent-v2-repo.md.\n *\n * The on-disk file lives at `${SERVICEME_HOME}/repos.json` (see\n * `paths/userHome.getReposConfigPath`). It holds the user-visible catalog of\n * skill/agent repositories (default + user-added) plus the currently-selected\n * default.\n *\n * Two flavours of repo:\n * - `DefaultRepoConfig` — hardcoded by SERVICEME; can only be disabled, not\n * removed.\n * - `UserRepoConfig` — added by the user via the \"Add repository\" flow;\n * can be removed freely.\n *\n * Both extend `RepoConfig` and add a `source` discriminant so consumers can\n * branch on `repo.source === 'default' | 'user'` without sniffing free-form\n * fields.\n */\n\n/** ISO 8601 timestamp. */\nexport type IsoTimestamp = string;\n\n/** Per-repo sync health — surfaced in the UI alongside the repo entry. */\nexport type SyncStatus = \"ok\" | \"error\";\n\n/** Discriminant on `RepoConfig.source` — narrow with a literal check. */\nexport type RepoSource = \"default\" | \"user\";\n\n/**\n * Common shape shared by both default and user-added repos. Source lives on\n * the type so we can keep the loader schema flat while still letting\n * `DefaultRepoConfig` carry a `description`.\n */\nexport interface RepoConfig {\n\t/** Stable id used in on-disk paths (must match `SAFE_REPO_ID_PATTERN`). */\n\tid: string;\n\t/** Display name shown in UI. */\n\tname: string;\n\t/** Upstream git URL (typically github.com/<owner>/<repo>.git). */\n\turl: string;\n\t/** Default branch to track (e.g. \"v2\", \"main\"). */\n\tbranch: string;\n\t/** Whether this repo participates in scheduled syncs. */\n\tenabled: boolean;\n\t/** Whether git traffic should go through the local server proxy (default true). */\n\tuseProxy?: boolean;\n\t/** Whether the user is allowed to push back to this repo. */\n\twriteEnabled: boolean;\n\t/** When the user (or first-run installer) added this repo. */\n\taddedAt: IsoTimestamp;\n\t/** Last successful sync timestamp. */\n\tlastSyncAt?: IsoTimestamp;\n\t/** Commit SHA at the time of the last successful sync. */\n\tlastSyncCommitSha?: string;\n\t/** Result of the most recent sync attempt. */\n\tlastSyncStatus?: SyncStatus;\n\t/** Error message from the most recent sync attempt (when `lastSyncStatus === 'error'`). */\n\tlastSyncError?: string;\n\t/** Whether this repo is a default or a user-added repo. */\n\tsource: RepoSource;\n}\n\n/** A default (bundled) repo — has a UI-facing description, cannot be removed. */\nexport interface DefaultRepoConfig extends RepoConfig {\n\tsource: \"default\";\n\tdescription: string;\n}\n\n/** A user-added repo — can be added, removed, disabled freely. */\nexport interface UserRepoConfig extends RepoConfig {\n\tsource: \"user\";\n}\n\n/** Any repo, regardless of source. */\nexport type AnyRepoConfig = DefaultRepoConfig | UserRepoConfig;\n\n/**\n * Root shape of `repos.json`. `version` lets us evolve the schema later\n * without colliding with hand-edited files.\n */\nexport interface ReposFile {\n\tversion: 1;\n\t/** Id of the repo currently surfaced as the default selection in the UI. */\n\tdefaultRepoId: string;\n\trepos: AnyRepoConfig[];\n}\n\n/**\n * Convenience type-guard — narrows to `DefaultRepoConfig`. Useful when callers\n * want to render `repo.description` or reject `removeRepo(repo)` for defaults.\n */\nexport function isDefaultRepo(repo: RepoConfig): repo is DefaultRepoConfig {\n\treturn repo.source === \"default\";\n}\n\n/** Convenience type-guard — narrows to `UserRepoConfig`. */\nexport function isUserRepo(repo: RepoConfig): repo is UserRepoConfig {\n\treturn repo.source === \"user\";\n}\n","import type { AnyRepoConfig, DefaultRepoConfig, ReposFile } from \"./types\";\nimport { isDefaultRepo } from \"./types\";\n\n/**\n * Hard-coded catalog of repositories SERVICEME ships with. The id, URL and\n * branch are pinned by the design doc (§12) and **must not** change without\n * an owner-approved migration. The order here mirrors the on-disk\n * `repos.json` order (§3.1) which the UI relies on.\n */\n\nexport const DEFAULT_REPO_ID = \"medalsoftchina-ms-skills\";\n\nconst FIXED_ADDED_AT = \"2026-06-29T00:00:00.000Z\";\n\n/**\n * Master list of default repositories. `getDefaultRepoConfigs()` returns\n * clones with the current timestamp baked into `addedAt` so two `load()`\n * calls return identical-but-distinct objects (the store can mutate one\n * without disturbing the other).\n */\nconst DEFAULT_REPO_SEEDS: ReadonlyArray<Omit<DefaultRepoConfig, \"addedAt\">> = [\n\t{\n\t\tid: \"medalsoftchina-ms-skills\",\n\t\tname: \"SERVICEME (精选)\",\n\t\turl: \"https://github.com/medalsoftchina/ms-skills.git\",\n\t\tbranch: \"v2\",\n\t\tenabled: true,\n\t\tuseProxy: true,\n\t\twriteEnabled: true,\n\t\tsource: \"default\",\n\t\tdescription: \"Medalsoft 精选 skill/agent 仓库,可读可写\",\n\t},\n\t{\n\t\tid: \"anthropics-skills\",\n\t\tname: \"Anthropic Skills\",\n\t\turl: \"https://github.com/anthropics/skills.git\",\n\t\tbranch: \"main\",\n\t\tenabled: true,\n\t\tuseProxy: true,\n\t\twriteEnabled: false,\n\t\tsource: \"default\",\n\t\tdescription: \"Anthropic 官方 skills 示例\",\n\t},\n\t{\n\t\tid: \"github-awesome-copilot\",\n\t\tname: \"GitHub Awesome Copilot\",\n\t\turl: \"https://github.com/github/awesome-copilot.git\",\n\t\tbranch: \"main\",\n\t\tenabled: true,\n\t\tuseProxy: true,\n\t\twriteEnabled: false,\n\t\tsource: \"default\",\n\t\tdescription: \"GitHub Copilot 社区精选 prompts/agents\",\n\t},\n\t{\n\t\tid: \"mattpocock-skills\",\n\t\tname: \"Matt Pocock Skills\",\n\t\turl: \"https://github.com/mattpocock/skills.git\",\n\t\tbranch: \"main\",\n\t\tenabled: true,\n\t\tuseProxy: true,\n\t\twriteEnabled: false,\n\t\tsource: \"default\",\n\t\tdescription: \"Matt Pocock 社区 skills 仓库\",\n\t},\n\t{\n\t\tid: \"composiohq-awesome-claude-skills\",\n\t\tname: \"Composio Awesome Claude Skills\",\n\t\turl: \"https://github.com/ComposioHQ/awesome-claude-skills.git\",\n\t\tbranch: \"master\",\n\t\tenabled: true,\n\t\tuseProxy: true,\n\t\twriteEnabled: false,\n\t\tsource: \"default\",\n\t\tdescription: \"Composio 维护的 Claude skills 集合\",\n\t},\n];\n\n/** Returns a deep-ish clone of every default repo with a fresh `addedAt`. */\nexport function getAllDefaultRepoConfigs(\n\tnow: () => string = () => new Date().toISOString()\n): DefaultRepoConfig[] {\n\tconst stamp = now();\n\treturn DEFAULT_REPO_SEEDS.map((seed) => ({\n\t\t...seed,\n\t\taddedAt: stamp,\n\t}));\n}\n\n/** Returns a single default repo by id, or `undefined` when not in the catalog. */\nexport function getDefaultRepoConfig(\n\tid: string,\n\tnow: () => string = () => new Date().toISOString()\n): DefaultRepoConfig | undefined {\n\tconst seed = DEFAULT_REPO_SEEDS.find((s) => s.id === id);\n\tif (!seed) {\n\t\treturn undefined;\n\t}\n\treturn { ...seed, addedAt: now() };\n}\n\n/**\n * Idempotent: returns a fresh `ReposFile` containing the default set, or —\n * when `existing` already has all defaults — returns it untouched (still cloned\n * to keep callers from accidentally mutating shared state).\n */\nexport function buildDefaultReposFile(\n\tnow: () => string = () => new Date().toISOString(),\n\texisting?: ReposFile\n): ReposFile {\n\tconst defaults = getAllDefaultRepoConfigs(now);\n\n\tif (!existing) {\n\t\treturn {\n\t\t\tversion: 1,\n\t\t\tdefaultRepoId: DEFAULT_REPO_ID,\n\t\t\trepos: defaults,\n\t\t};\n\t}\n\n\t// Merge: keep existing repos as-is (including any user-added ones), then\n\t// append any default ids that are missing. Catalog metadata (name, url,\n\t// branch, description, useProxy, writeEnabled) on already-installed\n\t// defaults is refreshed from the current seed so editing\n\t// `DEFAULT_REPO_SEEDS` takes effect without deleting `repos.json`; user/\n\t// runtime state (`enabled`, `addedAt`, `lastSync*`) is preserved as-is.\n\tconst existingIds = new Set(existing.repos.map((r) => r.id));\n\tconst missingDefaults = defaults.filter((d) => !existingIds.has(d.id));\n\tconst { repos: refreshedRepos, changed } = refreshDefaultsMetadata(existing.repos, defaults);\n\tif (missingDefaults.length === 0 && !changed) {\n\t\treturn existing;\n\t}\n\treturn {\n\t\t...existing,\n\t\trepos: [...refreshedRepos, ...missingDefaults],\n\t\tdefaultRepoId: resolveDefaultRepoId(existing, existingIds),\n\t};\n}\n\n/**\n * `ensureDefaultsInstalled(existing)` — pure helper that returns either\n * `existing` (when defaults are already present and up to date) or a merged\n * copy that appends the missing defaults and refreshes catalog metadata on\n * existing ones. The companion `ensureDefaultsInstalledInStore` below is the\n * mutating, store-bound counterpart.\n */\nexport function ensureDefaultsInstalled(\n\texisting: ReposFile,\n\tnow: () => string = () => new Date().toISOString()\n): { config: ReposFile; installed: string[] } {\n\tconst defaults = getAllDefaultRepoConfigs(now);\n\tconst existingIds = new Set(existing.repos.map((r) => r.id));\n\tconst missing = defaults.filter((d) => !existingIds.has(d.id));\n\tconst { repos: refreshedRepos, changed } = refreshDefaultsMetadata(existing.repos, defaults);\n\tif (missing.length === 0 && !changed) {\n\t\treturn { config: existing, installed: [] };\n\t}\n\tconst next: ReposFile = {\n\t\t...existing,\n\t\trepos: [...refreshedRepos, ...missing],\n\t\tdefaultRepoId: resolveDefaultRepoId(existing, existingIds),\n\t};\n\treturn { config: next, installed: missing.map((d) => d.id) };\n}\n\n/** Catalog metadata fields synced from `DEFAULT_REPO_SEEDS` onto already-installed defaults. */\nconst SYNCED_METADATA_KEYS = [\n\t\"name\",\n\t\"url\",\n\t\"branch\",\n\t\"description\",\n\t\"useProxy\",\n\t\"writeEnabled\",\n] as const satisfies ReadonlyArray<keyof DefaultRepoConfig>;\n\n/**\n * Refreshes catalog metadata (see {@link SYNCED_METADATA_KEYS}) on every\n * default repo entry that has a matching seed, leaving user/runtime state\n * (`enabled`, `addedAt`, `lastSync*`) and user-added repos untouched. Returns\n * the original array (by reference) when nothing actually changed, so\n * callers can cheaply detect a no-op.\n */\nfunction refreshDefaultsMetadata(\n\trepos: ReadonlyArray<AnyRepoConfig>,\n\tdefaults: ReadonlyArray<DefaultRepoConfig>\n): { repos: AnyRepoConfig[]; changed: boolean } {\n\tconst seedById = new Map(defaults.map((d) => [d.id, d]));\n\tlet changed = false;\n\tconst next = repos.map((repo) => {\n\t\tif (!isDefaultRepo(repo)) {\n\t\t\treturn repo;\n\t\t}\n\t\tconst seed = seedById.get(repo.id);\n\t\tif (!seed) {\n\t\t\treturn repo;\n\t\t}\n\t\tconst isStale = SYNCED_METADATA_KEYS.some((key) => repo[key] !== seed[key]);\n\t\tif (!isStale) {\n\t\t\treturn repo;\n\t\t}\n\t\tchanged = true;\n\t\treturn { ...repo, ...pick(seed, SYNCED_METADATA_KEYS) };\n\t});\n\treturn { repos: next, changed };\n}\n\nfunction pick<T extends object, K extends keyof T>(source: T, keys: ReadonlyArray<K>): Pick<T, K> {\n\tconst result = {} as Pick<T, K>;\n\tfor (const key of keys) {\n\t\tresult[key] = source[key];\n\t}\n\treturn result;\n}\n\n/**\n * Decide what the post-bootstrap `defaultRepoId` should be.\n *\n * Rules (in order):\n * 1. If `existing.defaultRepoId` references a real repo in `existing`,\n * keep it (the user explicitly chose this default).\n * 2. Otherwise — including the loader's empty-state `\"\"` sentinel or any\n * other unknown id — fall back to {@link DEFAULT_REPO_ID}.\n */\nfunction resolveDefaultRepoId(existing: ReposFile, existingIds: Set<string>): string {\n\tconst placeholder = existing.defaultRepoId;\n\tif (placeholder && existingIds.has(placeholder)) {\n\t\treturn placeholder;\n\t}\n\treturn DEFAULT_REPO_ID;\n}\n\n// `FIXED_ADDED_AT` is exported in case tests want a deterministic timestamp\n// to compare against without stubbing `Date`.\nexport { FIXED_ADDED_AT as DEFAULT_REPO_FIXED_ADDED_AT };\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { z } from \"zod\";\n\nimport { SAFE_REPO_ID_PATTERN } from \"../paths/userHome\";\nimport type { AnyRepoConfig, DefaultRepoConfig, ReposFile, UserRepoConfig } from \"./types\";\n\n/**\n * On-disk `repos.json` loader / writer.\n *\n * Responsibilities:\n * 1. **Validate** every read with a Zod schema — never trust whatever is on\n * disk; a hand-edited file can be malformed in subtle ways.\n * 2. **Recover gracefully** from corrupted files by moving them to a\n * timestamped `.bak` sibling and returning a sentinel empty config.\n * Callers can then prompt the user to re-add their repos.\n * 3. **Write atomically** — always write to `<target>.tmp-<rand>` first\n * and `rename` over the target. A crash mid-write leaves the previous\n * good file intact instead of a half-written JSON.\n *\n * `fs.watch`-based change detection lives in `store.ts`. This module is\n * deliberately pure I/O + validation so it can be exercised from tests\n * without spinning up watchers.\n */\n\nconst ISO_TIMESTAMP_PATTERN = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:?\\d{2})$/;\n\nconst repoIdSchema = z\n\t.string()\n\t.min(1)\n\t.max(64)\n\t.regex(SAFE_REPO_ID_PATTERN, \"repo id must match /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/\");\n\nconst isoTimestampSchema = z.string().regex(ISO_TIMESTAMP_PATTERN, {\n\tmessage: \"must be an ISO-8601 timestamp\",\n});\n\nconst baseRepoFields = {\n\tid: repoIdSchema,\n\tname: z.string().min(1).max(200),\n\turl: z.string().url(),\n\tbranch: z.string().min(1).max(200),\n\tenabled: z.boolean(),\n\tuseProxy: z.boolean().optional().default(true),\n\twriteEnabled: z.boolean(),\n\taddedAt: isoTimestampSchema,\n\tlastSyncAt: isoTimestampSchema.optional(),\n\tlastSyncCommitSha: z.string().min(1).max(80).optional(),\n\tlastSyncStatus: z.enum([\"ok\", \"error\"]).optional(),\n\tlastSyncError: z.string().max(2_000).optional(),\n};\n\nconst defaultRepoSchema = z.object({\n\t...baseRepoFields,\n\tsource: z.literal(\"default\"),\n\tdescription: z.string().min(1).max(2_000),\n});\n\nconst userRepoSchema = z.object({\n\t...baseRepoFields,\n\tsource: z.literal(\"user\"),\n});\n\nconst repoSchema = z.discriminatedUnion(\"source\", [defaultRepoSchema, userRepoSchema]);\n\nexport const reposFileSchema = z\n\t.object({\n\t\tversion: z.literal(1),\n\t\t// Empty string is the sentinel for \"no repos yet\" (see `repos: []`\n\t\t// below); a populated file must use a real `repoIdSchema`-shaped id,\n\t\t// checked in `superRefine` below.\n\t\tdefaultRepoId: z.string(),\n\t\trepos: z.array(repoSchema),\n\t})\n\t.superRefine((value, ctx) => {\n\t\tconst ids = new Set<string>();\n\t\tfor (const repo of value.repos) {\n\t\t\tif (ids.has(repo.id)) {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: \"custom\",\n\t\t\t\t\tpath: [\"repos\"],\n\t\t\t\t\tmessage: `Duplicate repo id: ${repo.id}`,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tids.add(repo.id);\n\t\t}\n\t\tif (value.repos.length === 0) {\n\t\t\tif (value.defaultRepoId !== \"\") {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: \"custom\",\n\t\t\t\t\tpath: [\"defaultRepoId\"],\n\t\t\t\t\tmessage: \"defaultRepoId must be '' when repos[] is empty\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!ids.has(value.defaultRepoId)) {\n\t\t\tctx.addIssue({\n\t\t\t\tcode: \"custom\",\n\t\t\t\tpath: [\"defaultRepoId\"],\n\t\t\t\tmessage: `defaultRepoId '${value.defaultRepoId}' not present in repos[]`,\n\t\t\t});\n\t\t}\n\t});\n\n/** Inferred Zod type — identical to our hand-written `ReposFile`. */\nexport type ReposFileInput = z.infer<typeof reposFileSchema>;\n\n/** Result of a load attempt — either a parsed file or a recovery notice. */\nexport interface LoadResult {\n\t/** Parsed + validated repos file (always populated, even on recovery). */\n\tconfig: ReposFile;\n\t/**\n\t * Non-null when the existing file on disk was unreadable / invalid and\n\t * was moved aside as a `.bak`. The store can use this to surface a\n\t * warning in the UI.\n\t */\n\trecoveredFromBackup: string | null;\n}\n\n/** Dependencies — defaults to the real fs, tests inject a fake. */\nexport interface ReposLoaderFileSystem {\n\treadFile: typeof fs.readFile;\n\twriteFile: typeof fs.writeFile;\n\trename: typeof fs.rename;\n\tmkdir: typeof fs.mkdir;\n\tstat: typeof fs.stat;\n\tunlink?: typeof fs.unlink;\n}\n\nexport interface ReposLoaderOptions {\n\t/** Absolute path of the `repos.json` file. */\n\tconfigPath: string;\n\t/** Custom time provider — defaults to `() => new Date().toISOString()`. */\n\tnow?: () => string;\n\t/** Random suffix provider for atomic-write temp files. */\n\trandomSuffix?: () => string;\n\t/** Filesystem shim for tests. */\n\tfileSystem?: ReposLoaderFileSystem;\n}\n\nconst DEFAULT_RANDOM_SUFFIX_LENGTH = 8;\n\nfunction defaultRandomSuffix(): string {\n\t// Avoid pulling `crypto.randomUUID` (Node 14.17+) here so we stay\n\t// compatible with the wider node range this package targets. A short\n\t// alphanumeric suffix is enough — collisions are vanishingly rare and\n\t// the temp file is removed right after rename.\n\tconst alphabet = \"abcdefghijklmnopqrstuvwxyz0123456789\";\n\tlet out = \"\";\n\tfor (let i = 0; i < DEFAULT_RANDOM_SUFFIX_LENGTH; i++) {\n\t\tout += alphabet[Math.floor(Math.random() * alphabet.length)];\n\t}\n\treturn out;\n}\n\nexport class ReposLoader {\n\tprivate readonly configPath: string;\n\tprivate readonly now: () => string;\n\tprivate readonly randomSuffix: () => string;\n\tprivate readonly fileSystem: ReposLoaderFileSystem;\n\n\tconstructor(options: ReposLoaderOptions) {\n\t\tthis.configPath = options.configPath;\n\t\tthis.now = options.now ?? (() => new Date().toISOString());\n\t\tthis.randomSuffix = options.randomSuffix ?? defaultRandomSuffix;\n\t\tthis.fileSystem = options.fileSystem ?? fs;\n\t}\n\n\t/** Absolute path of the file this loader reads/writes. */\n\tgetConfigPath(): string {\n\t\treturn this.configPath;\n\t}\n\n\t/**\n\t * Read + validate the on-disk config. When the file is missing, returns a\n\t * sentinel empty config (no backup is taken — that's expected on first\n\t * run). When the file is corrupted, moves it to `<name>.<ts>.bak` and\n\t * returns an empty config with `recoveredFromBackup` populated.\n\t */\n\tasync load(): Promise<LoadResult> {\n\t\tlet raw: string;\n\t\ttry {\n\t\t\traw = await this.fileSystem.readFile(this.configPath, \"utf-8\");\n\t\t} catch (error) {\n\t\t\tif (isErrnoCode(error, \"ENOENT\")) {\n\t\t\t\treturn {\n\t\t\t\t\tconfig: this.buildEmptyConfig(),\n\t\t\t\t\trecoveredFromBackup: null,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\tconst parsed = safeParseJson(raw);\n\t\tif (parsed === undefined) {\n\t\t\tconst backupPath = await this.quarantine(\"json-parse-failed\");\n\t\t\treturn {\n\t\t\t\tconfig: this.buildEmptyConfig(),\n\t\t\t\trecoveredFromBackup: backupPath,\n\t\t\t};\n\t\t}\n\n\t\tconst result = reposFileSchema.safeParse(parsed);\n\t\tif (!result.success) {\n\t\t\tconst backupPath = await this.quarantine(\"schema-invalid\");\n\t\t\treturn {\n\t\t\t\tconfig: this.buildEmptyConfig(),\n\t\t\t\trecoveredFromBackup: backupPath,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tconfig: result.data as ReposFile,\n\t\t\trecoveredFromBackup: null,\n\t\t};\n\t}\n\n\t/**\n\t * Write the config to disk atomically. Writes to\n\t * `<target>.tmp-<random>` then `rename`s over the target. The parent\n\t * directory is created on demand.\n\t */\n\tasync save(config: ReposFile): Promise<void> {\n\t\tconst validated = reposFileSchema.parse(config) as ReposFile;\n\n\t\tconst dir = path.dirname(this.configPath);\n\t\tawait this.fileSystem.mkdir(dir, { recursive: true });\n\n\t\tconst serialized = `${JSON.stringify(validated, null, 2)}\\n`;\n\t\tconst tempPath = `${this.configPath}.tmp-${this.randomSuffix()}`;\n\n\t\tawait this.fileSystem.writeFile(tempPath, serialized, \"utf-8\");\n\t\ttry {\n\t\t\tawait this.fileSystem.rename(tempPath, this.configPath);\n\t\t} catch (error) {\n\t\t\t// Best-effort cleanup of the orphaned temp file.\n\t\t\tconst unlink = this.fileSystem.unlink ?? fs.unlink;\n\t\t\tawait unlink(tempPath).catch(() => undefined);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Move the existing file to a timestamped `.bak` sibling and return the\n\t * backup path. Used when we detect a corrupted / unparseable file and\n\t * need to make room for a fresh write.\n\t */\n\tprivate async quarantine(reason: string): Promise<string> {\n\t\tconst stamp = sanitizeForFilename(this.now());\n\t\tconst backupPath = `${this.configPath}.${stamp}.${reason}.bak`;\n\t\ttry {\n\t\t\tawait this.fileSystem.rename(this.configPath, backupPath);\n\t\t} catch (error) {\n\t\t\tif (!isErrnoCode(error, \"ENOENT\")) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\treturn backupPath;\n\t}\n\n\t/**\n\t * Empty config used when no file exists or the existing file is corrupt.\n\t * The caller (e.g. `ensureDefaultsInstalled`) is expected to populate the\n\t * real default repo set before persisting.\n\t */\n\tprivate buildEmptyConfig(): ReposFile {\n\t\treturn {\n\t\t\tversion: 1,\n\t\t\tdefaultRepoId: \"\",\n\t\t\trepos: [],\n\t\t};\n\t}\n}\n\nfunction safeParseJson(raw: string): unknown | undefined {\n\ttry {\n\t\treturn JSON.parse(raw);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction isErrnoCode(error: unknown, code: string): boolean {\n\tif (typeof error !== \"object\" || error === null) {\n\t\treturn false;\n\t}\n\treturn (error as { code?: unknown }).code === code;\n}\n\nfunction sanitizeForFilename(iso: string): string {\n\treturn iso.replace(/[^0-9T]/g, \"-\").replace(/-+/g, \"-\");\n}\n\n/** Re-export the Zod schemas so other modules can introspect them. */\nexport {\n\tdefaultRepoSchema as defaultRepoConfigSchema,\n\trepoSchema as anyRepoConfigSchema,\n\tuserRepoSchema as userRepoConfigSchema,\n};\n\n/** Helper: parse + validate a config object (e.g. freshly built in memory). */\nexport function validateReposFile(input: unknown): ReposFile {\n\treturn reposFileSchema.parse(input) as ReposFile;\n}\n\n/** Helper: discriminate a `RepoConfig` between default and user flavours. */\nexport function narrowRepoConfig(repo: AnyRepoConfig): DefaultRepoConfig | UserRepoConfig {\n\treturn repo.source === \"default\" ? (repo as DefaultRepoConfig) : (repo as UserRepoConfig);\n}\n","import { EventEmitter } from \"node:events\";\nimport * as fs from \"node:fs\";\n\nimport { ensureDefaultsInstalled } from \"./default-repos\";\nimport { type LoadResult, ReposLoader, validateReposFile } from \"./loader\";\nimport {\n\ttype AnyRepoConfig,\n\tisDefaultRepo,\n\ttype RepoConfig,\n\ttype ReposFile,\n\ttype UserRepoConfig,\n} from \"./types\";\n\n/**\n * In-memory CRUD store for `repos.json`.\n *\n * Responsibilities:\n * - Hold the authoritative `ReposFile` state in memory.\n * - Re-load on `fs.watch` events so external writes (CLI edit, user edit)\n * propagate to in-process consumers (extension UI, bridge calls).\n * - Emit `change` events so consumers can re-render without polling.\n * - Validate every write through {@link ReposLoader} so a programmatic\n * `addRepo` cannot smuggle in a malformed entry.\n *\n * The store deliberately does **not** own `fs.watch` resources by default.\n * Callers opt in via {@link ReposStore.startWatching} / {@link stopWatching}\n * so tests can run without lingering watchers.\n */\n\n/** Mutating event from the store. */\nexport type ReposStoreChange =\n\t| { kind: \"load\"; config: ReposFile }\n\t| { kind: \"add\"; repo: AnyRepoConfig }\n\t| { kind: \"update\"; repo: AnyRepoConfig }\n\t| { kind: \"remove\"; repoId: string }\n\t| { kind: \"default-change\"; defaultRepoId: string }\n\t| { kind: \"error\"; error: Error };\n\n/** Subscriber callback. */\nexport type ReposStoreListener = (change: ReposStoreChange) => void;\n\n/** Dependencies — defaults to the real fs, tests inject a fake. */\nexport interface ReposStoreFileSystem {\n\twatch: typeof fs.watch;\n}\n\nexport interface ReposStoreOptions {\n\t/** Use an explicit loader (defaults to one wrapping the resolved configPath). */\n\tloader?: ReposLoader;\n\t/** File-system watch implementation — defaults to `fs.watch`. */\n\tfileSystem?: ReposStoreFileSystem;\n\t/** Time provider for `addedAt` stamping. */\n\tnow?: () => string;\n\t/**\n\t * Debounce window for `fs.watch` callbacks. Editors sometimes emit\n\t * multiple `change` events for a single logical write — coalesce them.\n\t * Defaults to 50ms.\n\t */\n\tdebounceMs?: number;\n\t/**\n\t * Watch implementation factory — defaults to a no-deps wrapper around\n\t * `fs.watch`. Tests inject a fake watcher here. The store wires the\n\t * returned handle to its own schedule/error flow via the supplied\n\t * callbacks.\n\t */\n\tcreateFsWatcher?: (configPath: string, callbacks: FsWatcherCallbacks) => FsWatcherHandle;\n}\n\n/**\n * Minimal subset of `fs.FSWatcher` that the store depends on. Defining our\n * own type lets tests inject a fake without dragging in real fs handles.\n */\nexport interface FsWatcherHandle {\n\tclose(): void;\n}\n\n/**\n * Callbacks the store wires to a watcher. The default `fs.watch` factory\n * uses these to forward events into `scheduleReload` / `error` emission;\n * tests can ignore them when they only care about lifecycle.\n */\nexport interface FsWatcherCallbacks {\n\tonChange?: () => void;\n\tonRename?: () => void;\n\tonError?: (err: Error) => void;\n}\n\nexport class ReposStore {\n\tprivate readonly loader: ReposLoader;\n\tprivate readonly fileSystem: ReposStoreFileSystem;\n\tprivate readonly now: () => string;\n\tprivate readonly debounceMs: number;\n\tprivate readonly createFsWatcher: (\n\t\tconfigPath: string,\n\t\tcallbacks: FsWatcherCallbacks\n\t) => FsWatcherHandle;\n\n\tprivate config: ReposFile | null = null;\n\tprivate readonly emitter = new EventEmitter();\n\tprivate watcher: FsWatcherHandle | null = null;\n\tprivate reloadTimer: NodeJS.Timeout | null = null;\n\tprivate lastLoadResult: LoadResult | null = null;\n\n\tconstructor(options: ReposStoreOptions = {}) {\n\t\tthis.loader = options.loader ?? new ReposLoader({ configPath: \"\" });\n\t\tthis.fileSystem = options.fileSystem ?? { watch: fs.watch };\n\t\tthis.now = options.now ?? (() => new Date().toISOString());\n\t\tthis.debounceMs = options.debounceMs ?? 50;\n\t\tthis.createFsWatcher =\n\t\t\toptions.createFsWatcher ?? ((p, cb) => this.defaultCreateFsWatcher(p, cb));\n\t}\n\n\t/** Currently held config, or `null` if `load()` has not been called yet. */\n\tgetConfig(): ReposFile | null {\n\t\treturn this.config;\n\t}\n\n\t/** Path of the underlying `repos.json` file (via the loader). */\n\tgetConfigPath(): string {\n\t\treturn this.loader.getConfigPath();\n\t}\n\n\t/** Subscribe to store mutations. Returns an unsubscribe function. */\n\tsubscribe(listener: ReposStoreListener): () => void {\n\t\tthis.emitter.on(\"change\", listener);\n\t\treturn () => {\n\t\t\tthis.emitter.off(\"change\", listener);\n\t\t};\n\t}\n\n\t/**\n\t * Read the config from disk (delegating to the loader) and seed the\n\t * in-memory state. Safe to call repeatedly.\n\t */\n\tasync load(): Promise<LoadResult> {\n\t\tconst result = await this.loader.load();\n\t\tthis.lastLoadResult = result;\n\t\tthis.config = result.config;\n\t\tthis.emit({ kind: \"load\", config: result.config });\n\t\treturn result;\n\t}\n\n\t/** Convenience getter — wraps `load()` and returns the config. */\n\tasync ensureLoaded(): Promise<ReposFile> {\n\t\tconst result = await this.load();\n\t\treturn result.config;\n\t}\n\n\t/** The most recent `LoadResult`, useful for surfacing recovery warnings. */\n\tgetLastLoadResult(): LoadResult | null {\n\t\treturn this.lastLoadResult;\n\t}\n\n\t/**\n\t * Start watching the config file for external changes. Subsequent writes\n\t * (or edits from another process) trigger a debounced re-load.\n\t *\n\t * No-op when already watching.\n\t */\n\tstartWatching(): void {\n\t\tif (this.watcher) {\n\t\t\treturn;\n\t\t}\n\t\tconst configPath = this.loader.getConfigPath();\n\t\tif (!configPath) {\n\t\t\tthrow new Error(\"Cannot watch: loader has no configPath.\");\n\t\t}\n\t\tthis.watcher = this.createFsWatcher(configPath, {\n\t\t\tonChange: () => this.scheduleReload(),\n\t\t\tonRename: () => this.scheduleReload(),\n\t\t\tonError: (err) => this.emit({ kind: \"error\", error: err }),\n\t\t});\n\t}\n\n\t/** Stop watching and release any pending reload timers. */\n\tstopWatching(): void {\n\t\tif (this.reloadTimer) {\n\t\t\tclearTimeout(this.reloadTimer);\n\t\t\tthis.reloadTimer = null;\n\t\t}\n\t\tif (this.watcher) {\n\t\t\tthis.watcher.close();\n\t\t\tthis.watcher = null;\n\t\t}\n\t}\n\n\t// ───────────────────────────── CRUD operations ─────────────────────────────\n\n\tlist(): AnyRepoConfig[] {\n\t\treturn this.config?.repos.slice() ?? [];\n\t}\n\n\tlistDefault(): AnyRepoConfig[] {\n\t\treturn this.list().filter(isDefaultRepo);\n\t}\n\n\tlistUser(): UserRepoConfig[] {\n\t\treturn this.list().filter((r): r is UserRepoConfig => r.source === \"user\");\n\t}\n\n\tget(repoId: string): AnyRepoConfig | undefined {\n\t\treturn this.config?.repos.find((r) => r.id === repoId);\n\t}\n\n\t/**\n\t * Add a user repo. Throws when the id already exists. The `addedAt`\n\t * timestamp defaults to `now()` when not provided. Persists to disk.\n\t */\n\tasync addUserRepo(input: Omit<UserRepoConfig, \"source\">): Promise<UserRepoConfig> {\n\t\tconst enriched: UserRepoConfig = {\n\t\t\t...input,\n\t\t\tsource: \"user\",\n\t\t\taddedAt: input.addedAt ?? this.now(),\n\t\t};\n\t\tawait this.mutate((current) => {\n\t\t\tif (current.repos.some((r) => r.id === enriched.id)) {\n\t\t\t\tthrow new RepoAlreadyExistsError(enriched.id);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...current,\n\t\t\t\trepos: [...current.repos, enriched],\n\t\t\t};\n\t\t});\n\t\tthis.emit({ kind: \"add\", repo: enriched });\n\t\treturn enriched;\n\t}\n\n\t/**\n\t * Update an existing repo by id (partial). Throws when not found.\n\t * Persists to disk.\n\t */\n\tasync updateRepo(repoId: string, patch: Partial<RepoConfig>): Promise<AnyRepoConfig> {\n\t\tconst updated = await this.mutate((current) => {\n\t\t\tconst idx = current.repos.findIndex((r) => r.id === repoId);\n\t\t\tif (idx === -1) {\n\t\t\t\tthrow new RepoNotFoundError(repoId);\n\t\t\t}\n\t\t\tconst previous = current.repos[idx];\n\t\t\tif (!previous) {\n\t\t\t\t// Unreachable: idx === -1 caught above.\n\t\t\t\tthrow new RepoNotFoundError(repoId);\n\t\t\t}\n\t\t\tconst merged: AnyRepoConfig = {\n\t\t\t\t...previous,\n\t\t\t\t...patch,\n\t\t\t\tid: previous.id,\n\t\t\t\tsource: previous.source,\n\t\t\t} as AnyRepoConfig;\n\t\t\tconst nextRepos = current.repos.slice();\n\t\t\tnextRepos[idx] = merged;\n\t\t\treturn { ...current, repos: nextRepos };\n\t\t});\n\t\tconst targetRepo = updated.repos.find((r) => r.id === repoId);\n\t\tif (!targetRepo) {\n\t\t\tthrow new RepoNotFoundError(repoId);\n\t\t}\n\t\tthis.emit({ kind: \"update\", repo: targetRepo });\n\t\treturn targetRepo;\n\t}\n\n\t/**\n\t * Remove a user repo. Throws when the repo is a default or does not exist.\n\t * Persists to disk.\n\t */\n\tasync removeUserRepo(repoId: string): Promise<void> {\n\t\tconst existed = this.get(repoId);\n\t\tif (!existed) {\n\t\t\tthrow new RepoNotFoundError(repoId);\n\t\t}\n\t\tif (existed.source === \"default\") {\n\t\t\tthrow new CannotRemoveDefaultRepoError(repoId);\n\t\t}\n\t\tawait this.mutate((current) => ({\n\t\t\t...current,\n\t\t\trepos: current.repos.filter((r) => r.id !== repoId),\n\t\t}));\n\t\tthis.emit({ kind: \"remove\", repoId });\n\t}\n\n\t/**\n\t * Disable a repo (any source). Useful for the \"Disable\" button on default\n\t * repos. Persists to disk.\n\t */\n\tasync disableRepo(repoId: string): Promise<AnyRepoConfig> {\n\t\treturn this.updateRepo(repoId, { enabled: false });\n\t}\n\n\t/** Inverse of {@link disableRepo}. */\n\tasync enableRepo(repoId: string): Promise<AnyRepoConfig> {\n\t\treturn this.updateRepo(repoId, { enabled: true });\n\t}\n\n\t/** Change which repo the UI surfaces as the default. Persists to disk. */\n\tasync setDefaultRepoId(repoId: string): Promise<void> {\n\t\tconst target = this.get(repoId);\n\t\tif (!target) {\n\t\t\tthrow new RepoNotFoundError(repoId);\n\t\t}\n\t\tawait this.mutate((current) => ({\n\t\t\t...current,\n\t\t\tdefaultRepoId: repoId,\n\t\t}));\n\t\tthis.emit({ kind: \"default-change\", defaultRepoId: repoId });\n\t}\n\n\tgetDefaultRepoId(): string | null {\n\t\treturn this.config?.defaultRepoId ?? null;\n\t}\n\n\t/** Returns the configured `now` provider — exposed for `bootstrapDefaults`. */\n\tgetNow(): () => string {\n\t\treturn this.now;\n\t}\n\n\t/**\n\t * Replace the in-memory config and persist it. Used by helpers like\n\t * `bootstrapDefaults` that build a new config wholesale (rather than\n\t * mutating a single field).\n\t */\n\tasync replaceConfig(next: ReposFile): Promise<ReposFile> {\n\t\tconst validated = validateReposFile(next);\n\t\tawait this.loader.save(validated);\n\t\tthis.config = validated;\n\t\treturn validated;\n\t}\n\n\t// ────────────────────────── Internal helpers ──────────────────────────\n\n\tprivate async mutate(updater: (current: ReposFile) => ReposFile): Promise<ReposFile> {\n\t\tconst current = this.config;\n\t\tif (!current) {\n\t\t\tthrow new Error(\"ReposStore.mutate called before load().\");\n\t\t}\n\t\tconst next = updater(current);\n\t\tconst validated = validateReposFile(next);\n\t\tawait this.loader.save(validated);\n\t\tthis.config = validated;\n\t\treturn validated;\n\t}\n\n\tprivate emit(change: ReposStoreChange): void {\n\t\tthis.emitter.emit(\"change\", change);\n\t}\n\n\t/**\n\t * Default `fs.watch` adapter. Public so tests can wrap a real watcher\n\t * with a debounce trampoline if they need to.\n\t */\n\tprivate defaultCreateFsWatcher(\n\t\tconfigPath: string,\n\t\tcallbacks: FsWatcherCallbacks\n\t): FsWatcherHandle {\n\t\tconst handle = this.fileSystem.watch(configPath, { persistent: false });\n\t\tif (callbacks.onChange) {\n\t\t\thandle.on(\"change\", () => callbacks.onChange?.());\n\t\t}\n\t\tif (callbacks.onRename) {\n\t\t\thandle.on(\"rename\", () => callbacks.onRename?.());\n\t\t}\n\t\tif (callbacks.onError) {\n\t\t\thandle.on(\"error\", (err: Error) => callbacks.onError?.(err));\n\t\t}\n\t\treturn {\n\t\t\tclose: () => handle.close(),\n\t\t};\n\t}\n\n\t// Override createFsWatcher with the debounced default if no factory was supplied.\n\tprivate scheduleReload(): void {\n\t\tif (this.reloadTimer) {\n\t\t\tclearTimeout(this.reloadTimer);\n\t\t}\n\t\tthis.reloadTimer = setTimeout(() => {\n\t\t\tthis.reloadTimer = null;\n\t\t\tthis.load().catch((err: unknown) => {\n\t\t\t\tconst error = err instanceof Error ? err : new Error(String(err));\n\t\t\t\tthis.emit({ kind: \"error\", error });\n\t\t\t});\n\t\t}, this.debounceMs);\n\t}\n}\n\n/** Factory: creates a `ReposStore` bound to a specific config path. */\nexport function createReposStore(\n\tconfigPath: string,\n\toptions: Omit<ReposStoreOptions, \"loader\"> = {}\n): ReposStore {\n\treturn new ReposStore({\n\t\tloader: new ReposLoader({ configPath }),\n\t\t...options,\n\t});\n}\n\n/** Error: tried to add a repo whose id already exists. */\nexport class RepoAlreadyExistsError extends Error {\n\tconstructor(public readonly repoId: string) {\n\t\tsuper(`Repo already exists: ${repoId}`);\n\t\tthis.name = \"RepoAlreadyExistsError\";\n\t}\n}\n\n/** Error: looked up a repo id that isn't present in the current config. */\nexport class RepoNotFoundError extends Error {\n\tconstructor(public readonly repoId: string) {\n\t\tsuper(`Repo not found: ${repoId}`);\n\t\tthis.name = \"RepoNotFoundError\";\n\t}\n}\n\n/** Error: tried to remove a default repo (defaults are disable-only). */\nexport class CannotRemoveDefaultRepoError extends Error {\n\tconstructor(public readonly repoId: string) {\n\t\tsuper(`Cannot remove default repo (use disableRepo instead): ${repoId}`);\n\t\tthis.name = \"CannotRemoveDefaultRepoError\";\n\t}\n}\n\n/**\n * Bootstrap helper — invokes `ensureDefaultsInstalled` on the store to\n * guarantee that all default repos are present and their catalog metadata\n * is up to date with `DEFAULT_REPO_SEEDS`. Returns the resulting config.\n * Lives here (not in `default-repos.ts`) to keep the default-repo module\n * free of store concerns.\n */\nexport async function bootstrapDefaults(store: ReposStore): Promise<ReposFile> {\n\tconst config = store.getConfig() ?? (await store.ensureLoaded());\n\tconst result = ensureDefaultsInstalled(config, store.getNow());\n\t// `ensureDefaultsInstalled` returns the same `config` reference when\n\t// nothing changed (no missing defaults, no stale metadata) — compare by\n\t// reference instead of `installed.length` so metadata-only refreshes\n\t// (e.g. editing `DEFAULT_REPO_SEEDS`) still get persisted.\n\tif (result.config === config) {\n\t\treturn config;\n\t}\n\tawait store.replaceConfig(result.config);\n\treturn store.getConfig() ?? result.config;\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nconst CONFIG_DIR = \".serviceme\";\nconst LOG_FILE = \"scheduler.log\";\nconst MAX_LOG_SIZE = 1024 * 1024; // 1MB\n\nexport class DaemonLogger {\n\tprivate readonly logPath: string;\n\n\tconstructor(workspacePath: string, options: { logPath?: string } = {}) {\n\t\tthis.logPath = options.logPath ?? path.join(workspacePath, CONFIG_DIR, LOG_FILE);\n\t\tconst dir = path.dirname(this.logPath);\n\t\tif (!fs.existsSync(dir)) {\n\t\t\tfs.mkdirSync(dir, { recursive: true });\n\t\t}\n\t}\n\n\tgetLogPath(): string {\n\t\treturn this.logPath;\n\t}\n\n\tlog(level: \"info\" | \"warn\" | \"error\", message: string): void {\n\t\tconst ts = new Date().toISOString();\n\t\tconst line = `[${ts}] [${level.toUpperCase()}] ${message}\\n`;\n\t\tthis.rotateIfNeeded();\n\t\tfs.appendFileSync(this.logPath, line, \"utf-8\");\n\t}\n\n\tprivate rotateIfNeeded(): void {\n\t\ttry {\n\t\t\tconst stats = fs.statSync(this.logPath);\n\t\t\tif (stats.size > MAX_LOG_SIZE) {\n\t\t\t\t// Keep last half of the file\n\t\t\t\tconst content = fs.readFileSync(this.logPath, \"utf-8\");\n\t\t\t\tconst halfIdx = content.indexOf(\"\\n\", Math.floor(content.length / 2));\n\t\t\t\tif (halfIdx > 0) {\n\t\t\t\t\tfs.writeFileSync(this.logPath, content.slice(halfIdx + 1), \"utf-8\");\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// File doesn't exist yet — fine\n\t\t}\n\t}\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nconst CONFIG_DIR = \".serviceme\";\nconst PID_FILE = \"scheduler.pid\";\n\nexport interface PidManagerOptions {\n\t/**\n\t * Override the on-disk PID path. Defaults to\n\t * `<workspacePath>/.serviceme/scheduler.pid` for backward compat.\n\t * V2 callers pass an absolute path (e.g. `getSchedulerPidPath()`)\n\t * to read the global daemon PID file.\n\t */\n\tpidPath?: string;\n}\n\nexport class PidManager {\n\tprivate readonly pidPath: string;\n\n\tconstructor(workspacePath: string, options: PidManagerOptions = {}) {\n\t\tthis.pidPath = options.pidPath ?? path.join(workspacePath, CONFIG_DIR, PID_FILE);\n\t}\n\n\tgetPidPath(): string {\n\t\treturn this.pidPath;\n\t}\n\n\twritePid(pid: number): void {\n\t\tconst dir = path.dirname(this.pidPath);\n\t\tif (!fs.existsSync(dir)) {\n\t\t\tfs.mkdirSync(dir, { recursive: true });\n\t\t}\n\t\tfs.writeFileSync(this.pidPath, String(pid), \"utf-8\");\n\t}\n\n\treadPid(): number | null {\n\t\t// statSync (vs existsSync + readFileSync) so that a path that\n\t\t// points at a directory — or any non-file — is treated as\n\t\t// \"no pid\" instead of crashing with EISDIR. Activation paths\n\t\t// hit this code path on every reload; one stale directory\n\t\t// shell from a half-removed daemon would otherwise throw and\n\t\t// take down extension activation. See ms-devtools-vscode\n\t\t// agent memory \"PidManager EISDIR regression\".\n\t\tlet stat: fs.Stats;\n\t\ttry {\n\t\t\tstat = fs.statSync(this.pidPath);\n\t\t} catch {\n\t\t\t// ENOENT / EACCES / EPERM / etc. — all treated as no pid.\n\t\t\treturn null;\n\t\t}\n\t\tif (!stat.isFile()) return null;\n\t\tconst raw = fs.readFileSync(this.pidPath, \"utf-8\").trim();\n\t\tconst pid = Number.parseInt(raw, 10);\n\t\treturn Number.isNaN(pid) ? null : pid;\n\t}\n\n\tremovePid(): void {\n\t\tif (fs.existsSync(this.pidPath)) {\n\t\t\tfs.unlinkSync(this.pidPath);\n\t\t}\n\t}\n\n\tisProcessRunning(pid: number): boolean {\n\t\ttry {\n\t\t\tprocess.kill(pid, 0);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tgetRunningPid(): number | null {\n\t\tconst pid = this.readPid();\n\t\tif (pid === null) return null;\n\t\tif (this.isProcessRunning(pid)) return pid;\n\t\t// Stale PID file — remove\n\t\tthis.removePid();\n\t\treturn null;\n\t}\n}\n","// SchedulerDaemonV2 — global single-instance scheduler.\n//\n// Differences from the removed v1 per-workspace SchedulerDaemon:\n// - Reads the global ~/.serviceme/scheduled-tasks.json (v2 schema).\n// - One process for the whole machine, guarded by PID file + startup lock.\n// - Per-workspace in-memory mutex: same workspace runs serially, different\n// workspaces run in parallel.\n// - Switches cwd to task.workspace.path on every execute (no implicit\n// \"current workspace\" — daemon is machine-scoped).\n// - Disables tasks whose workspace path is missing.\n//\n// See docs/architecture/skill-agent-v2-repo.md §14.3.b + §14.4.\n\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { ScheduledTask, ScheduledTasksConfig } from \"@serviceme/devtools-protocol\";\nimport { getSchedulerPidPath } from \"../../paths/userHome\";\nimport { getExecutor } from \"../executors\";\nimport { TaskConfigManager, validateTaskPayload } from \"../TaskConfigManager\";\nimport { resolveTaskExecutionPayload } from \"../TaskExecutionEngine\";\nimport { TaskLogManager } from \"../TaskLogManager\";\nimport { DaemonLogger } from \"./DaemonLogger\";\nimport { PidManager } from \"./PidManager\";\n\nconst TICK_INTERVAL = 1000; // Check every second\nconst MIN_SCHEDULE_INTERVAL = 1_000; // 1 second minimum\nconst SCHEDULER_LOG_FILENAME = \"scheduler.log\";\n\nexport interface SchedulerDaemonV2Options {\n\t/**\n\t * Override the executor factory. Production callers should leave this\n\t * undefined; tests inject a fake to drive the execution branch without\n\t * spawning real subprocesses.\n\t */\n\tgetExecutor?: typeof getExecutor;\n\t/** Override the config manager. Tests inject a fake pointing at a tmp dir. */\n\tconfigManager?: TaskConfigManager;\n\t/** Override the log manager. */\n\tlogManager?: TaskLogManager;\n\t/** Override the PID manager. */\n\tpidManager?: PidManager;\n\t/** Override the daemon logger (for tests; the default writes to ~/.serviceme/scheduler.log). */\n\tlogger?: DaemonLogger;\n\t/** Override the startup lock acquisition (tests use this to inject \"lock held\" / \"lock free\"). */\n\ttryAcquireLock?: () => boolean;\n\t/** Override the startup lock release. */\n\treleaseLock?: () => void;\n\t/**\n\t * Override the PID file existence + process liveness check. Production\n\t * reads PID file + `process.kill(pid, 0)`; tests inject a stub.\n\t */\n\tgetRunningPid?: () => number | null;\n}\n\nexport class SchedulerDaemonV2 {\n\tprivate readonly configManager: TaskConfigManager;\n\tprivate readonly logManager: TaskLogManager;\n\tprivate readonly pidManager: PidManager;\n\tprivate readonly logger: DaemonLogger;\n\tprivate readonly getExecutor: typeof getExecutor;\n\tprivate readonly tryAcquireLock: () => boolean;\n\tprivate readonly releaseLock: () => void;\n\tprivate readonly getRunningPid: () => number | null;\n\n\tprivate tickTimer: ReturnType<typeof setInterval> | null = null;\n\tprivate running = false;\n\t// Stable references to our own signal listeners so stop() can detach\n\t// them — otherwise repeated start()/stop() cycles (especially in tests)\n\t// leak listeners and keep the event loop alive past the daemon's lifetime.\n\tprivate readonly sigtermHandler: () => void;\n\tprivate readonly sigintHandler: () => void;\n\tprivate startTime: number = 0;\n\n\t// Per-task state (machines are global, but the daemon only knows about\n\t// tasks currently in the config — `lastRun` keys are task IDs).\n\tprivate lastRun: Map<string, number> = new Map();\n\n\t// Per-workspace mutex queue: same workspacePath runs serially, different\n\t// workspacePaths can run in parallel. Each entry is a promise that\n\t// resolves when the previous + this task has finished.\n\tprivate workspaceLocks: Map<string, Promise<unknown>> = new Map();\n\n\tconstructor(options: SchedulerDaemonV2Options = {}) {\n\t\tthis.configManager = options.configManager ?? new TaskConfigManager();\n\t\tthis.logManager = options.logManager ?? new TaskLogManager();\n\t\tthis.pidManager = options.pidManager ?? new PidManager(\"\", { pidPath: getSchedulerPidPath() });\n\t\t// Daemon log lives at ~/.serviceme/scheduler.log (global, not per-ws).\n\t\t// Computed from the PID file's directory to stay in sync with the\n\t\t// user-home resolution.\n\t\tthis.logger =\n\t\t\toptions.logger ??\n\t\t\tnew DaemonLogger(os.homedir(), {\n\t\t\t\tlogPath: path.join(path.dirname(this.pidManager.getPidPath()), SCHEDULER_LOG_FILENAME),\n\t\t\t});\n\t\tthis.getExecutor = options.getExecutor ?? getExecutor;\n\t\tthis.tryAcquireLock = options.tryAcquireLock ?? (() => true);\n\t\tthis.releaseLock = options.releaseLock ?? (() => {});\n\t\tthis.getRunningPid = options.getRunningPid ?? (() => this.pidManager.getRunningPid());\n\t\t// Bind the signal handlers once so stop() can detach the SAME\n\t\t// function references later.\n\t\tthis.sigtermHandler = () => this.stop();\n\t\tthis.sigintHandler = () => this.stop();\n\t}\n\n\tstart(): void {\n\t\tif (this.running) return;\n\n\t\t// 1. Try to acquire the startup flock. If we can't, another instance\n\t\t// is mid-startup — exit 0 (silent, not an error).\n\t\tif (!this.tryAcquireLock()) {\n\t\t\tthis.logger.log(\"info\", \"Another daemon instance holds the lock; exiting\");\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\t// 2. Check existing PID file. If a live process holds the slot, also\n\t\t// exit 0. (Recoverable if the recorded PID is stale.)\n\t\tconst existingPid = this.getRunningPid();\n\t\tif (existingPid !== null && existingPid !== process.pid) {\n\t\t\tthis.logger.log(\"info\", `Another daemon already running (pid: ${existingPid}); exiting`);\n\t\t\tthis.releaseLock();\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tthis.running = true;\n\t\tthis.startTime = Date.now();\n\t\tthis.pidManager.writePid(process.pid);\n\n\t\tthis.logger.log(\n\t\t\t\"info\",\n\t\t\t`SchedulerDaemonV2 started\n PID: ${process.pid}\n Node: ${process.version}\n OS: ${os.type()} ${os.release()} (${process.arch})\n Config: ${this.configManager.getConfigPath()}\n LogPath: ${this.logger.getLogPath()}\n PidPath: ${this.pidManager.getPidPath()}`\n\t\t);\n\n\t\tthis.tickTimer = setInterval(() => this.tick(), TICK_INTERVAL);\n\t\tprocess.on(\"SIGTERM\", this.sigtermHandler);\n\t\tprocess.on(\"SIGINT\", this.sigintHandler);\n\t}\n\n\tstop(): void {\n\t\tif (!this.running) return;\n\t\tthis.running = false;\n\t\tif (this.tickTimer) {\n\t\t\tclearInterval(this.tickTimer);\n\t\t\tthis.tickTimer = null;\n\t\t}\n\t\tthis.pidManager.removePid();\n\t\tthis.releaseLock();\n\t\t// Detach our own signal handlers so multiple start()/stop() cycles\n\t\t// (especially in test scenarios) don't leak listeners and keep the\n\t\t// event loop alive past the daemon's lifetime.\n\t\tprocess.removeListener(\"SIGTERM\", this.sigtermHandler);\n\t\tprocess.removeListener(\"SIGINT\", this.sigintHandler);\n\t\tthis.logger.log(\"info\", \"SchedulerDaemonV2 stopped\");\n\t\tprocess.exit(0);\n\t}\n\n\tprivate tick(): void {\n\t\tconst config = this.configManager.readConfig();\n\t\tconst now = Date.now();\n\n\t\tfor (const task of config.tasks) {\n\t\t\tif (!task.enabled) continue;\n\n\t\t\t// Workspace-missing: auto-disable + log. Done outside the mutex\n\t\t\t// chain because no execution is going to happen anyway.\n\t\t\tif (!fs.existsSync(task.workspace.path)) {\n\t\t\t\tthis.disableTaskForMissingWorkspace(task, config);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// First-encounter: seed lastRun to the current tick so the task\n\t\t\t// waits a full interval before firing.\n\t\t\tlet lastExec = this.lastRun.get(task.id);\n\t\t\tif (lastExec === undefined) {\n\t\t\t\tthis.lastRun.set(task.id, now);\n\t\t\t\tlastExec = now;\n\t\t\t}\n\t\t\tif (!this.shouldRun(task, lastExec, now)) continue;\n\n\t\t\t// Per-workspace mutex: chain onto the existing promise for that\n\t\t\t// workspace path. Different workspaces run independently.\n\t\t\tconst wsPath = task.workspace.path;\n\t\t\tconst prev = this.workspaceLocks.get(wsPath) ?? Promise.resolve();\n\t\t\tconst next = prev\n\t\t\t\t.catch(() => undefined)\n\t\t\t\t.then(() => this.executeTask(task, now))\n\t\t\t\t.catch((err: unknown) => {\n\t\t\t\t\tthis.logger.log(\"error\", `Task ${task.id} execution failed: ${String(err)}`);\n\t\t\t\t});\n\t\t\tthis.workspaceLocks.set(wsPath, next);\n\t\t}\n\t}\n\n\tprivate disableTaskForMissingWorkspace(task: ScheduledTask, config: ScheduledTasksConfig): void {\n\t\tconst idx = config.tasks.findIndex((t) => t.id === task.id);\n\t\tif (idx === -1) return;\n\t\tconst existing = config.tasks[idx];\n\t\tif (!existing) return;\n\t\tif (existing.enabled === false && existing.lastRunError === \"workspace missing\") return;\n\t\tthis.logger.log(\n\t\t\t\"warn\",\n\t\t\t`Disabling task ${task.name} (${task.id}): workspace ${task.workspace.path} not found`\n\t\t);\n\t\tconfig.tasks[idx] = {\n\t\t\t...existing,\n\t\t\tenabled: false,\n\t\t\tlastRunError: \"workspace missing\",\n\t\t};\n\t\tthis.configManager.editTask(task.id, {\n\t\t\tenabled: false,\n\t\t\tworkspace: config.tasks[idx].workspace,\n\t\t});\n\t}\n\n\tprivate shouldRun(task: ScheduledTask, lastExec: number, now: number): boolean {\n\t\tif (task.scheduleType === \"interval\") {\n\t\t\tconst intervalMs = parseIntervalMs(task.schedule);\n\t\t\tif (intervalMs < MIN_SCHEDULE_INTERVAL) return false;\n\t\t\treturn now - lastExec >= intervalMs;\n\t\t}\n\t\tif (task.scheduleType === \"cron\") {\n\t\t\tif (now - lastExec < 60_000) return false;\n\t\t\treturn matchesCron(task.schedule, new Date(now));\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate async executeTask(task: ScheduledTask, now: number): Promise<void> {\n\t\tthis.lastRun.set(task.id, now);\n\t\tconst startedAt = new Date(now).toISOString();\n\t\tconst startMs = Date.now();\n\t\tconst origCwd = process.cwd();\n\n\t\tthis.logger.log(\"info\", `Executing task: ${task.name} (${task.id}) in ${task.workspace.path}`);\n\n\t\ttry {\n\t\t\tprocess.chdir(task.workspace.path);\n\t\t\tconst executionPayload = resolveTaskExecutionPayload(\n\t\t\t\ttask.taskType,\n\t\t\t\ttask.payload,\n\t\t\t\ttask.workspace.path\n\t\t\t);\n\t\t\tvalidateTaskPayload(task.taskType, executionPayload);\n\t\t\tconst executor = this.getExecutor(task.taskType);\n\t\t\tconst result = await executor.execute(executionPayload);\n\t\t\tconst finishedAt = new Date().toISOString();\n\t\t\tconst durationMs = Date.now() - startMs;\n\n\t\t\tthis.logManager.appendLog({\n\t\t\t\ttaskId: task.id,\n\t\t\t\ttaskName: task.name,\n\t\t\t\tstartedAt,\n\t\t\t\tfinishedAt,\n\t\t\t\tstatus: result.status === \"running\" ? \"failure\" : result.status,\n\t\t\t\toutput: result.output,\n\t\t\t\terror: result.error,\n\t\t\t});\n\n\t\t\t// Update runtime metadata on the task itself.\n\t\t\tthis.configManager.editTask(task.id, {\n\t\t\t\tenabled: task.enabled,\n\t\t\t\tworkspace: task.workspace,\n\t\t\t});\n\n\t\t\tthis.logger.log(\n\t\t\t\t\"info\",\n\t\t\t\t`Task completed: ${task.name} status=${result.status} duration=${durationMs}ms`\n\t\t\t);\n\t\t} catch (err: unknown) {\n\t\t\tconst finishedAt = new Date().toISOString();\n\t\t\tconst durationMs = Date.now() - startMs;\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tthis.logManager.appendLog({\n\t\t\t\ttaskId: task.id,\n\t\t\t\ttaskName: task.name,\n\t\t\t\tstartedAt,\n\t\t\t\tfinishedAt,\n\t\t\t\tstatus: \"failure\",\n\t\t\t\terror: message,\n\t\t\t});\n\t\t\tthis.logger.log(\n\t\t\t\t\"error\",\n\t\t\t\t`Task failed: ${task.name} (${task.id}) duration=${durationMs}ms error=${message}`\n\t\t\t);\n\t\t} finally {\n\t\t\tprocess.chdir(origCwd);\n\t\t}\n\t}\n\n\tgetStatus() {\n\t\tconst config = this.configManager.readConfig();\n\t\treturn {\n\t\t\trunning: this.running,\n\t\t\tpid: process.pid,\n\t\t\tuptimeSeconds: this.running ? Math.floor((Date.now() - this.startTime) / 1000) : null,\n\t\t\ttasksRegistered: config.tasks.length,\n\t\t\ttasksEnabled: config.tasks.filter((t) => t.enabled).length,\n\t\t\tpidFile: this.pidManager.getPidPath(),\n\t\t};\n\t}\n}\n\n// ─── Helpers (duplicated from v1 to keep v1 untouched) ─────────────────────\n\nfunction parseIntervalMs(schedule: string): number {\n\tconst match = /^every\\s+(\\d+)\\s*(s|sec|m|min|h|hr|d|day)s?$/i.exec(schedule);\n\tif (!match) return 0;\n\tconst [, numStr, unit] = match;\n\tconst num = Number.parseInt(numStr ?? \"0\", 10);\n\tswitch (unit?.toLowerCase()) {\n\t\tcase \"s\":\n\t\tcase \"sec\":\n\t\t\treturn num * 1000;\n\t\tcase \"m\":\n\t\tcase \"min\":\n\t\t\treturn num * 60 * 1000;\n\t\tcase \"h\":\n\t\tcase \"hr\":\n\t\t\treturn num * 60 * 60 * 1000;\n\t\tcase \"d\":\n\t\tcase \"day\":\n\t\t\treturn num * 24 * 60 * 60 * 1000;\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n}\n\nfunction matchesCron(expression: string, date: Date): boolean {\n\tconst parts = expression.trim().split(/\\s+/);\n\tif (parts.length < 5) return false;\n\tconst [minPart, hourPart, dayPart, monthPart, weekdayPart] = parts;\n\tif (!minPart || !hourPart || !dayPart || !monthPart || !weekdayPart) return false;\n\treturn (\n\t\tmatchCronField(minPart, date.getMinutes()) &&\n\t\tmatchCronField(hourPart, date.getHours()) &&\n\t\tmatchCronField(dayPart, date.getDate()) &&\n\t\tmatchCronField(monthPart, date.getMonth() + 1) &&\n\t\tmatchCronField(weekdayPart, date.getDay())\n\t);\n}\n\nfunction matchCronField(field: string, value: number): boolean {\n\tif (field === \"*\") return true;\n\tif (field.startsWith(\"*/\")) {\n\t\tconst step = Number.parseInt(field.slice(2), 10);\n\t\treturn step > 0 && value % step === 0;\n\t}\n\tconst values = field.split(\",\");\n\treturn values.some((v) => Number.parseInt(v, 10) === value);\n}\n\n// Export helpers for testing.\nexport { matchesCron, parseIntervalMs };\n","import { spawn } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport type { GithubCopilotCliPayload } from \"@serviceme/devtools-protocol\";\nimport { isCopilotAuthenticated } from \"../../copilot/doctor\";\nimport { resolveConfiguredTimeoutMs } from \"./timeout\";\nimport type {\n\tExecutorResult,\n\tOutputEventCallback,\n\tStreamingExecutorHandle,\n\tStreamingTaskExecutor,\n} from \"./types\";\n\nconst MAX_OUTPUT_BYTES = 2 * 1024 * 1024; // 2 MB\nconst DEFAULT_TIMEOUT_MS = 300_000;\n\nexport interface ResolvedGithubCopilotCliExecution {\n\tcommand: \"serviceme\";\n\targs: string[];\n\tcwd?: string;\n\ttimeoutMs: number | undefined;\n\tdiagnosticArgs: string[];\n\tpromptLen: number;\n}\n\nfunction redactArgs(args: string[]): string[] {\n\tconst redacted: string[] = [];\n\tlet redactNext = false;\n\tfor (const arg of args) {\n\t\tif (redactNext) {\n\t\t\tredacted.push(\"<redacted>\");\n\t\t\tredactNext = false;\n\t\t\tcontinue;\n\t\t}\n\t\tredacted.push(arg);\n\t\tif (arg === \"--prompt\") {\n\t\t\tredactNext = true;\n\t\t}\n\t}\n\treturn redacted;\n}\n\nfunction writeDiagnostic(message: string): void {\n\tconst logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH;\n\tif (logPath) {\n\t\tfs.appendFileSync(logPath, message);\n\t\treturn;\n\t}\n\tprocess.stderr.write(message);\n}\n\nexport function resolveGithubCopilotCliExecution(\n\tpayload: GithubCopilotCliPayload\n): ResolvedGithubCopilotCliExecution {\n\tconst args: string[] = [\"copilot\", \"prompt\", \"--prompt\", payload.prompt];\n\tif (payload.autopilot) {\n\t\targs.push(\"--autopilot\");\n\t}\n\tif (payload.allowTools && payload.allowTools.length > 0) {\n\t\targs.push(\"--allow-tools\", payload.allowTools.join(\",\"));\n\t}\n\tif (payload.model) {\n\t\targs.push(\"--model\", payload.model);\n\t}\n\tif (payload.agent) {\n\t\targs.push(\"--agent\", payload.agent);\n\t}\n\targs.push(\"--timeout\", String(payload.timeout ?? 0));\n\n\treturn {\n\t\tcommand: \"serviceme\",\n\t\targs,\n\t\tcwd: payload.workspace,\n\t\ttimeoutMs: resolveConfiguredTimeoutMs(payload.timeout, DEFAULT_TIMEOUT_MS),\n\t\tdiagnosticArgs: redactArgs(args),\n\t\tpromptLen: payload.prompt.length,\n\t};\n}\n\nexport class GithubCopilotCliExecutor implements StreamingTaskExecutor {\n\tasync execute(payload: unknown, abortSignal?: AbortSignal): Promise<ExecutorResult> {\n\t\t// Pre-flight auth check\n\t\tconst authenticated = await isCopilotAuthenticated();\n\t\tif (!authenticated) {\n\t\t\treturn {\n\t\t\t\tstatus: \"failure\",\n\t\t\t\terror:\n\t\t\t\t\t\"GitHub Copilot CLI authentication failed. Please run `gh auth login` to re-authenticate.\",\n\t\t\t};\n\t\t}\n\n\t\tlet output = \"\";\n\t\tconst handle = this.executeStreaming(\n\t\t\tpayload,\n\t\t\t(_stream, data) => {\n\t\t\t\toutput += data;\n\t\t\t},\n\t\t\tabortSignal\n\t\t);\n\t\tconst result = await handle.result;\n\t\treturn { ...result, output: output || result.output };\n\t}\n\n\texecuteStreaming(\n\t\tpayload: unknown,\n\t\tonOutput: OutputEventCallback,\n\t\tabortSignal?: AbortSignal\n\t): StreamingExecutorHandle {\n\t\tconst p = payload as GithubCopilotCliPayload;\n\t\tconst execution = resolveGithubCopilotCliExecution(p);\n\n\t\tlet resolve: (value: ExecutorResult) => void;\n\t\tconst resultPromise = new Promise<ExecutorResult>((r) => {\n\t\t\tresolve = r;\n\t\t});\n\t\tlet settled = false;\n\n\t\tconst settle = (result: ExecutorResult) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tif (timer) clearTimeout(timer);\n\t\t\tresolve?.(result);\n\t\t};\n\n\t\tif (abortSignal?.aborted) {\n\t\t\treturn {\n\t\t\t\tresult: Promise.resolve({\n\t\t\t\t\tstatus: \"cancelled\" as const,\n\t\t\t\t\terror: \"Execution aborted\",\n\t\t\t\t}),\n\t\t\t\tcancel: () => {},\n\t\t\t};\n\t\t}\n\n\t\twriteDiagnostic(\n\t\t\t`[GithubCopilotCliExecutor] spawn: command=${execution.command}, args=${JSON.stringify(execution.diagnosticArgs)}, promptLen=${execution.promptLen}, cwd=${execution.cwd ?? \"(default)\"}, platform=${process.platform}, windowsHide=true, pid=${process.pid}\\n`\n\t\t);\n\n\t\tconst child = spawn(execution.command, execution.args, {\n\t\t\tcwd: execution.cwd,\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\twindowsHide: true,\n\t\t});\n\n\t\tif (child.pid) {\n\t\t\twriteDiagnostic(`[GithubCopilotCliExecutor] spawned child PID=${child.pid}\\n`);\n\t\t}\n\n\t\tconst timeoutMs = execution.timeoutMs;\n\t\tconst timer =\n\t\t\ttimeoutMs != null\n\t\t\t\t? setTimeout(() => {\n\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tif (!child.killed) child.kill(\"SIGKILL\");\n\t\t\t\t\t\t}, 5000);\n\t\t\t\t\t\tsettle({\n\t\t\t\t\t\t\tstatus: \"timeout\",\n\t\t\t\t\t\t\terror: `Copilot CLI execution timed out after ${timeoutMs / 1000}s`,\n\t\t\t\t\t\t});\n\t\t\t\t\t}, timeoutMs)\n\t\t\t\t: undefined;\n\n\t\tlet stdoutBuf = \"\";\n\t\tlet stderrBuf = \"\";\n\n\t\tchild.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\tconst data = chunk.toString();\n\t\t\tstdoutBuf += data;\n\t\t\tif (stdoutBuf.length > MAX_OUTPUT_BYTES) stdoutBuf = stdoutBuf.slice(-MAX_OUTPUT_BYTES);\n\t\t\tonOutput(\"stdout\", data);\n\t\t});\n\n\t\tchild.stderr.on(\"data\", (chunk: Buffer) => {\n\t\t\tconst data = chunk.toString();\n\t\t\tstderrBuf += data;\n\t\t\tif (stderrBuf.length > MAX_OUTPUT_BYTES) stderrBuf = stderrBuf.slice(-MAX_OUTPUT_BYTES);\n\t\t\tonOutput(\"stderr\", data);\n\t\t});\n\n\t\tchild.on(\"close\", (code) => {\n\t\t\tif (code === 0) {\n\t\t\t\tsettle({ status: \"success\", output: stdoutBuf || undefined });\n\t\t\t} else {\n\t\t\t\tsettle({\n\t\t\t\t\tstatus: \"failure\",\n\t\t\t\t\toutput: stdoutBuf || undefined,\n\t\t\t\t\terror: stderrBuf || `Process exited with code ${code}`,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\tchild.on(\"error\", (err) => {\n\t\t\tsettle({ status: \"failure\", error: err.message });\n\t\t});\n\n\t\tconst cancelFn = () => {\n\t\t\tchild.kill(\"SIGTERM\");\n\t\t\tsetTimeout(() => {\n\t\t\t\tif (!child.killed) child.kill(\"SIGKILL\");\n\t\t\t}, 5000);\n\t\t\tsettle({ status: \"cancelled\", error: \"Execution cancelled\" });\n\t\t};\n\n\t\tabortSignal?.addEventListener(\"abort\", () => cancelFn(), { once: true });\n\n\t\treturn { result: resultPromise, cancel: cancelFn };\n\t}\n}\n","export function resolveConfiguredTimeoutMs(\n\ttimeoutSeconds: number | undefined,\n\tdefaultTimeoutMs: number\n): number | undefined {\n\tif (timeoutSeconds === 0) {\n\t\treturn undefined;\n\t}\n\tif (\n\t\ttypeof timeoutSeconds !== \"number\" ||\n\t\t!Number.isFinite(timeoutSeconds) ||\n\t\ttimeoutSeconds < 0\n\t) {\n\t\treturn defaultTimeoutMs;\n\t}\n\treturn timeoutSeconds * 1000;\n}\n","import type { HttpRequestPayload } from \"@serviceme/devtools-protocol\";\nimport { resolveConfiguredTimeoutMs } from \"./timeout\";\nimport type { ExecutorResult, TaskExecutor } from \"./types\";\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\nexport class HttpRequestExecutor implements TaskExecutor {\n\tasync execute(payload: unknown, abortSignal?: AbortSignal): Promise<ExecutorResult> {\n\t\tconst p = payload as HttpRequestPayload;\n\t\tconst timeoutMs = resolveConfiguredTimeoutMs(p.timeout, DEFAULT_TIMEOUT_MS);\n\n\t\tconst ac = new AbortController();\n\t\tlet timedOut = false;\n\t\tconst timer =\n\t\t\ttimeoutMs != null\n\t\t\t\t? setTimeout(() => {\n\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\tac.abort();\n\t\t\t\t\t}, timeoutMs)\n\t\t\t\t: undefined;\n\n\t\tif (abortSignal?.aborted) {\n\t\t\tif (timer) clearTimeout(timer);\n\t\t\treturn { status: \"cancelled\", error: \"Execution aborted\" };\n\t\t}\n\n\t\tlet externalAbort = false;\n\t\tabortSignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\texternalAbort = true;\n\t\t\t\tif (timer) clearTimeout(timer);\n\t\t\t\tac.abort();\n\t\t\t},\n\t\t\t{ once: true }\n\t\t);\n\n\t\ttry {\n\t\t\tconst response = await fetch(p.url, {\n\t\t\t\tmethod: p.method,\n\t\t\t\theaders: p.headers,\n\t\t\t\tbody: p.body,\n\t\t\t\tsignal: ac.signal,\n\t\t\t});\n\t\t\tif (timer) clearTimeout(timer);\n\n\t\t\tconst body = await response.text();\n\t\t\tif (response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\toutput: `${response.status} ${response.statusText}\\n${body}`.trim(),\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstatus: \"failure\",\n\t\t\t\terror: `HTTP ${response.status} ${response.statusText}\\n${body}`.trim(),\n\t\t\t};\n\t\t} catch (err: unknown) {\n\t\t\tif (timer) clearTimeout(timer);\n\t\t\tif (err instanceof Error && err.name === \"AbortError\") {\n\t\t\t\tif (externalAbort) {\n\t\t\t\t\treturn { status: \"cancelled\", error: \"Execution cancelled\" };\n\t\t\t\t}\n\t\t\t\tif (!timedOut || timeoutMs == null) {\n\t\t\t\t\treturn { status: \"failure\", error: err.message };\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"timeout\",\n\t\t\t\t\terror: `HTTP request timed out after ${timeoutMs / 1000}s`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn { status: \"failure\", error: message };\n\t\t}\n\t}\n}\n","import { type SpawnOptions, spawn } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { ShellPayload } from \"@serviceme/devtools-protocol\";\nimport { resolveConfiguredTimeoutMs } from \"./timeout\";\nimport type {\n\tExecutorResult,\n\tOutputEventCallback,\n\tStreamingExecutorHandle,\n\tStreamingTaskExecutor,\n} from \"./types\";\n\nconst MAX_OUTPUT_BYTES = 1024 * 1024; // 1 MB\nconst DEFAULT_TIMEOUT_MS = 60_000;\nconst POSIX_SHELL_CANDIDATES = [\"bash.exe\", \"sh.exe\"];\n\nexport interface ShellExecutionResolutionOptions {\n\tplatform?: NodeJS.Platform;\n\tenv?: NodeJS.ProcessEnv;\n\tfileExists?: (candidate: string) => boolean;\n}\n\nexport interface ResolvedShellExecution {\n\tcommand: string;\n\targs: string[];\n\tstdinScript?: string;\n\toutputEncoding: BufferEncoding;\n\tshellKind: \"cmd\" | \"posix\";\n}\n\nexport function resolveShellExecution(\n\tscript: string,\n\toptions: ShellExecutionResolutionOptions = {}\n): ResolvedShellExecution {\n\tconst platform = options.platform ?? process.platform;\n\tconst env = options.env ?? process.env;\n\tconst fileExists = options.fileExists ?? fs.existsSync;\n\n\tif (platform === \"win32\") {\n\t\tconst posixShell = usesPosixShellSyntax(script) ? findWindowsPosixShell(env, fileExists) : null;\n\t\tif (posixShell) {\n\t\t\treturn {\n\t\t\t\tcommand: posixShell,\n\t\t\t\targs: [\"-s\"],\n\t\t\t\tstdinScript: script,\n\t\t\t\toutputEncoding: \"utf8\",\n\t\t\t\tshellKind: \"posix\",\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tcommand: env.ComSpec ?? env.COMSPEC ?? \"cmd.exe\",\n\t\t\targs: [\"/d\", \"/s\", \"/c\", script],\n\t\t\toutputEncoding: \"utf8\",\n\t\t\tshellKind: \"cmd\",\n\t\t};\n\t}\n\n\treturn {\n\t\tcommand: \"sh\",\n\t\targs: [\"-s\"],\n\t\tstdinScript: script,\n\t\toutputEncoding: \"utf8\",\n\t\tshellKind: \"posix\",\n\t};\n}\n\nfunction usesPosixShellSyntax(script: string): boolean {\n\treturn /\\$\\([^)]*\\)|`[^`]*`/.test(script);\n}\n\nfunction findWindowsPosixShell(\n\tenv: NodeJS.ProcessEnv,\n\tfileExists: (candidate: string) => boolean\n): string | null {\n\tconst explicitShell = env.SERVICEME_POSIX_SHELL;\n\tif (explicitShell && fileExists(explicitShell)) {\n\t\treturn explicitShell;\n\t}\n\n\tconst knownGitBashPaths = [\n\t\t\"C:\\\\Program Files\\\\Git\\\\bin\\\\bash.exe\",\n\t\t\"C:\\\\Program Files\\\\Git\\\\usr\\\\bin\\\\bash.exe\",\n\t\t\"C:\\\\Program Files (x86)\\\\Git\\\\bin\\\\bash.exe\",\n\t\t\"C:\\\\Program Files (x86)\\\\Git\\\\usr\\\\bin\\\\bash.exe\",\n\t];\n\tfor (const candidate of knownGitBashPaths) {\n\t\tif (fileExists(candidate)) return candidate;\n\t}\n\n\tconst pathValue = env.Path ?? env.PATH ?? \"\";\n\tfor (const dir of pathValue.split(path.win32.delimiter)) {\n\t\tif (!dir) continue;\n\t\tfor (const executable of POSIX_SHELL_CANDIDATES) {\n\t\t\tconst candidate = path.win32.join(dir, executable);\n\t\t\tif (fileExists(candidate) && !isWindowsWslLauncher(candidate)) {\n\t\t\t\treturn candidate;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction isWindowsWslLauncher(candidate: string): boolean {\n\tconst normalized = path.win32.normalize(candidate).toLowerCase();\n\treturn (\n\t\tnormalized.endsWith(\"\\\\windows\\\\system32\\\\bash.exe\") ||\n\t\tnormalized.endsWith(\"\\\\windows\\\\syswow64\\\\bash.exe\")\n\t);\n}\n\nfunction writeDiagnostic(message: string): void {\n\tconst logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH;\n\tif (logPath) {\n\t\tfs.appendFileSync(logPath, message);\n\t\treturn;\n\t}\n\tprocess.stderr.write(message);\n}\n\nexport class ShellExecutor implements StreamingTaskExecutor {\n\tasync execute(payload: unknown, abortSignal?: AbortSignal): Promise<ExecutorResult> {\n\t\tlet output = \"\";\n\t\tconst handle = this.executeStreaming(\n\t\t\tpayload,\n\t\t\t(_stream, data) => {\n\t\t\t\toutput += data;\n\t\t\t},\n\t\t\tabortSignal\n\t\t);\n\t\tconst result = await handle.result;\n\t\treturn { ...result, output: output || result.output };\n\t}\n\n\texecuteStreaming(\n\t\tpayload: unknown,\n\t\tonOutput: OutputEventCallback,\n\t\tabortSignal?: AbortSignal\n\t): StreamingExecutorHandle {\n\t\tconst p = payload as ShellPayload;\n\t\tconst timeoutMs = resolveConfiguredTimeoutMs(p.timeout, DEFAULT_TIMEOUT_MS);\n\n\t\tlet resolve: (value: ExecutorResult) => void;\n\t\tconst resultPromise = new Promise<ExecutorResult>((r) => {\n\t\t\tresolve = r;\n\t\t});\n\t\tlet settled = false;\n\n\t\tconst settle = (result: ExecutorResult) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tif (timer) clearTimeout(timer);\n\t\t\tresolve?.(result);\n\t\t};\n\n\t\tif (abortSignal?.aborted) {\n\t\t\treturn {\n\t\t\t\tresult: Promise.resolve({\n\t\t\t\t\tstatus: \"cancelled\" as const,\n\t\t\t\t\terror: \"Execution aborted\",\n\t\t\t\t}),\n\t\t\t\tcancel: () => {},\n\t\t\t};\n\t\t}\n\n\t\tconst shellExecution = resolveShellExecution(p.script);\n\n\t\tconst spawnOpts: SpawnOptions = {\n\t\t\tcwd: p.cwd,\n\t\t\tstdio: [shellExecution.stdinScript ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n\t\t\twindowsHide: true,\n\t\t};\n\n\t\twriteDiagnostic(\n\t\t\t`[ShellExecutor] spawn:\n platform=${process.platform}\n shell=${shellExecution.shellKind}\n command=${shellExecution.command}\n args=${JSON.stringify(shellExecution.args.filter((a) => a !== p.script))}\n stdin=${shellExecution.stdinScript ? \"true\" : \"false\"}\n cwd=${p.cwd ?? \"(default)\"}\n timeout=${timeoutMs != null ? `${timeoutMs}ms` : \"unlimited\"}\n scriptLen=${p.script.length}\n windowsHide=true\n daemonPid=${process.pid}\\n`\n\t\t);\n\n\t\tconst child = spawn(shellExecution.command, shellExecution.args, spawnOpts);\n\n\t\tif (shellExecution.stdinScript && child.stdin) {\n\t\t\tchild.stdin.end(shellExecution.stdinScript);\n\t\t}\n\n\t\tif (child.pid) {\n\t\t\twriteDiagnostic(`[ShellExecutor] spawned child PID=${child.pid}\\n`);\n\t\t}\n\n\t\tconst timer =\n\t\t\ttimeoutMs != null\n\t\t\t\t? setTimeout(() => {\n\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tif (!child.killed) child.kill(\"SIGKILL\");\n\t\t\t\t\t\t}, 5000);\n\t\t\t\t\t\tsettle({\n\t\t\t\t\t\t\tstatus: \"timeout\",\n\t\t\t\t\t\t\terror: `Shell execution timed out after ${timeoutMs / 1000}s`,\n\t\t\t\t\t\t});\n\t\t\t\t\t}, timeoutMs)\n\t\t\t\t: undefined;\n\n\t\tlet stdoutBuf = \"\";\n\t\tlet stderrBuf = \"\";\n\n\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => {\n\t\t\tconst data = chunk.toString(shellExecution.outputEncoding);\n\t\t\tstdoutBuf += data;\n\t\t\tif (stdoutBuf.length > MAX_OUTPUT_BYTES) stdoutBuf = stdoutBuf.slice(-MAX_OUTPUT_BYTES);\n\t\t\tonOutput(\"stdout\", data);\n\t\t});\n\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\tconst data = chunk.toString(shellExecution.outputEncoding);\n\t\t\tstderrBuf += data;\n\t\t\tif (stderrBuf.length > MAX_OUTPUT_BYTES) stderrBuf = stderrBuf.slice(-MAX_OUTPUT_BYTES);\n\t\t\tonOutput(\"stderr\", data);\n\t\t});\n\n\t\tchild.on(\"close\", (code: number | null) => {\n\t\t\twriteDiagnostic(`[ShellExecutor] child PID=${child.pid} exited: code=${code}\\n`);\n\t\t\tif (code === 0) {\n\t\t\t\tsettle({ status: \"success\", output: stdoutBuf || undefined });\n\t\t\t} else {\n\t\t\t\tsettle({\n\t\t\t\t\tstatus: \"failure\",\n\t\t\t\t\toutput: stdoutBuf || undefined,\n\t\t\t\t\terror: stderrBuf || `Process exited with code ${code}`,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\tchild.on(\"error\", (err: Error) => {\n\t\t\tsettle({ status: \"failure\", error: err.message });\n\t\t});\n\n\t\tconst cancelFn = () => {\n\t\t\tchild.kill(\"SIGTERM\");\n\t\t\tsetTimeout(() => {\n\t\t\t\tif (!child.killed) child.kill(\"SIGKILL\");\n\t\t\t}, 5000);\n\t\t\tsettle({ status: \"cancelled\", error: \"Execution cancelled\" });\n\t\t};\n\n\t\tabortSignal?.addEventListener(\"abort\", () => cancelFn(), { once: true });\n\n\t\treturn { result: resultPromise, cancel: cancelFn };\n\t}\n}\n","import type { TaskExecutionStatus } from \"@serviceme/devtools-protocol\";\n\nexport interface ExecutorResult {\n\tstatus: TaskExecutionStatus;\n\toutput?: string;\n\terror?: string;\n}\n\nexport interface TaskExecutor {\n\texecute(payload: unknown, abortSignal?: AbortSignal): Promise<ExecutorResult>;\n}\n\n// Streaming interfaces for Bridge task execution\n\nexport type OutputEventCallback = (stream: \"stdout\" | \"stderr\", data: string) => void;\n\nexport interface StreamingExecutorHandle {\n\tresult: Promise<ExecutorResult>;\n\tcancel: () => void;\n}\n\nexport interface StreamingTaskExecutor extends TaskExecutor {\n\texecuteStreaming(\n\t\tpayload: unknown,\n\t\tonOutput: OutputEventCallback,\n\t\tabortSignal?: AbortSignal\n\t): StreamingExecutorHandle;\n}\n\nexport function isStreamingTaskExecutor(executor: TaskExecutor): executor is StreamingTaskExecutor {\n\treturn (\n\t\t\"executeStreaming\" in executor &&\n\t\ttypeof (executor as StreamingTaskExecutor).executeStreaming === \"function\"\n\t);\n}\n","import type { ScheduledTaskType } from \"@serviceme/devtools-protocol\";\nimport { GithubCopilotCliExecutor } from \"./GithubCopilotCliExecutor\";\nimport { HttpRequestExecutor } from \"./HttpRequestExecutor\";\nimport { ShellExecutor } from \"./ShellExecutor\";\nimport type { TaskExecutor } from \"./types\";\n\n/** Task types executable by the bridge (excludes \"command\" which runs in-extension) */\ntype BridgeTaskType = Exclude<ScheduledTaskType, \"command\">;\n\nconst executors: Record<BridgeTaskType, TaskExecutor> = {\n\tshell: new ShellExecutor(),\n\thttp_request: new HttpRequestExecutor(),\n\tgithub_copilot_cli: new GithubCopilotCliExecutor(),\n};\n\nexport function getExecutor(taskType: ScheduledTaskType): TaskExecutor {\n\tconst executor = executors[taskType as BridgeTaskType];\n\tif (!executor) {\n\t\tthrow new Error(`Unsupported bridge task type: ${taskType}`);\n\t}\n\treturn executor;\n}\n\nexport type {\n\tExecutorResult,\n\tOutputEventCallback,\n\tStreamingExecutorHandle,\n\tStreamingTaskExecutor,\n\tTaskExecutor,\n} from \"./types\";\nexport { isStreamingTaskExecutor } from \"./types\";\nexport { GithubCopilotCliExecutor, HttpRequestExecutor, ShellExecutor };\n","import { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type {\n\tScheduledTask,\n\tScheduledTasksConfig,\n\tScheduledTasksConfigV1,\n\tScheduledTaskType,\n\tTaskPayload,\n\tTaskWorkspaceRef,\n} from \"@serviceme/devtools-protocol\";\nimport {\n\tcreateServicemeError,\n\tisScheduledTasksConfig,\n\tisScheduledTasksConfigV1,\n\tisScheduledTaskV1,\n\tmigrateV1ToV2,\n} from \"@serviceme/devtools-protocol\";\nimport { getMigrationFailuresPath, getScheduledTasksConfigPath } from \"../paths/userHome\";\n\nfunction emptyConfig(): ScheduledTasksConfig {\n\treturn { version: 2, tasks: [] };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\n/**\n * Detect the \"v2 container with v1-shaped tasks\" hybrid: a config object\n * whose top-level `version` is `2` and whose `tasks` array is non-empty,\n * but where every task fails the v2 validator specifically because of the\n * missing required `workspace` field. The task entries must still pass\n * the v1 validator (otherwise the file is genuinely corrupt, not just\n * stale).\n */\nfunction isV2ContainerWithV1Tasks(value: unknown): value is {\n\tversion: 2;\n\ttasks: unknown[];\n} {\n\tif (!isRecord(value)) return false;\n\tif (value.version !== 2) return false;\n\tif (!Array.isArray(value.tasks) || value.tasks.length === 0) return false;\n\tconst allValidV2 = value.tasks.every((t) => !isScheduledTaskV1(t) || tIsValidV2(t));\n\tif (allValidV2) return false;\n\t// All tasks pass v1 — exactly the v2-container-with-v1-tasks hybrid.\n\treturn value.tasks.every((t) => isScheduledTaskV1(t));\n}\n\nfunction tIsValidV2(t: unknown): boolean {\n\treturn isRecord(t) && isRecord((t as Record<string, unknown>).workspace);\n}\n\n/**\n * Cast a v2-container-with-v1-tasks hybrid back to a v1 `ScheduledTasksConfigV1`\n * so we can feed it through the existing `migrateV1ToV2` helper. No data is\n * transformed — only the type assertion; the tasks inside are already\n * v1-shaped.\n */\nfunction v1ContainerShape(value: { tasks: unknown[] }): ScheduledTasksConfigV1 {\n\treturn { version: 1, tasks: value.tasks.filter(isScheduledTaskV1) };\n}\n\n/**\n * Fallback workspace context used when the manager has to auto-migrate a\n * v1-shaped task and the caller did not supply one. Points at the user's\n * home directory; the user can always re-edit the task in the webview to\n * point at the real workspace afterwards. This is intentionally\n * pessimistic — surfacing the task at all beats the previous behaviour\n * of silently dropping it.\n */\nfunction defaultWorkspaceContext(): TaskWorkspaceRef {\n\tconst home = os.homedir() || \"/\";\n\treturn { path: home, name: path.basename(home) || home };\n}\n\nfunction requireNonEmptyString(\n\tpayload: TaskPayload,\n\tfield: string,\n\ttaskType: ScheduledTaskType\n): void {\n\tif (!isRecord(payload) || typeof payload[field] !== \"string\" || !payload[field].trim()) {\n\t\tthrow createServicemeError(\"invalid_payload\", `${taskType} payload ${field} is required`);\n\t}\n}\n\nexport function validateTaskPayload(taskType: ScheduledTaskType, payload: TaskPayload): void {\n\tswitch (taskType) {\n\t\tcase \"command\":\n\t\t\trequireNonEmptyString(payload, \"command\", taskType);\n\t\t\treturn;\n\t\tcase \"shell\":\n\t\t\trequireNonEmptyString(payload, \"script\", taskType);\n\t\t\treturn;\n\t\tcase \"http_request\":\n\t\t\trequireNonEmptyString(payload, \"url\", taskType);\n\t\t\trequireNonEmptyString(payload, \"method\", taskType);\n\t\t\treturn;\n\t\tcase \"github_copilot_cli\":\n\t\t\trequireNonEmptyString(payload, \"prompt\", taskType);\n\t\t\treturn;\n\t}\n}\n\nexport interface CreateTaskInput {\n\tname: string;\n\tdescription?: string;\n\tscheduleType: \"cron\" | \"interval\";\n\tschedule: string;\n\ttaskType: ScheduledTaskType;\n\tpayload: TaskPayload;\n\tworkspace: TaskWorkspaceRef;\n\tenabled?: boolean;\n}\n\nexport interface EditTaskInput {\n\tname?: string;\n\tdescription?: string;\n\tscheduleType?: \"cron\" | \"interval\";\n\tschedule?: string;\n\ttaskType?: ScheduledTaskType;\n\tpayload?: TaskPayload;\n\tworkspace?: TaskWorkspaceRef;\n\tenabled?: boolean;\n}\n\nexport interface TaskConfigManagerOptions {\n\t/**\n\t * Override the on-disk config path. Defaults to the global\n\t * `~/.serviceme/scheduled-tasks.json`. Tests use this to point at a tmp\n\t * directory; production code should leave it unset.\n\t */\n\tconfigPath?: string;\n\t/**\n\t * Workspace context used when a legacy v1 config is read. v1 tasks had no\n\t * per-task workspace, so the manager needs a fallback to attach when\n\t * auto-migrating. Required only if the underlying file might still be v1.\n\t */\n\tworkspaceContext?: TaskWorkspaceRef;\n\t/**\n\t * Override the path where malformed-config diagnostic entries are appended.\n\t * Defaults to `~/.serviceme/migration-failures.json`. Tests can point at a\n\t * tmp file to assert failures were recorded.\n\t */\n\tmigrationFailuresPath?: string;\n\t/**\n\t * Inject a logger for the manager's recovery / migration warnings. Defaults\n\t * to `console.warn`. Tests pass a `vi.fn`/`mock.fn` to assert the warnings\n\t * were emitted.\n\t */\n\tlogger?: (message: string) => void;\n}\n\nexport class TaskConfigManager {\n\tprivate readonly configPath: string;\n\tprivate readonly workspaceContext?: TaskWorkspaceRef;\n\tprivate readonly migrationFailuresPath?: string;\n\tprivate readonly logger?: (message: string) => void;\n\n\tconstructor(options: TaskConfigManagerOptions = {}) {\n\t\tthis.configPath = options.configPath ?? getScheduledTasksConfigPath();\n\t\tthis.workspaceContext = options.workspaceContext;\n\t\tthis.migrationFailuresPath = options.migrationFailuresPath;\n\t\tthis.logger = options.logger;\n\t}\n\n\tgetConfigPath(): string {\n\t\treturn this.configPath;\n\t}\n\n\treadConfig(): ScheduledTasksConfig {\n\t\tif (!fs.existsSync(this.configPath)) {\n\t\t\treturn emptyConfig();\n\t\t}\n\t\tconst raw = fs.readFileSync(this.configPath, \"utf-8\");\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(raw);\n\t\t} catch (error) {\n\t\t\t// Corrupted file — record failure + warn, then treat as empty.\n\t\t\t// Mirrors the log manager's repair-on-read policy.\n\t\t\tthis.recordMalformedConfigFailure(\"parse_error\", String(error), raw);\n\t\t\treturn emptyConfig();\n\t\t}\n\n\t\t// v2 (current): return as-is.\n\t\tif (isScheduledTasksConfig(parsed)) {\n\t\t\treturn parsed;\n\t\t}\n\n\t\t// Mixed / recoverable: a v2 container holding v1-shaped tasks (no\n\t\t// `workspace` field). Happens when a tool writes the header\n\t\t// `\"version\": 2` but each task is missing the v2-required `workspace`\n\t\t// ref — observed when users hand-edit `~/.serviceme/scheduled-tasks.json`\n\t\t// or when older bugged writers stamped v2 onto v1 task shapes. Treat\n\t\t// as v1 + auto-migrate using either the explicit `workspaceContext`\n\t\t// (preferred) or a sensible default (the user's home dir) so the\n\t\t// tasks stay visible instead of being silently dropped. Repairs the\n\t\t// on-disk file on success.\n\t\tif (isV2ContainerWithV1Tasks(parsed)) {\n\t\t\tconst v1 = v1ContainerShape(parsed);\n\t\t\tconst ctx = this.workspaceContext ?? defaultWorkspaceContext();\n\t\t\tconst { config, issues } = migrateV1ToV2(v1, ctx);\n\t\t\tthis.warn(\n\t\t\t\t`TaskConfigManager: detected v2 container with v1-shaped tasks in ${this.configPath} — auto-migrated in place. ` +\n\t\t\t\t\t`Tasks are now bound to workspace \"${ctx.path}\". Pass TaskConfigManager({ workspaceContext }) to override.`\n\t\t\t);\n\t\t\tfor (const issue of issues) this.warn(`TaskConfigManager migration issue: ${issue}`);\n\t\t\ttry {\n\t\t\t\tthis.writeConfig(config);\n\t\t\t} catch (writeError) {\n\t\t\t\tthis.warn(\n\t\t\t\t\t`TaskConfigManager: failed to rewrite repaired config to ${this.configPath}: ${String(writeError)}`\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn config;\n\t\t}\n\n\t\t// v1 (legacy): auto-migrate in memory. The MigrateToGlobal scanner is\n\t\t// the long-term path forward; this branch is the safety net for any\n\t\t// global config file that was somehow written in v1 format during the\n\t\t// migration window.\n\t\tif (isScheduledTasksConfigV1(parsed)) {\n\t\t\tif (!this.workspaceContext) {\n\t\t\t\tthrow createServicemeError(\n\t\t\t\t\t\"invalid_params\",\n\t\t\t\t\t\"TaskConfigManager: v1 config found but no workspaceContext provided. \" +\n\t\t\t\t\t\t\"Pass TaskConfigManager({ workspaceContext }) when reading a legacy v1 file.\"\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst v1 = parsed as ScheduledTasksConfigV1;\n\t\t\treturn migrateV1ToV2(v1, this.workspaceContext).config;\n\t\t}\n\n\t\t// Unknown shape — record a failure entry so the user can debug, then\n\t\t// treat as empty. Don't return silently: a previous version's CLI\n\t\t// sometimes left an unrecoverable file that ate tasks invisibly.\n\t\tthis.recordMalformedConfigFailure(\"unknown_shape\", \"Config did not match v1 or v2 schema\", raw);\n\t\treturn emptyConfig();\n\t}\n\n\tprivate warn(message: string): void {\n\t\tif (this.logger) this.logger(message);\n\t\telse console.warn(`[TaskConfigManager] ${message}`);\n\t}\n\n\tprivate recordMalformedConfigFailure(reason: string, detail: string, raw: string): void {\n\t\tthis.warn(\n\t\t\t`TaskConfigManager: malformed config at ${this.configPath} (${reason}): ${detail}. ` +\n\t\t\t\t`Tasks in this file are invisible until repaired. A diagnostic entry is written to ${\n\t\t\t\t\tthis.migrationFailuresPath ?? getMigrationFailuresPath()\n\t\t\t\t}.`\n\t\t);\n\t\ttry {\n\t\t\tconst target = this.migrationFailuresPath ?? getMigrationFailuresPath();\n\t\t\tconst prior = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.parse(fs.readFileSync(target, \"utf-8\")) as unknown;\n\t\t\t\t} catch {\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t})();\n\t\t\tconst failures = Array.isArray(prior) ? (prior as unknown[]) : [];\n\t\t\tfailures.push({\n\t\t\t\tpath: this.configPath,\n\t\t\t\treason,\n\t\t\t\tdetail,\n\t\t\t\tsnippet: raw.slice(0, 500),\n\t\t\t\trecordedAt: new Date().toISOString(),\n\t\t\t});\n\t\t\tfs.mkdirSync(path.dirname(target), { recursive: true });\n\t\t\tfs.writeFileSync(target, JSON.stringify(failures, null, \"\\t\"), \"utf-8\");\n\t\t} catch (writeError) {\n\t\t\tthis.warn(\n\t\t\t\t`TaskConfigManager: also failed to write migration-failures log: ${String(writeError)}`\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate writeConfig(config: ScheduledTasksConfig): void {\n\t\tconst dir = path.dirname(this.configPath);\n\t\tif (!fs.existsSync(dir)) {\n\t\t\tfs.mkdirSync(dir, { recursive: true });\n\t\t}\n\t\t// Atomic write: write to temp then rename\n\t\tconst tmp = `${this.configPath}.tmp`;\n\t\tfs.writeFileSync(tmp, JSON.stringify(config, null, \"\\t\"), \"utf-8\");\n\t\tfs.renameSync(tmp, this.configPath);\n\t}\n\n\tlistTasks(): ScheduledTask[] {\n\t\treturn this.readConfig().tasks;\n\t}\n\n\tgetTask(id: string): ScheduledTask | undefined {\n\t\treturn this.readConfig().tasks.find((t) => t.id === id);\n\t}\n\n\tgetTaskByName(name: string): ScheduledTask | undefined {\n\t\treturn this.readConfig().tasks.find((t) => t.name === name);\n\t}\n\n\tcreateTask(input: CreateTaskInput): ScheduledTask {\n\t\tif (!input.workspace?.path || !input.workspace.name) {\n\t\t\tthrow createServicemeError(\n\t\t\t\t\"invalid_params\",\n\t\t\t\t\"createTask: workspace with path and name is required (v2 schema)\"\n\t\t\t);\n\t\t}\n\t\tvalidateTaskPayload(input.taskType, input.payload);\n\t\tconst config = this.readConfig();\n\t\tconst now = new Date().toISOString();\n\t\tconst task: ScheduledTask = {\n\t\t\tid: randomUUID(),\n\t\t\tname: input.name,\n\t\t\tdescription: input.description,\n\t\t\tenabled: input.enabled ?? true,\n\t\t\tscheduleType: input.scheduleType,\n\t\t\tschedule: input.schedule,\n\t\t\ttaskType: input.taskType,\n\t\t\tpayload: input.payload,\n\t\t\tworkspace: input.workspace,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t};\n\t\tconfig.tasks.push(task);\n\t\tthis.writeConfig(config);\n\t\treturn task;\n\t}\n\n\teditTask(id: string, input: EditTaskInput): ScheduledTask {\n\t\tconst config = this.readConfig();\n\t\tconst idx = config.tasks.findIndex((t) => t.id === id);\n\t\tif (idx === -1) {\n\t\t\tthrow new Error(`Task '${id}' not found`);\n\t\t}\n\t\tconst existing = config.tasks[idx];\n\t\tif (!existing) {\n\t\t\tthrow new Error(`Task '${id}' not found`);\n\t\t}\n\t\tconst nextTaskType = input.taskType ?? existing.taskType;\n\t\tconst nextPayload = input.payload ?? existing.payload;\n\t\tconst nextWorkspace = input.workspace ?? existing.workspace;\n\t\tvalidateTaskPayload(nextTaskType, nextPayload);\n\t\tconst updated: ScheduledTask = {\n\t\t\t...existing,\n\t\t\t...(input.name !== undefined && { name: input.name }),\n\t\t\t...(input.description !== undefined && {\n\t\t\t\tdescription: input.description,\n\t\t\t}),\n\t\t\t...(input.scheduleType !== undefined && {\n\t\t\t\tscheduleType: input.scheduleType,\n\t\t\t}),\n\t\t\t...(input.schedule !== undefined && { schedule: input.schedule }),\n\t\t\t...(input.taskType !== undefined && { taskType: input.taskType }),\n\t\t\t...(input.payload !== undefined && { payload: input.payload }),\n\t\t\t...(input.workspace !== undefined && { workspace: nextWorkspace }),\n\t\t\t...(input.enabled !== undefined && { enabled: input.enabled }),\n\t\t\tupdatedAt: new Date().toISOString(),\n\t\t};\n\t\tconfig.tasks[idx] = updated;\n\t\tthis.writeConfig(config);\n\t\treturn updated;\n\t}\n\n\tdeleteTask(id: string): ScheduledTask {\n\t\tconst config = this.readConfig();\n\t\tconst idx = config.tasks.findIndex((t) => t.id === id);\n\t\tif (idx === -1) {\n\t\t\tthrow new Error(`Task '${id}' not found`);\n\t\t}\n\t\tconst removed = config.tasks[idx];\n\t\tif (!removed) {\n\t\t\tthrow new Error(`Task '${id}' not found`);\n\t\t}\n\t\tconfig.tasks.splice(idx, 1);\n\t\tthis.writeConfig(config);\n\t\treturn removed;\n\t}\n\n\ttoggleTask(id: string, enabled: boolean): ScheduledTask {\n\t\treturn this.editTask(id, { enabled });\n\t}\n}\n","import type {\n\tConcurrencyPolicy,\n\tGithubCopilotCliPayload,\n\tRunningTaskInfo,\n\tScheduledTaskType,\n\tShellPayload,\n\tTaskCancelledEventParams,\n\tTaskCompletedEventParams,\n\tTaskExecutionLog,\n\tTaskExecutionSnapshot,\n\tTaskFailedEventParams,\n\tTaskOutputEventParams,\n\tTaskPayload,\n\tTaskStartedEventParams,\n} from \"@serviceme/devtools-protocol\";\nimport { type ExecutorResult, isStreamingTaskExecutor, type TaskExecutor } from \"./executors/types\";\nimport { validateTaskPayload } from \"./TaskConfigManager\";\n\nexport interface TaskEventListener {\n\tonStarted(params: TaskStartedEventParams): void;\n\tonOutput(params: TaskOutputEventParams): void;\n\tonCompleted(params: TaskCompletedEventParams): void;\n\tonFailed(params: TaskFailedEventParams): void;\n\tonCancelled(params: TaskCancelledEventParams): void;\n}\n\ninterface RunningExecution {\n\texecutionId: string;\n\ttaskId: string;\n\tstartedAt: string;\n\tcancel: () => void;\n}\n\nfunction hasNonEmptyString(value: unknown): value is string {\n\treturn typeof value === \"string\" && value.trim().length > 0;\n}\n\nexport function resolveTaskExecutionPayload(\n\ttaskType: ScheduledTaskType,\n\tpayload: TaskPayload,\n\tworkspacePath: string\n): TaskPayload {\n\tif (!hasNonEmptyString(workspacePath)) {\n\t\treturn payload;\n\t}\n\n\tif (taskType === \"shell\") {\n\t\tconst shellPayload = payload as ShellPayload;\n\t\tif (hasNonEmptyString(shellPayload.cwd)) {\n\t\t\treturn shellPayload;\n\t\t}\n\t\treturn { ...shellPayload, cwd: workspacePath };\n\t}\n\n\tif (taskType === \"github_copilot_cli\") {\n\t\tconst copilotPayload = payload as GithubCopilotCliPayload;\n\t\tif (hasNonEmptyString(copilotPayload.workspace)) {\n\t\t\treturn copilotPayload;\n\t\t}\n\t\treturn { ...copilotPayload, workspace: workspacePath };\n\t}\n\n\treturn payload;\n}\n\nexport class TaskExecutionEngine {\n\tprivate readonly running = new Map<string, RunningExecution>();\n\tprivate readonly taskExecutions = new Map<string, Set<string>>();\n\tprivate listener: TaskEventListener | null = null;\n\n\tconstructor(private readonly getExecutor: (taskType: string) => TaskExecutor) {}\n\n\tsetListener(listener: TaskEventListener): void {\n\t\tthis.listener = listener;\n\t}\n\n\tasync execute(snapshot: TaskExecutionSnapshot): Promise<void> {\n\t\tconst policy: ConcurrencyPolicy = snapshot.concurrencyPolicy ?? \"reject\";\n\n\t\t// Enforce concurrency policy\n\t\tif (policy === \"reject\") {\n\t\t\tconst existingIds = this.taskExecutions.get(snapshot.taskId);\n\t\t\tif (existingIds && existingIds.size > 0) {\n\t\t\t\tthrow new Error(`TASK_ALREADY_RUNNING: Task ${snapshot.taskId} is already running`);\n\t\t\t}\n\t\t}\n\n\t\tconst startedAt = new Date().toISOString();\n\n\t\t// Track by taskId → executionId\n\t\tif (!this.taskExecutions.has(snapshot.taskId)) {\n\t\t\tthis.taskExecutions.set(snapshot.taskId, new Set());\n\t\t}\n\t\tthis.taskExecutions.get(snapshot.taskId)?.add(snapshot.executionId);\n\n\t\t// Set up cancellation\n\t\tconst ac = new AbortController();\n\t\tconst runningExec: RunningExecution = {\n\t\t\texecutionId: snapshot.executionId,\n\t\t\ttaskId: snapshot.taskId,\n\t\t\tstartedAt,\n\t\t\tcancel: () => ac.abort(),\n\t\t};\n\t\tthis.running.set(snapshot.executionId, runningExec);\n\n\t\tthis.listener?.onStarted({\n\t\t\texecutionId: snapshot.executionId,\n\t\t\ttaskId: snapshot.taskId,\n\t\t\tstartedAt,\n\t\t});\n\n\t\ttry {\n\t\t\tconst executionPayload = resolveTaskExecutionPayload(\n\t\t\t\tsnapshot.type,\n\t\t\t\tsnapshot.payload,\n\t\t\t\tsnapshot.workspacePath\n\t\t\t);\n\t\t\tvalidateTaskPayload(snapshot.type, executionPayload);\n\t\t\tconst executor = this.getExecutor(snapshot.type);\n\t\t\tlet result: ExecutorResult;\n\t\t\tif (isStreamingTaskExecutor(executor)) {\n\t\t\t\tconst handle = executor.executeStreaming(\n\t\t\t\t\texecutionPayload,\n\t\t\t\t\t(stream, data) => {\n\t\t\t\t\t\tthis.listener?.onOutput({\n\t\t\t\t\t\t\texecutionId: snapshot.executionId,\n\t\t\t\t\t\t\tstream,\n\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t\tac.signal\n\t\t\t\t);\n\t\t\t\trunningExec.cancel = () => {\n\t\t\t\t\thandle.cancel();\n\t\t\t\t\tac.abort();\n\t\t\t\t};\n\t\t\t\tresult = await handle.result;\n\t\t\t} else {\n\t\t\t\tresult = await executor.execute(executionPayload, ac.signal);\n\t\t\t}\n\n\t\t\tconst log: TaskExecutionLog = {\n\t\t\t\tid: snapshot.executionId,\n\t\t\t\ttaskId: snapshot.taskId,\n\t\t\t\ttaskName: snapshot.name,\n\t\t\t\tstartedAt,\n\t\t\t\tfinishedAt: new Date().toISOString(),\n\t\t\t\tstatus: result.status === \"running\" ? \"failure\" : result.status,\n\t\t\t\toutput: result.output,\n\t\t\t\terror: result.error,\n\t\t\t};\n\n\t\t\tswitch (log.status) {\n\t\t\t\tcase \"success\":\n\t\t\t\t\tthis.listener?.onCompleted({\n\t\t\t\t\t\texecutionId: snapshot.executionId,\n\t\t\t\t\t\tlog,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"cancelled\":\n\t\t\t\t\tthis.listener?.onCancelled({\n\t\t\t\t\t\texecutionId: snapshot.executionId,\n\t\t\t\t\t\tlog,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"timeout\":\n\t\t\t\t\tthis.listener?.onFailed({\n\t\t\t\t\t\texecutionId: snapshot.executionId,\n\t\t\t\t\t\tstatus: \"timeout\",\n\t\t\t\t\t\treason: \"timeout\",\n\t\t\t\t\t\tlog,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthis.listener?.onFailed({\n\t\t\t\t\t\texecutionId: snapshot.executionId,\n\t\t\t\t\t\tstatus: \"failure\",\n\t\t\t\t\t\treason: \"error\",\n\t\t\t\t\t\tlog,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconst log: TaskExecutionLog = {\n\t\t\t\tid: snapshot.executionId,\n\t\t\t\ttaskId: snapshot.taskId,\n\t\t\t\ttaskName: snapshot.name,\n\t\t\t\tstartedAt,\n\t\t\t\tfinishedAt: new Date().toISOString(),\n\t\t\t\tstatus: ac.signal.aborted ? \"cancelled\" : \"failure\",\n\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t};\n\n\t\t\tif (ac.signal.aborted) {\n\t\t\t\tthis.listener?.onCancelled({\n\t\t\t\t\texecutionId: snapshot.executionId,\n\t\t\t\t\tlog,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.listener?.onFailed({\n\t\t\t\t\texecutionId: snapshot.executionId,\n\t\t\t\t\tstatus: \"failure\",\n\t\t\t\t\treason: \"error\",\n\t\t\t\t\tlog,\n\t\t\t\t});\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.running.delete(snapshot.executionId);\n\t\t\tconst taskExecIds = this.taskExecutions.get(snapshot.taskId);\n\t\t\tif (taskExecIds) {\n\t\t\t\ttaskExecIds.delete(snapshot.executionId);\n\t\t\t\tif (taskExecIds.size === 0) {\n\t\t\t\t\tthis.taskExecutions.delete(snapshot.taskId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tcancel(executionId: string): boolean {\n\t\tconst exec = this.running.get(executionId);\n\t\tif (!exec) return false;\n\t\texec.cancel();\n\t\treturn true;\n\t}\n\n\tlistRunning(): RunningTaskInfo[] {\n\t\treturn Array.from(this.running.values()).map((e) => ({\n\t\t\texecutionId: e.executionId,\n\t\t\ttaskId: e.taskId,\n\t\t\tstartedAt: e.startedAt,\n\t\t}));\n\t}\n\n\tdispose(): void {\n\t\tfor (const exec of this.running.values()) {\n\t\t\texec.cancel();\n\t\t}\n\t\tthis.running.clear();\n\t\tthis.taskExecutions.clear();\n\t}\n}\n","import { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type {\n\tScheduledTasksLogFile,\n\tTaskExecutionLog,\n\tTaskExecutionStatus,\n} from \"@serviceme/devtools-protocol\";\nimport { getScheduledTasksLogPath } from \"../paths/userHome\";\n\nconst MAX_LOGS = 200;\n\nfunction emptyLogFile(): ScheduledTasksLogFile {\n\treturn { logs: [] };\n}\n\nfunction isValidLogEntry(entry: unknown): entry is TaskExecutionLog {\n\tif (!entry || typeof entry !== \"object\") return false;\n\tconst e = entry as Record<string, unknown>;\n\treturn (\n\t\ttypeof e.id === \"string\" &&\n\t\ttypeof e.taskId === \"string\" &&\n\t\ttypeof e.taskName === \"string\" &&\n\t\ttypeof e.startedAt === \"string\" &&\n\t\ttypeof e.finishedAt === \"string\" &&\n\t\ttypeof e.status === \"string\"\n\t);\n}\n\nfunction validateAndRepairLogFile(raw: unknown): ScheduledTasksLogFile {\n\tif (!raw || typeof raw !== \"object\") {\n\t\treturn emptyLogFile();\n\t}\n\tconst file = raw as Record<string, unknown>;\n\tif (!Array.isArray(file.logs)) {\n\t\treturn emptyLogFile();\n\t}\n\tconst validLogs = file.logs.filter(isValidLogEntry);\n\treturn { logs: validLogs };\n}\n\nexport interface AppendLogInput {\n\ttaskId: string;\n\ttaskName: string;\n\tstartedAt: string;\n\tfinishedAt: string;\n\tstatus: Exclude<TaskExecutionStatus, \"running\">;\n\toutput?: string;\n\terror?: string;\n}\n\nexport interface TaskLogManagerOptions {\n\t/**\n\t * Override the on-disk log path. Defaults to the global\n\t * `~/.serviceme/scheduled-tasks-log.json`. Tests use this to point at a\n\t * tmp directory; production code should leave it unset.\n\t */\n\tlogPath?: string;\n}\n\nexport class TaskLogManager {\n\tprivate readonly logPath: string;\n\n\tconstructor(options: TaskLogManagerOptions = {}) {\n\t\tthis.logPath = options.logPath ?? getScheduledTasksLogPath();\n\t}\n\n\tgetLogPath(): string {\n\t\treturn this.logPath;\n\t}\n\n\tprivate readLogFile(): ScheduledTasksLogFile {\n\t\tif (!fs.existsSync(this.logPath)) {\n\t\t\treturn emptyLogFile();\n\t\t}\n\t\ttry {\n\t\t\tconst raw = fs.readFileSync(this.logPath, \"utf-8\");\n\t\t\tconst parsed = JSON.parse(raw);\n\t\t\tconst file = validateAndRepairLogFile(parsed);\n\t\t\t// If repair removed entries or structure was invalid, rewrite the file\n\t\t\tif (\n\t\t\t\t!parsed ||\n\t\t\t\ttypeof parsed !== \"object\" ||\n\t\t\t\t!Array.isArray(parsed.logs) ||\n\t\t\t\tparsed.logs.length !== file.logs.length\n\t\t\t) {\n\t\t\t\tthis.writeLogFile(file);\n\t\t\t}\n\t\t\treturn file;\n\t\t} catch {\n\t\t\t// JSON parse failed — file is corrupted, back it up and start fresh\n\t\t\tthis.backupCorruptedFile();\n\t\t\tconst fresh = emptyLogFile();\n\t\t\tthis.writeLogFile(fresh);\n\t\t\treturn fresh;\n\t\t}\n\t}\n\n\tprivate backupCorruptedFile(): void {\n\t\ttry {\n\t\t\tif (fs.existsSync(this.logPath)) {\n\t\t\t\tconst backupPath = `${this.logPath}.corrupted.${Date.now()}`;\n\t\t\t\tfs.copyFileSync(this.logPath, backupPath);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Best-effort backup\n\t\t}\n\t}\n\n\tprivate writeLogFile(file: ScheduledTasksLogFile): void {\n\t\tconst dir = path.dirname(this.logPath);\n\t\tif (!fs.existsSync(dir)) {\n\t\t\tfs.mkdirSync(dir, { recursive: true });\n\t\t}\n\t\tconst tmp = `${this.logPath}.tmp`;\n\t\tfs.writeFileSync(tmp, JSON.stringify(file, null, \"\\t\"), \"utf-8\");\n\t\tfs.renameSync(tmp, this.logPath);\n\t}\n\n\tappendLog(input: AppendLogInput): TaskExecutionLog {\n\t\tconst file = this.readLogFile();\n\t\tconst log: TaskExecutionLog = {\n\t\t\tid: randomUUID(),\n\t\t\ttaskId: input.taskId,\n\t\t\ttaskName: input.taskName,\n\t\t\tstartedAt: input.startedAt,\n\t\t\tfinishedAt: input.finishedAt,\n\t\t\tstatus: input.status,\n\t\t\toutput: input.output,\n\t\t\terror: input.error,\n\t\t};\n\t\tfile.logs.push(log);\n\t\t// FIFO trim\n\t\tif (file.logs.length > MAX_LOGS) {\n\t\t\tfile.logs = file.logs.slice(file.logs.length - MAX_LOGS);\n\t\t}\n\t\tthis.writeLogFile(file);\n\t\treturn log;\n\t}\n\n\tgetLogs(options?: { taskId?: string; limit?: number }): {\n\t\tlogs: TaskExecutionLog[];\n\t\ttotal: number;\n\t} {\n\t\tconst file = this.readLogFile();\n\t\tlet logs = file.logs;\n\t\tif (options?.taskId) {\n\t\t\tlogs = logs.filter((l) => l.taskId === options.taskId);\n\t\t}\n\t\tconst total = logs.length;\n\t\t// Return newest first\n\t\tlogs = logs.slice().reverse();\n\t\tif (options?.limit && options.limit > 0) {\n\t\t\tlogs = logs.slice(0, options.limit);\n\t\t}\n\t\treturn { logs, total };\n\t}\n\n\tclearLogs(taskId?: string): number {\n\t\tconst file = this.readLogFile();\n\t\tconst before = file.logs.length;\n\t\tif (taskId) {\n\t\t\tfile.logs = file.logs.filter((l) => l.taskId !== taskId);\n\t\t} else {\n\t\t\tfile.logs = [];\n\t\t}\n\t\tthis.writeLogFile(file);\n\t\treturn before - file.logs.length;\n\t}\n}\n","// MigrateToGlobal — scan known workspaces for legacy per-workspace\n// <workspace>/.serviceme/scheduled-tasks.json, migrate v1 → v2, append\n// to the global ~/.serviceme/scheduled-tasks.json, and DELETE the\n// original file. Failures (parse errors, schema issues) are recorded\n// in ~/.serviceme/migration-failures.json and the original is also\n// deleted — no half-migration state is left on disk.\n// See docs/architecture/skill-agent-v2-repo.md §14.3.a + §14.4 + §14.5.\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type {\n\tScheduledTask,\n\tScheduledTasksConfig,\n\tScheduledTasksConfigV1,\n\tTaskWorkspaceRef,\n} from \"@serviceme/devtools-protocol\";\nimport { isScheduledTasksConfigV1, migrateV1ToV2 } from \"@serviceme/devtools-protocol\";\nimport { getMigrationFailuresPath, getScheduledTasksConfigPath } from \"../../paths/userHome\";\n\nexport interface MigrateToGlobalOptions {\n\t/**\n\t * Workspace paths to scan for legacy v1 files. May include the current\n\t * workspace + any persisted in `~/.serviceme/known-workspaces.json`.\n\t */\n\tworkspacePaths: string[];\n\t/**\n\t * Workspace context to apply to each scanned file. The scanner calls the\n\t * probe for each candidate path to populate path/name/git metadata.\n\t * Tests can inject a stub that returns synthetic data.\n\t */\n\tprobe?: (workspacePath: string) => Promise<TaskWorkspaceRef> | TaskWorkspaceRef;\n\t/**\n\t * Override the global config path. Tests use this; production should\n\t * leave it unset to get the standard ~/.serviceme/scheduled-tasks.json.\n\t */\n\tglobalConfigPath?: string;\n\t/**\n\t * Override the migration-failures path. Same convention.\n\t */\n\tmigrationFailuresPath?: string;\n}\n\nexport interface MigrationResult {\n\t/** Number of v1 files successfully migrated into the global config. */\n\tmigrated: number;\n\t/** Number of v1 files that failed to parse / migrate. */\n\tfailed: number;\n\t/** Task names that had to be disambiguated because they appeared in multiple workspaces. */\n\tconflicts: string[];\n\t/** Soft issues surfaced by migrateV1ToV2 (e.g. unknown taskType). */\n\tissues: string[];\n}\n\ninterface FailureEntry {\n\tworkspacePath: string;\n\tv1Path: string;\n\terror: string;\n\tat: string; // ISO timestamp\n}\n\nconst WORKSPACE_DIR = \".serviceme\";\nconst V1_FILENAME = \"scheduled-tasks.json\";\n\n/**\n * Default workspace probe: returns a minimal TaskWorkspaceRef derived from\n * the path (name = basename). The extension injects the real WorkspaceProbe\n * at startup; tests inject a stub.\n */\nfunction defaultProbe(workspacePath: string): TaskWorkspaceRef {\n\treturn {\n\t\tpath: workspacePath,\n\t\tname: path.basename(workspacePath) || workspacePath,\n\t};\n}\n\nfunction readV1Config(\n\tv1Path: string\n): { ok: true; config: ScheduledTasksConfigV1 } | { ok: false; error: string } {\n\tlet raw: string;\n\ttry {\n\t\traw = fs.readFileSync(v1Path, \"utf-8\");\n\t} catch (err) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: `read failed: ${err instanceof Error ? err.message : String(err)}`,\n\t\t};\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (err) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: `JSON parse failed: ${err instanceof Error ? err.message : String(err)}`,\n\t\t};\n\t}\n\tif (!isScheduledTasksConfigV1(parsed)) {\n\t\treturn { ok: false, error: \"not a valid v1 scheduledTasksConfig\" };\n\t}\n\treturn { ok: true, config: parsed };\n}\n\nfunction safeDelete(filePath: string): void {\n\ttry {\n\t\tfs.unlinkSync(filePath);\n\t} catch {\n\t\t// Best-effort: file may have been already removed or never existed.\n\t}\n}\n\nfunction ensureDir(filePath: string): void {\n\tconst dir = path.dirname(filePath);\n\tif (!fs.existsSync(dir)) {\n\t\tfs.mkdirSync(dir, { recursive: true });\n\t}\n}\n\nfunction readJsonFile<T>(filePath: string): T | null {\n\tif (!fs.existsSync(filePath)) return null;\n\ttry {\n\t\treturn JSON.parse(fs.readFileSync(filePath, \"utf-8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction writeJsonFile(filePath: string, data: unknown): void {\n\tensureDir(filePath);\n\tfs.writeFileSync(filePath, JSON.stringify(data, null, \"\\t\"), \"utf-8\");\n}\n\n/**\n * Disambiguate task names that collide across workspaces by suffixing the\n * workspace name in brackets. Mutates a copy of `task` and returns it.\n */\nfunction disambiguateName(\n\ttask: ScheduledTask,\n\texistingNames: Set<string>,\n\tworkspaceName: string\n): ScheduledTask {\n\tif (!existingNames.has(task.name)) {\n\t\texistingNames.add(task.name);\n\t\treturn task;\n\t}\n\tconst base = `${task.name} [${workspaceName}]`;\n\tlet candidate = base;\n\tlet counter = 2;\n\twhile (existingNames.has(candidate)) {\n\t\tcandidate = `${base} (${counter})`;\n\t\tcounter += 1;\n\t}\n\texistingNames.add(candidate);\n\treturn { ...task, name: candidate };\n}\n\n/**\n * Run the migration. Idempotent: each v1 file is deleted after processing,\n * so re-running this on a partially-migrated state is safe (the\n * already-migrated files no longer exist; only the remainder is processed).\n */\nexport async function migrateToGlobal(options: MigrateToGlobalOptions): Promise<MigrationResult> {\n\tconst globalConfigPath = options.globalConfigPath ?? getScheduledTasksConfigPath();\n\tconst migrationFailuresPath = options.migrationFailuresPath ?? getMigrationFailuresPath();\n\tconst probe = options.probe ?? defaultProbe;\n\n\t// Load or seed the global config.\n\tconst existing = readJsonFile<ScheduledTasksConfig>(globalConfigPath);\n\tconst baseConfig: ScheduledTasksConfig =\n\t\texisting && existing.version === 2 ? existing : { version: 2, tasks: [] };\n\tconst existingNames = new Set(baseConfig.tasks.map((t) => t.name));\n\n\t// Read prior failures (so we accumulate, not overwrite).\n\tconst priorFailures = readJsonFile<FailureEntry[]>(migrationFailuresPath) ?? [];\n\tconst failures: FailureEntry[] = [...priorFailures];\n\n\tlet migrated = 0;\n\tconst conflicts: string[] = [];\n\tconst issues: string[] = [];\n\n\tfor (const workspacePath of options.workspacePaths) {\n\t\tconst v1Path = path.join(workspacePath, WORKSPACE_DIR, V1_FILENAME);\n\t\tif (!fs.existsSync(v1Path)) continue;\n\n\t\tconst v1 = readV1Config(v1Path);\n\t\tif (!v1.ok) {\n\t\t\tfailures.push({\n\t\t\t\tworkspacePath,\n\t\t\t\tv1Path,\n\t\t\t\terror: v1.error,\n\t\t\t\tat: new Date().toISOString(),\n\t\t\t});\n\t\t\t// DELETE the original — no half-migration state.\n\t\t\tsafeDelete(v1Path);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet workspaceRef: TaskWorkspaceRef;\n\t\ttry {\n\t\t\tworkspaceRef = await probe(workspacePath);\n\t\t} catch (err) {\n\t\t\tfailures.push({\n\t\t\t\tworkspacePath,\n\t\t\t\tv1Path,\n\t\t\t\terror: `probe failed: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\tat: new Date().toISOString(),\n\t\t\t});\n\t\t\tsafeDelete(v1Path);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst { config, issues: taskIssues } = migrateV1ToV2(v1.config, workspaceRef);\n\t\tissues.push(...taskIssues);\n\n\t\t// Disambiguate name conflicts.\n\t\tconst newTasks: ScheduledTask[] = config.tasks.map((t) => {\n\t\t\tif (existingNames.has(t.name)) {\n\t\t\t\tconflicts.push(t.name);\n\t\t\t\treturn disambiguateName(t, existingNames, workspaceRef.name);\n\t\t\t}\n\t\t\texistingNames.add(t.name);\n\t\t\treturn t;\n\t\t});\n\n\t\tbaseConfig.tasks.push(...newTasks);\n\t\tmigrated += 1;\n\n\t\t// DELETE the original — no half-migration state.\n\t\tsafeDelete(v1Path);\n\t}\n\n\t// Persist the new global config + failures.\n\tif (migrated > 0) {\n\t\t// Use TaskConfigManager's atomic write semantics via the public\n\t\t// listTasks + createTask path would lose ordering and re-validate\n\t\t// everything, which is wasteful. Write directly using the same\n\t\t// tmp → rename pattern the manager uses.\n\t\tensureDir(globalConfigPath);\n\t\tconst tmp = `${globalConfigPath}.tmp`;\n\t\tfs.writeFileSync(tmp, JSON.stringify(baseConfig, null, \"\\t\"), \"utf-8\");\n\t\tfs.renameSync(tmp, globalConfigPath);\n\t}\n\tif (failures.length > priorFailures.length) {\n\t\twriteJsonFile(migrationFailuresPath, failures);\n\t} else if (failures.length === 0 && priorFailures.length > 0) {\n\t\t// All prior failures cleared; remove the file.\n\t\tsafeDelete(migrationFailuresPath);\n\t}\n\n\treturn {\n\t\tmigrated,\n\t\tfailed: failures.length - priorFailures.length,\n\t\tconflicts,\n\t\tissues,\n\t};\n}\n","// WorkspaceProbe — auto-detects git metadata for a workspace path.\n// Used by extension startup and task creation to populate TaskWorkspaceRef.\n// See docs/architecture/skill-agent-v2-repo.md §14.3.c (workspace auto-probe).\n\nimport { spawn } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { TaskWorkspaceRef } from \"@serviceme/devtools-protocol\";\n\n/** Result of probing a single workspace. */\nexport interface ProbeResult {\n\t/** Always populated; empty fields are left undefined. */\n\tworkspace: TaskWorkspaceRef;\n\t/** null on success; one of the {@link ProbeError} tags on failure. */\n\terror: ProbeError | null;\n}\n\nexport type ProbeError = \"path-not-found\" | \"not-a-git-repo\" | \"git-timeout\" | \"git-error\";\n\nexport interface ProbeOptions {\n\t/** Timeout for each git invocation in ms. Default: 2000. */\n\ttimeoutMs?: number;\n\t/** Override the git binary (default: 'git'). Tests inject a stub. */\n\tgitBinary?: string;\n\t/**\n\t * Override the underlying runner. Receives the args + cwd and returns\n\t * { stdout, stderr, code } on success, or throws on timeout. The default\n\t * implementation spawns `gitBinary` with the given args.\n\t */\n\trunGit?: (\n\t\targs: string[],\n\t\tcwd: string\n\t) => Promise<{\n\t\tstdout: string;\n\t\tstderr: string;\n\t\tcode: number;\n\t}>;\n}\n\nconst DEFAULT_TIMEOUT_MS = 2000;\n\nclass GitTimeoutError extends Error {\n\tconstructor() {\n\t\tsuper(\"git-timeout\");\n\t\tthis.name = \"GitTimeoutError\";\n\t}\n}\n\nfunction isTimeout(err: unknown): boolean {\n\tif (err instanceof GitTimeoutError) return true;\n\tif (err instanceof Error) {\n\t\treturn err.name === \"GitTimeoutError\" || err.message === \"git-timeout\";\n\t}\n\treturn false;\n}\n\nfunction defaultRunGit(\n\tgitBinary: string,\n\targs: string[],\n\tcwd: string,\n\ttimeoutMs: number\n): Promise<{ stdout: string; stderr: string; code: number }> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst child = spawn(gitBinary, args, {\n\t\t\tcwd,\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t});\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\n\t\tconst timer = setTimeout(() => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tchild.kill(\"SIGTERM\");\n\t\t\treject(new GitTimeoutError());\n\t\t}, timeoutMs);\n\n\t\tchild.stdout.on(\"data\", (chunk) => {\n\t\t\tstdout += chunk.toString(\"utf-8\");\n\t\t});\n\t\tchild.stderr.on(\"data\", (chunk) => {\n\t\t\tstderr += chunk.toString(\"utf-8\");\n\t\t});\n\t\tchild.on(\"error\", (err) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\treject(err);\n\t\t});\n\t\tchild.on(\"close\", (code) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\tresolve({ stdout, stderr, code: code ?? 0 });\n\t\t});\n\t});\n}\n\n/**\n * Probes a workspace for git metadata. Returns a {@link ProbeResult} with\n * `error: null` on success and an error tag on failure — the workspace\n * descriptor is always populated so callers can still surface a partial\n * record.\n */\nexport class WorkspaceProbe {\n\tprivate readonly timeoutMs: number;\n\tprivate readonly gitBinary: string;\n\tprivate readonly runGitFn: (\n\t\targs: string[],\n\t\tcwd: string\n\t) => Promise<{ stdout: string; stderr: string; code: number }>;\n\n\tconstructor(options: ProbeOptions = {}) {\n\t\tthis.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\t\tthis.gitBinary = options.gitBinary ?? \"git\";\n\t\tif (options.runGit) {\n\t\t\tthis.runGitFn = options.runGit;\n\t\t} else {\n\t\t\tconst binary = this.gitBinary;\n\t\t\tconst timeoutMs = this.timeoutMs;\n\t\t\tthis.runGitFn = (args, cwd) => defaultRunGit(binary, args, cwd, timeoutMs);\n\t\t}\n\t}\n\n\tasync probe(workspacePath: string): Promise<ProbeResult> {\n\t\tconst name = path.basename(workspacePath) || workspacePath;\n\n\t\tif (!workspacePath || !fs.existsSync(workspacePath)) {\n\t\t\treturn {\n\t\t\t\tworkspace: { path: workspacePath, name },\n\t\t\t\terror: \"path-not-found\",\n\t\t\t};\n\t\t}\n\n\t\tlet gitRemote: string | undefined;\n\t\ttry {\n\t\t\tconst remote = await this.runGitFn([\"remote\", \"get-url\", \"origin\"], workspacePath);\n\t\t\tif (remote.code === 0 && remote.stdout.trim()) {\n\t\t\t\tgitRemote = remote.stdout.trim();\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (isTimeout(err)) {\n\t\t\t\treturn {\n\t\t\t\t\tworkspace: { path: workspacePath, name, gitRemote },\n\t\t\t\t\terror: \"git-timeout\",\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tworkspace: { path: workspacePath, name },\n\t\t\t\terror: \"not-a-git-repo\",\n\t\t\t};\n\t\t}\n\n\t\tlet gitBranch: string | undefined;\n\t\ttry {\n\t\t\tconst branch = await this.runGitFn([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], workspacePath);\n\t\t\tif (branch.code === 0 && branch.stdout.trim()) {\n\t\t\t\tgitBranch = branch.stdout.trim();\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (isTimeout(err)) {\n\t\t\t\treturn {\n\t\t\t\t\tworkspace: { path: workspacePath, name, gitRemote, gitBranch },\n\t\t\t\t\terror: \"git-timeout\",\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tworkspace: { path: workspacePath, name, gitRemote },\n\t\t\t\terror: \"not-a-git-repo\",\n\t\t\t};\n\t\t}\n\n\t\t// If neither command produced output, treat as not-a-git-repo.\n\t\tif (!gitRemote && !gitBranch) {\n\t\t\treturn {\n\t\t\t\tworkspace: { path: workspacePath, name },\n\t\t\t\terror: \"not-a-git-repo\",\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tworkspace: {\n\t\t\t\tpath: workspacePath,\n\t\t\t\tname,\n\t\t\t\tgitRemote,\n\t\t\t\tgitBranch,\n\t\t\t\tlastSeenAt: new Date().toISOString(),\n\t\t\t},\n\t\t\terror: null,\n\t\t};\n\t}\n}\n","import type { SkillMarketplaceEntry } from \"@serviceme/devtools-protocol\";\nimport { createServicemeError } from \"@serviceme/devtools-protocol\";\nimport type { SkillDownloadFile } from \"./types\";\n\nexport interface SkillCatalog {\n\tskills: SkillMarketplaceEntry[];\n\tfetchedAt: string;\n}\n\nexport interface SkillCatalogClientOptions {\n\tfetchImpl?: typeof fetch;\n\tbaseUrl?: string;\n}\n\nexport class SkillCatalogClient {\n\tprivate readonly fetchImpl: typeof fetch;\n\tprivate readonly baseUrl?: string;\n\n\tconstructor(options: SkillCatalogClientOptions = {}) {\n\t\tthis.fetchImpl = options.fetchImpl ?? fetch;\n\t\tthis.baseUrl = options.baseUrl;\n\t}\n\n\tasync getCatalog(): Promise<SkillCatalog> {\n\t\tif (!this.baseUrl) {\n\t\t\tthrow createServicemeError(\"workspace_not_found\", \"Skill catalog baseUrl is not configured.\");\n\t\t}\n\n\t\tconst response = await this.fetchImpl(`${this.baseUrl}/api/v1/marketplace/skills`);\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Failed to fetch skills catalog: ${response.status}`);\n\t\t}\n\n\t\tconst data = (await response.json()) as {\n\t\t\tskills?: SkillMarketplaceEntry[];\n\t\t};\n\t\treturn {\n\t\t\tskills: data.skills ?? [],\n\t\t\tfetchedAt: new Date().toISOString(),\n\t\t};\n\t}\n\n\tasync downloadSkill(remoteId: string): Promise<SkillDownloadFile[]> {\n\t\tif (!this.baseUrl) {\n\t\t\tthrow createServicemeError(\"workspace_not_found\", \"Skill catalog baseUrl is not configured.\");\n\t\t}\n\n\t\tconst response = await this.fetchImpl(\n\t\t\t`${this.baseUrl}/api/v1/marketplace/skills/download/${remoteId}`\n\t\t);\n\t\tif (!response.ok) {\n\t\t\tif (response.status === 404) {\n\t\t\t\tthrow createServicemeError(\"not_found\", `Skill '${remoteId}' not found`);\n\t\t\t}\n\t\t\tthrow new Error(`Failed to download skill ${remoteId}: ${response.status}`);\n\t\t}\n\n\t\tconst payload = (await response.json()) as {\n\t\t\tdata?: { files?: SkillDownloadFile[] };\n\t\t};\n\t\treturn payload.data?.files ?? [];\n\t}\n}\n","import type { SkillMutationRequest } from \"@serviceme/devtools-protocol\";\n\ninterface CatalogSkillLike {\n\tid: string;\n\thasScripts?: boolean;\n\thasHooks?: boolean;\n}\n\ninterface CatalogLike {\n\tskills: CatalogSkillLike[];\n\tfetchedAt: string;\n}\n\ninterface SkillStoreLike {\n\tnormalizeRemoteSkillId(remoteId: string): string;\n\tlistWorkspaceSkillIds(): Promise<string[]>;\n\tlistUserSkillIds(): Promise<string[]>;\n}\n\ninterface SkillCatalogClientLike {\n\tgetCatalog(): Promise<CatalogLike>;\n}\n\nexport interface SkillReconcilerDependencies {\n\tskillStore: SkillStoreLike;\n\tcatalogClient: SkillCatalogClientLike;\n}\n\nexport interface SkillMutateResult {\n\tstatus: \"success\" | \"blocked\" | \"requires_confirmation\";\n\tchanged: boolean;\n\tmessage?: string;\n\thasScripts?: boolean;\n\thasHooks?: boolean;\n}\n\nexport class SkillReconciler {\n\tconstructor(private readonly deps: SkillReconcilerDependencies) {}\n\n\tasync mutate(request: SkillMutationRequest): Promise<SkillMutateResult> {\n\t\tif (request.targetScope !== \"workspace\" && request.targetScope !== \"user\") {\n\t\t\tthrow new Error(`Invalid target scope: ${String(request.targetScope)}`);\n\t\t}\n\n\t\tif (\n\t\t\trequest.action === \"uninstall\" ||\n\t\t\trequest.action === \"move\" ||\n\t\t\trequest.action === \"removeExternal\"\n\t\t) {\n\t\t\treturn {\n\t\t\t\tstatus: \"success\",\n\t\t\t\tchanged: true,\n\t\t\t\tmessage: `Skill ${request.action} completed.`,\n\t\t\t};\n\t\t}\n\n\t\tif (request.action !== \"install\") {\n\t\t\treturn {\n\t\t\t\tstatus: \"blocked\",\n\t\t\t\tchanged: false,\n\t\t\t\tmessage: `Skill action is not supported by bridge reconciler: ${request.action}`,\n\t\t\t};\n\t\t}\n\n\t\tconst catalog = await this.deps.catalogClient.getCatalog();\n\t\tconst remoteSkill = catalog.skills.find(\n\t\t\t(skill) => this.deps.skillStore.normalizeRemoteSkillId(skill.id) === request.skillId\n\t\t);\n\n\t\tif (!remoteSkill) {\n\t\t\treturn {\n\t\t\t\tstatus: \"blocked\",\n\t\t\t\tchanged: false,\n\t\t\t\tmessage: \"Skill not found in catalog.\",\n\t\t\t};\n\t\t}\n\n\t\tif (!request.confirmed && (remoteSkill.hasScripts || remoteSkill.hasHooks)) {\n\t\t\treturn {\n\t\t\t\tstatus: \"requires_confirmation\",\n\t\t\t\tchanged: false,\n\t\t\t\thasScripts: Boolean(remoteSkill.hasScripts),\n\t\t\t\thasHooks: Boolean(remoteSkill.hasHooks),\n\t\t\t\tmessage: \"This skill contains executable scripts that require confirmation.\",\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tchanged: true,\n\t\t\tmessage: \"Skill installed.\",\n\t\t};\n\t}\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type { SkillDownloadFile, SkillStoreFileSystem, SkillStoreOptions } from \"./types\";\n\nconst USER_SKILL_MARKER_FILE = \".serviceme-skill.json\";\nconst LEGACY_USER_SKILL_MARKER_FILE = \".ms-devtools-skill.json\";\nconst WORKSPACE_SKILLS_ROOT_RELATIVE = \".github/skills\";\nconst WORKSPACE_SKILLS_MARKER_RELATIVE = \".github/.serviceme-skills.yml\";\nconst SAFE_LOCAL_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;\n\nfunction assertSafeLocalSkillId(skillId: string): string {\n\tif (\n\t\ttypeof skillId !== \"string\" ||\n\t\tskillId.length === 0 ||\n\t\tskillId === \".\" ||\n\t\tskillId === \"..\" ||\n\t\tskillId.includes(\"/\") ||\n\t\tskillId.includes(\"\\\\\") ||\n\t\t!SAFE_LOCAL_ID_PATTERN.test(skillId)\n\t) {\n\t\tthrow new Error(`Invalid skill id: ${skillId}`);\n\t}\n\n\treturn skillId;\n}\n\nexport class SkillStore {\n\tprivate readonly workspacePath: string;\n\tprivate readonly userSkillsRoot: string;\n\tprivate readonly fileSystem: SkillStoreFileSystem;\n\n\tconstructor(options: SkillStoreOptions) {\n\t\tthis.workspacePath = options.workspacePath;\n\t\tthis.userSkillsRoot = options.userSkillsRoot;\n\t\tthis.fileSystem = options.fileSystem ?? fs;\n\t}\n\n\tnormalizeRemoteSkillId(remoteId: string): string {\n\t\tif (remoteId.startsWith(\"official/\")) {\n\t\t\treturn assertSafeLocalSkillId(remoteId.slice(\"official/\".length));\n\t\t}\n\t\tif (remoteId.startsWith(\"community/\")) {\n\t\t\tconst lastSlash = remoteId.lastIndexOf(\"/\");\n\t\t\treturn assertSafeLocalSkillId(remoteId.slice(lastSlash + 1));\n\t\t}\n\t\treturn assertSafeLocalSkillId(remoteId);\n\t}\n\n\tgetWorkspaceSkillPath(skillId: string): string {\n\t\treturn `${WORKSPACE_SKILLS_ROOT_RELATIVE}/${skillId}`;\n\t}\n\n\tgetWorkspaceMarkerPath(): string {\n\t\treturn WORKSPACE_SKILLS_MARKER_RELATIVE;\n\t}\n\n\tgetUserSkillPath(skillId: string): string {\n\t\treturn path.join(this.userSkillsRoot, skillId);\n\t}\n\n\tasync listWorkspaceSkillIds(): Promise<string[]> {\n\t\tconst skillsRootPath = path.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE);\n\t\ttry {\n\t\t\tconst entries = await this.fileSystem.readdir(skillsRootPath, {\n\t\t\t\twithFileTypes: true,\n\t\t\t});\n\t\t\treturn entries\n\t\t\t\t.filter((entry) => entry.isDirectory() && !entry.name.startsWith(\".\"))\n\t\t\t\t.map((entry) => entry.name)\n\t\t\t\t.sort();\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tasync listUserSkillIds(): Promise<string[]> {\n\t\ttry {\n\t\t\tconst entries = await this.fileSystem.readdir(this.userSkillsRoot, {\n\t\t\t\twithFileTypes: true,\n\t\t\t});\n\t\t\treturn entries\n\t\t\t\t.filter((entry) => entry.isDirectory() && !entry.name.startsWith(\".\"))\n\t\t\t\t.map((entry) => entry.name)\n\t\t\t\t.sort();\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tasync writeManagedUserSkillMarker(skillId: string): Promise<void> {\n\t\tconst targetDir = this.getUserSkillPath(skillId);\n\t\tawait this.fileSystem.mkdir(targetDir, { recursive: true });\n\t\tawait this.fileSystem.writeFile(\n\t\t\tpath.join(targetDir, USER_SKILL_MARKER_FILE),\n\t\t\tJSON.stringify({ skillId, installedBy: \"serviceme\" }, null, 2),\n\t\t\t\"utf-8\"\n\t\t);\n\t}\n\n\tasync isManagedUserSkill(skillId: string): Promise<boolean> {\n\t\tawait this.migrateLegacyUserSkillMarker(skillId);\n\t\ttry {\n\t\t\tconst marker = await this.fileSystem.readFile(\n\t\t\t\tpath.join(this.getUserSkillPath(skillId), USER_SKILL_MARKER_FILE),\n\t\t\t\t\"utf-8\"\n\t\t\t);\n\t\t\tconst parsed = JSON.parse(marker) as { skillId?: string };\n\t\t\treturn parsed.skillId === skillId;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * One-time migration: the user-scope skill marker used to be named\n\t * `.ms-devtools-skill.json`. If the new `.serviceme-skill.json` doesn't\n\t * exist yet but the legacy marker does, copy it forward so an existing\n\t * skill doesn't lose its \"managed\" status.\n\t */\n\tprivate async migrateLegacyUserSkillMarker(skillId: string): Promise<void> {\n\t\tconst targetDir = this.getUserSkillPath(skillId);\n\t\tconst newPath = path.join(targetDir, USER_SKILL_MARKER_FILE);\n\t\tconst legacyPath = path.join(targetDir, LEGACY_USER_SKILL_MARKER_FILE);\n\t\ttry {\n\t\t\tawait this.fileSystem.readFile(newPath, \"utf-8\");\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// new marker missing — check the legacy marker below\n\t\t}\n\t\ttry {\n\t\t\tconst legacyContent = await this.fileSystem.readFile(legacyPath, \"utf-8\");\n\t\t\tawait this.fileSystem.writeFile(newPath, legacyContent, \"utf-8\");\n\t\t} catch {\n\t\t\t// legacy marker doesn't exist either — nothing to migrate\n\t\t}\n\t}\n\n\tasync writeSkillFiles(\n\t\tskillId: string,\n\t\tscope: \"workspace\" | \"user\",\n\t\tfiles: SkillDownloadFile[]\n\t): Promise<void> {\n\t\tconst root =\n\t\t\tscope === \"workspace\"\n\t\t\t\t? path.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE)\n\t\t\t\t: this.userSkillsRoot;\n\t\tconst targetDir = path.join(root, skillId);\n\t\tawait this.fileSystem.mkdir(targetDir, { recursive: true });\n\n\t\tfor (const file of files) {\n\t\t\tconst filePath = path.join(targetDir, file.path);\n\t\t\tawait this.fileSystem.mkdir(path.dirname(filePath), { recursive: true });\n\t\t\tawait this.fileSystem.writeFile(filePath, file.content, \"utf-8\");\n\t\t\tif (file.executable) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.fileSystem.chmod(filePath, 0o755);\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore chmod failures on unsupported environments\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type { GitClient } from \"../git-client\";\nimport { getRepoDir } from \"../paths/userHome\";\nimport type { SkillFile } from \"../skill-store/types\";\nimport type { SubmitValidationRequest, SubmitValidationResponse } from \"./types\";\nimport { SubmitError } from \"./types\";\n\n// Re-export so consumers (bridge handlers, CLI commands) can detect\n// SubmitError via `instanceof` without reaching into the internal\n// `./types` module.\nexport { SubmitError } from \"./types\";\n\n/**\n * Skill & Agent v2 — SubmitClient (M4)\n *\n * SubmitClient orchestrates the \"validate then push\" flow described\n * in docs/architecture/skill-agent-v2-repo.md §5.7:\n *\n * 1. **validate** — POST the candidate files to the server's\n * `/api/v1/skills/validate` endpoint (5 deny reasons: repo_not_\n * writable, file_too_large, path_traversal, invalid_frontmatter,\n * name_conflict).\n * 2. **write** — materialize the files into the local repo clone at\n * `~/.serviceme/repos/<repoId>/skills/<name>/` (or `agents/`).\n * 3. **commit** — `git add . && git commit -m \"feat(skills): add <name>\"`.\n * 4. **push** — `git push origin <branch>` via the server proxy.\n *\n * The client is intentionally thin: it does NOT do its own validation\n * (the server is the gate), and it does NOT cache anything across\n * calls. The single source of truth for \"is this submission allowed?\"\n * is the server's validate endpoint.\n *\n * @see docs/architecture/skill-agent-v2-repo.md §5.7 SubmitClient\n */\n\nexport interface SubmitOptions {\n\t/** Override the server's validate URL. Defaults to `http://localhost:3000/api/v1`. */\n\tserverBaseUrl?: string;\n\t/** Override the branch to push. Defaults to the repo's `branch` field. */\n\tbranch?: string;\n\t/** Skip the actual push (for tests + dry-runs). When true, step 4 returns a synthetic PushResult. */\n\tskipPush?: boolean;\n}\n\nexport interface SubmitResult {\n\trepoId: string;\n\tskillName: string;\n\tcommitSha: string;\n\tpushedRef?: string;\n\tpushedSha?: string;\n}\n\nexport interface SubmitClientOptions {\n\tgitClient: GitClient;\n\t/** Lookup the default branch for a repo (config-driven). */\n\tgetRepoBranch?: (repoId: string) => string | undefined;\n\t/** HTTP fetch impl (defaults to the global `fetch`). */\n\tfetcher?: typeof fetch;\n\t/** Default server base URL when no override is supplied. */\n\tdefaultServerBaseUrl?: string;\n}\n\nexport class SubmitClient {\n\tprivate readonly git: GitClient;\n\tprivate readonly getRepoBranch: (repoId: string) => string | undefined;\n\tprivate readonly fetcher: typeof fetch;\n\tprivate readonly defaultServerBaseUrl: string;\n\n\tconstructor(opts: SubmitClientOptions) {\n\t\tthis.git = opts.gitClient;\n\t\tthis.getRepoBranch = opts.getRepoBranch ?? (() => undefined);\n\t\tthis.fetcher = opts.fetcher ?? (globalThis.fetch as typeof fetch);\n\t\tthis.defaultServerBaseUrl = opts.defaultServerBaseUrl ?? \"http://localhost:3000\";\n\t}\n\n\t/**\n\t * Validate-only path. Useful for the UI's \"Save Draft\" flow which\n\t * wants to surface validation errors without committing or pushing.\n\t */\n\tasync validate(req: SubmitValidationRequest): Promise<SubmitValidationResponse> {\n\t\tconst url = `${this.defaultServerBaseUrl}/api/v1/skills/validate`;\n\t\tconst res = await this.fetcher(url, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\tbody: JSON.stringify(req),\n\t\t});\n\t\tif (!res.ok) {\n\t\t\tthrow new SubmitError(\"network_error\", `validate request failed: HTTP ${res.status}`);\n\t\t}\n\t\treturn (await res.json()) as SubmitValidationResponse;\n\t}\n\n\t/**\n\t * Full submit pipeline. Throws `SubmitError` on:\n\t * - validate deny (reason echoed)\n\t * - network failure (network_error)\n\t * - local write failure\n\t * - commit/push failure\n\t */\n\tasync submit(\n\t\trepoId: string,\n\t\tskillName: string,\n\t\tfiles: SkillFile[],\n\t\topts: SubmitOptions = {}\n\t): Promise<SubmitResult> {\n\t\t// (1) Validate\n\t\tconst v = await this.validate({ repoId, skillName, files });\n\t\tif (!v.allow) {\n\t\t\tthrow new SubmitError(v.reason ?? \"unknown\", v.detail ?? \"validation denied\");\n\t\t}\n\n\t\t// (2) Write files into the local repo clone\n\t\tconst localRepoPath = getRepoDir(repoId);\n\t\tconst targetDir = path.join(localRepoPath, \"skills\", skillName);\n\t\tawait fs.mkdir(targetDir, { recursive: true });\n\t\tfor (const f of files) {\n\t\t\tconst full = path.join(targetDir, f.path);\n\t\t\tawait fs.mkdir(path.dirname(full), { recursive: true });\n\t\t\tconst tmp = `${full}.${process.pid}.${Date.now()}.tmp`;\n\t\t\tawait fs.writeFile(tmp, f.content, \"utf8\");\n\t\t\tawait fs.rename(tmp, full);\n\t\t}\n\n\t\t// (3) Commit (convention: `feat(skills): add <name>`)\n\t\tconst commitMessage = `feat(skills): add ${skillName}`;\n\t\tconst { commitSha } = await this.git.commit(localRepoPath, commitMessage);\n\n\t\t// (4) Push\n\t\tlet pushedRef: string | undefined;\n\t\tlet pushedSha: string | undefined;\n\t\tif (!opts.skipPush) {\n\t\t\tconst branch = opts.branch ?? this.getRepoBranch(repoId) ?? \"main\";\n\t\t\tconst pushResult = await this.git.push(repoId, localRepoPath, branch);\n\t\t\tpushedRef = pushResult.ref;\n\t\t\tpushedSha = pushResult.commitSha;\n\t\t}\n\n\t\treturn {\n\t\t\trepoId,\n\t\t\tskillName,\n\t\t\tcommitSha,\n\t\t\tpushedRef,\n\t\t\tpushedSha,\n\t\t};\n\t}\n}\n","/**\n * Skill & Agent v2 — SubmitClient Types (M4)\n *\n * Mirrors the server's POST /api/v1/skills/validate contract (M2\n * SubmitApi). Defined here as a separate types file so the test\n * fixtures + the client can both import without circular deps.\n */\n\n/** Reasons the server may deny a submission. */\nexport type DenyReason =\n\t| \"repo_not_writable\"\n\t| \"file_too_large\"\n\t| \"path_traversal\"\n\t| \"invalid_frontmatter\"\n\t| \"name_conflict\"\n\t/** Local-client-side errors that don't come from the server. */\n\t| \"network_error\"\n\t| \"write_error\"\n\t| \"commit_error\"\n\t| \"push_error\"\n\t| \"unknown\";\n\n/** Request payload. Identical to the server's SubmitValidationRequest. */\nexport interface SubmitValidationRequest {\n\trepoId: string;\n\tskillName: string;\n\tfiles: Array<{ path: string; content: string }>;\n}\n\n/** Response payload. Identical to the server's SubmitValidationResponse. */\nexport interface SubmitValidationResponse {\n\tallow: boolean;\n\treason?: DenyReason;\n\tdetail?: string;\n}\n\n/** Sentinel error thrown by SubmitClient.submit() / validate(). */\nexport class SubmitError extends Error {\n\treadonly reason: DenyReason;\n\treadonly detail: string;\n\treadonly status?: number;\n\n\tconstructor(reason: DenyReason, detail: string, options?: { status?: number }) {\n\t\tsuper(`submit failed (${reason}): ${detail}`);\n\t\tthis.name = \"SubmitError\";\n\t\tthis.reason = reason;\n\t\tthis.detail = detail;\n\t\tthis.status = options?.status;\n\t}\n}\n","/**\n * toolbox sort + dedup — pure helpers, no I/O.\n *\n * The Extension's `ToolBoxService` keeps a stable `order` index and\n * surfaces \"recently used\" via the `lastUsedAt` ISO timestamp. These\n * helpers codify that algorithm so the CLI and Extension agree on the\n * \"最近使用置顶\" UX.\n *\n * Refs:\n * - 4.功能规划.md §2.3 — `sort.ts 排序与去重纯函数`\n */\n\nimport type { ExternalTool } from \"@serviceme/devtools-protocol\";\n\n/**\n * Parsed timestamp from `lastUsedAt`. Returns 0 when the field is\n * missing or unparseable (so the entry falls below entries with a\n * real timestamp).\n */\nfunction lastUsedTimestamp(tool: ExternalTool): number {\n\tif (!tool.lastUsedAt) return 0;\n\tconst ms = Date.parse(tool.lastUsedAt);\n\treturn Number.isFinite(ms) ? ms : 0;\n}\n\n/**\n * Sort by \"recently used\" — entries with newer `lastUsedAt` float to\n * the top, entries with no `lastUsedAt` sort to the bottom (preserving\n * their relative `order` index when both are absent).\n *\n * Ties (same `lastUsedAt` or both missing) are broken by `order`, then\n * by `id` for determinism.\n */\nexport function sortByRecentFirst(tools: readonly ExternalTool[]): ExternalTool[] {\n\treturn [...tools].sort((a, b) => {\n\t\tconst tsA = lastUsedTimestamp(a);\n\t\tconst tsB = lastUsedTimestamp(b);\n\t\tif (tsA !== tsB) return tsB - tsA;\n\t\tconst orderA = a.order ?? Number.MAX_SAFE_INTEGER;\n\t\tconst orderB = b.order ?? Number.MAX_SAFE_INTEGER;\n\t\tif (orderA !== orderB) return orderA - orderB;\n\t\treturn a.id.localeCompare(b.id);\n\t});\n}\n\n/**\n * Sort by user-defined `order` field. Entries without `order` are\n * appended in their input order (stable sort via `id` tiebreaker).\n */\nexport function sortByUserOrder(tools: readonly ExternalTool[]): ExternalTool[] {\n\treturn [...tools].sort((a, b) => {\n\t\tconst orderA = a.order ?? Number.MAX_SAFE_INTEGER;\n\t\tconst orderB = b.order ?? Number.MAX_SAFE_INTEGER;\n\t\tif (orderA !== orderB) return orderA - orderB;\n\t\treturn a.id.localeCompare(b.id);\n\t});\n}\n\n/**\n * Deduplicate by `id`. The first occurrence wins; later ones are\n * discarded. Used when merging built-in + user seeds so duplicates\n * from the on-disk file don't shadow the built-in defaults.\n */\nexport function dedupeById(tools: readonly ExternalTool[]): ExternalTool[] {\n\tconst seen = new Set<string>();\n\tconst out: ExternalTool[] = [];\n\tfor (const tool of tools) {\n\t\tif (seen.has(tool.id)) continue;\n\t\tseen.add(tool.id);\n\t\tout.push(tool);\n\t}\n\treturn out;\n}\n\n/**\n * Merge built-in defaults with user-stored tools. Defaults keep\n * `isDefault: true` and their `order`; user tools are appended with\n * their stored metadata. Output is sorted by user order.\n */\nexport function mergeWithDefaults(\n\tdefaults: readonly ExternalTool[],\n\tuserTools: readonly ExternalTool[]\n): ExternalTool[] {\n\tconst defaultIds = new Set(defaults.map((d) => d.id));\n\tconst uniqueUserTools = userTools.filter((t) => !defaultIds.has(t.id));\n\treturn sortByUserOrder([...defaults, ...uniqueUserTools]);\n}\n\n/**\n * Re-number `order` for a list of tool ids. Used by\n * `toolbox.update` when the user drags-and-drops entries in the UI.\n */\nexport function reindexOrder(\n\ttools: ExternalTool[],\n\tnewOrderIds: readonly string[]\n): ExternalTool[] {\n\tconst orderMap = new Map<string, number>();\n\tfor (const [index, id] of newOrderIds.entries()) {\n\t\torderMap.set(id, index);\n\t}\n\tfor (const tool of tools) {\n\t\tconst next = orderMap.get(tool.id);\n\t\tif (next !== undefined) tool.order = next;\n\t}\n\treturn tools;\n}\n\n/**\n * Stamp `lastUsedAt` on the targeted tool (clones the array). Returns\n * a new array; the input is left untouched.\n */\nexport function touchLastUsedAt(\n\ttools: readonly ExternalTool[],\n\tid: string,\n\twhen: Date = new Date()\n): ExternalTool[] {\n\treturn tools.map((tool) => (tool.id === id ? { ...tool, lastUsedAt: when.toISOString() } : tool));\n}\n","/**\n * ToolboxStore — JSON persistence for the toolbox entries.\n *\n * Two scopes:\n * - `user` → `~/.serviceme/toolbox.json`\n * - `workspace` → `<cwd>/.github/.serviceme-toolbox.json`\n *\n * Both files share the `PersistedToolbox` shape and the same atomic\n * write pattern (tmp + rename + fsync) as `IdentityStore`. Concurrent\n * writers are serialized via a mkdir-based file lock (Phase 6+ may\n * upgrade to `proper-lockfile`).\n *\n * Refs:\n * - 4.功能规划.md §2.3 — `ToolboxStore.ts JSON 持久化(user + workspace scope)`\n * - `3.功能拆分.md` §3 — toolbox wire shape\n */\n\nimport * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\n\nimport type { ExternalTool, ToolboxScope } from \"@serviceme/devtools-protocol\";\n\nimport { getToolboxJsonPath } from \"../paths/userHome\";\n\nimport { mergeWithDefaults, sortByUserOrder } from \"./sort\";\nimport {\n\tBUILTIN_DEFAULT_TOOLS,\n\ttype DefaultToolSeed,\n\ttype PersistedToolbox,\n\ttype ResolvedToolbox,\n\tTOOLBOX_JSON_SCHEMA_VERSION,\n} from \"./types\";\n\nconst FILE_MODE = 0o600;\nconst LOCK_DIR_MODE = 0o700;\nconst DEFAULT_LOCK_TIMEOUT_MS = 5000;\nconst DEFAULT_LOCK_RETRY_MS = 25;\n// `mkdir` (lock acquisition) and writing the pid file are two separate\n// syscalls, so there's a brief window where the lock dir exists but the\n// pid file doesn't yet. A grace period keeps a concurrent acquirer from\n// mistaking that window for an abandoned lock (see `isStaleLock`).\nconst LOCK_STALE_GRACE_MS = 200;\nconst TMP_SUFFIX = \".tmp\";\nexport const WORKSPACE_TOOLBOX_RELATIVE_PATH = path.join(\".github\", \".serviceme-toolbox.json\");\nconst LEGACY_WORKSPACE_TOOLBOX_FILENAME = \".ms-devtools-toolbox.json\";\n\n/**\n * One-time migration: the workspace-scope toolbox file used to be named\n * `.ms-devtools-toolbox.json`. If the new `.serviceme-toolbox.json` doesn't\n * exist yet but the legacy file does (in the same directory), rename it\n * forward so existing toolbox entries aren't silently lost.\n */\nasync function migrateLegacyWorkspaceToolboxFile(filePath: string | null): Promise<void> {\n\tif (!filePath) return;\n\tconst legacyPath = path.join(path.dirname(filePath), LEGACY_WORKSPACE_TOOLBOX_FILENAME);\n\tif (legacyPath === filePath) return;\n\ttry {\n\t\tawait fsp.access(filePath);\n\t\treturn;\n\t} catch {\n\t\t// new file missing — check the legacy path below\n\t}\n\ttry {\n\t\tawait fsp.rename(legacyPath, filePath);\n\t} catch {\n\t\t// legacy file doesn't exist either — nothing to migrate\n\t}\n}\n\nexport interface ToolboxFileBackend {\n\tread(filePath: string): Promise<PersistedToolbox | null>;\n\twrite(filePath: string, payload: PersistedToolbox): Promise<void>;\n\texists(filePath: string): Promise<boolean>;\n\t/** Maximum number of `.corrupted.*.bak` snapshots to keep per toolbox file (default: 5). */\n\tmaxBackupCount?: number;\n}\n\nexport interface ToolboxStoreOptions {\n\t/** Override the user-scope file path (default: `getToolboxJsonPath()`). */\n\tuserFilePath?: string;\n\t/** Override the workspace-scope file path resolver (default: cwd-relative). */\n\tresolveWorkspacePath?: () => string | null;\n\t/** Injectable built-in tool seeds (default: `BUILTIN_DEFAULT_TOOLS`). */\n\tdefaultTools?: readonly DefaultToolSeed[];\n\thooks?: ToolboxStoreHooks;\n\tbackend?: ToolboxFileBackend;\n\tlockTimeoutMs?: number;\n\tlockRetryMs?: number;\n}\n\nexport interface ToolboxStoreHooks {\n\tbeforeWrite?: (scope: ToolboxScope, payload: PersistedToolbox) => void | Promise<void>;\n\tafterWrite?: (scope: ToolboxScope, payload: PersistedToolbox) => void | Promise<void>;\n}\n\nexport class FsToolboxFileBackend implements ToolboxFileBackend {\n\tmaxBackupCount: number = 5;\n\n\tasync exists(filePath: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait fsp.access(filePath);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync read(filePath: string): Promise<PersistedToolbox | null> {\n\t\tlet buf: string;\n\t\ttry {\n\t\t\tbuf = await fsp.readFile(filePath, \"utf8\");\n\t\t} catch (err) {\n\t\t\tif (isNodeError(err) && err.code === \"ENOENT\") return null;\n\t\t\tthrow err;\n\t\t}\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(buf) as unknown;\n\t\t\treturn coercePersistedToolbox(parsed);\n\t\t} catch {\n\t\t\t// File exists but is unparseable JSON or fails schema validation\n\t\t\t// (e.g. hand-edited, written by an older/incompatible schema, or\n\t\t\t// truncated by a crash). Quarantine it as a timestamped `.bak`\n\t\t\t// sibling and fall back to an empty toolbox (defaults still\n\t\t\t// merge in via `ToolboxStore.read()`) rather than crashing the\n\t\t\t// whole \"get external tools\" request.\n\t\t\tawait this.backupCorruptedFile(filePath);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate async backupCorruptedFile(filePath: string): Promise<void> {\n\t\ttry {\n\t\t\tconst backupPath = `${filePath}.corrupted.${Date.now()}.bak`;\n\t\t\tawait fsp.copyFile(filePath, backupPath);\n\t\t\tawait this.purgeExcessBackups(filePath);\n\t\t} catch {\n\t\t\t// Best-effort backup — never let backup failure mask the real recovery.\n\t\t}\n\t}\n\n\tprivate async purgeExcessBackups(filePath: string): Promise<void> {\n\t\tconst dir = path.dirname(filePath);\n\t\tconst base = path.basename(filePath);\n\t\tlet entries: string[];\n\t\ttry {\n\t\t\tentries = await fsp.readdir(dir);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tconst backups = entries\n\t\t\t.filter((n) => n.startsWith(base) && n.endsWith(\".bak\"))\n\t\t\t.map((n) => ({ name: n, filePath: path.join(dir, n) }))\n\t\t\t.sort((a, b) => {\n\t\t\t\t// Sort oldest-first so we drop the earliest ones first.\n\t\t\t\treturn a.name.localeCompare(b.name);\n\t\t\t});\n\t\tconst excess = backups.length - this.maxBackupCount;\n\t\tif (excess <= 0) return;\n\t\tawait Promise.all(\n\t\t\tbackups.slice(0, excess).map((b) => fsp.rm(b.filePath).catch(() => undefined))\n\t\t);\n\t}\n\n\tasync write(filePath: string, payload: PersistedToolbox): Promise<void> {\n\t\tawait fsp.mkdir(path.dirname(filePath), { recursive: true });\n\t\tconst tmpPath = `${filePath}${TMP_SUFFIX}`;\n\t\tconst bytes = Buffer.from(JSON.stringify(payload, null, \"\\t\"), \"utf8\");\n\t\tawait fsp.rm(tmpPath, { force: true });\n\t\tconst handle = await fsp.open(tmpPath, \"w\", FILE_MODE);\n\t\ttry {\n\t\t\tawait handle.writeFile(bytes);\n\t\t\tawait handle.sync();\n\t\t} finally {\n\t\t\tawait handle.close();\n\t\t}\n\t\tawait fsp.rename(tmpPath, filePath);\n\t\tawait fsp.chmod(filePath, FILE_MODE).catch(() => undefined);\n\t}\n}\n\nfunction coercePersistedToolbox(parsed: unknown): PersistedToolbox {\n\tif (typeof parsed !== \"object\" || parsed === null) {\n\t\tthrow new Error(\"toolbox.json: top-level must be an object\");\n\t}\n\tconst obj = parsed as Record<string, unknown>;\n\tconst version = obj.version;\n\tif (version !== TOOLBOX_JSON_SCHEMA_VERSION) {\n\t\tthrow new Error(`toolbox.json: unsupported schema version ${String(version)}`);\n\t}\n\tif (!Array.isArray(obj.tools)) {\n\t\tthrow new Error(\"toolbox.json: 'tools' must be an array\");\n\t}\n\treturn { version, tools: obj.tools as ExternalTool[] };\n}\n\nfunction isNodeError(value: unknown): value is NodeJS.ErrnoException {\n\treturn value instanceof Error && typeof (value as { code?: unknown }).code === \"string\";\n}\n\nfunction isProcessAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nclass ToolboxFileLock {\n\tprivate readonly dirPath: string;\n\tprivate readonly pidFilePath: string;\n\tprivate readonly timeoutMs: number;\n\tprivate readonly retryMs: number;\n\tprivate acquired = false;\n\n\tconstructor(filePath: string, timeoutMs: number, retryMs: number) {\n\t\tthis.dirPath = `${filePath}.lock`;\n\t\tthis.pidFilePath = path.join(this.dirPath, \"pid\");\n\t\tthis.timeoutMs = timeoutMs;\n\t\tthis.retryMs = retryMs;\n\t}\n\n\tasync acquire(): Promise<void> {\n\t\tconst start = Date.now();\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\tawait fsp.mkdir(this.dirPath, { mode: LOCK_DIR_MODE });\n\t\t\t\tawait fsp.writeFile(this.pidFilePath, String(process.pid), \"utf8\").catch(() => undefined);\n\t\t\t\tthis.acquired = true;\n\t\t\t\treturn;\n\t\t\t} catch (err) {\n\t\t\t\tif (!isNodeError(err) || err.code !== \"EEXIST\") throw err;\n\t\t\t\tconst stale = await this.isStaleLock();\n\t\t\t\tif (stale) {\n\t\t\t\t\tawait fsp.rm(this.dirPath, { recursive: true, force: true });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Date.now() - start >= this.timeoutMs) {\n\t\t\t\t\tthrow new Error(`ToolboxStore lock acquisition timed out for ${this.dirPath}`);\n\t\t\t\t}\n\t\t\t\tawait delay(this.retryMs);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async isStaleLock(): Promise<boolean> {\n\t\tlet pidStr: string;\n\t\ttry {\n\t\t\tpidStr = await fsp.readFile(this.pidFilePath, \"utf8\");\n\t\t} catch {\n\t\t\t// The pid file may not exist yet because another acquirer just\n\t\t\t// created the lock dir and hasn't finished writing its pid file\n\t\t\t// (mkdir + writeFile is not atomic). Give it a short grace window\n\t\t\t// before concluding the owner crashed between mkdir and writeFile.\n\t\t\ttry {\n\t\t\t\tconst stat = await fsp.stat(this.dirPath);\n\t\t\t\treturn Date.now() - stat.mtimeMs > LOCK_STALE_GRACE_MS;\n\t\t\t} catch {\n\t\t\t\t// Lock dir disappeared concurrently (e.g. released mid-check) —\n\t\t\t\t// not stale, just gone; the caller's next mkdir will succeed.\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tconst pid = Number.parseInt(pidStr.trim(), 10);\n\t\tif (!Number.isFinite(pid) || pid <= 0) return true;\n\t\treturn !isProcessAlive(pid);\n\t}\n\n\tasync release(): Promise<void> {\n\t\tif (!this.acquired) return;\n\t\tthis.acquired = false;\n\t\tawait fsp.rm(this.dirPath, { recursive: true, force: true });\n\t}\n}\n\nfunction defaultWorkspacePath(): string | null {\n\t// The store caller (ToolboxCore) supplies a cwd, but tests want a\n\t// stable default. Return null when the env var says \"no workspace\".\n\tif (process.env.SERVICEME_NO_WORKSPACE_TOOLBOX === \"1\") return null;\n\treturn path.join(process.cwd(), WORKSPACE_TOOLBOX_RELATIVE_PATH);\n}\n\nexport class ToolboxStore {\n\tprivate readonly userFilePath: string;\n\tprivate readonly resolveWorkspacePath: () => string | null;\n\tprivate readonly defaults: readonly DefaultToolSeed[];\n\tprivate readonly hooks: ToolboxStoreHooks;\n\tprivate readonly backend: ToolboxFileBackend;\n\tprivate readonly lockTimeoutMs: number;\n\tprivate readonly lockRetryMs: number;\n\n\tconstructor(opts: ToolboxStoreOptions = {}) {\n\t\tthis.userFilePath = opts.userFilePath ?? getToolboxJsonPath();\n\t\tthis.resolveWorkspacePath = opts.resolveWorkspacePath ?? defaultWorkspacePath;\n\t\tthis.defaults = opts.defaultTools ?? BUILTIN_DEFAULT_TOOLS;\n\t\tthis.hooks = opts.hooks ?? {};\n\t\tthis.backend = opts.backend ?? new FsToolboxFileBackend();\n\t\tthis.lockTimeoutMs = opts.lockTimeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;\n\t\tthis.lockRetryMs = opts.lockRetryMs ?? DEFAULT_LOCK_RETRY_MS;\n\t}\n\n\t/** Read a scope; returns the resolved toolbox (with defaults merged when empty). */\n\tasync read(scope: ToolboxScope): Promise<ResolvedToolbox> {\n\t\tconst filePath = this.filePathFor(scope);\n\t\tif (scope === \"workspace\") {\n\t\t\tawait migrateLegacyWorkspaceToolboxFile(filePath);\n\t\t}\n\t\tconst stored = filePath ? await this.backend.read(filePath) : null;\n\t\tconst storedTools = stored?.tools ?? [];\n\t\tconst defaults = this.defaults.map((seed, index) => toDefaultTool(seed, index));\n\t\tconst merged = mergeWithDefaults(defaults, storedTools);\n\t\tconst userOnly = storedTools.filter((t) => !this.defaults.some((d) => d.id === t.id));\n\t\tvoid userOnly;\n\t\treturn { scope, tools: merged };\n\t}\n\n\t/**\n\t * Atomic write under a file lock. Replaces the entire `tools` array\n\t * with the provided snapshot.\n\t */\n\tasync write(scope: ToolboxScope, tools: readonly ExternalTool[]): Promise<void> {\n\t\tconst filePath = this.filePathFor(scope);\n\t\tif (!filePath) {\n\t\t\tthrow new Error(`Cannot write toolbox scope '${scope}': file path is unavailable`);\n\t\t}\n\t\tif (scope === \"workspace\") {\n\t\t\tawait migrateLegacyWorkspaceToolboxFile(filePath);\n\t\t}\n\t\tconst payload: PersistedToolbox = {\n\t\t\tversion: TOOLBOX_JSON_SCHEMA_VERSION,\n\t\t\ttools: sortByUserOrder([...tools]),\n\t\t};\n\t\tawait this.hooks.beforeWrite?.(scope, payload);\n\t\tconst lock = new ToolboxFileLock(filePath, this.lockTimeoutMs, this.lockRetryMs);\n\t\tawait lock.acquire();\n\t\ttry {\n\t\t\tawait this.backend.write(filePath, payload);\n\t\t} finally {\n\t\t\tawait lock.release();\n\t\t}\n\t\tawait this.hooks.afterWrite?.(scope, payload);\n\t}\n\n\t/**\n\t * Read-modify-write under the file lock. The mutator receives the\n\t * current user-only list (defaults not included) and returns the\n\t * replacement list. Throwing inside the mutator aborts the write.\n\t */\n\tasync mutate(\n\t\tscope: ToolboxScope,\n\t\tmutator: (current: ExternalTool[]) => Promise<ExternalTool[]>\n\t): Promise<ExternalTool[]> {\n\t\tconst filePath = this.filePathFor(scope);\n\t\tif (!filePath) {\n\t\t\tthrow new Error(`Cannot mutate toolbox scope '${scope}': file path is unavailable`);\n\t\t}\n\t\tif (scope === \"workspace\") {\n\t\t\tawait migrateLegacyWorkspaceToolboxFile(filePath);\n\t\t}\n\t\tconst lock = new ToolboxFileLock(filePath, this.lockTimeoutMs, this.lockRetryMs);\n\t\tawait lock.acquire();\n\t\ttry {\n\t\t\tconst stored = await this.backend.read(filePath);\n\t\t\tconst storedTools = stored?.tools ?? [];\n\t\t\tconst defaultIds = new Set(this.defaults.map((d) => d.id));\n\t\t\tconst userTools = storedTools.filter((t) => !defaultIds.has(t.id));\n\t\t\tconst next = await mutator(userTools);\n\t\t\tconst payload: PersistedToolbox = {\n\t\t\t\tversion: TOOLBOX_JSON_SCHEMA_VERSION,\n\t\t\t\ttools: sortByUserOrder([...next]),\n\t\t\t};\n\t\t\tawait this.hooks.beforeWrite?.(scope, payload);\n\t\t\tawait this.backend.write(filePath, payload);\n\t\t\tawait this.hooks.afterWrite?.(scope, payload);\n\t\t\treturn next;\n\t\t} finally {\n\t\t\tawait lock.release();\n\t\t}\n\t}\n\n\t/** Wipe a scope entirely (used by `toolbox.remove --all` extensions). */\n\tasync clear(scope: ToolboxScope): Promise<void> {\n\t\tconst filePath = this.filePathFor(scope);\n\t\tif (!filePath) return;\n\t\tawait fsp.rm(filePath, { force: true });\n\t}\n\n\t/** Test seam — resolve the user-scope file path. */\n\tgetUserFilePath(): string {\n\t\treturn this.userFilePath;\n\t}\n\n\t/** Test seam — resolve the workspace-scope file path (or null when disabled). */\n\tgetWorkspaceFilePath(): string | null {\n\t\treturn this.resolveWorkspacePath();\n\t}\n\n\tprivate filePathFor(scope: ToolboxScope): string | null {\n\t\treturn scope === \"user\" ? this.userFilePath : this.resolveWorkspacePath();\n\t}\n}\n\nfunction toDefaultTool(seed: DefaultToolSeed, order: number): ExternalTool {\n\treturn {\n\t\tid: seed.id,\n\t\tname: seed.name,\n\t\tdescription: seed.description,\n\t\ticon: seed.icon,\n\t\turl: seed.url,\n\t\tisDefault: true,\n\t\torder,\n\t\tscope: \"user\",\n\t};\n}\n","/**\n * toolbox types — internal-only data shapes.\n *\n * The public data model lives in `@serviceme/devtools-protocol/toolbox`\n * (`ExternalTool`, `ToolboxScope`, `ToolboxList`). The types below are\n * the persisted on-disk shape and the patch helpers, scoped to the\n * toolbox domain only.\n *\n * Refs:\n * - 4.功能规划.md §2.3 — `toolbox/types.ts`\n */\n\nimport type { ExternalTool, ToolboxScope } from \"@serviceme/devtools-protocol\";\n\n/** Schema version of the on-disk toolbox JSON files. Bumped on breaking changes. */\nexport const TOOLBOX_JSON_SCHEMA_VERSION = 1;\n\nexport interface PersistedToolbox {\n\tversion: number;\n\ttools: ExternalTool[];\n}\n\n/** Patch payload accepted by `ToolboxCore.update` (mirrors `ExternalToolPatch`). */\nexport type ToolboxPatch = Partial<Omit<ExternalTool, \"id\" | \"isDefault\">>;\n\n/**\n * Built-in (default) tool seeds. Mirrors the Extension's\n * `apps/extension/src/config/external-tools.ts`. The Core stores these\n * verbatim and never mutates them — the Extension's `ToolBoxService`\n * runs the same merge on top of the Core's view.\n */\nexport interface DefaultToolSeed {\n\tid: string;\n\tname: string;\n\tdescription: string;\n\ticon: string;\n\turl: string;\n}\n\n/** Built-in tools rendered when the JSON file is missing or empty. */\nexport const BUILTIN_DEFAULT_TOOLS: readonly DefaultToolSeed[] = [\n\t{\n\t\tid: \"builtin-docs\",\n\t\tname: \"SERVICEME Docs\",\n\t\tdescription: \"Official documentation portal\",\n\t\ticon: \"book\",\n\t\turl: \"https://docs.medalsoft.com/serviceme\",\n\t},\n\t{\n\t\tid: \"builtin-issues\",\n\t\tname: \"Issue Tracker\",\n\t\tdescription: \"Report bugs and feature requests\",\n\t\ticon: \"bug\",\n\t\turl: \"https://github.com/medalsoftchina/ms-devtools-vscode/issues\",\n\t},\n\t{\n\t\tid: \"builtin-changelog\",\n\t\tname: \"Changelog\",\n\t\tdescription: \"Release notes for every published version\",\n\t\ticon: \"history\",\n\t\turl: \"https://github.com/medalsoftchina/ms-devtools-vscode/releases\",\n\t},\n];\n\n/** Resolved toolbox shape returned by `ToolboxStore.read()`. */\nexport interface ResolvedToolbox {\n\tscope: ToolboxScope;\n\ttools: ExternalTool[];\n}\n","/**\n * ToolboxCore — Main entry for the toolbox domain.\n *\n * Orchestrates the user + workspace scopes via `ToolboxStore`, applies\n * the \"最近使用置顶\" sort, and exposes a small surface that mirrors\n * the Phase 5.1 bridge methods:\n *\n * - `list(scope?)` → `toolbox.list`\n * - `add(tool, scope)` → `toolbox.add`\n * - `remove(id, scope?)` → `toolbox.remove`\n * - `update(id, patch, scope?)` → `toolbox.update`\n *\n * Default tools (built-in seeds) are never user-deletable. `remove()`\n * is a no-op for default tools and surfaces a structured error so\n * callers can map it to `TOOLBOX_DEFAULT_IMMUTABLE` (Phase 5.3 error\n * code).\n *\n * Refs:\n * - 4.功能规划.md §2.3 — `ToolboxCore.ts 主入口`\n * - `3.功能拆分.md` §3 — wire shape\n */\n\nimport type { ExternalTool, ToolboxList, ToolboxScope } from \"@serviceme/devtools-protocol\";\n\nimport { sortByRecentFirst, sortByUserOrder, touchLastUsedAt } from \"./sort\";\nimport { ToolboxStore } from \"./ToolboxStore\";\nimport { BUILTIN_DEFAULT_TOOLS, type DefaultToolSeed, type ToolboxPatch } from \"./types\";\n\n/** Sentinel — caller tried to remove a built-in (immutable) tool. */\nexport class DefaultToolImmutableError extends Error {\n\tconstructor(public readonly toolId: string) {\n\t\tsuper(`Cannot remove default toolbox entry: ${toolId}`);\n\t\tthis.name = \"DefaultToolImmutableError\";\n\t}\n}\n\nexport interface ToolboxCoreOptions {\n\tstore?: ToolboxStore;\n\tdefaultTools?: readonly DefaultToolSeed[];\n\t/** Sort applied to the merged list returned by `list()`. Default: `sortByUserOrder`. */\n\tlistSort?: (tools: readonly ExternalTool[]) => ExternalTool[];\n}\n\nexport class ToolboxCore {\n\tprivate readonly store: ToolboxStore;\n\tprivate readonly defaults: readonly DefaultToolSeed[];\n\tprivate readonly listSort: (tools: readonly ExternalTool[]) => ExternalTool[];\n\n\tconstructor(opts: ToolboxCoreOptions = {}) {\n\t\tthis.store = opts.store ?? new ToolboxStore();\n\t\tthis.defaults = opts.defaultTools ?? BUILTIN_DEFAULT_TOOLS;\n\t\tthis.listSort = opts.listSort ?? sortByUserOrder;\n\t}\n\n\t/** List all tools in a scope. Built-in defaults are merged in. */\n\tasync list(scope: ToolboxScope = \"user\"): Promise<ToolboxList> {\n\t\tconst resolved = await this.store.read(scope);\n\t\treturn {\n\t\t\tscope,\n\t\t\ttools: this.listSort(resolved.tools),\n\t\t};\n\t}\n\n\t/**\n\t * Append a new tool to the requested scope. Default tools are\n\t * rejected (they are seeds, not user entries).\n\t */\n\tasync add(tool: ExternalTool, scope: ToolboxScope = \"user\"): Promise<ToolboxList> {\n\t\tif (this.isDefaultId(tool.id)) {\n\t\t\tthrow new DefaultToolImmutableError(tool.id);\n\t\t}\n\t\tconst next = await this.store.mutate(scope, async (current) => {\n\t\t\tconst filtered = current.filter((t) => t.id !== tool.id);\n\t\t\treturn [\n\t\t\t\t...filtered,\n\t\t\t\t{\n\t\t\t\t\t...tool,\n\t\t\t\t\tscope,\n\t\t\t\t\tisDefault: false,\n\t\t\t\t\torder: tool.order ?? filtered.length,\n\t\t\t\t},\n\t\t\t];\n\t\t});\n\t\treturn { scope, tools: next };\n\t}\n\n\t/**\n\t * Remove a tool by id. Returns `success: false` when the id is a\n\t * built-in default (idempotent, never throws on missing entries).\n\t */\n\tasync remove(\n\t\tid: string,\n\t\tscope: ToolboxScope = \"user\"\n\t): Promise<{ scope: ToolboxScope; toolId: string; success: boolean }> {\n\t\tif (this.isDefaultId(id)) {\n\t\t\tthrow new DefaultToolImmutableError(id);\n\t\t}\n\t\tconst removed = await this.store.mutate(scope, async (current) =>\n\t\t\tcurrent.filter((t) => t.id !== id)\n\t\t);\n\t\treturn { scope, toolId: id, success: !removed.some((t) => t.id === id) };\n\t}\n\n\t/**\n\t * Patch a tool by id. Default tools can only have their `order`\n\t * updated; other patches are silently ignored for default entries\n\t * (callers can compare before/after to detect the ignore).\n\t */\n\tasync update(\n\t\tid: string,\n\t\tpatch: ToolboxPatch,\n\t\tscope: ToolboxScope = \"user\"\n\t): Promise<ToolboxList> {\n\t\tconst isDefault = this.isDefaultId(id);\n\t\tconst next = await this.store.mutate(scope, async (current) => {\n\t\t\tif (isDefault) {\n\t\t\t\t// Default tools are not stored on disk — silently ignore\n\t\t\t\t// non-order patches. The order field is also not persisted\n\t\t\t\t// (default order is set in memory at merge time), so the\n\t\t\t\t// caller can compare before/after to detect the ignore.\n\t\t\t\treturn current;\n\t\t\t}\n\t\t\treturn current.map((tool) =>\n\t\t\t\ttool.id === id ? { ...tool, ...patch, scope, isDefault: false } : tool\n\t\t\t);\n\t\t});\n\t\treturn { scope, tools: next };\n\t}\n\n\t/**\n\t * Stamp `lastUsedAt` on the targeted tool. This is the \"recently\n\t * used\" hook the Extension's webview uses when a user clicks a\n\t * toolbox entry.\n\t */\n\tasync recordUsage(\n\t\tid: string,\n\t\tscope: ToolboxScope = \"user\",\n\t\twhen: Date = new Date()\n\t): Promise<ExternalTool | null> {\n\t\tif (this.isDefaultId(id)) {\n\t\t\t// Defaults live in memory only — return a synthetic record so\n\t\t\t// callers can render the click without persisting anything.\n\t\t\tconst seed = this.defaults.find((d) => d.id === id);\n\t\t\tif (!seed) return null;\n\t\t\treturn { ...seed, scope, isDefault: true, order: 0, lastUsedAt: when.toISOString() };\n\t\t}\n\t\tlet updated: ExternalTool | null = null;\n\t\tawait this.store.mutate(scope, async (current) => {\n\t\t\tconst next = touchLastUsedAt(current, id, when);\n\t\t\tupdated = next.find((t) => t.id === id) ?? null;\n\t\t\treturn next;\n\t\t});\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Combined view: user + workspace scopes merged, sorted by recent\n\t * usage. Workspace tools overlay user tools (workspace entries win\n\t * on `id` collision).\n\t */\n\tasync listMerged(): Promise<ToolboxList> {\n\t\tconst [user, workspace] = await Promise.all([\n\t\t\tthis.store.read(\"user\"),\n\t\t\tthis.store.read(\"workspace\"),\n\t\t]);\n\t\tconst seen = new Set<string>();\n\t\tconst merged: ExternalTool[] = [];\n\t\tfor (const tool of workspace.tools) {\n\t\t\tseen.add(tool.id);\n\t\t\tmerged.push({ ...tool, scope: \"workspace\" });\n\t\t}\n\t\tfor (const tool of user.tools) {\n\t\t\tif (seen.has(tool.id)) continue;\n\t\t\tmerged.push({ ...tool, scope: \"user\" });\n\t\t}\n\t\treturn { scope: \"user\", tools: sortByRecentFirst(merged) };\n\t}\n\n\t/** Expose the underlying store (CLI / Bridge use it for path-level access). */\n\tgetStore(): ToolboxStore {\n\t\treturn this.store;\n\t}\n\n\tprivate isDefaultId(id: string): boolean {\n\t\treturn this.defaults.some((d) => d.id === id);\n\t}\n}\n"],"mappings":"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","names":["resolve","createServicemeError","resolve","createServicemeError","COPILOT_ERROR_CODES","createServicemeError","COPILOT_COMMAND","createServicemeError","COPILOT_ERROR_CODES","createHash","platform","os","path","os","path","stat","platform","fs","path","fs","path","fs","path","stat","homedir","join","createServicemeError","platform","createServicemeError","homedir","join","isTimeout","spawn","path","resolve","spawn","fs","path","createServicemeError","createServicemeError","createServicemeError","randomUUID","fs","open","path","randomUUID","open","randomUUID","fs","path","randomUUID","fs","path","createServicemeError","access","mkdir","readdir","rename","rm","dirname","join","resolve","createServicemeError","fs","path","fs","path","fs","unlink","EventEmitter","fs","EventEmitter","fs","path","fs","path","CONFIG_DIR","stat","fs","os","path","spawn","fs","DEFAULT_TIMEOUT_MS","resolve","spawn","DEFAULT_TIMEOUT_MS","spawn","fs","path","MAX_OUTPUT_BYTES","DEFAULT_TIMEOUT_MS","platform","writeDiagnostic","resolve","spawn","randomUUID","fs","os","path","createServicemeError","isRecord","createServicemeError","randomUUID","getExecutor","randomUUID","fs","path","randomUUID","SCHEDULER_LOG_FILENAME","fs","path","isScheduledTasksConfigV1","migrateV1ToV2","isScheduledTasksConfigV1","migrateV1ToV2","spawn","fs","path","DEFAULT_TIMEOUT_MS","resolve","createServicemeError","fs","path","SAFE_LOCAL_ID_PATTERN","fs","path","fsp","path","delay","FILE_MODE","LOCK_DIR_MODE","DEFAULT_LOCK_TIMEOUT_MS","DEFAULT_LOCK_RETRY_MS","LOCK_STALE_GRACE_MS","TMP_SUFFIX","isNodeError","isProcessAlive","delay","stat"]}
1
+ {"version":3,"sources":["../src/agents/AgentCatalogClient.ts","../src/permissions/agent-permissions.ts","../src/agents/AgentReconciler.ts","../src/agents/AgentStore.ts","../src/auth/AccessControl.ts","../src/logger.ts","../src/auth/AuthStateManager.ts","../src/auth/ProviderRegistry.ts","../src/auth/AuthCore.ts","../src/auth/KeychainAuthTokenStore.ts","../src/auth/utils/githubUserEmail.ts","../src/auth/providers/GitHubAuthProvider.ts","../src/auth/providers/MicrosoftAuthProvider.ts","../src/copilot/doctor.ts","../src/process/runCommand.ts","../src/copilot/prompt.ts","../src/copilot-content/copilot-host-capabilities.ts","../src/paths/userHome.ts","../src/copilot-content/copilot-link-materializer.ts","../src/copilot-content/copilot-plugin-registrar.ts","../src/copilot-content/plugin-materializer.ts","../src/copilot-content/customization-model.ts","../src/copilot-content/customization-view.ts","../src/copilot-content/disabled-content-store.ts","../src/copilot-content/filesystem-integration-adapters.ts","../src/copilot-content/integration-adapters.ts","../src/copilot-content/package-installation-service.ts","../src/copilot-content/workspace-exclude-store.ts","../src/copilot-content/workspace-manifest.ts","../src/copilot-content/workspace-state-store.ts","../src/copilot-content/personal-copilot-content-reconciler.ts","../src/copilot-content/personal-installation-store.ts","../src/copilot-content/plugin-catalog-service.ts","../src/copilot-content/plugin-resolver.ts","../src/copilot-content/types.ts","../src/copilot-content/source-catalog-service.ts","../src/copilot-content/workspace-copilot-content-reconciler.ts","../src/copilot-content/workspace-declaration-reader.ts","../src/device/deviceAuth.ts","../src/device/Enroller.ts","../src/device/InstallationId.ts","../src/device/IdentityStore.ts","../src/device/types.ts","../src/device/DeviceCore.ts","../src/drafts/index.ts","../src/skill-store/index.ts","../src/repo-layout/index.ts","../src/env/environmentInspector.ts","../src/git-client/index.ts","../src/git-client/types.ts","../src/image/imageTools.ts","../src/json/jsonTools.ts","../src/paths/serverProxyGlobal.ts","../src/phase5/bootstrap.ts","../src/project/projectTools.ts","../src/utils/fileUtils.ts","../src/repo-manager/index.ts","../src/repos/types.ts","../src/repos/default-repos.ts","../src/repos/loader.ts","../src/repos/store.ts","../src/scheduled-tasks/daemon/DaemonLogger.ts","../src/scheduled-tasks/daemon/PidManager.ts","../src/scheduled-tasks/daemon/SchedulerDaemonV2.ts","../src/scheduled-tasks/executors/GithubCopilotCliExecutor.ts","../src/scheduled-tasks/executors/timeout.ts","../src/scheduled-tasks/executors/HttpRequestExecutor.ts","../src/scheduled-tasks/executors/ShellExecutor.ts","../src/scheduled-tasks/executors/types.ts","../src/scheduled-tasks/executors/index.ts","../src/scheduled-tasks/TaskConfigManager.ts","../src/scheduled-tasks/TaskExecutionEngine.ts","../src/scheduled-tasks/TaskLogManager.ts","../src/scheduled-tasks/migration/MigrateToGlobal.ts","../src/scheduled-tasks/workspace-probe/WorkspaceProbe.ts","../src/skills/SkillCatalogClient.ts","../src/skills/SkillReconciler.ts","../src/skills/SkillStore.ts","../src/submit/index.ts","../src/submit/types.ts","../src/toolbox/sort.ts","../src/toolbox/ToolboxStore.ts","../src/toolbox/types.ts","../src/toolbox/ToolboxCore.ts"],"sourcesContent":["import type { AgentMarketplaceEntry } from \"@serviceme/devtools-protocol\";\nimport { createServicemeError } from \"@serviceme/devtools-protocol\";\nimport type { AgentDownloadFile } from \"./types\";\n\nexport interface AgentCatalog {\n\tagents: AgentMarketplaceEntry[];\n\tfetchedAt: string;\n}\n\nexport interface AgentCatalogClientOptions {\n\tfetchImpl?: typeof fetch;\n\tbaseUrl?: string;\n}\n\nexport class AgentCatalogClient {\n\tprivate readonly fetchImpl: typeof fetch;\n\tprivate readonly baseUrl?: string;\n\n\tconstructor(options: AgentCatalogClientOptions = {}) {\n\t\tthis.fetchImpl = options.fetchImpl ?? fetch;\n\t\tthis.baseUrl = options.baseUrl;\n\t}\n\n\tasync getCatalog(): Promise<AgentCatalog> {\n\t\tif (!this.baseUrl) {\n\t\t\tthrow createServicemeError(\"workspace_not_found\", \"Agent catalog baseUrl is not configured.\");\n\t\t}\n\n\t\tconst response = await this.fetchImpl(`${this.baseUrl}/api/v1/marketplace/agents`);\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Failed to fetch agents catalog: ${response.status}`);\n\t\t}\n\n\t\tconst data = (await response.json()) as {\n\t\t\tagents?: AgentMarketplaceEntry[];\n\t\t};\n\t\treturn {\n\t\t\tagents: data.agents ?? [],\n\t\t\tfetchedAt: new Date().toISOString(),\n\t\t};\n\t}\n\n\tasync downloadAgent(remoteId: string): Promise<AgentDownloadFile[]> {\n\t\tif (!this.baseUrl) {\n\t\t\tthrow createServicemeError(\"workspace_not_found\", \"Agent catalog baseUrl is not configured.\");\n\t\t}\n\n\t\tconst response = await this.fetchImpl(\n\t\t\t`${this.baseUrl}/api/v1/marketplace/agents/download/${remoteId}`\n\t\t);\n\t\tif (!response.ok) {\n\t\t\tif (response.status === 404) {\n\t\t\t\tthrow createServicemeError(\"not_found\", `Agent '${remoteId}' not found`);\n\t\t\t}\n\t\t\tthrow new Error(`Failed to download agent ${remoteId}: ${response.status}`);\n\t\t}\n\n\t\tconst payload = (await response.json()) as {\n\t\t\tdata?: { files?: AgentDownloadFile[] };\n\t\t};\n\t\treturn payload.data?.files ?? [];\n\t}\n}\n","import type { AgentToolPermission, AgentToolRiskLevel } from \"@serviceme/devtools-protocol\";\n\nexport const TOOL_RISK_MAP: Record<string, AgentToolRiskLevel> = {\n\tshell: \"high\",\n\tterminal: \"high\",\n\trun_in_terminal: \"high\",\n\texecution_subagent: \"high\",\n\tfilesystem: \"medium\",\n\tfetch: \"medium\",\n\tfetch_webpage: \"medium\",\n\tcreate_file: \"medium\",\n\treplace_string_in_file: \"medium\",\n\tmulti_replace_string_in_file: \"medium\",\n\tread_file: \"low\",\n\tsearch: \"low\",\n\tgrep_search: \"low\",\n\tfile_search: \"low\",\n\tsemantic_search: \"low\",\n\tlist_dir: \"low\",\n};\n\nconst FRONTMATTER_REGEX = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/;\nconst TOOLS_LINE_REGEX = /^tools:\\s*$/m;\nconst TOOLS_INLINE_REGEX = /^tools:\\s*\\[([^\\]]*)\\]/m;\nconst LIST_ITEM_REGEX = /^\\s*-\\s+(.+)$/;\n\nexport function parseAgentToolPermissions(content: string): AgentToolPermission[] {\n\tconst fmMatch = content.match(FRONTMATTER_REGEX);\n\tif (!fmMatch?.[1]) return [];\n\n\tconst frontmatter = fmMatch[1];\n\n\tconst inlineMatch = frontmatter.match(TOOLS_INLINE_REGEX);\n\tif (inlineMatch?.[1] != null) {\n\t\tconst raw = inlineMatch[1];\n\t\treturn raw\n\t\t\t.split(\",\")\n\t\t\t.map((t) => t.trim())\n\t\t\t.filter(Boolean)\n\t\t\t.map((tool) => ({\n\t\t\t\ttool,\n\t\t\t\triskLevel: TOOL_RISK_MAP[tool] ?? \"medium\",\n\t\t\t}));\n\t}\n\n\tconst blockMatch = frontmatter.match(TOOLS_LINE_REGEX);\n\tif (!blockMatch?.[0]) return [];\n\n\tconst toolsStartIndex = frontmatter.indexOf(blockMatch[0]) + blockMatch[0].length;\n\tconst remaining = frontmatter.slice(toolsStartIndex);\n\tconst lines = remaining.split(/\\r?\\n/);\n\tconst tools: AgentToolPermission[] = [];\n\n\tfor (const line of lines) {\n\t\tconst itemMatch = line.match(LIST_ITEM_REGEX);\n\t\tif (itemMatch?.[1]) {\n\t\t\tconst tool = itemMatch[1].trim();\n\t\t\ttools.push({ tool, riskLevel: TOOL_RISK_MAP[tool] ?? \"medium\" });\n\t\t} else if (line.trim() !== \"\" && !line.startsWith(\" \") && !line.startsWith(\"\\t\")) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn tools;\n}\n","import type {\n\tAgentMarketplaceEntry,\n\tAgentMutationRequest,\n\tAgentPermissionSummary,\n} from \"@serviceme/devtools-protocol\";\nimport { parseAgentToolPermissions, TOOL_RISK_MAP } from \"../permissions\";\n\ninterface AgentStoreLike {\n\tnormalizeRemoteAgentId(remoteId: string): string;\n\tlistWorkspaceAgentIds(): Promise<string[]>;\n\tlistUserAgentIds(): Promise<string[]>;\n}\n\ninterface AgentCatalogLike {\n\tagents: AgentMarketplaceEntry[];\n\tfetchedAt: string;\n}\n\ninterface AgentCatalogClientLike {\n\tgetCatalog(): Promise<AgentCatalogLike>;\n}\n\nexport interface AgentReconcilerDependencies {\n\tagentStore: AgentStoreLike;\n\tcatalogClient: AgentCatalogClientLike;\n}\n\nexport interface AgentMutateResult {\n\tstatus: \"success\" | \"blocked\" | \"requires_confirmation\";\n\tchanged: boolean;\n\tmessage?: string;\n\ttools?: string[];\n}\n\nexport class AgentReconciler {\n\tconstructor(private readonly deps: AgentReconcilerDependencies) {}\n\n\tasync mutate(request: AgentMutationRequest): Promise<AgentMutateResult> {\n\t\tif (request.targetScope !== \"workspace\" && request.targetScope !== \"user\") {\n\t\t\tthrow new Error(`Invalid target scope: ${String(request.targetScope)}`);\n\t\t}\n\n\t\tif (\n\t\t\trequest.action === \"uninstall\" ||\n\t\t\trequest.action === \"move\" ||\n\t\t\trequest.action === \"removeExternal\"\n\t\t) {\n\t\t\treturn {\n\t\t\t\tstatus: \"success\",\n\t\t\t\tchanged: true,\n\t\t\t\tmessage: `Agent ${request.action} completed.`,\n\t\t\t};\n\t\t}\n\n\t\tif (request.action !== \"install\") {\n\t\t\treturn {\n\t\t\t\tstatus: \"blocked\",\n\t\t\t\tchanged: false,\n\t\t\t\tmessage: `Agent action is not supported by bridge reconciler: ${request.action}`,\n\t\t\t};\n\t\t}\n\n\t\tconst catalog = await this.deps.catalogClient.getCatalog();\n\t\tconst remoteAgent = catalog.agents.find(\n\t\t\t(agent) => this.deps.agentStore.normalizeRemoteAgentId(agent.id) === request.agentId\n\t\t);\n\n\t\tif (!remoteAgent) {\n\t\t\treturn {\n\t\t\t\tstatus: \"blocked\",\n\t\t\t\tchanged: false,\n\t\t\t\tmessage: \"Agent not found in catalog.\",\n\t\t\t};\n\t\t}\n\n\t\tif (!request.confirmed && this.hasHighRiskTool(remoteAgent.tools)) {\n\t\t\treturn {\n\t\t\t\tstatus: \"requires_confirmation\",\n\t\t\t\tchanged: false,\n\t\t\t\tmessage: \"This agent uses high-risk tools that require confirmation.\",\n\t\t\t\ttools: remoteAgent.tools,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tchanged: true,\n\t\t\tmessage: \"Agent installed.\",\n\t\t};\n\t}\n\n\tgetPermissionSummary(\n\t\tagentId: string,\n\t\tagentName: string,\n\t\tcontent: string\n\t): AgentPermissionSummary {\n\t\tconst tools = parseAgentToolPermissions(content);\n\t\treturn {\n\t\t\tagentId,\n\t\t\tagentName,\n\t\t\ttools,\n\t\t\thighRiskCount: tools.filter((tool) => tool.riskLevel === \"high\").length,\n\t\t\tmediumRiskCount: tools.filter((tool) => tool.riskLevel === \"medium\").length,\n\t\t\tlowRiskCount: tools.filter((tool) => tool.riskLevel === \"low\").length,\n\t\t};\n\t}\n\n\tprivate hasHighRiskTool(tools: string[]): boolean {\n\t\treturn tools.some((tool) => TOOL_RISK_MAP[tool] === \"high\");\n\t}\n}\n","import type { Dirent } from \"node:fs\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type {\n\tAgentDownloadFile,\n\tAgentStoreFileSystem,\n\tAgentStoreOptions,\n\tAgentsStateFile,\n\tInstalledAgent,\n} from \"./types\";\n\nconst WORKSPACE_AGENTS_ROOT_RELATIVE = \".github/agents\";\nconst WORKSPACE_AGENTS_STATE_RELATIVE = \".github/.serviceme-agents.yml\";\nconst LEGACY_WORKSPACE_AGENTS_STATE_RELATIVE = \".github/.ms-devtools-agents.yml\";\nconst LEGACY_USER_AGENTS_ROOT_RELATIVE = \".agents/agents\";\nconst SAFE_LOCAL_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;\n\nfunction assertSafeLocalAgentId(agentId: string): string {\n\tif (\n\t\ttypeof agentId !== \"string\" ||\n\t\tagentId.length === 0 ||\n\t\tagentId === \".\" ||\n\t\tagentId === \"..\" ||\n\t\tagentId.includes(\"/\") ||\n\t\tagentId.includes(\"\\\\\") ||\n\t\t!SAFE_LOCAL_ID_PATTERN.test(agentId)\n\t) {\n\t\tthrow new Error(`Invalid agent id: ${agentId}`);\n\t}\n\n\treturn agentId;\n}\n\nexport class AgentStore {\n\tprivate readonly workspacePath: string;\n\tprivate readonly userAgentsRoot: string;\n\tprivate readonly fileSystem: AgentStoreFileSystem;\n\tprivate readonly schemaVersion: number;\n\n\tconstructor(options: AgentStoreOptions) {\n\t\tthis.workspacePath = options.workspacePath;\n\t\tthis.userAgentsRoot = options.userAgentsRoot;\n\t\tthis.fileSystem = options.fileSystem ?? fs;\n\t\tthis.schemaVersion = options.schemaVersion ?? 1;\n\t}\n\n\tnormalizeRemoteAgentId(remoteId: string): string {\n\t\tif (remoteId.startsWith(\"official/\")) {\n\t\t\treturn assertSafeLocalAgentId(remoteId.slice(\"official/\".length));\n\t\t}\n\t\tif (remoteId.startsWith(\"community/\")) {\n\t\t\tconst lastSlash = remoteId.lastIndexOf(\"/\");\n\t\t\treturn assertSafeLocalAgentId(remoteId.slice(lastSlash + 1));\n\t\t}\n\t\treturn assertSafeLocalAgentId(remoteId);\n\t}\n\n\tgetWorkspaceAgentsRootPath(): string {\n\t\treturn WORKSPACE_AGENTS_ROOT_RELATIVE;\n\t}\n\n\tgetWorkspaceStateFilePath(): string {\n\t\treturn WORKSPACE_AGENTS_STATE_RELATIVE;\n\t}\n\n\tgetUserAgentsRootPath(): string {\n\t\treturn this.userAgentsRoot;\n\t}\n\n\tasync listWorkspaceAgentIds(): Promise<string[]> {\n\t\treturn this.listAgentIds(path.join(this.workspacePath, WORKSPACE_AGENTS_ROOT_RELATIVE));\n\t}\n\n\tasync listUserAgentIds(): Promise<string[]> {\n\t\treturn this.listAgentIds(this.userAgentsRoot);\n\t}\n\n\tasync readState(): Promise<AgentsStateFile | null> {\n\t\tawait this.migrateLegacyState();\n\t\ttry {\n\t\t\tconst raw = await this.fileSystem.readFile(\n\t\t\t\tpath.join(this.workspacePath, WORKSPACE_AGENTS_STATE_RELATIVE),\n\t\t\t\t\"utf-8\"\n\t\t\t);\n\t\t\tconst parsed = JSON.parse(raw) as AgentsStateFile;\n\t\t\tif (typeof parsed.schemaVersion !== \"number\" || !Array.isArray(parsed.installedAgents)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn parsed;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * One-time migration: the workspace agents state file used to be named\n\t * `.github/.ms-devtools-agents.yml`. If the new `.serviceme-agents.yml`\n\t * doesn't exist yet but the legacy file does, copy its content forward\n\t * so existing installed-agent state isn't silently lost.\n\t */\n\tprivate async migrateLegacyState(): Promise<void> {\n\t\tconst newPath = path.join(this.workspacePath, WORKSPACE_AGENTS_STATE_RELATIVE);\n\t\tconst legacyPath = path.join(this.workspacePath, LEGACY_WORKSPACE_AGENTS_STATE_RELATIVE);\n\t\ttry {\n\t\t\tawait this.fileSystem.readFile(newPath, \"utf-8\");\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// new file missing — check the legacy path below\n\t\t}\n\t\ttry {\n\t\t\tconst legacyContent = await this.fileSystem.readFile(legacyPath, \"utf-8\");\n\t\t\tawait this.fileSystem.mkdir(path.dirname(newPath), { recursive: true });\n\t\t\tawait this.fileSystem.writeFile(newPath, legacyContent, \"utf-8\");\n\t\t} catch {\n\t\t\t// legacy file doesn't exist either — nothing to migrate\n\t\t}\n\t}\n\n\tasync writeState(state: AgentsStateFile): Promise<void> {\n\t\tconst statePath = path.join(this.workspacePath, WORKSPACE_AGENTS_STATE_RELATIVE);\n\t\tawait this.fileSystem.mkdir(path.dirname(statePath), { recursive: true });\n\t\tawait this.fileSystem.writeFile(statePath, JSON.stringify(state, null, 2), \"utf-8\");\n\t}\n\n\tasync addInstalledAgent(entry: InstalledAgent): Promise<void> {\n\t\tconst state =\n\t\t\t(await this.readState()) ??\n\t\t\t({\n\t\t\t\tschemaVersion: this.schemaVersion,\n\t\t\t\tinstalledAgents: [],\n\t\t\t} as AgentsStateFile);\n\t\tstate.installedAgents = state.installedAgents.filter((agent) => agent.id !== entry.id);\n\t\tstate.installedAgents.push(entry);\n\t\tawait this.writeState(state);\n\t}\n\n\tasync removeInstalledAgent(agentId: string): Promise<void> {\n\t\tconst state = await this.readState();\n\t\tif (!state) {\n\t\t\treturn;\n\t\t}\n\t\tstate.installedAgents = state.installedAgents.filter((agent) => agent.id !== agentId);\n\t\tawait this.writeState(state);\n\t}\n\n\tasync writeAgentFiles(\n\t\tagentId: string,\n\t\tscope: \"workspace\" | \"user\",\n\t\tfiles: AgentDownloadFile[]\n\t): Promise<void> {\n\t\tconst root =\n\t\t\tscope === \"workspace\"\n\t\t\t\t? path.join(this.workspacePath, WORKSPACE_AGENTS_ROOT_RELATIVE)\n\t\t\t\t: this.userAgentsRoot;\n\t\tconst firstFile = files[0];\n\t\tconst isSingleFlatFile =\n\t\t\tfiles.length === 1 &&\n\t\t\tfirstFile !== undefined &&\n\t\t\tfirstFile.path === `${agentId}.agent.md` &&\n\t\t\t!firstFile.path.includes(\"/\");\n\t\tconst targetDir = isSingleFlatFile ? root : path.join(root, agentId);\n\t\tawait this.fileSystem.mkdir(targetDir, { recursive: true });\n\n\t\tfor (const file of files) {\n\t\t\tconst filePath = path.join(targetDir, file.path);\n\t\t\tawait this.fileSystem.mkdir(path.dirname(filePath), { recursive: true });\n\t\t\tawait this.fileSystem.writeFile(filePath, file.content, \"utf-8\");\n\t\t\tif (file.executable) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.fileSystem.chmod(filePath, 0o755);\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore chmod failures on unsupported environments\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async listAgentIds(dir: string): Promise<string[]> {\n\t\ttry {\n\t\t\tconst entries = await this.fileSystem.readdir(dir, {\n\t\t\t\twithFileTypes: true,\n\t\t\t});\n\t\t\tconst ids: string[] = [];\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (entry.name.startsWith(\".\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (entry.isDirectory()) {\n\t\t\t\t\tids.push(entry.name);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (entry.isFile() && entry.name.endsWith(\".agent.md\")) {\n\t\t\t\t\tids.push(entry.name.replace(/\\.agent\\.md$/, \"\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ids.sort();\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n}\n\n/** One legacy content entry reported by {@link migrateLegacyUserContent}. */\nexport interface LegacyUserContentEntry {\n\t/** Legacy agent id derived from the file or directory name. */\n\tid: string;\n\t/** Absolute path of the legacy content under ~/.agents. */\n\tlegacyPath: string;\n\t/** Target path under ~/.copilot the migration would create. */\n\ttargetPath: string;\n\t/** Always `migration_available` until the user runs the migration. */\n\tstatus: \"migration_available\";\n}\n\nexport interface LegacyUserContentMigrationResult {\n\tentries: LegacyUserContentEntry[];\n}\n\n/**\n * Detect ~/.agents agent content that can migrate to ~/.copilot.\n *\n * Read-only by design: legacy content stays untouched until the user\n * explicitly invokes the migration, so a detection pass never destroys\n * anything another tool still depends on.\n */\nexport async function migrateLegacyUserContent(input: {\n\thomeDir: string;\n\tworkspaceDir: string;\n\tfileSystem?: AgentStoreFileSystem;\n}): Promise<LegacyUserContentMigrationResult> {\n\tconst fileSystem = input.fileSystem ?? fs;\n\tconst legacyRoot = path.join(input.homeDir, ...LEGACY_USER_AGENTS_ROOT_RELATIVE.split(\"/\"));\n\tconst targetRoot = path.join(input.homeDir, \".copilot\", \"agents\");\n\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await fileSystem.readdir(legacyRoot, { withFileTypes: true });\n\t} catch {\n\t\treturn { entries: [] };\n\t}\n\n\tconst result: LegacyUserContentEntry[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.name.startsWith(\".\")) continue;\n\t\tconst isFlatAgentFile = entry.isFile() && entry.name.endsWith(\".agent.md\");\n\t\tif (!entry.isDirectory() && !isFlatAgentFile) continue;\n\t\tresult.push({\n\t\t\tid: entry.name.replace(/\\.agent\\.md$/, \"\"),\n\t\t\tlegacyPath: path.join(legacyRoot, entry.name),\n\t\t\ttargetPath: isFlatAgentFile\n\t\t\t\t? path.join(targetRoot, entry.name)\n\t\t\t\t: path.join(targetRoot, entry.name),\n\t\t\tstatus: \"migration_available\",\n\t\t});\n\t}\n\treturn { entries: result };\n}\n","/**\n * AccessControl — Pure logic for org-membership gating.\n *\n * Ported from `apps/extension/src/services/auth/AccessControlService.ts`\n * but stripped of all VSCode dependencies:\n * - No `vscode.window.showWarningMessage` — callers render the notice.\n * - No `vscode.context.globalState` — callers inject a `KeyValueStore`.\n * - No `vscode.Event` — uses a plain `onDidChange` callback.\n *\n * The class is generic over the membership-fetcher function so it can\n * run identically with the CLI's `@serviceme/devtools-shared` `getGitHubOrgMembership`\n * or with the Extension's bundled copy. ADL-003 forbids Core from\n * importing `@serviceme/devtools-shared` directly, so the org membership\n * result type is defined locally and the runtime adapter wraps the\n * shared helper at the boundary (Phase 5.3+).\n *\n * Refs:\n * - 4.功能规划.md §2.1 — `AccessControl.ts 从 AccessControlService 改写`\n * - ADL-003 — `@serviceme/devtools-shared` boundary (Core MUST NOT import shared)\n */\n\nimport type { AuthAccountMeta, AuthProvider } from \"@serviceme/devtools-protocol\";\n\n/** Core-local mirror of `GitHubOrgMembershipCheckResult` (defined in `@serviceme/devtools-shared`). */\nexport interface CoreOrgMembershipResult {\n\tstatus: \"active\" | \"pending\" | \"not_member\" | \"unknown\";\n\thttpStatus?: number;\n\trole?: string;\n\tdirectMembership?: boolean;\n}\n\n/**\n * Upstream org-membership fetcher. CLI / Extension adapters wrap\n * `getGitHubOrgMembership` (or the local extension copy) to match\n * this signature.\n */\nexport type OrgMembershipFetcher = (token: string, org: string) => Promise<CoreOrgMembershipResult>;\n\n/** Pluggable key/value store. Extension injects `globalState`-backed impl; CLI injects a file-backed impl. */\nexport interface KeyValueStore {\n\tget<T>(key: string): T | undefined;\n\tupdate<T>(key: string, value: T): Promise<void>;\n}\n\nexport interface AccessCheckResult {\n\tallowed: boolean;\n\treason?: \"not_authenticated\" | \"not_member\";\n\tusername?: string;\n}\n\nexport interface AccessControlOptions {\n\torg: string;\n\t/** Domains that auto-qualify Microsoft users without a GitHub link. */\n\tmicrosoftEmailDomains?: readonly string[];\n\t/** TTL for in-memory membership cache (ms). */\n\tcacheTtlMs?: number;\n\t/** TTL for persisted membership cache (ms). */\n\tpersistentCacheTtlMs?: number;\n\t/** Tokens whose bearer can be supplied to `OrgMembershipFetcher`. */\n\ttokenFetcher: (provider: AuthProvider) => Promise<string | null>;\n\t/** Upstream org-membership fetcher (typically `getGitHubOrgMembership` from `@serviceme/devtools-shared`). */\n\torgFetcher: OrgMembershipFetcher;\n\t/** Clock seam for tests. */\n\tnow?: () => number;\n}\n\nconst DEFAULT_CACHE_TTL_MS = 10 * 60 * 1000;\nconst DEFAULT_PERSISTENT_CACHE_TTL_MS = 24 * 60 * 60 * 1000;\nconst KEY_GRANTED_USERS = \"msDevTools.accessControl.grantedUsers\";\nconst KEY_MEMBERSHIP_CACHE = \"msDevTools.accessControl.membershipCache\";\nconst KEY_SERVER_IN_ORG = \"msDevTools.accessControl.serverInOrg\";\n\ninterface CacheEntry {\n\tisMember: boolean;\n\tat: number;\n}\n\ninterface PersistentCache {\n\t[username: string]: CacheEntry;\n}\n\nexport class AccessControl {\n\tprivate readonly memCache = new Map<string, CacheEntry>();\n\tprivate readonly serverInOrgMem = new Map<string, boolean>();\n\tprivate readonly listeners = new Set<() => void>();\n\tprivate readonly opts: Required<\n\t\tOmit<AccessControlOptions, \"now\" | \"tokenFetcher\" | \"microsoftEmailDomains\" | \"orgFetcher\">\n\t> & {\n\t\ttokenFetcher: AccessControlOptions[\"tokenFetcher\"];\n\t\torgFetcher: OrgMembershipFetcher;\n\t\tmicrosoftEmailDomains: readonly string[];\n\t\tnow: () => number;\n\t};\n\n\tconstructor(\n\t\tprivate readonly kv: KeyValueStore,\n\t\toptions: AccessControlOptions\n\t) {\n\t\tthis.opts = {\n\t\t\torg: options.org,\n\t\t\ttokenFetcher: options.tokenFetcher,\n\t\t\torgFetcher: options.orgFetcher,\n\t\t\tmicrosoftEmailDomains: options.microsoftEmailDomains ?? [],\n\t\t\tcacheTtlMs: options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS,\n\t\t\tpersistentCacheTtlMs: options.persistentCacheTtlMs ?? DEFAULT_PERSISTENT_CACHE_TTL_MS,\n\t\t\tnow: options.now ?? (() => Date.now()),\n\t\t};\n\t\t// Hydrate `serverInOrg` from persistent storage so the gate can run instantly on restart.\n\t\tconst persistedServerInOrg = this.kv.get<Record<string, boolean>>(KEY_SERVER_IN_ORG);\n\t\tif (persistedServerInOrg) {\n\t\t\tfor (const [key, value] of Object.entries(persistedServerInOrg)) {\n\t\t\t\tthis.serverInOrgMem.set(key, value);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Subscribe to inOrg changes (used by extension to refresh access-gated UI). */\n\tonDidChange(listener: () => void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => this.listeners.delete(listener);\n\t}\n\n\t/** Full check: requires authentication + org membership / admin grant. */\n\tasync checkAccess(user: AuthAccountMeta | null): Promise<AccessCheckResult> {\n\t\tif (!user) {\n\t\t\treturn { allowed: false, reason: \"not_authenticated\" };\n\t\t}\n\n\t\t// Microsoft users: server-confirmed inOrg takes priority over GitHub link.\n\t\tif (user.provider === \"microsoft\" && user.login) {\n\t\t\tconst inOrg = this.serverInOrgMem.get(user.login.toLowerCase());\n\t\t\tif (inOrg === true) return { allowed: true };\n\t\t\tif (inOrg === false) {\n\t\t\t\treturn { allowed: false, reason: \"not_member\", username: user.login };\n\t\t\t}\n\t\t}\n\n\t\t// Find a GitHub account if available (linked for Microsoft users).\n\t\tconst githubAccount = user.provider === \"github\" ? user : null;\n\t\tif (!githubAccount && user.provider === \"microsoft\" && user.login) {\n\t\t\tconst [, domain = \"\"] = user.login.split(\"@\");\n\t\t\tconst isDomainMember =\n\t\t\t\tdomain.length > 0 && this.opts.microsoftEmailDomains.includes(domain.toLowerCase());\n\t\t\tif (isDomainMember) return { allowed: true };\n\t\t\t// No GitHub link + unknown domain — deny until the next server-confirmed claim.\n\t\t\treturn { allowed: false, reason: \"not_member\", username: user.login };\n\t\t}\n\t\tif (!githubAccount) {\n\t\t\t// No identity at all — treat as authenticated for now (caller may have other gating).\n\t\t\treturn { allowed: true };\n\t\t}\n\n\t\tconst accessUsername = githubAccount.login ?? user.login ?? \"\";\n\t\tif (accessUsername.length === 0) {\n\t\t\treturn { allowed: false, reason: \"not_member\", username: \"\" };\n\t\t}\n\n\t\t// Admin-granted users bypass the org check.\n\t\tconst granted = this.getGrantedUsers();\n\t\tif (granted.includes(accessUsername.toLowerCase())) {\n\t\t\treturn { allowed: true };\n\t\t}\n\n\t\tconst isMember = await this.checkOrgMembership(accessUsername);\n\t\treturn isMember\n\t\t\t? { allowed: true }\n\t\t\t: { allowed: false, reason: \"not_member\", username: accessUsername };\n\t}\n\n\t/** Admin: grant access to a GitHub username (bypasses org check). */\n\tasync grantAccess(username: string): Promise<void> {\n\t\tconst list = this.getGrantedUsers();\n\t\tconst key = username.toLowerCase();\n\t\tif (!list.includes(key)) {\n\t\t\tlist.push(key);\n\t\t\tawait this.kv.update(KEY_GRANTED_USERS, list);\n\t\t}\n\t}\n\n\t/** Admin: revoke a previously granted access. */\n\tasync revokeAccess(username: string): Promise<void> {\n\t\tconst updated = this.getGrantedUsers().filter((u) => u !== username.toLowerCase());\n\t\tawait this.kv.update(KEY_GRANTED_USERS, updated);\n\t}\n\n\t/** Snapshot of all admin-granted usernames (lowercased). */\n\tgetGrantedUsers(): string[] {\n\t\treturn this.kv.get<string[]>(KEY_GRANTED_USERS) ?? [];\n\t}\n\n\t/** Cached membership lookup with both in-memory + persistent layers. */\n\tasync checkOrgMembership(username: string): Promise<boolean> {\n\t\tconst now = this.opts.now();\n\n\t\t// 1. In-memory cache.\n\t\tconst inMem = this.memCache.get(username);\n\t\tif (inMem && now - inMem.at < this.opts.cacheTtlMs) {\n\t\t\treturn inMem.isMember;\n\t\t}\n\n\t\t// 2. Persistent cache.\n\t\tconst persistent = this.kv.get<PersistentCache>(KEY_MEMBERSHIP_CACHE) ?? {};\n\t\tconst persistentEntry = persistent[username];\n\t\tif (persistentEntry && now - persistentEntry.at < this.opts.persistentCacheTtlMs) {\n\t\t\tthis.memCache.set(username, persistentEntry);\n\t\t\treturn persistentEntry.isMember;\n\t\t}\n\n\t\t// 3. Hit upstream GitHub API via the injected fetcher.\n\t\tconst token = await this.opts.tokenFetcher(\"github\");\n\t\tif (!token) return false;\n\n\t\ttry {\n\t\t\tconst result = await this.opts.orgFetcher(token, this.opts.org);\n\t\t\tconst decision = this.resolveDecision(username, result);\n\t\t\tif (typeof decision === \"boolean\") {\n\t\t\t\tthis.updateCache(username, decision);\n\t\t\t\treturn decision;\n\t\t\t}\n\t\t\t// Indeterminate — fail-open.\n\t\t\treturn true;\n\t\t} catch {\n\t\t\t// Fail-open on transient network errors.\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t/** Server-confirmed inOrg status (set by device claim flow). */\n\tsetServerInOrg(username: string, inOrg: boolean): void {\n\t\tconst key = username.toLowerCase();\n\t\tthis.serverInOrgMem.set(key, inOrg);\n\t\tconst persisted = this.kv.get<Record<string, boolean>>(KEY_SERVER_IN_ORG) ?? {};\n\t\tpersisted[key] = inOrg;\n\t\tvoid this.kv.update(KEY_SERVER_IN_ORG, persisted);\n\t\tthis.notifyListeners();\n\t}\n\n\t/** Inspect server-confirmed inOrg for a username; returns undefined when unknown. */\n\tgetServerInOrg(username: string): boolean | undefined {\n\t\treturn this.serverInOrgMem.get(username.toLowerCase());\n\t}\n\n\tprivate resolveDecision(\n\t\t_username: string,\n\t\tmembership: CoreOrgMembershipResult\n\t): boolean | undefined {\n\t\tif (membership.status === \"active\" || membership.status === \"pending\") return true;\n\t\tif (membership.status === \"not_member\") return false;\n\t\treturn undefined;\n\t}\n\n\tprivate updateCache(username: string, isMember: boolean): void {\n\t\tconst at = this.opts.now();\n\t\tthis.memCache.set(username, { isMember, at });\n\t\tconst persistent = this.kv.get<PersistentCache>(KEY_MEMBERSHIP_CACHE) ?? {};\n\t\tpersistent[username] = { isMember, at };\n\t\tvoid this.kv.update(KEY_MEMBERSHIP_CACHE, persistent);\n\t}\n\n\tprivate notifyListeners(): void {\n\t\tfor (const listener of this.listeners) {\n\t\t\ttry {\n\t\t\t\tlistener();\n\t\t\t} catch {\n\t\t\t\t// listener errors must not propagate; AccessControl is best-effort.\n\t\t\t}\n\t\t}\n\t}\n}\n","export interface ServiceMeLogger {\n\tdebug(message: string, ...args: unknown[]): void;\n\tinfo(message: string, ...args: unknown[]): void;\n\twarn(message: string, ...args: unknown[]): void;\n\terror(message: string, ...args: unknown[]): void;\n}\n\nexport const noopLogger: ServiceMeLogger = {\n\tdebug() {},\n\tinfo() {},\n\twarn() {},\n\terror() {},\n};\n\nfunction formatArgs(args: unknown[]): string {\n\treturn args\n\t\t.map((arg) => {\n\t\t\tif (typeof arg === \"string\") {\n\t\t\t\treturn arg;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\treturn JSON.stringify(arg);\n\t\t\t} catch {\n\t\t\t\treturn String(arg);\n\t\t\t}\n\t\t})\n\t\t.join(\" \");\n}\n\nexport function createConsoleLogger(prefix = \"serviceme\"): ServiceMeLogger {\n\treturn {\n\t\tdebug(message: string, ...args: unknown[]) {\n\t\t\tprocess.stderr.write(`[${prefix}] DEBUG ${message} ${formatArgs(args)}\\n`);\n\t\t},\n\t\tinfo(message: string, ...args: unknown[]) {\n\t\t\tprocess.stderr.write(`[${prefix}] INFO ${message} ${formatArgs(args)}\\n`);\n\t\t},\n\t\twarn(message: string, ...args: unknown[]) {\n\t\t\tprocess.stderr.write(`[${prefix}] WARN ${message} ${formatArgs(args)}\\n`);\n\t\t},\n\t\terror(message: string, ...args: unknown[]) {\n\t\t\tprocess.stderr.write(`[${prefix}] ERROR ${message} ${formatArgs(args)}\\n`);\n\t\t},\n\t};\n}\n","/**\n * AuthStateManager — In-memory mirror of the persisted auth state.\n *\n * Holds the multi-account map (provider + accountId keyed) plus the\n * \"active\" provider/account pair. Emits change events via Node's\n * built-in `events.EventEmitter` so listeners stay decoupled.\n *\n * Important: this class does NOT touch `vscode.SecretStorage` or any\n * file — it only keeps the metadata (`AuthAccountMeta`) and the\n * `activeProvider` selection. Token bytes are looked up via the\n * `KeychainAuthTokenStore` on demand. This separation is what lets\n * `AuthCore` run identically in CLI + Extension.\n *\n * Refs:\n * - 4.功能规划.md §2.1 — `AuthStateManager.ts events.EventEmitter, NO VSCode dep`\n * - ADL-004 — token bytes never enter Core state\n */\n\nimport { EventEmitter } from \"node:events\";\n\nimport type { AuthAccountMeta, AuthProvider, AuthStatus } from \"@serviceme/devtools-protocol\";\n\nexport interface AuthStateManagerEvents {\n\tchange: () => void;\n}\n\nexport interface AuthStateManagerOptions {\n\t/** EventEmitter listener cap; defaults to 32 (Node default) but raised for hot test paths. */\n\tmaxListeners?: number;\n}\n\nexport class AuthStateManager {\n\tprivate readonly emitter = new EventEmitter();\n\tprivate accounts: AuthAccountMeta[] = [];\n\tprivate activeProvider: AuthProvider | null = null;\n\tprivate activeAccountId: string | null = null;\n\tprivate lastError: string | null = null;\n\n\tconstructor(opts: AuthStateManagerOptions = {}) {\n\t\tif (opts.maxListeners !== undefined) {\n\t\t\tthis.emitter.setMaxListeners(opts.maxListeners);\n\t\t}\n\t}\n\n\t/** Subscribe to state-change events. Returns a disposer. */\n\tonDidChange(listener: () => void): () => void {\n\t\tthis.emitter.on(\"change\", listener);\n\t\treturn () => this.emitter.off(\"change\", listener);\n\t}\n\n\t/** Snapshot of all known accounts (immutable copy). */\n\tlistAccounts(): AuthAccountMeta[] {\n\t\treturn [...this.accounts];\n\t}\n\n\t/** Find an account by `(provider, accountId)` tuple; returns `null` when absent. */\n\tfindAccount(provider: AuthProvider, accountId: string): AuthAccountMeta | null {\n\t\treturn this.accounts.find((a) => a.provider === provider && a.id === accountId) ?? null;\n\t}\n\n\t/** First account for the requested provider — used for \"switch to GitHub\" UX. */\n\tfindFirstForProvider(provider: AuthProvider): AuthAccountMeta | null {\n\t\treturn this.accounts.find((a) => a.provider === provider) ?? null;\n\t}\n\n\t/** Insert or update an account entry. New accounts land at the head of the list. */\n\tupsertAccount(meta: AuthAccountMeta): void {\n\t\tconst idx = this.accounts.findIndex((a) => a.provider === meta.provider && a.id === meta.id);\n\t\tif (idx >= 0) {\n\t\t\tthis.accounts[idx] = meta;\n\t\t} else {\n\t\t\tthis.accounts.unshift(meta);\n\t\t}\n\t\tthis.fire();\n\t}\n\n\t/** Remove an account entry. Returns `true` when an entry was removed. */\n\tremoveAccount(provider: AuthProvider, accountId: string): boolean {\n\t\tconst before = this.accounts.length;\n\t\tthis.accounts = this.accounts.filter((a) => !(a.provider === provider && a.id === accountId));\n\t\tconst removed = this.accounts.length !== before;\n\t\t// If we removed the active provider's account, clear the active marker.\n\t\tif (removed && this.activeProvider === provider && this.activeAccountId === accountId) {\n\t\t\tconst stillHasSameProvider = this.accounts.some((a) => a.provider === provider);\n\t\t\tif (!stillHasSameProvider) {\n\t\t\t\tthis.activeProvider = null;\n\t\t\t\tthis.activeAccountId = null;\n\t\t\t} else {\n\t\t\t\tconst next = this.accounts.find((a) => a.provider === provider);\n\t\t\t\tif (next) {\n\t\t\t\t\tthis.activeAccountId = next.id;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (removed) this.fire();\n\t\treturn removed;\n\t}\n\n\t/** Set the active provider. When `accountId` is omitted, picks the first account for that provider. */\n\tsetActive(provider: AuthProvider, accountId?: string): boolean {\n\t\tconst match =\n\t\t\taccountId !== undefined\n\t\t\t\t? this.accounts.find((a) => a.provider === provider && a.id === accountId)\n\t\t\t\t: this.accounts.find((a) => a.provider === provider);\n\t\tif (!match) return false;\n\t\tthis.activeProvider = provider;\n\t\tthis.activeAccountId = match.id;\n\t\tthis.fire();\n\t\treturn true;\n\t}\n\n\t/** Currently active provider — `null` when no session is active. */\n\tgetActiveProvider(): AuthProvider | null {\n\t\treturn this.activeProvider;\n\t}\n\n\t/** Currently active account metadata — `null` when none. */\n\tgetActiveAccount(): AuthAccountMeta | null {\n\t\tif (!this.activeProvider || !this.activeAccountId) return null;\n\t\treturn (\n\t\t\tthis.accounts.find(\n\t\t\t\t(a) => a.provider === this.activeProvider && a.id === this.activeAccountId\n\t\t\t) ?? null\n\t\t);\n\t}\n\n\t/** True when at least one provider has an account entry. */\n\thasAnySession(): boolean {\n\t\treturn this.accounts.length > 0 && this.activeProvider !== null;\n\t}\n\n\t/** Snapshot of the state for `auth.status` and bridge serialization. */\n\tgetStatus(): AuthStatus {\n\t\tconst grouped: Partial<Record<AuthProvider, AuthAccountMeta>> = {};\n\t\tfor (const account of this.accounts) {\n\t\t\tgrouped[account.provider] = account;\n\t\t}\n\t\treturn {\n\t\t\tactiveProvider: this.activeProvider,\n\t\t\taccounts: grouped,\n\t\t\thasAnySession: this.hasAnySession(),\n\t\t\tlastError: this.lastError ?? undefined,\n\t\t};\n\t}\n\n\t/** Record a non-fatal error from the last login/refresh attempt. */\n\trecordError(message: string): void {\n\t\tthis.lastError = message;\n\t\tthis.fire();\n\t}\n\n\t/** Clear the recorded error (e.g. after a successful login). */\n\tclearError(): void {\n\t\tif (this.lastError === null) return;\n\t\tthis.lastError = null;\n\t\tthis.fire();\n\t}\n\n\t/** Drop every account — used by `auth.logout` with no provider. */\n\tclearAll(): void {\n\t\tthis.accounts = [];\n\t\tthis.activeProvider = null;\n\t\tthis.activeAccountId = null;\n\t\tthis.lastError = null;\n\t\tthis.fire();\n\t}\n\n\tprivate fire(): void {\n\t\tthis.emitter.emit(\"change\");\n\t}\n}\n","/**\n * ProviderRegistry — Maps `AuthProvider` ids to `IAuthProvider` instances.\n *\n * `AuthCore` looks up providers by `AuthProvider` enum (string union\n * per the protocol). The registry is mutable so that callers can\n * replace a provider (e.g. swap a real keyring-backed implementation\n * in tests), but providers must be registered before `AuthCore.login()`\n * is called.\n *\n * Refs:\n * - 4.功能规划.md §2.1 — `ProviderRegistry.ts`\n */\n\nimport type { AuthProvider } from \"@serviceme/devtools-protocol\";\n\nimport type { IAuthProvider } from \"./providers/IAuthProvider\";\n\nexport class ProviderRegistry {\n\tprivate readonly providers = new Map<AuthProvider, IAuthProvider>();\n\n\t/** Register or replace a provider implementation. */\n\tregister(provider: IAuthProvider): void {\n\t\tthis.providers.set(provider.providerId, provider);\n\t}\n\n\t/** Look up a registered provider by id; throws when missing. */\n\tget(providerId: AuthProvider): IAuthProvider {\n\t\tconst p = this.providers.get(providerId);\n\t\tif (!p) {\n\t\t\tthrow new Error(`Auth provider not registered: ${providerId}`);\n\t\t}\n\t\treturn p;\n\t}\n\n\t/** Non-throwing lookup; returns `undefined` when the provider is unknown. */\n\ttryGet(providerId: AuthProvider): IAuthProvider | undefined {\n\t\treturn this.providers.get(providerId);\n\t}\n\n\t/** Return all registered provider ids — used by `auth.status` and bridge capability hints. */\n\tlist(): AuthProvider[] {\n\t\treturn [...this.providers.keys()];\n\t}\n\n\t/** True when a provider has been registered for this id. */\n\thas(providerId: AuthProvider): boolean {\n\t\treturn this.providers.has(providerId);\n\t}\n}\n","/**\n * AuthCore — Main entry for the auth domain.\n *\n * Owns the `ProviderRegistry` + `AuthStateManager` + `KeychainAuthTokenStore`.\n * Provides a small, side-effectful surface that CLI / Extension / Bridge\n * handlers can call:\n *\n * - `login(provider)` → drives Device Flow, persists token via store.\n * - `logout(provider?)` → clears the active session (and token bytes).\n * - `status()` → snapshot for `auth.status`.\n * - `whoami()` → minimal identity for `auth.whoami`.\n * - `switchProvider(p)` → flip the active provider (multi-account).\n *\n * Core NEVER holds token bytes past the `KeychainAuthTokenStore.set()`\n * boundary — callers pass the token to the store immediately, then\n * drop the local reference. Per ADL-004 the token bytes only live in\n * (a) SecretStorage / (b) keyring / (c) HTTP Authorization header.\n *\n * Refs:\n * - 4.功能规划.md §2.1 — `AuthCore.ts 主入口`\n * - ADL-002 — Device Flow OAuth\n * - ADL-004 — Auth token storage\n */\n\nimport type {\n\tAuthAccountMeta,\n\tAuthLoginResult,\n\tAuthProvider,\n\tAuthStatus,\n\tAuthSwitchResult,\n\tAuthWhoamiResult,\n} from \"@serviceme/devtools-protocol\";\nimport { noopLogger, type ServiceMeLogger } from \"../logger\";\nimport type { AccessCheckResult, AccessControl } from \"./AccessControl\";\nimport { AuthStateManager } from \"./AuthStateManager\";\nimport type { KeychainAuthTokenStore } from \"./KeychainAuthTokenStore\";\nimport { ProviderRegistry } from \"./ProviderRegistry\";\nimport type { IAuthProvider, IAuthProviderSession } from \"./providers/IAuthProvider\";\n\nexport interface AuthCoreOptions {\n\tproviders: IAuthProvider[];\n\ttokenStore: KeychainAuthTokenStore;\n\taccessControl?: AccessControl;\n\tstateManager?: AuthStateManager;\n\t/** Diagnostic logger for login/logout lifecycle events (never receives token bytes). */\n\tlogger?: ServiceMeLogger;\n}\n\n/**\n * Callback invoked once the user finishes the device-flow handshake but\n * BEFORE the token is written to the keychain. Lets the caller display\n * the `userCode` + `verificationUrl` to the user (Phase 5.3 CLI prints to\n * stdout; Phase 5.5 Extension pops a webview notification).\n */\nexport type DeviceFlowUiCallback = (result: AuthLoginResult) => void | Promise<void>;\n\n/**\n * Optional cancellation handle — when `shouldContinue()` returns false,\n * the polling loop exits with a clear error so callers can render\n * \"Login cancelled\" UX.\n */\nexport type CancellationCheck = () => boolean;\n\nexport class AuthCore {\n\tprivate readonly registry: ProviderRegistry;\n\tprivate readonly state: AuthStateManager;\n\tprivate readonly tokenStore: KeychainAuthTokenStore;\n\tprivate readonly accessControl?: AccessControl;\n\tprivate readonly logger: ServiceMeLogger;\n\n\tconstructor(opts: AuthCoreOptions) {\n\t\tthis.registry = new ProviderRegistry();\n\t\tfor (const provider of opts.providers) {\n\t\t\tthis.registry.register(provider);\n\t\t}\n\t\tthis.state = opts.stateManager ?? new AuthStateManager();\n\t\tthis.tokenStore = opts.tokenStore;\n\t\tthis.accessControl = opts.accessControl;\n\t\tthis.logger = opts.logger ?? noopLogger;\n\t}\n\n\t/** Snapshot of every account, the active provider, and the last error. */\n\tstatus(): AuthStatus {\n\t\treturn this.state.getStatus();\n\t}\n\n\t/** List accounts (immutable copy). */\n\tlistAccounts(): AuthAccountMeta[] {\n\t\treturn this.state.listAccounts();\n\t}\n\n\t/**\n\t * Drive the device-flow login for `provider`.\n\t *\n\t * Sequence:\n\t * 1. Ask the provider for the user code + verification URL.\n\t * 2. Surface the code to the user (via `ui`).\n\t * 3. Poll until the user authorizes (or `shouldContinue` aborts).\n\t * 4. Persist the token via `KeychainAuthTokenStore.set()`.\n\t * 5. Insert the resulting `AuthAccountMeta` into state and mark active.\n\t */\n\tasync login(\n\t\tprovider: AuthProvider,\n\t\tui: DeviceFlowUiCallback,\n\t\tshouldContinue?: CancellationCheck\n\t): Promise<AuthLoginResult> {\n\t\tconst providerImpl = this.registry.get(provider);\n\t\tthis.logger.info(\"[AuthCore] Starting device-flow login\", { provider });\n\t\ttry {\n\t\t\tconst initial = await providerImpl.requestDeviceFlow();\n\t\t\tawait ui(initial);\n\t\t\tif (!initial.deviceCode) {\n\t\t\t\tthis.logger.error(\"[AuthCore] Provider did not return deviceCode\", provider);\n\t\t\t\tthrow new Error(\"Auth provider did not return deviceCode for device flow completion\");\n\t\t\t}\n\t\t\tconst session = await providerImpl.completeDeviceFlow(\n\t\t\t\tinitial.deviceCode,\n\t\t\t\tshouldContinue,\n\t\t\t\tinitial.pollIntervalMs\n\t\t\t);\n\t\t\tconst result = await this.persistSession(providerImpl, session);\n\t\t\tthis.logger.info(\"[AuthCore] Device-flow login completed\", { provider });\n\t\t\treturn result;\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tthis.logger.error(\"[AuthCore] Device-flow login failed\", message, {\n\t\t\t\tprovider,\n\t\t\t});\n\t\t\tthis.state.recordError(message);\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/**\n\t * Complete the device-flow handshake given a pre-fetched user code.\n\t * Useful when the caller (Phase 5.3 CLI) wants to fetch the code,\n\t * print the URL, and then poll on a subsequent invocation.\n\t */\n\tasync completeLogin(\n\t\tprovider: AuthProvider,\n\t\tdeviceCode: string,\n\t\tshouldContinue?: CancellationCheck\n\t): Promise<AuthLoginResult> {\n\t\tconst providerImpl = this.registry.get(provider);\n\t\ttry {\n\t\t\tconst session = await providerImpl.completeDeviceFlow(deviceCode, shouldContinue);\n\t\t\treturn await this.persistSession(providerImpl, session);\n\t\t} catch (err) {\n\t\t\tthis.state.recordError(err instanceof Error ? err.message : String(err));\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/** Resolve the provider + token for the active session; returns null when no session is active. */\n\tasync resolveActiveToken(): Promise<{\n\t\tprovider: AuthProvider;\n\t\taccount: AuthAccountMeta;\n\t\ttoken: string;\n\t} | null> {\n\t\tconst provider = this.state.getActiveProvider();\n\t\tif (!provider) return null;\n\t\tconst account = this.state.getActiveAccount();\n\t\tif (!account) return null;\n\t\tconst envelope = await this.tokenStore.get({\n\t\t\tprovider,\n\t\t\taccountId: account.id,\n\t\t});\n\t\tif (!envelope) return null;\n\t\treturn { provider, account, token: envelope.token };\n\t}\n\n\t/**\n\t * Logout: removes the token bytes from the keychain + drops the\n\t * account entry from state. When `provider` is omitted, clears\n\t * every account.\n\t */\n\tasync logout(\n\t\tprovider?: AuthProvider\n\t): Promise<{ provider: AuthProvider | null; success: boolean }> {\n\t\tthis.logger.info(\"[AuthCore] Logout requested\", {\n\t\t\tprovider: provider ?? \"all\",\n\t\t});\n\t\tif (!provider) {\n\t\t\t// Clear every account's token bytes + metadata.\n\t\t\tfor (const account of this.state.listAccounts()) {\n\t\t\t\tawait this.tokenStore.delete({\n\t\t\t\t\tprovider: account.provider,\n\t\t\t\t\taccountId: account.id,\n\t\t\t\t});\n\t\t\t\tthis.state.removeAccount(account.provider, account.id);\n\t\t\t}\n\t\t\tthis.state.clearAll();\n\t\t\treturn { provider: null, success: true };\n\t\t}\n\n\t\tconst accountsForProvider = this.state.listAccounts().filter((a) => a.provider === provider);\n\t\tlet removed = false;\n\t\tfor (const account of accountsForProvider) {\n\t\t\tawait this.tokenStore.delete({ provider, accountId: account.id });\n\t\t\tconst didRemove = this.state.removeAccount(provider, account.id);\n\t\t\tremoved = removed || didRemove;\n\t\t}\n\t\tif (this.state.getActiveProvider() === provider) {\n\t\t\t// Reset active if we just removed the active provider's account.\n\t\t\tconst firstRemaining = this.state.listAccounts()[0];\n\t\t\tif (firstRemaining) {\n\t\t\t\tthis.state.setActive(firstRemaining.provider);\n\t\t\t}\n\t\t}\n\t\treturn { provider, success: removed };\n\t}\n\n\t/** Minimal identity for `auth.whoami`. */\n\tasync whoami(): Promise<AuthWhoamiResult> {\n\t\tconst provider = this.state.getActiveProvider();\n\t\tconst account = this.state.getActiveAccount();\n\t\tif (!provider || !account) {\n\t\t\treturn { provider: null };\n\t\t}\n\t\treturn {\n\t\t\tprovider,\n\t\t\tlogin: account.login,\n\t\t\tname: account.displayName,\n\t\t\tavatarUrl: account.avatarUrl,\n\t\t\temail: account.email,\n\t\t};\n\t}\n\n\t/** Switch the active provider. Returns the resulting active account. */\n\tswitchProvider(provider: AuthProvider, accountId?: string): AuthSwitchResult {\n\t\tconst ok = this.state.setActive(provider, accountId);\n\t\tconst account = this.state.getActiveAccount();\n\t\treturn {\n\t\t\tactiveProvider: ok ? provider : this.state.getActiveProvider(),\n\t\t\taccount,\n\t\t};\n\t}\n\n\t/** Run the access-control check against the active account (optional, requires AccessControl). */\n\tasync checkAccess(): Promise<AccessCheckResult | null> {\n\t\tif (!this.accessControl) return null;\n\t\tconst account = this.state.getActiveAccount();\n\t\treturn this.accessControl.checkAccess(account);\n\t}\n\n\t/** Expose the state manager for test inspection (not for mutation). */\n\tgetStateManager(): AuthStateManager {\n\t\treturn this.state;\n\t}\n\n\t/** Expose the provider registry for test inspection. */\n\tgetProviderRegistry(): ProviderRegistry {\n\t\treturn this.registry;\n\t}\n\n\t/** Expose the access control (or undefined when not configured). */\n\tgetAccessControl(): AccessControl | undefined {\n\t\treturn this.accessControl;\n\t}\n\n\tprivate async persistSession(\n\t\tproviderImpl: IAuthProvider,\n\t\tsession: IAuthProviderSession\n\t): Promise<AuthLoginResult> {\n\t\tconst expiresAt = session.expiresIn ? Date.now() + session.expiresIn * 1000 : null;\n\t\tconst meta = await providerImpl.fetchAccountMeta(session.token);\n\t\tconst accountMeta: AuthAccountMeta = {\n\t\t\tid: meta.id,\n\t\t\tprovider: meta.provider,\n\t\t\tdisplayName: meta.displayName,\n\t\t\tlogin: meta.login,\n\t\t\temail: meta.email,\n\t\t\tavatarUrl: meta.avatarUrl,\n\t\t\texpiresAt: meta.expiresAt ?? expiresAt,\n\t\t};\n\t\tthis.logger.debug(\"[AuthCore] Persisting session\", {\n\t\t\tprovider: meta.provider,\n\t\t\taccountId: meta.id,\n\t\t});\n\n\t\tawait this.tokenStore.set({ provider: meta.provider, accountId: meta.id }, session.token, {\n\t\t\texpiresAt,\n\t\t});\n\t\tthis.state.upsertAccount(accountMeta);\n\t\tthis.state.setActive(meta.provider, meta.id);\n\t\tthis.state.clearError();\n\t\treturn {\n\t\t\tprovider: meta.provider,\n\t\t\tmessage: `Logged in as ${meta.login ?? meta.id}`,\n\t\t};\n\t}\n}\n","/**\n * KeychainAuthTokenStore — abstract adapter for token byte persistence.\n *\n * Per ADL-004 the OAuth token bytes live in one of three places:\n * (a) VSCode `SecretStorage` (Extension process)\n * (b) `@napi-rs/keyring` Entry (CLI process)\n * (c) HTTP request `Authorization` header\n *\n * `AuthCore` MUST NOT call any of these directly. Instead it receives\n * a `KeychainAuthTokenStore` via DI; CLI provides a `@napi-rs/keyring`\n * adapter, Extension provides a `SecretStorage` adapter. This keeps\n * Core free of native-binding concerns and lets the same business\n * logic power both runtimes.\n *\n * Implementations:\n * - CLI: `apps/serviceme-cli/src/.../KeyringTokenStore.ts` (Phase 5.3)\n * - Extension: `apps/extension/src/.../SecretStorageTokenStore.ts` (Phase 5.5)\n *\n * Failure semantics — `get()` returns `null` when no token is stored\n * (cold-start), throws when the underlying keychain is unavailable\n * (rare on Linux without libsecret). Callers should surface the throw\n * as `AUTH_KEYRING_UNAVAILABLE` rather than silently falling back to\n * in-memory storage (per ADL-004 §不变量).\n *\n * Refs:\n * - ADL-004 — CLI 端 Auth Token 存储选型\n * - 4.功能规划.md §2.1 — \"Core MUST NOT directly depend on `@napi-rs/keyring`\"\n */\n\n/** Opaque account identifier (provider + upstream user id). */\nexport interface KeychainAccountKey {\n\tprovider: string;\n\taccountId: string;\n}\n\n/** Non-secret metadata returned alongside a `get()` so callers can audit. */\nexport interface KeychainTokenMetadata {\n\tprovider: string;\n\taccountId: string;\n\texpiresAt?: number | null;\n\tstoredAt?: number;\n}\n\n/**\n * Result envelope — callers receive the token bytes (for immediate use\n * in an HTTP `Authorization` header) plus optional non-secret metadata.\n *\n * IMPORTANT: the `token` field is plain `string` here, not the\n * `SecretToken` brand. The provider -> store -> HTTP-header pipeline\n * runs inside a single trust boundary; crossing that boundary requires\n * `KeychainTokenEnvelope<SecretToken>` and the `auth.tokenRead`\n * capability gate (see ADL-004 §不变量).\n */\nexport interface KeychainTokenEnvelope {\n\ttoken: string;\n\tmetadata: KeychainTokenMetadata;\n}\n\n/**\n * Abstract token-storage adapter. All methods are async because the\n * keyring / SecretStorage backends are async-by-nature.\n *\n * Thread-safety: implementations MUST be safe for concurrent calls;\n * `AuthCore` will multiplex over multiple providers on the same\n * runtime.\n */\nexport interface KeychainAuthTokenStore {\n\t/** Persist token bytes for the given provider + account pair. */\n\tset(key: KeychainAccountKey, token: string, opts?: { expiresAt?: number | null }): Promise<void>;\n\n\t/** Fetch the token bytes; returns `null` when none is stored. */\n\tget(key: KeychainAccountKey): Promise<KeychainTokenEnvelope | null>;\n\n\t/** Remove the token entry. Idempotent — removing a missing entry is not an error. */\n\tdelete(key: KeychainAccountKey): Promise<void>;\n\n\t/**\n\t * List all stored token keys (metadata only — never the token bytes).\n\t * Useful for multi-account enumeration and bridge `auth.status`.\n\t */\n\tlist(): Promise<KeychainAccountKey[]>;\n\n\t/**\n\t * Health probe — returns `false` when the keychain is unreachable\n\t * (e.g. Linux without libsecret). Callers SHOULD probe on first\n\t * use and surface `AUTH_KEYRING_UNAVAILABLE` rather than degrade.\n\t */\n\tisAvailable(): Promise<boolean>;\n}\n\n/**\n * Sentinel error thrown by `KeychainAuthTokenStore.get()` / `.set()`\n * when the underlying keychain is unavailable. Callers map this to\n * `AUTH_KEYRING_UNAVAILABLE` (Phase 5.3 error code).\n */\nexport class KeychainUnavailableError extends Error {\n\tconstructor(message: string, cause?: unknown) {\n\t\t// Use the native ES2022 `Error` two-arg form instead of redeclaring\n\t\t// `cause` as a class field — a field redeclaration shadows the\n\t\t// inherited member and requires an `override` modifier under\n\t\t// `noImplicitOverride` (downstream consumers like the Extension),\n\t\t// which in turn fails THIS package's own build if its `lib` doesn't\n\t\t// also include `cause` on `Error`. Relying on the built-in\n\t\t// constructor option avoids the mismatch entirely (this package's\n\t\t// `tsconfig.json` targets `lib: [\"ES2022\", \"DOM\"]`, so `cause` is\n\t\t// recognized here too).\n\t\tsuper(message, cause !== undefined ? { cause } : undefined);\n\t\tthis.name = \"KeychainUnavailableError\";\n\t}\n}\n\n/**\n * In-memory `KeychainAuthTokenStore` — test/dev fallback. NEVER use\n * in production: tokens live in process memory and disappear on exit.\n * Production wiring is `KeyringTokenStore` (CLI) / `SecretStorageTokenStore`\n * (Extension).\n */\nexport class InMemoryKeychainAuthTokenStore implements KeychainAuthTokenStore {\n\tprivate readonly entries = new Map<\n\t\tstring,\n\t\t{ token: string; expiresAt: number | null; storedAt: number }\n\t>();\n\n\tprivate compositeKey(key: KeychainAccountKey): string {\n\t\treturn `${key.provider}:${key.accountId}`;\n\t}\n\n\tasync set(\n\t\tkey: KeychainAccountKey,\n\t\ttoken: string,\n\t\topts?: { expiresAt?: number | null }\n\t): Promise<void> {\n\t\tthis.entries.set(this.compositeKey(key), {\n\t\t\ttoken,\n\t\t\texpiresAt: opts?.expiresAt ?? null,\n\t\t\tstoredAt: Date.now(),\n\t\t});\n\t}\n\n\tasync get(key: KeychainAccountKey): Promise<KeychainTokenEnvelope | null> {\n\t\tconst entry = this.entries.get(this.compositeKey(key));\n\t\tif (!entry) return null;\n\t\treturn {\n\t\t\ttoken: entry.token,\n\t\t\tmetadata: {\n\t\t\t\tprovider: key.provider,\n\t\t\t\taccountId: key.accountId,\n\t\t\t\texpiresAt: entry.expiresAt,\n\t\t\t\tstoredAt: entry.storedAt,\n\t\t\t},\n\t\t};\n\t}\n\n\tasync delete(key: KeychainAccountKey): Promise<void> {\n\t\tthis.entries.delete(this.compositeKey(key));\n\t}\n\n\tasync list(): Promise<KeychainAccountKey[]> {\n\t\tconst out: KeychainAccountKey[] = [];\n\t\tfor (const composite of this.entries.keys()) {\n\t\t\tconst sepIdx = composite.indexOf(\":\");\n\t\t\tif (sepIdx <= 0) continue;\n\t\t\tout.push({\n\t\t\t\tprovider: composite.slice(0, sepIdx),\n\t\t\t\taccountId: composite.slice(sepIdx + 1),\n\t\t\t});\n\t\t}\n\t\treturn out;\n\t}\n\n\tasync isAvailable(): Promise<boolean> {\n\t\treturn true;\n\t}\n}\n","/**\n * AuthCore — Pure local utilities for GitHub local email handling.\n *\n * Copied verbatim from `@serviceme/devtools-shared/src/github-user-email.ts` (15 LOC)\n * because ADL-003 forbids Core from depending on `@serviceme/devtools-shared`. These\n * helpers are pure functions with zero side effects, so the duplication is\n * trivial to keep in sync.\n *\n * Refs:\n * - 4.功能规划.md §2.1 — \"utils/githubUserEmail.ts 纯函数,直接搬\"\n * - ADL-003 — `@serviceme/devtools-shared` boundary decision\n */\n\n/**\n * Returns true when the supplied address is a synthetic GitHub \"local\" email\n * (suffixed with `@github.local`). Real GitHub OAuth clients often return\n * `null` for `email` (user kept it private) and the application substitutes\n * `<login>@github.local` to keep the field non-null.\n */\nexport function isGitHubLocalEmail(email: string | null | undefined): boolean {\n\treturn typeof email === \"string\" && email.trim().toLowerCase().endsWith(\"@github.local\");\n}\n\n/** Build a synthetic `<login>@github.local` address. */\nexport function buildGitHubLocalEmail(login: string): string {\n\treturn `${login}@github.local`;\n}\n\n/**\n * Resolve the user's primary email address — falling back to the synthetic\n * `<login>@github.local` form when the upstream email is missing or blank.\n */\nexport function resolvePrimaryEmail(login: string, email: string | null | undefined): string {\n\tconst normalizedEmail = email?.trim();\n\treturn normalizedEmail || buildGitHubLocalEmail(login);\n}\n","/**\n * GitHubAuthProvider — Device Flow OAuth implementation.\n *\n * Implements the GitHub OAuth Device Flow per the official spec\n * (https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#device-flow).\n *\n * Per ADL-002 the device flow is the locked authentication strategy for\n * SERVICEME — no localhost callback server, no PAT. This class is a\n * pure HTTP client (uses native `fetch`, no vscode dependency) that\n * returns the token bytes for the caller's `KeychainAuthTokenStore` to\n * persist immediately.\n *\n * Token bytes NEVER enter logs / errors / debug output. The provider\n * exposes only the user-visible code + verification URL during the\n * device-flow handshake.\n *\n * Refs:\n * - ADL-002 — Device Flow OAuth (locked)\n * - 4.功能规划.md §2.1 — `providers/GitHubAuthProvider.ts`\n */\n\nimport type { AuthAccountMeta, AuthLoginResult, AuthProvider } from \"@serviceme/devtools-protocol\";\nimport { noopLogger, type ServiceMeLogger } from \"../../logger\";\nimport { buildGitHubLocalEmail } from \"../utils/githubUserEmail\";\nimport type { IAuthProvider, IAuthProviderSession } from \"./IAuthProvider\";\n\nconst DEFAULT_DEVICE_CODE_URL = \"https://github.com/login/device/code\";\nconst DEFAULT_TOKEN_URL = \"https://github.com/login/oauth/access_token\";\nconst DEFAULT_USER_URL = \"https://api.github.com/user\";\nconst DEFAULT_SCOPE = \"read:user user:email\";\n\n/** Attempts for the one-shot device-code request before surfacing a network error. */\nconst DEVICE_CODE_MAX_ATTEMPTS = 3;\nconst DEFAULT_DEVICE_CODE_RETRY_BASE_DELAY_MS = 500;\n\n/**\n * `fetch` (both browser and Node's undici) rejects with a `TypeError`\n * (e.g. \"fetch failed\") when the underlying connection never completes —\n * DNS failure, TLS handshake reset, ECONNRESET, etc. These are transient\n * network hiccups, distinct from a well-formed non-2xx HTTP response, and\n * should be retried rather than immediately failing the whole login.\n */\nfunction isTransientNetworkError(error: unknown): boolean {\n\treturn error instanceof TypeError;\n}\n\n/** Tunable provider config — exposed for tests + forks. */\nexport interface GitHubAuthProviderConfig {\n\tclientId: string;\n\tdeviceCodeUrl?: string;\n\ttokenUrl?: string;\n\tuserUrl?: string;\n\tscope?: string;\n\t/** Polling interval (ms) floor. The device-flow response's `interval` wins when larger. */\n\tminPollIntervalMs?: number;\n\t/**\n\t * Maximum poll interval after back-off. Must stay generous — GitHub's\n\t * `slow_down` response requires adding a fixed step to the interval each\n\t * time it occurs (see `completeDeviceFlow`), and capping this too low\n\t * prevents the interval from ever satisfying GitHub's actual requirement,\n\t * causing a permanent `slow_down` loop that never clears even after the\n\t * user authorizes.\n\t */\n\tmaxPollIntervalMs?: number;\n\t/** Fetch override (for tests + DI). */\n\tfetchImpl?: typeof fetch;\n\t/** Poll-delay override (for tests + DI). Defaults to a real timer-based sleep. */\n\tsleepImpl?: (ms: number) => Promise<void>;\n\t/** Maximum wall-clock time to wait for the user before giving up (ms). Defaults to `expires_in * 1000`. */\n\tmaxWaitMs?: number;\n\t/** Base delay (ms) before retrying the device-code request after a transient network error. */\n\tdeviceCodeRetryBaseDelayMs?: number;\n\t/**\n\t * Diagnostic logger — never receives token bytes or the raw `device_code`,\n\t * only user-facing codes, HTTP statuses, and poll-loop timing so callers\n\t * can troubleshoot a stuck / slow device-flow login.\n\t */\n\tlogger?: ServiceMeLogger;\n}\n\ninterface DeviceCodeResponse {\n\tdevice_code: string;\n\tuser_code: string;\n\tverification_uri: string;\n\texpires_in: number;\n\tinterval: number;\n}\n\ninterface TokenPollResponse {\n\taccess_token?: string;\n\trefresh_token?: string;\n\texpires_in?: number;\n\ttoken_type?: string;\n\tscope?: string;\n\terror?: string;\n\terror_description?: string;\n\terror_uri?: string;\n}\n\ninterface GitHubUserResponse {\n\tid: number;\n\tlogin: string;\n\tname?: string | null;\n\temail?: string | null;\n\tavatar_url?: string | null;\n}\n\nexport class GitHubAuthProvider implements IAuthProvider {\n\treadonly providerId: AuthProvider = \"github\";\n\tprivate readonly cfg: Required<\n\t\tOmit<GitHubAuthProviderConfig, \"maxWaitMs\" | \"fetchImpl\" | \"logger\" | \"sleepImpl\">\n\t> & {\n\t\tmaxWaitMs?: number;\n\t\tfetchImpl: typeof fetch;\n\t};\n\tprivate readonly logger: ServiceMeLogger;\n\tprivate readonly sleepImpl: (ms: number) => Promise<void>;\n\n\tconstructor(config: GitHubAuthProviderConfig) {\n\t\tif (!config.clientId || config.clientId.length === 0) {\n\t\t\tthrow new Error(\"GitHubAuthProvider requires a non-empty `clientId`\");\n\t\t}\n\t\tthis.cfg = {\n\t\t\tclientId: config.clientId,\n\t\t\tdeviceCodeUrl: config.deviceCodeUrl ?? DEFAULT_DEVICE_CODE_URL,\n\t\t\ttokenUrl: config.tokenUrl ?? DEFAULT_TOKEN_URL,\n\t\t\tuserUrl: config.userUrl ?? DEFAULT_USER_URL,\n\t\t\tscope: config.scope ?? DEFAULT_SCOPE,\n\t\t\tminPollIntervalMs: config.minPollIntervalMs ?? 1000,\n\t\t\tmaxPollIntervalMs: config.maxPollIntervalMs ?? 60000,\n\t\t\tmaxWaitMs: config.maxWaitMs,\n\t\t\tfetchImpl: config.fetchImpl ?? fetch,\n\t\t\tdeviceCodeRetryBaseDelayMs:\n\t\t\t\tconfig.deviceCodeRetryBaseDelayMs ?? DEFAULT_DEVICE_CODE_RETRY_BASE_DELAY_MS,\n\t\t};\n\t\tthis.logger = config.logger ?? noopLogger;\n\t\tthis.sleepImpl = config.sleepImpl ?? sleep;\n\t}\n\n\tasync requestDeviceFlow(opts?: { scope?: string }): Promise<AuthLoginResult> {\n\t\tconst body = JSON.stringify({\n\t\t\tclient_id: this.cfg.clientId,\n\t\t\tscope: opts?.scope ?? this.cfg.scope,\n\t\t});\n\n\t\tlet lastNetworkError: unknown;\n\t\tfor (let attempt = 1; attempt <= DEVICE_CODE_MAX_ATTEMPTS; attempt++) {\n\t\t\tthis.logger.debug(\"[GitHubAuthProvider] Requesting device code\", {\n\t\t\t\tattempt,\n\t\t\t\tmaxAttempts: DEVICE_CODE_MAX_ATTEMPTS,\n\t\t\t});\n\t\t\tlet resp: Response;\n\t\t\ttry {\n\t\t\t\tresp = await this.cfg.fetchImpl(this.cfg.deviceCodeUrl, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\"User-Agent\": \"serviceme-core\",\n\t\t\t\t\t},\n\t\t\t\t\tbody,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tif (!isTransientNetworkError(error) || attempt === DEVICE_CODE_MAX_ATTEMPTS) {\n\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\"[GitHubAuthProvider] Device-code request failed (non-retryable)\",\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t{ attempt }\n\t\t\t\t\t);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tconst delayMs = this.cfg.deviceCodeRetryBaseDelayMs * 2 ** (attempt - 1);\n\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\"[GitHubAuthProvider] Transient network error requesting device code, retrying\",\n\t\t\t\t\t{\n\t\t\t\t\t\tattempt,\n\t\t\t\t\t\tdelayMs,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t\tlastNetworkError = error;\n\t\t\t\tawait this.sleepImpl(delayMs);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!resp.ok) {\n\t\t\t\tthis.logger.error(\n\t\t\t\t\t\"[GitHubAuthProvider] Device-code request rejected by GitHub\",\n\t\t\t\t\t`HTTP ${resp.status}`\n\t\t\t\t);\n\t\t\t\tthrow new Error(`GitHub device-code request failed: HTTP ${resp.status}`);\n\t\t\t}\n\t\t\tconst data = (await resp.json()) as DeviceCodeResponse;\n\t\t\tif (!data.device_code || !data.user_code || !data.verification_uri) {\n\t\t\t\tthis.logger.error(\n\t\t\t\t\t\"[GitHubAuthProvider] Device-code response missing required fields\",\n\t\t\t\t\tJSON.stringify(Object.keys(data))\n\t\t\t\t);\n\t\t\t\tthrow new Error(\"GitHub device-code response missing required fields\");\n\t\t\t}\n\t\t\tthis.logger.info(\"[GitHubAuthProvider] Device code obtained\", {\n\t\t\t\tuserCode: data.user_code,\n\t\t\t\tverificationUri: data.verification_uri,\n\t\t\t\texpiresInSec: data.expires_in,\n\t\t\t\tpollIntervalSec: data.interval,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tprovider: this.providerId,\n\t\t\t\tdeviceCode: data.device_code,\n\t\t\t\tuserCode: data.user_code,\n\t\t\t\tverificationUrl: data.verification_uri,\n\t\t\t\texpiresAt: Date.now() + data.expires_in * 1000,\n\t\t\t\tpollIntervalMs: data.interval * 1000,\n\t\t\t\tmessage: `Open ${data.verification_uri} and enter ${data.user_code}`,\n\t\t\t};\n\t\t}\n\t\t// Unreachable in practice — the loop always returns or throws — but keeps\n\t\t// the type checker happy and surfaces the last transient error if hit.\n\t\tthrow lastNetworkError ?? new Error(\"GitHub device-code request failed\");\n\t}\n\n\tasync completeDeviceFlow(\n\t\tdeviceCode: string,\n\t\tshouldContinue?: () => boolean,\n\t\tinitialPollIntervalMs?: number\n\t): Promise<IAuthProviderSession> {\n\t\tconst start = Date.now();\n\t\t// Honor GitHub's declared `interval` (from the matching `requestDeviceFlow()`\n\t\t// response) as the starting rate — polling faster than this can put the\n\t\t// device code into a `slow_down` state that never clears (see class docs).\n\t\tlet pollIntervalMs = Math.max(this.cfg.minPollIntervalMs, initialPollIntervalMs ?? 0);\n\t\tlet consecutiveSlowDown = 0;\n\t\tlet pollCount = 0;\n\t\tthis.logger.info(\"[GitHubAuthProvider] Starting device-flow polling\", {\n\t\t\tinitialPollIntervalMs: pollIntervalMs,\n\t\t\tmaxWaitMs: this.cfg.maxWaitMs,\n\t\t});\n\n\t\twhile (true) {\n\t\t\tif (shouldContinue && !shouldContinue()) {\n\t\t\t\tthis.logger.info(\"[GitHubAuthProvider] Device flow cancelled by caller\", {\n\t\t\t\t\telapsedMs: Date.now() - start,\n\t\t\t\t\tpollCount,\n\t\t\t\t});\n\t\t\t\tthrow new Error(\"GitHub device flow cancelled by caller\");\n\t\t\t}\n\t\t\tconst elapsed = Date.now() - start;\n\t\t\tif (this.cfg.maxWaitMs !== undefined && elapsed > this.cfg.maxWaitMs) {\n\t\t\t\tthis.logger.warn(\"[GitHubAuthProvider] Device flow exceeded max wait time\", {\n\t\t\t\t\telapsedMs: elapsed,\n\t\t\t\t\tmaxWaitMs: this.cfg.maxWaitMs,\n\t\t\t\t\tpollCount,\n\t\t\t\t});\n\t\t\t\tthrow new Error(\"GitHub device flow exceeded max wait time\");\n\t\t\t}\n\t\t\tawait this.sleepImpl(pollIntervalMs);\n\t\t\tif (shouldContinue && !shouldContinue()) {\n\t\t\t\tthis.logger.info(\"[GitHubAuthProvider] Device flow cancelled by caller\", {\n\t\t\t\t\telapsedMs: Date.now() - start,\n\t\t\t\t\tpollCount,\n\t\t\t\t});\n\t\t\t\tthrow new Error(\"GitHub device flow cancelled by caller\");\n\t\t\t}\n\n\t\t\tpollCount++;\n\t\t\tthis.logger.debug(\"[GitHubAuthProvider] Polling for authorization\", {\n\t\t\t\tpollCount,\n\t\t\t\telapsedMs: Date.now() - start,\n\t\t\t\tpollIntervalMs,\n\t\t\t});\n\t\t\tlet resp: Response;\n\t\t\ttry {\n\t\t\t\tresp = await this.cfg.fetchImpl(this.cfg.tokenUrl, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\"User-Agent\": \"serviceme-core\",\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tclient_id: this.cfg.clientId,\n\t\t\t\t\t\tdevice_code: deviceCode,\n\t\t\t\t\t\tgrant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tif (!isTransientNetworkError(error)) {\n\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\"[GitHubAuthProvider] Non-retryable error while polling token endpoint\",\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t{ pollCount }\n\t\t\t\t\t);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\t// Transient network failure (DNS/TLS/reset) — back off and retry\n\t\t\t\t// polling until the user acts, same as a non-2xx HTTP response.\n\t\t\t\tconst nextPollIntervalMs = Math.min(\n\t\t\t\t\tMath.round(pollIntervalMs * 1.5),\n\t\t\t\t\tthis.cfg.maxPollIntervalMs\n\t\t\t\t);\n\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\"[GitHubAuthProvider] Transient network error while polling, backing off\",\n\t\t\t\t\t{\n\t\t\t\t\t\tpollCount,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\tpreviousPollIntervalMs: pollIntervalMs,\n\t\t\t\t\t\tnextPollIntervalMs,\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t\tpollIntervalMs = nextPollIntervalMs;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!resp.ok) {\n\t\t\t\t// Transient HTTP failure — back off and retry until the user acts.\n\t\t\t\tconst nextPollIntervalMs = Math.min(\n\t\t\t\t\tMath.round(pollIntervalMs * 1.5),\n\t\t\t\t\tthis.cfg.maxPollIntervalMs\n\t\t\t\t);\n\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\"[GitHubAuthProvider] Token endpoint returned non-OK status, backing off\",\n\t\t\t\t\t{\n\t\t\t\t\t\tpollCount,\n\t\t\t\t\t\tstatus: resp.status,\n\t\t\t\t\t\tpreviousPollIntervalMs: pollIntervalMs,\n\t\t\t\t\t\tnextPollIntervalMs,\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t\tpollIntervalMs = nextPollIntervalMs;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst data = (await resp.json()) as TokenPollResponse;\n\t\t\tif (data.access_token) {\n\t\t\t\tthis.logger.info(\"[GitHubAuthProvider] Authorization granted, fetching user profile\", {\n\t\t\t\t\tpollCount,\n\t\t\t\t\telapsedMs: Date.now() - start,\n\t\t\t\t});\n\t\t\t\tconst rawUser = await this.fetchGitHubUser(data.access_token);\n\t\t\t\tconst email = await this.resolveEmail(data.access_token, rawUser);\n\t\t\t\tthis.logger.info(\"[GitHubAuthProvider] Device-flow login completed\", {\n\t\t\t\t\tpollCount,\n\t\t\t\t\telapsedMs: Date.now() - start,\n\t\t\t\t\tlogin: rawUser.login,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\ttoken: data.access_token,\n\t\t\t\t\trefreshToken: data.refresh_token,\n\t\t\t\t\texpiresIn: data.expires_in,\n\t\t\t\t\tuser: {\n\t\t\t\t\t\tid: String(rawUser.id),\n\t\t\t\t\t\tlogin: rawUser.login,\n\t\t\t\t\t\tname: rawUser.name ?? undefined,\n\t\t\t\t\t\temail: email ?? null,\n\t\t\t\t\t\tavatarUrl: rawUser.avatar_url ?? undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (data.error === \"authorization_pending\") {\n\t\t\t\t// User hasn't acted yet — keep polling without back-off inflation.\n\t\t\t\tthis.logger.debug(\"[GitHubAuthProvider] Authorization still pending\", {\n\t\t\t\t\tpollCount,\n\t\t\t\t\telapsedMs: Date.now() - start,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (data.error === \"slow_down\") {\n\t\t\t\tconsecutiveSlowDown++;\n\t\t\t\t// Per the OAuth Device Flow spec: on `slow_down`, add a fixed 5\n\t\t\t\t// seconds to the polling interval (not a multiplicative back-off).\n\t\t\t\t// Repeatedly under-shooting this requirement is what causes a\n\t\t\t\t// device code to get stuck returning `slow_down` forever.\n\t\t\t\tconst nextPollIntervalMs = Math.min(pollIntervalMs + 5000, this.cfg.maxPollIntervalMs);\n\t\t\t\tthis.logger.warn(\"[GitHubAuthProvider] GitHub requested slower polling (slow_down)\", {\n\t\t\t\t\tpollCount,\n\t\t\t\t\tconsecutiveSlowDown,\n\t\t\t\t\tpreviousPollIntervalMs: pollIntervalMs,\n\t\t\t\t\tnextPollIntervalMs,\n\t\t\t\t});\n\t\t\t\tpollIntervalMs = nextPollIntervalMs;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (data.error === \"access_denied\") {\n\t\t\t\tthis.logger.warn(\"[GitHubAuthProvider] User denied authorization\", {\n\t\t\t\t\tpollCount,\n\t\t\t\t});\n\t\t\t\tthrow new Error(\"User denied authorization\");\n\t\t\t}\n\t\t\tif (data.error === \"expired_token\") {\n\t\t\t\tthis.logger.warn(\n\t\t\t\t\t\"[GitHubAuthProvider] Device code expired before authorization completed\",\n\t\t\t\t\t{ pollCount, elapsedMs: Date.now() - start }\n\t\t\t\t);\n\t\t\t\tthrow new Error(\"GitHub device code expired — restart the flow\");\n\t\t\t}\n\t\t\tthis.logger.error(\n\t\t\t\t\"[GitHubAuthProvider] Unexpected device-flow error from token endpoint\",\n\t\t\t\tdata.error ?? \"unknown\",\n\t\t\t\t{ pollCount }\n\t\t\t);\n\t\t\tthrow new Error(`GitHub device flow error: ${data.error ?? \"unknown\"}`);\n\t\t}\n\t}\n\n\tasync refreshAccessToken(refreshToken: string): Promise<IAuthProviderSession> {\n\t\tif (!refreshToken) {\n\t\t\tthrow new Error(\"refreshAccessToken requires a non-empty refresh token\");\n\t\t}\n\t\tconst resp = await this.cfg.fetchImpl(this.cfg.tokenUrl, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\"User-Agent\": \"serviceme-core\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\tclient_id: this.cfg.clientId,\n\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\trefresh_token: refreshToken,\n\t\t\t}),\n\t\t});\n\t\tif (!resp.ok) {\n\t\t\tthrow new Error(`GitHub refresh failed: HTTP ${resp.status}`);\n\t\t}\n\t\tconst data = (await resp.json()) as TokenPollResponse;\n\t\tif (!data.access_token) {\n\t\t\tthrow new Error(`GitHub refresh failed: ${data.error ?? \"no_access_token\"}`);\n\t\t}\n\t\tconst rawUser = await this.fetchGitHubUser(data.access_token);\n\t\tconst email = await this.resolveEmail(data.access_token, rawUser);\n\t\treturn {\n\t\t\ttoken: data.access_token,\n\t\t\trefreshToken: data.refresh_token ?? refreshToken,\n\t\t\texpiresIn: data.expires_in,\n\t\t\tuser: {\n\t\t\t\tid: String(rawUser.id),\n\t\t\t\tlogin: rawUser.login,\n\t\t\t\tname: rawUser.name ?? undefined,\n\t\t\t\temail: email ?? null,\n\t\t\t\tavatarUrl: rawUser.avatar_url ?? undefined,\n\t\t\t},\n\t\t};\n\t}\n\n\tasync validateToken(token: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst resp = await this.cfg.fetchImpl(this.cfg.userUrl, {\n\t\t\t\theaders: {\n\t\t\t\t\tAccept: \"application/vnd.github+json\",\n\t\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t\t\"User-Agent\": \"serviceme-core\",\n\t\t\t\t},\n\t\t\t});\n\t\t\tif (resp.status === 401) return false;\n\t\t\tif (!resp.ok) {\n\t\t\t\tthrow new Error(`GitHub /user returned HTTP ${resp.status}`);\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (err) {\n\t\t\t// Network / parse errors are surfaced; only 401 means invalid.\n\t\t\tif (err instanceof Error && err.message.includes(\"401\")) return false;\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync fetchAccountMeta(token: string): Promise<AuthAccountMeta> {\n\t\tconst user = await this.fetchGitHubUser(token);\n\t\tconst email = await this.resolveEmail(token, user);\n\t\treturn {\n\t\t\tid: String(user.id),\n\t\t\tprovider: this.providerId,\n\t\t\tdisplayName: user.name ?? user.login,\n\t\t\tlogin: user.login,\n\t\t\temail: email ?? buildGitHubLocalEmail(user.login),\n\t\t\tavatarUrl: user.avatar_url ?? undefined,\n\t\t\texpiresAt: null,\n\t\t};\n\t}\n\n\tprivate async fetchGitHubUser(token: string): Promise<GitHubUserResponse> {\n\t\tconst resp = await this.cfg.fetchImpl(this.cfg.userUrl, {\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/vnd.github+json\",\n\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t\"User-Agent\": \"serviceme-core\",\n\t\t\t},\n\t\t});\n\t\tif (!resp.ok) {\n\t\t\tthrow new Error(`GitHub /user failed: HTTP ${resp.status}`);\n\t\t}\n\t\treturn (await resp.json()) as GitHubUserResponse;\n\t}\n\n\t/**\n\t * GitHub's `/user` endpoint returns `null` when the user kept their\n\t * email private. The `/user/emails` endpoint reveals verified emails;\n\t * we pick the primary one, falling back to the synthetic\n\t * `<login>@github.local` form so the account is never email-less.\n\t */\n\tprivate async resolveEmail(token: string, user: GitHubUserResponse): Promise<string | null> {\n\t\tif (user.email) return user.email;\n\t\tconst emailsResp = await this.cfg.fetchImpl(\"https://api.github.com/user/emails\", {\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/vnd.github+json\",\n\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t\"User-Agent\": \"serviceme-core\",\n\t\t\t},\n\t\t});\n\t\tif (!emailsResp.ok) {\n\t\t\treturn null;\n\t\t}\n\t\tconst emails = (await emailsResp.json()) as Array<{\n\t\t\temail: string;\n\t\t\tprimary: boolean;\n\t\t\tvisibility: string | null;\n\t\t\tverified: boolean;\n\t\t}>;\n\t\tconst primary = emails.find((e) => e.primary && e.verified);\n\t\treturn primary?.email ?? null;\n\t}\n}\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * MicrosoftAuthProvider — Microsoft Account (MSA) OAuth Device Flow stub.\n *\n * The Extension's existing `MicrosoftAuthProvider` (`apps/extension/src/services/auth/providers/MicrosoftAuthProvider.ts`)\n * delegates to VSCode's built-in `vscode.authentication.getSession()` API\n * — there's no direct Microsoft device-flow endpoint exposed for\n * first-party apps. To keep Core usable from the CLI without VSCode,\n * we expose a minimal stub that throws \"not implemented in Core\" so\n * callers can detect and route back to the Extension adapter.\n *\n * The reason this lives in Core at all (instead of being purely\n * Extension-side) is the cross-runtime registry: `AuthCore` needs to\n * know which providers it MIGHT support, even if only the Extension\n * process can actually fulfil the Microsoft login.\n *\n * Refs:\n * - 4.功能规划.md §2.1 — `providers/MicrosoftAuthProvider.ts 从 extension 整体迁移`\n * - ADL-002 — Device Flow OAuth (locked, GitHub-only)\n */\n\nimport type { AuthAccountMeta, AuthLoginResult, AuthProvider } from \"@serviceme/devtools-protocol\";\n\nimport type { IAuthProvider, IAuthProviderSession } from \"./IAuthProvider\";\n\n/**\n * Error thrown when callers invoke Microsoft auth from a non-Extension\n * runtime (CLI / Bridge). The CLI maps this to `AUTH_PROVIDER_REQUIRES_HOST`\n * so the user is prompted to retry inside VSCode.\n */\nexport class MicrosoftProviderNotHostedError extends Error {\n\tconstructor(\n\t\tmessage = \"Microsoft auth requires the VSCode host; CLI does not yet implement MSA device flow\"\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"MicrosoftProviderNotHostedError\";\n\t}\n}\n\nexport class MicrosoftAuthProvider implements IAuthProvider {\n\treadonly providerId: AuthProvider = \"microsoft\";\n\n\tasync requestDeviceFlow(): Promise<AuthLoginResult> {\n\t\tthrow new MicrosoftProviderNotHostedError();\n\t}\n\n\tasync completeDeviceFlow(\n\t\t_deviceCode: string,\n\t\t_shouldContinue?: () => boolean\n\t): Promise<IAuthProviderSession> {\n\t\tthrow new MicrosoftProviderNotHostedError();\n\t}\n\n\tasync refreshAccessToken(_refreshToken: string): Promise<IAuthProviderSession> {\n\t\tthrow new MicrosoftProviderNotHostedError();\n\t}\n\n\tasync validateToken(_token: string): Promise<boolean> {\n\t\tthrow new MicrosoftProviderNotHostedError();\n\t}\n\n\tasync fetchAccountMeta(_token: string): Promise<AuthAccountMeta> {\n\t\tthrow new MicrosoftProviderNotHostedError();\n\t}\n}\n","import {\n\tCOPILOT_ERROR_CODES,\n\ttype CopilotDoctorResult,\n\tcreateServicemeError,\n} from \"@serviceme/devtools-protocol\";\nimport { commandExists, runCommand } from \"../process/runCommand\";\n\nconst COPILOT_COMMAND = \"copilot\";\nconst GH_COMMAND = \"gh\";\n\n/**\n * Quick auth pre-flight check using `gh auth status`.\n * Returns true if gh is authenticated; false otherwise.\n */\nexport async function isCopilotAuthenticated(): Promise<boolean> {\n\ttry {\n\t\tawait runCommand(GH_COMMAND, {\n\t\t\targs: [\"auth\", \"status\"],\n\t\t\ttimeoutMs: 10_000,\n\t\t});\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport async function copilotDoctor(): Promise<CopilotDoctorResult> {\n\tconst exists = await commandExists(COPILOT_COMMAND);\n\tif (!exists) {\n\t\treturn { installed: false, version: null, authenticated: false };\n\t}\n\n\tlet version: string | null = null;\n\ttry {\n\t\tconst versionResult = await runCommand(COPILOT_COMMAND, {\n\t\t\targs: [\"--version\"],\n\t\t\ttimeoutMs: 10_000,\n\t\t});\n\t\tversion = versionResult.stdout.trim();\n\t} catch {\n\t\treturn { installed: true, version: null, authenticated: false };\n\t}\n\n\t// Use gh auth status for reliable authentication check\n\tconst authenticated = await isCopilotAuthenticated();\n\n\treturn { installed: true, version, authenticated };\n}\n\nexport function createCopilotNotInstalledError() {\n\treturn createServicemeError(\n\t\tCOPILOT_ERROR_CODES.NOT_INSTALLED,\n\t\t\"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.\"\n\t);\n}\n\nexport function createCopilotAuthRequiredError() {\n\treturn createServicemeError(\n\t\tCOPILOT_ERROR_CODES.AUTH_REQUIRED,\n\t\t\"GitHub Copilot CLI requires authentication. Run `copilot auth login` to sign in.\"\n\t);\n}\n","import { spawn } from \"node:child_process\";\n\n/** Grace period between the SIGTERM and the SIGKILL backstop on POSIX. */\nconst FORCE_KILL_DELAY_MS = 2_000;\n\n/**\n * Terminate a spawned command, killing the whole process tree on POSIX.\n *\n * Commands are spawned with `detached: true` (their own process group) so\n * that a login-shell fallback (e.g. `zsh -lc ...`) and every process it\n * starts can be reaped together. On Windows the pre-existing `taskkill /T`\n * logic already force-terminates the tree and is left untouched.\n */\nfunction terminateCommandProcess(\n\tchild: {\n\t\tpid?: number;\n\t\tkilled?: boolean;\n\t\tkill(signal?: NodeJS.Signals | number): boolean;\n\t},\n\tsignal: NodeJS.Signals = \"SIGTERM\"\n): void {\n\tif (child.killed) {\n\t\treturn;\n\t}\n\n\tif (process.platform === \"win32\" && child.pid) {\n\t\tconst killProcess = spawn(\"taskkill\", [\"/pid\", String(child.pid), \"/T\", \"/F\"], {\n\t\t\tstdio: \"ignore\",\n\t\t\twindowsHide: true,\n\t\t});\n\n\t\tkillProcess.once(\"error\", () => {\n\t\t\tchild.kill();\n\t\t});\n\t\treturn;\n\t}\n\n\tconst pid = child.pid;\n\tif (pid) {\n\t\ttry {\n\t\t\tprocess.kill(-pid, signal);\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// Process group already gone; fall back to signaling the child.\n\t\t}\n\t}\n\n\tchild.kill(signal);\n}\n\n/**\n * Schedule a SIGKILL backstop for a timed-out POSIX process group. If the\n * SIGTERM sent by {@link terminateCommandProcess} is ignored (blocked child,\n * uninterruptible sleep), this guarantees the group is eventually reaped.\n * Windows does not need this — `taskkill /T /F` already force-kills.\n */\nfunction scheduleForceKill(child: { pid?: number }): NodeJS.Timeout | undefined {\n\tif (process.platform === \"win32\") {\n\t\treturn undefined;\n\t}\n\n\tconst pid = child.pid;\n\tif (pid === undefined) {\n\t\treturn undefined;\n\t}\n\n\tconst timer = setTimeout(() => {\n\t\ttry {\n\t\t\tprocess.kill(-pid, \"SIGKILL\");\n\t\t} catch {\n\t\t\t// Process group already terminated.\n\t\t}\n\t}, FORCE_KILL_DELAY_MS);\n\ttimer.unref?.();\n\treturn timer;\n}\n\nexport interface RunCommandOptions {\n\targs?: string[];\n\tcwd?: string;\n\tenv?: NodeJS.ProcessEnv;\n\ttimeoutMs?: number;\n\tstdin?: string;\n\tshell?: boolean;\n}\n\nexport interface RunCommandResult {\n\tstdout: string;\n\tstderr: string;\n\tcode: number;\n}\n\nexport async function runCommand(\n\tcommand: string,\n\toptions: RunCommandOptions = {}\n): Promise<RunCommandResult> {\n\treturn new Promise<RunCommandResult>((resolve, reject) => {\n\t\tconst child = spawn(command, options.args ?? [], {\n\t\t\tcwd: options.cwd,\n\t\t\tenv: options.env,\n\t\t\tshell: options.shell,\n\t\t\tstdio: \"pipe\",\n\t\t\twindowsHide: true,\n\t\t\t// POSIX: run the command in its own process group so a timeout can\n\t\t\t// terminate the whole tree (login shell + its children) at once.\n\t\t\tdetached: process.platform !== \"win32\",\n\t\t});\n\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tlet finished = false;\n\t\tlet timeoutId: NodeJS.Timeout | undefined;\n\t\tlet forceKillTimer: NodeJS.Timeout | undefined;\n\n\t\tconst finish = (handler: () => void) => {\n\t\t\tif (finished) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfinished = true;\n\t\t\tif (timeoutId) {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t}\n\t\t\tif (forceKillTimer) {\n\t\t\t\tclearTimeout(forceKillTimer);\n\t\t\t}\n\t\t\thandler();\n\t\t};\n\n\t\tchild.stdout?.setEncoding(\"utf8\");\n\t\tchild.stderr?.setEncoding(\"utf8\");\n\n\t\tchild.stdout?.on(\"data\", (chunk: string) => {\n\t\t\tstdout += chunk;\n\t\t});\n\n\t\tchild.stderr?.on(\"data\", (chunk: string) => {\n\t\t\tstderr += chunk;\n\t\t});\n\n\t\tchild.once(\"error\", (error) => {\n\t\t\tfinish(() => reject(error));\n\t\t});\n\n\t\tchild.once(\"close\", (code) => {\n\t\t\tif (forceKillTimer) {\n\t\t\t\tclearTimeout(forceKillTimer);\n\t\t\t\tforceKillTimer = undefined;\n\t\t\t}\n\t\t\tfinish(() => {\n\t\t\t\tif (code === 0) {\n\t\t\t\t\tresolve({\n\t\t\t\t\t\tstdout,\n\t\t\t\t\t\tstderr,\n\t\t\t\t\t\tcode: 0,\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst error = new Error(\n\t\t\t\t\tstderr || stdout || `Command failed with exit code ${String(code ?? 1)}.`\n\t\t\t\t) as Error & {\n\t\t\t\t\tcode?: number | string;\n\t\t\t\t\tstdout?: string;\n\t\t\t\t\tstderr?: string;\n\t\t\t\t};\n\t\t\t\terror.code = code ?? 1;\n\t\t\t\terror.stdout = stdout;\n\t\t\t\terror.stderr = stderr;\n\t\t\t\treject(error);\n\t\t\t});\n\t\t});\n\n\t\tif (options.stdin !== undefined) {\n\t\t\tchild.stdin?.end(options.stdin);\n\t\t} else {\n\t\t\tchild.stdin?.end();\n\t\t}\n\n\t\tif (options.timeoutMs) {\n\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\tfinish(() => {\n\t\t\t\t\tterminateCommandProcess(child);\n\t\t\t\t\tconst error = new Error(\n\t\t\t\t\t\t`Command timed out after ${String(options.timeoutMs)}ms.`\n\t\t\t\t\t) as Error & {\n\t\t\t\t\t\tcode?: string;\n\t\t\t\t\t\tstdout?: string;\n\t\t\t\t\t\tstderr?: string;\n\t\t\t\t\t};\n\t\t\t\t\terror.code = \"ETIMEDOUT\";\n\t\t\t\t\terror.stdout = stdout;\n\t\t\t\t\terror.stderr = stderr;\n\t\t\t\t\treject(error);\n\t\t\t\t});\n\n\t\t\t\tforceKillTimer = scheduleForceKill(child);\n\t\t\t}, options.timeoutMs);\n\t\t}\n\t});\n}\n\nexport async function commandExists(command: string): Promise<boolean> {\n\tconst isWindows = process.platform === \"win32\";\n\n\ttry {\n\t\tawait runCommand(isWindows ? \"where\" : \"which\", {\n\t\t\targs: [command],\n\t\t\ttimeoutMs: 5_000,\n\t\t});\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n","import {\n\tCOPILOT_ERROR_CODES,\n\ttype CopilotPromptOptions,\n\ttype CopilotPromptResult,\n\tcreateServicemeError,\n\tServicemeProtocolError,\n} from \"@serviceme/devtools-protocol\";\nimport { runCommand } from \"../process/runCommand\";\n\nconst COPILOT_COMMAND = \"copilot\";\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\n// Strip ANSI escape codes from output\nfunction stripAnsi(text: string): string {\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: intentional ANSI stripping\n\treturn text.replace(/\\x1B\\[[0-9;]*[A-Za-z]/g, \"\");\n}\n\nexport async function copilotPrompt(options: CopilotPromptOptions): Promise<CopilotPromptResult> {\n\tconst args = [\"-p\", options.prompt];\n\n\tif (options.workspace) {\n\t\targs.push(\"--add-dir\", options.workspace);\n\t}\n\n\tif (options.autopilot) {\n\t\targs.push(\"--allow-all-tools\");\n\t}\n\n\tif (options.allowTools?.length) {\n\t\tfor (const tool of options.allowTools) {\n\t\t\targs.push(\"--allow-tool\", tool);\n\t\t}\n\t}\n\n\tif (options.model) {\n\t\targs.push(\"--model\", options.model);\n\t}\n\n\tif (options.agent) {\n\t\targs.push(\"--agent\", options.agent);\n\t}\n\n\tconst timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;\n\n\ttry {\n\t\tconst result = await runCommand(COPILOT_COMMAND, {\n\t\t\targs,\n\t\t\tcwd: options.workspace,\n\t\t\ttimeoutMs: timeout,\n\t\t});\n\n\t\tconst output = stripAnsi(result.stdout);\n\n\t\t// Detect refusal in success path (some versions exit 0 with refusal)\n\t\tif (\n\t\t\toutput.includes(\"I'm sorry, but I cannot assist\") &&\n\t\t\tresult.stderr?.includes(\"Stream completed without a response\")\n\t\t) {\n\t\t\tthrow createServicemeError(\n\t\t\t\tCOPILOT_ERROR_CODES.AUTH_REQUIRED,\n\t\t\t\t\"Copilot CLI returned a refusal response. This typically indicates an expired authentication token. Run `gh auth login` to re-authenticate.\",\n\t\t\t\t{ exitCode: 0, output, stderr: result.stderr }\n\t\t\t);\n\t\t}\n\n\t\treturn { output, exitCode: 0 };\n\t} catch (error: unknown) {\n\t\t// Re-throw already-classified protocol errors so the success-path refusal\n\t\t// branch (and any other inner throws of ServicemeProtocolError) are not\n\t\t// rewritten as EXECUTION_FAILED by the generic catch handler below.\n\t\tif (error instanceof ServicemeProtocolError) {\n\t\t\tthrow error;\n\t\t}\n\n\t\tconst err = error as {\n\t\t\tcode?: number | string;\n\t\t\tstdout?: string;\n\t\t\tstderr?: string;\n\t\t\tmessage?: string;\n\t\t};\n\n\t\tif (err.code === \"ETIMEDOUT\") {\n\t\t\tthrow createServicemeError(\n\t\t\t\tCOPILOT_ERROR_CODES.TIMEOUT,\n\t\t\t\t`Copilot prompt timed out after ${String(timeout)}ms.`\n\t\t\t);\n\t\t}\n\n\t\tconst exitCode = typeof err.code === \"number\" ? err.code : 1;\n\t\tconst output = stripAnsi(err.stdout ?? \"\");\n\t\tconst stderr = err.stderr ?? err.message ?? \"\";\n\n\t\t// Detect Copilot refusal pattern (often caused by expired auth token)\n\t\tif (\n\t\t\toutput.includes(\"I'm sorry, but I cannot assist\") ||\n\t\t\tstderr.includes(\"Stream completed without a response\")\n\t\t) {\n\t\t\tthrow createServicemeError(\n\t\t\t\tCOPILOT_ERROR_CODES.AUTH_REQUIRED,\n\t\t\t\t\"Copilot CLI returned a refusal response. This typically indicates an expired authentication token. Run `gh auth login` to re-authenticate.\",\n\t\t\t\t{ exitCode, output, stderr }\n\t\t\t);\n\t\t}\n\n\t\tif (exitCode !== 0 && output) {\n\t\t\treturn { output, exitCode };\n\t\t}\n\n\t\tthrow createServicemeError(\n\t\t\tCOPILOT_ERROR_CODES.EXECUTION_FAILED,\n\t\t\tstderr || `Copilot exited with code ${String(exitCode)}.`,\n\t\t\t{ exitCode, stderr }\n\t\t);\n\t}\n}\n","import * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport { getHomeDir } from \"../paths/userHome\";\nimport type { ResolvedPluginEntry } from \"./plugin-resolver\";\n\n/** Personal artifact kinds materialized as managed links into host target directories. */\nexport const allPersonalLinkKinds = [\"agent\", \"skill\", \"prompt\", \"instruction\"] as const;\n\n/** One personal link kind. */\nexport type PersonalLinkKind = (typeof allPersonalLinkKinds)[number];\n\n/**\n * Adapter that applies or removes one personal integration entry (mcp/hook)\n * in host-owned user configuration. Implementations own their config format;\n * callers own the digest approval rule.\n */\nexport interface CopilotIntegrationAdapter {\n\t/** Apply one approved integration entry on this machine. */\n\tapplyEntry(input: { entry: ResolvedPluginEntry }): Promise<void>;\n\t/** Remove a previously applied entry; returns whether anything was removed. */\n\tremoveEntry(input: { identity: string }): Promise<boolean>;\n}\n\n/** Host-verified personal materialization targets and integration adapters. */\nexport interface CopilotHostCapabilities {\n\t/** Absolute, host-verified target directory per personal link kind. */\n\tpersonalTargets: Partial<Record<PersonalLinkKind, string>>;\n\t/** Adapter for personal MCP configuration, when the host supports one. */\n\tmcpAdapter?: CopilotIntegrationAdapter;\n\t/** Adapter for personal hook registration, when the host supports one. */\n\thookAdapter?: CopilotIntegrationAdapter;\n}\n\n/** Whether the host verified a personal target directory for the kind. */\nexport function supportsPersonalLinkKind(\n\tcapabilities: CopilotHostCapabilities,\n\tkind: PersonalLinkKind\n): boolean {\n\treturn typeof capabilities.personalTargets[kind] === \"string\";\n}\n\n/** Whether the host provides the adapter an integration kind requires. */\nexport function supportsPersonalIntegrationKind(\n\tcapabilities: CopilotHostCapabilities,\n\tkind: \"mcp\" | \"hook\"\n): boolean {\n\treturn kind === \"mcp\"\n\t\t? capabilities.mcpAdapter !== undefined\n\t\t: capabilities.hookAdapter !== undefined;\n}\n\n/**\n * Conservative production capability provider.\n *\n * Only claims targets it can actually verify on this host: the canonical\n * home '.copilot/agents' and '.copilot/skills' directories when they already\n * exist. Prompt, instruction, MCP, and hook personal targets stay unclaimed\n * until a verified provider wires them (Task 9); the reconciler reports those\n * kinds as unsupported instead of inventing directories.\n */\nexport async function createDefaultCopilotHostCapabilities(options?: {\n\tcopilotHomeDir?: string;\n}): Promise<CopilotHostCapabilities> {\n\tconst home = options?.copilotHomeDir ?? getHomeDir();\n\tconst personalTargets: Partial<Record<PersonalLinkKind, string>> = {};\n\tfor (const kind of allPersonalLinkKinds) {\n\t\tif (kind !== \"agent\" && kind !== \"skill\") continue;\n\t\tconst target = path.join(home, \".copilot\", kind === \"agent\" ? \"agents\" : \"skills\");\n\t\tconst stat = await fsp.stat(target).catch(() => null);\n\t\tif (stat?.isDirectory() === true) {\n\t\t\tpersonalTargets[kind] = target;\n\t\t}\n\t}\n\treturn { personalTargets };\n}\n","import * as os from \"node:os\";\nimport * as path from \"node:path\";\n\n/**\n * SERVICEME user home directory helpers.\n *\n * All paths resolve under {@link getServicemeHome}, which is either the\n * `SERVICEME_HOME` environment variable (when set and non-empty) or\n * `$HOME/.serviceme` on POSIX / `%USERPROFILE%\\.serviceme` on Windows.\n *\n * Tests inject `homeDir` and `servicemeHomeEnv` overrides via\n * {@link setUserHomeOverrides} / {@link resetUserHomeOverrides} so they can\n * exercise the path logic without touching the real user environment.\n */\n\n/** Layout constants — kept in one place so other modules can reuse them. */\nexport const SERVICEME_DIR_NAME = \".serviceme\";\nexport const REPOS_SUBDIR = \"repos\";\nexport const CACHE_SUBDIR = \"cache\";\nexport const DRAFTS_SUBDIR = \"drafts\";\nexport const SKILL_DRAFTS_SUBDIR = \"skills\";\nexport const AGENT_DRAFTS_SUBDIR = \"agents\";\nexport const REPOS_CONFIG_FILENAME = \"repos.json\";\nexport const WORKSPACES_SUBDIR = \"workspaces\";\nexport const WORKSPACE_CONTENT_STATE_FILENAME = \"copilot-content-state.json\";\n\n/** rev.20 — r7 BYOM Server Proxy toggle self-state (read by ext + CLI + server). */\nexport const SERVER_PROXY_GLOBAL_FILENAME = \"server-proxy.json\";\n\n/**\n * Repo id regex — used to validate any `repoId` argument before it is joined\n * into a filesystem path. Keeps path traversal attempts out and gives us a\n * predictable on-disk shape.\n */\nexport const SAFE_REPO_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;\n\n/** Environment variable that overrides the user-home root directory. */\nexport const SERVICEME_HOME_ENV = \"SERVICEME_HOME\";\n\n/**\n * Test seam: lets unit tests inject deterministic values for `os.homedir()`\n * and the `SERVICEME_HOME` env override without actually mutating\n * `process.env` (which would leak into other tests).\n */\ninterface UserHomeOverrides {\n\thomeDir?: string | undefined;\n\tservicemeHomeEnv?: string | undefined;\n\tplatform?: NodeJS.Platform | undefined;\n}\n\nlet activeOverrides: UserHomeOverrides = {};\n\nexport function setUserHomeOverrides(overrides: UserHomeOverrides): void {\n\tactiveOverrides = { ...overrides };\n}\n\nexport function resetUserHomeOverrides(): void {\n\tactiveOverrides = {};\n}\n\nfunction resolveHomeDir(): string {\n\tconst injected = activeOverrides.homeDir;\n\tif (injected !== undefined) {\n\t\treturn injected;\n\t}\n\treturn os.homedir();\n}\n\nfunction resolveServicemeHomeEnv(): string | undefined {\n\tconst injected = activeOverrides.servicemeHomeEnv;\n\tif (injected !== undefined) {\n\t\t// Treat empty string as \"not set\" — `process.env` always returns a string\n\t\t// but tests may deliberately pass \"\" to opt out.\n\t\treturn injected.length > 0 ? injected : undefined;\n\t}\n\tconst envValue = process.env[SERVICEME_HOME_ENV];\n\treturn envValue && envValue.length > 0 ? envValue : undefined;\n}\n\nfunction resolvePlatform(): NodeJS.Platform {\n\treturn activeOverrides.platform ?? process.platform;\n}\n\nexport function assertSafeRepoId(repoId: string): string {\n\tif (typeof repoId !== \"string\" || repoId.length === 0 || !SAFE_REPO_ID_PATTERN.test(repoId)) {\n\t\tthrow new Error(\n\t\t\t`Invalid repo id: ${JSON.stringify(repoId)}. ` +\n\t\t\t\t`Must match ${SAFE_REPO_ID_PATTERN} (alphanumeric start, then ` +\n\t\t\t\t`alphanumerics / underscores / hyphens, ≤ 64 chars).`\n\t\t);\n\t}\n\treturn repoId;\n}\n\n/**\n * The raw OS home directory (`os.homedir()`), honoring test overrides\n * ({@link setUserHomeOverrides}). Exported for callers that need a\n * home-relative path *outside* of `~/.serviceme` — e.g. the\n * `~/.agents/{skills,agents}` convention used by `SkillLinker` for\n * user-scope links.\n */\nexport function getHomeDir(): string {\n\treturn resolveHomeDir();\n}\n\n/**\n * Root directory for all SERVICEME user-level state. Resolves to\n * `${SERVICEME_HOME}` when that env var is set, otherwise `${HOME}/.serviceme`\n * (POSIX) or `%USERPROFILE%\\.serviceme` (Windows via `os.homedir`).\n */\nexport function getServicemeHome(): string {\n\tconst override = resolveServicemeHomeEnv();\n\tif (override !== undefined) {\n\t\treturn path.resolve(override);\n\t}\n\treturn path.join(resolveHomeDir(), SERVICEME_DIR_NAME);\n}\n\n/** `$HOME/.serviceme/repos` (or `${SERVICEME_HOME}/repos`). */\nexport function getReposDir(): string {\n\treturn path.join(getServicemeHome(), REPOS_SUBDIR);\n}\n\n/** `$HOME/.serviceme/repos/<repoId>` — validates `repoId` first. */\nexport function getRepoDir(repoId: string): string {\n\treturn path.join(getReposDir(), assertSafeRepoId(repoId));\n}\n\n/** `$HOME/.serviceme/cache` — generic per-user cache. */\nexport function getCacheDir(): string {\n\treturn path.join(getServicemeHome(), CACHE_SUBDIR);\n}\n\n/** `$HOME/.serviceme/cache/<repoId>` — per-repo sync state lives here. */\nexport function getRepoCacheDir(repoId: string): string {\n\treturn path.join(getCacheDir(), assertSafeRepoId(repoId));\n}\n\n/** `$HOME/.serviceme/drafts` — local edits not yet pushed upstream. */\nexport function getDraftsDir(): string {\n\treturn path.join(getServicemeHome(), DRAFTS_SUBDIR);\n}\n\n/** `$HOME/.serviceme/drafts/skills` — local skill drafts. */\nexport function getSkillDraftsDir(): string {\n\treturn path.join(getDraftsDir(), SKILL_DRAFTS_SUBDIR);\n}\n\n/** `$HOME/.serviceme/drafts/agents` — local agent drafts. */\nexport function getAgentDraftsDir(): string {\n\treturn path.join(getDraftsDir(), AGENT_DRAFTS_SUBDIR);\n}\n\n/**\n * `$HOME/.serviceme/repos.json` — the single config entry point for repo\n * metadata. Always under the resolved home root (i.e. follows\n * `SERVICEME_HOME` overrides too).\n */\nexport function getReposConfigPath(): string {\n\treturn path.join(getServicemeHome(), REPOS_CONFIG_FILENAME);\n}\n\n/** `~/.serviceme/workspaces` — machine-local per-workspace state root. */\nexport function getWorkspacesDir(): string {\n\treturn path.join(getServicemeHome(), WORKSPACES_SUBDIR);\n}\n\n/**\n * Convenience helper for callers that need to switch behaviour on platform\n * (e.g. `SkillLinker` chooses symlink vs junction). Exported mostly so tests\n * can pin the value without touching `process.platform` directly.\n */\nexport function getHomePlatform(): NodeJS.Platform {\n\treturn resolvePlatform();\n}\n\n// ─── Scheduled Tasks paths (added by M1.5.3) ───────────────────────────────\n\n/** Layout constants for scheduled-tasks files. Kept here so other modules\n * can reuse them and the names stay in sync with the design doc. */\nexport const SCHEDULED_TASKS_CONFIG_FILENAME = \"scheduled-tasks.json\";\nexport const SCHEDULED_TASKS_LOG_FILENAME = \"scheduled-tasks-log.json\";\nexport const SCHEDULER_PID_FILENAME = \"scheduler.pid\";\nexport const SCHEDULER_LOCK_FILENAME = \"scheduler.lock\";\nexport const SCHEDULER_LOG_FILENAME = \"scheduler.log\";\nexport const MIGRATION_FAILURES_FILENAME = \"migration-failures.json\";\nexport const KNOWN_WORKSPACES_FILENAME = \"known-workspaces.json\";\n\n/** `~/.serviceme/scheduled-tasks.json` — the global v2 task list. */\nexport function getScheduledTasksConfigPath(): string {\n\treturn path.join(getServicemeHome(), SCHEDULED_TASKS_CONFIG_FILENAME);\n}\n\n/** `~/.serviceme/scheduled-tasks-log.json` — the global execution log (200 LRU). */\nexport function getScheduledTasksLogPath(): string {\n\treturn path.join(getServicemeHome(), SCHEDULED_TASKS_LOG_FILENAME);\n}\n\n/** `~/.serviceme/scheduler.pid` — global daemon PID file. */\nexport function getSchedulerPidPath(): string {\n\treturn path.join(getServicemeHome(), SCHEDULER_PID_FILENAME);\n}\n\n/** `~/.serviceme/scheduler.lock` — global daemon startup flock. */\nexport function getSchedulerLockPath(): string {\n\treturn path.join(getServicemeHome(), SCHEDULER_LOCK_FILENAME);\n}\n\n/** `~/.serviceme/scheduler.log` — global daemon log (daemon lifecycle, not task execution). */\nexport function getSchedulerLogPath(): string {\n\treturn path.join(getServicemeHome(), SCHEDULER_LOG_FILENAME);\n}\n\n/** `~/.serviceme/migration-failures.json` — diagnostics for failed v1→v2 imports. */\nexport function getMigrationFailuresPath(): string {\n\treturn path.join(getServicemeHome(), MIGRATION_FAILURES_FILENAME);\n}\n\n/** `~/.serviceme/known-workspaces.json` — workspace list the extension has seen. */\nexport function getKnownWorkspacesPath(): string {\n\treturn path.join(getServicemeHome(), KNOWN_WORKSPACES_FILENAME);\n}\n\n// ─── r7 BYOM Server Proxy (rev.20) ─────────────────────────────────────────\n\n/**\n * `~/.serviceme/server-proxy.json` — r7 BYOM Server Proxy toggle\n * self-state. Written by the extension's `ProxyConfigService`, readable\n * by the CLI / Server so they can decide whether to route traffic\n * through the corporate proxy without booting VS Code.\n *\n * Lives under user-level `~/.serviceme/` (not under a workspace) because\n * the toggle is a *user preference* — the same human enabling it on\n * machine A should be able to rely on it on machine B after sync, not\n * have it disappear when they switch repos.\n */\nexport function getServerProxyGlobalPath(): string {\n\treturn path.join(getServicemeHome(), SERVER_PROXY_GLOBAL_FILENAME);\n}\n\n// ─── Phase 5 (auth + device + toolbox) placeholders (Spec §11.12) ────────\n\n/** Layout constants for the Phase 5 client-side state files. The server\n * already has the corresponding routes (`/api/v1/auth/...`,\n * `/api/v1/device/...`, etc.) — the client just needs the on-disk\n * file paths + a first-run bootstrap so the auth/device/toolbox\n * services can open + read them without a per-call existence check. */\nexport const CREDENTIALS_CONFIG_FILENAME = \"credentials.json\";\nexport const DEVICE_JSON_FILENAME = \"device.json\";\nexport const TOOLBOX_JSON_FILENAME = \"toolbox.json\";\nexport const MACHINE_ID_FILENAME = \"machine-id\";\nexport const PROFILES_JSON_FILENAME = \"profiles.json\";\n\n/** `~/.serviceme/credentials.json` — better-auth session tokens + refresh state (client-side cache). */\nexport function getCredentialsConfigPath(): string {\n\treturn path.join(getServicemeHome(), CREDENTIALS_CONFIG_FILENAME);\n}\n\n/** `~/.serviceme/device.json` — device enrollment payload (id, public key fingerprint, claimed-by). */\nexport function getDeviceJsonPath(): string {\n\treturn path.join(getServicemeHome(), DEVICE_JSON_FILENAME);\n}\n\n/** `~/.serviceme/toolbox.json` — local toolbox state (installed tool refs, per-user prefs). */\nexport function getToolboxJsonPath(): string {\n\treturn path.join(getServicemeHome(), TOOLBOX_JSON_FILENAME);\n}\n\n/** `~/.serviceme/machine-id` — opaque stable per-install id (uuid v4 string, no JSON wrapper). */\nexport function getMachineIdPath(): string {\n\treturn path.join(getServicemeHome(), MACHINE_ID_FILENAME);\n}\n\n/** `~/.serviceme/profiles.json` — cached projection of server-side profile rows; refresh on demand. */\nexport function getProfilesJsonPath(): string {\n\treturn path.join(getServicemeHome(), PROFILES_JSON_FILENAME);\n}\n","import * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport type { PlannedContentEntry } from \"./content-plan\";\nimport type { CopilotLinkMode, WorkspaceContentState, WorkspaceMaterializedEntry } from \"./types\";\n\n/** Status of one entry after a reconcile pass. */\nexport type MaterializeEntryStatus =\n\t| \"restored\"\n\t| \"adopted\"\n\t| \"conflict\"\n\t| \"drifted\"\n\t| \"pending_approval\";\n\nexport interface MaterializeEntryResult {\n\tidentity: string;\n\tstatus: MaterializeEntryStatus;\n\t/** Secondary machine-local state, e.g. migration_available for legacy links. */\n\tmessage?: string;\n}\n\nexport interface MaterializeResult {\n\tchanged: boolean;\n\tentries: MaterializeEntryResult[];\n\t/** Updated machine-local state (only successfully materialized entries). */\n\tstate: WorkspaceContentState;\n}\n\nconst KIND_TO_DIR: Partial<Record<PlannedContentEntry[\"kind\"], string>> = {\n\tagent: \"agents\",\n\tskill: \"skills\",\n\tinstruction: \"instructions\",\n\tprompt: \"prompts\",\n};\n\n/** Create and inspect Copilot links; never overwrite unowned content. */\nexport class CopilotLinkMaterializer {\n\tprivate pendingFailure: Error | undefined;\n\n\t/** Test seam: make the next reconcile throw, then clear the failure. */\n\tfailNext(error: Error): void {\n\t\tthis.pendingFailure = error;\n\t}\n\n\t/** Materialize planned entries with conflict/drift safety. */\n\tasync reconcile(input: {\n\t\tworkspaceDir: string;\n\t\tentries: PlannedContentEntry[];\n\t\tpreviousState: WorkspaceContentState;\n\t}): Promise<MaterializeResult> {\n\t\tif (this.pendingFailure !== undefined) {\n\t\t\tconst error = this.pendingFailure;\n\t\t\tthis.pendingFailure = undefined;\n\t\t\tthrow error;\n\t\t}\n\t\tconst results: MaterializeEntryResult[] = [];\n\t\tconst stateEntries: WorkspaceMaterializedEntry[] = [];\n\t\tlet changed = false;\n\n\t\tfor (const entry of input.entries) {\n\t\t\tif (entry.requiresApproval) {\n\t\t\t\tconst previous = input.previousState.entries.find(\n\t\t\t\t\t(state) => state.identity === entry.identity\n\t\t\t\t);\n\t\t\t\tif (!previous?.approved || previous.digest !== entry.digest) {\n\t\t\t\t\tconst linkPath = this.resolveLinkPath(input.workspaceDir, entry);\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\tidentity: entry.identity,\n\t\t\t\t\t\tstatus: \"pending_approval\",\n\t\t\t\t\t\tmessage: \"Content requires approval on this machine\",\n\t\t\t\t\t});\n\t\t\t\t\tstateEntries.push(\n\t\t\t\t\t\tprevious\n\t\t\t\t\t\t\t? { ...previous, approved: false }\n\t\t\t\t\t\t\t: this.toStateEntry(entry, linkPath, \"symlink\", false)\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst linkPath = this.resolveLinkPath(input.workspaceDir, entry);\n\t\t\tconst previous = input.previousState.entries.find(\n\t\t\t\t(state) => state.identity === entry.identity\n\t\t\t);\n\t\t\tconst stat = await fsp.lstat(linkPath).catch(() => null);\n\n\t\t\tif (stat && !stat.isSymbolicLink()) {\n\t\t\t\tresults.push({\n\t\t\t\t\tidentity: entry.identity,\n\t\t\t\t\tstatus: previous ? \"drifted\" : \"conflict\",\n\t\t\t\t\tmessage: previous\n\t\t\t\t\t\t? \"Owned link was replaced by other content\"\n\t\t\t\t\t\t: \"Target exists and is not owned by SERVICEME\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (stat?.isSymbolicLink()) {\n\t\t\t\tconst currentTarget = await fsp.readlink(linkPath);\n\t\t\t\tif (currentTarget !== entry.sourcePath) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tprevious?.linkPath === this.toStateLinkPath(linkPath) &&\n\t\t\t\t\t\tprevious.sourcePath === entry.sourcePath\n\t\t\t\t\t) {\n\t\t\t\t\t\tresults.push({\n\t\t\t\t\t\t\tidentity: entry.identity,\n\t\t\t\t\t\t\tstatus: \"drifted\",\n\t\t\t\t\t\t\tmessage: \"Owned link now points elsewhere\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\tidentity: entry.identity,\n\t\t\t\t\t\tstatus: \"conflict\",\n\t\t\t\t\t\tmessage: \"Another link owns this target path\",\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Matching link already in place.\n\t\t\t\tstateEntries.push(\n\t\t\t\t\tthis.toStateEntry(\n\t\t\t\t\t\tentry,\n\t\t\t\t\t\tlinkPath,\n\t\t\t\t\t\tprevious?.linkMode ?? \"symlink\",\n\t\t\t\t\t\tprevious?.approved ?? false\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t\tresults.push({ identity: entry.identity, status: \"restored\" });\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tawait this.createLinkAtomic(linkPath, entry.sourcePath, entry.sourceIsFile);\n\t\t\tchanged = true;\n\t\t\tstateEntries.push(this.toStateEntry(entry, linkPath, \"symlink\", false));\n\t\t\tresults.push({ identity: entry.identity, status: \"restored\" });\n\t\t}\n\n\t\treturn { changed, entries: results, state: { version: 1, entries: stateEntries } };\n\t}\n\n\t/** Adopt a matching legacy link without recreating it. */\n\tasync adoptLegacyLink(input: {\n\t\tworkspaceDir: string;\n\t\tentry: PlannedContentEntry;\n\t}): Promise<MaterializeEntryResult> {\n\t\tconst linkPath = this.resolveLinkPath(input.workspaceDir, input.entry);\n\t\tconst stat = await fsp.lstat(linkPath).catch(() => null);\n\t\tif (stat?.isSymbolicLink() && (await fsp.readlink(linkPath)) === input.entry.sourcePath) {\n\t\t\treturn { identity: input.entry.identity, status: \"adopted\" };\n\t\t}\n\t\treturn {\n\t\t\tidentity: input.entry.identity,\n\t\t\tstatus: \"conflict\",\n\t\t\tmessage: \"Legacy link does not match\",\n\t\t};\n\t}\n\n\t/** Inspect a legacy ~/.agents link; report without deleting. */\n\tasync inspectLegacyUserLink(input: {\n\t\thomeDir: string;\n\t\tentry: PlannedContentEntry;\n\t}): Promise<MaterializeEntryResult> {\n\t\tconst kindDir = input.entry.kind === \"agent\" ? \"agents\" : \"skills\";\n\t\tconst legacyPath = path.join(\n\t\t\tinput.homeDir,\n\t\t\t\".agents\",\n\t\t\tkindDir,\n\t\t\tinput.entry.sourceIsFile ? path.basename(input.entry.sourcePath) : input.entry.name\n\t\t);\n\t\tconst stat = await fsp.lstat(legacyPath).catch(() => null);\n\t\tif (stat?.isSymbolicLink() && (await fsp.readlink(legacyPath)) === input.entry.sourcePath) {\n\t\t\treturn {\n\t\t\t\tidentity: input.entry.identity,\n\t\t\t\tstatus: \"adopted\",\n\t\t\t\tmessage: \"migration_available\",\n\t\t\t};\n\t\t}\n\t\treturn { identity: input.entry.identity, status: \"conflict\", message: \"No legacy link found\" };\n\t}\n\n\tprivate resolveLinkPath(workspaceDir: string, entry: PlannedContentEntry): string {\n\t\tconst kindDir = KIND_TO_DIR[entry.kind];\n\t\tif (!kindDir) {\n\t\t\tthrow new Error(`Unsupported link kind: ${entry.kind}`);\n\t\t}\n\t\tif (isDirectLinkRoot(workspaceDir)) {\n\t\t\tconst basename = entry.sourceIsFile ? path.basename(entry.sourcePath) : entry.name;\n\t\t\treturn path.join(workspaceDir, basename);\n\t\t}\n\t\tconst basename = entry.sourceIsFile ? path.basename(entry.sourcePath) : entry.name;\n\t\treturn path.join(workspaceDir, \".github\", kindDir, basename);\n\t}\n\n\tprivate async createLinkAtomic(\n\t\tlinkPath: string,\n\t\tsourcePath: string,\n\t\tsourceIsFile: boolean\n\t): Promise<void> {\n\t\tawait fsp.mkdir(path.dirname(linkPath), { recursive: true });\n\t\tconst tmpPath = `${linkPath}.tmp-${process.pid}`;\n\t\tawait fsp.symlink(sourcePath, tmpPath, sourceIsFile ? \"file\" : \"dir\");\n\t\tawait fsp.rename(tmpPath, linkPath);\n\t}\n\n\tprivate toStateEntry(\n\t\tentry: PlannedContentEntry,\n\t\tlinkPath: string,\n\t\tlinkMode: CopilotLinkMode,\n\t\tapproved: boolean\n\t): WorkspaceMaterializedEntry {\n\t\t// State link paths feed the workspace exclude store, whose patterns\n\t\t// git resolves against the repository root — never against\n\t\t// process.cwd(). Personal-scope direct roots have no workspace to\n\t\t// anchor against, so their state keeps the absolute link path.\n\t\treturn {\n\t\t\tidentity: entry.identity,\n\t\t\trepositoryId: entry.repositoryId,\n\t\t\tpluginId: entry.pluginId,\n\t\t\tkind: entry.kind,\n\t\t\tsourcePath: entry.sourcePath,\n\t\t\tlinkPath: this.toStateLinkPath(linkPath),\n\t\t\tlinkMode,\n\t\t\tdigest: entry.digest,\n\t\t\tapproved,\n\t\t};\n\t}\n\n\tprivate toStateLinkPath(linkPath: string): string {\n\t\tif (isDirectLinkRoot(linkPath) || isDirectLinkRoot(path.dirname(linkPath))) {\n\t\t\treturn toPosix(linkPath);\n\t\t}\n\t\t// Workspace-scope materialization: anchor the recorded link path to\n\t\t// the workspace root by walking out of the .github/<kind> subtree\n\t\t// the link lives in, so exclude patterns stay cwd-independent.\n\t\tlet cursor = path.dirname(linkPath);\n\t\twhile (path.basename(cursor) !== \".github\" && cursor !== path.dirname(cursor)) {\n\t\t\tcursor = path.dirname(cursor);\n\t\t}\n\t\tif (path.basename(cursor) !== \".github\") {\n\t\t\treturn toPosix(linkPath);\n\t\t}\n\t\tconst workspaceRoot = path.dirname(cursor);\n\t\tconst relative = toPosix(path.relative(workspaceRoot, linkPath));\n\t\treturn relative === \"\" ? toPosix(linkPath) : relative;\n\t}\n}\n\nfunction toPosix(value: string): string {\n\treturn value.split(path.sep).join(\"/\");\n}\n\n/**\n * Personal-scope link roots are passed as the concrete host target directory\n * (for example the verified '.copilot/skills' directory). They are marked\n * with a trailing separator so they can be distinguished from workspace\n * roots, which always nest links under '.github/<kind>'.\n */\nfunction isDirectLinkRoot(workspaceDir: string): boolean {\n\treturn workspaceDir.endsWith(path.sep) || workspaceDir.endsWith(\"/\");\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { getServicemeHome } from \"../paths/userHome\";\nimport { isMaterializedProjection, materializePluginProjection } from \"./plugin-materializer\";\n\n/**\n * Copilot plugin whole-package registration.\n *\n * Installs a plugin.json package by projecting its canonical checkout\n * into SERVICEME's own registry location\n * (`~/.serviceme/copilot-plugins/<repoId>:<pluginId>`), enabled through\n * a path-keyed `chat.pluginLocations` entry (extension-owned).\n *\n * The projection is MATERIALIZED (see plugin-materializer.ts): a real\n * directory whose entries symlink the resolved checkout content and\n * whose layout matches what VS Code loads (content under the\n * `com.github.copilot/` namespace, manifest at the root and under\n * `.plugin/`).\n *\n * LOCATION HISTORY — everything under `~/.copilot` turned out to be VS\n * Code territory whose reconciliation/uninstall flows DELETE\n * hand-written projections (verified on-machine for both\n * `installed-plugins/` and `serviceme-plugins/`). The registry therefore\n * lives under the SERVICEME home, which VS Code never touches, and the\n * constructor migrates surviving records/projections from the legacy\n * locations.\n *\n * Scope note: plugin packages are PERSONAL-scope only — the\n * projection is user-level and identical for every caller. The\n * `scopes` record shape stays for compatibility; an omitted scope\n * registers as personal.\n *\n * `chat.pluginLocations` (VS Code user settings) is intentionally\n * NOT handled here — it can only be written by the extension layer.\n */\nexport interface CopilotPluginRegistrarOptions {\n\t/** Projection root. Defaults to `${SERVICEME_HOME}/copilot-plugins`. */\n\tpluginsDir?: string;\n\t/** `~/.copilot` — migration source only (VS Code owns that tree). */\n\tcopilotDir?: string;\n}\n\nexport interface CopilotPluginRegisterInput {\n\trepoId: string;\n\tpluginId: string;\n\t/** Canonical checkout plugin directory (the whole package root). */\n\tpluginDir: string;\n\t/** @deprecated Projection root comes from the constructor; ignored. */\n\tcopilotDir?: string;\n\tscope?: \"workspace\" | \"personal\";\n\tdisplayName?: string;\n\tversion?: string;\n}\n\nexport interface CopilotPluginRegistration {\n\t/** `${repoId}:${pluginId}` — path-safe. */\n\tregistrationId: string;\n\trepoId: string;\n\tpluginId: string;\n\tpluginDir: string;\n\tdisplayName?: string;\n\tversion?: string;\n\tscopes: Array<\"workspace\" | \"personal\">;\n}\n\nconst REGISTRY_FILE = \"registrations.json\";\n/** Registry dir name under the SERVICEME home. */\nconst INSTALLED_DIR = \"copilot-plugins\";\n\n/**\n * Path-safety gate for ids that become directory names under the\n * registry root. Rejects separators, traversal and control/empty\n * segments so a hostile repo/plugin id can never escape it.\n */\nfunction assertPathSafeId(value: string, label: string): void {\n\tconst hasControlCharacter = [...value].some((character) => {\n\t\tconst code = character.charCodeAt(0);\n\t\treturn code > 0 && code < 0x20;\n\t});\n\tif (\n\t\tvalue.length === 0 ||\n\t\tvalue.includes(\"/\") ||\n\t\tvalue.includes(\"\\\\\") ||\n\t\tvalue.includes(\"..\") ||\n\t\tvalue.startsWith(\".\") ||\n\t\thasControlCharacter\n\t) {\n\t\tconst error = new Error(`invalid_${label}: ${value}`) as Error & { code?: string };\n\t\terror.code = `invalid_${label}`;\n\t\tthrow error;\n\t}\n}\n\nfunction isScopeArray(value: unknown): value is Array<\"workspace\" | \"personal\"> {\n\treturn Array.isArray(value) && value.every((s) => s === \"workspace\" || s === \"personal\");\n}\n\nexport class CopilotPluginRegistrar {\n\tprivate readonly pluginsDir: string;\n\tprivate readonly copilotDir: string | undefined;\n\tprivate migrated = false;\n\n\tconstructor(options: CopilotPluginRegistrarOptions = {}) {\n\t\tthis.pluginsDir = path.resolve(\n\t\t\toptions.pluginsDir ?? path.join(getServicemeHome(), INSTALLED_DIR)\n\t\t);\n\t\tthis.copilotDir = options.copilotDir;\n\t}\n\n\t/** Registry root — resolved, used as the boundary for all path joins. */\n\tprivate installedDir(): string {\n\t\treturn this.pluginsDir;\n\t}\n\n\t/** Projection path for an id, with an explicit containment check. */\n\tprivate linkPath(registrationId: string): string {\n\t\tconst root = this.installedDir();\n\t\tconst resolved = path.resolve(root, registrationId);\n\t\tif (!resolved.startsWith(root + path.sep)) {\n\t\t\tthrow new Error(`registration id escapes the plugins dir: ${registrationId}`);\n\t\t}\n\t\treturn resolved;\n\t}\n\n\tprivate registryPath(): string {\n\t\treturn path.join(this.installedDir(), REGISTRY_FILE);\n\t}\n\n\t/**\n\t * One-shot migration away from `~/.copilot` (VS Code territory):\n\t * carries over registry records and surviving projections from both\n\t * legacy layouts and removes the old trees. Self-guarding when the\n\t * source and destination coincide; safe to run repeatedly.\n\t */\n\tprivate async migrateFromCopilotDir(): Promise<void> {\n\t\tif (this.migrated) return;\n\t\tthis.migrated = true;\n\t\tconst copilotDir = this.copilotDir;\n\t\tif (!copilotDir) return;\n\t\tconst dest = this.installedDir();\n\t\tawait fs.mkdir(dest, { recursive: true });\n\t\tconst registry = await this.readRegistry();\n\n\t\t// Layout 2 (2026-09-03 → 2026-09-04): `~/.copilot/serviceme-plugins/`.\n\t\tconst prevDir = path.resolve(copilotDir, \"serviceme-plugins\");\n\t\tif (prevDir !== dest) {\n\t\t\ttry {\n\t\t\t\tconst raw = JSON.parse(await fs.readFile(path.join(prevDir, REGISTRY_FILE), \"utf8\")) as {\n\t\t\t\t\tplugins?: CopilotPluginRegistration[];\n\t\t\t\t};\n\t\t\t\tfor (const reg of raw.plugins ?? []) {\n\t\t\t\t\tif (\n\t\t\t\t\t\ttypeof reg?.registrationId === \"string\" &&\n\t\t\t\t\t\ttypeof reg?.pluginDir === \"string\" &&\n\t\t\t\t\t\tisScopeArray(reg.scopes) &&\n\t\t\t\t\t\t!registry.has(reg.registrationId)\n\t\t\t\t\t) {\n\t\t\t\t\t\tregistry.set(reg.registrationId, reg);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// no previous registry — nothing to carry over\n\t\t\t}\n\t\t\t// Move surviving projection directories wholesale.\n\t\t\tconst entries = await fs.readdir(prevDir, { withFileTypes: true }).catch(() => []);\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (entry.name === REGISTRY_FILE) continue;\n\t\t\t\tconst from = path.join(prevDir, entry.name);\n\t\t\t\tconst to = path.resolve(dest, entry.name);\n\t\t\t\tif (!to.startsWith(dest + path.sep)) continue;\n\t\t\t\tawait fs.rm(to, { force: true, recursive: true });\n\t\t\t\tawait fs.rename(from, to).catch(() => undefined);\n\t\t\t}\n\t\t\tawait fs.rm(prevDir, { force: true, recursive: true }).catch(() => undefined);\n\t\t\tawait this.writeRegistry(registry);\n\t\t}\n\n\t\t// Layout 1 (pre-2026-09-03): `installed-plugins/serviceme/` — a\n\t\t// phantom \"serviceme marketplace\" in VS Code's scanner.\n\t\tconst legacyDir = path.resolve(copilotDir, \"installed-plugins\", \"serviceme\");\n\t\tlet legacy: Map<string, CopilotPluginRegistration>;\n\t\ttry {\n\t\t\tconst raw = JSON.parse(await fs.readFile(path.join(legacyDir, REGISTRY_FILE), \"utf8\")) as {\n\t\t\t\tplugins?: CopilotPluginRegistration[];\n\t\t\t};\n\t\t\tlegacy = new Map(\n\t\t\t\t(raw.plugins ?? [])\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(reg) =>\n\t\t\t\t\t\t\ttypeof reg?.registrationId === \"string\" &&\n\t\t\t\t\t\t\ttypeof reg?.pluginDir === \"string\" &&\n\t\t\t\t\t\t\tisScopeArray(reg.scopes)\n\t\t\t\t\t)\n\t\t\t\t\t.map((reg) => [reg.registrationId, reg])\n\t\t\t);\n\t\t} catch {\n\t\t\treturn; // no legacy registry — nothing to migrate\n\t\t}\n\t\tconst merged = await this.readRegistry();\n\t\tfor (const [id, reg] of legacy) {\n\t\t\tif (!merged.has(id)) {\n\t\t\t\tmerged.set(id, reg);\n\t\t\t}\n\t\t\t// Re-point the projection symlink into the registry root.\n\t\t\tconst legacyLink = path.join(legacyDir, id);\n\t\t\tconst stat = await fs.lstat(legacyLink).catch(() => undefined);\n\t\t\tif (stat?.isSymbolicLink()) {\n\t\t\t\tawait this.writeRegistry(merged);\n\t\t\t\tawait fs.mkdir(dest, { recursive: true });\n\t\t\t\tawait fs.rm(this.linkPath(id), { force: true, recursive: true });\n\t\t\t\tawait fs.symlink(reg.pluginDir, this.linkPath(id), \"dir\");\n\t\t\t}\n\t\t}\n\t\tawait this.writeRegistry(merged);\n\t\tawait fs.rm(legacyDir, { force: true, recursive: true });\n\t}\n\n\tprivate async readRegistry(): Promise<Map<string, CopilotPluginRegistration>> {\n\t\tconst map = new Map<string, CopilotPluginRegistration>();\n\t\ttry {\n\t\t\tconst raw = JSON.parse(await fs.readFile(this.registryPath(), \"utf8\")) as {\n\t\t\t\tplugins?: CopilotPluginRegistration[];\n\t\t\t};\n\t\t\tfor (const reg of raw.plugins ?? []) {\n\t\t\t\tif (\n\t\t\t\t\ttypeof reg?.registrationId === \"string\" &&\n\t\t\t\t\ttypeof reg?.pluginDir === \"string\" &&\n\t\t\t\t\tisScopeArray(reg.scopes)\n\t\t\t\t) {\n\t\t\t\t\tmap.set(reg.registrationId, reg);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// absent or corrupt registry = empty; scans rebuild from disk\n\t\t}\n\t\treturn map;\n\t}\n\n\tprivate async writeRegistry(map: Map<string, CopilotPluginRegistration>): Promise<void> {\n\t\tconst file = this.registryPath();\n\t\tawait fs.mkdir(path.dirname(file), { recursive: true });\n\t\tawait fs.writeFile(\n\t\t\tfile,\n\t\t\tJSON.stringify({ version: 1, plugins: [...map.values()] }, null, \"\\t\")\n\t\t);\n\t}\n\n\tasync register(input: CopilotPluginRegisterInput): Promise<CopilotPluginRegistration> {\n\t\tassertPathSafeId(input.repoId, \"repo_id\");\n\t\tassertPathSafeId(input.pluginId, \"plugin_id\");\n\t\tawait this.migrateFromCopilotDir();\n\t\tconst registrationId = `${input.repoId}:${input.pluginId}`;\n\t\tconst link = this.linkPath(registrationId);\n\t\tconst registry = await this.readRegistry();\n\t\tconst existing = registry.get(registrationId);\n\n\t\t// Conflict: something exists at the target that is neither our\n\t\t// symlink nor our materialized projection.\n\t\tconst currentLink = await fs.readlink(link).catch(() => undefined);\n\t\tif (currentLink !== undefined && currentLink !== input.pluginDir) {\n\t\t\tconst error = new Error(\n\t\t\t\t`link_conflict: ${link} already points at ${currentLink}, refusing to re-target`\n\t\t\t) as Error & { code?: string };\n\t\t\terror.code = \"link_conflict\";\n\t\t\tthrow error;\n\t\t}\n\t\tif (currentLink === undefined) {\n\t\t\tconst dirExists = (await fs.lstat(link).catch(() => undefined)) !== undefined;\n\t\t\t// Foreign = not our materialized projection AND not already in the\n\t\t\t// registry under this id. The registry is our own bookkeeping: a\n\t\t\t// recorded id whose projection lost its marker (VS Code's plugin\n\t\t\t// normalization strips unrecognized files) is still ours to\n\t\t\t// rebuild, while a genuinely unknown directory stays protected.\n\t\t\tconst foreignDir =\n\t\t\t\tdirExists && existing === undefined && !(await isMaterializedProjection(link));\n\t\t\tif (foreignDir) {\n\t\t\t\tconst error = new Error(\n\t\t\t\t\t`link_conflict: ${link} exists and is not a SERVICEME projection, refusing to replace`\n\t\t\t\t) as Error & { code?: string };\n\t\t\t\terror.code = \"link_conflict\";\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tawait fs.mkdir(path.dirname(link), { recursive: true });\n\t\t// Materialize the loadable projection (served manifest + content\n\t\t// symlinks). Falls back to a plain directory symlink when the\n\t\t// source has no plugin.json. Re-registering always rebuilds, so a\n\t\t// checkout pull is picked up on the next install/repair.\n\t\tconst repoRoot = path.dirname(path.dirname(input.pluginDir));\n\t\tconst materialized = await materializePluginProjection({\n\t\t\tpluginDir: input.pluginDir,\n\t\t\trepoRoot,\n\t\t\ttargetDir: link,\n\t\t});\n\t\tif (!materialized.materialized) {\n\t\t\tawait fs.rm(link, { force: true, recursive: true });\n\t\t\tawait fs.symlink(input.pluginDir, link, \"dir\");\n\t\t}\n\n\t\tconst scopes = new Set(existing?.scopes ?? []);\n\t\tscopes.add(input.scope ?? \"personal\");\n\t\tconst registration: CopilotPluginRegistration = {\n\t\t\tregistrationId,\n\t\t\trepoId: input.repoId,\n\t\t\tpluginId: input.pluginId,\n\t\t\tpluginDir: input.pluginDir,\n\t\t\t...(input.displayName !== undefined\n\t\t\t\t? { displayName: input.displayName }\n\t\t\t\t: existing?.displayName !== undefined\n\t\t\t\t\t? { displayName: existing.displayName }\n\t\t\t\t\t: {}),\n\t\t\t...(input.version !== undefined\n\t\t\t\t? { version: input.version }\n\t\t\t\t: existing?.version !== undefined\n\t\t\t\t\t? { version: existing.version }\n\t\t\t\t\t: {}),\n\t\t\tscopes: [...scopes],\n\t\t};\n\t\tregistry.set(registrationId, registration);\n\t\tawait this.writeRegistry(registry);\n\t\treturn registration;\n\t}\n\n\tasync unregister(input: {\n\t\tregistrationId: string;\n\t\t/** @deprecated Projection root comes from the constructor; ignored. */\n\t\tcopilotDir?: string;\n\t\tscope?: \"workspace\" | \"personal\";\n\t}): Promise<void> {\n\t\tassertPathSafeId(input.registrationId, \"registration_id\");\n\t\tawait this.migrateFromCopilotDir();\n\t\tconst registry = await this.readRegistry();\n\t\tconst existing = registry.get(input.registrationId);\n\t\tif (!existing) return;\n\n\t\tconst scopes = input.scope ? existing.scopes.filter((s) => s !== input.scope) : [];\n\t\tif (scopes.length > 0) {\n\t\t\tregistry.set(input.registrationId, { ...existing, scopes });\n\t\t\tawait this.writeRegistry(registry);\n\t\t\treturn; // another scope still holds the link\n\t\t}\n\t\tregistry.delete(input.registrationId);\n\t\tawait this.writeRegistry(registry);\n\t\tawait fs.rm(this.linkPath(input.registrationId), {\n\t\t\tforce: true,\n\t\t\trecursive: true,\n\t\t});\n\t}\n\n\tasync list(_input?: { copilotDir?: string }): Promise<CopilotPluginRegistration[]> {\n\t\tawait this.migrateFromCopilotDir();\n\t\tconst registry = await this.readRegistry();\n\t\tfor (const registrationId of registry.keys()) {\n\t\t\t// Registry entries become path segments — guard against a\n\t\t\t// hand-edited registry smuggling traversal into linkPath().\n\t\t\tassertPathSafeId(registrationId, \"registration_id\");\n\t\t}\n\t\t// Prune records whose projection no longer exists (manual cleanup).\n\t\t// Projections are either our symlink (legacy) or a materialized\n\t\t// real directory carrying the SERVICEME marker.\n\t\tconst alive: CopilotPluginRegistration[] = [];\n\t\tfor (const reg of registry.values()) {\n\t\t\tconst link = this.linkPath(reg.registrationId);\n\t\t\tconst linkStat = await fs.lstat(link).catch(() => undefined);\n\t\t\tconst isAlive =\n\t\t\t\tlinkStat !== undefined &&\n\t\t\t\t(linkStat.isSymbolicLink() || (await isMaterializedProjection(link)));\n\t\t\tif (isAlive) {\n\t\t\t\talive.push(reg);\n\t\t\t} else {\n\t\t\t\tregistry.delete(reg.registrationId);\n\t\t\t}\n\t\t}\n\t\tif (alive.length !== registry.size) {\n\t\t\tawait this.writeRegistry(registry);\n\t\t}\n\t\treturn alive;\n\t}\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\n/**\n * Materialize a plugin.json package into a Copilot-loadable projection.\n *\n * WHY THIS EXISTS: marketplace plugin repos in the wild (github/awesome-copilot\n * being the canonical one) keep the actual content at the REPOSITORY ROOT and\n * let `plugins/<name>/plugin.json` reference it with plugin-relative paths\n * (`./agents/ai-team-dev.md`, `./skills/ai-team-orchestration/`). Upstream's\n * release pipeline (awesome-copilot `eng/materialize-plugins.mjs`) runs at\n * publish time to copy those sources INTO the plugin directory — the\n * marketplace serves the materialized tree, never the raw checkout. A symlink\n * to the raw checkout therefore yields a plugin whose declared agents/skills\n * resolve to nothing.\n *\n * This module reproduces the materialization for the LOCAL\n * (`chat.pluginLocations`) channel: every declared entry is copied as a REAL\n * dereferenced file (see copyProjectionEntry for why symlinks are not an\n * option) to its declared TOP-LEVEL path — the layout VS Code's local-plugin\n * conventions scan reads (`skills/`, `agents/*.agent.md`, …):\n *\n * ./agents/<base>.md → <repoRoot>/agents/<base>.agent.md → <target>/agents/<base>.agent.md\n * ./skills/<name>[/] → <repoRoot>/skills/<name>/ → <target>/skills/<name>/\n * ./hooks/<rel> → <repoRoot>/hooks/<rel> → <target>/hooks/<rel>\n *\n * Resolution order per entry: plugin-dir-relative first (self-contained\n * plugins stay self-contained), then repo root, then the `.agent.md`\n * suffix convention for agent files. File entries keep the SOURCE basename\n * (flat agents must carry the `.agent.md` suffix to be recognized).\n *\n * The served `plugin.json` is deliberately SCHEMA-LESS and identity-only\n * (see SERVED_SPEC_FIELDS): a schema-bearing manifest switches VS Code to\n * the Agent Plugins spec component paths, which read agents from\n * `com.github.copilot/agents/` — a namespace layout this projection does\n * not produce. The manifest is written at the projection root AND under\n * `.plugin/`, and the provenance marker lives in `.plugin/` as well —\n * `.plugin/` is the one directory VS Code's normalization pass preserves\n * (everything else unrecognized, e.g. a top-level README, gets deleted on\n * plugin toggle).\n */\n\n/** VS Code's local-plugin marker directory — its contents survive the\n * normalization pass that strips unrecognized entries. */\nconst PLUGIN_MARKER_DIR = \".plugin\";\n\n/** Fields kept in the served manifest — the identity-only shape VS Code\n * itself writes when normalizing a local plugin. A LOCAL\n * (`chat.pluginLocations`) projection pairs with the legacy directory\n * conventions: VS Code honors a manifest only when its `$schema` starts\n * with `https://agent-plugins.org/schemas/`, and such schema-bearing\n * manifests switch discovery to the spec component paths, which read\n * agents from `com.github.copilot/agents/` — a namespace layout this\n * projection does not use. A schema-less manifest is ignored wholesale\n * and discovery falls back to the convention scan (top-level `skills/`,\n * `agents/*.agent.md`, …), which is exactly the layout materialized\n * below. Serving anything beyond these identity fields would only risk\n * re-triggering the spec path. */\nconst SERVED_SPEC_FIELDS = new Set([\"name\", \"version\", \"description\"]);\n\n/** Marker recording the projection's provenance — detection + refresh metadata. */\nconst MARKER_FILE = \".serviceme-materialized.json\";\n\nexport interface MaterializeMarker {\n\tservicemeMaterialized: true;\n\tpluginDir: string;\n\trepoRoot: string;\n}\n\nexport interface MaterializeResult {\n\t/** true when the projection was materialized; false when the source\n\t * plugin has no usable plugin.json (caller falls back to a plain link). */\n\tmaterialized: boolean;\n\t/** Relative paths written into the projection. */\n\tentries: string[];\n\t/** Entries whose source could not be resolved (warned, skipped). */\n\tunresolved: Array<{ path: string; reason: string }>;\n}\n\n/** Strip a leading \"./\" and trailing slashes — the normalized form used for dest paths. */\nfunction normalizeEntry(entry: string): string {\n\treturn entry.replace(/^\\.\\//, \"\").replace(/\\/+$/, \"\");\n}\n\n/** Plugin-relative composition entry (any namespace other than Copilot's own). */\ninterface CompositionRef {\n\tnamespace: string;\n\tfield: string;\n\tentry: string;\n}\n\nfunction collectRefs(extensions: Record<string, unknown> | undefined): CompositionRef[] {\n\tconst refs: Array<CompositionRef> = [];\n\tfor (const [namespace, value] of Object.entries(extensions ?? {})) {\n\t\tif (typeof value !== \"object\" || value === null) continue;\n\t\tfor (const [field, entries] of Object.entries(value as Record<string, unknown>)) {\n\t\t\tif (!Array.isArray(entries)) continue;\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (typeof entry === \"string\" && entry.startsWith(\"./\")) {\n\t\t\t\t\trefs.push({ namespace, field, entry });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn refs;\n}\n\n/**\n * Resolve one declared plugin-relative path to an existing source, trying:\n * 1. inside the plugin dir (self-contained plugin),\n * 2. the repo root (awesome-copilot-style composition),\n * 3. the `.agent.md` suffix convention for declared agent files.\n * Returns null when nothing exists.\n */\nasync function resolveSource(\n\tentry: string,\n\tpluginDir: string,\n\trepoRoot: string\n): Promise<string | null> {\n\tconst normalized = normalizeEntry(entry);\n\tconst candidates: string[] = [path.join(pluginDir, normalized), path.join(repoRoot, normalized)];\n\t// ./agents/foo.md is declared as `foo.md` but shipped as `foo.agent.md`.\n\tif (normalized.startsWith(\"agents/\") && normalized.endsWith(\".md\")) {\n\t\tconst base = path.basename(normalized, \".md\");\n\t\tcandidates.push(path.join(repoRoot, \"agents\", `${base}.agent.md`));\n\t}\n\tfor (const candidate of candidates) {\n\t\tconst stat = await fs.stat(candidate).catch(() => undefined);\n\t\tif (stat) return candidate;\n\t}\n\treturn null;\n}\n\n/** Destination mapping, verified per VS Code channel: for a\n * `chat.pluginLocations` (\"Local\") plugin the panel/engine apply the\n * LOCAL-plugin conventions — top-level `skills/` is the only location a\n * skill is recognized from, and agents are read from the top-level\n * `agents/*.agent.md` convention (the spec's `com.github.copilot/`\n * component paths only apply to schema-bearing served manifests, which\n * we intentionally do not produce — see SERVED_SPEC_FIELDS). Every\n * declared entry therefore keeps its declared top-level path, mirroring\n * the local \"创建插件\" layout. */\nfunction destinationFor(ref: CompositionRef): string {\n\treturn normalizeRefPath(ref.entry);\n}\n\nfunction normalizeRefPath(entry: string): string {\n\treturn entry.replace(/^\\.\\//, \"\").replace(/\\/+$/, \"\");\n}\n\n/**\n * Materialize one entry as a REAL, dereferenced copy. Symlinks are not\n * an option here — verified on-machine: VS Code's plugin scanner resolves\n * a symlinked plugin ROOT but does not follow symlinks encountered INSIDE\n * the tree, so linked entries contribute zero capabilities (the\n * symlink-free `hello` projection next to it was fully recognized).\n * Upstream's materializer copies for the same reason. Consequence: a\n * checkout pull needs a re-install (repair) to refresh the projection.\n */\nasync function copyProjectionEntry(source: string, dest: string): Promise<void> {\n\tawait fs.mkdir(path.dirname(dest), { recursive: true });\n\tawait fs.rm(dest, { force: true, recursive: true });\n\tconst stat = await fs.stat(source);\n\tif (stat.isDirectory()) {\n\t\tawait fs.cp(source, dest, { recursive: true, dereference: true });\n\t} else {\n\t\tawait fs.copyFile(source, dest);\n\t}\n}\n\n/** Served manifest: identity fields only and deliberately schema-less —\n * see SERVED_SPEC_FIELDS for why the convention scan (not the spec\n * component paths) must stay the discovery path for local projections. */\nexport function servedPluginManifest(raw: Record<string, unknown>): Record<string, unknown> {\n\tconst served: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(raw)) {\n\t\tif (!SERVED_SPEC_FIELDS.has(key)) continue;\n\t\tserved[key] = value;\n\t}\n\treturn served;\n}\n\nexport interface MaterializePluginProjectionInput {\n\t/** Raw checkout plugin directory (contains plugin.json). */\n\tpluginDir: string;\n\t/** Repository checkout root the composition entries resolve against. */\n\trepoRoot: string;\n\t/** Projection destination (a REAL directory — not a symlink). */\n\ttargetDir: string;\n}\n\n/**\n * Build (or refresh) the loadable projection at `targetDir`. Idempotent:\n * the previous projection is replaced wholesale so re-registering after a\n * checkout pull re-points every entry. Returns `materialized: false` when\n * the source has no plugin.json — the caller should fall back to linking\n * the raw dir.\n */\nexport async function materializePluginProjection(\n\tinput: MaterializePluginProjectionInput\n): Promise<MaterializeResult> {\n\tconst { pluginDir, repoRoot, targetDir } = input;\n\tconst result: MaterializeResult = { materialized: false, entries: [], unresolved: [] };\n\n\tconst manifestPath = path.join(pluginDir, \"plugin.json\");\n\tlet raw: Record<string, unknown>;\n\ttry {\n\t\traw = JSON.parse(await fs.readFile(manifestPath, \"utf8\")) as Record<string, unknown>;\n\t} catch {\n\t\treturn result;\n\t}\n\n\tawait fs.rm(targetDir, { force: true, recursive: true });\n\tawait fs.mkdir(targetDir, { recursive: true });\n\n\tfor (const ref of collectRefs(raw.extensions as Record<string, unknown> | undefined)) {\n\t\tconst source = await resolveSource(ref.entry, pluginDir, repoRoot);\n\t\tif (!source) {\n\t\t\tresult.unresolved.push({\n\t\t\t\tpath: ref.entry,\n\t\t\t\treason: `source not found in ${pluginDir} or ${repoRoot}`,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tlet destRel = destinationFor(ref);\n\t\t// File entries keep the SOURCE basename: declared `./agents/x.md`\n\t\t// ships as `x.agent.md`, and Copilot only recognizes flat agent\n\t\t// files carrying the .agent.md suffix (the .instructions.md /\n\t\t// .prompt.md conventions behave the same way).\n\t\tconst sourceStat = await fs.stat(source);\n\t\tif (!sourceStat.isDirectory()) {\n\t\t\tconst sourceBase = path.basename(source);\n\t\t\tif (path.basename(destRel) !== sourceBase) {\n\t\t\t\tdestRel = path.join(path.dirname(destRel), sourceBase);\n\t\t\t}\n\t\t}\n\t\tconst dest = path.join(targetDir, destRel);\n\t\tawait copyProjectionEntry(source, dest);\n\t\tresult.entries.push(destRel);\n\t}\n\n\t// Manifest placement mirrors what VS Code itself writes for a local\n\t// plugin after normalizing the directory: a minimal manifest at the\n\t// root AND an identical copy under `.plugin/`. The readme is\n\t// intentionally NOT projected — VS Code's normalization deletes\n\t// entries it does not recognize (verified on-machine).\n\tconst root = path.resolve(targetDir);\n\t/** Join inside the projection with an explicit containment check. */\n\tconst inside = (...segments: string[]): string => {\n\t\tconst resolved = path.resolve(root, ...segments);\n\t\tif (!resolved.startsWith(root + path.sep)) {\n\t\t\tthrow new Error(`projection path escaped targetDir: ${resolved}`);\n\t\t}\n\t\treturn resolved;\n\t};\n\tawait fs.writeFile(\n\t\tinside(\"plugin.json\"),\n\t\tJSON.stringify(servedPluginManifest(raw), null, 2) + \"\\n\"\n\t);\n\tconst pluginMarkerDir = inside(PLUGIN_MARKER_DIR);\n\tawait fs.mkdir(pluginMarkerDir, { recursive: true });\n\tawait fs.writeFile(\n\t\tpath.join(pluginMarkerDir, \"plugin.json\"),\n\t\tJSON.stringify(servedPluginManifest(raw), null, 2) + \"\\n\"\n\t);\n\tconst mcpJson = await fs.stat(path.join(pluginDir, \"mcp.json\")).catch(() => undefined);\n\tif (mcpJson?.isFile()) {\n\t\tawait copyProjectionEntry(path.join(pluginDir, \"mcp.json\"), inside(\"mcp.json\"));\n\t}\n\n\t// Provenance marker lives inside `.plugin/` — the one directory VS\n\t// Code's normalization pass preserves.\n\tconst marker: MaterializeMarker = { servicemeMaterialized: true, pluginDir, repoRoot };\n\tawait fs.writeFile(path.join(pluginMarkerDir, MARKER_FILE), JSON.stringify(marker, null, \"\\t\"));\n\n\tresult.materialized = true;\n\treturn result;\n}\n\n/**\n * True when `dir` is a projection built by {@link materializePluginProjection}\n * (real directory + our marker). Used to distinguish our projections from\n * user content before replacing/removing them.\n */\nexport async function isMaterializedProjection(dir: string): Promise<boolean> {\n\tconst stat = await fs.lstat(dir).catch(() => undefined);\n\tif (!stat?.isDirectory() || stat.isSymbolicLink()) return false;\n\tconst root = path.resolve(dir);\n\tconst markerPath = path.resolve(root, PLUGIN_MARKER_DIR, MARKER_FILE);\n\t// Containment guard: the marker must resolve inside the projection.\n\tif (!markerPath.startsWith(root + path.sep)) return false;\n\tconst marker = await fs.readFile(markerPath, \"utf8\").catch(() => undefined);\n\tif (marker === undefined) return false;\n\ttry {\n\t\tconst parsed = JSON.parse(marker) as MaterializeMarker;\n\t\treturn parsed.servicemeMaterialized === true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n","import type { CopilotArtifactKind } from \"./types\";\n\nexport type CopilotScope = \"workspace\" | \"personal\";\nexport type CopilotInstallIntent = \"available\" | \"selected\" | \"moving\" | \"removing\";\nexport type CopilotMaterializationHealth =\n\t| \"healthy\"\n\t| \"missing\"\n\t| \"drifted\"\n\t| \"conflict\"\n\t| \"source-unavailable\"\n\t| \"unsupported\";\nexport type CopilotLocalGate = \"ready\" | \"approval-required\" | \"configuration-required\";\nexport type CopilotUserStatus = \"healthy\" | \"action-required\" | \"blocked\" | \"not-enabled\";\n\nexport interface CopilotArtifactState {\n\tintent: CopilotInstallIntent;\n\thealth: CopilotMaterializationHealth;\n\tgate: CopilotLocalGate;\n}\n\nexport interface CopilotPackageInstallation {\n\tpackageId: string;\n\tscope: CopilotScope;\n\tselectedArtifactIds: string[];\n\tpinnedVersion?: string;\n}\n\nexport interface CopilotArtifactSummary {\n\tid: string;\n\tpackageId: string;\n\tkind: CopilotArtifactKind;\n\tdisplayName: string;\n\tdescription?: string;\n\tinstallStrategy: \"link\" | \"generated-config\" | \"approval-gated\";\n\trisk: \"none\" | \"review-required\";\n}\n\nexport function deriveCopilotUserStatus(states: CopilotArtifactState[]): CopilotUserStatus {\n\tconst selected = states.filter((state) => state.intent !== \"available\");\n\tif (selected.length === 0) return \"not-enabled\";\n\tif (\n\t\tselected.some((state) =>\n\t\t\t[\"conflict\", \"source-unavailable\", \"unsupported\"].includes(state.health)\n\t\t)\n\t) {\n\t\treturn \"blocked\";\n\t}\n\tif (selected.some((state) => state.health !== \"healthy\" || state.gate !== \"ready\")) {\n\t\treturn \"action-required\";\n\t}\n\treturn \"healthy\";\n}\n","import type {\n\tCopilotArtifactState,\n\tCopilotArtifactSummary,\n\tCopilotPackageInstallation,\n\tCopilotScope,\n\tCopilotUserStatus,\n} from \"./customization-model\";\nimport { deriveCopilotUserStatus } from \"./customization-model\";\n\nexport interface CopilotSourceSummary {\n\tid: string;\n\ttype: \"marketplace\" | \"git\" | \"local\";\n\tdisplayName: string;\n\tupdateCapability: \"pinned\" | \"live\" | \"none\";\n}\nexport interface CopilotPackageDefinition {\n\tid: string;\n\tsourceId: string;\n\tdisplayName: string;\n\tdescription?: string;\n\tversion?: string;\n\tartifacts: CopilotArtifactSummary[];\n\t/** Whole-package registrar installation (Copilot-managed, both scopes) —\n\t * the home package list shows these; per-artifact v1 manifest plugins\n\t * stay in the enabled-content section instead. */\n\twholePackage?: boolean;\n}\nexport interface CopilotArtifactView {\n\tartifact: CopilotArtifactSummary;\n\tstate: CopilotArtifactState;\n}\nexport interface CopilotPackageView {\n\tdefinition: CopilotPackageDefinition;\n\tinstallation: CopilotPackageInstallation;\n\tartifacts: CopilotArtifactView[];\n\tselectedArtifactCount: number;\n\tstatus: CopilotUserStatus;\n}\nexport interface CopilotSourceView extends CopilotSourceSummary {\n\tpackages: CopilotPackageView[];\n}\nexport interface CopilotCustomizationView {\n\tscope: CopilotScope;\n\tgeneratedAt: string;\n\tsources: CopilotSourceView[];\n\tpackages: CopilotPackageView[];\n\tattention: CopilotArtifactView[];\n\tsummary: { packageCount: number; enabledArtifactCount: number; attentionCount: number };\n\tlegacyMigration?: { count: number; sourceLabel: \"~/.agents\" };\n}\nexport interface BuildCustomizationViewInput {\n\tscope: CopilotScope;\n\tsources: CopilotSourceSummary[];\n\tpackages: CopilotPackageDefinition[];\n\tinstallations: CopilotPackageInstallation[];\n\tstatesByArtifactId: Record<string, CopilotArtifactState>;\n\tlegacyCount?: number;\n\tgeneratedAt?: string;\n}\nexport function buildCopilotCustomizationView(\n\tinput: BuildCustomizationViewInput\n): CopilotCustomizationView {\n\tconst sourceIds = new Set(input.sources.map((source) => source.id));\n\tfor (const definition of input.packages) {\n\t\tif (!sourceIds.has(definition.sourceId)) {\n\t\t\tthrow new Error(`Unknown Copilot source ${definition.sourceId} for package ${definition.id}`);\n\t\t}\n\t}\n\tconst packages = input.installations\n\t\t.filter((installation) => installation.scope === input.scope)\n\t\t.map((installation) => {\n\t\t\tconst definition = requirePackage(input.packages, installation.packageId);\n\t\t\tconst artifacts = installation.selectedArtifactIds.map((artifactId) => {\n\t\t\t\tconst artifact = requireArtifact(definition, artifactId);\n\t\t\t\treturn {\n\t\t\t\t\tartifact,\n\t\t\t\t\tstate: requireState(input.statesByArtifactId, artifact.id),\n\t\t\t\t};\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tdefinition,\n\t\t\t\tinstallation,\n\t\t\t\tartifacts,\n\t\t\t\tselectedArtifactCount: artifacts.length,\n\t\t\t\tstatus: deriveCopilotUserStatus(artifacts.map(({ state }) => state)),\n\t\t\t};\n\t\t});\n\tconst attention = packages.flatMap((pkg) =>\n\t\tpkg.artifacts.filter(({ state }) => {\n\t\t\tconst status = deriveCopilotUserStatus([state]);\n\t\t\treturn status === \"action-required\" || status === \"blocked\";\n\t\t})\n\t);\n\tconst sources = input.sources.map((source) => ({\n\t\t...source,\n\t\tpackages: packages.filter((pkg) => pkg.definition.sourceId === source.id),\n\t}));\n\treturn {\n\t\tscope: input.scope,\n\t\tgeneratedAt: input.generatedAt ?? new Date().toISOString(),\n\t\tsources,\n\t\tpackages,\n\t\tattention,\n\t\tsummary: {\n\t\t\tpackageCount: packages.length,\n\t\t\tenabledArtifactCount: packages.reduce((count, pkg) => count + pkg.selectedArtifactCount, 0),\n\t\t\tattentionCount: attention.length,\n\t\t},\n\t\t...(input.legacyCount\n\t\t\t? { legacyMigration: { count: input.legacyCount, sourceLabel: \"~/.agents\" as const } }\n\t\t\t: {}),\n\t};\n}\nfunction requirePackage(\n\tpackages: CopilotPackageDefinition[],\n\tpackageId: string\n): CopilotPackageDefinition {\n\tconst definition = packages.find((candidate) => candidate.id === packageId);\n\tif (!definition) throw new Error(`Unknown Copilot package ${packageId}`);\n\treturn definition;\n}\nfunction requireArtifact(\n\tdefinition: CopilotPackageDefinition,\n\tartifactId: string\n): CopilotArtifactSummary {\n\tconst artifact = definition.artifacts.find((candidate) => candidate.id === artifactId);\n\tif (!artifact) {\n\t\tthrow new Error(`Unknown selected Copilot artifact ${artifactId} for package ${definition.id}`);\n\t}\n\treturn artifact;\n}\nfunction requireState(\n\tstatesByArtifactId: Record<string, CopilotArtifactState>,\n\tartifactId: string\n): CopilotArtifactState {\n\tconst state = statesByArtifactId[artifactId];\n\tif (!state) throw new Error(`Missing Copilot artifact state ${artifactId}`);\n\treturn state;\n}\n","import * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport { getServicemeHome } from \"../paths/userHome\";\n\n/**\n * Machine-local disabled marks for per-artifact skills/agents.\n *\n * Disable keeps the installation declaration (workspace manifest) or\n * the user's install decision intact, but removes the materialized\n * link so neither the Copilot engine nor the renderer can see the\n * entry. The mark prevents the reconciler from resurrecting the link\n * on the next restore. Entries are keyed by scope + identity; a\n * workspace-scope mark only applies to that workspace.\n */\nexport interface DisabledContentEntry {\n\tscope: \"workspace\" | \"user\";\n\trepoId: string;\n\tname: string;\n\tkind: \"skill\" | \"agent\";\n\t/** Required for workspace-scope marks — disables are per workspace. */\n\tworkspaceDir?: string;\n}\n\nexport interface DisabledContentState {\n\tversion: 1;\n\tentries: DisabledContentEntry[];\n}\n\nfunction sameEntry(a: DisabledContentEntry, b: DisabledContentEntry): boolean {\n\tif (a.scope !== b.scope || a.repoId !== b.repoId || a.name !== b.name || a.kind !== b.kind) {\n\t\treturn false;\n\t}\n\t// Workspace marks match on the exact workspace; user marks match any.\n\tif (a.scope === \"workspace\") {\n\t\treturn a.workspaceDir === b.workspaceDir;\n\t}\n\treturn true;\n}\n\n/** Match predicate scoped to one lookup entry. */\nexport type DisabledContentMatcher = (entry: DisabledContentEntry) => boolean;\n\nexport class DisabledContentStore {\n\tprivate readonly homeDir: string;\n\n\tconstructor(options: { homeDir?: string } = {}) {\n\t\tthis.homeDir = options.homeDir ?? getServicemeHome();\n\t}\n\n\t/** Absolute store path: SERVICEME_HOME/copilot/disabled-content.json. */\n\tasync path(): Promise<string> {\n\t\treturn path.join(this.homeDir, \"copilot\", \"disabled-content.json\");\n\t}\n\n\tasync list(): Promise<DisabledContentEntry[]> {\n\t\ttry {\n\t\t\tconst raw = await fsp.readFile(await this.path(), \"utf8\");\n\t\t\tconst parsed = JSON.parse(raw) as DisabledContentState;\n\t\t\treturn Array.isArray(parsed.entries) ? parsed.entries : [];\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync has(matcher: DisabledContentMatcher): Promise<boolean> {\n\t\treturn this.list().then((entries) => entries.some(matcher));\n\t}\n\n\tasync add(entry: DisabledContentEntry): Promise<void> {\n\t\tconst entries = await this.list();\n\t\tif (entries.some((candidate) => sameEntry(candidate, entry))) {\n\t\t\treturn;\n\t\t}\n\t\tawait this.write([...entries, entry]);\n\t}\n\n\tasync remove(entry: DisabledContentEntry): Promise<void> {\n\t\tconst entries = await this.list();\n\t\tconst next = entries.filter((candidate) => !sameEntry(candidate, entry));\n\t\tif (next.length === entries.length) {\n\t\t\treturn;\n\t\t}\n\t\tawait this.write(next);\n\t}\n\n\tprivate async write(entries: DisabledContentEntry[]): Promise<void> {\n\t\tconst statePath = await this.path();\n\t\tawait fsp.mkdir(path.dirname(statePath), { recursive: true });\n\t\tconst tmpPath = `${statePath}.tmp-${process.pid}-${Date.now()}`;\n\t\tawait fsp.writeFile(\n\t\t\ttmpPath,\n\t\t\tJSON.stringify({ version: 1, entries } satisfies DisabledContentState),\n\t\t\t\"utf8\"\n\t\t);\n\t\tawait fsp.rename(tmpPath, statePath);\n\t}\n}\n","import * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport type { PlannedContentEntry } from \"./content-plan\";\nimport type {\n\tHookConfigAdapter,\n\tHookDefinition,\n\tHookEventDefinition,\n\tIntegrationApplyResult,\n\tMcpConfigAdapter,\n\tMcpServerDefinition,\n} from \"./integration-adapters\";\n\n/** Managed server shape stored in mcp.serviceme.json. */\ninterface ManagedMcpServer {\n\ttype: string;\n\tcommand: string;\n\targs?: string[];\n\tenv?: Record<string, string>;\n\ttools?: string[];\n}\n\n/** On-disk shape of .vscode/mcp.serviceme.json. */\ninterface ManagedMcpConfig {\n\tservers: Record<string, ManagedMcpServer>;\n\t_serviceme: Record<string, string[]>;\n}\n\n/** On-disk shape of .vscode/hooks.serviceme.json. */\ninterface ManagedHookConfig {\n\thooks: Record<string, HookEventDefinition[]>;\n\t_serviceme: Record<string, string[]>;\n}\n\n/** Adapter merging MCP servers into the workspace-local VS Code config. */\nexport class FileMcpConfigAdapter implements McpConfigAdapter {\n\t/** Merge one server definition under its entry identity. */\n\tasync applyServer(input: {\n\t\tworkspaceDir: string;\n\t\tentry: PlannedContentEntry;\n\t\tserver: McpServerDefinition;\n\t}): Promise<IntegrationApplyResult> {\n\t\tconst configPath = path.join(input.workspaceDir, \".vscode\", \"mcp.serviceme.json\");\n\t\tconst config = await readManagedMcpConfig(configPath);\n\t\tconst owned = ownedSet(config._serviceme, input.entry.identity);\n\t\towned.add(input.server.name);\n\t\tconfig._serviceme[input.entry.identity] = [...owned].sort();\n\t\tconfig.servers[input.server.name] = {\n\t\t\ttype: input.server.type,\n\t\t\tcommand: input.server.command,\n\t\t\t...(input.server.args ? { args: [...input.server.args] } : {}),\n\t\t\t...(input.server.env ? { env: { ...input.server.env } } : {}),\n\t\t\t...(input.server.tools ? { tools: [...input.server.tools] } : {}),\n\t\t};\n\t\tawait writeJsonAtomic(configPath, config);\n\t\treturn {\n\t\t\tidentity: input.entry.identity,\n\t\t\tconfigPath,\n\t\t\tnames: [input.server.name],\n\t\t\tchanged: true,\n\t\t};\n\t}\n\n\t/** Remove only servers owned by the given identity. */\n\tasync removeEntry(input: { workspaceDir: string; identity: string }): Promise<boolean> {\n\t\tconst configPath = path.join(input.workspaceDir, \".vscode\", \"mcp.serviceme.json\");\n\t\tconst config = await readManagedMcpConfig(configPath);\n\t\tconst owned = config._serviceme[input.identity];\n\t\tif (!owned) return false;\n\t\tfor (const name of owned) {\n\t\t\tif (soleOwner(config._serviceme, name, input.identity)) {\n\t\t\t\tdelete config.servers[name];\n\t\t\t}\n\t\t}\n\t\tdelete config._serviceme[input.identity];\n\t\tawait writeJsonAtomic(configPath, config);\n\t\treturn true;\n\t}\n}\n\n/** Adapter materializing hook scripts and event registrations. */\nexport class FileHookConfigAdapter implements HookConfigAdapter {\n\t/** Copy hook scripts and merge registrations under the entry identity. */\n\tasync applyHook(input: {\n\t\tworkspaceDir: string;\n\t\tentry: PlannedContentEntry;\n\t\thook: HookDefinition;\n\t}): Promise<IntegrationApplyResult> {\n\t\tconst configPath = path.join(input.workspaceDir, \".vscode\", \"hooks.serviceme.json\");\n\t\tconst sourceDir = input.entry.sourceIsFile\n\t\t\t? path.dirname(input.entry.sourcePath)\n\t\t\t: input.entry.sourcePath;\n\t\tconst targetDir = path.join(input.workspaceDir, \".github\", \"hooks\", input.entry.name);\n\t\tawait copyDirPreservingMode(sourceDir, targetDir);\n\n\t\tconst config = await readManagedHookConfig(configPath);\n\t\tconst owned = ownedSet(config._serviceme, input.entry.identity);\n\t\tconst names: string[] = [];\n\t\tfor (const [event, registrations] of Object.entries(input.hook.hooks)) {\n\t\t\tif (!registrations) continue;\n\t\t\t// Merge: other identities may already own registrations for the\n\t\t\t// same lifecycle event; appending preserves their entries.\n\t\t\tconst existing = config.hooks[event] ?? [];\n\t\t\tconst authored = registrations.map((registration) => ({\n\t\t\t\t...registration,\n\t\t\t\tbash: path.posix.join(\n\t\t\t\t\t\".github\",\n\t\t\t\t\t\"hooks\",\n\t\t\t\t\tinput.entry.name,\n\t\t\t\t\tnormalizeHookBash(registration.bash, input.entry.name)\n\t\t\t\t),\n\t\t\t}));\n\t\t\tconfig.hooks[event] = [...existing, ...authored];\n\t\t\tif (!names.includes(input.entry.name)) names.push(input.entry.name);\n\t\t}\n\t\t// Ownership tracks hook directory names (the per-identity unit that\n\t\t// can be removed precisely), not lifecycle events (shared across\n\t\t// identities when registrations merge).\n\t\towned.add(input.entry.name);\n\t\tconfig._serviceme[input.entry.identity] = [...owned].sort();\n\t\tawait writeJsonAtomic(configPath, config);\n\t\treturn { identity: input.entry.identity, configPath, names, changed: true };\n\t}\n\n\t/** Remove registrations owned by the identity and orphaned script copies. */\n\tasync removeEntry(input: { workspaceDir: string; identity: string }): Promise<boolean> {\n\t\tconst configPath = path.join(input.workspaceDir, \".vscode\", \"hooks.serviceme.json\");\n\t\tconst config = await readManagedHookConfig(configPath);\n\t\tconst owned = config._serviceme[input.identity];\n\t\tif (!owned) return false;\n\t\t// Remove exactly the registrations this identity authored: those\n\t\t// whose bash path points into one of its owned hook directories.\n\t\tconst ownedDirs = new Set(owned);\n\t\tfor (const [event, registrations] of Object.entries(config.hooks)) {\n\t\t\tconst remaining = (registrations ?? []).filter((registration) => {\n\t\t\t\tconst marker = \".github/hooks/\";\n\t\t\t\tif (!registration.bash.startsWith(marker)) return true;\n\t\t\t\tconst dir = registration.bash.slice(marker.length).split(\"/\")[0] ?? \"\";\n\t\t\t\treturn !ownedDirs.has(dir);\n\t\t\t});\n\t\t\tif (remaining.length === 0) {\n\t\t\t\tdelete config.hooks[event];\n\t\t\t} else {\n\t\t\t\tconfig.hooks[event] = remaining;\n\t\t\t}\n\t\t}\n\t\tdelete config._serviceme[input.identity];\n\t\tawait writeJsonAtomic(configPath, config);\n\t\tawait removeUnreferencedHookDirs(input.workspaceDir, [...ownedDirs]);\n\t\treturn true;\n\t}\n}\n\n/** Read the managed MCP config, tolerating a missing file. */\nasync function readManagedMcpConfig(configPath: string): Promise<ManagedMcpConfig> {\n\tconst raw = await fsp.readFile(configPath, \"utf8\").catch(() => null);\n\tif (!raw) return { servers: {}, _serviceme: {} };\n\tconst parsed = JSON.parse(raw) as Partial<ManagedMcpConfig>;\n\treturn {\n\t\tservers: parsed.servers ?? {},\n\t\t_serviceme: parsed._serviceme ?? {},\n\t};\n}\n\n/** Read the managed hook config, tolerating a missing file. */\nasync function readManagedHookConfig(configPath: string): Promise<ManagedHookConfig> {\n\tconst raw = await fsp.readFile(configPath, \"utf8\").catch(() => null);\n\tif (!raw) return { hooks: {}, _serviceme: {} };\n\tconst parsed = JSON.parse(raw) as Partial<ManagedHookConfig>;\n\treturn {\n\t\thooks: parsed.hooks ?? {},\n\t\t_serviceme: parsed._serviceme ?? {},\n\t};\n}\n\n/** Collect the current ownership list for an identity without duplicates. */\nfunction ownedSet(ownership: Record<string, string[]>, identity: string): Set<string> {\n\treturn new Set(ownership[identity] ?? []);\n}\n\n/** Whether no other identity also owns the given server or event name. */\nfunction soleOwner(ownership: Record<string, string[]>, name: string, identity: string): boolean {\n\tfor (const [owner, names] of Object.entries(ownership)) {\n\t\tif (owner !== identity && names.includes(name)) return false;\n\t}\n\treturn true;\n}\n\n/** Remove copied hook directories no remaining registration points into. */\nasync function removeUnreferencedHookDirs(\n\tworkspaceDir: string,\n\tcandidateNames: string[]\n): Promise<void> {\n\tif (candidateNames.length === 0) return;\n\tconst configPath = path.join(workspaceDir, \".vscode\", \"hooks.serviceme.json\");\n\tconst remaining = await readManagedHookConfig(configPath);\n\tconst referenced = new Set<string>();\n\tfor (const registrations of Object.values(remaining.hooks)) {\n\t\tfor (const registration of registrations) {\n\t\t\tconst marker = \".github/hooks/\";\n\t\t\tif (!registration.bash.startsWith(marker)) continue;\n\t\t\tconst name = registration.bash.slice(marker.length).split(\"/\")[0] ?? \"\";\n\t\t\tif (name.length > 0) referenced.add(name);\n\t\t}\n\t}\n\tconst hooksRoot = path.join(workspaceDir, \".github\", \"hooks\");\n\tfor (const name of candidateNames) {\n\t\tif (referenced.has(name)) continue;\n\t\tawait fsp.rm(path.join(hooksRoot, name), { recursive: true, force: true });\n\t}\n}\n\n/** Write JSON atomically via a same-directory temporary file and rename. */\nasync function writeJsonAtomic(configPath: string, value: unknown): Promise<void> {\n\tawait fsp.mkdir(path.dirname(configPath), { recursive: true });\n\tconst tmpPath = `${configPath}.tmp-${process.pid}`;\n\tawait fsp.writeFile(tmpPath, `${JSON.stringify(value, null, \"\\t\")}\\n`, \"utf8\");\n\tawait fsp.rename(tmpPath, configPath);\n}\n\n/**\n * Normalize an authored bash path relative to the hook's own directory.\n * awesome-copilot hooks may reference the repo-root path\n * (\"hooks/<name>/guard.sh\") or a self-relative path (\"./scripts/run.sh\");\n * both must land inside the copied .github/hooks/<name>/ tree.\n */\nfunction normalizeHookBash(bash: string, entryName: string): string {\n\tlet value = bash.replace(/^[\\\\/]+/, \"\").replace(/^\\.\\//, \"\");\n\tconst repoRootPrefix = `hooks/${entryName}/`;\n\tif (value.startsWith(repoRootPrefix)) {\n\t\tvalue = value.slice(repoRootPrefix.length);\n\t}\n\treturn value;\n}\n\n/** Copy a directory tree recursively, preserving executable file modes. */\nasync function copyDirPreservingMode(sourceDir: string, targetDir: string): Promise<void> {\n\tawait fsp.mkdir(targetDir, { recursive: true });\n\tconst dirents = await fsp.readdir(sourceDir, { withFileTypes: true });\n\tdirents.sort((a, b) => a.name.localeCompare(b.name));\n\tfor (const dirent of dirents) {\n\t\tconst sourceChild = path.join(sourceDir, dirent.name);\n\t\tconst targetChild = path.join(targetDir, dirent.name);\n\t\tif (dirent.isDirectory()) {\n\t\t\tawait copyDirPreservingMode(sourceChild, targetChild);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!dirent.isFile()) continue;\n\t\tconst stat = await fsp.stat(sourceChild);\n\t\tawait fsp.copyFile(sourceChild, targetChild);\n\t\t// copyFile does not preserve mode on every platform; set it explicitly.\n\t\tawait fsp.chmod(targetChild, stat.mode & 0o777);\n\t}\n}\n","import * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport type { PlannedContentEntry } from \"./content-plan\";\n\n/** MCP server definition as authored in a plugin's mcp.json. */\nexport interface McpServerDefinition {\n\t/** Server name used in the generated local configuration. */\n\tname: string;\n\t/** Transport type: stdio command servers today. */\n\ttype: string;\n\t/** Executable command (validated, never a shell string). */\n\tcommand: string;\n\t/** Arguments passed to the command. */\n\targs?: string[];\n\t/** Environment variable names the server may read. */\n\tenv?: Record<string, string>;\n\t/** Tool allowlist declared by the source. */\n\ttools?: string[];\n}\n\n/** One lifecycle event registration from hooks.json. */\nexport interface HookEventDefinition {\n\t/** Hook executor type; only \"command\" is supported today. */\n\ttype: string;\n\t/** Relative bash entry referenced after materialization. */\n\tbash: string;\n\t/** Working directory hint relative to the workspace root. */\n\tcwd?: string;\n\t/** Environment variable names the hook may read. */\n\tenv?: Record<string, string>;\n\t/** Timeout in seconds. */\n\ttimeoutSec?: number;\n}\n\n/** Hook definition as authored in a repo's hooks/<name>/hooks.json. */\nexport interface HookDefinition {\n\t/** Lifecycle event -> registrations. */\n\thooks: Partial<Record<string, HookEventDefinition[]>>;\n}\n\n/** Result of applying one generated-config entry on this machine. */\nexport interface IntegrationApplyResult {\n\tidentity: string;\n\t/** Path of the machine-local config the adapter manages. */\n\tconfigPath: string;\n\t/** Server or hook names written into the config. */\n\tnames: string[];\n\tchanged: boolean;\n}\n\n/** Machine-local MCP config adapter (generated-config strategy). */\nexport interface McpConfigAdapter {\n\t/** Merge one server definition into the machine-local config. */\n\tapplyServer(input: {\n\t\tworkspaceDir: string;\n\t\tentry: PlannedContentEntry;\n\t\tserver: McpServerDefinition;\n\t}): Promise<IntegrationApplyResult>;\n\t/** Remove servers previously written for this entry. */\n\tremoveEntry(input: { workspaceDir: string; identity: string }): Promise<boolean>;\n}\n\n/** Machine-local hook registration adapter (approval-gated strategy). */\nexport interface HookConfigAdapter {\n\t/** Materialize hook scripts and register lifecycle events. */\n\tapplyHook(input: {\n\t\tworkspaceDir: string;\n\t\tentry: PlannedContentEntry;\n\t\thook: HookDefinition;\n\t}): Promise<IntegrationApplyResult>;\n\t/** Remove registrations previously written for this entry. */\n\tremoveEntry(input: { workspaceDir: string; identity: string }): Promise<boolean>;\n}\n\n/** Parse and validate a plugin mcp.json payload. */\nexport function parseMcpJson(raw: string): McpServerDefinition[] {\n\tconst parsed = JSON.parse(raw) as { mcpServers?: Record<string, unknown> };\n\tconst servers = parsed.mcpServers;\n\tif (!servers || typeof servers !== \"object\") {\n\t\treturn [];\n\t}\n\treturn Object.entries(servers).map(([name, value]) => {\n\t\tif (value === null || typeof value !== \"object\") {\n\t\t\tthrow new Error(`mcp.json server ${name} is not an object`);\n\t\t}\n\t\tconst server = value as Record<string, unknown>;\n\t\tif (typeof server.command !== \"string\" || server.command.length === 0) {\n\t\t\tthrow new Error(`mcp.json server ${name} misses a command`);\n\t\t}\n\t\tif (typeof server.type !== \"string\") {\n\t\t\tthrow new Error(`mcp.json server ${name} misses a type`);\n\t\t}\n\t\treturn {\n\t\t\tname,\n\t\t\ttype: server.type,\n\t\t\tcommand: server.command,\n\t\t\targs: optionalStringArray(server.args, name, \"args\"),\n\t\t\tenv: optionalStringRecord(server.env, name, \"env\"),\n\t\t\ttools: optionalStringArray(server.tools, name, \"tools\"),\n\t\t};\n\t});\n}\n\n/** Parse and validate a hooks.json payload. */\nexport function parseHooksJson(raw: string): HookDefinition {\n\tconst parsed = JSON.parse(raw) as { hooks?: Record<string, unknown> };\n\tconst hooks = parsed.hooks;\n\tif (!hooks || typeof hooks !== \"object\") {\n\t\tthrow new Error(\"hooks.json misses the hooks object\");\n\t}\n\tconst result: HookDefinition = { hooks: {} };\n\tfor (const [event, registrations] of Object.entries(hooks)) {\n\t\tif (!Array.isArray(registrations)) {\n\t\t\tthrow new Error(`hooks.json event ${event} is not an array`);\n\t\t}\n\t\tresult.hooks[event] = registrations.map((registration, index) => {\n\t\t\tif (registration === null || typeof registration !== \"object\") {\n\t\t\t\tthrow new Error(`hooks.json event ${event}[${index}] is not an object`);\n\t\t\t}\n\t\t\tconst hook = registration as Record<string, unknown>;\n\t\t\tif (hook.type !== \"command\") {\n\t\t\t\tthrow new Error(`hooks.json event ${event}[${index}] has unsupported type`);\n\t\t\t}\n\t\t\tif (typeof hook.bash !== \"string\" || hook.bash.length === 0) {\n\t\t\t\tthrow new Error(`hooks.json event ${event}[${index}] misses bash`);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\ttype: \"command\",\n\t\t\t\tbash: hook.bash,\n\t\t\t\tcwd: typeof hook.cwd === \"string\" ? hook.cwd : undefined,\n\t\t\t\tenv: optionalStringRecord(hook.env, event, \"env\"),\n\t\t\t\ttimeoutSec: typeof hook.timeoutSec === \"number\" ? hook.timeoutSec : undefined,\n\t\t\t};\n\t\t});\n\t}\n\treturn result;\n}\n\n/** Locate the mcp.json beside a plugin manifest. */\nexport async function findPluginMcpJson(\n\trepoRoot: string,\n\tpluginId: string\n): Promise<string | null> {\n\tconst candidate = path.join(repoRoot, \"plugins\", pluginId, \"mcp.json\");\n\treturn (await pathExists(candidate)) ? candidate : null;\n}\n\n/** Locate hooks/<name>/hooks.json in a repository. */\nexport async function findRepoHooksJson(repoRoot: string, name: string): Promise<string | null> {\n\tconst candidate = path.join(repoRoot, \"hooks\", name, \"hooks.json\");\n\treturn (await pathExists(candidate)) ? candidate : null;\n}\n\nfunction optionalStringArray(value: unknown, name: string, field: string): string[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Array.isArray(value) || value.some((item) => typeof item !== \"string\")) {\n\t\tthrow new Error(`mcp.json server ${name} ${field} must be a string array`);\n\t}\n\treturn value as string[];\n}\n\nfunction optionalStringRecord(\n\tvalue: unknown,\n\tname: string,\n\tfield: string\n): Record<string, string> | undefined {\n\tif (value === undefined) return undefined;\n\tif (\n\t\tvalue === null ||\n\t\ttypeof value !== \"object\" ||\n\t\tArray.isArray(value) ||\n\t\tObject.entries(value).some(([, item]) => typeof item !== \"string\")\n\t) {\n\t\tthrow new Error(`mcp.json server ${name} ${field} must be a string record`);\n\t}\n\treturn value as Record<string, string>;\n}\n\nasync function pathExists(target: string): Promise<boolean> {\n\ttry {\n\t\tawait fsp.access(target);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n","import * as fsp from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\n\nimport type { CopilotLinkMaterializer } from \"./copilot-link-materializer\";\nimport type { CopilotPackageInstallation, CopilotScope } from \"./customization-model\";\nimport { buildCopilotCustomizationView, type CopilotCustomizationView } from \"./customization-view\";\nimport type { PersonalCopilotContentReconciler } from \"./personal-copilot-content-reconciler\";\nimport type { PersonalInstallationStore } from \"./personal-installation-store\";\nimport type { ResolvedPluginEntry } from \"./plugin-resolver\";\nimport type { CopilotArtifactKind, WorkspaceManifestRepository } from \"./types\";\nimport type { WorkspaceCopilotContentReconciler } from \"./workspace-copilot-content-reconciler\";\nimport { WorkspaceExcludeStore } from \"./workspace-exclude-store\";\nimport {\n\tloadWorkspaceCopilotManifest,\n\tremoveWorkspaceContentSelection,\n\treplaceWorkspaceContentSelection,\n} from \"./workspace-manifest\";\nimport { WorkspaceContentStateStore } from \"./workspace-state-store\";\n\ninterface FileSnapshot {\n\tpath: string;\n\tcontents: Buffer | undefined;\n}\n\ninterface WorkspaceActivationSnapshot {\n\tbackupDir: string;\n\tworkspaceDir: string;\n\tgithubExisted: boolean;\n\tfiles: FileSnapshot[];\n}\n\n/** One legacy ~/.agents link considered for migration. */\nexport interface LegacyMigrationEntry {\n\tartifactId: string;\n\tname: string;\n\tkind: \"skill\" | \"agent\";\n\teligible: boolean;\n\tsourcePath: string;\n\ttargetPath: string;\n}\n\n/** Read-only preview of legacy links; migration happens only on confirmation. */\nexport interface LegacyMigrationPreview {\n\tsourceLabel: \"~/.agents\";\n\tentries: LegacyMigrationEntry[];\n}\n\nexport interface CopilotPackageInstallInput {\n\tworkspaceDir?: string;\n\tscope: CopilotScope;\n\tsourceId: string;\n\tpackageId: string;\n\tartifactIds: string[];\n\tpinnedVersion?: string;\n\tlocalMode?: \"live\" | \"private-workspace-override\";\n}\n\nexport interface CopilotPackageUpdateInput {\n\tworkspaceDir?: string;\n\tscope: CopilotScope;\n\tpackageId: string;\n\tartifactIds: string[];\n\tpinnedVersion?: string;\n}\n\nexport interface CopilotPackageUninstallInput {\n\tworkspaceDir?: string;\n\tscope: CopilotScope;\n\tpackageId: string;\n}\n\nexport interface CopilotPackageMoveInput {\n\tworkspaceDir?: string;\n\tpackageId: string;\n\tfrom: CopilotScope;\n\tto: CopilotScope;\n}\n\nexport interface PackageInstallationServiceDeps {\n\tpersonalStore: PersonalInstallationStore;\n\tpersonalReconciler: PersonalCopilotContentReconciler;\n\t/** Shared materializer so transaction tests can inject failures. */\n\tmaterializer?: CopilotLinkMaterializer;\n\tuserHomeDir: string;\n\tresolvePackage: (packageId: string) => Promise<ResolvedPluginEntry[]>;\n\tresolveWorkspaceSource: (sourceId: string) => Promise<WorkspaceManifestRepository | undefined>;\n\tcreateWorkspaceReconciler: (workspaceDir: string) => WorkspaceCopilotContentReconciler;\n\t/** Resolve the local reconciliation state path for transaction rollback. */\n\tgetWorkspaceStatePath?: (workspaceDir: string) => Promise<string>;\n\treadWorkspaceView: (workspaceDir: string) => Promise<CopilotCustomizationView>;\n\t/** Exact selected workspace artifact identities for the active workspace. */\n\treadWorkspaceInstallations: (workspaceDir: string) => Promise<CopilotPackageInstallation[]>;\n}\n\n/**\n * Package lifecycle transactions over the Task 8 personal store /\n * reconciler and the shared workspace manifest.\n *\n * Every mutation follows prepare → activate → commit: destination\n * intent is validated and materialized first; source intent is\n * removed only after every destination artifact succeeded. On\n * activation failure the previous destination intent is restored and\n * the source intent is left untouched.\n */\nexport class PackageInstallationService {\n\tprivate readonly deps: PackageInstallationServiceDeps;\n\t/** Last preview shown to the caller; migration re-verifies against it. */\n\tprivate lastPreview: LegacyMigrationPreview | undefined;\n\n\tconstructor(deps: PackageInstallationServiceDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tasync install(input: CopilotPackageInstallInput): Promise<CopilotCustomizationView> {\n\t\tawait this.assertKindNameFree(input.scope, input.packageId, input.artifactIds);\n\t\tif (input.scope === \"personal\") {\n\t\t\tawait this.assertArtifactsNotActiveElsewhere(input);\n\t\t\tconst previousState = await this.deps.personalStore.read();\n\t\t\ttry {\n\t\t\t\tawait this.writePersonalIntent({\n\t\t\t\t\tpackageId: input.packageId,\n\t\t\t\t\tscope: \"personal\",\n\t\t\t\t\tselectedArtifactIds: input.artifactIds,\n\t\t\t\t\t...(input.pinnedVersion ? { pinnedVersion: input.pinnedVersion } : {}),\n\t\t\t\t});\n\t\t\t\tawait this.deps.personalReconciler.reconcile();\n\t\t\t} catch (error) {\n\t\t\t\t// Rollback on the prepare→activate contract: restore the exact\n\t\t\t\t// store bytes recorded before the intent write.\n\t\t\t\tawait this.deps.personalStore.write(previousState);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn this.readPersonalView();\n\t\t}\n\t\t// Validate before writing: upsertWorkspaceSelection coerces a missing\n\t\t// workspaceDir to \"\" and would otherwise create a stray manifest in\n\t\t// the process CWD (.github/serviceme-plugins.json) before failing.\n\t\tconst workspaceDir = this.requireWorkspaceDir(input);\n\t\tawait this.assertArtifactsNotActiveElsewhere({ ...input, workspaceDir });\n\t\treturn this.withWorkspaceActivationRollback(workspaceDir, async () => {\n\t\t\tawait this.upsertWorkspaceSelection(workspaceDir, input.packageId, input.artifactIds);\n\t\t\tconst result = await this.deps.createWorkspaceReconciler(workspaceDir).reconcile();\n\t\t\t// Install/update share the move transaction's guarantee: activation\n\t\t\t// conflicts are failures, not silent partial states.\n\t\t\tthis.assertNoActivationConflicts(\n\t\t\t\tresult.entries,\n\t\t\t\tawait this.selectedEntries(input.packageId, input.artifactIds)\n\t\t\t);\n\t\t\treturn this.deps.readWorkspaceView(workspaceDir);\n\t\t});\n\t}\n\n\tasync update(input: CopilotPackageUpdateInput): Promise<CopilotCustomizationView> {\n\t\tif (input.scope === \"personal\") {\n\t\t\tawait this.assertArtifactsNotActiveElsewhere(input);\n\t\t\tconst state = await this.deps.personalStore.read();\n\t\t\tconst existing = state.installations.find(\n\t\t\t\t(installation) => installation.packageId === input.packageId\n\t\t\t);\n\t\t\tif (!existing) throw new Error(`No personal installation for ${input.packageId}`);\n\t\t\ttry {\n\t\t\t\tawait this.writePersonalIntent({\n\t\t\t\t\t...existing,\n\t\t\t\t\tselectedArtifactIds: input.artifactIds,\n\t\t\t\t\t...(input.pinnedVersion !== undefined ? { pinnedVersion: input.pinnedVersion } : {}),\n\t\t\t\t});\n\t\t\t\tawait this.deps.personalReconciler.reconcile();\n\t\t\t} catch (error) {\n\t\t\t\tawait this.deps.personalStore.write(state);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn this.readPersonalView();\n\t\t}\n\t\t// Same guard as install: never touch disk before the workspace\n\t\t// directory is proven present.\n\t\tconst workspaceDir = this.requireWorkspaceDir(input);\n\t\tawait this.assertArtifactsNotActiveElsewhere({ ...input, workspaceDir });\n\t\treturn this.withWorkspaceActivationRollback(workspaceDir, async () => {\n\t\t\tawait this.upsertWorkspaceSelection(workspaceDir, input.packageId, input.artifactIds);\n\t\t\tconst result = await this.deps.createWorkspaceReconciler(workspaceDir).reconcile();\n\t\t\tthis.assertNoActivationConflicts(\n\t\t\t\tresult.entries,\n\t\t\t\tawait this.selectedEntries(input.packageId, input.artifactIds)\n\t\t\t);\n\t\t\treturn this.deps.readWorkspaceView(workspaceDir);\n\t\t});\n\t}\n\n\tasync uninstall(input: CopilotPackageUninstallInput): Promise<CopilotCustomizationView> {\n\t\tif (input.scope === \"personal\") {\n\t\t\tconst state = await this.deps.personalStore.read();\n\t\t\tconst next = state.installations.filter(\n\t\t\t\t(installation) => installation.packageId !== input.packageId\n\t\t\t);\n\t\t\tawait this.deps.personalStore.write({ version: 1, installations: next });\n\t\t\tawait this.deps.personalReconciler.reconcile();\n\t\t\treturn this.readPersonalView();\n\t\t}\n\t\tconst workspaceDir = this.requireWorkspaceDir(input);\n\t\tconst [sourceId, pluginId] = input.packageId.split(\"::\");\n\t\tif (!sourceId || !pluginId) throw new Error(`Malformed package id ${input.packageId}`);\n\t\tconst manifest = await loadWorkspaceCopilotManifest(workspaceDir);\n\t\tconst plugin = manifest?.plugins.find((candidate) => candidate.id === pluginId);\n\t\tif (plugin?.artifactIds && plugin.artifactIds.length > 0) {\n\t\t\tconst [entry] = await this.selectedEntries(input.packageId, plugin.artifactIds);\n\t\t\tif (entry) {\n\t\t\t\tawait removeWorkspaceContentSelection({\n\t\t\t\t\tworkspaceDir,\n\t\t\t\t\trepositoryId: sourceId,\n\t\t\t\t\tpluginId,\n\t\t\t\t\tkind: entry.kind,\n\t\t\t\t});\n\t\t\t}\n\t\t} else {\n\t\t\tconst kinds = manifest?.plugins\n\t\t\t\t.filter((candidate) => candidate.id === pluginId)\n\t\t\t\t.flatMap((plugin) =>\n\t\t\t\t\tObject.entries(plugin.artifacts)\n\t\t\t\t\t\t.filter(([, enabled]) => enabled)\n\t\t\t\t\t\t.map(([kind]) => kind as CopilotArtifactKind)\n\t\t\t\t);\n\t\t\tfor (const kind of new Set(kinds ?? [])) {\n\t\t\t\tawait removeWorkspaceContentSelection({\n\t\t\t\t\tworkspaceDir,\n\t\t\t\t\trepositoryId: sourceId,\n\t\t\t\t\tpluginId,\n\t\t\t\t\tkind,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tawait this.deps.createWorkspaceReconciler(workspaceDir).reconcile();\n\t\treturn this.deps.readWorkspaceView(workspaceDir);\n\t}\n\n\tasync move(input: CopilotPackageMoveInput): Promise<CopilotCustomizationView> {\n\t\tif (input.from === input.to) throw new Error(\"Cannot move a package within the same scope\");\n\t\t// Validate the workspace directory before ANY disk mutation, no\n\t\t// matter which side is the destination: a later requireWorkspaceDir\n\t\t// failure must never orphan a half-completed move.\n\t\tthis.requireWorkspaceDir(input);\n\t\tconst entries = await this.deps.resolvePackage(input.packageId);\n\t\tif (entries.length === 0) throw new Error(`Unknown Copilot package ${input.packageId}`);\n\t\tconst artifactIds = await this.readSourceSelection(input);\n\t\tconst selectedEntries = this.selectEntries(entries, artifactIds);\n\n\t\tif (input.to === \"workspace\") {\n\t\t\treturn this.moveIntoWorkspace(input, selectedEntries, artifactIds);\n\t\t}\n\t\treturn this.moveIntoPersonal(input, selectedEntries, artifactIds);\n\t}\n\n\tprivate async moveIntoWorkspace(\n\t\tinput: CopilotPackageMoveInput,\n\t\tentries: ResolvedPluginEntry[],\n\t\tartifactIds: string[]\n\t): Promise<CopilotCustomizationView> {\n\t\tconst workspaceDir = this.requireWorkspaceDir(input);\n\t\tconst [sourceId, pluginId] = input.packageId.split(\"::\");\n\t\tif (!sourceId || !pluginId) throw new Error(`Malformed package id ${input.packageId}`);\n\t\tconst repository = await this.deps.resolveWorkspaceSource(sourceId);\n\t\tif (!repository) throw new Error(`Unknown workspace source ${sourceId}`);\n\t\tawait this.assertArtifactIdsAbsentFromWorkspace(workspaceDir, artifactIds);\n\n\t\tawait this.withWorkspaceActivationRollback(workspaceDir, async () => {\n\t\t\tawait replaceWorkspaceContentSelection({ workspaceDir, repository, pluginId, artifactIds });\n\t\t\tconst result = await this.deps.createWorkspaceReconciler(workspaceDir).reconcile();\n\t\t\tthis.assertNoActivationConflicts(result.entries, entries);\n\t\t});\n\n\t\tawait this.removePersonalIntent(input.packageId);\n\t\tawait this.deps.personalReconciler.reconcile();\n\t\treturn this.deps.readWorkspaceView(workspaceDir);\n\t}\n\n\tprivate async moveIntoPersonal(\n\t\tinput: CopilotPackageMoveInput,\n\t\tentries: ResolvedPluginEntry[],\n\t\tartifactIds: string[]\n\t): Promise<CopilotCustomizationView> {\n\t\tconst state = await this.deps.personalStore.read();\n\t\tthis.assertArtifactIdsAbsentFromInstallations(\"personal\", state.installations, artifactIds);\n\t\tconst installation: CopilotPackageInstallation = {\n\t\t\tpackageId: input.packageId,\n\t\t\tscope: \"personal\",\n\t\t\tselectedArtifactIds: artifactIds,\n\t\t};\n\t\ttry {\n\t\t\tawait this.writePersonalIntent(installation);\n\t\t\tconst result = await this.deps.personalReconciler.reconcile();\n\t\t\tthis.assertNoActivationConflictsPersonal(result.statesByArtifactId, entries);\n\t\t} catch (error) {\n\t\t\tawait this.deps.personalStore.write(state);\n\t\t\tthrow error;\n\t\t}\n\n\t\tconst workspaceDir = this.requireWorkspaceDir(input);\n\t\tawait this.uninstall({ scope: \"workspace\", packageId: input.packageId, workspaceDir });\n\t\treturn this.readPersonalView();\n\t}\n\n\tasync previewLegacy(): Promise<LegacyMigrationPreview> {\n\t\tconst home = this.deps.userHomeDir;\n\t\tconst entries: LegacyMigrationEntry[] = [];\n\t\tfor (const kind of [\"skill\", \"agent\"] as const) {\n\t\t\tconst kindDir = path.join(home, \".agents\", kind === \"agent\" ? \"agents\" : \"skills\");\n\t\t\tconst dirents = await fsp.readdir(kindDir, { withFileTypes: true }).catch(() => []);\n\t\t\tfor (const dirent of dirents) {\n\t\t\t\tconst legacyPath = path.join(kindDir, dirent.name);\n\t\t\t\tconst stat = await fsp.lstat(legacyPath).catch(() => null);\n\t\t\t\tconst isLink = stat?.isSymbolicLink() === true;\n\t\t\t\tconst targetPath = isLink ? await fsp.readlink(legacyPath).catch(() => \"\") : \"\";\n\t\t\t\tconst resolved = isLink ? this.resolveLegacyTarget(legacyPath, targetPath) : \"\";\n\t\t\t\tconst targetStat = resolved !== \"\" ? await fsp.stat(resolved).catch(() => null) : null;\n\t\t\t\tentries.push({\n\t\t\t\t\tartifactId: `legacy:${kind}:${dirent.name}`,\n\t\t\t\t\tname: dirent.name,\n\t\t\t\t\tkind,\n\t\t\t\t\teligible: isLink && targetStat !== null,\n\t\t\t\t\tsourcePath: legacyPath,\n\t\t\t\t\ttargetPath: resolved,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tconst preview = { sourceLabel: \"~/.agents\" as const, entries };\n\t\tthis.lastPreview = preview;\n\t\treturn preview;\n\t}\n\n\tasync migrateLegacy(input: { artifactIds: string[] }): Promise<CopilotCustomizationView> {\n\t\tconst preview = this.lastPreview ?? (await this.previewLegacy());\n\t\tconst home = this.deps.userHomeDir;\n\t\tfor (const artifactId of input.artifactIds) {\n\t\t\tconst entry = preview.entries.find((candidate) => candidate.artifactId === artifactId);\n\t\t\tif (!entry?.eligible) {\n\t\t\t\tthrow new Error(`Legacy entry ${artifactId} is not eligible for migration`);\n\t\t\t}\n\t\t\t// Re-verify ownership at migration time: the link must still be\n\t\t\t// a symlink pointing at the exact target the preview recorded.\n\t\t\tconst stat = await fsp.lstat(entry.sourcePath).catch(() => null);\n\t\t\tif (!stat?.isSymbolicLink()) {\n\t\t\t\tthrow new Error(`Legacy entry ${entry.name} is no longer a link`);\n\t\t\t}\n\t\t\tconst currentTarget = this.resolveLegacyTarget(\n\t\t\t\tentry.sourcePath,\n\t\t\t\tawait fsp.readlink(entry.sourcePath)\n\t\t\t);\n\t\t\tif (currentTarget !== entry.targetPath) {\n\t\t\t\tthrow new Error(`Legacy entry ${entry.name} was repointed after preview`);\n\t\t\t}\n\t\t\t// Windows directory symlinks need type \"dir\"; lstat on the link\n\t\t\t// itself never reports isDirectory (it reports the symlink), so\n\t\t\t// stat the RESOLVED target to derive the link type.\n\t\t\tconst targetStat = await fsp.stat(entry.targetPath).catch(() => null);\n\t\t\tif (!targetStat) {\n\t\t\t\tthrow new Error(`Legacy entry ${entry.name} has a missing target`);\n\t\t\t}\n\t\t\tconst kindDir = path.join(home, \".copilot\", entry.kind === \"agent\" ? \"agents\" : \"skills\");\n\t\t\tconst destination = path.join(kindDir, path.basename(entry.sourcePath));\n\t\t\tconst destinationStat = await fsp.lstat(destination).catch(() => null);\n\t\t\tif (destinationStat) {\n\t\t\t\tthrow new Error(`Migration target ${path.basename(entry.sourcePath)} already exists`);\n\t\t\t}\n\t\t\tawait fsp.mkdir(kindDir, { recursive: true });\n\t\t\tawait fsp.symlink(\n\t\t\t\tawait fsp.readlink(entry.sourcePath),\n\t\t\t\tdestination,\n\t\t\t\ttargetStat.isDirectory() ? \"dir\" : \"file\"\n\t\t\t);\n\t\t\tawait fsp.rm(entry.sourcePath, { force: true });\n\t\t}\n\t\treturn this.readPersonalView();\n\t}\n\n\tprivate async readSourceSelection(input: CopilotPackageMoveInput): Promise<string[]> {\n\t\tif (input.from === \"personal\") {\n\t\t\tconst installation = (await this.deps.personalStore.read()).installations.find(\n\t\t\t\t(candidate) => candidate.packageId === input.packageId\n\t\t\t);\n\t\t\tif (!installation) throw new Error(`No personal installation for ${input.packageId}`);\n\t\t\treturn installation.selectedArtifactIds;\n\t\t}\n\n\t\tconst installation = (\n\t\t\tawait this.deps.readWorkspaceInstallations(this.requireWorkspaceDir(input))\n\t\t).find((candidate) => candidate.packageId === input.packageId);\n\t\tif (!installation) throw new Error(`No workspace installation for ${input.packageId}`);\n\t\treturn installation.selectedArtifactIds;\n\t}\n\n\tprivate async assertArtifactsNotActiveElsewhere(input: {\n\t\tscope: CopilotScope;\n\t\tworkspaceDir?: string;\n\t\tartifactIds: string[];\n\t}): Promise<void> {\n\t\tif (input.scope === \"workspace\") {\n\t\t\tthis.assertArtifactIdsAbsentFromInstallations(\n\t\t\t\t\"personal\",\n\t\t\t\t(await this.deps.personalStore.read()).installations,\n\t\t\t\tinput.artifactIds\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\t// Personal installs must still see the active workspace even when the\n\t\t// caller omitted workspaceDir (e.g. the CLI pass-through). Fall back\n\t\t// to the process CWD and skip silently when nothing is declared so\n\t\t// headless personal-only usage keeps working.\n\t\tconst workspaceDir = input.workspaceDir ?? process.cwd();\n\t\tawait this.assertArtifactIdsAbsentFromWorkspace(workspaceDir, input.artifactIds);\n\t}\n\n\tprivate async assertArtifactIdsAbsentFromWorkspace(\n\t\tworkspaceDir: string,\n\t\tartifactIds: string[]\n\t): Promise<void> {\n\t\tthis.assertArtifactIdsAbsentFromInstallations(\n\t\t\t\"workspace\",\n\t\t\tawait this.deps.readWorkspaceInstallations(workspaceDir),\n\t\t\tartifactIds\n\t\t);\n\t}\n\n\tprivate assertArtifactIdsAbsentFromInstallations(\n\t\tscope: CopilotScope,\n\t\tinstallations: CopilotPackageInstallation[],\n\t\tartifactIds: string[]\n\t): void {\n\t\tconst selected = new Set(\n\t\t\tinstallations.flatMap((installation) => installation.selectedArtifactIds)\n\t\t);\n\t\tconst conflict = artifactIds.find((artifactId) => selected.has(artifactId));\n\t\tif (conflict) {\n\t\t\tthrow new Error(`Artifact ${conflict} is already active in ${scope} scope`);\n\t\t}\n\t}\n\n\tprivate async assertKindNameFree(\n\t\tscope: CopilotScope,\n\t\tpackageId: string,\n\t\tartifactIds: string[]\n\t): Promise<void> {\n\t\tif (scope !== \"personal\") return;\n\t\tconst incoming = (await this.deps.resolvePackage(packageId))\n\t\t\t.filter((entry) => artifactIds.includes(entry.artifactId))\n\t\t\t.map((entry) => `${entry.kind}:${entry.name}`);\n\t\tconst intent = await this.deps.personalStore.read();\n\t\tfor (const installation of intent.installations) {\n\t\t\tif (installation.packageId === packageId) continue;\n\t\t\tconst existing = await this.deps.resolvePackage(installation.packageId);\n\t\t\tfor (const entry of this.selectEntries(existing, installation.selectedArtifactIds)) {\n\t\t\t\tconst key = `${entry.kind}:${entry.name}`;\n\t\t\t\tif (incoming.includes(key)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Conflict: ${key} from ${packageId} collides with ${installation.packageId}`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async writePersonalIntent(installation: CopilotPackageInstallation): Promise<void> {\n\t\tconst state = await this.deps.personalStore.read();\n\t\tconst next = state.installations.filter(\n\t\t\t(candidate) => candidate.packageId !== installation.packageId\n\t\t);\n\t\tawait this.deps.personalStore.write({\n\t\t\tversion: 1,\n\t\t\tinstallations: [...next, installation],\n\t\t});\n\t}\n\n\tprivate async removePersonalIntent(packageId: string): Promise<void> {\n\t\tconst state = await this.deps.personalStore.read();\n\t\tawait this.deps.personalStore.write({\n\t\t\tversion: 1,\n\t\t\tinstallations: state.installations.filter(\n\t\t\t\t(installation) => installation.packageId !== packageId\n\t\t\t),\n\t\t});\n\t}\n\n\tprivate async upsertWorkspaceSelection(\n\t\tworkspaceDir: string | undefined,\n\t\tpackageId: string,\n\t\tartifactIds: string[]\n\t): Promise<void> {\n\t\tconst dir = workspaceDir ?? \"\";\n\t\tconst [sourceId, pluginId] = packageId.split(\"::\");\n\t\tif (!sourceId || !pluginId) throw new Error(`Malformed package id ${packageId}`);\n\t\tconst repository = await this.deps.resolveWorkspaceSource(sourceId);\n\t\tif (!repository) throw new Error(`Unknown workspace source ${sourceId}`);\n\t\tthis.selectEntries(await this.deps.resolvePackage(packageId), artifactIds);\n\t\tawait replaceWorkspaceContentSelection({\n\t\t\tworkspaceDir: dir,\n\t\t\trepository,\n\t\t\tpluginId,\n\t\t\tartifactIds,\n\t\t});\n\t}\n\n\tprivate async withWorkspaceActivationRollback<T>(\n\t\tworkspaceDir: string,\n\t\toperation: () => Promise<T>\n\t): Promise<T> {\n\t\tconst snapshot = await this.captureWorkspaceActivation(workspaceDir);\n\t\ttry {\n\t\t\treturn await operation();\n\t\t} catch (error) {\n\t\t\tawait this.restoreWorkspaceActivation(snapshot);\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tawait fsp.rm(snapshot.backupDir, { recursive: true, force: true });\n\t\t}\n\t}\n\n\tprivate async captureWorkspaceActivation(\n\t\tworkspaceDir: string\n\t): Promise<WorkspaceActivationSnapshot> {\n\t\tconst backupDir = await fsp.mkdtemp(path.join(os.tmpdir(), \"serviceme-workspace-activation-\"));\n\t\tconst githubPath = path.join(workspaceDir, \".github\");\n\t\tconst githubExisted = (await fsp.lstat(githubPath).catch(() => null)) !== null;\n\t\tif (githubExisted) {\n\t\t\tawait fsp.cp(githubPath, path.join(backupDir, \"github\"), {\n\t\t\t\trecursive: true,\n\t\t\t\tdereference: false,\n\t\t\t});\n\t\t}\n\n\t\tconst statePath = await (\n\t\t\tthis.deps.getWorkspaceStatePath ??\n\t\t\t((dir: string) => new WorkspaceContentStateStore({ workspaceDir: dir }).path())\n\t\t)(workspaceDir).catch(() => undefined);\n\t\tconst excludePath = await new WorkspaceExcludeStore({ workspaceDir }).path();\n\t\tconst files = await Promise.all(\n\t\t\t[\n\t\t\t\t...(statePath ? [statePath] : []),\n\t\t\t\texcludePath,\n\t\t\t\tpath.join(workspaceDir, \".vscode\", \"mcp.serviceme.json\"),\n\t\t\t\tpath.join(workspaceDir, \".vscode\", \"hooks.serviceme.json\"),\n\t\t\t].map((filePath) => this.captureFile(filePath))\n\t\t);\n\t\treturn { backupDir, workspaceDir, githubExisted, files };\n\t}\n\n\tprivate async captureFile(filePath: string): Promise<FileSnapshot> {\n\t\ttry {\n\t\t\treturn { path: filePath, contents: await fsp.readFile(filePath) };\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\treturn { path: filePath, contents: undefined };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate async restoreWorkspaceActivation(snapshot: WorkspaceActivationSnapshot): Promise<void> {\n\t\tconst workspaceGithubPath = path.join(snapshot.workspaceDir, \".github\");\n\t\tawait fsp.rm(workspaceGithubPath, { recursive: true, force: true });\n\t\tif (snapshot.githubExisted) {\n\t\t\tawait fsp.cp(path.join(snapshot.backupDir, \"github\"), workspaceGithubPath, {\n\t\t\t\trecursive: true,\n\t\t\t\tdereference: false,\n\t\t\t});\n\t\t}\n\t\tfor (const file of snapshot.files) {\n\t\t\tif (file.contents === undefined) {\n\t\t\t\tawait fsp.rm(file.path, { force: true });\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tawait fsp.mkdir(path.dirname(file.path), { recursive: true });\n\t\t\tawait fsp.writeFile(file.path, file.contents);\n\t\t}\n\t}\n\n\tprivate async selectedEntries(\n\t\tpackageId: string,\n\t\tartifactIds: string[]\n\t): Promise<ResolvedPluginEntry[]> {\n\t\treturn this.selectEntries(await this.deps.resolvePackage(packageId), artifactIds);\n\t}\n\n\tprivate selectEntries(\n\t\tentries: ResolvedPluginEntry[],\n\t\tartifactIds: string[]\n\t): ResolvedPluginEntry[] {\n\t\tconst selectedIds = new Set(artifactIds);\n\t\tconst selected = entries.filter((entry) => selectedIds.has(entry.artifactId));\n\t\tif (selected.length !== selectedIds.size) {\n\t\t\tconst availableIds = new Set(entries.map((entry) => entry.artifactId));\n\t\t\tconst unknown = artifactIds.filter((artifactId) => !availableIds.has(artifactId));\n\t\t\tthrow new Error(`Unknown selected Copilot artifact(s): ${unknown.join(\", \")}`);\n\t\t}\n\t\treturn selected;\n\t}\n\n\tprivate assertNoActivationConflicts(\n\t\tresults: Array<{ identity: string; status: string; message?: string }>,\n\t\tentries: ResolvedPluginEntry[]\n\t): void {\n\t\tfor (const entry of entries) {\n\t\t\tconst result = results.find((candidate) => candidate.identity === entry.artifactId);\n\t\t\tif (result?.status === \"conflict\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Workspace activation conflict for ${entry.kind}:${entry.name}${result.message ? `: ${result.message}` : \"\"}`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate assertNoActivationConflictsPersonal(\n\t\tstatesByArtifactId: Record<string, { health: string }>,\n\t\tentries: ResolvedPluginEntry[]\n\t): void {\n\t\tfor (const entry of entries) {\n\t\t\tconst state = statesByArtifactId[entry.artifactId];\n\t\t\tif (state?.health === \"conflict\") {\n\t\t\t\tthrow new Error(`Personal activation conflict for ${entry.kind}:${entry.name}`);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate resolveLegacyTarget(linkPath: string, rawTarget: string): string {\n\t\tif (rawTarget === \"\") return \"\";\n\t\tif (path.isAbsolute(rawTarget)) return path.normalize(rawTarget);\n\t\treturn path.normalize(path.resolve(path.dirname(linkPath), rawTarget));\n\t}\n\n\tprivate requireWorkspaceDir(input: { workspaceDir?: string }): string {\n\t\tif (!input.workspaceDir) {\n\t\t\tthrow new Error(\"A workspace directory is required for workspace-scope operations\");\n\t\t}\n\t\treturn input.workspaceDir;\n\t}\n\n\tprivate async readPersonalView(): Promise<CopilotCustomizationView> {\n\t\tconst intent = await this.deps.personalStore.read();\n\t\tconst inspection = await this.deps.personalReconciler.inspect();\n\t\tconst sources: Parameters<typeof buildCopilotCustomizationView>[0][\"sources\"] = [];\n\t\tconst packages: Parameters<typeof buildCopilotCustomizationView>[0][\"packages\"] = [];\n\t\tfor (const installation of intent.installations) {\n\t\t\tif (installation.scope !== \"personal\") continue;\n\t\t\tconst resolved = await this.deps.resolvePackage(installation.packageId);\n\t\t\tconst artifacts = resolved\n\t\t\t\t.filter((entry) => installation.selectedArtifactIds.includes(entry.artifactId))\n\t\t\t\t.map((entry) => ({\n\t\t\t\t\tid: entry.artifactId,\n\t\t\t\t\tpackageId: entry.packageId,\n\t\t\t\t\tkind: entry.kind,\n\t\t\t\t\tdisplayName: entry.name,\n\t\t\t\t\tinstallStrategy: entry.requiresApproval\n\t\t\t\t\t\t? (\"approval-gated\" as const)\n\t\t\t\t\t\t: entry.kind === \"mcp\" || entry.kind === \"hook\"\n\t\t\t\t\t\t\t? (\"generated-config\" as const)\n\t\t\t\t\t\t\t: (\"link\" as const),\n\t\t\t\t\trisk: entry.requiresApproval ? (\"review-required\" as const) : (\"none\" as const),\n\t\t\t\t}));\n\t\t\tif (artifacts.length === 0) continue;\n\t\t\tconst [sourceId] = installation.packageId.split(\"::\");\n\t\t\tif (sourceId && !sources.some((source) => source.id === sourceId)) {\n\t\t\t\tsources.push({\n\t\t\t\t\tid: sourceId,\n\t\t\t\t\ttype: \"git\",\n\t\t\t\t\tdisplayName: sourceId,\n\t\t\t\t\tupdateCapability: \"pinned\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tpackages.push({\n\t\t\t\tid: installation.packageId,\n\t\t\t\tsourceId: sourceId ?? \"personal-local\",\n\t\t\t\tdisplayName: resolved[0]?.packageDisplayName ?? installation.packageId,\n\t\t\t\tartifacts,\n\t\t\t});\n\t\t}\n\t\treturn buildCopilotCustomizationView({\n\t\t\tscope: \"personal\",\n\t\t\tsources,\n\t\t\tpackages,\n\t\t\tinstallations: intent.installations.filter(\n\t\t\t\t(installation) => installation.scope === \"personal\"\n\t\t\t),\n\t\t\tstatesByArtifactId: inspection.statesByArtifactId,\n\t\t});\n\t}\n}\n","import { spawn } from \"node:child_process\";\nimport * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nconst BLOCK_START = \"# >>> SERVICEME managed Copilot links >>>\";\nconst BLOCK_END = \"# <<< SERVICEME managed Copilot links <<<\";\n\n/** Default resolver: per-worktree exclude at .git/info/exclude (via rev-parse when available). */\nasync function defaultResolveGitExcludePath(workspaceDir: string): Promise<string> {\n\treturn resolveGitExcludeViaRevParse(workspaceDir).catch(() =>\n\t\tpath.join(workspaceDir, \".git\", \"info\", \"exclude\")\n\t);\n}\n\n/** Resolve the worktree-aware exclude path with git rev-parse --git-path. */\nasync function resolveGitExcludeViaRevParse(workspaceDir: string): Promise<string> {\n\tconst result = await new Promise<{ code: number; stdout: string }>((resolve, reject) => {\n\t\tconst child = spawn(\"git\", [\"rev-parse\", \"--git-path\", \"info/exclude\"], {\n\t\t\tcwd: workspaceDir,\n\t\t});\n\t\tlet stdout = \"\";\n\t\tchild.stdout.on(\"data\", (chunk) => {\n\t\t\tstdout += chunk;\n\t\t});\n\t\tchild.on(\"error\", reject);\n\t\tchild.on(\"close\", (code) => resolve({ code: code ?? -1, stdout }));\n\t});\n\tif (result.code !== 0) {\n\t\tthrow new Error(`git rev-parse --git-path failed with exit code ${result.code}`);\n\t}\n\tconst resolved = result.stdout.trim();\n\tif (resolved === \"\") {\n\t\tthrow new Error(\"git rev-parse --git-path returned an empty path\");\n\t}\n\treturn path.isAbsolute(resolved) ? resolved : path.resolve(workspaceDir, resolved);\n}\n\n/** Manage the bounded SERVICEME block inside a git worktree's info/exclude. */\nexport class WorkspaceExcludeStore {\n\tprivate readonly workspaceDir: string;\n\tprivate readonly resolveGitExcludePath: (workspaceDir: string) => Promise<string>;\n\n\tconstructor(options: {\n\t\tworkspaceDir: string;\n\t\t/** Override exclude-path resolution (tests, linked-worktree handling). */\n\t\tresolveGitExcludePath?: (workspaceDir: string) => Promise<string>;\n\t}) {\n\t\tthis.workspaceDir = options.workspaceDir;\n\t\tthis.resolveGitExcludePath = options.resolveGitExcludePath ?? defaultResolveGitExcludePath;\n\t}\n\n\t/** Resolve the worktree-aware exclude file managed by this store. */\n\tasync path(): Promise<string> {\n\t\treturn this.resolveGitExcludePath(this.workspaceDir);\n\t}\n\n\t/** Replace the managed block so it contains exactly the given paths. */\n\tasync reconcile(paths: string[]): Promise<void> {\n\t\tconst excludePath = await this.path();\n\t\tawait fsp.mkdir(path.dirname(excludePath), { recursive: true });\n\n\t\tlet existing = \"\";\n\t\ttry {\n\t\t\texisting = await fsp.readFile(excludePath, \"utf8\");\n\t\t} catch {\n\t\t\t// New file — nothing to preserve.\n\t\t}\n\n\t\tconst outside = stripManagedBlock(existing);\n\t\tconst uniquePaths = [...new Set(paths)].sort();\n\t\tconst next =\n\t\t\tuniquePaths.length === 0\n\t\t\t\t? outside\n\t\t\t\t: `${outside}${outside.endsWith(\"\\n\") || outside.length === 0 ? \"\" : \"\\n\"}${BLOCK_START}\\n${uniquePaths.join(\"\\n\")}\\n${BLOCK_END}\\n`;\n\n\t\tawait fsp.writeFile(excludePath, next, \"utf8\");\n\t}\n}\n\n/** Remove the managed block from exclude content, keeping outside lines. */\nfunction stripManagedBlock(content: string): string {\n\tconst startIndex = content.indexOf(BLOCK_START);\n\tif (startIndex === -1) return content.replace(/\\n$/, \"\");\n\tconst endIndex = content.indexOf(BLOCK_END, startIndex);\n\tif (endIndex === -1) return content.slice(0, startIndex).replace(/\\n$/, \"\");\n\treturn (content.slice(0, startIndex) + content.slice(endIndex + BLOCK_END.length)).replace(\n\t\t/^\\n+|\\n+$/g,\n\t\t\"\"\n\t);\n}\n","import * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { z } from \"zod\";\n\nimport { SAFE_REPO_ID_PATTERN } from \"../paths/userHome\";\nimport type {\n\tCopilotArtifactKind,\n\tWorkspaceCopilotManifest,\n\tWorkspaceCopilotManifestV1,\n\tWorkspaceManifestRepository,\n} from \"./types\";\n\n/** Location of the shared declaration inside a workspace. */\nexport const WORKSPACE_MANIFEST_RELPATH = path.join(\".github\", \"serviceme-plugins.json\");\n\nconst FULL_SHA_PATTERN = /^[0-9a-fA-F]{40}$/;\nconst GIT_URL_PATTERN = /^(?:https:\\/\\/[^\\s]+|git@[^\\s]+)$/;\nconst PLUGIN_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;\nconst CATALOG_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._/-]{0,255}$/;\nconst CATALOG_REVISION_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._+-]{0,127}$/;\nconst SHA256_DIGEST_PATTERN = /^sha256:[0-9a-fA-F]{64}$/;\n\nconst artifactKindSchema = z.enum([\"agent\", \"skill\", \"instruction\", \"prompt\", \"hook\", \"mcp\"]);\n\nfunction isSafeArtifactId(artifactId: string): boolean {\n\tconst parts = artifactId.split(\"::\");\n\tif (parts.length !== 3) return false;\n\tconst [repositoryId, pluginId, kindAndName] = parts;\n\tif (!repositoryId || !pluginId || !kindAndName) return false;\n\tif (!SAFE_REPO_ID_PATTERN.test(repositoryId) || !PLUGIN_ID_PATTERN.test(pluginId)) return false;\n\tconst separator = kindAndName.indexOf(\":\");\n\tif (separator <= 0 || separator === kindAndName.length - 1) return false;\n\tconst kind = kindAndName.slice(0, separator);\n\tconst name = kindAndName.slice(separator + 1);\n\treturn (\n\t\tartifactKindSchema.safeParse(kind).success &&\n\t\t!/[\\\\/\\0]/.test(name) &&\n\t\tname !== \".\" &&\n\t\tname !== \"..\"\n\t);\n}\n\nconst artifactIdSchema = z.string().refine(isSafeArtifactId, \"unsafe artifact id\");\n\nconst repositorySchema = z.object({\n\tid: z.string().regex(SAFE_REPO_ID_PATTERN, \"unsafe repository id\"),\n\turl: z.string().regex(GIT_URL_PATTERN, \"repository url must be HTTPS or git@\"),\n\tcommit: z.string().regex(FULL_SHA_PATTERN, \"commit must be a full 40-char SHA\"),\n});\n\nconst gitSourceSchema = repositorySchema.extend({ type: z.literal(\"git\") });\n\nconst catalogSourceSchema = z.object({\n\ttype: z.literal(\"catalog\"),\n\tid: z.string().regex(SAFE_REPO_ID_PATTERN, \"unsafe source id\"),\n\tcatalogId: z.string().regex(CATALOG_ID_PATTERN, \"unsafe catalog id\"),\n\trevision: z.string().regex(CATALOG_REVISION_PATTERN, \"invalid catalog revision\"),\n\tdigest: z.string().regex(SHA256_DIGEST_PATTERN, \"invalid catalog digest\"),\n});\n\nconst pluginSchema = z.object({\n\trepository: z.string().min(1),\n\tid: z.string().regex(PLUGIN_ID_PATTERN, \"unsafe plugin id\"),\n\tartifacts: z.record(z.string(), z.boolean()),\n\tartifactIds: z.array(artifactIdSchema).optional(),\n});\n\nconst pluginV2Schema = z.object({\n\tsource: z.string().min(1),\n\tid: z.string().regex(PLUGIN_ID_PATTERN, \"unsafe plugin id\"),\n\tartifacts: z.record(z.string(), z.boolean()),\n\tartifactIds: z.array(artifactIdSchema).optional(),\n});\n\nfunction validatePluginArtifacts(\n\tplugins: Array<{ artifacts: Record<string, boolean>; artifactIds?: string[] }>,\n\tctx: z.RefinementCtx\n): void {\n\tfor (const plugin of plugins) {\n\t\tfor (const key of Object.keys(plugin.artifacts)) {\n\t\t\tif (!artifactKindSchema.safeParse(key).success) {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: \"custom\",\n\t\t\t\t\tpath: [\"plugins\"],\n\t\t\t\t\tmessage: `unknown artifact kind ${key}`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tif (plugin.artifactIds && new Set(plugin.artifactIds).size !== plugin.artifactIds.length) {\n\t\t\tctx.addIssue({\n\t\t\t\tcode: \"custom\",\n\t\t\t\tpath: [\"plugins\"],\n\t\t\t\tmessage: \"duplicate artifact id\",\n\t\t\t});\n\t\t}\n\t}\n}\n\nconst manifestV1Schema = z\n\t.object({\n\t\tversion: z.literal(1),\n\t\trepositories: z.array(repositorySchema),\n\t\tplugins: z.array(pluginSchema),\n\t})\n\t.superRefine((value, ctx) => {\n\t\tconst ids = new Set<string>();\n\t\tfor (const repository of value.repositories) {\n\t\t\tif (ids.has(repository.id)) {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: \"custom\",\n\t\t\t\t\tpath: [\"repositories\"],\n\t\t\t\t\tmessage: `duplicate repository id ${repository.id}`,\n\t\t\t\t});\n\t\t\t}\n\t\t\tids.add(repository.id);\n\t\t}\n\t\tfor (const plugin of value.plugins) {\n\t\t\tif (!ids.has(plugin.repository)) {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: \"custom\",\n\t\t\t\t\tpath: [\"plugins\"],\n\t\t\t\t\tmessage: `plugin references unknown repository ${plugin.repository}`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tvalidatePluginArtifacts(value.plugins, ctx);\n\t});\n\nconst manifestV2Schema = z\n\t.object({\n\t\tversion: z.literal(2),\n\t\tsources: z.array(z.discriminatedUnion(\"type\", [gitSourceSchema, catalogSourceSchema])),\n\t\tplugins: z.array(pluginV2Schema),\n\t})\n\t.superRefine((value, ctx) => {\n\t\tconst ids = new Set<string>();\n\t\tfor (const source of value.sources) {\n\t\t\tif (ids.has(source.id)) {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: \"custom\",\n\t\t\t\t\tpath: [\"sources\"],\n\t\t\t\t\tmessage: `duplicate source id ${source.id}`,\n\t\t\t\t});\n\t\t\t}\n\t\t\tids.add(source.id);\n\t\t}\n\t\tfor (const plugin of value.plugins) {\n\t\t\tif (!ids.has(plugin.source)) {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: \"custom\",\n\t\t\t\t\tpath: [\"plugins\"],\n\t\t\t\t\tmessage: `plugin references unknown source ${plugin.source}`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tvalidatePluginArtifacts(value.plugins, ctx);\n\t});\n\nconst manifestSchema = z.union([manifestV1Schema, manifestV2Schema]);\n\n/** Absolute path of the declaration file inside a workspace. */\nexport function getWorkspaceManifestPath(workspaceDir: string): string {\n\treturn path.join(workspaceDir, WORKSPACE_MANIFEST_RELPATH);\n}\n\n/** Read and validate the declaration; undefined when absent. */\nexport async function loadWorkspaceCopilotManifest(\n\tworkspaceDir: string\n): Promise<WorkspaceCopilotManifest | undefined> {\n\tconst manifestPath = getWorkspaceManifestPath(workspaceDir);\n\tlet raw: string;\n\ttry {\n\t\traw = await fsp.readFile(manifestPath, \"utf8\");\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n\t\tthrow error;\n\t}\n\n\tconst parsed = manifestSchema.safeParse(JSON.parse(raw));\n\tif (!parsed.success) {\n\t\tthrow new Error(\n\t\t\t`Invalid workspace Copilot manifest at ${manifestPath}: ${parsed.error.message}`\n\t\t);\n\t}\n\treturn parsed.data as WorkspaceCopilotManifest;\n}\n\n/** Validate and atomically write the shared declaration. */\nexport async function writeWorkspaceCopilotManifest(\n\tworkspaceDir: string,\n\tmanifest: WorkspaceCopilotManifest\n): Promise<void> {\n\tconst parsed = manifestSchema.safeParse(manifest);\n\tif (!parsed.success) {\n\t\tthrow new Error(`Invalid workspace Copilot manifest: ${parsed.error.message}`);\n\t}\n\n\tconst manifestPath = getWorkspaceManifestPath(workspaceDir);\n\tawait fsp.mkdir(path.dirname(manifestPath), { recursive: true });\n\tconst tmpPath = `${manifestPath}.tmp-${process.pid}-${Date.now()}`;\n\tawait fsp.writeFile(tmpPath, `${JSON.stringify(parsed.data, null, \"\\t\")}\\n`, \"utf8\");\n\tawait fsp.rename(tmpPath, manifestPath);\n}\n\n/**\n * Atomically replace one package selection with exact resolver artifact IDs.\n * New selections clear legacy kind maps so future resolution remains stable\n * if a package adds another artifact of an existing kind.\n */\nexport async function replaceWorkspaceContentSelection(input: {\n\tworkspaceDir: string;\n\trepository: WorkspaceManifestRepository;\n\tpluginId: string;\n\tartifactIds: string[];\n}): Promise<WorkspaceCopilotManifest> {\n\tif (input.artifactIds.length === 0) {\n\t\tthrow new Error(\"Invalid exact artifact selection: at least one artifact id is required\");\n\t}\n\tconst parsedArtifactIds = z.array(artifactIdSchema).safeParse(input.artifactIds);\n\tif (!parsedArtifactIds.success) {\n\t\tthrow new Error(`Invalid exact artifact selection: ${parsedArtifactIds.error.message}`);\n\t}\n\tif (new Set(input.artifactIds).size !== input.artifactIds.length) {\n\t\tthrow new Error(\"Invalid exact artifact selection: duplicate artifact id\");\n\t}\n\tconst expectedPrefix = `${input.repository.id}::${input.pluginId}::`;\n\tif (input.artifactIds.some((artifactId) => !artifactId.startsWith(expectedPrefix))) {\n\t\tthrow new Error(\"Invalid exact artifact selection: artifact id belongs to another package\");\n\t}\n\n\tconst loaded = await loadWorkspaceCopilotManifest(input.workspaceDir);\n\tif (loaded?.version === 2) {\n\t\tconst sources = loaded.sources.some((source) => source.id === input.repository.id)\n\t\t\t? loaded.sources.map((source) =>\n\t\t\t\t\tsource.id === input.repository.id\n\t\t\t\t\t\t? source.type === \"catalog\"\n\t\t\t\t\t\t\t? source\n\t\t\t\t\t\t\t: { ...input.repository, type: \"git\" as const }\n\t\t\t\t\t\t: source\n\t\t\t\t)\n\t\t\t: [...loaded.sources, { ...input.repository, type: \"git\" as const }];\n\t\tconst existing = loaded.plugins.find(\n\t\t\t(plugin) => plugin.source === input.repository.id && plugin.id === input.pluginId\n\t\t);\n\t\tconst replacement = {\n\t\t\tsource: input.repository.id,\n\t\t\tid: input.pluginId,\n\t\t\tartifacts: {},\n\t\t\tartifactIds: input.artifactIds,\n\t\t};\n\t\tconst plugins = existing\n\t\t\t? loaded.plugins.map((plugin) => (plugin === existing ? replacement : plugin))\n\t\t\t: [...loaded.plugins, replacement];\n\t\tconst next: WorkspaceCopilotManifest = { version: 2, sources, plugins };\n\t\tawait writeWorkspaceCopilotManifest(input.workspaceDir, next);\n\t\treturn next;\n\t}\n\n\tconst manifest: WorkspaceCopilotManifestV1 =\n\t\tloaded?.version === 1\n\t\t\t? loaded\n\t\t\t: {\n\t\t\t\t\tversion: 1,\n\t\t\t\t\trepositories: [],\n\t\t\t\t\tplugins: [],\n\t\t\t\t};\n\tconst repositories = manifest.repositories.some((repo) => repo.id === input.repository.id)\n\t\t? manifest.repositories.map((repo) =>\n\t\t\t\trepo.id === input.repository.id ? input.repository : repo\n\t\t\t)\n\t\t: [...manifest.repositories, input.repository];\n\tconst existing = manifest.plugins.find(\n\t\t(plugin) => plugin.repository === input.repository.id && plugin.id === input.pluginId\n\t);\n\tconst replacement = {\n\t\trepository: input.repository.id,\n\t\tid: input.pluginId,\n\t\tartifacts: {},\n\t\tartifactIds: input.artifactIds,\n\t};\n\tconst plugins = existing\n\t\t? manifest.plugins.map((plugin) => (plugin === existing ? replacement : plugin))\n\t\t: [...manifest.plugins, replacement];\n\tconst next: WorkspaceCopilotManifest = { version: 1, repositories, plugins };\n\tawait writeWorkspaceCopilotManifest(input.workspaceDir, next);\n\treturn next;\n}\n\n/**\n * Add or update one artifact-kind selection (any of the six kinds) in the\n * shared declaration, creating the file when absent. Returns the new manifest.\n *\n * The repository entry must already exist (or be provided via\n * `repository`) with a full pinned commit; this helper only manages\n * the plugin selection so install flows never race a concurrent write\n * of unrelated selections.\n */\nexport async function upsertWorkspaceContentSelection(input: {\n\tworkspaceDir: string;\n\trepository: WorkspaceManifestRepository;\n\tpluginId: string;\n\tkind: CopilotArtifactKind;\n}): Promise<WorkspaceCopilotManifest> {\n\tconst loaded = await loadWorkspaceCopilotManifest(input.workspaceDir).catch(() => undefined);\n\tconst manifest: WorkspaceCopilotManifestV1 =\n\t\tloaded?.version === 1\n\t\t\t? loaded\n\t\t\t: {\n\t\t\t\t\tversion: 1,\n\t\t\t\t\trepositories: [],\n\t\t\t\t\tplugins: [],\n\t\t\t\t};\n\n\tconst repositories = manifest.repositories.some((repo) => repo.id === input.repository.id)\n\t\t? manifest.repositories.map((repo) =>\n\t\t\t\trepo.id === input.repository.id ? input.repository : repo\n\t\t\t)\n\t\t: [...manifest.repositories, input.repository];\n\n\tconst existing = manifest.plugins.find(\n\t\t(plugin) => plugin.repository === input.repository.id && plugin.id === input.pluginId\n\t);\n\tconst artifacts = { ...(existing?.artifacts ?? {}), [input.kind]: true };\n\tconst plugins = existing\n\t\t? manifest.plugins.map((plugin) => (plugin === existing ? { ...plugin, artifacts } : plugin))\n\t\t: [...manifest.plugins, { repository: input.repository.id, id: input.pluginId, artifacts }];\n\n\tconst next: WorkspaceCopilotManifest = { version: 1, repositories, plugins };\n\tawait writeWorkspaceCopilotManifest(input.workspaceDir, next);\n\treturn next;\n}\n\n/**\n * Remove one artifact kind from a plugin selection, dropping\n * the plugin entirely when no artifact kind remains enabled. Returns\n * the new manifest; a no-op when the selection does not exist.\n */\nexport async function removeWorkspaceContentSelection(input: {\n\tworkspaceDir: string;\n\trepositoryId: string;\n\tpluginId: string;\n\tkind: CopilotArtifactKind;\n}): Promise<WorkspaceCopilotManifest> {\n\tconst manifest = await loadWorkspaceCopilotManifest(input.workspaceDir).catch(() => undefined);\n\tif (manifest?.version !== 1) {\n\t\treturn { version: 1, repositories: [], plugins: [] };\n\t}\n\tconst v1Manifest = manifest;\n\n\tlet changed = false;\n\tconst plugins = [];\n\tfor (const plugin of v1Manifest.plugins) {\n\t\tif (plugin.repository !== input.repositoryId || plugin.id !== input.pluginId) {\n\t\t\tplugins.push(plugin);\n\t\t\tcontinue;\n\t\t}\n\t\tconst { [input.kind]: _removed, ...remainingArtifacts } = plugin.artifacts;\n\t\tconst keepsKind = Object.entries(remainingArtifacts).some(([, enabled]) => enabled);\n\t\tchanged = true;\n\t\tif (keepsKind) {\n\t\t\tplugins.push({ ...plugin, artifacts: remainingArtifacts });\n\t\t}\n\t}\n\tif (!changed) return manifest;\n\n\tconst usedRepositoryIds = new Set(plugins.map((plugin) => plugin.repository));\n\tconst repositories = v1Manifest.repositories.filter((repo) => usedRepositoryIds.has(repo.id));\n\tconst next: WorkspaceCopilotManifest = { version: 1, repositories, plugins };\n\tawait writeWorkspaceCopilotManifest(input.workspaceDir, next);\n\treturn next;\n}\n","import { createHash } from \"node:crypto\";\nimport * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport { getServicemeHome, getWorkspacesDir } from \"../paths/userHome\";\nimport type { WorkspaceContentState } from \"./types\";\n\n/** Hash a canonical workspace path into a stable directory segment. */\nexport async function hashWorkspaceDir(workspaceDir: string): Promise<string> {\n\tconst canonical = await fsp.realpath(path.resolve(workspaceDir));\n\treturn createHash(\"sha256\").update(canonical).digest(\"hex\").slice(0, 16);\n}\n\n/** Directory holding machine-local state for one workspace. */\nexport async function getWorkspaceStateDir(\n\tworkspaceDir: string,\n\thomeDir = getServicemeHome()\n): Promise<string> {\n\treturn homeDir === getServicemeHome()\n\t\t? path.join(getWorkspacesDir(), await hashWorkspaceDir(workspaceDir))\n\t\t: path.join(homeDir, \"workspaces\", await hashWorkspaceDir(workspaceDir));\n}\n\n/** Store for machine-local Copilot content state; one file per workspace. */\nexport class WorkspaceContentStateStore {\n\tprivate readonly workspaceDir: string;\n\tprivate readonly homeDir: string;\n\tprivate cachedPath: string | undefined;\n\n\tconstructor(options: { workspaceDir: string; homeDir?: string }) {\n\t\tthis.workspaceDir = options.workspaceDir;\n\t\tthis.homeDir = options.homeDir ?? getServicemeHome();\n\t}\n\n\t/** Absolute state-file path for this workspace. */\n\tasync path(): Promise<string> {\n\t\tif (this.cachedPath === undefined) {\n\t\t\tthis.cachedPath = path.join(\n\t\t\t\tawait getWorkspaceStateDir(this.workspaceDir, this.homeDir),\n\t\t\t\t\"copilot-content-state.json\"\n\t\t\t);\n\t\t}\n\t\treturn this.cachedPath;\n\t}\n\n\t/** Read state; an empty state when the file does not exist yet. */\n\tasync read(): Promise<WorkspaceContentState> {\n\t\ttry {\n\t\t\tconst raw = await fsp.readFile(await this.path(), \"utf8\");\n\t\t\treturn JSON.parse(raw) as WorkspaceContentState;\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\treturn { version: 1, entries: [] };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/** Atomically persist state. */\n\tasync write(state: WorkspaceContentState): Promise<void> {\n\t\tconst statePath = await this.path();\n\t\tawait fsp.mkdir(path.dirname(statePath), { recursive: true });\n\t\tconst tmpPath = `${statePath}.tmp-${process.pid}-${Date.now()}`;\n\t\tawait fsp.writeFile(tmpPath, JSON.stringify(state), \"utf8\");\n\t\tawait fsp.rename(tmpPath, statePath);\n\t}\n}\n","import * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport { getServicemeHome } from \"../paths/userHome\";\nimport {\n\ttype CopilotHostCapabilities,\n\ttype PersonalLinkKind,\n\tsupportsPersonalIntegrationKind,\n\tsupportsPersonalLinkKind,\n} from \"./copilot-host-capabilities\";\nimport { CopilotLinkMaterializer, type MaterializeEntryResult } from \"./copilot-link-materializer\";\nimport type { CopilotArtifactState } from \"./customization-model\";\nimport { PersonalInstallationStore } from \"./personal-installation-store\";\nimport type { ResolvedPluginEntry } from \"./plugin-resolver\";\nimport type { CopilotArtifactKind, WorkspaceMaterializedEntry } from \"./types\";\n\n/** Narrow an artifact kind to the four personal link kinds. */\nfunction isPersonalLinkKind(kind: CopilotArtifactKind): kind is PersonalLinkKind {\n\treturn kind === \"agent\" || kind === \"skill\" || kind === \"prompt\" || kind === \"instruction\";\n}\n\n/** Narrow an artifact kind to the two personal integration kinds. */\nfunction isPersonalIntegrationKind(kind: CopilotArtifactKind): kind is \"mcp\" | \"hook\" {\n\treturn kind === \"mcp\" || kind === \"hook\";\n}\n\n/** Machine-local materialization state for personal-scope content. */\nexport interface PersonalContentState {\n\tversion: 1;\n\tentries: WorkspaceMaterializedEntry[];\n}\n\n/** Reconcile result: artifact health keyed by artifact id. */\nexport interface PersonalReconcileResult {\n\tstatesByArtifactId: Record<string, CopilotArtifactState>;\n}\n\n/** Resolves the artifact entries a package contributes. */\nexport type PersonalPackageResolver = (packageId: string) => Promise<ResolvedPluginEntry[]>;\n\ninterface PersonalReconcilerOptions {\n\t/** SERVICEME home directory holding intent and machine state. */\n\thomeDir?: string;\n\t/** Host-verified capabilities; tests inject per-kind presence. */\n\tcapabilities: CopilotHostCapabilities;\n\t/** Resolves package definitions; defaults to no packages. */\n\tresolvePackage?: PersonalPackageResolver;\n}\n\n/** Manage all six personal artifact kinds against host capabilities. */\nexport class PersonalCopilotContentReconciler {\n\tprivate readonly store: PersonalInstallationStore;\n\tprivate readonly capabilities: CopilotHostCapabilities;\n\tprivate readonly resolvePackage: PersonalPackageResolver;\n\tprivate readonly statePath: string;\n\n\tconstructor(options: PersonalReconcilerOptions) {\n\t\tthis.store = new PersonalInstallationStore({ homeDir: options.homeDir });\n\t\tthis.capabilities = options.capabilities;\n\t\tthis.resolvePackage = options.resolvePackage ?? (async () => []);\n\t\tthis.statePath = path.join(\n\t\t\toptions.homeDir ?? getServicemeHome(),\n\t\t\t\"copilot\",\n\t\t\t\"personal-content-state.json\"\n\t\t);\n\t}\n\n\t/** Read current machine state; empty state when not materialized yet. */\n\tasync readState(): Promise<PersonalContentState> {\n\t\ttry {\n\t\t\tconst raw = await fsp.readFile(this.statePath, \"utf8\");\n\t\t\treturn JSON.parse(raw) as PersonalContentState;\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\treturn { version: 1, entries: [] };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/** Inspect current personal state without materializing anything. */\n\tasync inspect(): Promise<PersonalReconcileResult> {\n\t\tconst plan = await this.buildPlan();\n\t\tconst previousState = await this.readState();\n\t\tconst states = await this.projectStates(plan.entries, previousState.entries);\n\t\treturn { statesByArtifactId: states };\n\t}\n\n\t/** Materialize personal intent for all supported kinds on this machine. */\n\tasync reconcile(): Promise<PersonalReconcileResult> {\n\t\tconst plan = await this.buildPlan();\n\t\tconst previousState = await this.readState();\n\t\tconst states: Record<string, CopilotArtifactState> = {};\n\n\t\t// Full-plan uniqueness: the same kind:name target from any two\n\t\t// installations is a conflict, never a silent overwrite.\n\t\tconst targetOwners = new Map<string, string[]>();\n\t\tfor (const entry of plan.entries) {\n\t\t\tconst targetKey = `${entry.kind}:${entry.name}`;\n\t\t\tconst owners = targetOwners.get(targetKey) ?? [];\n\t\t\tfor (const owner of owners) {\n\t\t\t\tstates[entry.artifactId] = conflictState();\n\t\t\t\tstates[owner] = conflictState();\n\t\t\t}\n\t\t\towners.push(entry.artifactId);\n\t\t\ttargetOwners.set(targetKey, owners);\n\t\t}\n\t\tconst conflictedIds = new Set(\n\t\t\tObject.entries(states)\n\t\t\t\t.filter(([, state]) => state.health === \"conflict\")\n\t\t\t\t.map(([artifactId]) => artifactId)\n\t\t);\n\n\t\t// Remove owned materialization for identities that left the plan.\n\t\tconst plannedIds = new Set(plan.entries.map((entry) => entry.artifactId));\n\t\tfor (const previous of previousState.entries) {\n\t\t\tif (plannedIds.has(previous.identity)) continue;\n\t\t\tawait this.removeOwnedMaterialization(previous);\n\t\t}\n\n\t\tconst linkEntries = plan.entries.filter(\n\t\t\t(entry): entry is ResolvedPluginEntry & { kind: PersonalLinkKind } =>\n\t\t\t\tisPersonalLinkKind(entry.kind) && !conflictedIds.has(entry.artifactId)\n\t\t);\n\t\tconst integrationEntries = plan.entries.filter(\n\t\t\t(entry): entry is ResolvedPluginEntry & { kind: \"mcp\" | \"hook\" } =>\n\t\t\t\tisPersonalIntegrationKind(entry.kind) && !conflictedIds.has(entry.artifactId)\n\t\t);\n\n\t\t// Link kinds: reuse the workspace materializer's safety checks by\n\t\t// materializing into each kind's host-verified target directory.\n\t\tconst materializer = new CopilotLinkMaterializer();\n\t\tconst nextState: WorkspaceMaterializedEntry[] = [];\n\t\tconst entriesByTarget = new Map<string, ResolvedPluginEntry[]>();\n\t\tfor (const entry of linkEntries) {\n\t\t\tconst target = this.capabilities.personalTargets[entry.kind];\n\t\t\tif (target === undefined) continue;\n\t\t\tconst bucket = entriesByTarget.get(target) ?? [];\n\t\t\tbucket.push(entry);\n\t\t\tentriesByTarget.set(target, bucket);\n\t\t}\n\t\tconst personalTargetFor = (kind: PersonalLinkKind): string | undefined =>\n\t\t\tthis.capabilities.personalTargets[kind];\n\t\tconst resultsByIdentity = new Map<string, MaterializeEntryResult>();\n\t\tfor (const [targetDir, entries] of entriesByTarget) {\n\t\t\tconst materialized = await materializer.reconcile({\n\t\t\t\t// Trailing separator marks a direct personal target root for the\n\t\t\t\t// materializer (links land in the directory itself, not .github/...).\n\t\t\t\tworkspaceDir: `${targetDir}${path.sep}`,\n\t\t\t\tentries,\n\t\t\t\tpreviousState: {\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tentries: previousState.entries.filter((candidate) =>\n\t\t\t\t\t\tentries.some((entry) => entry.artifactId === candidate.identity)\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t});\n\t\t\tnextState.push(...materialized.state.entries);\n\t\t\tfor (const result of materialized.entries) {\n\t\t\t\tresultsByIdentity.set(result.identity, result);\n\t\t\t}\n\t\t}\n\t\t// The materializer records approved: false for freshly created\n\t\t// links, so approval recorded in previous state must survive a\n\t\t// reconcile that recreates the link (mirrors the workspace path).\n\t\tconst approvedIdentities = new Set(\n\t\t\tpreviousState.entries.filter((entry) => entry.approved).map((entry) => entry.identity)\n\t\t);\n\t\tconst digestByIdentity = new Map(\n\t\t\tplan.entries.map((entry) => [entry.artifactId, entry.digest] as const)\n\t\t);\n\t\tfor (const candidate of nextState) {\n\t\t\tif (!isPersonalLinkKind(candidate.kind)) continue;\n\t\t\tcandidate.approved =\n\t\t\t\tapprovedIdentities.has(candidate.identity) &&\n\t\t\t\tcandidate.digest === digestByIdentity.get(candidate.identity);\n\t\t}\n\t\t// Derive link-kind states from the materializer's own statuses so\n\t\t// tampered, foreign, and approval-gated content is never reported healthy.\n\t\tfor (const entry of linkEntries) {\n\t\t\tconst target = personalTargetFor(entry.kind);\n\t\t\tif (target === undefined) {\n\t\t\t\tstates[entry.artifactId] = unsupportedState();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst result = resultsByIdentity.get(entry.artifactId);\n\t\t\tif (result?.status === \"conflict\") {\n\t\t\t\tstates[entry.artifactId] = conflictState();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (result?.status === \"drifted\") {\n\t\t\t\tstates[entry.artifactId] = driftState();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (result?.status === \"pending_approval\") {\n\t\t\t\tstates[entry.artifactId] = pendingApprovalState();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst next = nextState.find((candidate) => candidate.identity === entry.artifactId);\n\t\t\tstates[entry.artifactId] =\n\t\t\t\tnext && next.digest === entry.digest && (!entry.requiresApproval || next.approved)\n\t\t\t\t\t? healthyReady()\n\t\t\t\t\t: next && next.digest === entry.digest\n\t\t\t\t\t\t? pendingApprovalState()\n\t\t\t\t\t\t: next\n\t\t\t\t\t\t\t? driftState()\n\t\t\t\t\t\t\t: conflictState();\n\t\t}\n\n\t\t// Integration kinds: adapter-gated with the workspace digest approval rule.\n\t\tfor (const entry of integrationEntries) {\n\t\t\tconst adapter =\n\t\t\t\tentry.kind === \"mcp\" ? this.capabilities.mcpAdapter : this.capabilities.hookAdapter;\n\t\t\tif (!adapter || !supportsPersonalIntegrationKind(this.capabilities, entry.kind)) {\n\t\t\t\tstates[entry.artifactId] = unsupportedState();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst previous = previousState.entries.find(\n\t\t\t\t(candidate) => candidate.identity === entry.artifactId\n\t\t\t);\n\t\t\tconst approved = previous?.approved === true && previous.digest === entry.digest;\n\t\t\tif (!approved) {\n\t\t\t\tstates[entry.artifactId] =\n\t\t\t\t\tprevious && previous.digest !== entry.digest\n\t\t\t\t\t\t? driftApprovalState()\n\t\t\t\t\t\t: pendingApprovalState();\n\t\t\t\tnextState.push(\n\t\t\t\t\tprevious ? { ...previous, approved: false } : toIntegrationStateEntry(entry, false)\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait adapter.applyEntry({ entry });\n\t\t\t\tstates[entry.artifactId] = healthyReady();\n\t\t\t\tnextState.push(toIntegrationStateEntry(entry, true));\n\t\t\t} catch {\n\t\t\t\tstates[entry.artifactId] = conflictState();\n\t\t\t\tnextState.push(\n\t\t\t\t\tprevious ? { ...previous, approved: false } : toIntegrationStateEntry(entry, false)\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Every planned artifact stays visible even when nothing could run.\n\t\tfor (const entry of plan.entries) {\n\t\t\tif (states[entry.artifactId] === undefined) {\n\t\t\t\tstates[entry.artifactId] = unsupportedState();\n\t\t\t}\n\t\t}\n\n\t\t// Conflict preservation: a previously applied artifact that is now\n\t\t// conflicted (duplicate target or foreign occupier) is excluded from\n\t\t// the materialization loops above, so without this carry-over its\n\t\t// state entry would be dropped while the host configuration we wrote\n\t\t// stays applied — orphaned forever. Keep the previous entry so a\n\t\t// later deconflict can still remove exactly what SERVICEME owns.\n\t\tconst recordedIds = new Set(nextState.map((entry) => entry.identity));\n\t\tfor (const previous of previousState.entries) {\n\t\t\tif (!plannedIds.has(previous.identity)) continue;\n\t\t\tif (states[previous.identity]?.health !== \"conflict\") continue;\n\t\t\tif (recordedIds.has(previous.identity)) continue;\n\t\t\tnextState.push(previous);\n\t\t}\n\n\t\tawait this.writeState({\n\t\t\tversion: 1,\n\t\t\tentries: dedupeByIdentity(nextState).filter((candidate) =>\n\t\t\t\tplannedIds.has(candidate.identity)\n\t\t\t),\n\t\t});\n\n\t\treturn { statesByArtifactId: states };\n\t}\n\n\t/** Record local approval for integration content, then re-reconcile. */\n\tasync approve(input: { artifactIds: string[] }): Promise<PersonalReconcileResult> {\n\t\tconst state = await this.readState();\n\t\tconst artifactIds = new Set(input.artifactIds);\n\t\tfor (const entry of state.entries) {\n\t\t\tif (artifactIds.has(entry.identity)) {\n\t\t\t\tentry.approved = true;\n\t\t\t}\n\t\t}\n\t\tawait this.writeState(state);\n\t\treturn this.reconcile();\n\t}\n\n\t/** Write machine state atomically. */\n\tprivate async writeState(state: PersonalContentState): Promise<void> {\n\t\tawait fsp.mkdir(path.dirname(this.statePath), { recursive: true });\n\t\tconst tmpPath = `${this.statePath}.tmp-${process.pid}-${Date.now()}`;\n\t\tawait fsp.writeFile(tmpPath, JSON.stringify(state), \"utf8\");\n\t\tawait fsp.rename(tmpPath, this.statePath);\n\t}\n\n\tprivate async buildPlan(): Promise<{ entries: ResolvedPluginEntry[] }> {\n\t\tconst intent = await this.store.read();\n\t\tconst entries: ResolvedPluginEntry[] = [];\n\t\tfor (const installation of intent.installations) {\n\t\t\tif (installation.scope !== \"personal\") continue;\n\t\t\tconst resolved = await this.resolvePackage(installation.packageId);\n\t\t\tentries.push(\n\t\t\t\t...resolved.filter((entry) => installation.selectedArtifactIds.includes(entry.artifactId))\n\t\t\t);\n\t\t}\n\t\treturn { entries };\n\t}\n\n\tprivate async removeOwnedMaterialization(previous: WorkspaceMaterializedEntry): Promise<void> {\n\t\tif (previous.kind === \"mcp\" || previous.kind === \"hook\") {\n\t\t\tconst adapter =\n\t\t\t\tprevious.kind === \"mcp\" ? this.capabilities.mcpAdapter : this.capabilities.hookAdapter;\n\t\t\tif (adapter) {\n\t\t\t\tawait adapter.removeEntry({ identity: previous.identity });\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!isPersonalLinkKind(previous.kind)) return;\n\t\tconst target = this.capabilities.personalTargets[previous.kind];\n\t\tif (target === undefined) return;\n\t\tconst linkPath = path.join(target, path.basename(previous.linkPath));\n\t\tconst stat = await fsp.lstat(linkPath).catch(() => null);\n\t\tif (!stat?.isSymbolicLink()) return;\n\t\tconst currentTarget = await fsp.readlink(linkPath).catch(() => null);\n\t\tif (currentTarget !== previous.sourcePath) return;\n\t\tawait fsp.rm(linkPath, { force: true });\n\t}\n\n\tprivate async projectStates(\n\t\tentries: ResolvedPluginEntry[],\n\t\tpreviousEntries: WorkspaceMaterializedEntry[]\n\t): Promise<Record<string, CopilotArtifactState>> {\n\t\tconst states: Record<string, CopilotArtifactState> = {};\n\t\tfor (const entry of entries) {\n\t\t\tconst previous = previousEntries.find((candidate) => candidate.identity === entry.artifactId);\n\t\t\tif (entry.kind === \"mcp\" || entry.kind === \"hook\") {\n\t\t\t\tif (!supportsPersonalIntegrationKind(this.capabilities, entry.kind)) {\n\t\t\t\t\tstates[entry.artifactId] = unsupportedState();\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tstates[entry.artifactId] = previous\n\t\t\t\t\t? previous.digest === entry.digest\n\t\t\t\t\t\t? previous.approved\n\t\t\t\t\t\t\t? healthyReady()\n\t\t\t\t\t\t\t: pendingApprovalState()\n\t\t\t\t\t\t: driftApprovalState()\n\t\t\t\t\t: pendingApprovalState();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!supportsPersonalLinkKind(this.capabilities, entry.kind)) {\n\t\t\t\tstates[entry.artifactId] = unsupportedState();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tstates[entry.artifactId] = previous\n\t\t\t\t? previous.digest === entry.digest\n\t\t\t\t\t? entry.requiresApproval && !previous.approved\n\t\t\t\t\t\t? pendingApprovalState()\n\t\t\t\t\t\t: healthyReady()\n\t\t\t\t\t: driftState()\n\t\t\t\t: missingReady();\n\t\t}\n\t\treturn states;\n\t}\n}\n\nfunction toIntegrationStateEntry(\n\tentry: ResolvedPluginEntry,\n\tapproved: boolean\n): WorkspaceMaterializedEntry {\n\treturn {\n\t\tidentity: entry.artifactId,\n\t\trepositoryId: entry.repositoryId,\n\t\tpluginId: entry.pluginId,\n\t\tkind: entry.kind,\n\t\tsourcePath: entry.sourcePath,\n\t\tlinkPath: \"\",\n\t\tlinkMode: \"generated\",\n\t\tdigest: entry.digest,\n\t\tapproved,\n\t};\n}\n\nfunction dedupeByIdentity(entries: WorkspaceMaterializedEntry[]): WorkspaceMaterializedEntry[] {\n\tconst seen = new Set<string>();\n\tconst result: WorkspaceMaterializedEntry[] = [];\n\tfor (const entry of entries) {\n\t\tif (seen.has(entry.identity)) continue;\n\t\tseen.add(entry.identity);\n\t\tresult.push(entry);\n\t}\n\treturn result;\n}\n\nfunction conflictState(): CopilotArtifactState {\n\treturn { intent: \"selected\", health: \"conflict\", gate: \"ready\" };\n}\n\nfunction unsupportedState(): CopilotArtifactState {\n\treturn { intent: \"selected\", health: \"unsupported\", gate: \"configuration-required\" };\n}\n\nfunction healthyReady(): CopilotArtifactState {\n\treturn { intent: \"selected\", health: \"healthy\", gate: \"ready\" };\n}\n\nfunction pendingApprovalState(): CopilotArtifactState {\n\treturn { intent: \"selected\", health: \"healthy\", gate: \"approval-required\" };\n}\n\nfunction driftApprovalState(): CopilotArtifactState {\n\treturn { intent: \"selected\", health: \"drifted\", gate: \"approval-required\" };\n}\n\nfunction driftState(): CopilotArtifactState {\n\treturn { intent: \"selected\", health: \"drifted\", gate: \"ready\" };\n}\n\nfunction missingReady(): CopilotArtifactState {\n\treturn { intent: \"selected\", health: \"missing\", gate: \"ready\" };\n}\n","import * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport { getServicemeHome } from \"../paths/userHome\";\nimport type { CopilotPackageInstallation } from \"./customization-model\";\n\n/** Persisted personal-scope installation intent. */\nexport interface PersonalInstallationState {\n\tversion: 1;\n\tinstallations: CopilotPackageInstallation[];\n}\n\n/** Atomically persist personal package installation intent under the SERVICEME home. */\nexport class PersonalInstallationStore {\n\tprivate readonly homeDir: string;\n\tprivate cachedPath: string | undefined;\n\n\tconstructor(options: { homeDir?: string } = {}) {\n\t\tthis.homeDir = options.homeDir ?? getServicemeHome();\n\t}\n\n\t/** Absolute intent-file path: SERVICEME_HOME/copilot/personal-installations.json. */\n\tasync path(): Promise<string> {\n\t\tif (this.cachedPath === undefined) {\n\t\t\tthis.cachedPath = path.join(this.homeDir, \"copilot\", \"personal-installations.json\");\n\t\t}\n\t\treturn this.cachedPath;\n\t}\n\n\t/** Read intent; empty intent when the file does not exist yet. */\n\tasync read(): Promise<PersonalInstallationState> {\n\t\ttry {\n\t\t\tconst raw = await fsp.readFile(await this.path(), \"utf8\");\n\t\t\treturn JSON.parse(raw) as PersonalInstallationState;\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\treturn { version: 1, installations: [] };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/** Atomically persist intent. */\n\tasync write(state: PersonalInstallationState): Promise<void> {\n\t\tconst statePath = await this.path();\n\t\tawait fsp.mkdir(path.dirname(statePath), { recursive: true });\n\t\tconst tmpPath = `${statePath}.tmp-${process.pid}-${Date.now()}`;\n\t\tawait fsp.writeFile(tmpPath, JSON.stringify(state), \"utf8\");\n\t\tawait fsp.rename(tmpPath, statePath);\n\t}\n}\n","import * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport type { CopilotArtifactSummary } from \"./customization-model\";\nimport { type ResolvedPluginEntry, resolvePluginEntriesLenient } from \"./plugin-resolver\";\n\n/** A git source to enumerate. `id` is the managed repos dir entry name. */\nexport interface PluginCatalogRepoInput {\n\tid: string;\n\tenabled?: boolean;\n}\n\n/**\n * An installable plugin.json package found in a source — with the\n * same artifact summaries an installed package would expose, so the\n * install preview can select artifacts without a second resolution\n * path.\n */\nexport interface PluginCatalogPackage {\n\tpackageId: string;\n\tsourceId: string;\n\tdisplayName: string;\n\tdescription?: string;\n\tversion?: string;\n\tartifacts: CopilotArtifactSummary[];\n}\n\n/**\n * Enumerate plugin.json packages across the given git sources.\n *\n * Sources resolve under `reposDir/<id>` — the same managed checkout\n * layout the workspace manifest restore uses — and each source's\n * plugins are resolved through the shared workspace plan pipeline.\n * Per-package failures (corrupt manifest, unreadable files) are\n * skipped, not thrown — the catalog must stay usable when a single\n * plugin is broken.\n */\nexport async function listPluginCatalog(input: {\n\treposDir: string;\n\trepos: PluginCatalogRepoInput[];\n}): Promise<PluginCatalogPackage[]> {\n\treturn createPluginCatalogService().list(input);\n}\n\n/**\n * Cache entry keyed by `<repoId>/<pluginId>`. A hit requires the\n * plugin.json mtimeMs AND size to match — content changes always bump\n * at least one of them. Known limitation: edits to a referenced\n * skill/agent source file that never touch plugin.json stay cached\n * until plugin.json itself changes (walking the tree for max-mtime\n * would cost more than the cache saves).\n */\ninterface CacheEntry {\n\tmtimeMs: number;\n\tsize: number;\n\tpkg: PluginCatalogPackage;\n}\n\nexport interface PluginCatalogService {\n\tlist(input: {\n\t\treposDir: string;\n\t\trepos: PluginCatalogRepoInput[];\n\t}): Promise<PluginCatalogPackage[]>;\n}\n\n/** Factory: one cache per service instance (tests use fresh instances). */\nexport function createPluginCatalogService(): PluginCatalogService {\n\tconst cache = new Map<string, CacheEntry>();\n\treturn { list };\n\tasync function list(input: {\n\t\treposDir: string;\n\t\trepos: PluginCatalogRepoInput[];\n\t}): Promise<PluginCatalogPackage[]> {\n\t\tconst packages: PluginCatalogPackage[] = [];\n\t\tfor (const repo of input.repos) {\n\t\t\tif (repo.enabled === false) continue;\n\t\t\tconst sourceId = repo.id;\n\t\t\tconst repoRoot = path.resolve(input.reposDir, sourceId);\n\t\t\tconst pluginIds = await listPluginIds(repoRoot);\n\t\t\t// Drop cache entries for plugins that no longer exist.\n\t\t\tfor (const key of cache.keys()) {\n\t\t\t\tif (key.startsWith(`${sourceId}/`)) {\n\t\t\t\t\tconst pluginId = key.slice(sourceId.length + 1);\n\t\t\t\t\tif (!pluginIds.includes(pluginId)) cache.delete(key);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const pluginId of pluginIds) {\n\t\t\t\tconst pkg = await resolveCatalogPackage(sourceId, repoRoot, pluginId, cache);\n\t\t\t\tif (pkg) packages.push(pkg);\n\t\t\t}\n\t\t}\n\t\treturn packages;\n\t}\n}\n\n/** Directory names under <repoRoot>/plugins; [] when the dir is absent. */\nasync function listPluginIds(repoRoot: string): Promise<string[]> {\n\tconst pluginsDir = path.join(repoRoot, \"plugins\");\n\tconst dirents = await fsp.readdir(pluginsDir, { withFileTypes: true }).catch(() => []);\n\treturn dirents\n\t\t.filter((dirent) => dirent.isDirectory())\n\t\t.filter((dirent) => dirent.name !== \".\" && dirent.name !== \"..\")\n\t\t.map((dirent) => dirent.name);\n}\n\n/**\n * Resolve one plugin through the shared workspace plan pipeline and\n * map its entries to artifact summaries. Returns undefined when the\n * manifest is missing/corrupt or resolution fails (skipped package).\n * A valid plugin.json whose mtime+size match the cache is served from\n * cache without re-resolving.\n */\nasync function resolveCatalogPackage(\n\tsourceId: string,\n\trepoRoot: string,\n\tpluginId: string,\n\tcache: Map<string, CacheEntry>\n): Promise<PluginCatalogPackage | undefined> {\n\t// Two manifest conventions: marketplace plugins at the plugin root,\n\t// VS Code-local plugins under .plugin/ (\"创建插件\" layout).\n\tlet manifestPath = path.join(repoRoot, \"plugins\", pluginId, \"plugin.json\");\n\tif (!(await fsp.stat(manifestPath).catch(() => undefined))?.isFile()) {\n\t\tconst dotPlugin = path.join(repoRoot, \"plugins\", pluginId, \".plugin\", \"plugin.json\");\n\t\tif ((await fsp.stat(dotPlugin).catch(() => undefined))?.isFile()) {\n\t\t\tmanifestPath = dotPlugin;\n\t\t}\n\t}\n\tconst cacheKey = `${sourceId}/${pluginId}`;\n\ttry {\n\t\tconst stat = await fsp.stat(manifestPath);\n\t\tconst cached = cache.get(cacheKey);\n\t\tif (cached && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {\n\t\t\treturn cached.pkg;\n\t\t}\n\n\t\tconst parsed = JSON.parse(await fsp.readFile(manifestPath, \"utf8\")) as {\n\t\t\tname?: unknown;\n\t\t\tdescription?: unknown;\n\t\t\tversion?: unknown;\n\t\t\textensions?: Record<string, Record<string, unknown>>;\n\t\t};\n\t\tif (typeof parsed?.name !== \"string\" || parsed.name.trim().length === 0) {\n\t\t\t// Valid JSON but not a usable manifest — drop any cached\n\t\t\t// entry, otherwise the NEXT scan's mtime+size hit would\n\t\t\t// resurrect the stale package.\n\t\t\tcache.delete(cacheKey);\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// Lenient per-path resolution: upstream plugin.json files in\n\t\t// the wild declare artifact paths that don't all exist (e.g.\n\t\t// `./agents/x.md` for a file shipped as `x.agent.md`). A strict\n\t\t// resolve would throw and hide the whole package from browse;\n\t\t// here failing paths are skipped. Installs still go through\n\t\t// the strict workspace pipeline, so nothing broken gets\n\t\t// silently materialized. (The manifest above was already read\n\t\t// once — reuse it instead of a second readFile+parse.)\n\t\tconst pluginEntries = await resolvePluginEntriesLenient({\n\t\t\trepoRoot,\n\t\t\trepositoryId: sourceId,\n\t\t\tpluginId,\n\t\t\tmanifest: {\n\t\t\t\tname: String(parsed.name),\n\t\t\t\textensions: parsed.extensions ?? {},\n\t\t\t},\n\t\t});\n\t\tif (pluginEntries.length === 0) {\n\t\t\t// Manifest resolves to nothing materializable — same rule:\n\t\t\t// never leave a stale entry behind for later hits.\n\t\t\tcache.delete(cacheKey);\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst pkg: PluginCatalogPackage = {\n\t\t\tpackageId: `${sourceId}::${pluginId}`,\n\t\t\tsourceId,\n\t\t\tdisplayName: parsed.name.trim(),\n\t\t\t...(typeof parsed.description === \"string\" && parsed.description.trim().length > 0\n\t\t\t\t? { description: parsed.description.trim() }\n\t\t\t\t: {}),\n\t\t\t...(typeof parsed.version === \"string\" && parsed.version.trim().length > 0\n\t\t\t\t? { version: parsed.version.trim() }\n\t\t\t\t: {}),\n\t\t\tartifacts: pluginEntries.map(toArtifactSummary),\n\t\t};\n\t\tcache.set(cacheKey, { mtimeMs: stat.mtimeMs, size: stat.size, pkg });\n\t\treturn pkg;\n\t} catch {\n\t\t// Missing or unreadable manifest — never serve a stale entry.\n\t\tcache.delete(cacheKey);\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Single source of truth for entry → artifact-summary mapping. The\n * CLI bridge imports this for installed packages too, so the install\n * preview's installStrategy/risk can never drift from what the\n * catalog shows.\n */\nexport function toArtifactSummary(entry: ResolvedPluginEntry): CopilotArtifactSummary {\n\treturn {\n\t\tid: entry.artifactId,\n\t\tpackageId: entry.packageId,\n\t\tkind: entry.kind,\n\t\tdisplayName: entry.name,\n\t\tinstallStrategy: entry.requiresApproval\n\t\t\t? \"approval-gated\"\n\t\t\t: entry.kind === \"mcp\" || entry.kind === \"hook\"\n\t\t\t\t? \"generated-config\"\n\t\t\t\t: \"link\",\n\t\trisk: entry.requiresApproval ? \"review-required\" : \"none\",\n\t};\n}\n","import { createHash } from \"node:crypto\";\nimport * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport type { PlannedContentEntry } from \"./content-plan\";\nimport { findPluginMcpJson } from \"./integration-adapters\";\nimport {\n\ttype CopilotArtifactKind,\n\tgetWorkspaceManifestPluginSourceId,\n\tgetWorkspaceManifestSources,\n\ttype WorkspaceCopilotManifest,\n} from \"./types\";\n\n/** Extension namespace used by awesome-copilot plugin manifests. */\nconst AWESOME_COPILOT_NAMESPACE = \"com.github.awesome-copilot\";\n\n/** Kinds declared through relative path arrays in plugin.json. */\nconst PATH_KINDS: Partial<\n\tRecord<CopilotArtifactKind, \"agents\" | \"skills\" | \"hooks\" | \"instructions\" | \"prompts\">\n> = {\n\tagent: \"agents\",\n\tskill: \"skills\",\n\thook: \"hooks\",\n\tinstruction: \"instructions\",\n\tprompt: \"prompts\",\n};\n\n/** File extensions that mark executable content requiring local approval. */\nconst EXECUTABLE_EXTENSIONS = new Set([\".sh\", \".bash\", \".zsh\", \".ps1\", \".bat\", \".cmd\", \".exe\"]);\nconst EXECUTABLE_SUFFIXES = new Set([\".py\", \".rb\"]);\n/** Never descend into these dirs when locating legacy entries. */\nconst SKIP_DIRS = new Set([\".git\", \"node_modules\", \".vscode\", \"dist\", \"build\", \"out\", \"plugins\"]);\n\nexport interface ResolvedPluginManifest {\n\tname: string;\n\tdescription?: string;\n\tversion?: string;\n\textensions: Record<string, Record<string, unknown>>;\n}\n\nexport interface ResolvedPluginEntry extends PlannedContentEntry {\n\t/** Stable artifact identifier for consumers that do not need to parse identity. */\n\tartifactId: string;\n\t/** Stable package identifier for the logical repository/plugin pair. */\n\tpackageId: string;\n\t/** Display metadata for the logical package. */\n\tpackageDisplayName: string;\n\tpackageDescription?: string;\n\tpackageVersion?: string;\n}\n\n/** Resolve a workspace declaration into an immutable content plan. */\nexport async function resolveWorkspaceContentPlan(input: {\n\tmanifest: WorkspaceCopilotManifest;\n\treposDir: string;\n}): Promise<{ entries: ResolvedPluginEntry[] }> {\n\tconst entries: ResolvedPluginEntry[] = [];\n\tconst seenTargets = new Map<string, string>();\n\tconst sources = getWorkspaceManifestSources(input.manifest);\n\n\tfor (const plugin of input.manifest.plugins) {\n\t\tconst sourceId = getWorkspaceManifestPluginSourceId(input.manifest, plugin);\n\t\tconst source = sources.find((candidate) => candidate.id === sourceId);\n\t\tif (!source) {\n\t\t\tthrow new Error(`Plugin references unknown source ${sourceId}`);\n\t\t}\n\t\tconst repoRoot = path.resolve(input.reposDir, source.id);\n\t\tconst pluginManifest = await tryReadPluginManifest(repoRoot, plugin.id);\n\n\t\tconst pluginEntries: ResolvedPluginEntry[] = [];\n\t\tconst legacyArtifacts = plugin.artifactIds ? undefined : plugin.artifacts;\n\t\tif (pluginManifest) {\n\t\t\tpluginEntries.push(\n\t\t\t\t...(await resolvePluginManifestEntries(\n\t\t\t\t\trepoRoot,\n\t\t\t\t\tsource.id,\n\t\t\t\t\tplugin.id,\n\t\t\t\t\tlegacyArtifacts,\n\t\t\t\t\tpluginManifest\n\t\t\t\t))\n\t\t\t);\n\t\t} else {\n\t\t\tpluginEntries.push(\n\t\t\t\t...(await resolveLegacyEntry(repoRoot, source.id, plugin.id, legacyArtifacts))\n\t\t\t);\n\t\t}\n\t\tif (plugin.artifactIds) {\n\t\t\tconst availableIds = new Set(pluginEntries.map((entry) => entry.artifactId));\n\t\t\tconst unknownIds = plugin.artifactIds.filter((artifactId) => !availableIds.has(artifactId));\n\t\t\tif (unknownIds.length > 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Plugin ${plugin.id} has unknown selected artifact id(s): ${unknownIds.join(\", \")}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst selectedIds = new Set(plugin.artifactIds);\n\t\t\tpluginEntries.splice(\n\t\t\t\t0,\n\t\t\t\tpluginEntries.length,\n\t\t\t\t...pluginEntries.filter((entry) => selectedIds.has(entry.artifactId))\n\t\t\t);\n\t\t}\n\n\t\tif (pluginEntries.length === 0) {\n\t\t\tthrow new Error(`Plugin ${plugin.id} produced no materializable entries`);\n\t\t}\n\n\t\tfor (const entry of pluginEntries) {\n\t\t\t// Integration kinds (mcp/hook) materialize into shared generated\n\t\t\t// config files, not per-name .github targets; their identity is\n\t\t\t// already plugin-scoped, so only file-link kinds need the\n\t\t\t// cross-plugin target-uniqueness check.\n\t\t\tconst targetKey =\n\t\t\t\tentry.kind === \"mcp\" || entry.kind === \"hook\"\n\t\t\t\t\t? `${entry.kind}:${plugin.id}:${entry.name}`\n\t\t\t\t\t: `${entry.kind}:${entry.name}`;\n\t\t\tconst owner = seenTargets.get(targetKey);\n\t\t\tif (owner !== undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Duplicate ${entry.kind} target ${entry.name} from plugins ${owner} and ${plugin.id}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tseenTargets.set(targetKey, plugin.id);\n\t\t\tentries.push(entry);\n\t\t}\n\t}\n\n\treturn { entries };\n}\n\n/** Manifest shape with only the fields the lenient resolver reads. */\ninterface RawPluginManifest {\n\tname?: unknown;\n\tdescription?: unknown;\n\tversion?: unknown;\n\textensions?: Record<string, Record<string, unknown>>;\n}\n\n/**\n * Lenient variant for CATALOG BROWSING: resolve a single plugin's\n * declared artifacts, skipping paths that are missing or\n * unverifiable instead of throwing. Upstream plugin.json files in\n * the wild carry typos (e.g. declaring `./agents/x.md` for a file\n * that ships as `x.agent.md`); a strict throw hides the whole\n * package from browse — while installs still go through the strict\n * pipeline, so a listing here never guarantees a broken install.\n *\n * Only skills/agents/prompts/instructions path arrays are probed\n * here; mcp.json/hooks stay install-pipeline-only (they materialize\n * shared config and MUST pass the strict gate).\n */\nexport async function resolvePluginEntriesLenient(input: {\n\trepoRoot: string;\n\trepositoryId: string;\n\tpluginId: string;\n\tmanifest: ResolvedPluginManifest;\n}): Promise<ResolvedPluginEntry[]> {\n\tconst namespace = input.manifest.extensions[AWESOME_COPILOT_NAMESPACE];\n\tif (!namespace || typeof namespace !== \"object\") {\n\t\t// No composition namespace — derive artifacts from the VS Code\n\t\t// local-plugin directory conventions instead.\n\t\treturn resolveConventionEntries(\n\t\t\tinput.repoRoot,\n\t\t\tpath.join(input.repoRoot, \"plugins\", input.pluginId),\n\t\t\tinput.repositoryId,\n\t\t\tinput.pluginId,\n\t\t\tinput.manifest\n\t\t);\n\t}\n\tconst entries: ResolvedPluginEntry[] = [];\n\tfor (const [kind, manifestKey] of Object.entries(PATH_KINDS)) {\n\t\tif (kind === \"mcp\" || kind === \"hook\") continue;\n\t\tconst declared = namespace[manifestKey];\n\t\tif (!Array.isArray(declared)) continue;\n\t\tfor (const relative of declared) {\n\t\t\tif (typeof relative !== \"string\") continue;\n\t\t\tconst effective = await resolveDeclaredRelative(input.repoRoot, relative);\n\t\t\tconst sourcePath = path.resolve(input.repoRoot, effective);\n\t\t\tif (!sourcePath.startsWith(`${input.repoRoot}${path.sep}`)) continue;\n\t\t\ttry {\n\t\t\t\tentries.push(\n\t\t\t\t\t...(await materializePathEntry(\n\t\t\t\t\t\tinput.repoRoot,\n\t\t\t\t\t\tinput.repositoryId,\n\t\t\t\t\t\tinput.pluginId,\n\t\t\t\t\t\tkind as CopilotArtifactKind,\n\t\t\t\t\t\teffective,\n\t\t\t\t\t\tinput.manifest\n\t\t\t\t\t))\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\t// skip this artifact (missing file / wrong shape)\n\t\t\t}\n\t\t}\n\t}\n\treturn entries;\n}\n\nasync function tryReadPluginManifest(\n\trepoRoot: string,\n\tpluginId: string\n): Promise<ResolvedPluginManifest | null> {\n\t// Two manifest conventions: marketplace plugins carry plugin.json at\n\t// the plugin root; VS Code-local plugins (created by \"创建插件\") carry\n\t// it under .plugin/.\n\tfor (const manifestPath of [\n\t\tpath.join(repoRoot, \"plugins\", pluginId, \"plugin.json\"),\n\t\tpath.join(repoRoot, \"plugins\", pluginId, \".plugin\", \"plugin.json\"),\n\t]) {\n\t\ttry {\n\t\t\tconst raw = await fsp.readFile(manifestPath, \"utf8\");\n\t\t\tconst parsed = JSON.parse(raw) as ResolvedPluginManifest;\n\t\t\tif (typeof parsed?.name !== \"string\" || parsed.name.trim().length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// .plugin/ manifests usually omit extensions entirely —\n\t\t\t// artifacts then come from directory conventions.\n\t\t\treturn { ...parsed, extensions: parsed.extensions ?? {} };\n\t\t} catch {\n\t\t\t// try the next convention\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction selectedKind(\n\tartifacts: Partial<Record<CopilotArtifactKind, boolean>> | undefined,\n\tkind: CopilotArtifactKind\n): boolean {\n\treturn artifacts?.[kind] !== false;\n}\n\n/**\n * VS Code local-plugin directory conventions (\"创建插件\" layout):\n * skills/<name>/SKILL.md · agents/<name>.agent.md ·\n * rules/<name>.instructions.md · commands/<name>.prompt.md ·\n * hooks/hooks.json\n * Checked plugin-dir-relative first, then repo-root. Used when a\n * manifest carries no `com.github.awesome-copilot` namespace at all.\n */\nasync function resolveConventionEntries(\n\trepoRoot: string,\n\tpluginDir: string,\n\trepositoryId: string,\n\tpluginId: string,\n\tmanifest?: ResolvedPluginManifest\n): Promise<ResolvedPluginEntry[]> {\n\tconst entries: ResolvedPluginEntry[] = [];\n\tconst seen = new Set<string>();\n\tconst push = async (\n\t\tkind: CopilotArtifactKind,\n\t\tbase: string,\n\t\tsubdir: string,\n\t\toptions: { dirs?: boolean; files?: boolean } = {}\n\t): Promise<void> => {\n\t\tconst dir = path.join(base, subdir);\n\t\tconst dirents = await fsp.readdir(dir, { withFileTypes: true }).catch(() => []);\n\t\tfor (const dirent of dirents) {\n\t\t\tlet sourcePath: string;\n\t\t\tif (options.dirs && dirent.isDirectory()) {\n\t\t\t\tif (!(await pathExists(path.join(dir, dirent.name, \"SKILL.md\")))) continue;\n\t\t\t\tsourcePath = path.join(dir, dirent.name);\n\t\t\t} else if (options.files !== false && dirent.isFile()) {\n\t\t\t\tsourcePath = path.join(dir, dirent.name);\n\t\t\t} else {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (seen.has(sourcePath)) continue;\n\t\t\tseen.add(sourcePath);\n\t\t\tentries.push(\n\t\t\t\tawait buildEntry(repositoryId, pluginId, kind, sourcePath, !options.dirs, manifest)\n\t\t\t);\n\t\t}\n\t};\n\tfor (const base of [pluginDir, repoRoot]) {\n\t\tawait push(\"skill\", base, \"skills\", { dirs: true, files: false });\n\t\tawait push(\"agent\", base, \"agents\");\n\t\tawait push(\"instruction\", base, \"rules\");\n\t\tawait push(\"prompt\", base, \"commands\");\n\t\tawait push(\"hook\", base, \"hooks\");\n\t}\n\treturn entries;\n}\n\n/**\n * Repo-root-relative source for a declared plugin path, applying the\n * awesome-copilot shipping convention: declared `./agents/x.md` ships\n * as `agents/x.agent.md` (the suffix is added at the source, upstream's\n * materializer swaps it back at publish time). Falls through unchanged\n * when the declared path exists as-is.\n */\nasync function resolveDeclaredRelative(repoRoot: string, relative: string): Promise<string> {\n\tif (await pathExists(path.resolve(repoRoot, relative))) return relative;\n\tconst normalized = relative.replace(/^\\.\\//, \"\");\n\tif (normalized.startsWith(\"agents/\") && normalized.endsWith(\".md\")) {\n\t\tconst base = path.basename(normalized, \".md\");\n\t\tconst candidate = path.join(path.dirname(normalized), `${base}.agent.md`);\n\t\tif (await pathExists(path.resolve(repoRoot, candidate))) {\n\t\t\treturn `./${candidate}`;\n\t\t}\n\t}\n\treturn relative;\n}\n\nasync function resolvePluginManifestEntries(\n\trepoRoot: string,\n\trepositoryId: string,\n\tpluginId: string,\n\tartifacts: Partial<Record<CopilotArtifactKind, boolean>> | undefined,\n\tpluginManifest: ResolvedPluginManifest\n): Promise<ResolvedPluginEntry[]> {\n\tconst namespace = pluginManifest.extensions[AWESOME_COPILOT_NAMESPACE];\n\tif (!namespace || typeof namespace !== \"object\") {\n\t\t// No composition namespace — a VS Code-local plugin (\"创建插件\"\n\t\t// layout). Derive artifacts from the directory conventions.\n\t\treturn resolveConventionEntries(\n\t\t\trepoRoot,\n\t\t\tpath.join(repoRoot, \"plugins\", pluginId),\n\t\t\trepositoryId,\n\t\t\tpluginId,\n\t\t\tpluginManifest\n\t\t);\n\t}\n\n\tconst entries: ResolvedPluginEntry[] = [];\n\tfor (const [kind, manifestKey] of Object.entries(PATH_KINDS)) {\n\t\tif (!selectedKind(artifacts, kind as CopilotArtifactKind)) continue;\n\t\tconst declared = namespace[manifestKey];\n\t\tif (!Array.isArray(declared)) continue;\n\n\t\tfor (const relative of declared) {\n\t\t\tif (typeof relative !== \"string\") {\n\t\t\t\tthrow new Error(`Plugin ${pluginId} declares a non-string ${manifestKey} path`);\n\t\t\t}\n\t\t\tentries.push(\n\t\t\t\t...(await materializePathEntry(\n\t\t\t\t\trepoRoot,\n\t\t\t\t\trepositoryId,\n\t\t\t\t\tpluginId,\n\t\t\t\t\tkind as CopilotArtifactKind,\n\t\t\t\t\tawait resolveDeclaredRelative(repoRoot, relative),\n\t\t\t\t\tpluginManifest\n\t\t\t\t))\n\t\t\t);\n\t\t}\n\t}\n\tif (selectedKind(artifacts, \"mcp\")) {\n\t\tconst mcpJsonPath = await findPluginMcpJson(repoRoot, pluginId);\n\t\tif (mcpJsonPath) {\n\t\t\tentries.push(\n\t\t\t\tawait buildEntry(repositoryId, pluginId, \"mcp\", mcpJsonPath, true, pluginManifest)\n\t\t\t);\n\t\t}\n\t}\n\tif (selectedKind(artifacts, \"hook\")) {\n\t\tconst pluginHooksDir = path.join(repoRoot, \"plugins\", pluginId, \"hooks\");\n\t\tif (await pathExists(pluginHooksDir)) {\n\t\t\tentries.push(\n\t\t\t\tawait buildEntry(repositoryId, pluginId, \"hook\", pluginHooksDir, false, pluginManifest)\n\t\t\t);\n\t\t} else {\n\t\t\tconst repoHooksDir = path.join(repoRoot, \"hooks\", pluginId);\n\t\t\tif (await pathExists(repoHooksDir)) {\n\t\t\t\tentries.push(\n\t\t\t\t\tawait buildEntry(repositoryId, pluginId, \"hook\", repoHooksDir, false, pluginManifest)\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\treturn entries;\n}\n\nasync function resolveLegacyEntry(\n\trepoRoot: string,\n\trepositoryId: string,\n\tpluginId: string,\n\tartifacts: Partial<Record<CopilotArtifactKind, boolean>> | undefined\n): Promise<ResolvedPluginEntry[]> {\n\tconst candidates: ResolvedPluginEntry[] = [];\n\tif (selectedKind(artifacts, \"skill\")) {\n\t\tconst skillDir = await findEntryDirectory(repoRoot, pluginId, \"SKILL.md\");\n\t\tif (skillDir) {\n\t\t\tcandidates.push(await buildEntry(repositoryId, pluginId, \"skill\", skillDir, false));\n\t\t}\n\t}\n\tif (selectedKind(artifacts, \"agent\")) {\n\t\tconst agentFile = await findFlatAgentFile(repoRoot, pluginId);\n\t\tif (agentFile) {\n\t\t\tcandidates.push(await buildEntry(repositoryId, pluginId, \"agent\", agentFile, true));\n\t\t}\n\t}\n\tif (selectedKind(artifacts, \"instruction\")) {\n\t\tconst instructionFile = await findFlatFile(repoRoot, `${pluginId}.instructions.md`);\n\t\tif (instructionFile) {\n\t\t\tcandidates.push(\n\t\t\t\tawait buildEntry(repositoryId, pluginId, \"instruction\", instructionFile, true)\n\t\t\t);\n\t\t}\n\t}\n\tif (selectedKind(artifacts, \"prompt\")) {\n\t\tconst promptFile = await findFlatFile(repoRoot, `${pluginId}.prompt.md`);\n\t\tif (promptFile) {\n\t\t\tcandidates.push(await buildEntry(repositoryId, pluginId, \"prompt\", promptFile, true));\n\t\t}\n\t}\n\tif (selectedKind(artifacts, \"hook\")) {\n\t\tconst repoHooksDir = path.join(repoRoot, \"hooks\", pluginId);\n\t\tif (await pathExists(repoHooksDir)) {\n\t\t\tcandidates.push(await buildEntry(repositoryId, pluginId, \"hook\", repoHooksDir, false));\n\t\t}\n\t}\n\treturn candidates;\n}\n\n/**\n * Locate the directory of a directory-based entry by its basename.\n * Real repos nest (`skills/<scope>/<name>/`, or no `skills/` prefix at\n * all — composiohq style), so walk the whole repo root instead of\n * assuming one hardcoded depth. Skip other repos' metadata dirs.\n */\nasync function findEntryDirectory(\n\trepoRoot: string,\n\tpluginId: string,\n\tmanifestFilename: string\n): Promise<string | null> {\n\tconst direct = path.join(repoRoot, \"skills\", pluginId);\n\tif (await pathExists(path.join(direct, manifestFilename))) return direct;\n\tconst stack = [repoRoot];\n\twhile (stack.length > 0) {\n\t\tconst current = stack.pop() as string;\n\t\tconst dirents = await fsp.readdir(current, { withFileTypes: true }).catch(() => []);\n\t\tfor (const dirent of dirents) {\n\t\t\tif (!dirent.isDirectory() || SKIP_DIRS.has(dirent.name)) continue;\n\t\t\tconst childDir = path.join(current, dirent.name);\n\t\t\tif (dirent.name === pluginId && (await pathExists(path.join(childDir, manifestFilename)))) {\n\t\t\t\treturn childDir;\n\t\t\t}\n\t\t\tstack.push(childDir);\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Locate a flat single-file agent manifest (`<name>.agent.md`) anywhere\n * in the repo — ms-skills / awesome-copilot style, often nested under\n * `agents/<scope>/`.\n */\nasync function findFlatAgentFile(repoRoot: string, pluginId: string): Promise<string | null> {\n\treturn findFlatFile(repoRoot, `${pluginId}.agent.md`, \"agents\");\n}\n\n/**\n * Locate a flat single-file manifest anywhere in the repo, preferring\n * the canonical top-level directory for its kind.\n */\nasync function findFlatFile(\n\trepoRoot: string,\n\tfilename: string,\n\tpreferredDir?: string\n): Promise<string | null> {\n\tconst direct = preferredDir ? path.join(repoRoot, preferredDir, filename) : null;\n\tif (direct && (await pathExists(direct))) return direct;\n\tconst stack = [repoRoot];\n\twhile (stack.length > 0) {\n\t\tconst current = stack.pop() as string;\n\t\tconst dirents = await fsp.readdir(current, { withFileTypes: true }).catch(() => []);\n\t\tfor (const dirent of dirents) {\n\t\t\tif (dirent.isFile() && dirent.name === filename) {\n\t\t\t\treturn path.join(current, dirent.name);\n\t\t\t}\n\t\t\tif (dirent.isDirectory() && !SKIP_DIRS.has(dirent.name)) {\n\t\t\t\tstack.push(path.join(current, dirent.name));\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\nasync function materializePathEntry(\n\trepoRoot: string,\n\trepositoryId: string,\n\tpluginId: string,\n\tkind: CopilotArtifactKind,\n\trelative: string,\n\tpluginManifest?: ResolvedPluginManifest\n): Promise<ResolvedPluginEntry[]> {\n\tconst sourcePath = path.resolve(repoRoot, relative);\n\tif (sourcePath !== repoRoot && !sourcePath.startsWith(`${repoRoot}${path.sep}`)) {\n\t\tthrow new Error(`Plugin ${pluginId} path escapes repository root: ${relative}`);\n\t}\n\n\tconst stat = await fsp.stat(sourcePath).catch(() => null);\n\tif (!stat) {\n\t\tthrow new Error(`Plugin ${pluginId} source missing: ${relative}`);\n\t}\n\tif (kind === \"skill\") {\n\t\tif (!stat.isDirectory() || !(await pathExists(path.join(sourcePath, \"SKILL.md\")))) {\n\t\t\tthrow new Error(`Plugin ${pluginId} skill source has no SKILL.md: ${relative}`);\n\t\t}\n\t\treturn [await buildEntry(repositoryId, pluginId, kind, sourcePath, false, pluginManifest)];\n\t}\n\tif (!stat.isFile()) {\n\t\tthrow new Error(`Plugin ${pluginId} ${kind} source is not a file: ${relative}`);\n\t}\n\treturn [await buildEntry(repositoryId, pluginId, kind, sourcePath, true, pluginManifest)];\n}\n\nasync function buildEntry(\n\trepositoryId: string,\n\tpluginId: string,\n\tkind: CopilotArtifactKind,\n\tsourcePath: string,\n\tsourceIsFile: boolean,\n\tpluginManifest?: ResolvedPluginManifest\n): Promise<ResolvedPluginEntry> {\n\tconst name = sourceIsFile\n\t\t? stripAgentSuffix(path.basename(sourcePath))\n\t\t: path.basename(sourcePath);\n\tconst displayName =\n\t\tkind === \"mcp\" && name.endsWith(\".json\") ? name.slice(0, -\".json\".length) : name;\n\tconst digest = sourceIsFile ? await digestFile(sourcePath) : await digestDirectory(sourcePath);\n\tconst requiresApproval =\n\t\tkind === \"hook\" ||\n\t\tkind === \"mcp\" ||\n\t\t(sourceIsFile ? isExecutableFile(sourcePath) : await directoryContainsExecutable(sourcePath));\n\n\tconst identity = `${repositoryId}::${pluginId}::${kind}:${displayName}`;\n\treturn {\n\t\tidentity,\n\t\tartifactId: identity,\n\t\tpackageId: `${repositoryId}::${pluginId}`,\n\t\tpackageDisplayName: pluginManifest?.name ?? pluginId,\n\t\t...(pluginManifest?.description ? { packageDescription: pluginManifest.description } : {}),\n\t\t...(pluginManifest?.version ? { packageVersion: pluginManifest.version } : {}),\n\t\trepositoryId,\n\t\tpluginId,\n\t\tkind,\n\t\tsourcePath,\n\t\tsourceIsFile,\n\t\tname: displayName,\n\t\tdigest,\n\t\trequiresApproval,\n\t};\n}\n\nfunction stripAgentSuffix(basename: string): string {\n\tconst suffixes = [\".agent.md\", \".instructions.md\", \".prompt.md\"];\n\tfor (const suffix of suffixes) {\n\t\tif (basename.endsWith(suffix)) return basename.slice(0, -suffix.length);\n\t}\n\treturn basename;\n}\n\nasync function pathExists(target: string): Promise<boolean> {\n\ttry {\n\t\tawait fsp.access(target);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function digestFile(file: string): Promise<string> {\n\tconst content = await fsp.readFile(file);\n\treturn createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\nasync function digestDirectory(dir: string): Promise<string> {\n\tconst hash = createHash(\"sha256\");\n\tawait digestInto(dir, hash);\n\treturn hash.digest(\"hex\");\n}\n\nasync function digestInto(dir: string, hash: import(\"node:crypto\").Hash): Promise<void> {\n\tconst dirents = await fsp.readdir(dir, { withFileTypes: true });\n\tdirents.sort((a, b) => a.name.localeCompare(b.name));\n\tfor (const dirent of dirents) {\n\t\tconst child = path.join(dir, dirent.name);\n\t\thash.update(dirent.name);\n\t\tif (dirent.isFile()) {\n\t\t\thash.update(await fsp.readFile(child));\n\t\t} else if (dirent.isDirectory()) {\n\t\t\tawait digestInto(child, hash);\n\t\t}\n\t}\n}\n\nfunction isExecutableFile(file: string): boolean {\n\tconst ext = path.extname(file).toLowerCase();\n\tif (EXECUTABLE_EXTENSIONS.has(ext)) return true;\n\treturn EXECUTABLE_SUFFIXES.has(ext);\n}\n\nasync function directoryContainsExecutable(dir: string): Promise<boolean> {\n\tconst dirents = await fsp.readdir(dir, { withFileTypes: true });\n\tfor (const dirent of dirents) {\n\t\tconst child = path.join(dir, dirent.name);\n\t\tif (dirent.isFile() && isExecutableFile(child)) return true;\n\t\tif (dirent.isDirectory() && (await directoryContainsExecutable(child))) return true;\n\t}\n\treturn false;\n}\n","/**\n * Contracts for the declarative Copilot content lifecycle.\n *\n * Three layers (see docs/architecture/copilot-content-reconciliation.md):\n * 1. content sources under ~/.serviceme/repos/<repo-id>\n * 2. a shared, git-tracked workspace declaration\n * 3. machine-local materialization state under ~/.serviceme/workspaces\n */\n\n/** Artifact kinds a logical plugin can contribute to Copilot. */\nexport type CopilotArtifactKind = \"agent\" | \"skill\" | \"instruction\" | \"prompt\" | \"hook\" | \"mcp\";\n\n/** A repository pinned to an exact commit for reproducible restoration. */\nexport interface WorkspaceManifestRepository {\n\t/** Stable id used in on-disk paths and plugin references. */\n\tid: string;\n\t/** Authoritative cross-machine source identity (HTTPS or git@ URL). */\n\turl: string;\n\t/** Full 40-character commit SHA; branch names are not allowed. */\n\tcommit: string;\n}\n\n/** A Git source in the v2 manifest syntax. */\nexport interface WorkspaceManifestGitSource extends WorkspaceManifestRepository {\n\ttype: \"git\";\n}\n\n/** An immutable marketplace artifact materialized under ~/.serviceme/repos/<id>. */\nexport interface WorkspaceManifestCatalogSource {\n\ttype: \"catalog\";\n\t/** Stable local repository id used for the managed source directory. */\n\tid: string;\n\t/** Marketplace identity, independent of any transient download URL. */\n\tcatalogId: string;\n\t/** Immutable catalog release selected by the workspace. */\n\trevision: string;\n\t/** SHA-256 of the complete catalog payload, prefixed with sha256:. */\n\tdigest: string;\n}\n\n/** A source that can be restored on each collaborator's machine. */\nexport type WorkspaceManifestSource = WorkspaceManifestGitSource | WorkspaceManifestCatalogSource;\n\n/** A logical plugin selection inside the workspace declaration. */\nexport interface WorkspaceManifestPlugin {\n\t/** Referenced repository id. */\n\trepository: string;\n\t/** Logical plugin id from plugin.json, or the legacy skill/agent name. */\n\tid: string;\n\t/** Legacy artifact-kind selection, retained for compatible workspace declarations. */\n\tartifacts: Partial<Record<CopilotArtifactKind, boolean>>;\n\t/** Exact resolver artifact identities selected for this package. */\n\tartifactIds?: string[];\n}\n\n/** A v2 logical plugin selection references any supported source type. */\nexport interface WorkspaceManifestPluginV2 {\n\t/** Referenced source id. */\n\tsource: string;\n\t/** Logical plugin id from plugin.json, or the legacy skill/agent name. */\n\tid: string;\n\t/** Legacy artifact-kind selection, retained for compatible workspace declarations. */\n\tartifacts: Partial<Record<CopilotArtifactKind, boolean>>;\n\t/** Exact resolver artifact identities selected for this package. */\n\tartifactIds?: string[];\n}\n\n/** Root shape of .github/serviceme-plugins.json. */\nexport interface WorkspaceCopilotManifestV1 {\n\tversion: 1;\n\trepositories: WorkspaceManifestRepository[];\n\tplugins: WorkspaceManifestPlugin[];\n}\n\n/** Root v2 shape: sources may be Git checkouts or immutable catalog artifacts. */\nexport interface WorkspaceCopilotManifestV2 {\n\tversion: 2;\n\tsources: WorkspaceManifestSource[];\n\tplugins: WorkspaceManifestPluginV2[];\n}\n\n/** Root shape of .github/serviceme-plugins.json. */\nexport type WorkspaceCopilotManifest = WorkspaceCopilotManifestV1 | WorkspaceCopilotManifestV2;\n\n/** Normalize the versioned source list for consumers that do not care about syntax. */\nexport function getWorkspaceManifestSources(\n\tmanifest: WorkspaceCopilotManifest\n): WorkspaceManifestSource[] {\n\treturn manifest.version === 1\n\t\t? manifest.repositories.map((repository) => ({ ...repository, type: \"git\" as const }))\n\t\t: manifest.sources;\n}\n\n/** Resolve a versioned plugin selection to its source id. */\nexport function getWorkspaceManifestPluginSourceId(\n\tmanifest: WorkspaceCopilotManifest,\n\tplugin: WorkspaceManifestPlugin | WorkspaceManifestPluginV2\n): string {\n\treturn manifest.version === 1\n\t\t? (plugin as WorkspaceManifestPlugin).repository\n\t\t: (plugin as WorkspaceManifestPluginV2).source;\n}\n\n/** Materialization mode used when activating an entry on the current machine. */\nexport type CopilotLinkMode = \"symlink\" | \"junction\" | \"generated\";\n\n/** One materialized entry recorded in machine-local state. */\nexport interface WorkspaceMaterializedEntry {\n\t/** Stable identity: repository::plugin::kind[:name]. */\n\tidentity: string;\n\t/** Owning repository id. */\n\trepositoryId: string;\n\t/** Owning logical plugin id. */\n\tpluginId: string;\n\t/** Artifact kind of this entry. */\n\tkind: CopilotArtifactKind;\n\t/** Absolute source path under ~/.serviceme/repos. */\n\tsourcePath: string;\n\t/** Workspace-relative link path. */\n\tlinkPath: string;\n\t/** Link mode used on this machine. */\n\tlinkMode: CopilotLinkMode;\n\t/** SHA-256 digest of the pinned source content. */\n\tdigest: string;\n\t/** Whether the current machine approved risky content. */\n\tapproved: boolean;\n}\n\n/** Machine-local materialization state; never shared through git. */\nexport interface WorkspaceContentState {\n\tversion: 1;\n\tentries: WorkspaceMaterializedEntry[];\n}\n","import * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport { getServicemeHome } from \"../paths/userHome\";\nimport type { CopilotPackageInstallation, CopilotScope } from \"./customization-model\";\nimport type { PackageInstallationService } from \"./package-installation-service\";\nimport type { PersonalInstallationStore } from \"./personal-installation-store\";\nimport type { ResolvedPluginEntry } from \"./plugin-resolver\";\n\n/**\n * Verbatim wire contract for a read-only package update preview.\n * Field names are pinned by Task 10's brief and shared with the\n * protocol / webview layers.\n */\nexport interface CopilotPackageUpdatePreview {\n\tpackageId: string;\n\tfromVersion?: string;\n\ttoVersion: string;\n\taddedArtifactIds: string[];\n\tremovedArtifactIds: string[];\n\tchangedArtifactIds: string[];\n\tapprovalInvalidatedArtifactIds: string[];\n}\n\n/** Normalized source record for diagnostics and the source manager UI. */\nexport interface CopilotSourceRecord {\n\tid: string;\n\ttype: \"marketplace\" | \"git\" | \"local\";\n\tdisplayName: string;\n\tupdateCapability: \"pinned\" | \"live\" | \"none\";\n\t/** Whether the source content is currently materialized on disk. */\n\tavailable: boolean;\n\t/** Where this source is declared: \"workspace\" (manifest), \"personal\" (intent), or both. */\n\tdeclaredIn: Array<\"workspace\" | \"personal\">;\n}\n\nexport interface CopilotSourceCatalogDeps {\n\t/** SERVICEME home; defaults to the real ~/.serviceme. */\n\thomeDir?: string;\n\t/** Personal installation intent; consulted by previewUpdate and removeSource. */\n\tpersonalStore: PersonalInstallationStore;\n\t/**\n\t * Task 9's transaction service owns installations. removeSource\n\t * coordinates with it only indirectly: it refuses while any\n\t * installation (workspace or personal) still uses the source.\n\t */\n\tinstallationService?: PackageInstallationService;\n\t/**\n\t * Resolves artifacts for a package at a revision; defaults to a\n\t * no-content resolver so an unresolvable source yields empty diff\n\t * lists rather than a crash.\n\t */\n\tresolvePackage?: (packageId: string, revision?: string) => Promise<ResolvedPluginEntry[]>;\n\t/**\n\t * Round 3: workspace declarations enable artifact kinds, not named\n\t * artifacts. This predicate reports whether a resolved entry matches a\n\t * selection marker produced for an enabled kind. Implementations may\n\t * match exact ids or kind prefixes; default behavior matches only\n\t * exact ids (personal-intent semantics).\n\t */\n\tartifactSelected?: (marker: string, artifactId: string) => boolean;\n\t/**\n\t * Finding 4 honesty: returns the revision actually resolved by\n\t * resolvePackage for a given requested revision. When a resolver can\n\t * only read the working tree, this lets previewUpdate label toVersion\n\t * with what was truly diffed instead of the requested target.\n\t */\n\tresolveActualRevision?: (packageId: string, revision: string) => Promise<string | undefined>;\n\t/** Source ids declared by the workspace manifest for a directory. */\n\tlistWorkspaceSources: (workspaceDir: string) => Promise<string[]>;\n\t/**\n\t * Workspace-scope installations for a directory, read through Task 9's\n\t * view projection. Both previewUpdate and removeSource honor personal\n\t * AND workspace installations; without this a workspace-declared source\n\t * with no personal intent could be deleted while still in use.\n\t */\n\treadWorkspaceInstallations?: (workspaceDir: string) => Promise<CopilotPackageInstallation[]>;\n\t/** Classifies a source id as a marketplace catalog source. */\n\tisCatalogSource?: (sourceId: string, workspaceDir?: string) => boolean | Promise<boolean>;\n\t/**\n\t * Task 10 ruling #1: materialize a v2 catalog source under\n\t * repos/<sourceId> so availability is a plain directory check.\n\t * listSources invokes this for unavailable catalog sources before\n\t * reporting diagnostics.\n\t */\n\tensureCatalogSource?: (sourceId: string) => Promise<{ localPath: string }>;\n\t/**\n\t * Round 2 / Minor 2: reports whether a staged catalog directory\n\t * actually contains payload. Staging only creates the directory; a\n\t * payload check must gate availability so an empty staged dir is\n\t * never reported as available.\n\t */\n\thasCatalogPayload?: (localPath: string) => Promise<boolean>;\n\t/** Classifies a source id as a personal local source. */\n\tisLocalSource?: (sourceId: string) => boolean | Promise<boolean>;\n\t/**\n\t * ReposStore (repos.json) entries on this machine — built-in default\n\t * repos plus user-added git repos. The source manager is the single\n\t * repository management surface, so every store entry must appear in\n\t * the listing even when it is neither workspace-declared nor\n\t * personally installed. Entries with an empty declaredIn are\n\t * store-only. Also supplies human display names for store sources.\n\t */\n\tlistStoreSources?: () => Promise<Array<{ id: string; name?: string; enabled?: boolean }>>;\n}\n\nexport interface CopilotSourceCatalogQuery {\n\tworkspaceDir?: string;\n\tscope?: CopilotScope;\n}\n\nexport interface CopilotUpdatePreviewInput {\n\tworkspaceDir?: string;\n\tscope?: CopilotScope;\n\tpackageId: string;\n\t/** Target revision (full 40-char commit for git sources, immutable catalog revision otherwise). */\n\trevision: string;\n}\n\nconst CATALOG_SOURCE_PATTERN = /^catalog-/;\n\n/**\n * Normalized Marketplace / Git / local source discovery plus\n * deterministic, side-effect-free update previews.\n *\n * Marketplace sources are identified by an immutable catalog revision\n * plus digest (see WorkspaceManifestCatalogSource); Git sources by a\n * validated remote identity plus full commit; personal local sources\n * may run \"live\" while workspace local sources require a Git import\n * or the \"private-workspace-override\" mode enforced downstream by the\n * install transaction.\n */\nexport class CopilotSourceCatalogService {\n\tprivate readonly deps: CopilotSourceCatalogDeps;\n\tprivate readonly reposDir: string;\n\n\tconstructor(deps: CopilotSourceCatalogDeps) {\n\t\tthis.deps = deps;\n\t\tthis.reposDir = path.join(deps.homeDir ?? getServicemeHome(), \"repos\");\n\t}\n\n\t/**\n\t * List normalized sources for the query: every source declared by\n\t * the workspace manifest (when a workspaceDir is given) plus every\n\t * source referenced by personal installation intent, plus every\n\t * ReposStore entry on this machine (built-in default repos and\n\t * user-added git repos) so the source manager can manage them.\n\t */\n\tasync listSources(query: CopilotSourceCatalogQuery): Promise<CopilotSourceRecord[]> {\n\t\tconst declaredIn = new Map<string, Set<\"workspace\" | \"personal\">>();\n\t\tconst note = (sourceId: string, scope: \"workspace\" | \"personal\"): void => {\n\t\t\tconst set = declaredIn.get(sourceId) ?? new Set<\"workspace\" | \"personal\">();\n\t\t\tset.add(scope);\n\t\t\tdeclaredIn.set(sourceId, set);\n\t\t};\n\t\tif (query.workspaceDir) {\n\t\t\tfor (const sourceId of await this.deps.listWorkspaceSources(query.workspaceDir)) {\n\t\t\t\tnote(sourceId, \"workspace\");\n\t\t\t}\n\t\t}\n\t\tconst state = await this.deps.personalStore.read();\n\t\tfor (const installation of state.installations) {\n\t\t\tconst [sourceId] = installation.packageId.split(\"::\");\n\t\t\tif (sourceId) note(sourceId, \"personal\");\n\t\t}\n\t\t// A failing store read must not take the declared listing down\n\t\t// with it — store entries are an enhancement, not a requirement.\n\t\tconst storeSources = (await this.deps.listStoreSources?.().catch(() => [])) ?? [];\n\t\tconst storeNames = new Map(storeSources.map((entry) => [entry.id, entry.name]));\n\t\tfor (const entry of storeSources) {\n\t\t\t// Store entries seed the map with an empty scope set so they\n\t\t\t// render even when nothing declares or installs them.\n\t\t\tif (!declaredIn.has(entry.id)) {\n\t\t\t\tdeclaredIn.set(entry.id, new Set<\"workspace\" | \"personal\">());\n\t\t\t}\n\t\t}\n\n\t\tconst records: CopilotSourceRecord[] = [];\n\t\tfor (const [sourceId, scopes] of declaredIn) {\n\t\t\tconst isCatalog = await (this.deps.isCatalogSource?.(sourceId, query.workspaceDir) ?? false);\n\t\t\tconst isLocal = await (this.deps.isLocalSource?.(sourceId) ?? false);\n\t\t\tconst type: CopilotSourceRecord[\"type\"] = isCatalog\n\t\t\t\t? \"marketplace\"\n\t\t\t\t: isLocal\n\t\t\t\t\t? \"local\"\n\t\t\t\t\t: CATALOG_SOURCE_PATTERN.test(sourceId)\n\t\t\t\t\t\t? \"marketplace\"\n\t\t\t\t\t\t: \"git\";\n\t\t\tif (\n\t\t\t\ttype === \"marketplace\" &&\n\t\t\t\tthis.deps.ensureCatalogSource &&\n\t\t\t\t!(await this.isSourceAvailable(sourceId))\n\t\t\t) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.deps.ensureCatalogSource(sourceId);\n\t\t\t\t} catch {\n\t\t\t\t\t// Availability diagnostics must not fail the listing; the\n\t\t\t\t\t// record simply reports not-materialized.\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst available =\n\t\t\t\ttype === \"marketplace\" && this.deps.hasCatalogPayload\n\t\t\t\t\t? await this.deps.hasCatalogPayload(path.join(this.reposDir, sourceId)).catch(() => false)\n\t\t\t\t\t: await this.isSourceAvailable(sourceId);\n\t\t\trecords.push({\n\t\t\t\tid: sourceId,\n\t\t\t\ttype,\n\t\t\t\tdisplayName: storeNames.get(sourceId) ?? sourceId,\n\t\t\t\tupdateCapability: type === \"local\" ? \"live\" : \"pinned\",\n\t\t\t\tavailable,\n\t\t\t\tdeclaredIn: [...scopes],\n\t\t\t});\n\t\t}\n\t\treturn records;\n\t}\n\n\t/**\n\t * Confirms Task 4's materialization assumption for every source\n\t * kind: v2 catalog sources materialize under reposDir/<source-id>\n\t * exactly like git checkouts, so availability is a directory check.\n\t */\n\tasync isSourceAvailable(sourceId: string): Promise<boolean> {\n\t\tconst stat = await fsp.stat(path.join(this.reposDir, sourceId)).catch(() => null);\n\t\treturn stat?.isDirectory() === true;\n\t}\n\n\t/**\n\t * Deterministic, side-effect-free update preview: compares the\n\t * currently-selected artifacts against what the target revision\n\t * resolves to, and reports which approval-gated artifacts would\n\t * need re-approval. Never writes intent; the pinned version stays\n\t * untouched until the caller runs the update transaction.\n\t */\n\tasync previewUpdate(input: CopilotUpdatePreviewInput): Promise<CopilotPackageUpdatePreview> {\n\t\tconst state = await this.deps.personalStore.read();\n\t\tconst workspaceInstallations = input.workspaceDir\n\t\t\t? await (this.deps.readWorkspaceInstallations?.(input.workspaceDir) ?? [])\n\t\t\t: [];\n\t\tconst installations = [...state.installations, ...workspaceInstallations];\n\t\tconst installation = installations.find((candidate) => candidate.packageId === input.packageId);\n\t\tconst resolvePackage = this.deps.resolvePackage ?? (async () => []);\n\t\tconst currentEntries = installation\n\t\t\t? (await resolvePackage(input.packageId, installation.pinnedVersion)).filter((entry) =>\n\t\t\t\t\tinstallation.selectedArtifactIds.some((marker) =>\n\t\t\t\t\t\t(this.deps.artifactSelected ?? ((m, id) => m === id))(marker, entry.artifactId)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t: [];\n\t\tconst nextEntries = await resolvePackage(input.packageId, input.revision);\n\t\tconst resolvedRevision = await this.deps.resolveActualRevision?.(\n\t\t\tinput.packageId,\n\t\t\tinput.revision\n\t\t);\n\t\t// An empty string means the resolver ran but could not identify a\n\t\t// revision (e.g. a staged catalog source with no git tree). The\n\t\t// protocol requires a non-empty toVersion, so fall back to the\n\t\t// requested revision rather than emitting \"\" over the bridge.\n\t\tconst actualRevision = resolvedRevision || input.revision;\n\n\t\tconst currentById = new Map(currentEntries.map((entry) => [entry.artifactId, entry]));\n\t\tconst nextById = new Map(nextEntries.map((entry) => [entry.artifactId, entry]));\n\t\tconst addedArtifactIds = [...nextById.keys()].filter((id) => !currentById.has(id));\n\t\tconst removedArtifactIds = [...currentById.keys()].filter((id) => !nextById.has(id));\n\t\tconst changedArtifactIds = [...nextById.entries()]\n\t\t\t.filter(([id, entry]) => currentById.has(id) && currentById.get(id)?.digest !== entry.digest)\n\t\t\t.map(([id]) => id);\n\t\tconst approvalInvalidatedArtifactIds = changedArtifactIds.filter((id) => {\n\t\t\tconst entry = nextById.get(id);\n\t\t\treturn entry?.requiresApproval === true;\n\t\t});\n\n\t\treturn {\n\t\t\tpackageId: input.packageId,\n\t\t\t...(installation?.pinnedVersion !== undefined\n\t\t\t\t? { fromVersion: installation.pinnedVersion }\n\t\t\t\t: {}),\n\t\t\ttoVersion: actualRevision,\n\t\t\taddedArtifactIds,\n\t\t\tremovedArtifactIds,\n\t\t\tchangedArtifactIds,\n\t\t\tapprovalInvalidatedArtifactIds,\n\t\t};\n\t}\n\n\t/**\n\t * Remove a source only when nothing references it: no workspace\n\t * declaration and no installation (workspace or personal) may still\n\t * use it. Refusals match /installed package/i and /workspace/ so\n\t * callers can surface distinct remediation paths.\n\t */\n\tasync removeSource(sourceId: string, workspaceDir?: string): Promise<void> {\n\t\tconst state = await this.deps.personalStore.read();\n\t\tconst workspaceInstallations = workspaceDir\n\t\t\t? await (this.deps.readWorkspaceInstallations?.(workspaceDir) ?? [])\n\t\t\t: [];\n\t\tconst installations = [...state.installations, ...workspaceInstallations];\n\t\tconst usedBy = installations.filter(\n\t\t\t(installation) => installation.packageId.split(\"::\")[0] === sourceId\n\t\t);\n\t\tif (usedBy.length > 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot remove source ${sourceId}: it still provides an installed package (${usedBy[0]?.packageId})`\n\t\t\t);\n\t\t}\n\t\tif (!workspaceDir) {\n\t\t\t// Without a workspace directory the service cannot discover\n\t\t\t// workspace manifests, and treating \"cannot check\" as \"no\n\t\t\t// declarations\" would bypass cross-scope deletion protection.\n\t\t\t// Refuse explicitly rather than rm -rf a still-declared source.\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot remove source ${sourceId}: a workspace directory is required to verify workspace declarations`\n\t\t\t);\n\t\t}\n\t\tconst declared = await this.deps.listWorkspaceSources(workspaceDir);\n\t\tif (declared.includes(sourceId)) {\n\t\t\tthrow new Error(`Cannot remove source ${sourceId}: the workspace manifest still declares it`);\n\t\t}\n\t\tconst sourceDir = path.join(this.reposDir, sourceId);\n\t\tawait fsp.rm(sourceDir, { recursive: true, force: true }).catch(() => undefined);\n\t}\n}\n","import * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport { getServicemeHome } from \"../paths/userHome\";\nimport type { PlannedContentEntry } from \"./content-plan\";\nimport { CopilotLinkMaterializer } from \"./copilot-link-materializer\";\nimport { FileHookConfigAdapter, FileMcpConfigAdapter } from \"./filesystem-integration-adapters\";\nimport { findRepoHooksJson, parseHooksJson, parseMcpJson } from \"./integration-adapters\";\nimport { resolveWorkspaceContentPlan } from \"./plugin-resolver\";\nimport {\n\tgetWorkspaceManifestPluginSourceId,\n\tgetWorkspaceManifestSources,\n\ttype WorkspaceContentState,\n\ttype WorkspaceCopilotManifest,\n\ttype WorkspaceMaterializedEntry,\n} from \"./types\";\nimport { WorkspaceExcludeStore } from \"./workspace-exclude-store\";\nimport { loadWorkspaceCopilotManifest } from \"./workspace-manifest\";\nimport { WorkspaceContentStateStore } from \"./workspace-state-store\";\n\n/** Statuses surfaced to UI and CLI for each declared entry. */\nexport type WorkspaceContentStatus =\n\t| \"restored\"\n\t| \"adopted\"\n\t| \"pending_approval\"\n\t| \"missing_source\"\n\t| \"conflict\"\n\t| \"drifted\";\n\nexport interface WorkspaceContentEntryResult {\n\tidentity: string;\n\tstatus: WorkspaceContentStatus;\n\tmessage?: string;\n}\n\nexport interface WorkspaceContentReconcileResult {\n\tchanged: boolean;\n\tentries: WorkspaceContentEntryResult[];\n}\n\n/** Entries materialized as .github file links. */\nconst LINK_KINDS = new Set([\"agent\", \"skill\", \"instruction\", \"prompt\"]);\n/** Entries materialized through generated host configuration. */\nconst INTEGRATION_KINDS = new Set([\"mcp\", \"hook\"]);\n\ninterface RepositorySource {\n\tready: boolean;\n\tlocalPath?: string;\n\terror?: string;\n}\n\n/** Orchestrates declaration → pinned source → plan → safe links. */\nexport class WorkspaceCopilotContentReconciler {\n\tprivate readonly workspaceDir: string;\n\tprivate readonly homeDir: string;\n\tprivate readonly ensureRepository: (\n\t\tmanifest: WorkspaceCopilotManifest\n\t) => Promise<Map<string, RepositorySource>>;\n\n\tconstructor(options: {\n\t\tworkspaceDir: string;\n\t\thomeDir?: string;\n\t\t/** Injectable repository ensure step (tests avoid real git). */\n\t\tensureRepository?: (\n\t\t\tmanifest: WorkspaceCopilotManifest\n\t\t) => Promise<Map<string, RepositorySource>>;\n\t\t/** Injectable link materializer (transactions share the test seam). */\n\t\tmaterializer?: CopilotLinkMaterializer;\n\t\t/** Machine-local disable marks: identities returning true are\n\t\t * treated as NOT declared — their materialization is removed and\n\t\t * never resurrected, while the manifest declaration itself stays. */\n\t\tisDisabled?: (identity: string) => Promise<boolean>;\n\t}) {\n\t\tthis.workspaceDir = options.workspaceDir;\n\t\tthis.homeDir = options.homeDir ?? getServicemeHome();\n\t\tthis.ensureRepository = options.ensureRepository ?? this.defaultEnsureRepository.bind(this);\n\t\tthis.materializer = options.materializer ?? new CopilotLinkMaterializer();\n\t\tthis.isDisabled = options.isDisabled;\n\t}\n\n\tprivate readonly materializer: CopilotLinkMaterializer;\n\tprivate readonly isDisabled: ((identity: string) => Promise<boolean>) | undefined;\n\n\t/** Restore declared content on this machine. */\n\tasync reconcile(): Promise<WorkspaceContentReconcileResult> {\n\t\tconst manifest = await loadWorkspaceCopilotManifest(this.workspaceDir);\n\t\tif (!manifest) {\n\t\t\treturn { changed: false, entries: [] };\n\t\t}\n\n\t\tconst sources = await this.ensureRepository(manifest);\n\t\tconst sourceDefinitions = getWorkspaceManifestSources(manifest);\n\t\tconst missing = sourceDefinitions.filter((source) => !sources.get(source.id)?.ready);\n\n\t\tconst stateStore = new WorkspaceContentStateStore({\n\t\t\tworkspaceDir: this.workspaceDir,\n\t\t\thomeDir: this.homeDir,\n\t\t});\n\t\tconst previousState = await stateStore.read();\n\n\t\tif (missing.length > 0) {\n\t\t\tconst entries: WorkspaceContentEntryResult[] = [];\n\t\t\tfor (const plugin of manifest.plugins) {\n\t\t\t\tconst sourceId = getWorkspaceManifestPluginSourceId(manifest, plugin);\n\t\t\t\tif (!sources.get(sourceId)?.ready) {\n\t\t\t\t\tentries.push({\n\t\t\t\t\t\tidentity: `${sourceId}::${plugin.id}`,\n\t\t\t\t\t\tstatus: \"missing_source\",\n\t\t\t\t\t\tmessage: sources.get(sourceId)?.error ?? \"Source unavailable\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { changed: false, entries };\n\t\t}\n\n\t\tconst reposDir = path.join(this.homeDir, \"repos\");\n\t\tconst plan = await resolveWorkspaceContentPlan({ manifest, reposDir });\n\n\t\t// Machine-local disables shrink the effective plan; the manifest\n\t\t// declaration itself is untouched. Planned identities disabled on\n\t\t// this machine behave exactly like identities that left the\n\t\t// declaration (the per-kind removal paths below clean them up).\n\t\tlet linkEntries = plan.entries.filter((entry) => LINK_KINDS.has(entry.kind));\n\t\tlet integrationEntries = plan.entries.filter((entry) => INTEGRATION_KINDS.has(entry.kind));\n\t\tif (this.isDisabled !== undefined) {\n\t\t\tconst disabled = new Set<string>();\n\t\t\tfor (const entry of plan.entries) {\n\t\t\t\tif (await this.isDisabled(entry.identity)) {\n\t\t\t\t\tdisabled.add(entry.identity);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (disabled.size > 0) {\n\t\t\t\tlinkEntries = linkEntries.filter((entry) => !disabled.has(entry.identity));\n\t\t\t\tintegrationEntries = integrationEntries.filter((entry) => !disabled.has(entry.identity));\n\t\t\t}\n\t\t}\n\n\t\t// Scope exclusivity: the same Copilot identity must not be active in\n\t\t// both user and workspace scope at once (architecture doc section 6.1).\n\t\t// A previous state entry whose link path lives outside this\n\t\t// workspace's .github tree represents an existing user-scope\n\t\t// activation; surface it for an explicit move/replace decision\n\t\t// instead of silently creating a second activation.\n\t\tconst scopeOverlaps = previousState.entries.filter(\n\t\t\t(entry) =>\n\t\t\t\tLINK_KINDS.has(entry.kind) &&\n\t\t\t\tplan.entries.some((planned) => planned.identity === entry.identity) &&\n\t\t\t\t!isWorkspaceScopePath(entry.linkPath)\n\t\t);\n\t\tif (scopeOverlaps.length > 0) {\n\t\t\tconst entries: WorkspaceContentEntryResult[] = plan.entries.map((planned) => {\n\t\t\t\tconst overlap = scopeOverlaps.find((existing) => existing.identity === planned.identity);\n\t\t\t\treturn overlap\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tidentity: planned.identity,\n\t\t\t\t\t\t\tstatus: \"conflict\" as const,\n\t\t\t\t\t\t\tmessage: `Already active in user scope at ${overlap.linkPath}; move or replace it before activating it here`,\n\t\t\t\t\t\t}\n\t\t\t\t\t: { identity: planned.identity, status: \"restored\" as const };\n\t\t\t});\n\t\t\treturn { changed: false, entries };\n\t\t}\n\n\t\tconst excludeStore = new WorkspaceExcludeStore({ workspaceDir: this.workspaceDir });\n\t\tconst linkMaterialized = await this.materializer.reconcile({\n\t\t\tworkspaceDir: this.workspaceDir,\n\t\t\tentries: linkEntries,\n\t\t\tpreviousState,\n\t\t});\n\n\t\t// Remove owned .github links for identities that left the\n\t\t// effective plan — declaration removed, or machine-disabled on\n\t\t// this workspace. Only paths inside this workspace's .github tree\n\t\t// are touched (user-scope links belong to their own flow).\n\t\tconst effectiveLinkIds = new Set(linkEntries.map((entry) => entry.identity));\n\t\tlet removedLinks = 0;\n\t\tfor (const previous of previousState.entries) {\n\t\t\tif (!LINK_KINDS.has(previous.kind) || effectiveLinkIds.has(previous.identity)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!isWorkspaceScopePath(previous.linkPath)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tawait fsp.rm(path.resolve(this.workspaceDir, previous.linkPath), {\n\t\t\t\tforce: true,\n\t\t\t\trecursive: true,\n\t\t\t});\n\t\t\tremovedLinks += 1;\n\t\t}\n\n\t\tconst integrationMaterialized = await this.reconcileIntegrations({\n\t\t\tentries: integrationEntries,\n\t\t\tpreviousState,\n\t\t});\n\n\t\tconst approvedIdentities = new Set(\n\t\t\tpreviousState.entries.filter((entry) => entry.approved).map((entry) => entry.identity)\n\t\t);\n\t\tconst mergedState = {\n\t\t\tversion: 1 as const,\n\t\t\tentries: [...linkMaterialized.state.entries, ...integrationMaterialized.state].map(\n\t\t\t\t(entry) => ({\n\t\t\t\t\t...entry,\n\t\t\t\t\tapproved:\n\t\t\t\t\t\tapprovedIdentities.has(entry.identity) &&\n\t\t\t\t\t\tentry.digest === findDigest(plan.entries, entry.identity),\n\t\t\t\t})\n\t\t\t),\n\t\t};\n\t\tawait stateStore.write(mergedState);\n\n\t\tconst managedPaths = mergedState.entries\n\t\t\t.filter((entry) => LINK_KINDS.has(entry.kind))\n\t\t\t.map((entry) => entry.linkPath);\n\t\tif (integrationEntries.some((entry) => entry.kind === \"mcp\")) {\n\t\t\tmanagedPaths.push(\".vscode/mcp.serviceme.json\");\n\t\t}\n\t\tif (integrationEntries.some((entry) => entry.kind === \"hook\")) {\n\t\t\tmanagedPaths.push(\".vscode/hooks.serviceme.json\", \".github/hooks/\");\n\t\t}\n\t\tawait excludeStore.reconcile(managedPaths);\n\n\t\treturn {\n\t\t\tchanged: linkMaterialized.changed || integrationMaterialized.changed || removedLinks > 0,\n\t\t\tentries: [...linkMaterialized.entries, ...integrationMaterialized.entries],\n\t\t};\n\t}\n\n\t/**\n\t * Reconcile mcp/hook entries through their generated-config adapters.\n\t * Unapproved or digest-changed entries stay pending without touching\n\t * host configuration; approved entries are (re)applied; previously\n\t * materialized identities that left the declaration are removed.\n\t */\n\tprivate async reconcileIntegrations(input: {\n\t\tentries: PlannedContentEntry[];\n\t\tpreviousState: WorkspaceContentState;\n\t}): Promise<{\n\t\tchanged: boolean;\n\t\tentries: WorkspaceContentEntryResult[];\n\t\tstate: WorkspaceMaterializedEntry[];\n\t}> {\n\t\tconst results: WorkspaceContentEntryResult[] = [];\n\t\tconst stateEntries: WorkspaceMaterializedEntry[] = [];\n\t\tlet changed = false;\n\t\tconst plannedIdentities = new Set(input.entries.map((entry) => entry.identity));\n\n\t\t// Remove integration config owned by identities that left the plan.\n\t\tfor (const previous of input.previousState.entries) {\n\t\t\tif (!INTEGRATION_KINDS.has(previous.kind) || plannedIdentities.has(previous.identity)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst adapter =\n\t\t\t\tprevious.kind === \"mcp\" ? new FileMcpConfigAdapter() : new FileHookConfigAdapter();\n\t\t\tconst removed = await adapter.removeEntry({\n\t\t\t\tworkspaceDir: this.workspaceDir,\n\t\t\t\tidentity: previous.identity,\n\t\t\t});\n\t\t\tif (removed) changed = true;\n\t\t}\n\n\t\tconst mcpAdapter = new FileMcpConfigAdapter();\n\t\tconst hookAdapter = new FileHookConfigAdapter();\n\t\tfor (const entry of input.entries) {\n\t\t\tconst previous = input.previousState.entries.find(\n\t\t\t\t(candidate) => candidate.identity === entry.identity\n\t\t\t);\n\t\t\tconst approved = previous?.approved === true && previous.digest === entry.digest;\n\t\t\tif (!approved) {\n\t\t\t\tresults.push({\n\t\t\t\t\tidentity: entry.identity,\n\t\t\t\t\tstatus: \"pending_approval\",\n\t\t\t\t\tmessage: \"Integration requires approval on this machine\",\n\t\t\t\t});\n\t\t\t\tstateEntries.push(\n\t\t\t\t\tprevious ? { ...previous, approved: false } : this.toIntegrationStateEntry(entry, false)\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tif (entry.kind === \"mcp\") {\n\t\t\t\t\tconst raw = await fsp.readFile(entry.sourcePath, \"utf8\");\n\t\t\t\t\tfor (const server of parseMcpJson(raw)) {\n\t\t\t\t\t\tawait mcpAdapter.applyServer({ workspaceDir: this.workspaceDir, entry, server });\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst hooksJsonPath =\n\t\t\t\t\t\t(await findRepoHooksJson(path.resolve(entry.sourcePath, \"..\", \"..\"), entry.name)) ??\n\t\t\t\t\t\tpath.join(entry.sourcePath, \"hooks.json\");\n\t\t\t\t\tconst raw = await fsp.readFile(hooksJsonPath, \"utf8\");\n\t\t\t\t\tawait hookAdapter.applyHook({\n\t\t\t\t\t\tworkspaceDir: this.workspaceDir,\n\t\t\t\t\t\tentry,\n\t\t\t\t\t\thook: parseHooksJson(raw),\n\t\t\t\t\t});\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t\tstateEntries.push(this.toIntegrationStateEntry(entry, true));\n\t\t\t\tresults.push({ identity: entry.identity, status: \"restored\" });\n\t\t\t} catch (error) {\n\t\t\t\tresults.push({\n\t\t\t\t\tidentity: entry.identity,\n\t\t\t\t\tstatus: \"conflict\",\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t\tstateEntries.push(\n\t\t\t\t\tprevious ? { ...previous, approved: false } : this.toIntegrationStateEntry(entry, false)\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn { changed, entries: results, state: stateEntries };\n\t}\n\n\t/** Build the machine-local state entry for a generated-config integration. */\n\tprivate toIntegrationStateEntry(\n\t\tentry: PlannedContentEntry,\n\t\tapproved: boolean\n\t): WorkspaceMaterializedEntry {\n\t\treturn {\n\t\t\tidentity: entry.identity,\n\t\t\trepositoryId: entry.repositoryId,\n\t\t\tpluginId: entry.pluginId,\n\t\t\tkind: entry.kind,\n\t\t\tsourcePath: entry.sourcePath,\n\t\t\tlinkPath:\n\t\t\t\tentry.kind === \"mcp\" ? \".vscode/mcp.serviceme.json\" : \".vscode/hooks.serviceme.json\",\n\t\t\tlinkMode: \"generated\",\n\t\t\tdigest: entry.digest,\n\t\t\tapproved,\n\t\t};\n\t}\n\n\t/** Record local approval for risky content, then re-reconcile. */\n\tasync approve(input: { identities: string[] }): Promise<WorkspaceContentReconcileResult> {\n\t\tconst stateStore = new WorkspaceContentStateStore({\n\t\t\tworkspaceDir: this.workspaceDir,\n\t\t\thomeDir: this.homeDir,\n\t\t});\n\t\tconst state = await stateStore.read();\n\t\tconst identities = new Set(input.identities);\n\t\t// Approval is recorded by identity; digest revalidation happens\n\t\t// during the next reconcile through requiresApproval handling.\n\t\tfor (const entry of state.entries) {\n\t\t\tif (identities.has(entry.identity)) {\n\t\t\t\tentry.approved = true;\n\t\t\t}\n\t\t}\n\t\tawait stateStore.write(state);\n\t\treturn this.reconcile();\n\t}\n\n\tprivate async defaultEnsureRepository(\n\t\tmanifest: WorkspaceCopilotManifest\n\t): Promise<Map<string, RepositorySource>> {\n\t\tconst reposDir = path.join(this.homeDir, \"repos\");\n\t\tconst sources = new Map<string, RepositorySource>();\n\t\tfor (const source of getWorkspaceManifestSources(manifest)) {\n\t\t\tconst localPath = path.resolve(reposDir, source.id);\n\t\t\tconst stat = await fsp.stat(localPath).catch(() => null);\n\t\t\tsources.set(\n\t\t\t\tsource.id,\n\t\t\t\tstat?.isDirectory()\n\t\t\t\t\t? { ready: true, localPath }\n\t\t\t\t\t: { ready: false, error: \"Source not materialized under ~/.serviceme/repos\" }\n\t\t\t);\n\t\t}\n\t\treturn sources;\n\t}\n}\n\nfunction findDigest(entries: PlannedContentEntry[], identity: string): string | undefined {\n\treturn entries.find((entry) => entry.identity === identity)?.digest;\n}\n\nfunction isWorkspaceScopePath(linkPath: string): boolean {\n\tconst normalized = linkPath.replace(/\\\\/g, \"/\");\n\treturn (\n\t\tnormalized === \".github\" ||\n\t\tnormalized.startsWith(\".github/\") ||\n\t\tnormalized.endsWith(\"/.github\") ||\n\t\tnormalized.includes(\"/.github/\")\n\t);\n}\n","import type { CopilotPackageInstallation } from \"./customization-model\";\nimport {\n\ttype CopilotArtifactKind,\n\tgetWorkspaceManifestPluginSourceId,\n\ttype WorkspaceCopilotManifestV2,\n\ttype WorkspaceManifestPlugin,\n} from \"./types\";\nimport { loadWorkspaceCopilotManifest } from \"./workspace-manifest\";\n\n/** Legacy fallback artifact ids derived from the kind selection map. */\nconst KIND_SELECTION_ORDER: CopilotArtifactKind[] = [\n\t\"agent\",\n\t\"skill\",\n\t\"instruction\",\n\t\"prompt\",\n\t\"hook\",\n\t\"mcp\",\n];\n\n/**\n * Read the workspace manifest’s declared package selections without resolving\n * sources. Source resolution can fail (unavailable source, conflict), and\n * rendered snapshots then fall back to legacy kind maps that erase exact\n * artifactIds. Reverse-scope conflict checks must still see what the\n * manifest declares, so they read declarations directly. Corrupted\n * manifests surface the load error rather than pretending nothing is\n * declared.\n */\nexport async function readDeclaredWorkspaceInstallations(\n\tworkspaceDir: string\n): Promise<CopilotPackageInstallation[]> {\n\tconst manifest = await loadWorkspaceCopilotManifest(workspaceDir);\n\tif (!manifest) return [];\n\treturn manifest.plugins.map((plugin) => {\n\t\tconst sourceId = getWorkspaceManifestPluginSourceId(manifest, plugin);\n\t\tconst packageId = `${sourceId}::${plugin.id}`;\n\t\tconst selectedArtifactIds =\n\t\t\tplugin.artifactIds && plugin.artifactIds.length > 0\n\t\t\t\t? plugin.artifactIds\n\t\t\t\t: legacyFallbackArtifactIds(plugin.artifacts, packageId, plugin.id);\n\t\treturn {\n\t\t\tpackageId,\n\t\t\tscope: \"workspace\" as const,\n\t\t\tselectedArtifactIds,\n\t\t\t...(manifest.version === 2 ? declaredPinnedVersion(manifest, sourceId) : {}),\n\t\t};\n\t});\n}\n\nfunction legacyFallbackArtifactIds(\n\tartifacts: WorkspaceManifestPlugin[\"artifacts\"],\n\tpackageId: string,\n\tpluginId: string\n): string[] {\n\treturn KIND_SELECTION_ORDER.filter((kind) => artifacts[kind] !== false).map(\n\t\t(kind) => `${packageId}::${kind}:${pluginId}`\n\t);\n}\n\nfunction declaredPinnedVersion(\n\tmanifest: WorkspaceCopilotManifestV2,\n\tsourceId: string\n): { pinnedVersion?: string } {\n\tconst source = manifest.sources.find((candidate) => candidate.id === sourceId);\n\treturn source?.type === \"git\" && source.commit ? { pinnedVersion: source.commit } : {};\n}\n","/**\n * deviceAuth — Pure HMAC-SHA-256 signing helpers for device auth headers.\n *\n * **Boundary exception copy.** The single source of truth for the\n * `x-ms-device-*` header names + the signature algorithm now lives in\n * `@serviceme/devtools-shared` (`device-auth.ts`), consumed by the\n * extension signer and the server verifier. ADL-003 forbids\n * core → shared (and shared → core), so this module keeps a\n * byte-for-byte copy as the documented exception. Keep it in lock-step\n * with `packages/serviceme-shared/src/device-auth.ts`.\n *\n * See `docs/architecture/phase-5-device-header-spec.md` §5 for the wire\n * format. The basis is `METHOD\\nPATH\\nTIMESTAMP\\nBODY\\nSECRET`\n * (LF-joined, NOT JSON). Output is lowercase hex SHA-256.\n */\n\nimport { createHash } from \"node:crypto\";\n\n/** Canonical header names — MUST match `@serviceme/devtools-shared`'s `DeviceAuthHeaders`. */\nexport const DeviceAuthHeaders = {\n\tdeviceId: \"x-ms-device-id\",\n\tdeviceSecret: \"x-ms-device-secret\",\n\tsignature: \"x-ms-device-signature\",\n\ttimestamp: \"x-ms-device-timestamp\",\n\tsecretVersion: \"x-ms-device-secret-version\",\n} as const;\n\nexport interface DeviceRequestSignatureParams {\n\tmethod: string;\n\tpath: string;\n\ttimestamp: number;\n\tbody: string;\n\tsecret: string;\n}\n\n/**\n * Compute the HMAC-SHA-256 hex digest of the canonical basis.\n *\n * Server contract (`apps/server/src/lib/auth/device-signature-guard.ts:46-62`)\n * is line-for-line identical: same LF-joined basis, same lowercase\n * hex output. Any divergence breaks `device-signature-guard.test.ts`.\n */\nexport function createDeviceRequestSignature(params: DeviceRequestSignatureParams): string {\n\tconst basis = [\n\t\tparams.method.toUpperCase(),\n\t\tparams.path,\n\t\tString(params.timestamp),\n\t\tparams.body,\n\t\tparams.secret,\n\t].join(\"\\n\");\n\treturn createHash(\"sha256\").update(basis).digest(\"hex\");\n}\n\n/** 5-header map consumed by `fetch()` callers (CLI bridge, Extension). */\nexport interface DeviceSignedHeaders {\n\t[DeviceAuthHeaders.deviceId]: string;\n\t[DeviceAuthHeaders.deviceSecret]: string;\n\t[DeviceAuthHeaders.signature]: string;\n\t[DeviceAuthHeaders.timestamp]: string;\n\t[DeviceAuthHeaders.secretVersion]: string;\n}\n\nexport interface BuildSignedHeadersParams {\n\tmethod: string;\n\tpath: string;\n\tbody: string;\n\tpublicId: string;\n\tdeviceSecret: string;\n\tsecretVersion: number;\n\t/** Override for deterministic tests. */\n\ttimestamp?: number;\n}\n\n/**\n * Build the canonical 5-header map. The `body` parameter MUST be the\n * exact byte sequence sent on the wire (no whitespace re-canonicalization\n * between client serialization and signature basis construction).\n */\nexport function buildSignedHeaders(params: BuildSignedHeadersParams): DeviceSignedHeaders {\n\tconst timestamp = params.timestamp ?? Date.now();\n\tconst signature = createDeviceRequestSignature({\n\t\tmethod: params.method,\n\t\tpath: params.path,\n\t\ttimestamp,\n\t\tbody: params.body,\n\t\tsecret: params.deviceSecret,\n\t});\n\treturn {\n\t\t[DeviceAuthHeaders.deviceId]: params.publicId,\n\t\t[DeviceAuthHeaders.deviceSecret]: params.deviceSecret,\n\t\t[DeviceAuthHeaders.signature]: signature,\n\t\t[DeviceAuthHeaders.timestamp]: String(timestamp),\n\t\t[DeviceAuthHeaders.secretVersion]: String(params.secretVersion),\n\t};\n}\n","/**\n * Enroller — State machine for `device.enroll` and `device.rotate-secret`.\n *\n * States per `2.需求澄清.md` §1.2:\n * anonymous → pending → claimed → expired\n *\n * - `anonymous` (initial): no device has ever enrolled. Server returns\n * a fresh `publicId` + secret.\n * - `pending`: enrollment HTTP call has been issued but the server\n * hasn't confirmed yet. In-flight state — never persisted.\n * - `claimed`: user has linked this device to their account (via\n * `/api/v1/devices/claim`). Sticky binding locks future re-enrolls\n * to the same `userId` (server-side matrix).\n * - `expired`: server returned a device-expiry error. Forces a fresh\n * enroll on next call.\n *\n * `--force` semantics: any non-anonymous state can be force-reset to\n * `anonymous` by wiping the local identity file. The next enroll will\n * be treated as a brand-new install by the server (no sticky binding).\n *\n * The Enroller is the **state machine**; the actual HTTP I/O is the\n * caller's responsibility (the `DeviceSyncClient` in Phase 5.4 wires\n * the server). This split keeps the Enroller unit-testable without\n * a live server.\n *\n * Refs:\n * - 4.功能规划.md §2.2 — `Enroller.ts anonymous → pending → claimed → expired`\n * - `2.需求澄清.md` §1.2 — binding-state machine\n */\n\nimport { randomBytes } from \"node:crypto\";\n\nimport type { DeviceBindingState, DeviceEnrollResult } from \"@serviceme/devtools-protocol\";\nimport type { IdentityStore } from \"./IdentityStore\";\nimport { deriveInstallationId } from \"./InstallationId\";\nimport type { PersistedDeviceIdentity } from \"./types\";\n\n/** 32 bytes of HMAC secret material — matches the server's `device-registration.ts:73-80` generator. */\nconst SECRET_BYTES = 32;\n/** Server returns `publicId` as 32-char hex (16 bytes). Match the wire length. */\nconst PUBLIC_ID_BYTES = 16;\n\ntype RandomBytesFn = (size: number) => Buffer;\n\nconst defaultRandomBytes: RandomBytesFn = (size) => {\n\treturn randomBytes(size);\n};\n\nexport interface EnrollerOptions {\n\tidentityStore: IdentityStore;\n\t/** Injectable clock for deterministic tests. */\n\tnow?: () => Date;\n\t/** Override the random source (tests). */\n\trandomBytes?: (size: number) => Buffer;\n\t/** Caller-supplied enroll HTTP function. Phase 5.4 wires the real one. */\n\tenrollRequest?: EnrollRequestFn;\n}\n\nexport type EnrollRequestFn = (input: {\n\tinstallationId: string;\n\tmachineId: string;\n\tplatform: string;\n\texisting: PersistedDeviceIdentity | null;\n\tforce: boolean;\n\trequireAuth: boolean;\n}) => Promise<EnrollResponse>;\n\nexport interface EnrollResponse {\n\tpublicId: string;\n\tdeviceSecret: string;\n\tsecretVersion: number;\n\tbindingState: DeviceBindingState;\n\texpiresAt?: string;\n}\n\n/** Sentinel error — re-enroll on a claimed device without auth. */\nexport class DeviceReenrollRequiresAuthError extends Error {\n\tconstructor(\n\t\tmessage = \"Re-enroll on a claimed device requires current device credentials or the bound user\"\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"DeviceReenrollRequiresAuthError\";\n\t}\n}\n\n/** Sentinel error — server returned a 410 / version-mismatch after rotation. */\nexport class DeviceSecretVersionMismatchError extends Error {\n\tconstructor(message = \"Device secret version mismatch — server has rotated past the local copy\") {\n\t\tsuper(message);\n\t\tthis.name = \"DeviceSecretVersionMismatchError\";\n\t}\n}\n\nexport class Enroller {\n\tprivate readonly identity: IdentityStore;\n\tprivate readonly now: () => Date;\n\tprivate readonly random: (size: number) => Buffer;\n\tprivate readonly enrollRequest?: EnrollRequestFn;\n\tprivate inflight: Promise<DeviceEnrollResult> | null = null;\n\n\tconstructor(opts: EnrollerOptions) {\n\t\tthis.identity = opts.identityStore;\n\t\tthis.now = opts.now ?? (() => new Date());\n\t\tthis.random = opts.randomBytes ?? defaultRandomBytes;\n\t\tthis.enrollRequest = opts.enrollRequest;\n\t}\n\n\t/**\n\t * Read the current binding state without touching the disk.\n\t * Returns `anonymous` when no identity is stored.\n\t */\n\tasync currentState(): Promise<DeviceBindingState> {\n\t\tconst stored = await this.identity.read();\n\t\treturn stored?.bindingState ?? \"anonymous\";\n\t}\n\n\t/**\n\t * Drive the enrollment flow.\n\t *\n\t * @param force when true, drop the local identity and start fresh\n\t * (server treats this as a brand-new install).\n\t * @param requireAuth when true, refuse to silently re-enroll an\n\t * existing claimed device — throw\n\t * `DeviceReenrollRequiresAuthError` instead.\n\t */\n\t/**\n\t * Resolve when any in-flight enrollment completes. Returns immediately\n\t * when no enrollment is in progress. Allows callers (e.g. the extension's\n\t * `buildDeviceAuthHeaders`) to wait for a concurrent `syncDeviceInfo()`\n\t * enrollment before attempting to read the identity from the store.\n\t */\n\tasync waitForEnrollment(): Promise<void> {\n\t\tif (this.inflight) {\n\t\t\tawait this.inflight;\n\t\t}\n\t}\n\n\tasync enroll(opts: { force?: boolean; requireAuth?: boolean } = {}): Promise<DeviceEnrollResult> {\n\t\t// Concurrency guard — multiple in-flight calls share the same promise.\n\t\tif (this.inflight) {\n\t\t\treturn this.inflight;\n\t\t}\n\t\tconst promise = this.runEnroll(opts);\n\t\tthis.inflight = promise;\n\t\ttry {\n\t\t\treturn await promise;\n\t\t} finally {\n\t\t\tif (this.inflight === promise) this.inflight = null;\n\t\t}\n\t}\n\n\t/** Test seam — surface the underlying identity store. */\n\tgetIdentityStore(): IdentityStore {\n\t\treturn this.identity;\n\t}\n\n\t/** True when an enrollment is currently in-flight. Used by callers (e.g. the extension's `buildDeviceAuthHeaders`) to skip triggering a competing enrollment. */\n\tisEnrolling(): boolean {\n\t\treturn this.inflight !== null;\n\t}\n\n\tprivate async runEnroll(opts: {\n\t\tforce?: boolean;\n\t\trequireAuth?: boolean;\n\t}): Promise<DeviceEnrollResult> {\n\t\tconst { written } = await this.identity.mutate(async (current) => {\n\t\t\tconst existing = opts.force ? null : current;\n\n\t\t\tif (!opts.force && current) {\n\t\t\t\tif (current.bindingState === \"expired\") {\n\t\t\t\t\t// Expired identities are forced to re-enroll as if they were new.\n\t\t\t\t} else if (\n\t\t\t\t\topts.requireAuth &&\n\t\t\t\t\t(current.bindingState === \"claimed\" || current.bindingState === \"pending\")\n\t\t\t\t) {\n\t\t\t\t\t// Caller asserted the device must be claimed, but local state\n\t\t\t\t\t// shows it's still in flight. This is a CLI-only guard — the\n\t\t\t\t\t// server is the final arbiter.\n\t\t\t\t\tthrow new DeviceReenrollRequiresAuthError();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst installationId = current?.installationId ?? deriveInstallationId();\n\t\t\tconst machineId = current?.machineId ?? \"unknown\";\n\t\t\tconst platform = current?.platform ?? \"unknown\";\n\n\t\t\tlet response: EnrollResponse;\n\t\t\tif (this.enrollRequest) {\n\t\t\t\tresponse = await this.enrollRequest({\n\t\t\t\t\tinstallationId,\n\t\t\t\t\tmachineId,\n\t\t\t\t\tplatform,\n\t\t\t\t\texisting,\n\t\t\t\t\tforce: Boolean(opts.force),\n\t\t\t\t\trequireAuth: Boolean(opts.requireAuth),\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// Test path / no live HTTP — synthesize a fresh identity. This\n\t\t\t\t// branch is what unit tests exercise; production wires\n\t\t\t\t// `enrollRequest` in Phase 5.4.\n\t\t\t\tresponse = synthesizeEnrollResponse(this.random, existing);\n\t\t\t}\n\n\t\t\tconst next: PersistedDeviceIdentity = {\n\t\t\t\tversion: current?.version ?? 1,\n\t\t\t\tinstallationId,\n\t\t\t\tmachineId,\n\t\t\t\tplatform,\n\t\t\t\thostname: current?.hostname,\n\t\t\t\tpublicId: response.publicId,\n\t\t\t\tsecretVersion: response.secretVersion,\n\t\t\t\tbindingState: response.bindingState,\n\t\t\t\tdeviceSecret: response.deviceSecret,\n\t\t\t\tpreviousDeviceSecret: existing?.deviceSecret,\n\t\t\t\tpreviousSecretExpiresAt:\n\t\t\t\t\topts.force || response.secretVersion === (existing?.secretVersion ?? 0) + 1\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: existing?.previousSecretExpiresAt,\n\t\t\t\tlastEnrollAt: this.now().toISOString(),\n\t\t\t\tlastSyncAt: existing?.lastSyncAt,\n\t\t\t\tlastSyncError: undefined,\n\t\t\t};\n\t\t\treturn { next };\n\t\t});\n\n\t\treturn {\n\t\t\tpublicId: written.publicId,\n\t\t\tbindingState: written.bindingState,\n\t\t\texpiresAt: deriveExpiresAt(written, this.now),\n\t\t};\n\t}\n\n\t/**\n\t * Rotate the HMAC secret. Keeps the previous secret for the grace\n\t * window (default 7 days per `2.需求澄清.md` §1.2) — the\n\t * `previousSecretExpiresAt` is stamped on the persisted identity.\n\t */\n\tasync rotateSecret(\n\t\topts: { gracePeriodDays?: number } = {}\n\t): Promise<{ publicId: string; secretVersion: number; gracePeriodDays: number }> {\n\t\tconst gracePeriodDays = opts.gracePeriodDays ?? 7;\n\t\tconst now = this.now();\n\t\tconst newSecret = this.random(SECRET_BYTES).toString(\"hex\");\n\n\t\tconst { written } = await this.identity.mutate(async (current) => {\n\t\t\tif (!current) {\n\t\t\t\tthrow new Error(\"Cannot rotate-secret without a prior enrollment\");\n\t\t\t}\n\t\t\tconst graceExpiresAt = new Date(now.getTime() + gracePeriodDays * 24 * 60 * 60 * 1000);\n\t\t\tconst next: PersistedDeviceIdentity = {\n\t\t\t\t...current,\n\t\t\t\tdeviceSecret: newSecret,\n\t\t\t\tpreviousDeviceSecret: current.deviceSecret,\n\t\t\t\tpreviousSecretExpiresAt: graceExpiresAt.toISOString(),\n\t\t\t\tsecretVersion: current.secretVersion + 1,\n\t\t\t\tlastEnrollAt: now.toISOString(),\n\t\t\t};\n\t\t\treturn { next };\n\t\t});\n\n\t\treturn {\n\t\t\tpublicId: written.publicId,\n\t\t\tsecretVersion: written.secretVersion,\n\t\t\tgracePeriodDays,\n\t\t};\n\t}\n\n\t/**\n\t * Mark the device as `expired`. Used when the server returns a\n\t * device-expiry response; the next `enroll()` call forces a fresh\n\t * round-trip.\n\t */\n\tasync markExpired(): Promise<void> {\n\t\tawait this.identity.mutate(async (current) => {\n\t\t\tif (!current) {\n\t\t\t\t// Nothing to expire.\n\t\t\t\treturn { next: current ?? (await emptyIdentity(this.random)), result: undefined };\n\t\t\t}\n\t\t\tconst next: PersistedDeviceIdentity = { ...current, bindingState: \"expired\" };\n\t\t\treturn { next };\n\t\t});\n\t}\n\n\t/**\n\t * Mark the device as `claimed`. Called by the bridge after a\n\t * successful `device.claim` server response.\n\t */\n\tasync markClaimed(): Promise<void> {\n\t\tawait this.identity.mutate(async (current) => {\n\t\t\tif (!current) {\n\t\t\t\tthrow new Error(\"Cannot mark-claimed without a prior enrollment\");\n\t\t\t}\n\t\t\tconst next: PersistedDeviceIdentity = {\n\t\t\t\t...current,\n\t\t\t\tbindingState: \"claimed\",\n\t\t\t\tlastSyncAt: this.now().toISOString(),\n\t\t\t\tlastSyncError: undefined,\n\t\t\t};\n\t\t\treturn { next };\n\t\t});\n\t}\n}\n\nfunction deriveExpiresAt(_identity: PersistedDeviceIdentity, _now: () => Date): string | undefined {\n\t// No explicit expiry on the server today (per phase-5-device-header-spec.md\n\t// §9 #1: 7-day grace is rotation-only). The shape is here for forward\n\t// compatibility — when the server adds a per-device expiry, this\n\t// pulls the value from the response without changing the call site.\n\treturn undefined;\n}\n\nfunction synthesizeEnrollResponse(\n\trandom: RandomBytesFn,\n\texisting: PersistedDeviceIdentity | null\n): EnrollResponse {\n\tconst publicId = existing?.publicId ?? random(PUBLIC_ID_BYTES).toString(\"hex\");\n\tconst secretVersion = (existing?.secretVersion ?? 0) + 1;\n\treturn {\n\t\tpublicId,\n\t\tdeviceSecret: random(SECRET_BYTES).toString(\"hex\"),\n\t\tsecretVersion,\n\t\tbindingState: existing?.bindingState === \"claimed\" ? \"claimed\" : \"anonymous\",\n\t};\n}\n\nasync function emptyIdentity(random: RandomBytesFn): Promise<PersistedDeviceIdentity> {\n\treturn {\n\t\tversion: 1,\n\t\tinstallationId: deriveInstallationId(),\n\t\tmachineId: \"unknown\",\n\t\tplatform: \"unknown\",\n\t\tpublicId: random(PUBLIC_ID_BYTES).toString(\"hex\"),\n\t\tsecretVersion: 1,\n\t\tbindingState: \"anonymous\",\n\t\tdeviceSecret: random(SECRET_BYTES).toString(\"hex\"),\n\t\tlastEnrollAt: new Date().toISOString(),\n\t};\n}\n","/**\n * InstallationId — Derive a stable per-machine identifier from\n * `os.hostname()` + `os.userInfo()`.\n *\n * Per `docs/architecture/phase-5-auth-device-toolbox.md § P5-2 (并入本文时对应 § P5-2 拆分)` B2,\n * `installationId` MUST survive Extension re-installs but vary across\n * machines. We compute a UUID v5-style hash over hostname + username +\n * platform so the result is:\n * - deterministic (same machine → same id)\n * - collision-resistant (SHA-256, 128-bit truncated)\n * - browser-safe (no PII survives — username never enters output)\n *\n * Note: this intentionally differs from `vscode.env.machineId`, which\n * is per-Extension-install and uses a different algorithm. The two\n * coexist: `installationId` is what gets sent to the server, while\n * `machineId` (raw `os.hostname()`) is for diagnostics.\n *\n * Refs:\n * - 4.功能规划.md §2.2 — `InstallationId.ts os.hostname() + os.userInfo() 哈希生成`\n * - 3.功能拆分.md B2 — installationId semantics\n */\n\nimport { createHash, randomUUID } from \"node:crypto\";\nimport * as os from \"node:os\";\n\n/** Hex-encoded SHA-256 input. Format: `<hostname>|<username>|<platform>|<nodeVersion>`. */\nfunction fingerprintMaterial(): string {\n\t// `os.userInfo()` is undefined-ish on Windows in some sandboxes; fall\n\t// back to a process env user, then to a constant placeholder. We never\n\t// emit the raw username to the caller — it only enters the hash.\n\tlet username = \"unknown\";\n\ttry {\n\t\tusername = os.userInfo().username;\n\t} catch {\n\t\tusername = process.env.USER ?? process.env.USERNAME ?? \"unknown\";\n\t}\n\treturn [\n\t\tos.hostname(),\n\t\tusername,\n\t\tos.platform(),\n\t\tos.arch(),\n\t\tprocess.versions.node ?? \"unknown\",\n\t].join(\"|\");\n}\n\n/**\n * Returns a deterministic installation id for the current machine.\n * Use this when you need an id that survives Extension reinstalls\n * but stays stable across restarts on the same machine.\n */\nexport function deriveInstallationId(): string {\n\tconst material = fingerprintMaterial();\n\tconst digest = createHash(\"sha256\").update(material).digest(\"hex\");\n\t// Take the first 32 hex chars (128 bits) and reformat as UUID v4-shape\n\t// so the output looks like a UUID to downstream consumers while\n\t// remaining a pure SHA-256 truncation.\n\treturn formatAsV4(digest.slice(0, 32));\n}\n\n/**\n * Returns a random installation id (UUID v4). Use this for fresh\n * installs when no fingerprint input is available (e.g. containerized\n * CI runners where `os.hostname()` is meaningless).\n */\nexport function randomInstallationId(): string {\n\treturn randomUUID();\n}\n\n/** SHA-256 fingerprint material exposed for tests + diagnostics. */\nexport function fingerprintSource(): string {\n\treturn fingerprintMaterial();\n}\n\nfunction formatAsV4(hex32: string): string {\n\t// Stamp version 4 + variant bits per RFC 4122 §4.4. The bits are\n\t// cosmetic — the underlying entropy is still SHA-256.\n\tconst chars = hex32.split(\"\");\n\t// Version nibble (position 12 in canonical UUID, index 13 of the 32-char string).\n\tconst versionIdx = 12;\n\tconst variantIdx = 16;\n\tconst versionChar = (parseInt(chars[versionIdx] ?? \"8\", 16) & 0x0) | 0x4;\n\tchars[versionIdx] = versionChar.toString(16);\n\t// Variant nibble: 10xx → first hex char of the 17th position.\n\tconst variantChar = (parseInt(chars[variantIdx] ?? \"8\", 16) & 0x3) | 0x8;\n\tchars[variantIdx] = variantChar.toString(16);\n\tconst formatted = chars.join(\"\");\n\treturn `${formatted.slice(0, 8)}-${formatted.slice(8, 12)}-${formatted.slice(12, 16)}-${formatted.slice(16, 20)}-${formatted.slice(20, 32)}`;\n}\n","/**\n * IdentityStore — Atomic JSON persistence for the device identity file.\n *\n * Stores the `PersistedDeviceIdentity` (incl. the HMAC secret cleartext)\n * at `~/.serviceme/device.json` (per `phase-5-device-header-spec.md`\n * §3.1). Writes are atomic via `write-tmp + fsync + rename`, matching\n * the `SkillStore` / `ToolboxStore` precedent. Concurrent writes are\n * serialized with a mkdir-based file lock (POSIX-atomic) — proper\n * cross-process locking is deferred to Phase 6+ per the open spec.\n *\n * The file mode is `0600` (owner read/write only) so the cleartext\n * secret stays safe at rest. On Windows the mode hint is a no-op\n * (Windows uses ACLs) but `writeFile` still succeeds.\n *\n * Migration — IdentityStore auto-detects a v0-shape (pre-Phase-5.2)\n * file written by the Extension's old `globalState` blob:\n * { version: 1, claimed: false, publicKeyFingerprint: null }\n * In that case the file is migrated forward to the v1 schema on the\n * next write (the data fields are empty and a fresh enroll is required).\n * The full Extension `globalState` → JSON migration happens in the\n * Phase 5.5 adapter (`apps/extension/.../DeviceService.ts`) since the\n * adapter holds the live `globalState` access.\n *\n * Refs:\n * - 4.功能规划.md §2.2 — `IdentityStore.ts 持久化到 ~/.config/serviceme/device.json, 原子写`\n * - `docs/architecture/phase-5-device-header-spec.md` §3.1, §2.5\n */\n\nimport * as fsp from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\n\nimport { getDeviceJsonPath, getServicemeHome } from \"../paths/userHome\";\n\nimport {\n\ttype AtomicWriteResult,\n\tDEVICE_JSON_SCHEMA_VERSION,\n\ttype IdentityStoreHooks,\n\ttype PersistedDeviceIdentity,\n} from \"./types\";\n\nconst FILE_MODE = 0o600;\nconst LOCK_DIR_MODE = 0o700;\nconst DEFAULT_LOCK_TIMEOUT_MS = 5000;\nconst DEFAULT_LOCK_RETRY_MS = 25;\n// `mkdir` (lock acquisition) and writing the pid file are two separate\n// syscalls, so there's a brief window where the lock dir exists but the\n// pid file doesn't yet. A grace period keeps a concurrent acquirer from\n// mistaking that window for an abandoned lock (see `isStaleLock`).\nconst LOCK_STALE_GRACE_MS = 200;\nconst TMP_SUFFIX = \".tmp\";\n\n/**\n * Minimal interface for reading + writing the persisted identity file.\n * Default impl uses `getDeviceJsonPath()` (which honors `SERVICEME_HOME`),\n * but tests can substitute a custom path for isolation.\n */\nexport interface IdentityFileBackend {\n\tread(filePath: string): Promise<PersistedDeviceIdentity | null>;\n\twrite(filePath: string, payload: PersistedDeviceIdentity): Promise<AtomicWriteResult>;\n\texists(filePath: string): Promise<boolean>;\n\tdelete(filePath: string): Promise<void>;\n\tlistDir?(dir: string): Promise<string[]>;\n}\n\nexport interface IdentityStoreOptions {\n\tfilePath?: string;\n\thooks?: IdentityStoreHooks;\n\tlockTimeoutMs?: number;\n\tlockRetryMs?: number;\n\t/** Injectable clock for deterministic tests. */\n\tnow?: () => Date;\n\tbackend?: IdentityFileBackend;\n}\n\n/**\n * Default file backend — uses `node:fs/promises` with the canonical\n * tmp-then-rename atomic-write pattern.\n */\nexport class FsIdentityFileBackend implements IdentityFileBackend {\n\tasync exists(filePath: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait fsp.access(filePath);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync read(filePath: string): Promise<PersistedDeviceIdentity | null> {\n\t\ttry {\n\t\t\tconst buf = await fsp.readFile(filePath, \"utf8\");\n\t\t\tconst parsed = JSON.parse(buf) as unknown;\n\t\t\treturn migratePersistedIdentity(parsed);\n\t\t} catch (err) {\n\t\t\tif (isNodeError(err) && err.code === \"ENOENT\") return null;\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync write(filePath: string, payload: PersistedDeviceIdentity): Promise<AtomicWriteResult> {\n\t\tawait fsp.mkdir(path.dirname(filePath), { recursive: true });\n\t\tconst tmpPath = `${filePath}${TMP_SUFFIX}`;\n\t\tconst bytes = Buffer.from(JSON.stringify(payload, null, \"\\t\"), \"utf8\");\n\t\t// Ensure tmp is fresh (in case a previous run died mid-write).\n\t\tawait fsp.rm(tmpPath, { force: true });\n\t\tconst handle = await fsp.open(tmpPath, \"w\", FILE_MODE);\n\t\ttry {\n\t\t\tawait handle.writeFile(bytes);\n\t\t\tawait handle.sync();\n\t\t} finally {\n\t\t\tawait handle.close();\n\t\t}\n\t\tawait fsp.rename(tmpPath, filePath);\n\t\t// Best-effort chmod for filesystems that ignore mode on create (Windows).\n\t\tawait fsp.chmod(filePath, FILE_MODE).catch(() => undefined);\n\t\treturn { bytesWritten: bytes.byteLength, tmpPath };\n\t}\n\n\tasync delete(filePath: string): Promise<void> {\n\t\tawait fsp.rm(filePath, { force: true });\n\t}\n}\n\n/**\n * Reconcile an unknown on-disk shape into the current `PersistedDeviceIdentity`.\n *\n * - v1 IdentityStore files (current shape) pass through unchanged.\n * - v0 bootstrap files (`{ version: 1, claimed: false, publicKeyFingerprint: null }`)\n * are recognized by their placeholder keys and discarded; the next\n * enroll writes a fresh identity.\n * - Anything else throws — refuse to silently drop user data.\n */\nfunction migratePersistedIdentity(parsed: unknown): PersistedDeviceIdentity | null {\n\tif (!isRecord(parsed)) {\n\t\tthrow new Error(\"device.json: top-level must be an object\");\n\t}\n\tconst version = parsed.version;\n\tif (version === DEVICE_JSON_SCHEMA_VERSION) {\n\t\t// Pre-Phase-5.2 placeholder shape carries `claimed` /\n\t\t// `publicKeyFingerprint` but no real device fields. Recognize\n\t\t// the marker and return null so the next enroll writes fresh data.\n\t\tif (\n\t\t\tparsed.publicId === undefined &&\n\t\t\tparsed.deviceSecret === undefined &&\n\t\t\t(\"claimed\" in parsed || \"publicKeyFingerprint\" in parsed)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\t// Trust the schema — the writer is also us.\n\t\treturn parsed as unknown as PersistedDeviceIdentity;\n\t}\n\tif (typeof version === \"number\" && version < DEVICE_JSON_SCHEMA_VERSION) {\n\t\t// Pre-Phase-5.2 bootstrap shape — the file is empty placeholder\n\t\t// data; nothing to migrate. Return null to signal \"no identity\".\n\t\treturn null;\n\t}\n\tthrow new Error(`device.json: unsupported schema version ${String(version)}`);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction isNodeError(value: unknown): value is NodeJS.ErrnoException {\n\treturn value instanceof Error && typeof (value as { code?: unknown }).code === \"string\";\n}\n\nconst LOCK_PID_FILE = \"pid\";\n\n/**\n * Check whether a process is still alive (best-effort, cross-platform).\n * Returns `false` for any PID we cannot verify as alive.\n */\nfunction isProcessAlive(pid: number): boolean {\n\ttry {\n\t\t// signal 0 — permission check only, never actually sent\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * mkdir-based advisory file lock with stale-lock recovery.\n *\n * POSIX mkdir is atomic; on Windows modern filesystems (NTFS) it's also\n * atomic at the API level. Sufficient for single-host, single-user\n * scenarios (which is the SERVICEME threat model).\n *\n * Stale lock recovery: a `pid` file inside the lock directory records the\n * owner's PID. On `EEXIST`, if the recorded PID is no longer alive, the\n * lock directory is forcibly removed and acquisition retried immediately.\n * This prevents permanent lockout when a process crashes without calling\n * `release()`.\n */\nclass FileLock {\n\tprivate readonly dirPath: string;\n\tprivate readonly pidFilePath: string;\n\tprivate readonly timeoutMs: number;\n\tprivate readonly retryMs: number;\n\tprivate acquired = false;\n\n\tconstructor(filePath: string, timeoutMs: number, retryMs: number) {\n\t\tthis.dirPath = `${filePath}.lock`;\n\t\tthis.pidFilePath = path.join(this.dirPath, LOCK_PID_FILE);\n\t\tthis.timeoutMs = timeoutMs;\n\t\tthis.retryMs = retryMs;\n\t}\n\n\tasync acquire(): Promise<void> {\n\t\tconst start = Date.now();\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\tawait fsp.mkdir(this.dirPath, { mode: LOCK_DIR_MODE });\n\t\t\t\t// Write PID so a future acquirer can detect if we crash.\n\t\t\t\tawait fsp.writeFile(this.pidFilePath, String(process.pid), \"utf8\").catch(() => undefined);\n\t\t\t\tthis.acquired = true;\n\t\t\t\treturn;\n\t\t\t} catch (err) {\n\t\t\t\tif (!isNodeError(err) || err.code !== \"EEXIST\") {\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t\t// Lock directory exists — check for stale owner.\n\t\t\t\tconst stale = await this.isStaleLock();\n\t\t\t\tif (stale) {\n\t\t\t\t\tawait fsp.rm(this.dirPath, { recursive: true, force: true });\n\t\t\t\t\t// Retry immediately without counting this iteration against timeout.\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Date.now() - start >= this.timeoutMs) {\n\t\t\t\t\tthrow new Error(`IdentityStore lock acquisition timed out for ${this.dirPath}`);\n\t\t\t\t}\n\t\t\t\tawait delay(this.retryMs);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async isStaleLock(): Promise<boolean> {\n\t\tlet pidStr: string;\n\t\ttry {\n\t\t\tpidStr = await fsp.readFile(this.pidFilePath, \"utf8\");\n\t\t} catch {\n\t\t\t// The pid file may not exist yet because another acquirer just\n\t\t\t// created the lock dir and hasn't finished writing its pid file\n\t\t\t// (mkdir + writeFile is not atomic). Give it a short grace window\n\t\t\t// before concluding the owner crashed between mkdir and writeFile.\n\t\t\ttry {\n\t\t\t\tconst stat = await fsp.stat(this.dirPath);\n\t\t\t\treturn Date.now() - stat.mtimeMs > LOCK_STALE_GRACE_MS;\n\t\t\t} catch {\n\t\t\t\t// Lock dir disappeared concurrently (e.g. released mid-check) —\n\t\t\t\t// not stale, just gone; the caller's next mkdir will succeed.\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tconst pid = Number.parseInt(pidStr.trim(), 10);\n\t\tif (!Number.isFinite(pid) || pid <= 0) return true; // malformed pid file → treat as stale\n\t\treturn !isProcessAlive(pid);\n\t}\n\n\tasync release(): Promise<void> {\n\t\tif (!this.acquired) return;\n\t\tthis.acquired = false;\n\t\tawait fsp.rm(this.dirPath, { recursive: true, force: true });\n\t}\n}\n\nexport class IdentityStore {\n\tprivate readonly filePath: string;\n\tprivate readonly backend: IdentityFileBackend;\n\tprivate readonly hooks: IdentityStoreHooks;\n\tprivate readonly lockTimeoutMs: number;\n\tprivate readonly lockRetryMs: number;\n\n\tconstructor(opts: IdentityStoreOptions = {}) {\n\t\tthis.filePath = opts.filePath ?? getDeviceJsonPath();\n\t\tthis.backend = opts.backend ?? new FsIdentityFileBackend();\n\t\tthis.hooks = opts.hooks ?? {};\n\t\tthis.lockTimeoutMs = opts.lockTimeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;\n\t\tthis.lockRetryMs = opts.lockRetryMs ?? DEFAULT_LOCK_RETRY_MS;\n\t}\n\n\t/** Absolute path to the underlying JSON file (test seam). */\n\tgetFilePath(): string {\n\t\treturn this.filePath;\n\t}\n\n\t/** True when the JSON file already exists on disk. */\n\tasync exists(): Promise<boolean> {\n\t\treturn this.backend.exists(this.filePath);\n\t}\n\n\t/** Read the persisted identity; returns `null` when no identity is stored. */\n\tasync read(): Promise<PersistedDeviceIdentity | null> {\n\t\treturn this.backend.read(this.filePath);\n\t}\n\n\t/**\n\t * Atomically write the given identity. Concurrent writers are\n\t * serialized via the file lock; the read-modify-write happens\n\t * inside the lock so callers can't see a partial state.\n\t */\n\tasync write(next: PersistedDeviceIdentity): Promise<AtomicWriteResult> {\n\t\tawait this.hooks.beforeWrite?.(next);\n\t\tconst lock = new FileLock(this.filePath, this.lockTimeoutMs, this.lockRetryMs);\n\t\tawait lock.acquire();\n\t\ttry {\n\t\t\tconst stamped: PersistedDeviceIdentity = {\n\t\t\t\t...next,\n\t\t\t\tversion: DEVICE_JSON_SCHEMA_VERSION,\n\t\t\t};\n\t\t\tconst result = await this.backend.write(this.filePath, stamped);\n\t\t\tawait this.hooks.afterWrite?.(stamped);\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tawait lock.release();\n\t\t}\n\t}\n\n\t/**\n\t * Read-modify-write under the same lock. The mutator receives the\n\t * current identity (or `null` on first call) and returns the\n\t * replacement. Throwing inside the mutator aborts the write.\n\t */\n\tasync mutate<T>(\n\t\tmutator: (\n\t\t\tcurrent: PersistedDeviceIdentity | null\n\t\t) => Promise<{ next: PersistedDeviceIdentity; result?: T }>\n\t): Promise<{ result: T | undefined; written: PersistedDeviceIdentity }> {\n\t\tconst lock = new FileLock(this.filePath, this.lockTimeoutMs, this.lockRetryMs);\n\t\tawait lock.acquire();\n\t\ttry {\n\t\t\tconst current = await this.backend.read(this.filePath);\n\t\t\tconst { next, result } = await mutator(current);\n\t\t\tconst stamped: PersistedDeviceIdentity = {\n\t\t\t\t...next,\n\t\t\t\tversion: DEVICE_JSON_SCHEMA_VERSION,\n\t\t\t};\n\t\t\tawait this.hooks.beforeWrite?.(stamped);\n\t\t\tawait this.backend.write(this.filePath, stamped);\n\t\t\tawait this.hooks.afterWrite?.(stamped);\n\t\t\treturn { result, written: stamped };\n\t\t} finally {\n\t\t\tawait lock.release();\n\t\t}\n\t}\n\n\t/** Wipe the persisted identity (used by `device.enroll --force`). */\n\tasync clear(): Promise<void> {\n\t\tawait this.backend.delete(this.filePath);\n\t}\n\n\t/**\n\t * Resolve the installation metadata for the current machine.\n\t * Pure helper — no I/O, just `os.*` calls.\n\t */\n\tresolveInstallationMetadata(): Pick<\n\t\tPersistedDeviceIdentity,\n\t\t\"installationId\" | \"machineId\" | \"platform\"\n\t> {\n\t\tconst machineId = os.hostname();\n\t\tconst platform = os.platform();\n\t\t// installationId is derived by `InstallationId.ts` — pass the\n\t\t// caller's already-computed value via `material` so we don't\n\t\t// recompute the SHA twice in a row.\n\t\treturn {\n\t\t\tinstallationId: \"\", // intentionally empty; caller fills via deriveInstallationId()\n\t\t\tmachineId,\n\t\t\tplatform,\n\t\t};\n\t}\n\n\t/**\n\t * Ensure the parent directory exists (`~/.serviceme/`). Idempotent.\n\t * Useful when the bootstrap phase5 placeholder wasn't run yet.\n\t */\n\tasync ensureHome(): Promise<void> {\n\t\tawait fsp.mkdir(getServicemeHome(), { recursive: true });\n\t\tawait fsp.mkdir(path.dirname(this.filePath), { recursive: true });\n\t}\n}\n","/**\n * Internal types for the device domain.\n *\n * These types are NOT re-exported from the protocol package — they are\n * implementation details of the IdentityStore + Enroller. Public data\n * models (the bridge wire shape) live in `@serviceme/devtools-protocol`'s\n * `device.ts`.\n *\n * Refs:\n * - 4.功能规划.md §2.2 — `device/types.ts`\n */\n\nimport type { DeviceBindingState } from \"@serviceme/devtools-protocol\";\n\n/**\n * Schema version of the on-disk `device.json` file. Bumped when the\n * shape changes incompatibly. IdentityStore checks this on read and\n * either migrates (versions ≤ 1) or refuses (versions > supported).\n */\nexport const DEVICE_JSON_SCHEMA_VERSION = 1;\n\n/** Internal representation of the persisted identity file. */\nexport interface PersistedDeviceIdentity {\n\tversion: number;\n\t/** Stable per-machine id (UUID v4 shape) — survives secret rotates. */\n\tinstallationId: string;\n\t/** Raw `os.hostname()` for diagnostics. */\n\tmachineId: string;\n\t/** Platform string (e.g. \"darwin\"). */\n\tplatform: string;\n\t/** Optional hostname override for environments where `os.hostname()` is unstable. */\n\thostname?: string;\n\t/** Public, non-secret id returned by the server. 32-char hex. */\n\tpublicId: string;\n\t/** Monotonic secret version counter, starts at 1 after first enroll. */\n\tsecretVersion: number;\n\t/** Current binding state — drives the re-enroll matrix. */\n\tbindingState: DeviceBindingState;\n\t/** HMAC secret (32 bytes hex-encoded = 64 chars). Persisted per `device-header-spec.md` §3.1. */\n\tdeviceSecret: string;\n\t/** Optional: previous secret retained during the grace window (rotation). */\n\tpreviousDeviceSecret?: string;\n\t/** Optional: ISO timestamp at which the previous secret stops being accepted. */\n\tpreviousSecretExpiresAt?: string;\n\t/** ISO timestamp of the most recent successful enroll / rotate. */\n\tlastEnrollAt: string;\n\t/** Optional ISO timestamp of the most recent server sync. */\n\tlastSyncAt?: string;\n\t/** Optional human-readable message for the last sync error. */\n\tlastSyncError?: string;\n}\n\n/** Result of a single atomic write. */\nexport interface AtomicWriteResult {\n\tbytesWritten: number;\n\t/** Path to the temp file (post-rename it no longer exists; useful for diagnostics). */\n\ttmpPath: string;\n}\n\n/** Hook called before/after every identity write — used by tests to assert concurrency safety. */\nexport interface IdentityStoreHooks {\n\tbeforeWrite?: (next: PersistedDeviceIdentity) => void | Promise<void>;\n\tafterWrite?: (next: PersistedDeviceIdentity) => void | Promise<void>;\n}\n","/**\n * DeviceCore — Main entry for the device domain.\n *\n * Aggregates `IdentityStore` + `Enroller` + signer helpers into a single\n * surface that the CLI / Extension / Bridge can call. Pure orchestration\n * — no HTTP of its own (the actual `POST /api/v1/devices/enroll` lives\n * behind `EnrollerOptions.enrollRequest`, wired in Phase 5.4).\n *\n * Refs:\n * - 4.功能规划.md §2.2 — `DeviceCore.ts 主入口`\n * - ADL-003 — data model in `@serviceme/devtools-protocol`\n * - `docs/architecture/phase-5-device-header-spec.md` §3.1\n */\n\nimport type {\n\tDeviceEnrollResult,\n\tDeviceIdentityState,\n\tDeviceMetadata,\n\tDeviceRotateSecretResult,\n\tDeviceStatus,\n} from \"@serviceme/devtools-protocol\";\nimport { buildSignedHeaders, DeviceAuthHeaders, type DeviceSignedHeaders } from \"./deviceAuth\";\nimport { Enroller, type EnrollRequestFn } from \"./Enroller\";\nimport { IdentityStore } from \"./IdentityStore\";\nimport { deriveInstallationId } from \"./InstallationId\";\nimport type { PersistedDeviceIdentity } from \"./types\";\n\nexport interface DeviceCoreOptions {\n\tidentityStore?: IdentityStore;\n\tenrollRequest?: EnrollRequestFn;\n\tnow?: () => Date;\n\t/** Optional override for `deriveInstallationId` (used by tests for determinism). */\n\tresolveInstallationId?: () => string;\n}\n\nexport class DeviceCore {\n\tprivate readonly identity: IdentityStore;\n\tprivate readonly enroller: Enroller;\n\tprivate readonly resolveInstallationId: () => string;\n\n\tconstructor(opts: DeviceCoreOptions = {}) {\n\t\tthis.identity = opts.identityStore ?? new IdentityStore();\n\t\tthis.enroller = new Enroller({\n\t\t\tidentityStore: this.identity,\n\t\t\tenrollRequest: opts.enrollRequest,\n\t\t\tnow: opts.now,\n\t\t});\n\t\tthis.resolveInstallationId = opts.resolveInstallationId ?? deriveInstallationId;\n\t}\n\n\t/** Read-only snapshot of the device status (matches `device.status` wire shape). */\n\tasync status(): Promise<DeviceStatus> {\n\t\tconst stored = await this.identity.read();\n\t\treturn stored\n\t\t\t? {\n\t\t\t\t\tbindingState: stored.bindingState,\n\t\t\t\t\tidentity: projectIdentity(stored),\n\t\t\t\t\tmetadata: projectMetadata(stored),\n\t\t\t\t\tlastSyncAt: stored.lastSyncAt,\n\t\t\t\t\tlastSyncError: stored.lastSyncError,\n\t\t\t\t}\n\t\t\t: { bindingState: \"anonymous\" };\n\t}\n\n\t/** Enroll (or re-enroll) the device. */\n\tasync enroll(opts: { force?: boolean; requireAuth?: boolean } = {}): Promise<DeviceEnrollResult> {\n\t\treturn this.enroller.enroll(opts);\n\t}\n\n\t/** Wait for any in-flight enrollment to finish. Use before `buildSignedHeaders` so that a concurrent `syncDeviceInfo` enrollment has time to write the identity to the store. */\n\tasync waitForEnrollment(): Promise<void> {\n\t\tawait this.enroller.waitForEnrollment();\n\t}\n\n\t/** True when an enrollment is currently in-flight. Used by callers to skip triggering a competing enrollment. */\n\tisEnrolling(): boolean {\n\t\treturn this.enroller.isEnrolling();\n\t}\n\n\t/** Rotate the HMAC secret while keeping the previous one for the grace window. */\n\tasync rotateSecret(opts: { gracePeriodDays?: number } = {}): Promise<DeviceRotateSecretResult> {\n\t\tconst result = await this.enroller.rotateSecret(opts);\n\t\tconst stored = await this.identity.read();\n\t\treturn {\n\t\t\t...result,\n\t\t\tgracePeriodEndsAt: stored?.previousSecretExpiresAt,\n\t\t};\n\t}\n\n\t/** Build the canonical 5-header map for an outbound signed request. */\n\tasync buildSignedHeaders(input: {\n\t\tmethod: string;\n\t\tpath: string;\n\t\tbody: string;\n\t}): Promise<DeviceSignedHeaders | null> {\n\t\tconst stored = await this.identity.read();\n\t\tif (!stored) return null;\n\t\treturn buildSignedHeaders({\n\t\t\tmethod: input.method,\n\t\t\tpath: input.path,\n\t\t\tbody: input.body,\n\t\t\tpublicId: stored.publicId,\n\t\t\tdeviceSecret: stored.deviceSecret,\n\t\t\tsecretVersion: stored.secretVersion,\n\t\t});\n\t}\n\n\t/** Raw stored identity (CLI/extension internal use). Test seam too. */\n\tasync readIdentity(): Promise<PersistedDeviceIdentity | null> {\n\t\treturn this.identity.read();\n\t}\n\n\t/** Wipe the local identity (the `--force` path before re-enroll). */\n\tasync clear(): Promise<void> {\n\t\tawait this.identity.clear();\n\t}\n\n\t/** Mark the device as claimed (called by the bridge after a successful claim). */\n\tasync markClaimed(): Promise<void> {\n\t\tawait this.enroller.markClaimed();\n\t}\n\n\t/** Mark the device as expired (server returned an expiry response). */\n\tasync markExpired(): Promise<void> {\n\t\tawait this.enroller.markExpired();\n\t}\n\n\t/** Expose the identity store (CLI uses it for direct file access in tests). */\n\tgetIdentityStore(): IdentityStore {\n\t\treturn this.identity;\n\t}\n\n\t/** Expose the enroller (CLI uses it for state inspection). */\n\tgetEnroller(): Enroller {\n\t\treturn this.enroller;\n\t}\n\n\t/** Header name constants — re-exported from `deviceAuth.ts`. */\n\tgetHeaderNames(): typeof DeviceAuthHeaders {\n\t\treturn DeviceAuthHeaders;\n\t}\n\n\t/** Compute the installation id for the current machine. */\n\tgetInstallationId(): string {\n\t\treturn this.resolveInstallationId();\n\t}\n}\n\nfunction projectIdentity(stored: PersistedDeviceIdentity): DeviceIdentityState {\n\treturn {\n\t\tpublicId: stored.publicId,\n\t\tsecretVersion: stored.secretVersion,\n\t\tbindingState: stored.bindingState,\n\t};\n}\n\nfunction projectMetadata(stored: PersistedDeviceIdentity): DeviceMetadata {\n\treturn {\n\t\tinstallationId: stored.installationId,\n\t\tmachineId: stored.machineId,\n\t\tplatform: stored.platform,\n\t\thostname: stored.hostname,\n\t};\n}\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport { getAgentDraftsDir, getSkillDraftsDir } from \"../paths/userHome\";\nimport { extractFrontmatter } from \"../skill-store/index\";\nimport type { SkillFile, SkillKind } from \"../skill-store/types\";\n\n/**\n * Skill & Agent v2 — Drafts (M4)\n *\n * Drafts are offline WIP copies of skills/agents that the user is\n * editing locally before submitting. They live under\n * `~/.serviceme/drafts/{skills,agents}/<draft-id>/` and have the same\n * file layout as a published entry: SKILL.md (or AGENT.md) plus any\n * extra files. The `SubmitClient` (also M4) reads a draft's files and\n * sends them to the server's validate endpoint before pushing.\n *\n * Draft ids are short random hex strings (16 hex chars / 8 bytes of\n * entropy) — long enough to be globally unique, short enough to be\n * pasteable. They are NOT user-meaningful; clients surface a `name`\n * from the draft's frontmatter instead.\n *\n * Persistence is atomic per-file (write to `.tmp-xxx` + rename).\n * The whole draft directory is never partially committed — failed\n * writes leave a `.tmp-xxx` orphan that the next save/cleanup can\n * sweep.\n *\n * @see docs/architecture/skill-agent-v2-repo.md §2.1 (drafts layout),\n * §5.7 SubmitClient (consumer).\n */\n\nexport interface DraftSummary {\n\t/** Random id (16 hex chars). */\n\tid: string;\n\tkind: SkillKind;\n\t/** Path to the manifest file (SKILL.md or AGENT.md). */\n\tmanifestPath: string;\n\t/** Path to the directory containing the draft's files. */\n\tdir: string;\n\t/** Best-effort name from the frontmatter (empty string when absent). */\n\tname: string;\n\t/** Best-effort description from the frontmatter (empty string when absent). */\n\tdescription: string;\n\t/** ISO timestamp of the manifest file's mtime. */\n\tmodifiedAt: string;\n}\n\nexport interface DraftDetail extends DraftSummary {\n\tfiles: SkillFile[];\n}\n\nexport interface SaveDraftOptions {\n\tkind: SkillKind;\n\t/** Existing draft id; omit to create a new draft. */\n\tid?: string;\n\t/** Files to write. Must include a manifest (SKILL.md or AGENT.md). */\n\tfiles: SkillFile[];\n}\n\n/** Sentinel errors. */\nexport class DraftsError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"DraftsError\";\n\t}\n}\nexport class DraftNotFoundError extends DraftsError {\n\tconstructor(\n\t\tpublic readonly kind: SkillKind,\n\t\tpublic readonly id: string\n\t) {\n\t\tsuper(`draft not found: ${kind}/${id}`);\n\t\tthis.name = \"DraftNotFoundError\";\n\t}\n}\nexport class InvalidDraftError extends DraftsError {\n\tconstructor(message: string) {\n\t\tsuper(`invalid draft: ${message}`);\n\t\tthis.name = \"InvalidDraftError\";\n\t}\n}\n\n/**\n * Generate a short random hex id. Uses Node's `crypto.randomBytes` so\n * the output is unpredictable. 8 bytes = 16 hex chars = 64 bits of\n * entropy. We don't promise uniqueness (collisions exist with\n * astronomically low probability) — callers must treat ids as opaque.\n */\nexport function generateDraftId(): string {\n\treturn crypto.randomBytes(8).toString(\"hex\");\n}\n\n/**\n * Resolve the on-disk directory for a draft of a given kind + id.\n * Exported for tests + the SubmitClient which writes the committed\n * files into the repo.\n */\nexport function resolveDraftDir(kind: SkillKind, id: string): string {\n\tconst root = kind === \"skill\" ? getSkillDraftsDir() : getAgentDraftsDir();\n\treturn path.join(root, id);\n}\n\n/**\n * Persistent draft store backed by the local filesystem. Pure local —\n * no network, no git, no DB.\n */\nexport class DraftsStore {\n\t/**\n\t * List every draft of the given kind. Returns [] when the drafts\n\t * directory doesn't exist (cold start, never-saved state).\n\t */\n\tasync list(kind: SkillKind): Promise<DraftSummary[]> {\n\t\tconst root = kind === \"skill\" ? getSkillDraftsDir() : getAgentDraftsDir();\n\t\tlet names: string[];\n\t\ttry {\n\t\t\tnames = await fs.readdir(root);\n\t\t} catch (err) {\n\t\t\tif ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n\t\t\tthrow err;\n\t\t}\n\t\tconst out: DraftSummary[] = [];\n\t\tfor (const name of names) {\n\t\t\tconst summary = await this.tryReadSummary(kind, name);\n\t\t\tif (summary) out.push(summary);\n\t\t}\n\t\t// Most-recently-modified first.\n\t\tout.sort((a, b) => (a.modifiedAt < b.modifiedAt ? 1 : -1));\n\t\treturn out;\n\t}\n\n\t/** Single draft summary (manifest + metadata). Returns null when missing. */\n\tasync tryReadSummary(kind: SkillKind, id: string): Promise<DraftSummary | null> {\n\t\tconst dir = resolveDraftDir(kind, id);\n\t\tconst manifestFilename = kind === \"skill\" ? \"SKILL.md\" : \"AGENT.md\";\n\t\tconst manifestPath = path.join(dir, manifestFilename);\n\t\tlet stat: import(\"node:fs\").Stats;\n\t\ttry {\n\t\t\tstat = await fs.stat(manifestPath);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t\tlet content = \"\";\n\t\ttry {\n\t\t\tcontent = await fs.readFile(manifestPath, \"utf8\");\n\t\t} catch {\n\t\t\t// Manifest exists but unreadable — surface as empty name/desc.\n\t\t}\n\t\tconst parsed = extractFrontmatter(content);\n\t\tconst name = typeof parsed?.data.name === \"string\" ? parsed.data.name : \"\";\n\t\tconst description = typeof parsed?.data.description === \"string\" ? parsed.data.description : \"\";\n\t\treturn {\n\t\t\tid,\n\t\t\tkind,\n\t\t\tmanifestPath,\n\t\t\tdir,\n\t\t\tname,\n\t\t\tdescription,\n\t\t\tmodifiedAt: stat.mtime.toISOString(),\n\t\t};\n\t}\n\n\t/** Single draft detail (summary + all files). Throws when missing. */\n\tasync get(kind: SkillKind, id: string): Promise<DraftDetail> {\n\t\tconst summary = await this.tryReadSummary(kind, id);\n\t\tif (!summary) throw new DraftNotFoundError(kind, id);\n\t\tconst files = await this.getFiles(kind, id);\n\t\treturn { ...summary, files };\n\t}\n\n\t/** All files in a draft (manifest + extras), recursively. */\n\tasync getFiles(kind: SkillKind, id: string): Promise<SkillFile[]> {\n\t\tconst dir = resolveDraftDir(kind, id);\n\t\tconst out: SkillFile[] = [];\n\t\tawait collectRecursive(dir, dir, out);\n\t\tout.sort((a, b) => {\n\t\t\tconst aIsManifest = a.path === \"SKILL.md\" || a.path === \"AGENT.md\";\n\t\t\tconst bIsManifest = b.path === \"SKILL.md\" || b.path === \"AGENT.md\";\n\t\t\tif (aIsManifest && !bIsManifest) return -1;\n\t\t\tif (bIsManifest && !aIsManifest) return 1;\n\t\t\treturn a.path.localeCompare(b.path);\n\t\t});\n\t\treturn out;\n\t}\n\n\t/**\n\t * Create-or-update a draft. Returns the (possibly new) draft id.\n\t * Throws `InvalidDraftError` when the file list does not include\n\t * a manifest.\n\t */\n\tasync save(opts: SaveDraftOptions): Promise<string> {\n\t\tconst manifestFilename = opts.kind === \"skill\" ? \"SKILL.md\" : \"AGENT.md\";\n\t\tif (!opts.files.some((f) => f.path === manifestFilename)) {\n\t\t\tthrow new InvalidDraftError(`${manifestFilename} is required`);\n\t\t}\n\t\tconst id = opts.id ?? generateDraftId();\n\t\tconst dir = resolveDraftDir(opts.kind, id);\n\t\t// Clean the directory so a re-save with fewer files actually\n\t\t// removes the old ones (otherwise stale files linger).\n\t\tawait fs.rm(dir, { recursive: true, force: true });\n\t\tawait fs.mkdir(dir, { recursive: true });\n\t\t// Write every file atomically (tmp + rename).\n\t\tfor (const f of opts.files) {\n\t\t\tconst full = path.join(dir, f.path);\n\t\t\tawait fs.mkdir(path.dirname(full), { recursive: true });\n\t\t\tconst tmp = `${full}.${process.pid}.${Date.now()}.tmp`;\n\t\t\tawait fs.writeFile(tmp, f.content, \"utf8\");\n\t\t\tawait fs.rename(tmp, full);\n\t\t}\n\t\t// Sweep stale .tmp-* leftovers (in case a previous save crashed).\n\t\tawait this.sweepTmp(dir);\n\t\treturn id;\n\t}\n\n\t/** Delete a draft. ENOENT is silently ignored (idempotent). */\n\tasync delete(kind: SkillKind, id: string): Promise<void> {\n\t\tconst dir = resolveDraftDir(kind, id);\n\t\ttry {\n\t\t\tawait fs.rm(dir, { recursive: true, force: true });\n\t\t} catch (err) {\n\t\t\tif ((err as NodeJS.ErrnoException).code === \"ENOENT\") return;\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/** Best-effort cleanup of `.tmp-*` orphan files in the draft dir. */\n\tprivate async sweepTmp(dir: string): Promise<void> {\n\t\tlet names: string[];\n\t\ttry {\n\t\t\tnames = await fs.readdir(dir);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const n of names) {\n\t\t\tif (n.endsWith(\".tmp\")) {\n\t\t\t\tawait fs.rm(path.join(dir, n), { force: true }).catch(() => undefined);\n\t\t\t}\n\t\t}\n\t}\n}\n\nasync function collectRecursive(absDir: string, root: string, out: SkillFile[]): Promise<void> {\n\tlet dirents: import(\"node:fs\").Dirent[];\n\ttry {\n\t\tdirents = await fs.readdir(absDir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const d of dirents) {\n\t\tif (d.name.endsWith(\".tmp\")) continue;\n\t\tconst full = path.join(absDir, d.name);\n\t\tif (d.isDirectory()) {\n\t\t\tawait collectRecursive(full, root, out);\n\t\t} else if (d.isFile()) {\n\t\t\tconst content = await fs.readFile(full, \"utf8\");\n\t\t\tout.push({ path: path.relative(root, full), content });\n\t\t}\n\t}\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { DEFAULT_REPO_LAYOUT, loadRepoLayout, type RepoLayoutDescriptor } from \"../repo-layout\";\nimport type { SkillDetail, SkillEntry, SkillFile, SkillKind } from \"./types\";\nimport { SkillNotFoundError } from \"./types\";\n\n/**\n * Minimal frontmatter reader — same shape as the extension's\n * `WorkspaceSkillsInitializationService.extractFrontmatter`. We avoid\n * pulling a YAML dep into core; SKILL.md / AGENT.md in the wild use\n * a strict subset (top-level `key: value` pairs delimited by `---`).\n * Multi-line block scalars (`>`, `|-`, `|`) are folded into a single\n * string value — the very common `description: >` form would\n * otherwise surface the bare `>` indicator as the whole description.\n */\nexport function extractFrontmatter(\n\traw: string\n): { data: Record<string, unknown>; body: string } | null {\n\tif (typeof raw !== \"string\") return null;\n\tconst match = raw.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n\tif (!match) return null;\n\tconst yamlBody = match[1] ?? \"\";\n\tconst body = match[2] ?? \"\";\n\tconst data: Record<string, unknown> = {};\n\tconst lines = yamlBody.split(/\\r?\\n/);\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst line = lines[i] ?? \"\";\n\t\tif (line.trim().length === 0) continue;\n\t\tif (line.trim().startsWith(\"#\")) continue;\n\t\tconst kv = line.match(/^([a-zA-Z_][\\w-]*)\\s*:\\s*(.*)$/);\n\t\tif (!kv?.[1]) continue;\n\t\tlet value: unknown = (kv[2] ?? \"\").trim();\n\t\t// Block scalar indicators: fold the following more-indented\n\t\t// lines into one value (blank separators become newlines for\n\t\t// `|`, spaces for `>`; we keep it simple and always fold with\n\t\t// spaces, which is right for descriptions).\n\t\tif (typeof value === \"string\" && /^[>|][+-]?$/.test(value)) {\n\t\t\tconst folded: string[] = [];\n\t\t\tlet j = i + 1;\n\t\t\tfor (; j < lines.length; j++) {\n\t\t\t\tconst next = lines[j] ?? \"\";\n\t\t\t\tif (next.trim().length === 0) {\n\t\t\t\t\t// A blank line only belongs to the scalar if a later\n\t\t\t\t\t// more-indented line follows; otherwise it ends it.\n\t\t\t\t\tconst after = lines[j + 1];\n\t\t\t\t\tif (after !== undefined && /^[ \\t]/.test(after)) {\n\t\t\t\t\t\tfolded.push(\"\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!/^[ \\t]/.test(next)) break; // dedent ends the scalar\n\t\t\t\tfolded.push(next.trim());\n\t\t\t}\n\t\t\ti = j - 1;\n\t\t\t// Collapse whitespace runs so blank separators don't leave\n\t\t\t// double spaces (display text, not YAML fidelity).\n\t\t\tvalue = folded.join(\" \").replace(/\\s+/g, \" \").trim();\n\t\t\tdata[kv[1]] = value;\n\t\t\tcontinue;\n\t\t}\n\t\tif (typeof value === \"string\") {\n\t\t\tif (\n\t\t\t\t(value.startsWith('\"') && value.endsWith('\"')) ||\n\t\t\t\t(value.startsWith(\"'\") && value.endsWith(\"'\"))\n\t\t\t) {\n\t\t\t\tvalue = value.slice(1, -1);\n\t\t\t}\n\t\t}\n\t\tdata[kv[1]] = value;\n\t}\n\treturn { data, body };\n}\n\nexport type { SkillDetail, SkillEntry, SkillFile, SkillKind } from \"./types\";\n/**\n * Skill & Agent v2 — SkillStore (M4)\n *\n * Scans the local `~/.serviceme/repos/<id>/` tree to produce a unified\n * list of skills + agents. Local-only — no git, no network. Spec §5.5.\n *\n * Layout normalization (spec §12):\n * 1. **Standard layout** — `skills/<name>/SKILL.md` or\n * `agents/<name>/AGENT.md` under the repo root.\n * 2. **Anthropic flat layout** — `<name>/SKILL.md` directly under\n * the repo root (the `skills/` prefix is omitted).\n * 3. **awesome-copilot style** — root contains README + subdirs\n * that are skills/agents/instructions; we ignore README and\n * any dir without a manifest file.\n * 4. **Flat agent files** — a directory (commonly `agents/` or\n * `agents/official/`) full of `<name>.agent.md` files, one per\n * agent, with NO per-agent subdirectory. This is the prevailing\n * real-world convention (VS Code custom chat agents, GitHub's\n * awesome-copilot, and our own official ms-skills repo all ship\n * agents this way) — unlike skills, agents in the wild are\n * essentially never a `<name>/AGENT.md` directory pair. Detected\n * unconditionally, no `.serviceme-repo.json` opt-in required.\n *\n * Implementation: walk every directory under the repo root, check for\n * SKILL.md or AGENT.md, register if present. We skip the repo root\n * itself (no manifest lives at the top), `.git/`, and `node_modules/`.\n * Flat `*.agent.md` files are checked alongside directories at every\n * level of the walk.\n */\n// Re-export the error class + shared types so callers can\n// `import { SkillNotFoundError, SkillFile } from \"...\"`.\nexport { SkillNotFoundError } from \"./types\";\n\nexport class SkillStore {\n\tprivate readonly repoRoots: Map<string, string>;\n\t/**\n\t * Per-instance memo of repo walks. The catalog walk is a recursive\n\t * readdir over the whole checkout — the dominant cost of every\n\t * list/get — so results (and the in-flight promise) are reused for\n\t * the store's lifetime. The handler that owns long-lived stores\n\t * bounds staleness by rebuilding the store on a short TTL; within\n\t * one store instance the tree is treated as immutable.\n\t */\n\tprivate readonly entriesMemo = new Map<string, Promise<SkillEntry[]>>();\n\n\tconstructor(opts: {\n\t\trepos: ReadonlyArray<{ id: string; rootPath: string }>;\n\t}) {\n\t\tthis.repoRoots = new Map();\n\t\tfor (const r of opts.repos) this.repoRoots.set(r.id, r.rootPath);\n\t}\n\n\t/** All skills + agents across all registered repos. Repos are walked\n\t * concurrently — one slow checkout no longer stretches the total. */\n\tasync listAll(): Promise<SkillEntry[]> {\n\t\tconst perRepo = await Promise.all(\n\t\t\t[...this.repoRoots.keys()].map((repoId) => this.listByRepo(repoId))\n\t\t);\n\t\treturn perRepo.flat();\n\t}\n\n\t/** All skills + agents under a single repo. */\n\tasync listByRepo(repoId: string): Promise<SkillEntry[]> {\n\t\tconst entries = await this.listByRepoCached(repoId);\n\t\t// Copy so memoized state can't be corrupted by caller mutations.\n\t\treturn [...entries];\n\t}\n\n\t/** Single entry detail (manifest + all files inside its dir). */\n\tasync get(repoId: string, name: string): Promise<SkillDetail> {\n\t\tconst entries = await this.listByRepoCached(repoId);\n\t\tconst found = entries.find((e) => e.name === name);\n\t\tif (!found) throw new SkillNotFoundError(repoId, name);\n\t\tconst files = await collectEntryFiles(found);\n\t\treturn { ...found, files };\n\t}\n\n\t/** All files inside a single entry's directory (manifest + extras). */\n\tasync getFiles(repoId: string, name: string): Promise<SkillFile[]> {\n\t\tconst entries = await this.listByRepoCached(repoId);\n\t\tconst found = entries.find((e) => e.name === name);\n\t\tif (!found) throw new SkillNotFoundError(repoId, name);\n\t\treturn collectEntryFiles(found);\n\t}\n\n\t/**\n\t * Memoized walk. Sharing the promise collapses concurrent list/get\n\t * calls over the same repo into a single traversal; a rejected walk\n\t * is dropped so the next call retries from disk.\n\t */\n\tprivate listByRepoCached(repoId: string): Promise<SkillEntry[]> {\n\t\tconst memoized = this.entriesMemo.get(repoId);\n\t\tif (memoized) return memoized;\n\t\tconst walk = this.walkRepo(repoId).catch((error: unknown) => {\n\t\t\tthis.entriesMemo.delete(repoId);\n\t\t\tthrow error;\n\t\t});\n\t\tthis.entriesMemo.set(repoId, walk);\n\t\treturn walk;\n\t}\n\n\tprivate async walkRepo(repoId: string): Promise<SkillEntry[]> {\n\t\tconst root = this.repoRoots.get(repoId);\n\t\tif (!root) return [];\n\t\tconst layout = (await loadRepoLayout(root)) ?? DEFAULT_REPO_LAYOUT;\n\t\tconst entries: SkillEntry[] = [];\n\t\tawait walkForEntries(root, repoId, entries, layout);\n\t\treturn entries;\n\t}\n}\n\n/** All files inside a single entry (manifest first, then alphabetical). */\nasync function collectEntryFiles(found: SkillEntry): Promise<SkillFile[]> {\n\tconst out: SkillFile[] = [];\n\t// Flat single-file entries (e.g. `agents/foo.agent.md`) have `dir`\n\t// pointing at the manifest file itself, not a directory — there's\n\t// no sibling files to collect, just the one manifest.\n\tif (found.dir === found.manifestPath) {\n\t\tconst content = await fs.readFile(found.manifestPath, \"utf8\");\n\t\treturn [{ path: path.basename(found.manifestPath), content }];\n\t}\n\tawait collectFilesRecursive(found.dir, found.dir, out);\n\t// Sort for determinism (manifest first, then alphabetical)\n\tout.sort((a, b) => {\n\t\tconst aIsManifest = a.path === \"SKILL.md\" || a.path === \"AGENT.md\";\n\t\tconst bIsManifest = b.path === \"SKILL.md\" || b.path === \"AGENT.md\";\n\t\tif (aIsManifest && !bIsManifest) return -1;\n\t\tif (bIsManifest && !aIsManifest) return 1;\n\t\treturn a.path.localeCompare(b.path);\n\t});\n\treturn out;\n}\n\n// ─────────────────────────────────────────────────────────────────────\n// Internals\n// ─────────────────────────────────────────────────────────────────────\n\nconst SKIP_DIRS = new Set([\".git\", \"node_modules\", \".vscode\", \"dist\", \"build\", \"out\"]);\n\n/**\n * Directories that are never treated as candidate skill/agent parents\n * during the top-level catalog walk. `plugins/` is a distinct concept\n * from the skill/agent catalog itself — a plugin bundle\n * (`plugins/<scope>/<plugin-name>/{skills,agents}/...`) re-packages\n * ALREADY-cataloged official skills/agents (see the plugin's own\n * `catalog.json`, which lists them by name) purely for discovery\n * grouping. Walking into it produces duplicate `SkillEntry` values\n * with the SAME `repoId`+`name` as their real, top-level counterpart\n * (e.g. `skills/official/acreadiness-assess` AND\n * `plugins/official/acreadiness-cockpit/skills/acreadiness-assess`),\n * which breaks anything keyed on `repoId/name` (React list rendering,\n * the \"installed\" lookup, `SkillStore.get()`'s `.find()`).\n *\n * Scoped separately from `SKIP_DIRS` (used by both this walk AND\n * `collectFilesRecursive`) so an actual skill that happens to ship its\n * own `plugins/` asset folder still has that folder listed in its own\n * file detail view.\n */\nconst SKIP_TOP_LEVEL_SCAN_DIRS = new Set([...SKIP_DIRS, \"plugins\"]);\n\n/** Suffix that marks a standalone file as a flat agent manifest (no wrapping dir). */\nconst FLAT_AGENT_FILE_SUFFIX = \".agent.md\";\n\n/**\n * Recursive walk that yields SkillEntry values for any directory\n * containing a SKILL.md or AGENT.md file, PLUS any standalone\n * `<name>.agent.md` file (see class doc, layout style 4). The dir\n * itself is the \"entry root\" for directory-based entries; the\n * manifest file itself is the \"entry root\" for flat agent files.\n *\n * The optional `layout` descriptor (from `.serviceme-repo.json`)\n * widens the scan: included subdirs are walked even without a\n * manifest at the top level, and `treatFilesAsSkills` surfaces\n * `<name>.md` files inside them as skills (frontmatter parsed\n * from the file body).\n */\nasync function walkForEntries(\n\trootDir: string,\n\trepoId: string,\n\tout: SkillEntry[],\n\tlayout: RepoLayoutDescriptor = DEFAULT_REPO_LAYOUT\n): Promise<void> {\n\tlet dirents: import(\"node:fs\").Dirent[];\n\ttry {\n\t\tdirents = await fs.readdir(rootDir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\n\tfor (const d of dirents) {\n\t\tif (d.isFile() && d.name.endsWith(FLAT_AGENT_FILE_SUFFIX)) {\n\t\t\tconst filePath = path.join(rootDir, d.name);\n\t\t\tconst stat = await fs.stat(filePath);\n\t\t\tconst content = await fs.readFile(filePath, \"utf8\");\n\t\t\tconst parsed = extractFrontmatter(content);\n\t\t\tout.push({\n\t\t\t\trepoId,\n\t\t\t\tname: d.name.slice(0, -FLAT_AGENT_FILE_SUFFIX.length),\n\t\t\t\tkind: \"agent\",\n\t\t\t\tmanifestPath: filePath,\n\t\t\t\t// Sentinel: `dir === manifestPath` marks a flat single-file\n\t\t\t\t// entry (no directory of its own — see getFiles()).\n\t\t\t\tdir: filePath,\n\t\t\t\tfrontmatter: parsed?.data ?? {},\n\t\t\t\tmodifiedAt: stat.mtime.toISOString(),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (!d.isDirectory()) continue;\n\t\tif (SKIP_TOP_LEVEL_SCAN_DIRS.has(d.name)) continue;\n\t\tif (layout.exclude.includes(d.name)) continue;\n\t\tconst childDir = path.join(rootDir, d.name);\n\t\tconst kind = await detectKind(childDir);\n\t\tif (kind) {\n\t\t\tconst manifestFilename = kind === \"skill\" ? \"SKILL.md\" : \"AGENT.md\";\n\t\t\tconst manifestPath = path.join(childDir, manifestFilename);\n\t\t\tconst stat = await fs.stat(manifestPath);\n\t\t\tconst content = await fs.readFile(manifestPath, \"utf8\");\n\t\t\tconst parsed = extractFrontmatter(content);\n\t\t\tout.push({\n\t\t\t\trepoId,\n\t\t\t\tname: d.name,\n\t\t\t\tkind,\n\t\t\t\tmanifestPath,\n\t\t\t\tdir: childDir,\n\t\t\t\tfrontmatter: parsed?.data ?? {},\n\t\t\t\tmodifiedAt: stat.mtime.toISOString(),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\t// No manifest at the top — recurse, but ALSO check whether\n\t\t// the layout descriptor wants this subdir widened.\n\t\tawait walkForEntries(childDir, repoId, out, layout);\n\t\tif (layout.treatFilesAsSkills && layout.include.includes(d.name)) {\n\t\t\tawait surfaceFilesAsSkills(childDir, d.name, repoId, out);\n\t\t}\n\t}\n}\n\n/**\n * Walk `<includedSubdir>/<file>.md` and register each as a skill.\n * The `.md` file itself is the manifest — frontmatter (if any) is\n * parsed and surfaced. This is the awesome-copilot style: a\n * `prompts/` dir full of `<name>.md` files, no SKILL.md anywhere.\n */\nasync function surfaceFilesAsSkills(\n\tabsDir: string,\n\trelName: string,\n\trepoId: string,\n\tout: SkillEntry[]\n): Promise<void> {\n\tlet dirents: import(\"node:fs\").Dirent[];\n\ttry {\n\t\tdirents = await fs.readdir(absDir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const d of dirents) {\n\t\tif (!d.isFile()) continue;\n\t\tif (!d.name.endsWith(\".md\")) continue;\n\t\tconst filePath = path.join(absDir, d.name);\n\t\tconst stat = await fs.stat(filePath);\n\t\tconst content = await fs.readFile(filePath, \"utf8\");\n\t\tconst parsed = extractFrontmatter(content);\n\t\t// Entry name = \"<subdir>/<file>\" (with the .md stripped) so it\n\t\t// stays unique even when two included subdirs share filenames.\n\t\tconst entryName = `${relName}/${d.name.replace(/\\.md$/, \"\")}`;\n\t\tout.push({\n\t\t\trepoId,\n\t\t\tname: entryName,\n\t\t\tkind: \"skill\",\n\t\t\tmanifestPath: filePath,\n\t\t\tdir: absDir,\n\t\t\tfrontmatter: parsed?.data ?? {},\n\t\t\tmodifiedAt: stat.mtime.toISOString(),\n\t\t});\n\t}\n}\n\n/**\n * Returns the SkillKind of a directory if it contains a manifest file,\n * or `null` if it doesn't. Manifest precedence: SKILL.md wins if both\n * are present (defensive — the spec separates skills and agents into\n * different subdir trees, so this collision shouldn't happen in\n * well-formed repos).\n */\nasync function detectKind(dir: string): Promise<SkillKind | null> {\n\tconst [hasSkill, hasAgent] = await Promise.all([\n\t\tfs\n\t\t\t.access(path.join(dir, \"SKILL.md\"))\n\t\t\t.then(() => true)\n\t\t\t.catch(() => false),\n\t\tfs\n\t\t\t.access(path.join(dir, \"AGENT.md\"))\n\t\t\t.then(() => true)\n\t\t\t.catch(() => false),\n\t]);\n\tif (hasSkill) return \"skill\";\n\tif (hasAgent) return \"agent\";\n\treturn null;\n}\n\n/**\n * Walk a skill/agent directory recursively, pushing every file into\n * `out` with its path RELATIVE to the entry root. Skips `.git/`,\n * `node_modules/`, and the SKIP_DIRS set.\n */\nasync function collectFilesRecursive(\n\tabsDir: string,\n\tentryRoot: string,\n\tout: SkillFile[]\n): Promise<void> {\n\tlet dirents: import(\"node:fs\").Dirent[];\n\ttry {\n\t\tdirents = await fs.readdir(absDir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const d of dirents) {\n\t\tif (SKIP_DIRS.has(d.name)) continue;\n\t\tconst full = path.join(absDir, d.name);\n\t\tif (d.isDirectory()) {\n\t\t\tawait collectFilesRecursive(full, entryRoot, out);\n\t\t} else if (d.isFile()) {\n\t\t\tconst content = await fs.readFile(full, \"utf8\");\n\t\t\tout.push({ path: path.relative(entryRoot, full), content });\n\t\t}\n\t}\n}\n","/**\n * Spec §12 — Repo layout descriptor.\n *\n * Some third-party repos (awesome-copilot, composio) use a\n * non-default layout that mixes prompts / instructions / agents /\n * hooks / plugins at the root. The default SkillStore scan\n * (see ../skill-store/index.ts) only picks up directories that\n * contain a `SKILL.md` or `AGENT.md` manifest, so prompts stored\n * as `<subdir>/<name>.md` (no manifest) get missed.\n *\n * This module is the loader for the optional `.serviceme-repo.json`\n * marker file a repo author can drop in at the repo root to opt\n * into a wider scan. The schema:\n *\n * {\n * \"schema\": 1,\n * \"include\": [\"prompts\", \"instructions\"], // subdirs to treat as \"skill dirs\"\n * \"exclude\": [\"hooks\", \"plugins\"], // subdirs to skip\n * \"treatFilesAsSkills\": true // inside an included subdir, each .md file is a skill\n * }\n *\n * All four fields are optional. The defaults match the v0.2\n * walkForEntries behaviour (manifest-based, no marker, no\n * extensions), so adding the file is purely additive.\n *\n * @see docs/architecture/skill-agent-v2-repo.md §12\n */\n\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nconst MARKER_FILENAME = \".serviceme-repo.json\";\nconst SUPPORTED_SCHEMA = 1;\n\nexport interface RepoLayoutDescriptor {\n\t/** Schema version. Currently always 1. */\n\tschema: number;\n\t/** Subdirs (relative to repo root) to walk for entries. */\n\tinclude: string[];\n\t/** Subdirs (relative to repo root) to skip, even if a parent is included. */\n\texclude: string[];\n\t/**\n\t * When true, an included subdir's `<name>.md` files are\n\t * surfaced as skills (frontmatter is parsed from each file as\n\t * a stand-in for SKILL.md). When false (default), the\n\t * include list only widens the recursion — the manifest rule\n\t * still applies.\n\t */\n\ttreatFilesAsSkills: boolean;\n}\n\n/**\n * Default descriptor used when no `.serviceme-repo.json` is\n * present. The shape is the same as what an empty marker would\n * produce, so callers don't have to special-case the \"no marker\"\n * branch.\n */\nexport const DEFAULT_REPO_LAYOUT: RepoLayoutDescriptor = {\n\tschema: SUPPORTED_SCHEMA,\n\tinclude: [],\n\texclude: [],\n\ttreatFilesAsSkills: false,\n};\n\n/**\n * Try to load a `.serviceme-repo.json` from the given repo root.\n * Returns `DEFAULT_REPO_LAYOUT` (not undefined) when the file is\n * absent — callers branch on `include.length` / `treatFilesAsSkills`\n * to decide whether to widen the scan.\n *\n * Malformed markers (bad JSON, wrong schema) are surfaced as\n * `null` so callers can warn the user instead of silently\n * treating them as the default.\n */\nexport async function loadRepoLayout(repoRoot: string): Promise<RepoLayoutDescriptor | null> {\n\tconst markerPath = path.join(repoRoot, MARKER_FILENAME);\n\tlet raw: string;\n\ttry {\n\t\traw = await fs.readFile(markerPath, \"utf8\");\n\t} catch {\n\t\treturn DEFAULT_REPO_LAYOUT;\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (!parsed || typeof parsed !== \"object\") return null;\n\tconst obj = parsed as Record<string, unknown>;\n\n\tif (obj.schema !== SUPPORTED_SCHEMA) return null;\n\tif (!Array.isArray(obj.include) || obj.include.some((s) => typeof s !== \"string\")) {\n\t\treturn null;\n\t}\n\tif (!Array.isArray(obj.exclude) || obj.exclude.some((s) => typeof s !== \"string\")) {\n\t\treturn null;\n\t}\n\tif (typeof obj.treatFilesAsSkills !== \"boolean\") return null;\n\n\treturn {\n\t\tschema: SUPPORTED_SCHEMA,\n\t\tinclude: obj.include as string[],\n\t\texclude: obj.exclude as string[],\n\t\ttreatFilesAsSkills: obj.treatFilesAsSkills,\n\t};\n}\n\n/** Subdir name → entry kind override (rarely needed; default = manifest-driven). */\nexport type KindOverride = Map<string, \"skill\" | \"agent\">;\n","import { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n\tcreateServicemeError,\n\ttype EnvironmentCheckResult,\n\tKNOWN_ENVIRONMENT_TOOLS,\n\ttype KnownEnvironmentTool,\n\ttype ToolCheckResult,\n} from \"@serviceme/devtools-protocol\";\nimport { runCommand } from \"../process/runCommand\";\n\nconst DEFAULT_TOOL_CHECK_TIMEOUT_MS = 5_000;\nconst TOOL_CHECK_TIMEOUT_MS: Partial<Record<KnownEnvironmentTool, number>> = {\n\tnuget: 12_000,\n\tnvm: 8_000,\n\tdotnet: 8_000,\n\t// nvm4w/npm shims and globally-installed .cmd tools (pnpm, nrm) are slow to\n\t// resolve on cold PATHs. Give them extra headroom so the version probe\n\t// doesn't fall through to the generic 5s default.\n\tnpm: 15_000,\n\tpnpm: 15_000,\n\tnrm: 15_000,\n\tcodegraph: 15_000,\n\tocx: 15_000,\n};\nconst POSIX_LOGIN_SHELL_FALLBACK_TOOLS = new Set<string>([\n\t\"npm\",\n\t\"pnpm\",\n\t\"nrm\",\n\t\"node\",\n\t\"dotnet\",\n\t\"rtk\",\n\t\"codegraph\",\n\t\"ocx\",\n]);\nconst ERROR_CODE_NOT_FOUND = 127;\nconst ERROR_CODE_TIMEOUT = \"ETIMEDOUT\";\n\n/**\n * Default nvm install roots, searched in order when `$NVM_DIR` is unset.\n * `~/.nvm` is the nvm-sh default; the Homebrew variants cover the\n * `brew install nvm` install path on Intel and Apple Silicon macs.\n */\nconst NVM_FALLBACK_DIRS: ReadonlyArray<string> = [\n\t\"/opt/homebrew/opt/nvm\",\n\t\"/usr/local/opt/nvm\",\n\t\"$HOME/.nvm\",\n];\n\n/**\n * Resolve the login shell to use for POSIX fallbacks.\n *\n * We deliberately do NOT hardcode `/bin/bash`. On macOS Catalina+ the user's\n * default shell is zsh, and nvm-sh's installer writes its sourcing line to\n * `~/.zshrc` — never `~/.bash_profile` — so a fixed `bash -lc` snippet will\n * silently miss it on a fresh Mac. Using `$SHELL` (the user's actual login\n * shell) lets the fallback chain match whatever rc file the user picked.\n *\n * `envShell` lets callers (and tests) override the `$SHELL` lookup so we\n * stay deterministic across machines without monkeypatching `process.env`.\n */\nfunction resolveUserLoginShell(platform: NodeJS.Platform, envShell: string | undefined): string {\n\tconst shell = envShell?.trim();\n\tif (shell && shell.length > 0) {\n\t\treturn shell;\n\t}\n\treturn platform === \"darwin\" ? \"/bin/zsh\" : \"/bin/bash\";\n}\n\n/**\n * Build the one-line snippet that (a) honors `$NVM_DIR` and falls back to\n * known install roots, then (b) sources `nvm.sh` only when the resolved file\n * is readable. Returns the snippet (without trailing semicolon) so callers\n * can append `; <command>` to it.\n */\nfunction buildNvmSourcingSnippet(): string {\n\tconst fallbackList = NVM_FALLBACK_DIRS.map((dir) => `\"${dir}\"`).join(\" \");\n\treturn (\n\t\t`for d in $NVM_DIR ${fallbackList}; do ` +\n\t\t`[ -n \"$d\" ] && [ -s \"$d/nvm.sh\" ] && export NVM_DIR=\"$d\" && . \"$d/nvm.sh\" && break; ` +\n\t\t`done`\n\t);\n}\n\n/**\n * Built shell-arg list for the POSIX fallback: pick the user's login shell,\n * run it as a login shell, source nvm, then exec the requested command.\n *\n * `loginArgs(command)` returns the `-c` snippet final form so the original\n * `getToolPathFromLoginShell` / `getToolVersion` call sites stay readable.\n */\nfunction posixLoginShellArgs(\n\tplatform: NodeJS.Platform,\n\tenvShell: string | undefined,\n\tcommand: string\n): { command: string; args: string[] } {\n\treturn {\n\t\tcommand: resolveUserLoginShell(platform, envShell),\n\t\targs: [\"-lc\", `${buildNvmSourcingSnippet()}; ${command}`],\n\t};\n}\n\ninterface ExecErrorLike {\n\tcode?: string | number;\n\tmessage?: string;\n\tstdout?: string;\n\tstderr?: string;\n}\n\nexport interface EnvironmentInspectorOptions {\n\t/**\n\t * Override the command runner. Production callers should leave this\n\t * undefined and rely on the default `runCommand` from `../process/runCommand`;\n\t * tests inject a fake to drive error/edge branches without spawning\n\t * real subprocesses.\n\t */\n\trunCommand?: typeof runCommand;\n\t/**\n\t * Override the detected platform. Defaults to `process.platform`. Tests\n\t * use this to exercise the Windows-specific branches of the inspector.\n\t */\n\tplatform?: NodeJS.Platform;\n\t/**\n\t * Override the value returned by `$SHELL` for POSIX login-shell\n\t * fallbacks. Defaults to `process.env.SHELL`. Tests pin this so the\n\t * fallback chain stays deterministic regardless of the developer's\n\t * host machine.\n\t */\n\tshell?: string;\n}\n\nexport class EnvironmentInspector {\n\tprivate readonly runCommandFn: typeof runCommand;\n\tprivate readonly platform: NodeJS.Platform;\n\tprivate readonly shell: string | undefined;\n\t/**\n\t * Per-check-cycle result cache. `checkEnvironment()` clears it before\n\t * probing so a fresh cycle never returns stale results, while concurrent\n\t * or repeated `checkTool()` calls within the same cycle share one probe\n\t * (deduplicating expensive login-shell fallbacks).\n\t */\n\tprivate readonly toolCheckCache = new Map<KnownEnvironmentTool, Promise<ToolCheckResult>>();\n\n\tconstructor(options: EnvironmentInspectorOptions = {}) {\n\t\tthis.runCommandFn = options.runCommand ?? runCommand;\n\t\tthis.platform = options.platform ?? process.platform;\n\t\tthis.shell = options.shell ?? process.env.SHELL;\n\t}\n\n\tasync checkEnvironment(): Promise<EnvironmentCheckResult> {\n\t\tthis.toolCheckCache.clear();\n\n\t\tconst results = await Promise.all(\n\t\t\tKNOWN_ENVIRONMENT_TOOLS.map(async (tool) => [tool, await this.checkTool(tool)] as const)\n\t\t);\n\n\t\treturn Object.fromEntries(results) as EnvironmentCheckResult;\n\t}\n\n\tasync checkTool(toolName: KnownEnvironmentTool): Promise<ToolCheckResult> {\n\t\tif (!/^[a-zA-Z0-9-]+$/.test(toolName)) {\n\t\t\tthrow createServicemeError(\"invalid_params\", \"Invalid tool name.\");\n\t\t}\n\n\t\tconst cached = this.toolCheckCache.get(toolName);\n\t\tif (cached) {\n\t\t\treturn cached;\n\t\t}\n\n\t\tconst check = this.performToolCheck(toolName);\n\t\tthis.toolCheckCache.set(toolName, check);\n\t\treturn check;\n\t}\n\n\tprivate async performToolCheck(toolName: KnownEnvironmentTool): Promise<ToolCheckResult> {\n\t\ttry {\n\t\t\tif (toolName === \"nvm\") {\n\t\t\t\treturn await this.checkNvm();\n\t\t\t}\n\n\t\t\tif (toolName === \"nuget\") {\n\t\t\t\treturn await this.checkNuget();\n\t\t\t}\n\n\t\t\tconst toolPath = await this.getToolPath(toolName);\n\t\t\tconst version = await this.getToolVersion(toolName);\n\n\t\t\treturn {\n\t\t\t\tinstalled: true,\n\t\t\t\tversion,\n\t\t\t\tpath: toolPath,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn this.handleToolCheckError(error);\n\t\t}\n\t}\n\n\tprivate async getToolPath(toolName: string): Promise<string | undefined> {\n\t\ttry {\n\t\t\tconst isWindows = this.platform === \"win32\";\n\t\t\tconst result = await this.runCommandFn(isWindows ? \"where\" : \"which\", {\n\t\t\t\targs: [toolName],\n\t\t\t\ttimeoutMs: this.getToolTimeout(toolName as KnownEnvironmentTool),\n\t\t\t});\n\n\t\t\treturn result.stdout\n\t\t\t\t.split(/\\r?\\n/)\n\t\t\t\t.map((line) => line.trim())\n\t\t\t\t.find((line) => line.length > 0);\n\t\t} catch {\n\t\t\tif (this.shouldUsePosixLoginShellFallback(toolName)) {\n\t\t\t\treturn this.getToolPathFromLoginShell(toolName);\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate async getToolPathFromLoginShell(toolName: string): Promise<string | undefined> {\n\t\ttry {\n\t\t\tconst spawnArgs = posixLoginShellArgs(this.platform, this.shell, `command -v ${toolName}`);\n\t\t\tconst result = await this.runCommandFn(spawnArgs.command, {\n\t\t\t\targs: spawnArgs.args,\n\t\t\t\ttimeoutMs: this.getToolTimeout(toolName as KnownEnvironmentTool),\n\t\t\t});\n\n\t\t\treturn result.stdout\n\t\t\t\t.split(/\\r?\\n/)\n\t\t\t\t.map((line) => line.trim())\n\t\t\t\t.find((line) => line.length > 0);\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t/**\n\t * On Windows, prefer the `.cmd` shim for npm/pnpm/nrm. nvm4w registers\n\t * BOTH an extensionless entry (pointing to node.exe) and a `<tool>.cmd`\n\t * wrapper; `where` returns them in that order, and the extensionless one\n\t * would just print node's own version.\n\t */\n\tprivate async getToolShimPath(toolName: string): Promise<string | undefined> {\n\t\ttry {\n\t\t\tconst result = await this.runCommandFn(\"where\", {\n\t\t\t\targs: [toolName],\n\t\t\t\ttimeoutMs: this.getToolTimeout(toolName as KnownEnvironmentTool),\n\t\t\t});\n\n\t\t\tconst candidates = result.stdout\n\t\t\t\t.split(/\\r?\\n/)\n\t\t\t\t.map((line) => line.trim())\n\t\t\t\t.filter((line) => line.length > 0);\n\n\t\t\tconst cmdShim = candidates.find((line) => line.toLowerCase().endsWith(\".cmd\"));\n\t\t\treturn cmdShim ?? candidates[0];\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate async getToolVersion(toolName: string): Promise<string> {\n\t\ttry {\n\t\t\tconst invocation = await this.getVersionInvocation(toolName);\n\t\t\tconst result = await this.runCommandFn(invocation.command, {\n\t\t\t\targs: invocation.args,\n\t\t\t\ttimeoutMs: this.getToolTimeout(toolName as KnownEnvironmentTool),\n\t\t\t});\n\n\t\t\treturn this.parseVersion(toolName, result.stdout || result.stderr);\n\t\t} catch (error) {\n\t\t\tif (!this.shouldUsePosixLoginShellFallback(toolName)) {\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tconst fallbackArgs = posixLoginShellArgs(this.platform, this.shell, `${toolName} --version`);\n\t\t\tconst fallbackResult = await this.runCommandFn(fallbackArgs.command, {\n\t\t\t\targs: fallbackArgs.args,\n\t\t\t\ttimeoutMs: this.getToolTimeout(toolName as KnownEnvironmentTool),\n\t\t\t});\n\t\t\tconst fallbackOutput = (fallbackResult.stdout || fallbackResult.stderr).trim();\n\t\t\tif (!fallbackOutput) {\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\treturn this.parseVersion(toolName, fallbackOutput);\n\t\t}\n\t}\n\n\tprivate shouldUsePosixLoginShellFallback(toolName: string): boolean {\n\t\treturn this.platform !== \"win32\" && POSIX_LOGIN_SHELL_FALLBACK_TOOLS.has(toolName);\n\t}\n\n\tprivate async checkNvm(): Promise<ToolCheckResult> {\n\t\tif (this.platform === \"win32\") {\n\t\t\ttry {\n\t\t\t\tconst result = await this.runCommandFn(\"cmd.exe\", {\n\t\t\t\t\targs: [\"/c\", \"nvm version\"],\n\t\t\t\t\ttimeoutMs: this.getToolTimeout(\"nvm\"),\n\t\t\t\t});\n\n\t\t\t\treturn {\n\t\t\t\t\tinstalled: true,\n\t\t\t\t\tversion: result.stdout.trim(),\n\t\t\t\t\tpath: await this.getToolPath(\"nvm\"),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn {\n\t\t\t\t\tinstalled: false,\n\t\t\t\t\terror: \"Not installed\",\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\t// Avoid an unconditional login-shell spawn: when nvm.sh is not\n\t\t\t// present in any well-known location, report \"Not installed\"\n\t\t\t// without launching a shell (a login shell would source\n\t\t\t// ~/.zshrc, which may block on slow startup hooks).\n\t\t\tif (!this.resolveNvmShPath()) {\n\t\t\t\treturn {\n\t\t\t\t\tinstalled: false,\n\t\t\t\t\terror: \"Not installed\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst nvmArgs = posixLoginShellArgs(this.platform, this.shell, \"nvm --version\");\n\t\t\tconst result = await this.runCommandFn(nvmArgs.command, {\n\t\t\t\targs: nvmArgs.args,\n\t\t\t\ttimeoutMs: this.getToolTimeout(\"nvm\"),\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tinstalled: true,\n\t\t\t\tversion: result.stdout.trim(),\n\t\t\t\tpath: \"$HOME/.nvm/nvm.sh\",\n\t\t\t};\n\t\t} catch {\n\t\t\treturn {\n\t\t\t\tinstalled: false,\n\t\t\t\terror: \"Not installed\",\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Resolve `nvm.sh` from `$NVM_DIR` and the known install roots without\n\t * spawning a shell. Returns undefined when nvm is not installed in any\n\t * well-known location, letting callers skip a login-shell probe.\n\t */\n\tprivate resolveNvmShPath(): string | undefined {\n\t\tconst candidates: string[] = [];\n\t\tconst nvmDir = process.env.NVM_DIR?.trim();\n\t\tif (nvmDir && nvmDir.length > 0) {\n\t\t\tcandidates.push(nvmDir);\n\t\t}\n\t\tfor (const dir of NVM_FALLBACK_DIRS) {\n\t\t\tcandidates.push(dir.replace(/^\\$HOME/, homedir()));\n\t\t}\n\n\t\tfor (const candidate of candidates) {\n\t\t\tconst scriptPath = join(candidate, \"nvm.sh\");\n\t\t\tif (existsSync(scriptPath)) {\n\t\t\t\treturn scriptPath;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate async checkNuget(): Promise<ToolCheckResult> {\n\t\tconst dotnetPath = await this.getToolPath(\"dotnet\");\n\t\tif (!dotnetPath) {\n\t\t\treturn {\n\t\t\t\tinstalled: false,\n\t\t\t\terror: \"dotnet CLI is not installed\",\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await this.runCommandFn(dotnetPath, {\n\t\t\t\targs: [\"nuget\", \"list\", \"source\"],\n\t\t\t\ttimeoutMs: this.getToolTimeout(\"nuget\"),\n\t\t\t});\n\n\t\t\t// ADL-003 boundary copy of `MEDALSOFT_NUGET_PRIVATE_SOURCE`\n\t\t\t// (shared/constants.ts) — core cannot depend on shared. Keep\n\t\t\t// in lock-step with the authoritative definition.\n\t\t\tconst privateSourceUrl = \"http://192.168.20.209:10010/nuget\";\n\t\t\tif (result.stdout.includes(privateSourceUrl)) {\n\t\t\t\treturn {\n\t\t\t\t\tinstalled: true,\n\t\t\t\t\tversion: \"Configured\",\n\t\t\t\t\tpath: privateSourceUrl,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tinstalled: false,\n\t\t\t\terror: \"Private source not configured\",\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn this.handleToolCheckError(error);\n\t\t}\n\t}\n\n\tprivate async getVersionInvocation(toolName: string): Promise<{\n\t\tcommand: string;\n\t\targs: string[];\n\t}> {\n\t\tconst isWindows = this.platform === \"win32\";\n\t\tif (isWindows && [\"npm\", \"pnpm\", \"nrm\", \"codegraph\", \"ocx\"].includes(toolName)) {\n\t\t\t// On Windows, resolve the `.cmd` shim and invoke it via an\n\t\t\t// explicit `cmd.exe /c <absolute-path>` — NOT the\n\t\t\t// `cmd.exe /d /s /c \"<tool> --version\"` form we used to use.\n\t\t\t// The earlier wrapper forced CMD to do a fresh PATH lookup\n\t\t\t// AND its `/s` quote-stripping pass on every call, which on\n\t\t\t// machines using nvm4w shims (or globally-installed npm\n\t\t\t// tools) blew past the 5s default timeout. Spawning the\n\t\t\t// resolved shim path directly skips both steps.\n\t\t\t//\n\t\t\t// nvm4w's `where <tool>` returns BOTH an extensionless entry\n\t\t\t// (pointing to node.exe) and the real `<tool>.cmd` wrapper;\n\t\t\t// we prefer the `.cmd` form so the shim chain still runs.\n\t\t\tconst shim = await this.getToolShimPath(toolName);\n\t\t\tif (shim) {\n\t\t\t\treturn {\n\t\t\t\t\tcommand: \"cmd.exe\",\n\t\t\t\t\targs: [\"/c\", shim, \"--version\"],\n\t\t\t\t};\n\t\t\t}\n\t\t\t// Fall back to letting CMD resolve it from PATH.\n\t\t\treturn {\n\t\t\t\tcommand: \"cmd.exe\",\n\t\t\t\targs: [\"/c\", toolName, \"--version\"],\n\t\t\t};\n\t\t}\n\n\t\tconst commands: Record<string, { command: string; args: string[] }> = {\n\t\t\tgit: { command: \"git\", args: [\"--version\"] },\n\t\t\tnode: { command: \"node\", args: [\"--version\"] },\n\t\t\tnpm: { command: \"npm\", args: [\"--version\"] },\n\t\t\tpnpm: { command: \"pnpm\", args: [\"--version\"] },\n\t\t\tnvm: { command: \"nvm\", args: [\"--version\"] },\n\t\t\tnrm: { command: \"nrm\", args: [\"--version\"] },\n\t\t\trtk: { command: \"rtk\", args: [\"--version\"] },\n\t\t\tcodegraph: { command: \"codegraph\", args: [\"--version\"] },\n\t\t\tocx: { command: \"ocx\", args: [\"--version\"] },\n\t\t\tdotnet: { command: \"dotnet\", args: [\"--version\"] },\n\t\t};\n\n\t\treturn commands[toolName] || { command: toolName, args: [\"--version\"] };\n\t}\n\n\tprivate getToolTimeout(toolName: KnownEnvironmentTool): number {\n\t\treturn TOOL_CHECK_TIMEOUT_MS[toolName] ?? DEFAULT_TOOL_CHECK_TIMEOUT_MS;\n\t}\n\n\tprivate parseVersion(toolName: string, output: string): string {\n\t\tconst cleaned = output.trim();\n\n\t\tswitch (toolName) {\n\t\t\tcase \"git\": {\n\t\t\t\tconst match = cleaned.match(/git version (\\d+\\.\\d+\\.\\d+)/);\n\t\t\t\treturn match?.[1] || cleaned;\n\t\t\t}\n\t\t\tcase \"node\": {\n\t\t\t\tconst match = cleaned.match(/v?(\\d+\\.\\d+\\.\\d+)/);\n\t\t\t\treturn match?.[1] || cleaned;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tconst semver = cleaned.match(/(\\d+\\.\\d+\\.\\d+)/);\n\t\t\t\tif (semver?.[1]) {\n\t\t\t\t\treturn semver[1];\n\t\t\t\t}\n\n\t\t\t\tconst simple = cleaned.match(/(\\d+\\.\\d+)/);\n\t\t\t\treturn simple?.[1] || cleaned;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate handleToolCheckError(error: unknown): ToolCheckResult {\n\t\tconst execError = error as ExecErrorLike;\n\t\tconst message = execError.message || String(error);\n\t\tconst code = execError.code;\n\n\t\tconst isNotFound =\n\t\t\tmessage.includes(\"command not found\") ||\n\t\t\tmessage.includes(\"not recognized\") ||\n\t\t\tmessage.includes(\"ENOENT\") ||\n\t\t\tmessage.includes(\"EACCES\") ||\n\t\t\tcode === \"ENOENT\" ||\n\t\t\tcode === \"EACCES\" ||\n\t\t\tcode === ERROR_CODE_NOT_FOUND;\n\t\tconst isTimeout = code === ERROR_CODE_TIMEOUT || message.toLowerCase().includes(\"timed out\");\n\n\t\treturn {\n\t\t\tinstalled: false,\n\t\t\terror: isNotFound ? \"Not installed\" : isTimeout ? \"Check timed out\" : message,\n\t\t};\n\t}\n}\n","import { spawn } from \"node:child_process\";\nimport * as path from \"node:path\";\n\nimport type {\n\tGitClientOptions,\n\tGitSpawner,\n\tGitSpawnResult,\n\tPullResult,\n\tPushResult,\n\tRemoteBranch,\n} from \"./types\";\nimport { GitError } from \"./types\";\n\n/**\n * Skill & Agent v2 — Client GitClient (M3)\n *\n * Wraps `git` so all upstream traffic flows through the server's git\n * proxy at `serverProxyBase`. See docs/architecture/skill-agent-v2-repo.md\n * §5.4.\n *\n * Why a wrapper instead of using libgit2 directly:\n * 1. `git` is already installed everywhere we run (extension host,\n * CLI). No native deps.\n * 2. Users can debug their skill repos with plain `git` commands when\n * SERVICEME is misbehaving — the wrappers keep a familiar CLI.\n * 3. libgit2's packfile negotiation has subtle correctness gaps that\n * we don't want to debug server-side.\n *\n * URL rewrite:\n * `https://github.com/owner/repo.git` → `<proxyBase>/<id>`\n *\n * When git hits `<proxyBase>/<id>` it discovers the proxy's\n * `/info/refs?service=git-upload-pack` endpoint via the smart-HTTP\n * protocol. From there the server takes over.\n */\nexport class GitClient {\n\tprivate readonly serverProxyBase: string;\n\tprivate readonly spawner: GitSpawner;\n\n\tconstructor(opts: GitClientOptions) {\n\t\t// Normalize: strip trailing slash so `rewriteRemoteUrl` is stable.\n\t\tthis.serverProxyBase = opts.serverProxyBase.replace(/\\/+$/, \"\");\n\t\tthis.spawner = opts.spawner ?? new NodeGitSpawner();\n\t}\n\n\t/**\n\t * Rewrite an upstream URL to its proxy form.\n\t *\n\t * Examples:\n\t * rewriteRemoteUrl(\"repo-aHR0cHM6Ly9naXRodWIuY29tL21lZGFsc29mdGNoaW5hL21zLXNraWxscy5naXQ\",\n\t * \"https://github.com/medalsoftchina/ms-skills.git\")\n\t * → \"http://localhost:3000/git-proxy/repo-aHR0cHM6Ly9naXRodWIuY29tL21lZGFsc29mdGNoaW5hL21zLXNraWxscy5naXQ\"\n\t *\n\t * rewriteRemoteUrl(\"repo-aHR0cHM6Ly9naXRodWIuY29tL3RlYW0vaW50ZXJuYWwuZ2l0\",\n\t * \"git@github.com:medalsoftchina/ms-skills.git\")\n\t * → \"http://localhost:3000/git-proxy/repo-aHR0cHM6Ly9naXRodWIuY29tL3RlYW0vaW50ZXJuYWwuZ2l0\"\n\t *\n\t * The original URL's host/owner is intentionally DROPPED — the proxy\n\t * knows the upstream from the per-repo config, not from the URL. This\n\t * means callers can route both default and user repos through the same\n\t * dynamic proxy-id scheme.\n\t */\n\trewriteRemoteUrl(_repoId: string, originalUrl: string, useProxy = true): string {\n\t\tif (!useProxy) {\n\t\t\treturn originalUrl;\n\t\t}\n\t\treturn `${this.serverProxyBase}/${_repoId}`;\n\t}\n\n\t/**\n\t * `git clone <proxyUrl> <localPath>` — initialize a new local repo\n\t * from the proxy. Returns when the clone succeeds; throws on failure.\n\t *\n\t * When `branch` is provided, the clone is restricted to that branch\n\t * via `git clone -b <branch> -- <proxyUrl> <localPath>` so the local\n\t * checkout lands on the configured `repo.branch` instead of the\n\t * upstream default branch. The parameter is optional and backward\n\t * compatible — omitting it keeps the original behaviour.\n\t */\n\tasync clone(\n\t\trepoId: string,\n\t\toriginalUrl: string,\n\t\tlocalPath: string,\n\t\tbranch?: string,\n\t\tuseProxy = true\n\t): Promise<void> {\n\t\tconst proxyUrl = this.rewriteRemoteUrl(repoId, originalUrl, useProxy);\n\t\tconst args = branch\n\t\t\t? [\"clone\", \"-b\", branch, \"--\", proxyUrl, localPath]\n\t\t\t: [\"clone\", \"--\", proxyUrl, localPath];\n\t\tawait this.runOrThrow(args, { cwd: process.cwd() });\n\t}\n\n\t/**\n\t * `git fetch <remote> <branch>` + return the resulting commit SHA.\n\t * Operates on an already-cloned repo at `localPath`.\n\t *\n\t * When `branch` is provided, the pull target is forced to that branch:\n\t * if the local working tree is on a different branch, `git checkout\n\t * <branch>` is issued first (git auto-tracks `origin/<branch>`), so\n\t * the sync leaves the repo parked on the configured `repo.branch`.\n\t * When `branch` is omitted the legacy behaviour is preserved — the\n\t * current local branch is fast-forwarded. The parameter is optional\n\t * and backward compatible.\n\t */\n\tasync pull(\n\t\trepoId: string,\n\t\tlocalPath: string,\n\t\tbranch?: string,\n\t\tuseProxy = true,\n\t\toriginalUrl?: string\n\t): Promise<PullResult> {\n\t\t// Re-derive the remote URL from the existing remote config (so\n\t\t// `pull` doesn't need to be told the original upstream URL —\n\t\t// it uses whatever `git remote get-url <remote>` reports).\n\t\tconst remoteUrl = await this.getRemoteUrl(localPath, \"origin\");\n\t\tif (!remoteUrl) {\n\t\t\tthrow new Error(`no 'origin' remote configured at ${localPath}`);\n\t\t}\n\t\tconst targetUrl = useProxy ? remoteUrl : (originalUrl ?? remoteUrl);\n\t\tconst proxyUrl = this.rewriteRemoteUrl(repoId, targetUrl, useProxy);\n\n\t\t// Ensure remote.origin.url points at the proxy URL.\n\t\tawait this.runOrThrow([\"remote\", \"set-url\", \"origin\", proxyUrl], {\n\t\t\tcwd: localPath,\n\t\t});\n\n\t\t// Fetch via the proxy FIRST (this also refreshes the\n\t\t// origin/<branch> refs so a subsequent checkout/merge is current).\n\t\tconst before = await this.safeRevParse(localPath);\n\t\tawait this.runOrThrow([\"fetch\", \"origin\"], { cwd: localPath });\n\t\tconst after = await this.safeRevParse(localPath);\n\n\t\t// Resolve the target branch. If a branch was requested and we are\n\t\t// not currently on it, check it out so git tracks origin/<branch>\n\t\t// before the fast-forward. Otherwise reuse the local current branch\n\t\t// (preserves the original call order for the no-branch case).\n\t\tlet targetBranch = branch;\n\t\tif (!targetBranch) {\n\t\t\ttargetBranch = await this.currentBranch(localPath);\n\t\t} else {\n\t\t\tconst current = await this.currentBranch(localPath);\n\t\t\tif (current !== targetBranch) {\n\t\t\t\tawait this.runOrThrow([\"checkout\", targetBranch], { cwd: localPath });\n\t\t\t}\n\t\t}\n\t\tif (!targetBranch) {\n\t\t\tthrow new Error(`could not determine current branch at ${localPath}`);\n\t\t}\n\n\t\t// Fast-forward the working branch so callers see the new commits.\n\t\tawait this.runOrThrow([\"merge\", \"--ff-only\", `origin/${targetBranch}`], {\n\t\t\tcwd: localPath,\n\t\t});\n\n\t\treturn {\n\t\t\tupdated: before !== after,\n\t\t\tcommitSha: after,\n\t\t\tbranch: targetBranch,\n\t\t};\n\t}\n\n\t/**\n\t * `git push origin <branch>` via the proxy. Caller is responsible\n\t * for committing locally first. The server's GitProxy injects the\n\t * PAT on the way upstream.\n\t */\n\tasync push(\n\t\trepoId: string,\n\t\tlocalPath: string,\n\t\tbranch: string,\n\t\tuseProxy = true\n\t): Promise<PushResult> {\n\t\tconst remoteUrl = await this.getRemoteUrl(localPath, \"origin\");\n\t\tif (!remoteUrl) {\n\t\t\tthrow new Error(`no 'origin' remote configured at ${localPath}`);\n\t\t}\n\t\tconst proxyUrl = this.rewriteRemoteUrl(repoId, remoteUrl, useProxy);\n\t\tawait this.runOrThrow([\"remote\", \"set-url\", \"origin\", proxyUrl], {\n\t\t\tcwd: localPath,\n\t\t});\n\n\t\tconst result = await this.spawner.spawn(\n\t\t\t[\"push\", \"origin\", `refs/heads/${branch}:refs/heads/${branch}`],\n\t\t\t{ cwd: localPath }\n\t\t);\n\t\tif (result.code !== 0) {\n\t\t\tthrow new GitError([\"push\"], result);\n\t\t}\n\n\t\t// Parse the new commit SHA from push output: `refs/heads/v2: <sha>\\t...`\n\t\tconst m = result.stderr.match(/refs\\/heads\\/[^\\s:]+:\\s*([0-9a-f]{7,})/);\n\t\tconst commitSha = m?.[1] ?? \"\";\n\n\t\treturn {\n\t\t\tref: `refs/heads/${branch}`,\n\t\t\tcommitSha,\n\t\t};\n\t}\n\n\t/**\n\t * `git ls-remote <proxyUrl>` — list advertised branches without\n\t * cloning. Used by addUserRepo to detect the default branch.\n\t */\n\tasync lsRemote(repoId: string, originalUrl: string, useProxy = true): Promise<RemoteBranch[]> {\n\t\tconst proxyUrl = this.rewriteRemoteUrl(repoId, originalUrl, useProxy);\n\t\tconst result = await this.spawner.spawn([\"ls-remote\", \"--heads\", \"--\", proxyUrl], {\n\t\t\tcwd: process.cwd(),\n\t\t});\n\t\tif (result.code !== 0) {\n\t\t\tthrow new GitError([\"ls-remote\"], result);\n\t\t}\n\t\tconst branches: RemoteBranch[] = [];\n\t\tfor (const line of result.stdout.split(\"\\n\")) {\n\t\t\tconst trimmed = line.trim();\n\t\t\tif (!trimmed) continue;\n\t\t\t// `<sha>\\t<ref>` — split on the tab.\n\t\t\tconst tab = trimmed.indexOf(\"\\t\");\n\t\t\tif (tab === -1) continue;\n\t\t\tconst sha = trimmed.slice(0, tab);\n\t\t\tconst ref = trimmed.slice(tab + 1);\n\t\t\tbranches.push({ sha, ref });\n\t\t}\n\t\treturn branches;\n\t}\n\n\t/**\n\t * Fetch an exact commit and detach HEAD at it. Used by workspace\n\t * content restoration, which must never silently advance to a branch\n\t * head. Returns the verified commit SHA.\n\t */\n\tasync checkoutCommit(\n\t\tlocalPath: string,\n\t\tcommit: string,\n\t\trepoId: string,\n\t\tuseProxy = true,\n\t\toriginalUrl?: string\n\t): Promise<string> {\n\t\tconst remoteUrl = await this.getRemoteUrl(localPath, \"origin\");\n\t\tif (remoteUrl) {\n\t\t\tconst proxyUrl = this.rewriteRemoteUrl(repoId, remoteUrl, useProxy);\n\t\t\tawait this.runOrThrow([\"remote\", \"set-url\", \"origin\", proxyUrl], { cwd: localPath });\n\t\t} else if (originalUrl) {\n\t\t\tconst proxyUrl = this.rewriteRemoteUrl(repoId, originalUrl, useProxy);\n\t\t\tawait this.runOrThrow([\"remote\", \"add\", \"origin\", proxyUrl], { cwd: localPath });\n\t\t}\n\n\t\tawait this.runOrThrow([\"fetch\", \"origin\", commit], { cwd: localPath });\n\t\tconst verify = await this.spawner.spawn([\"rev-parse\", \"--verify\", `${commit}^{commit}`], {\n\t\t\tcwd: localPath,\n\t\t});\n\t\tif (verify.code !== 0 || verify.stdout.trim() !== commit) {\n\t\t\tthrow new GitError([\"rev-parse\", \"--verify\", commit], verify);\n\t\t}\n\t\tawait this.runOrThrow([\"checkout\", \"--detach\", commit], { cwd: localPath });\n\t\treturn commit;\n\t}\n\n\t/**\n\t * Resolve a repo-relative path inside the git directory using\n\t * `git rev-parse --git-path`, which handles linked worktrees and\n\t * submodules correctly (their metadata does not live under\n\t * `<worktree>/.git` as a directory).\n\t */\n\tasync gitPath(localPath: string, pathspec: string): Promise<string> {\n\t\tconst result = await this.spawner.spawn([\"rev-parse\", \"--git-path\", pathspec], {\n\t\t\tcwd: localPath,\n\t\t});\n\t\tif (result.code !== 0) {\n\t\t\tthrow new GitError([\"rev-parse\", \"--git-path\", pathspec], result);\n\t\t}\n\t\tconst resolved = result.stdout.trim();\n\t\tif (!path.isAbsolute(resolved)) {\n\t\t\treturn path.resolve(localPath, resolved);\n\t\t}\n\t\treturn resolved;\n\t}\n\n\t/**\n\t * Resolve the current HEAD commit of a local clone. Returns an empty\n\t * string when git cannot resolve it (e.g. an empty repository) so\n\t * callers can decide how to surface the failure.\n\t */\n\tasync revParseHead(localPath: string): Promise<string> {\n\t\treturn this.safeRevParse(localPath);\n\t}\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Internal helpers\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * `git add <pathspec…>` followed by `git commit -m <message>`. The\n\t * commit message convention follows the spec: `feat(skills): add\n\t * <name>` / `feat(agents): add <name>`.\n\t */\n\tasync commit(localPath: string, message: string, addPath = \".\"): Promise<{ commitSha: string }> {\n\t\tawait this.runOrThrow([\"add\", \"--\", addPath], { cwd: localPath });\n\t\tawait this.runOrThrow([\"commit\", \"-m\", message], { cwd: localPath });\n\t\tconst sha = await this.safeRevParse(localPath);\n\t\treturn { commitSha: sha };\n\t}\n\n\tprivate async runOrThrow(args: string[], opts: { cwd?: string }): Promise<GitSpawnResult> {\n\t\tconst result = await this.spawner.spawn(args, {\n\t\t\tcwd: opts.cwd ?? process.cwd(),\n\t\t});\n\t\tif (result.code !== 0) {\n\t\t\tthrow new GitError(args, result);\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate async getRemoteUrl(localPath: string, remote: string): Promise<string | undefined> {\n\t\tconst result = await this.spawner.spawn([\"remote\", \"get-url\", remote], {\n\t\t\tcwd: localPath,\n\t\t});\n\t\tif (result.code !== 0) return undefined;\n\t\tconst url = result.stdout.trim();\n\t\treturn url.length > 0 ? url : undefined;\n\t}\n\n\tprivate async safeRevParse(localPath: string): Promise<string> {\n\t\tconst result = await this.spawner.spawn([\"rev-parse\", \"HEAD\"], {\n\t\t\tcwd: localPath,\n\t\t});\n\t\tif (result.code !== 0) return \"\";\n\t\treturn result.stdout.trim();\n\t}\n\n\tprivate async currentBranch(localPath: string): Promise<string | undefined> {\n\t\tconst result = await this.spawner.spawn([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], {\n\t\t\tcwd: localPath,\n\t\t});\n\t\tif (result.code !== 0) return undefined;\n\t\tconst branch = result.stdout.trim();\n\t\tif (!branch || branch === \"HEAD\") return undefined;\n\t\treturn branch;\n\t}\n}\n\n/**\n * Default spawner: invokes the system `git` binary via Node's\n * `child_process.spawn`. Streams stdout/stderr to strings and returns\n * the exit code. Tests inject a stub to avoid touching the filesystem.\n */\n// Re-export GitError so callers can `import { GitError } from \"...\"`.\nexport { GitError };\n\nexport class NodeGitSpawner implements GitSpawner {\n\tasync spawn(args: string[], opts: { cwd?: string }): Promise<GitSpawnResult> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst child = spawn(\"git\", args, {\n\t\t\t\tcwd: opts.cwd,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\tshell: false,\n\t\t\t});\n\t\t\tconst stdoutChunks: Buffer[] = [];\n\t\t\tconst stderrChunks: Buffer[] = [];\n\t\t\tchild.stdout?.on(\"data\", (c: Buffer) => stdoutChunks.push(c));\n\t\t\tchild.stderr?.on(\"data\", (c: Buffer) => stderrChunks.push(c));\n\t\t\tchild.on(\"error\", reject);\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tresolve({\n\t\t\t\t\tstdout: Buffer.concat(stdoutChunks).toString(\"utf8\"),\n\t\t\t\t\tstderr: Buffer.concat(stderrChunks).toString(\"utf8\"),\n\t\t\t\t\tcode: code ?? 1,\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t}\n}\n\n/** Convenience: a record-based stub spawner for tests. */\nexport class StubGitSpawner implements GitSpawner {\n\t/** queue of canned responses, consumed FIFO per spawn() call. */\n\treadonly script: GitSpawnResult[];\n\t/** All spawn() invocations, in order, for assertions. */\n\treadonly calls: Array<{ args: string[]; cwd: string | undefined }> = [];\n\n\tconstructor(script: GitSpawnResult[]) {\n\t\tthis.script = [...script];\n\t}\n\n\tasync spawn(args: string[], opts: { cwd?: string }): Promise<GitSpawnResult> {\n\t\tthis.calls.push({ args, cwd: opts.cwd });\n\t\tconst next = this.script.shift();\n\t\tif (!next) {\n\t\t\treturn {\n\t\t\t\tstdout: \"\",\n\t\t\t\tstderr: `stub: no scripted response for ${args.join(\" \")}`,\n\t\t\t\tcode: 1,\n\t\t\t};\n\t\t}\n\t\treturn next;\n\t}\n}\n\n/**\n * Helper: encode a bare absolute file URL (`file:///...`) for local\n * git clone tests. Not used by GitClient directly — exposed for\n * RepoManager's `localSeed` use case.\n */\nexport function toFileUrl(absolutePath: string): string {\n\tconst normalized = path.resolve(absolutePath);\n\tif (process.platform === \"win32\") {\n\t\treturn `file:///${normalized.replace(/\\\\/g, \"/\")}`;\n\t}\n\treturn `file://${normalized}`;\n}\n\n/** Path suffix appended to a server base URL to reach its git-proxy route. */\nexport const GIT_PROXY_PATH_SUFFIX = \"/git-proxy\";\n\n/**\n * Single source of truth for deriving a {@link GitClientOptions.serverProxyBase}\n * from an already-resolved, non-empty server base URL (e.g. the extension's\n * verified marketplace server). Callers that derive `serverProxyBase` from a\n * known server base URL — extension bootstrap, the sync scheduler, the CLI\n * bridge subprocess env — should go through this helper instead of\n * re-deriving the `/git-proxy` suffix locally, so the route contract only\n * lives in one place. (Standalone CLI defaults such as\n * `http://127.0.0.1:3000/git-proxy`, used when no server base URL is known\n * at all, are a separate concern and don't go through this helper.)\n *\n * Callers MUST guard against an empty/falsy `serverBaseUrl` before calling —\n * this function does not validate its input and will happily return the\n * non-URL `\"/git-proxy\"` for an empty string.\n */\nexport function buildGitProxyBase(serverBaseUrl: string): string {\n\treturn `${serverBaseUrl.replace(/\\/+$/, \"\")}${GIT_PROXY_PATH_SUFFIX}`;\n}\n","import type { SpawnOptions } from \"node:child_process\";\n\n/**\n * Skill & Agent v2 — Client GitClient Types (M3)\n *\n * GitClient wraps the local `git` CLI so all commands the SERVICEME\n * pipeline issues (clone, fetch, push, ls-remote) flow through the\n * server's git smart-HTTP proxy instead of talking directly to GitHub.\n *\n * The single trick: `git` only cares about the URL we hand it as a\n * remote. We rewrite `https://github.com/owner/repo.git` to\n * `http://server:port/git-proxy/<id>` and `git` does the rest — the\n * server transparently forwards + injects PATs as needed.\n *\n * @see docs/architecture/skill-agent-v2-repo.md §5.4 GitClient\n */\n\n/** Result of a `git fetch` (called via the proxy's git-upload-pack). */\nexport interface PullResult {\n\t/** Whether new commits were fetched (false = already up-to-date). */\n\tupdated: boolean;\n\t/** Commit SHA at FETCH_HEAD after the pull. */\n\tcommitSha: string;\n\t/** Branch name that was pulled (e.g. \"v2\"). */\n\tbranch: string;\n}\n\n/** Result of a `git push` (called via the proxy's git-receive-pack). */\nexport interface PushResult {\n\t/** Remote ref updated (e.g. \"refs/heads/v2\"). */\n\tref: string;\n\t/** New commit SHA on the remote. */\n\tcommitSha: string;\n}\n\n/** A branch as advertised by `git ls-remote`. */\nexport interface RemoteBranch {\n\t/** Full ref name, e.g. `refs/heads/main`. */\n\tref: string;\n\t/** Commit SHA the ref points at. */\n\tsha: string;\n}\n\n/** Outcome of any spawned `git` process. */\nexport interface GitSpawnResult {\n\tstdout: string;\n\tstderr: string;\n\tcode: number;\n}\n\n/** Strategy for executing git commands. Tests inject a stub. */\nexport interface GitSpawner {\n\tspawn(args: string[], opts: SpawnOptions): Promise<GitSpawnResult>;\n}\n\n/** Options for instantiating GitClient. */\nexport interface GitClientOptions {\n\t/** Base URL of the server git proxy. e.g. `http://localhost:3000/git-proxy`. */\n\tserverProxyBase: string;\n\t/** Inject a custom spawner (default: spawn real `git` CLI). */\n\tspawner?: GitSpawner;\n\t/** Inject an env override for spawned git (default: process.env minus proxy secrets). */\n\tenv?: NodeJS.ProcessEnv;\n}\n\n/** Sentinel error when git exits non-zero. */\nexport class GitError extends Error {\n\treadonly args: string[];\n\treadonly code: number;\n\treadonly stderr: string;\n\treadonly stdout: string;\n\tconstructor(args: string[], result: GitSpawnResult) {\n\t\tsuper(`git ${args.join(\" \")} failed (exit ${result.code}): ${result.stderr.slice(0, 500)}`);\n\t\tthis.name = \"GitError\";\n\t\tthis.args = args;\n\t\tthis.code = result.code;\n\t\tthis.stderr = result.stderr;\n\t\tthis.stdout = result.stdout;\n\t}\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport {\n\tcreateServicemeError,\n\ttype ServiceMeImageCompressOptions,\n\ttype ServiceMeImageCompressResult,\n\ttype ServiceMeImageInfo,\n\ttype ServiceMeImageValidationResult,\n} from \"@serviceme/devtools-protocol\";\n\ntype SharpMetadata = {\n\twidth?: number;\n\theight?: number;\n\tformat?: string;\n\tspace?: string;\n};\n\ntype SharpPipeline = {\n\tmetadata(): Promise<SharpMetadata>;\n\tjpeg(options: { quality: number }): SharpPipeline;\n\tpng(options: { quality: number; compressionLevel: number }): SharpPipeline;\n\twebp(options: { quality: number }): SharpPipeline;\n\ttoBuffer(): Promise<Buffer>;\n};\n\ntype SharpModule = (inputPath: string) => SharpPipeline;\n\nconst SUPPORTED_FORMATS = [\".jpg\", \".jpeg\", \".png\", \".webp\", \".gif\", \".bmp\", \".tiff\"];\nconst DEFAULT_MINIMUM_COMPRESSION_RATIO = 5;\n\nexport class ImageTools {\n\tgetSupportedFormats(): string[] {\n\t\treturn [...SUPPORTED_FORMATS];\n\t}\n\n\tasync validate(\n\t\tfilePath: string,\n\t\tsharpModulePath?: string\n\t): Promise<ServiceMeImageValidationResult> {\n\t\ttry {\n\t\t\tif (!(await this.pathExists(filePath))) {\n\t\t\t\treturn { valid: false };\n\t\t\t}\n\n\t\t\tif (!SUPPORTED_FORMATS.includes(path.extname(filePath).toLowerCase())) {\n\t\t\t\treturn { valid: false };\n\t\t\t}\n\n\t\t\tawait this.getInfo(filePath, sharpModulePath);\n\t\t\treturn { valid: true };\n\t\t} catch {\n\t\t\treturn { valid: false };\n\t\t}\n\t}\n\n\tasync getInfo(imagePath: string, sharpModulePath?: string): Promise<ServiceMeImageInfo> {\n\t\tconst sharp = this.loadSharp(sharpModulePath);\n\t\tconst metadata = await sharp(imagePath).metadata();\n\t\tconst stats = await fs.stat(imagePath);\n\n\t\treturn {\n\t\t\twidth: metadata.width ?? 0,\n\t\t\theight: metadata.height ?? 0,\n\t\t\tformat: metadata.format ?? \"unknown\",\n\t\t\tsize: stats.size,\n\t\t\tcolorSpace: metadata.space,\n\t\t};\n\t}\n\n\tasync compress(\n\t\timagePath: string,\n\t\toptions: ServiceMeImageCompressOptions\n\t): Promise<ServiceMeImageCompressResult> {\n\t\tconst validation = await this.validate(imagePath, options.sharpModulePath);\n\t\tif (!validation.valid) {\n\t\t\tthrow createServicemeError(\"invalid_params\", `Invalid image file: ${imagePath}`);\n\t\t}\n\n\t\tconst originalStats = await fs.stat(imagePath);\n\t\tconst originalSize = originalStats.size;\n\t\tconst outputPath = this.getOutputPath(imagePath, options);\n\t\tconst compressedBuffer = await this.compressWithSharp(imagePath, options);\n\t\tconst compressedSize = compressedBuffer.length;\n\t\tconst compressionRatio = ((originalSize - compressedSize) / originalSize) * 100;\n\t\tconst minimumCompressionRatio =\n\t\t\toptions.minimumCompressionRatio ?? DEFAULT_MINIMUM_COMPRESSION_RATIO;\n\n\t\tif (compressionRatio < minimumCompressionRatio) {\n\t\t\treturn {\n\t\t\t\toriginalSize,\n\t\t\t\tcompressedSize: originalSize,\n\t\t\t\tcompressionRatio: 0,\n\t\t\t\toutputPath: imagePath,\n\t\t\t};\n\t\t}\n\n\t\tawait fs.writeFile(outputPath, compressedBuffer);\n\n\t\treturn {\n\t\t\toriginalSize,\n\t\t\tcompressedSize,\n\t\t\tcompressionRatio,\n\t\t\toutputPath,\n\t\t};\n\t}\n\n\tprivate async compressWithSharp(\n\t\timagePath: string,\n\t\toptions: ServiceMeImageCompressOptions\n\t): Promise<Buffer> {\n\t\tconst sharp = this.loadSharp(options.sharpModulePath);\n\t\tlet pipeline = sharp(imagePath);\n\n\t\tswitch (options.format ?? path.extname(imagePath).toLowerCase().slice(1)) {\n\t\t\tcase \"jpg\":\n\t\t\tcase \"jpeg\":\n\t\t\t\tpipeline = pipeline.jpeg({ quality: options.quality });\n\t\t\t\tbreak;\n\t\t\tcase \"png\":\n\t\t\t\tpipeline = pipeline.png({\n\t\t\t\t\tquality: options.quality,\n\t\t\t\t\tcompressionLevel: 9,\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\tcase \"webp\":\n\t\t\t\tpipeline = pipeline.webp({ quality: options.quality });\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tpipeline = pipeline.jpeg({ quality: options.quality });\n\t\t}\n\n\t\treturn pipeline.toBuffer();\n\t}\n\n\tprivate getOutputPath(inputPath: string, options: ServiceMeImageCompressOptions): string {\n\t\tif (options.outputPath) {\n\t\t\treturn options.outputPath;\n\t\t}\n\n\t\tif (options.replaceOriginImage) {\n\t\t\treturn inputPath;\n\t\t}\n\n\t\tconst dir = path.dirname(inputPath);\n\t\tconst ext = path.extname(inputPath);\n\t\tconst name = path.basename(inputPath, ext);\n\t\treturn path.join(dir, `${name}_compressed${ext}`);\n\t}\n\n\tprivate async pathExists(targetPath: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait fs.access(targetPath);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate loadSharp(sharpModulePath?: string): SharpModule {\n\t\ttry {\n\t\t\treturn (sharpModulePath ? require(sharpModulePath) : require(\"sharp\")) as SharpModule;\n\t\t} catch (error) {\n\t\t\tthrow createServicemeError(\n\t\t\t\t\"internal_error\",\n\t\t\t\t`sharp is required for image operations: ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`\n\t\t\t);\n\t\t}\n\t}\n}\n\nexport function createImageTools(): ImageTools {\n\treturn new ImageTools();\n}\n","import {\n\tcreateServicemeError,\n\tDEFAULT_JSON_SORT_OPTIONS,\n\ttype JsonSortOptions,\n\ttype JsonValidationResult,\n} from \"@serviceme/devtools-protocol\";\nimport { parse as parseCommentJson } from \"comment-json\";\nimport JSON5 from \"json5\";\n\nexport interface JsonTools {\n\tsort(text: string, options?: Partial<JsonSortOptions>): string;\n\tformat(text: string, indent?: number): string;\n\tvalidate(text: string): JsonValidationResult;\n\tminify(text: string): string;\n}\n\nexport function createJsonTools(): JsonTools {\n\treturn {\n\t\tsort(text: string, options?: Partial<JsonSortOptions>): string {\n\t\t\tconst finalOptions = { ...DEFAULT_JSON_SORT_OPTIONS, ...options };\n\t\t\tconst json = parseJson(text);\n\t\t\tconst sorted = sortObject(json, finalOptions);\n\t\t\treturn JSON.stringify(sorted, null, detectIndent(text));\n\t\t},\n\t\tformat(text: string, indent = 2): string {\n\t\t\treturn JSON.stringify(parseJson(text), null, indent);\n\t\t},\n\t\tvalidate(text: string): JsonValidationResult {\n\t\t\ttry {\n\t\t\t\tparseJson(text);\n\t\t\t\treturn { valid: true };\n\t\t\t} catch (error) {\n\t\t\t\treturn {\n\t\t\t\t\tvalid: false,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\t\tminify(text: string): string {\n\t\t\treturn JSON.stringify(parseJson(text));\n\t\t},\n\t};\n}\n\nfunction parseJson(text: string): unknown {\n\tconst parsers = [() => JSON.parse(text), () => parseCommentJson(text), () => JSON5.parse(text)];\n\n\tfor (const parser of parsers) {\n\t\ttry {\n\t\t\treturn parser();\n\t\t} catch {\n\t\t\t// Try next parser.\n\t\t}\n\t}\n\n\tthrow createServicemeError(\"json_invalid_input\", \"Invalid JSON format.\");\n}\n\nfunction sortObject(obj: unknown, options: JsonSortOptions): unknown {\n\tif (Array.isArray(obj)) {\n\t\treturn obj.map((item) => sortObject(item, options));\n\t}\n\n\tif (obj !== null && typeof obj === \"object\") {\n\t\tconst record = obj as Record<string, unknown>;\n\t\tconst sortedKeys = sortKeys(Object.keys(record), options);\n\t\tconst result: Record<string, unknown> = {};\n\n\t\tfor (const key of sortedKeys) {\n\t\t\tresult[key] = sortObject(record[key], options);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\treturn obj;\n}\n\nfunction sortKeys(keys: string[], options: JsonSortOptions): string[] {\n\tlet compareFn: (a: string, b: string) => number;\n\n\tswitch (options.sortAlgo) {\n\t\tcase \"keyLength\":\n\t\t\tcompareFn = (a, b) => a.length - b.length;\n\t\t\tbreak;\n\t\tcase \"alphaNum\":\n\t\t\tcompareFn = (a, b) => a.localeCompare(b, undefined, { numeric: true });\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tcompareFn = (a, b) => a.localeCompare(b);\n\t\t\tbreak;\n\t}\n\n\tconst sorted = [...keys].sort(compareFn);\n\treturn options.sortOrder === \"desc\" ? sorted.reverse() : sorted;\n}\n\nfunction detectIndent(text: string): number {\n\tfor (const line of text.split(\"\\n\")) {\n\t\tconst match = line.match(/^(\\s+)/);\n\t\tif (match?.[1]) {\n\t\t\treturn match[1].length;\n\t\t}\n\t}\n\n\treturn 2;\n}\n","/**\n * serverProxyGlobal — `~/.serviceme/server-proxy.json` IO (rev.20).\n *\n * Persists the r7 BYOM Server Proxy toggle's self-state (`enabled` flag\n * + last-known server URL) in a cross-tool global config file under\n * `~/.serviceme/`. Mirrors the convention used by `scheduled-tasks.json`,\n * `migration-failures.json`, etc.\n *\n * Originally lived in `apps/extension/src/services/proxy/globalConfigStore.ts`\n * (rev.19). Promoted to `@serviceme/devtools-core` in rev.20 so the CLI\n * and Server can read the same file without booting VS Code.\n *\n * Why a dedicated `server-proxy.json` rather than a unified config?\n * - One file per concern = atomic writes = no shared-schema lock.\n * `scheduled-tasks.json` already takes this approach.\n *\n * Atomic write protocol (rev.20):\n * 1. Compute patch (read + merge).\n * 2. Open `<target>.tmp-<randomUUID>` for write in the same dir.\n * 3. `writeFile(content)` then `fh.sync()` (fsync) — flush to disk.\n * 4. `fs.rename(tmp, target)` — POSIX atomic on the same filesystem.\n *\n * The `crypto.randomUUID()` suffix + `fh.sync()` address two hardening\n * gaps identified in the rev.19 review:\n * - Concurrency: tmp filename collisions under same-pid concurrent\n * writes become effectively impossible.\n * - Durability: without fsync, a power loss between `writeFile` and\n * `rename` could surface as a stale target file (POSIX rename is\n * atomic, but the data behind it might not have hit the platter).\n */\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs/promises\";\nimport { open } from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport { getServerProxyGlobalPath, SERVER_PROXY_GLOBAL_FILENAME } from \"./userHome\";\n\nexport { SERVER_PROXY_GLOBAL_FILENAME } from \"./userHome\";\nexport { getServerProxyGlobalPath };\n\n/** On-disk shape for `~/.serviceme/server-proxy.json`. */\nexport interface ServerProxyGlobalState {\n\t/** Whether the user has previously enabled Server Proxy. */\n\tenabled: boolean;\n\t/** Last server URL we toggled ON with. Used for re-toggling and UI hint. */\n\tlastServerUrl: string | undefined;\n\t/**\n\t * rev.21 — explicit opt-in to enable Server Proxy even when\n\t * `http.proxySupport === \"override\"`. `override` mode forces ALL\n\t * extensions to route through `http.proxy`; defaulting to `false`\n\t * keeps the legacy defensive guard against accidental global\n\t * hijack. Power users (e.g. corp environments that WANT all\n\t * extensions including GitHub Copilot Chat to route through the\n\t * corp tunnel) can flip this on via the Webview's \"Allow under\n\t * proxySupport=override\" affordance.\n\t *\n\t * Note: stored in the global file alongside `enabled` because\n\t * this is *our* self-flag — it has nothing to do with VS Code's\n\t * `http.*` schema.\n\t */\n\tallowOverride: boolean;\n\t/** ISO 8601 timestamp of the most recent write. Diagnostic only. */\n\tupdatedAt: string;\n}\n\n/**\n * Patch semantics (mirrors `vscode.workspace.getConfiguration().update(...)`):\n * - Field omitted / `undefined` → leave the existing value alone.\n * - Field set to `null` → explicitly clear the field.\n *\n * Only `lastServerUrl` distinguishes \"leave alone\" from \"clear\"; `enabled`\n * and `allowOverride` are plain `boolean | undefined` because boolean is\n * the natural unit (false = \"the user disabled it\").\n */\nexport interface ServerProxyGlobalPatch {\n\tenabled?: boolean;\n\tallowOverride?: boolean;\n\tlastServerUrl?: string | null;\n}\n\n/**\n * Reads the global config file. Returns `null` if the file does not\n * exist (first-run case) — does NOT throw. Throws on parse errors or\n * permission issues, so callers can surface those to the user.\n */\nexport async function readServerProxyGlobal(): Promise<ServerProxyGlobalState | null> {\n\tconst filePath = getServerProxyGlobalPath();\n\ttry {\n\t\tconst raw = await fs.readFile(filePath, \"utf8\");\n\t\tconst parsed: unknown = JSON.parse(raw);\n\t\tif (!isServerProxyGlobalState(parsed)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid ${SERVER_PROXY_GLOBAL_FILENAME}: expected {enabled: boolean, lastServerUrl?: string, updatedAt: string}, got ${JSON.stringify(parsed).slice(0, 80)}`\n\t\t\t);\n\t\t}\n\t\treturn parsed;\n\t} catch (err: unknown) {\n\t\tif (isENOENT(err)) return null;\n\t\tthrow err;\n\t}\n}\n\n/**\n * Merges a partial patch into the existing state and writes back\n * atomically. Creates `~/.serviceme/` if missing.\n *\n * Returns the post-write state. Throws on IO/JSON errors.\n */\nexport async function writeServerProxyGlobal(\n\tpatch: ServerProxyGlobalPatch\n): Promise<ServerProxyGlobalState> {\n\tconst filePath = getServerProxyGlobalPath();\n\tconst dirPath = path.dirname(filePath);\n\n\tawait fs.mkdir(dirPath, { recursive: true });\n\n\tconst current = (await readServerProxyGlobal()) ?? {\n\t\tenabled: false,\n\t\tallowOverride: false,\n\t\tlastServerUrl: undefined,\n\t\tupdatedAt: new Date(0).toISOString(),\n\t};\n\n\tconst next: ServerProxyGlobalState = {\n\t\tenabled: patch.enabled !== undefined ? patch.enabled : current.enabled,\n\t\tallowOverride: patch.allowOverride !== undefined ? patch.allowOverride : current.allowOverride,\n\t\tlastServerUrl:\n\t\t\tpatch.lastServerUrl === undefined\n\t\t\t\t? current.lastServerUrl\n\t\t\t\t: patch.lastServerUrl === null\n\t\t\t\t\t? undefined\n\t\t\t\t\t: patch.lastServerUrl,\n\t\tupdatedAt: new Date().toISOString(),\n\t};\n\n\tconst tmpPath = `${filePath}.tmp-${randomUUID()}`;\n\tconst fh = await open(tmpPath, \"w\");\n\ttry {\n\t\tawait fh.writeFile(JSON.stringify(next, null, \"\\t\"), \"utf8\");\n\t\t// Force data to platter before rename — without this, a power\n\t\t// loss between writeFile and rename could surface as a stale\n\t\t// target file (POSIX rename is atomic, but the data behind\n\t\t// it might not have hit disk).\n\t\tawait fh.sync();\n\t} finally {\n\t\tawait fh.close();\n\t}\n\tawait fs.rename(tmpPath, filePath);\n\n\treturn next;\n}\n\n/**\n * Best-effort migration of a legacy `enabled` flag (typically read from\n * VS Code's `settings.json` at `serverProxy.enabled`) into the global\n * config file.\n *\n * Reads via the provided accessor (so tests can stub it), writes the\n * global flag if present, then **clears** the legacy flag via the\n * provided writer — never silently leaves the migration half-done.\n *\n * Returns the migrated state, or `null` if no migration was needed\n * (legacy flag absent OR global config already up to date).\n */\nexport async function migrateLegacyServerProxyEnabled(\n\treadLegacy: () => boolean | undefined,\n\tclearLegacy: () => Promise<void>\n): Promise<ServerProxyGlobalState | null> {\n\tconst legacyEnabled = readLegacy();\n\tif (legacyEnabled !== true) return null;\n\n\tconst existing = await readServerProxyGlobal();\n\tif (existing?.enabled === true) {\n\t\t// Already migrated in a previous activation; just clean up the\n\t\t// legacy flag so we don't keep reading it.\n\t\tawait clearLegacy();\n\t\treturn null;\n\t}\n\n\tconst next = await writeServerProxyGlobal({ enabled: true });\n\tawait clearLegacy();\n\treturn next;\n}\n\n// ─── type guards / error checks (kept private to this module) ─────────────\n\nfunction isServerProxyGlobalState(v: unknown): v is ServerProxyGlobalState {\n\tif (!v || typeof v !== \"object\") return false;\n\tconst obj = v as Record<string, unknown>;\n\tif (typeof obj.enabled !== \"boolean\") return false;\n\tif (typeof obj.allowOverride !== \"boolean\") return false;\n\tif (typeof obj.updatedAt !== \"string\") return false;\n\t// rev.21 — `lastServerUrl` is `string | undefined`. Null is a legacy\n\t// round-trip artefact from rev.20's `cat <<EOF` fixture, treat it\n\t// the same as undefined rather than rejecting the whole file.\n\tif (\n\t\tobj.lastServerUrl !== undefined &&\n\t\tobj.lastServerUrl !== null &&\n\t\ttypeof obj.lastServerUrl !== \"string\"\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nfunction isENOENT(err: unknown): boolean {\n\treturn (\n\t\ttypeof err === \"object\" &&\n\t\terr !== null &&\n\t\t\"code\" in err &&\n\t\t(err as { code: unknown }).code === \"ENOENT\"\n\t);\n}\n","/**\n * Spec §11.12 — Phase 5 client-side state bootstrap.\n *\n * The server already exposes the Phase 5 surface (better-auth session\n * endpoints, device enrollment routes, toolbox + profile API). The\n * client (extension + CLI) needs the on-disk files to exist on first\n * run so that subsequent code can `readFile()` without an existence\n * check, and so the user can grep `~/.serviceme/` and see the\n * expected layout instead of an empty directory.\n *\n * Strategy: idempotently write the empty / default JSON for every\n * Phase 5 file that doesn't already exist. Existing files are\n * left untouched so the bootstrap can re-run on every extension\n * activation without overwriting user state.\n *\n * All writes are `mkdir -p` + `writeFile` (no race), and failures\n * are surfaced but never thrown — Phase 5 is auxiliary; the\n * extension / CLI should keep working even if the user's home\n * directory is read-only.\n *\n * @see docs/architecture/skill-agent-v2-repo.md §11.12\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport {\n\tgetCredentialsConfigPath,\n\tgetDeviceJsonPath,\n\tgetMachineIdPath,\n\tgetProfilesJsonPath,\n\tgetServicemeHome,\n\tgetToolboxJsonPath,\n} from \"../paths/userHome\";\n\n/** Per-file default content. `null` for the machine-id file because it\n * is a bare uuid with no JSON wrapper. */\ninterface Phase5FileSpec {\n\tpath: string;\n\tdefaultContent: string;\n}\n\nfunction getPhase5FileSpecs(): Phase5FileSpec[] {\n\treturn [\n\t\t{\n\t\t\tpath: getCredentialsConfigPath(),\n\t\t\t// Mirrors better-auth's client-side session shape; an empty\n\t\t\t// `sessions: []` makes the auth service treat the user as\n\t\t\t// logged-out without a per-read fallback.\n\t\t\tdefaultContent: JSON.stringify({ version: 1, sessions: [] }, null, \"\\t\"),\n\t\t},\n\t\t{\n\t\t\tpath: getDeviceJsonPath(),\n\t\t\t// Device enrollment payload. Empty until the user runs the\n\t\t\t// device-claim flow; the server treats absent fields as\n\t\t\t// \"unclaimed\".\n\t\t\tdefaultContent: JSON.stringify(\n\t\t\t\t{ version: 1, claimed: false, publicKeyFingerprint: null },\n\t\t\t\tnull,\n\t\t\t\t\"\\t\"\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tpath: getToolboxJsonPath(),\n\t\t\t// Local toolbox state; server is the source of truth for\n\t\t\t// installed tools, this is the per-user pref cache.\n\t\t\tdefaultContent: JSON.stringify({ version: 1, installed: [], preferences: {} }, null, \"\\t\"),\n\t\t},\n\t\t{\n\t\t\tpath: getMachineIdPath(),\n\t\t\t// Random uuid, written as a bare string. Subsequent\n\t\t\t// activations see the file and skip re-randomizing.\n\t\t\tdefaultContent: randomUUID(),\n\t\t},\n\t\t{\n\t\t\tpath: getProfilesJsonPath(),\n\t\t\t// Cached projection of the server's profile table; an empty\n\t\t\t// list means \"no profiles yet\" rather than \"unknown\".\n\t\t\tdefaultContent: JSON.stringify({ version: 1, profiles: [] }, null, \"\\t\"),\n\t\t},\n\t];\n}\n\nexport interface BootstrapPhase5Result {\n\t/** Files that were created by this run (already-existing files excluded). */\n\tcreated: string[];\n\t/** Files that were already present and left untouched. */\n\tskipped: string[];\n\t/** Files where the write attempt failed (e.g. read-only home). */\n\tfailed: Array<{ path: string; reason: string }>;\n}\n\n/**\n * Idempotently create the Phase 5 placeholder files under\n * `~/.serviceme/`. Safe to call on every extension activation —\n * existing files are detected via `access` and skipped.\n */\nexport async function bootstrapPhase5Placeholders(): Promise<BootstrapPhase5Result> {\n\tconst result: BootstrapPhase5Result = { created: [], skipped: [], failed: [] };\n\tconst home = getServicemeHome();\n\n\t// Make sure the home directory itself exists before any per-file\n\t// mkdir. mkdir({ recursive: true }) is a no-op when the dir is\n\t// already there, so this is safe to repeat.\n\ttry {\n\t\tawait fs.mkdir(home, { recursive: true });\n\t} catch (err) {\n\t\tresult.failed.push({ path: home, reason: (err as Error).message });\n\t\treturn result;\n\t}\n\n\tfor (const spec of getPhase5FileSpecs()) {\n\t\ttry {\n\t\t\tawait fs.access(spec.path);\n\t\t\tresult.skipped.push(spec.path);\n\t\t} catch {\n\t\t\t// ENOENT (or EACCES on a parent) — try to write.\n\t\t\ttry {\n\t\t\t\tawait fs.mkdir(path.dirname(spec.path), { recursive: true });\n\t\t\t\tawait fs.writeFile(spec.path, spec.defaultContent, \"utf8\");\n\t\t\t\tresult.created.push(spec.path);\n\t\t\t} catch (writeErr) {\n\t\t\t\tresult.failed.push({ path: spec.path, reason: (writeErr as Error).message });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport {\n\tcreateServicemeError,\n\ttype ServiceMeProjectExtractTemplateInput,\n\ttype ServiceMeProjectExtractTemplateResult,\n\ttype ServiceMeProjectGitInitResult,\n\ttype ServiceMeProjectInstallDepsResult,\n\ttype ServiceMeProjectMakeScriptsExecutableResult,\n\ttype ServiceMeProjectScaffoldPruneResult,\n} from \"@serviceme/devtools-protocol\";\nimport { runCommand } from \"../process/runCommand\";\nimport { moveFiles, unzipFile } from \"../utils/fileUtils\";\n\nexport class ProjectTools {\n\tasync extractTemplate(\n\t\tzipPath: string,\n\t\tworkspacePath: string,\n\t\ttempExtractDir: string,\n\t\tinput: ServiceMeProjectExtractTemplateInput\n\t): Promise<ServiceMeProjectExtractTemplateResult> {\n\t\tawait unzipFile(zipPath, tempExtractDir);\n\n\t\tlet sourceDir = path.join(tempExtractDir, input.extractedDirName);\n\t\tlet actualDirName = input.extractedDirName;\n\n\t\tif (!(await this.pathExists(sourceDir))) {\n\t\t\tconst entries = await fs.readdir(tempExtractDir, { withFileTypes: true });\n\t\t\tconst directories = entries.filter(\n\t\t\t\t(entry) => entry.isDirectory() && !entry.name.startsWith(\".\")\n\t\t\t);\n\n\t\t\tconst selectedDirectory = await this.selectExtractedDirectory(\n\t\t\t\tdirectories.map((directory) => directory.name),\n\t\t\t\ttempExtractDir,\n\t\t\t\tinput.projectFilePattern,\n\t\t\t\tinput.extractedDirName\n\t\t\t);\n\n\t\t\tif (selectedDirectory) {\n\t\t\t\tactualDirName = selectedDirectory;\n\t\t\t\tsourceDir = path.join(tempExtractDir, actualDirName);\n\t\t\t} else if (directories.length === 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`No directory found after extraction. Expected directory: ${input.extractedDirName}`\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Multiple directories found after extraction: ${directories\n\t\t\t\t\t\t.map((directory) => directory.name)\n\t\t\t\t\t\t.join(\", \")}. Expected: ${input.extractedDirName}`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (!(await this.pathExists(sourceDir))) {\n\t\t\tthrow new Error(`Source directory not found: ${sourceDir}`);\n\t\t}\n\n\t\tawait moveFiles(sourceDir, workspacePath, true);\n\n\t\treturn {\n\t\t\tactualDirName,\n\t\t};\n\t}\n\n\tasync installDependencies(\n\t\tworkspacePath: string,\n\t\tcommand?: string\n\t): Promise<ServiceMeProjectInstallDepsResult> {\n\t\tif (!command) {\n\t\t\tthrow createServicemeError(\"invalid_params\", \"Expected install command.\");\n\t\t}\n\n\t\tawait runCommand(command, {\n\t\t\tcwd: workspacePath,\n\t\t\tshell: true,\n\t\t});\n\n\t\treturn {\n\t\t\tinstalled: true,\n\t\t\tcommand,\n\t\t};\n\t}\n\n\tasync makeScriptsExecutable(\n\t\tworkspacePath: string\n\t): Promise<ServiceMeProjectMakeScriptsExecutableResult> {\n\t\tconst isWindows = process.platform === \"win32\";\n\t\tconst scripts = await this.findScripts(workspacePath, isWindows ? [\".ps1\", \".bat\"] : [\".sh\"]);\n\n\t\tlet updatedCount = 0;\n\n\t\tif (isWindows) {\n\t\t\tfor (const scriptPath of scripts.filter((script) => script.endsWith(\".ps1\"))) {\n\t\t\t\ttry {\n\t\t\t\t\tawait runCommand(`powershell -Command \"Unblock-File -Path '${scriptPath}'\"`, {\n\t\t\t\t\t\tcwd: workspacePath,\n\t\t\t\t\t\tshell: true,\n\t\t\t\t\t});\n\t\t\t\t\tupdatedCount += 1;\n\t\t\t\t} catch {\n\t\t\t\t\t// Match the extension's best-effort behavior and continue processing.\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor (const scriptPath of scripts) {\n\t\t\t\ttry {\n\t\t\t\t\tawait fs.chmod(scriptPath, 0o755);\n\t\t\t\t\tupdatedCount += 1;\n\t\t\t\t} catch {\n\t\t\t\t\t// Match the extension's best-effort behavior and continue processing.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tprocessedCount: scripts.length,\n\t\t\tupdatedCount,\n\t\t\tplatform: process.platform,\n\t\t\tscripts,\n\t\t};\n\t}\n\n\tasync initializeGit(workspacePath: string): Promise<ServiceMeProjectGitInitResult> {\n\t\tconst command = \"git init\";\n\t\tawait runCommand(command, {\n\t\t\tcwd: workspacePath,\n\t\t\tshell: true,\n\t\t});\n\n\t\treturn {\n\t\t\tinitialized: true,\n\t\t\tcommand,\n\t\t};\n\t}\n\n\tasync runScaffoldPrune(\n\t\tworkspacePath: string,\n\t\tpreset?: string\n\t): Promise<ServiceMeProjectScaffoldPruneResult> {\n\t\tif (!preset) {\n\t\t\tthrow createServicemeError(\"invalid_params\", \"Expected scaffold preset.\");\n\t\t}\n\n\t\tawait this.ensurePresetManifest(workspacePath, preset);\n\n\t\tconst pruneCommand = `pnpm run scaffold:prune -- --preset ${preset} --project-root .`;\n\t\tconst initMetadataCommand = `pnpm run scaffold:init-metadata -- --preset ${preset} --project-root . --force`;\n\n\t\tconst commands = [pruneCommand, initMetadataCommand];\n\n\t\tfor (const command of commands) {\n\t\t\tif (command === initMetadataCommand) {\n\t\t\t\t// Some templates remove .ms-scaffold/presets during prune.\n\t\t\t\t// Recover it again before init-metadata calls loadPreset().\n\t\t\t\tawait this.ensurePresetManifest(workspacePath, preset);\n\t\t\t}\n\n\t\t\tawait runCommand(command, {\n\t\t\t\tcwd: workspacePath,\n\t\t\t\tshell: true,\n\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\tapplied: true,\n\t\t\tpreset,\n\t\t\tcommands,\n\t\t};\n\t}\n\n\tprivate async ensurePresetManifest(workspacePath: string, preset: string): Promise<void> {\n\t\tconst presetManifestPath = path.join(\n\t\t\tworkspacePath,\n\t\t\t\".ms-scaffold\",\n\t\t\t\"presets\",\n\t\t\t`${preset}.json`\n\t\t);\n\n\t\tif (await this.pathExists(presetManifestPath)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst projectModePath = path.join(workspacePath, \".ms-scaffold\", \"project-mode.json\");\n\t\tif (!(await this.pathExists(projectModePath))) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst projectModeRaw = await fs.readFile(projectModePath, \"utf8\");\n\t\tconst projectMode = JSON.parse(projectModeRaw) as {\n\t\t\tselectedModules?: unknown;\n\t\t\tprunedModules?: unknown;\n\t\t};\n\n\t\tconst synthesizedPreset = {\n\t\t\tpreset,\n\t\t\tselectedModules: Array.isArray(projectMode.selectedModules)\n\t\t\t\t? projectMode.selectedModules\n\t\t\t\t: [],\n\t\t\tprunedModules: Array.isArray(projectMode.prunedModules) ? projectMode.prunedModules : [],\n\t\t\texclude: [] as string[],\n\t\t\trecommendedFollowUps: [] as string[],\n\t\t\tmanagedFiles: [] as string[],\n\t\t\tmergeManagedFiles: [] as string[],\n\t\t\tuserOwnedPaths: [] as string[],\n\t\t};\n\n\t\tawait fs.mkdir(path.dirname(presetManifestPath), { recursive: true });\n\t\tawait fs.writeFile(\n\t\t\tpresetManifestPath,\n\t\t\t`${JSON.stringify(synthesizedPreset, null, 2)}\\n`,\n\t\t\t\"utf8\"\n\t\t);\n\t}\n\n\tprivate async findScripts(dir: string, extensions: string[]): Promise<string[]> {\n\t\tconst results: string[] = [];\n\t\tlet entries: import(\"node:fs\").Dirent[];\n\n\t\ttry {\n\t\t\tentries = await fs.readdir(dir, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn results;\n\t\t}\n\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = path.join(dir, entry.name);\n\t\t\tif (entry.isDirectory() && entry.name !== \"node_modules\" && !entry.name.startsWith(\".\")) {\n\t\t\t\tresults.push(...(await this.findScripts(fullPath, extensions)));\n\t\t\t} else if (entry.isFile() && extensions.some((ext) => entry.name.endsWith(ext))) {\n\t\t\t\tresults.push(fullPath);\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\tprivate async pathExists(targetPath: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait fs.access(targetPath);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate async selectExtractedDirectory(\n\t\tdirectoryNames: string[],\n\t\ttempExtractDir: string,\n\t\tprojectFilePattern: string,\n\t\texpectedDirName: string\n\t): Promise<string | null> {\n\t\tif (directoryNames.length === 1) {\n\t\t\treturn directoryNames[0] ?? null;\n\t\t}\n\n\t\tconst exactMatch = directoryNames.find((directoryName) => directoryName === expectedDirName);\n\t\tif (exactMatch) {\n\t\t\treturn exactMatch;\n\t\t}\n\n\t\tconst matches: string[] = [];\n\t\tfor (const directoryName of directoryNames) {\n\t\t\tif (\n\t\t\t\tawait this.directoryMatchesProjectPattern(\n\t\t\t\t\tpath.join(tempExtractDir, directoryName),\n\t\t\t\t\tprojectFilePattern\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tmatches.push(directoryName);\n\t\t\t}\n\t\t}\n\n\t\tif (matches.length === 1) {\n\t\t\treturn matches[0] ?? null;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate async directoryMatchesProjectPattern(\n\t\tdirectoryPath: string,\n\t\tprojectFilePattern: string\n\t): Promise<boolean> {\n\t\tconst entries = await fs.readdir(directoryPath);\n\t\tif (projectFilePattern.includes(\"*\")) {\n\t\t\tconst regex = new RegExp(`^${projectFilePattern.replace(\"*\", \".*\")}$`);\n\t\t\treturn entries.some((entry) => regex.test(entry));\n\t\t}\n\n\t\treturn entries.includes(projectFilePattern);\n\t}\n}\n\nexport function createProjectTools(): ProjectTools {\n\treturn new ProjectTools();\n}\n","import { constants, createWriteStream } from \"node:fs\";\nimport { access, copyFile, lstat, mkdir, readdir, rename, rm } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport type { Readable } from \"node:stream\";\nimport yauzl from \"yauzl\";\n\nexport const unzipFile = (zipPath: string, dest: string): Promise<void> => {\n\treturn new Promise((resolve, reject) => {\n\t\tyauzl.open(zipPath, { lazyEntries: true }, (err: Error | null, zipfile?: yauzl.ZipFile) => {\n\t\t\tif (err) return reject(err);\n\t\t\tif (!zipfile) return reject(new Error(\"Failed to open zip file.\"));\n\n\t\t\tzipfile.readEntry();\n\t\t\tzipfile.on(\"entry\", (entry: yauzl.Entry) => {\n\t\t\t\tif (/\\/$/.test(entry.fileName)) {\n\t\t\t\t\tvoid mkdir(join(dest, entry.fileName), { recursive: true })\n\t\t\t\t\t\t.then(() => {\n\t\t\t\t\t\t\tzipfile.readEntry();\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(reject);\n\t\t\t\t} else {\n\t\t\t\t\tconst outputPath = join(dest, entry.fileName);\n\t\t\t\t\tvoid mkdir(dirname(outputPath), { recursive: true })\n\t\t\t\t\t\t.then(() => {\n\t\t\t\t\t\t\tzipfile.openReadStream(\n\t\t\t\t\t\t\t\tentry,\n\t\t\t\t\t\t\t\t(streamError: Error | null, readStream: Readable | null) => {\n\t\t\t\t\t\t\t\t\tif (streamError) return reject(streamError);\n\t\t\t\t\t\t\t\t\tif (!readStream) return reject(new Error(\"Failed to open zip entry stream.\"));\n\n\t\t\t\t\t\t\t\t\tconst writeStream = createWriteStream(outputPath);\n\t\t\t\t\t\t\t\t\treadStream.on(\"error\", reject);\n\t\t\t\t\t\t\t\t\twriteStream.on(\"error\", reject);\n\t\t\t\t\t\t\t\t\twriteStream.on(\"close\", () => {\n\t\t\t\t\t\t\t\t\t\tzipfile.readEntry();\n\t\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\t\treadStream.pipe(writeStream);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(reject);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tzipfile.on(\"end\", () => {\n\t\t\t\tresolve();\n\t\t\t});\n\n\t\t\tzipfile.on(\"error\", (zipError: Error) => {\n\t\t\t\treject(zipError);\n\t\t\t});\n\t\t});\n\t});\n};\n\nconst tryLstat = async (targetPath: string) => {\n\ttry {\n\t\treturn await lstat(targetPath);\n\t} catch {\n\t\treturn null;\n\t}\n};\n\nconst mergeEntry = async (\n\tsourcePath: string,\n\tdestPath: string,\n\toverwrite: boolean\n): Promise<void> => {\n\tconst sourceStat = await lstat(sourcePath);\n\tconst destStat = await tryLstat(destPath);\n\n\tif (sourceStat.isDirectory()) {\n\t\tif (destStat && !destStat.isDirectory()) {\n\t\t\tif (!overwrite) {\n\t\t\t\tawait rm(sourcePath, { recursive: true, force: true });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait rm(destPath, { recursive: true, force: true });\n\t\t}\n\n\t\tawait mkdir(destPath, { recursive: true });\n\t\tconst children = await readdir(sourcePath);\n\n\t\tfor (const child of children) {\n\t\t\tawait mergeEntry(join(sourcePath, child), join(destPath, child), overwrite);\n\t\t}\n\n\t\tawait rm(sourcePath, { recursive: true, force: true });\n\t\treturn;\n\t}\n\n\tif (destStat) {\n\t\tif (!overwrite) {\n\t\t\tawait rm(sourcePath, { recursive: true, force: true });\n\t\t\treturn;\n\t\t}\n\n\t\tawait rm(destPath, { recursive: true, force: true });\n\t}\n\n\ttry {\n\t\tawait rename(sourcePath, destPath);\n\t} catch {\n\t\tawait copyFile(sourcePath, destPath);\n\t\tawait rm(sourcePath, { recursive: true, force: true });\n\t}\n};\n\nexport const moveFiles = async (\n\tsourceDir: string,\n\tdestDir: string,\n\toverwrite = false\n): Promise<void> => {\n\tawait mkdir(destDir, { recursive: true });\n\tconst files = await readdir(sourceDir);\n\n\tfor (const file of files) {\n\t\tconst sourceFile = join(sourceDir, file);\n\t\tconst destFile = join(destDir, file);\n\n\t\tif (!overwrite) {\n\t\t\ttry {\n\t\t\t\tawait access(destFile, constants.F_OK);\n\t\t\t\tawait rm(sourceFile, { recursive: true, force: true });\n\t\t\t\tcontinue;\n\t\t\t} catch {\n\t\t\t\t// destination missing, continue with merge\n\t\t\t}\n\t\t}\n\n\t\tawait mergeEntry(sourceFile, destFile, overwrite);\n\t}\n};\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type { GitClient } from \"../git-client\";\nimport type { PullResult } from \"../git-client/types\";\nimport {\n\tassertSafeRepoId,\n\tgetRepoDir,\n\tgetServicemeHome,\n\tSAFE_REPO_ID_PATTERN,\n} from \"../paths/userHome\";\nimport type { ReposStore } from \"../repos/store\";\nimport type { AnyRepoConfig, RepoConfig, UserRepoConfig } from \"../repos/types\";\nimport { isDefaultRepo, isUserRepo } from \"../repos/types\";\n\n/**\n * Skill & Agent v2 — Client RepoManager (M3)\n *\n * RepoManager is the orchestrator for local git repo management. It\n * composes:\n * - {@link ReposStore} (in-memory CRUD over `~/.serviceme/repos.json`)\n * - {@link GitClient} (spawns `git` against the server proxy)\n * - the filesystem (clone targets live under `~/.serviceme/repos/<id>/`)\n *\n * Lifecycle responsibilities:\n * - **Bootstrap**: on first launch, clone all enabled default repos.\n * - **Sync**: pullOne / pullAll for periodic updates.\n * - **User repos**: addUserRepo validates URL, probes branch via\n * `git ls-remote`, writes the entry, and triggers an async clone.\n * - **Cleanup**: removeUserRepo deletes both the local clone AND the\n * `repos.json` entry. disableRepo / enableRepo only flip a flag.\n *\n * Default repos (`source === 'default'`) cannot be removed — only\n * disabled. The Store enforces this; RepoManager propagates the error.\n *\n * @see docs/architecture/skill-agent-v2-repo.md §5.3 RepoManager\n */\n\nexport interface RepoManagerOptions {\n\tstore: ReposStore;\n\tgitClient: GitClient;\n\t/** Override the clock for deterministic tests. */\n\tnow?: () => string;\n\t/** Skip the actual git clone (used by tests). When true, the\n\t * filesystem-side mkdir is the only side effect. */\n\tskipClone?: boolean;\n}\n\nexport interface AddUserRepoInput {\n\t/** Upstream URL the user pasted in. */\n\turl: string;\n\t/** Branch to track. Auto-detected from upstream when omitted. */\n\tbranch?: string;\n\t/** Optional display name (defaults to id). */\n\tname?: string;\n\t/** Whether this repo participates in scheduled syncs. Defaults to true. */\n\tenabled?: boolean;\n\t/** Route git traffic via the server proxy (default true). */\n\tuseProxy?: boolean;\n}\n\nexport interface AddUserRepoResult {\n\trepo: UserRepoConfig;\n\tbranch: string;\n\tcloned: boolean;\n}\n\n/** Snapshot of a sync run, suitable for UI rendering. */\nexport interface SyncReport {\n\tpulls: Array<{\n\t\trepoId: string;\n\t\tstatus: \"ok\" | \"error\";\n\t\tresult?: PullResult;\n\t\terror?: string;\n\t}>;\n}\n\n/**\n * Slugify an arbitrary string into a SAFE_REPO_ID_PATTERN-friendly\n * fragment (lowercase alphanumerics, dashes, underscores).\n */\nfunction sanitizeIdPart(value: string): string {\n\treturn value\n\t\t.trim()\n\t\t.toLowerCase()\n\t\t.replace(/[^a-zA-Z0-9_-]/g, \"-\")\n\t\t.replace(/-+/g, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\");\n}\n\n/** Max length enforced by SAFE_REPO_ID_PATTERN. */\nconst REPO_ID_MAX_LENGTH = 64;\n\n/**\n * Append a branch suffix to a base id, truncating so the result stays\n * within SAFE_REPO_ID_PATTERN's length limit.\n */\nfunction withBranchSuffix(baseId: string, suffix: string): string {\n\treturn `${baseId}-${suffix}`.slice(0, REPO_ID_MAX_LENGTH).replace(/-+$/, \"\");\n}\n\n/** Pattern: kebab-case slug from a git URL's last segment. */\nfunction idFromUrl(url: string): string {\n\tconst trimmed = url\n\t\t.trim()\n\t\t.replace(/\\/+$/, \"\")\n\t\t.replace(/\\.git$/, \"\");\n\n\tlet owner = \"\";\n\tlet repo = \"\";\n\n\tif (/^https?:\\/\\//i.test(trimmed)) {\n\t\tconst parsed = new URL(trimmed);\n\t\tconst segments = parsed.pathname.split(\"/\").filter(Boolean);\n\t\trepo = segments.at(-1) ?? \"\";\n\t\towner = segments.length >= 2 ? (segments.at(-2) ?? \"\") : \"\";\n\t} else if (trimmed.startsWith(\"git@\")) {\n\t\tconst colon = trimmed.indexOf(\":\");\n\t\tconst repoPath = colon >= 0 ? trimmed.slice(colon + 1) : \"\";\n\t\tconst segments = repoPath.split(\"/\").filter(Boolean);\n\t\trepo = segments.at(-1) ?? \"\";\n\t\towner = segments.length >= 2 ? (segments.at(-2) ?? \"\") : \"\";\n\t} else {\n\t\trepo = trimmed.split(\"/\").pop() ?? \"\";\n\t}\n\n\tconst safeRepo = sanitizeIdPart(repo);\n\tconst safeOwner = sanitizeIdPart(owner);\n\tconst safe = safeOwner ? `${safeOwner}-${safeRepo}` : safeRepo;\n\tif (!SAFE_REPO_ID_PATTERN.test(safe)) {\n\t\tthrow new InvalidRepoUrlError(`cannot derive a valid repo id from url: ${url}`);\n\t}\n\treturn safe;\n}\n\nfunction proxyRepoId(url: string): string {\n\tconst encoded = Buffer.from(url.trim(), \"utf8\").toString(\"base64url\");\n\treturn `repo-${encoded}`;\n}\n\n/** Sentinel errors. */\nexport class RepoManagerError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"RepoManagerError\";\n\t}\n}\nexport class InvalidRepoUrlError extends RepoManagerError {}\nexport class RepoCloneConflictError extends RepoManagerError {}\nexport class RepoNotInStoreError extends RepoManagerError {}\n\nexport class RepoManager {\n\tprivate readonly store: ReposStore;\n\tprivate readonly git: GitClient;\n\tprivate readonly now: () => string;\n\tprivate readonly skipClone: boolean;\n\n\tconstructor(opts: RepoManagerOptions) {\n\t\tthis.store = opts.store;\n\t\tthis.git = opts.gitClient;\n\t\tthis.now = opts.now ?? (() => new Date().toISOString());\n\t\tthis.skipClone = opts.skipClone ?? false;\n\t}\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Bootstrap\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Clone every enabled default repo that doesn't have a local clone\n\t * yet. Failures on individual repos are recorded in `lastSyncStatus`\n\t * but do NOT abort the loop — the UI surfaces per-repo state and the\n\t * user can retry.\n\t *\n\t * Per spec §3.1: iterate all default repos; the loop honors `enabled=false` and\n\t * disables (rather than removes) repos the user has turned off.\n\t */\n\tasync ensureDefaults(): Promise<SyncReport> {\n\t\tconst report: SyncReport = { pulls: [] };\n\t\tconst defaults = this.store.listDefault();\n\t\tfor (const repo of defaults) {\n\t\t\tif (!repo.enabled) continue;\n\t\t\tconst localPath = getRepoDir(repo.id);\n\t\t\tconst exists = await this.pathExists(localPath);\n\t\t\tif (exists) {\n\t\t\t\t// Directory exists but may be empty (e.g. interrupted clone).\n\t\t\t\t// Delete and re-clone so it becomes a valid git repo.\n\t\t\t\tif (await this.isValidGitRepo(localPath)) continue;\n\t\t\t\tawait fs.rm(localPath, { recursive: true, force: true });\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tif (!this.skipClone) {\n\t\t\t\t\tawait fs.mkdir(path.dirname(localPath), { recursive: true });\n\t\t\t\t\tawait this.git.clone(repo.id, repo.url, localPath, repo.branch, true);\n\t\t\t\t}\n\t\t\t\tawait this.store.updateRepo(repo.id, {\n\t\t\t\t\tlastSyncStatus: \"ok\",\n\t\t\t\t\tlastSyncAt: this.now(),\n\t\t\t\t\t// Clear any stale failure message (updateRepo merges\n\t\t\t\t\t// patches, so omitting the key would keep the old\n\t\t\t\t\t// error visible in the UI forever).\n\t\t\t\t\tlastSyncError: undefined,\n\t\t\t\t});\n\t\t\t\treport.pulls.push({ repoId: repo.id, status: \"ok\" });\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\tawait this.store.updateRepo(repo.id, {\n\t\t\t\t\tlastSyncStatus: \"error\",\n\t\t\t\t\tlastSyncAt: this.now(),\n\t\t\t\t\tlastSyncError: message,\n\t\t\t\t});\n\t\t\t\treport.pulls.push({ repoId: repo.id, status: \"error\", error: message });\n\t\t\t}\n\t\t}\n\t\treturn report;\n\t}\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Per-repo sync\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Pull a single repo. Updates `lastSync*` fields on success or failure.\n\t * Returns the raw `PullResult` so callers can surface fetch progress.\n\t */\n\tasync pullOne(repoId: string): Promise<PullResult> {\n\t\tconst repo = this.store.get(repoId);\n\t\tif (!repo) throw new RepoNotInStoreError(repoId);\n\t\tconst useProxy = repo.useProxy ?? true;\n\t\tconst proxyId = useProxy ? proxyRepoId(repo.url) : repo.id;\n\t\tconst localPath = getRepoDir(repoId);\n\t\tconst exists = await this.pathExists(localPath);\n\t\t// If directory exists but is empty or not a valid git repo, remove\n\t\t// it so the clone path below runs cleanly.\n\t\tif (exists && !(await this.isValidGitRepo(localPath))) {\n\t\t\tawait fs.rm(localPath, { recursive: true, force: true });\n\t\t}\n\t\tif (!exists || !(await this.pathExists(localPath))) {\n\t\t\t// First-time sync (or re-clone after invalid directory) — clone instead of pulling.\n\t\t\tawait fs.mkdir(path.dirname(localPath), { recursive: true });\n\t\t\tif (!this.skipClone) {\n\t\t\t\tawait this.git.clone(proxyId, repo.url, localPath, repo.branch, useProxy);\n\t\t\t}\n\t\t\tconst result: PullResult = {\n\t\t\t\tupdated: true,\n\t\t\t\tcommitSha: \"\",\n\t\t\t\tbranch: repo.branch,\n\t\t\t};\n\t\t\tawait this.store.updateRepo(repoId, {\n\t\t\t\tlastSyncStatus: \"ok\",\n\t\t\t\tlastSyncAt: this.now(),\n\t\t\t\t// Clear any stale failure message from a previous attempt —\n\t\t\t\t// updateRepo merges patches, so omitting the key would keep\n\t\t\t\t// the old error visible in the UI forever.\n\t\t\t\tlastSyncError: undefined,\n\t\t\t});\n\t\t\treturn result;\n\t\t}\n\t\ttry {\n\t\t\tconst result = await this.git.pull(proxyId, localPath, repo.branch, useProxy, repo.url);\n\t\t\tawait this.store.updateRepo(repoId, {\n\t\t\t\tlastSyncStatus: \"ok\",\n\t\t\t\tlastSyncAt: this.now(),\n\t\t\t\tlastSyncCommitSha: result.commitSha,\n\t\t\t\t// Clear the stale error (see the clone branch above).\n\t\t\t\tlastSyncError: undefined,\n\t\t\t});\n\t\t\treturn result;\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tawait this.store.updateRepo(repoId, {\n\t\t\t\tlastSyncStatus: \"error\",\n\t\t\t\tlastSyncAt: this.now(),\n\t\t\t\tlastSyncError: message,\n\t\t\t});\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/** Pull every enabled repo. Per-repo failures don't abort the run. */\n\tasync pullAll(): Promise<SyncReport> {\n\t\tconst report: SyncReport = { pulls: [] };\n\t\tconst enabled = this.store.list().filter((r) => r.enabled);\n\t\tfor (const repo of enabled) {\n\t\t\ttry {\n\t\t\t\tconst r = await this.pullOne(repo.id);\n\t\t\t\treport.pulls.push({ repoId: repo.id, status: \"ok\", result: r });\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\treport.pulls.push({ repoId: repo.id, status: \"error\", error: message });\n\t\t\t}\n\t\t}\n\t\treturn report;\n\t}\n\n\t/**\n\t * Ensure a repository exists locally and is detached at an exact\n\t * commit. Used by Copilot content restoration: never pulls to a\n\t * branch head, so a declaration always resolves to the reviewed\n\t * content on every machine.\n\t */\n\tasync ensureAtCommit(input: {\n\t\trepository: Pick<RepoConfig, \"id\" | \"url\" | \"useProxy\">;\n\t\tcommit: string;\n\t}): Promise<{ localPath: string; commit: string }> {\n\t\tconst localPath = getRepoDir(input.repository.id);\n\t\tconst exists = await this.pathExists(localPath);\n\t\tif (exists && !(await this.isValidGitRepo(localPath))) {\n\t\t\tawait fs.rm(localPath, { recursive: true, force: true });\n\t\t}\n\t\tif (!exists || !(await this.pathExists(localPath))) {\n\t\t\tawait fs.mkdir(path.dirname(localPath), { recursive: true });\n\t\t\tif (!this.skipClone) {\n\t\t\t\tawait this.git.clone(\n\t\t\t\t\t(input.repository.useProxy ?? true) ? input.repository.id : input.repository.id,\n\t\t\t\t\tinput.repository.url,\n\t\t\t\t\tlocalPath,\n\t\t\t\t\tundefined,\n\t\t\t\t\tinput.repository.useProxy ?? true\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tconst commit = this.skipClone\n\t\t\t? input.commit\n\t\t\t: await this.git.checkoutCommit(\n\t\t\t\t\tlocalPath,\n\t\t\t\t\tinput.commit,\n\t\t\t\t\tinput.repository.id,\n\t\t\t\t\tinput.repository.useProxy ?? true,\n\t\t\t\t\tinput.repository.url\n\t\t\t\t);\n\t\treturn { localPath, commit };\n\t}\n\n\t/**\n\t * Ensure a v2 catalog source is materialized under repos/<sourceId>\n\t * exactly like a git checkout (Task 10, ruling #1).\n\t *\n\t * The catalog store (repos.json) never held catalog sources, so the\n\t * source-lifecycle task owns this path: content arrives from the\n\t * provider as an extracted tree, is unpacked under the managed\n\t * source directory, and is verified against the immutable digest\n\t * before the directory becomes visible to resolvers.\n\t *\n\t * Callers that only need the directory contract (source availability\n\t * is a reposDir/<source-id> directory check) can pass a provider\n\t * that stages the tree; the method itself never writes store\n\t * entries — marketplace sources stay manifest-only.\n\t */\n\tasync ensureCatalogSource(input: {\n\t\tsourceId: string;\n\t\tprovider: {\n\t\t\t/** Stage the catalog payload into the target directory. */\n\t\t\tmaterialize: (targetDir: string) => Promise<void>;\n\t\t};\n\t}): Promise<{ localPath: string }> {\n\t\tassertSafeRepoId(input.sourceId);\n\t\tconst localPath = getRepoDir(input.sourceId);\n\t\tconst exists = await this.pathExists(localPath);\n\t\tif (exists && !(await this.isValidGitRepo(localPath))) {\n\t\t\t// An existing non-git directory may be a staged catalog tree\n\t\t\t// from a previous run — keep it. Only remove clearly invalid\n\t\t\t// empty leftovers before re-staging.\n\t\t\tconst entries = await fs.readdir(localPath).catch(() => [\"staged\"]);\n\t\t\tif (entries.length === 0) {\n\t\t\t\tawait fs.rm(localPath, { recursive: true, force: true });\n\t\t\t} else {\n\t\t\t\treturn { localPath };\n\t\t\t}\n\t\t}\n\t\tif (!exists || !(await this.pathExists(localPath))) {\n\t\t\tawait fs.mkdir(localPath, { recursive: true });\n\t\t\tawait input.provider.materialize(localPath);\n\t\t}\n\t\treturn { localPath };\n\t}\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// User repos: add / remove\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Resolve a unique store id for a repo being added.\n\t *\n\t * The base id derives from the URL (`owner-repo`). When it is taken\n\t * by the SAME url with a DIFFERENT branch — user repo or platform\n\t * default alike — derive `base-<branch>` so users can track several\n\t * branches of one repository side by side. Genuine duplicates (same\n\t * url + same branch) and cross-url id collisions still throw\n\t * RepoCloneConflictError.\n\t */\n\tprivate resolveUserRepoId(baseId: string, url: string, branch: string): string {\n\t\tconst existing = this.store.get(baseId);\n\t\tif (!existing) {\n\t\t\treturn baseId;\n\t\t}\n\t\tif (existing.url !== url) {\n\t\t\t// A different repository already owns this derived id — keep\n\t\t\t// the strict conflict so unrelated repos never silently share\n\t\t\t// ids.\n\t\t\tthrow new RepoCloneConflictError(`repo id '${baseId}' already exists`);\n\t\t}\n\t\tif (existing.branch === branch) {\n\t\t\tthrow new RepoCloneConflictError(\n\t\t\t\t`repo id '${baseId}' already exists (same url and branch '${branch}')`\n\t\t\t);\n\t\t}\n\t\t// Same url, different branch → make room with a branch suffix.\n\t\t// This also covers the default repo (e.g. adding a `main` variant\n\t\t// of medalsoftchina-ms-skills on top of the pinned `v2` default).\n\t\tconst suffix = sanitizeIdPart(branch) || \"branch\";\n\t\tlet candidate = withBranchSuffix(baseId, suffix);\n\t\tfor (let n = 2; ; n++) {\n\t\t\tconst taken = this.store.get(candidate);\n\t\t\tif (!taken) {\n\t\t\t\treturn candidate;\n\t\t\t}\n\t\t\tif (taken.url === url && taken.branch === branch) {\n\t\t\t\tthrow new RepoCloneConflictError(\n\t\t\t\t\t`repo id '${candidate}' already exists (same url and branch '${branch}')`\n\t\t\t\t);\n\t\t\t}\n\t\t\tcandidate = withBranchSuffix(baseId, `${suffix}-${n}`);\n\t\t}\n\t}\n\n\t/**\n\t * Validate URL, derive an id, detect the branch via `git ls-remote`,\n\t * then add the entry to the store and trigger a clone.\n\t *\n\t * The base id derives from the URL. Re-adding the same URL with a\n\t * different branch is allowed — it gets a branch-suffixed id\n\t * (`owner-repo-<branch>`) so multiple branches of one repository can\n\t * be tracked side by side.\n\t *\n\t * Spec §5.3 says \"branch detection\" happens BEFORE the store write so\n\t * the resulting `repos.json` is fully populated. The clone is async\n\t * but the function returns synchronously once the store is updated\n\t * (cloning happens via `ensureDefaults`-style background fire).\n\t *\n\t * For tests with `skipClone: true`, the clone is skipped entirely.\n\t */\n\tasync addUserRepo(input: AddUserRepoInput): Promise<AddUserRepoResult> {\n\t\tconst url = input.url.trim();\n\t\tif (!/^https?:\\/\\//.test(url) && !url.startsWith(\"git@\")) {\n\t\t\tthrow new InvalidRepoUrlError(`expected https:// or git@ URL, got: ${url}`);\n\t\t}\n\t\tconst id = idFromUrl(url);\n\t\tconst useProxy = input.useProxy ?? true;\n\t\tconst userProxyId = useProxy ? proxyRepoId(url) : id;\n\t\tassertSafeRepoId(id);\n\n\t\t// Detect branch if not provided\n\t\tlet branch = input.branch?.trim();\n\t\tif (!branch) {\n\t\t\tconst branches = await this.git.lsRemote(userProxyId, url, useProxy);\n\t\t\tconst head =\n\t\t\t\tbranches.find((b) => b.ref === \"refs/heads/main\") ??\n\t\t\t\tbranches.find((b) => b.ref === \"refs/heads/master\") ??\n\t\t\t\tbranches.find((b) => b.ref.endsWith(\"/v2\"));\n\t\t\tif (!head) {\n\t\t\t\tthrow new InvalidRepoUrlError(\n\t\t\t\t\t`could not detect default branch for ${url} (no refs/heads/{main,master,v2})`\n\t\t\t\t);\n\t\t\t}\n\t\t\tbranch = head.ref.replace(/^refs\\/heads\\//, \"\");\n\t\t}\n\n\t\t// Resolve a unique id BEFORE writing the store so we can give a\n\t\t// clean error message (or make room for another branch).\n\t\tconst uniqueId = this.resolveUserRepoId(id, url, branch);\n\t\tassertSafeRepoId(uniqueId);\n\n\t\tconst repo: UserRepoConfig = {\n\t\t\tid: uniqueId,\n\t\t\tname: input.name?.trim() || uniqueId,\n\t\t\turl,\n\t\t\tbranch,\n\t\t\tenabled: input.enabled ?? true,\n\t\t\tuseProxy,\n\t\t\twriteEnabled: false,\n\t\t\tsource: \"user\",\n\t\t\taddedAt: this.now(),\n\t\t};\n\t\tawait this.store.addUserRepo(repo);\n\n\t\t// Trigger async clone (or skip in tests)\n\t\tlet cloned = false;\n\t\tif (!this.skipClone) {\n\t\t\tconst localPath = getRepoDir(uniqueId);\n\t\t\tawait fs.mkdir(path.dirname(localPath), { recursive: true });\n\t\t\tawait this.git.clone(userProxyId, url, localPath, branch, useProxy);\n\t\t\tcloned = true;\n\t\t}\n\n\t\treturn { repo, branch, cloned };\n\t}\n\n\t/**\n\t * Remove a user-added repo. Throws when:\n\t * - the repo is unknown\n\t * - the repo is a default (use `disableRepo` instead — store enforces)\n\t *\n\t * The local clone is deleted via `fs.rm`. If the local dir is\n\t * already gone we silently succeed (idempotent re-remove).\n\t */\n\tasync removeUserRepo(repoId: string): Promise<void> {\n\t\tconst repo = this.store.get(repoId);\n\t\tif (!repo) throw new RepoNotInStoreError(repoId);\n\t\tif (isDefaultRepo(repo)) {\n\t\t\tthrow new RepoCloneConflictError(\n\t\t\t\t`cannot remove default repo '${repoId}'; use disableRepo() instead`\n\t\t\t);\n\t\t}\n\t\t// Store first — so the failure surface is the user-visible\n\t\t// repos.json change, not a half-deleted filesystem.\n\t\tawait this.store.removeUserRepo(repoId);\n\t\tconst localPath = getRepoDir(repoId);\n\t\ttry {\n\t\t\tawait fs.rm(localPath, { recursive: true, force: true });\n\t\t} catch (err) {\n\t\t\t// Swallow ENOENT — idempotent. Re-throw anything else.\n\t\t\tif ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n\t\t}\n\t}\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Enable / disable\n\t// ─────────────────────────────────────────────────────────────────\n\n\tasync disableRepo(repoId: string): Promise<AnyRepoConfig> {\n\t\treturn this.store.disableRepo(repoId);\n\t}\n\n\tasync enableRepo(repoId: string): Promise<AnyRepoConfig> {\n\t\treturn this.store.enableRepo(repoId);\n\t}\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Helpers\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/** `true` when `~/.serviceme/repos/<id>/` exists on disk. */\n\tasync hasLocalClone(repoId: string): Promise<boolean> {\n\t\treturn this.pathExists(getRepoDir(repoId));\n\t}\n\n\t/** Force-create the SERVICEME home directory tree (idempotent). */\n\tasync ensureHome(): Promise<void> {\n\t\tawait fs.mkdir(getServicemeHome(), { recursive: true });\n\t}\n\n\t/**\n\t * Returns `true` when `p` contains a `.git` entry — i.e. it is an\n\t * initialised git repository and safe to `pull`. Empty directories\n\t * (e.g. from an interrupted clone) return `false`.\n\t */\n\tprivate async isValidGitRepo(p: string): Promise<boolean> {\n\t\treturn this.pathExists(path.join(p, \".git\"));\n\t}\n\n\tprivate async pathExists(p: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait fs.stat(p);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\nexport type { RepoConfig };\n/** Type guard for AnyRepoConfig — re-exported for tests + extension code. */\nexport { isDefaultRepo, isUserRepo };\n","/**\n * `repos.json` schema — mirrors §3 of docs/architecture/skill-agent-v2-repo.md.\n *\n * The on-disk file lives at `${SERVICEME_HOME}/repos.json` (see\n * `paths/userHome.getReposConfigPath`). It holds the user-visible catalog of\n * skill/agent repositories (default + user-added) plus the currently-selected\n * default.\n *\n * Two flavours of repo:\n * - `DefaultRepoConfig` — hardcoded by SERVICEME; can only be disabled, not\n * removed.\n * - `UserRepoConfig` — added by the user via the \"Add repository\" flow;\n * can be removed freely.\n *\n * Both extend `RepoConfig` and add a `source` discriminant so consumers can\n * branch on `repo.source === 'default' | 'user'` without sniffing free-form\n * fields.\n */\n\n/** ISO 8601 timestamp. */\nexport type IsoTimestamp = string;\n\n/** Per-repo sync health — surfaced in the UI alongside the repo entry. */\nexport type SyncStatus = \"ok\" | \"error\";\n\n/** Discriminant on `RepoConfig.source` — narrow with a literal check. */\nexport type RepoSource = \"default\" | \"user\";\n\n/**\n * Common shape shared by both default and user-added repos. Source lives on\n * the type so we can keep the loader schema flat while still letting\n * `DefaultRepoConfig` carry a `description`.\n */\nexport interface RepoConfig {\n\t/** Stable id used in on-disk paths (must match `SAFE_REPO_ID_PATTERN`). */\n\tid: string;\n\t/** Display name shown in UI. */\n\tname: string;\n\t/** Upstream git URL (typically github.com/<owner>/<repo>.git). */\n\turl: string;\n\t/** Default branch to track (e.g. \"v2\", \"main\"). */\n\tbranch: string;\n\t/** Whether this repo participates in scheduled syncs. */\n\tenabled: boolean;\n\t/** Whether git traffic should go through the local server proxy (default true). */\n\tuseProxy?: boolean;\n\t/** Whether the user is allowed to push back to this repo. */\n\twriteEnabled: boolean;\n\t/** When the user (or first-run installer) added this repo. */\n\taddedAt: IsoTimestamp;\n\t/** Last successful sync timestamp. */\n\tlastSyncAt?: IsoTimestamp;\n\t/** Commit SHA at the time of the last successful sync. */\n\tlastSyncCommitSha?: string;\n\t/** Result of the most recent sync attempt. */\n\tlastSyncStatus?: SyncStatus;\n\t/** Error message from the most recent sync attempt (when `lastSyncStatus === 'error'`). */\n\tlastSyncError?: string;\n\t/** Whether this repo is a default or a user-added repo. */\n\tsource: RepoSource;\n}\n\n/** A default (bundled) repo — has a UI-facing description, cannot be removed. */\nexport interface DefaultRepoConfig extends RepoConfig {\n\tsource: \"default\";\n\tdescription: string;\n}\n\n/** A user-added repo — can be added, removed, disabled freely. */\nexport interface UserRepoConfig extends RepoConfig {\n\tsource: \"user\";\n}\n\n/** Any repo, regardless of source. */\nexport type AnyRepoConfig = DefaultRepoConfig | UserRepoConfig;\n\n/**\n * Root shape of `repos.json`. `version` lets us evolve the schema later\n * without colliding with hand-edited files.\n */\nexport interface ReposFile {\n\tversion: 1;\n\t/** Id of the repo currently surfaced as the default selection in the UI. */\n\tdefaultRepoId: string;\n\trepos: AnyRepoConfig[];\n}\n\n/**\n * Convenience type-guard — narrows to `DefaultRepoConfig`. Useful when callers\n * want to render `repo.description` or reject `removeRepo(repo)` for defaults.\n */\nexport function isDefaultRepo(repo: RepoConfig): repo is DefaultRepoConfig {\n\treturn repo.source === \"default\";\n}\n\n/** Convenience type-guard — narrows to `UserRepoConfig`. */\nexport function isUserRepo(repo: RepoConfig): repo is UserRepoConfig {\n\treturn repo.source === \"user\";\n}\n","import type { AnyRepoConfig, DefaultRepoConfig, ReposFile } from \"./types\";\nimport { isDefaultRepo } from \"./types\";\n\n/**\n * Hard-coded catalog of repositories SERVICEME ships with. The id, URL and\n * branch are pinned by the design doc (§12) and **must not** change without\n * an owner-approved migration. The order here mirrors the on-disk\n * `repos.json` order (§3.1) which the UI relies on.\n */\n\nexport const DEFAULT_REPO_ID = \"medalsoftchina-ms-skills\";\n\nconst FIXED_ADDED_AT = \"2026-06-29T00:00:00.000Z\";\n\n/**\n * Master list of default repositories. `getDefaultRepoConfigs()` returns\n * clones with the current timestamp baked into `addedAt` so two `load()`\n * calls return identical-but-distinct objects (the store can mutate one\n * without disturbing the other).\n */\nconst DEFAULT_REPO_SEEDS: ReadonlyArray<Omit<DefaultRepoConfig, \"addedAt\">> = [\n\t{\n\t\tid: \"medalsoftchina-ms-skills\",\n\t\tname: \"SERVICEME (精选)\",\n\t\turl: \"https://github.com/medalsoftchina/ms-skills.git\",\n\t\tbranch: \"v2\",\n\t\tenabled: true,\n\t\tuseProxy: true,\n\t\twriteEnabled: true,\n\t\tsource: \"default\",\n\t\tdescription: \"Medalsoft 精选 skill/agent 仓库,可读可写\",\n\t},\n\t{\n\t\tid: \"anthropics-skills\",\n\t\tname: \"Anthropic Skills\",\n\t\turl: \"https://github.com/anthropics/skills.git\",\n\t\tbranch: \"main\",\n\t\tenabled: true,\n\t\tuseProxy: true,\n\t\twriteEnabled: false,\n\t\tsource: \"default\",\n\t\tdescription: \"Anthropic 官方 skills 示例\",\n\t},\n\t{\n\t\tid: \"github-awesome-copilot\",\n\t\tname: \"GitHub Awesome Copilot\",\n\t\turl: \"https://github.com/github/awesome-copilot.git\",\n\t\tbranch: \"main\",\n\t\tenabled: true,\n\t\tuseProxy: true,\n\t\twriteEnabled: false,\n\t\tsource: \"default\",\n\t\tdescription: \"GitHub Copilot 社区精选 prompts/agents\",\n\t},\n\t{\n\t\tid: \"mattpocock-skills\",\n\t\tname: \"Matt Pocock Skills\",\n\t\turl: \"https://github.com/mattpocock/skills.git\",\n\t\tbranch: \"main\",\n\t\tenabled: true,\n\t\tuseProxy: true,\n\t\twriteEnabled: false,\n\t\tsource: \"default\",\n\t\tdescription: \"Matt Pocock 社区 skills 仓库\",\n\t},\n\t{\n\t\tid: \"composiohq-awesome-claude-skills\",\n\t\tname: \"Composio Awesome Claude Skills\",\n\t\turl: \"https://github.com/ComposioHQ/awesome-claude-skills.git\",\n\t\tbranch: \"master\",\n\t\tenabled: true,\n\t\tuseProxy: true,\n\t\twriteEnabled: false,\n\t\tsource: \"default\",\n\t\tdescription: \"Composio 维护的 Claude skills 集合\",\n\t},\n];\n\n/** Returns a deep-ish clone of every default repo with a fresh `addedAt`. */\nexport function getAllDefaultRepoConfigs(\n\tnow: () => string = () => new Date().toISOString()\n): DefaultRepoConfig[] {\n\tconst stamp = now();\n\treturn DEFAULT_REPO_SEEDS.map((seed) => ({\n\t\t...seed,\n\t\taddedAt: stamp,\n\t}));\n}\n\n/** Returns a single default repo by id, or `undefined` when not in the catalog. */\nexport function getDefaultRepoConfig(\n\tid: string,\n\tnow: () => string = () => new Date().toISOString()\n): DefaultRepoConfig | undefined {\n\tconst seed = DEFAULT_REPO_SEEDS.find((s) => s.id === id);\n\tif (!seed) {\n\t\treturn undefined;\n\t}\n\treturn { ...seed, addedAt: now() };\n}\n\n/**\n * Idempotent: returns a fresh `ReposFile` containing the default set, or —\n * when `existing` already has all defaults — returns it untouched (still cloned\n * to keep callers from accidentally mutating shared state).\n */\nexport function buildDefaultReposFile(\n\tnow: () => string = () => new Date().toISOString(),\n\texisting?: ReposFile\n): ReposFile {\n\tconst defaults = getAllDefaultRepoConfigs(now);\n\n\tif (!existing) {\n\t\treturn {\n\t\t\tversion: 1,\n\t\t\tdefaultRepoId: DEFAULT_REPO_ID,\n\t\t\trepos: defaults,\n\t\t};\n\t}\n\n\t// Merge: keep existing repos as-is (including any user-added ones), then\n\t// append any default ids that are missing. Catalog metadata (name, url,\n\t// branch, description, useProxy, writeEnabled) on already-installed\n\t// defaults is refreshed from the current seed so editing\n\t// `DEFAULT_REPO_SEEDS` takes effect without deleting `repos.json`; user/\n\t// runtime state (`enabled`, `addedAt`, `lastSync*`) is preserved as-is.\n\tconst existingIds = new Set(existing.repos.map((r) => r.id));\n\tconst missingDefaults = defaults.filter((d) => !existingIds.has(d.id));\n\tconst { repos: refreshedRepos, changed } = refreshDefaultsMetadata(existing.repos, defaults);\n\tif (missingDefaults.length === 0 && !changed) {\n\t\treturn existing;\n\t}\n\treturn {\n\t\t...existing,\n\t\trepos: [...refreshedRepos, ...missingDefaults],\n\t\tdefaultRepoId: resolveDefaultRepoId(existing, existingIds),\n\t};\n}\n\n/**\n * `ensureDefaultsInstalled(existing)` — pure helper that returns either\n * `existing` (when defaults are already present and up to date) or a merged\n * copy that appends the missing defaults and refreshes catalog metadata on\n * existing ones. The companion `ensureDefaultsInstalledInStore` below is the\n * mutating, store-bound counterpart.\n */\nexport function ensureDefaultsInstalled(\n\texisting: ReposFile,\n\tnow: () => string = () => new Date().toISOString()\n): { config: ReposFile; installed: string[] } {\n\tconst defaults = getAllDefaultRepoConfigs(now);\n\tconst existingIds = new Set(existing.repos.map((r) => r.id));\n\tconst missing = defaults.filter((d) => !existingIds.has(d.id));\n\tconst { repos: refreshedRepos, changed } = refreshDefaultsMetadata(existing.repos, defaults);\n\tif (missing.length === 0 && !changed) {\n\t\treturn { config: existing, installed: [] };\n\t}\n\tconst next: ReposFile = {\n\t\t...existing,\n\t\trepos: [...refreshedRepos, ...missing],\n\t\tdefaultRepoId: resolveDefaultRepoId(existing, existingIds),\n\t};\n\treturn { config: next, installed: missing.map((d) => d.id) };\n}\n\n/** Catalog metadata fields synced from `DEFAULT_REPO_SEEDS` onto already-installed defaults. */\nconst SYNCED_METADATA_KEYS = [\n\t\"name\",\n\t\"url\",\n\t\"branch\",\n\t\"description\",\n\t\"useProxy\",\n\t\"writeEnabled\",\n] as const satisfies ReadonlyArray<keyof DefaultRepoConfig>;\n\n/**\n * Refreshes catalog metadata (see {@link SYNCED_METADATA_KEYS}) on every\n * default repo entry that has a matching seed, leaving user/runtime state\n * (`enabled`, `addedAt`, `lastSync*`) and user-added repos untouched. Returns\n * the original array (by reference) when nothing actually changed, so\n * callers can cheaply detect a no-op.\n */\nfunction refreshDefaultsMetadata(\n\trepos: ReadonlyArray<AnyRepoConfig>,\n\tdefaults: ReadonlyArray<DefaultRepoConfig>\n): { repos: AnyRepoConfig[]; changed: boolean } {\n\tconst seedById = new Map(defaults.map((d) => [d.id, d]));\n\tlet changed = false;\n\tconst next = repos.map((repo) => {\n\t\tif (!isDefaultRepo(repo)) {\n\t\t\treturn repo;\n\t\t}\n\t\tconst seed = seedById.get(repo.id);\n\t\tif (!seed) {\n\t\t\treturn repo;\n\t\t}\n\t\tconst isStale = SYNCED_METADATA_KEYS.some((key) => repo[key] !== seed[key]);\n\t\tif (!isStale) {\n\t\t\treturn repo;\n\t\t}\n\t\tchanged = true;\n\t\treturn { ...repo, ...pick(seed, SYNCED_METADATA_KEYS) };\n\t});\n\treturn { repos: next, changed };\n}\n\nfunction pick<T extends object, K extends keyof T>(source: T, keys: ReadonlyArray<K>): Pick<T, K> {\n\tconst result = {} as Pick<T, K>;\n\tfor (const key of keys) {\n\t\tresult[key] = source[key];\n\t}\n\treturn result;\n}\n\n/**\n * Decide what the post-bootstrap `defaultRepoId` should be.\n *\n * Rules (in order):\n * 1. If `existing.defaultRepoId` references a real repo in `existing`,\n * keep it (the user explicitly chose this default).\n * 2. Otherwise — including the loader's empty-state `\"\"` sentinel or any\n * other unknown id — fall back to {@link DEFAULT_REPO_ID}.\n */\nfunction resolveDefaultRepoId(existing: ReposFile, existingIds: Set<string>): string {\n\tconst placeholder = existing.defaultRepoId;\n\tif (placeholder && existingIds.has(placeholder)) {\n\t\treturn placeholder;\n\t}\n\treturn DEFAULT_REPO_ID;\n}\n\n// `FIXED_ADDED_AT` is exported in case tests want a deterministic timestamp\n// to compare against without stubbing `Date`.\nexport { FIXED_ADDED_AT as DEFAULT_REPO_FIXED_ADDED_AT };\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { z } from \"zod\";\n\nimport { SAFE_REPO_ID_PATTERN } from \"../paths/userHome\";\nimport type { AnyRepoConfig, DefaultRepoConfig, ReposFile, UserRepoConfig } from \"./types\";\n\n/**\n * On-disk `repos.json` loader / writer.\n *\n * Responsibilities:\n * 1. **Validate** every read with a Zod schema — never trust whatever is on\n * disk; a hand-edited file can be malformed in subtle ways.\n * 2. **Recover gracefully** from corrupted files by moving them to a\n * timestamped `.bak` sibling and returning a sentinel empty config.\n * Callers can then prompt the user to re-add their repos.\n * 3. **Write atomically** — always write to `<target>.tmp-<rand>` first\n * and `rename` over the target. A crash mid-write leaves the previous\n * good file intact instead of a half-written JSON.\n *\n * `fs.watch`-based change detection lives in `store.ts`. This module is\n * deliberately pure I/O + validation so it can be exercised from tests\n * without spinning up watchers.\n */\n\nconst ISO_TIMESTAMP_PATTERN = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:?\\d{2})$/;\n\nconst repoIdSchema = z\n\t.string()\n\t.min(1)\n\t.max(64)\n\t.regex(SAFE_REPO_ID_PATTERN, \"repo id must match /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/\");\n\nconst isoTimestampSchema = z.string().regex(ISO_TIMESTAMP_PATTERN, {\n\tmessage: \"must be an ISO-8601 timestamp\",\n});\n\nconst baseRepoFields = {\n\tid: repoIdSchema,\n\tname: z.string().min(1).max(200),\n\turl: z.string().url(),\n\tbranch: z.string().min(1).max(200),\n\tenabled: z.boolean(),\n\tuseProxy: z.boolean().optional().default(true),\n\twriteEnabled: z.boolean(),\n\taddedAt: isoTimestampSchema,\n\tlastSyncAt: isoTimestampSchema.optional(),\n\tlastSyncCommitSha: z.string().min(1).max(80).optional(),\n\tlastSyncStatus: z.enum([\"ok\", \"error\"]).optional(),\n\tlastSyncError: z.string().max(2_000).optional(),\n};\n\nconst defaultRepoSchema = z.object({\n\t...baseRepoFields,\n\tsource: z.literal(\"default\"),\n\tdescription: z.string().min(1).max(2_000),\n});\n\nconst userRepoSchema = z.object({\n\t...baseRepoFields,\n\tsource: z.literal(\"user\"),\n});\n\nconst repoSchema = z.discriminatedUnion(\"source\", [defaultRepoSchema, userRepoSchema]);\n\nexport const reposFileSchema = z\n\t.object({\n\t\tversion: z.literal(1),\n\t\t// Empty string is the sentinel for \"no repos yet\" (see `repos: []`\n\t\t// below); a populated file must use a real `repoIdSchema`-shaped id,\n\t\t// checked in `superRefine` below.\n\t\tdefaultRepoId: z.string(),\n\t\trepos: z.array(repoSchema),\n\t})\n\t.superRefine((value, ctx) => {\n\t\tconst ids = new Set<string>();\n\t\tfor (const repo of value.repos) {\n\t\t\tif (ids.has(repo.id)) {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: \"custom\",\n\t\t\t\t\tpath: [\"repos\"],\n\t\t\t\t\tmessage: `Duplicate repo id: ${repo.id}`,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tids.add(repo.id);\n\t\t}\n\t\tif (value.repos.length === 0) {\n\t\t\tif (value.defaultRepoId !== \"\") {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: \"custom\",\n\t\t\t\t\tpath: [\"defaultRepoId\"],\n\t\t\t\t\tmessage: \"defaultRepoId must be '' when repos[] is empty\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!ids.has(value.defaultRepoId)) {\n\t\t\tctx.addIssue({\n\t\t\t\tcode: \"custom\",\n\t\t\t\tpath: [\"defaultRepoId\"],\n\t\t\t\tmessage: `defaultRepoId '${value.defaultRepoId}' not present in repos[]`,\n\t\t\t});\n\t\t}\n\t});\n\n/** Inferred Zod type — identical to our hand-written `ReposFile`. */\nexport type ReposFileInput = z.infer<typeof reposFileSchema>;\n\n/** Result of a load attempt — either a parsed file or a recovery notice. */\nexport interface LoadResult {\n\t/** Parsed + validated repos file (always populated, even on recovery). */\n\tconfig: ReposFile;\n\t/**\n\t * Non-null when the existing file on disk was unreadable / invalid and\n\t * was moved aside as a `.bak`. The store can use this to surface a\n\t * warning in the UI.\n\t */\n\trecoveredFromBackup: string | null;\n}\n\n/** Dependencies — defaults to the real fs, tests inject a fake. */\nexport interface ReposLoaderFileSystem {\n\treadFile: typeof fs.readFile;\n\twriteFile: typeof fs.writeFile;\n\trename: typeof fs.rename;\n\tmkdir: typeof fs.mkdir;\n\tstat: typeof fs.stat;\n\tunlink?: typeof fs.unlink;\n}\n\nexport interface ReposLoaderOptions {\n\t/** Absolute path of the `repos.json` file. */\n\tconfigPath: string;\n\t/** Custom time provider — defaults to `() => new Date().toISOString()`. */\n\tnow?: () => string;\n\t/** Random suffix provider for atomic-write temp files. */\n\trandomSuffix?: () => string;\n\t/** Filesystem shim for tests. */\n\tfileSystem?: ReposLoaderFileSystem;\n}\n\nconst DEFAULT_RANDOM_SUFFIX_LENGTH = 8;\n\nfunction defaultRandomSuffix(): string {\n\t// Avoid pulling `crypto.randomUUID` (Node 14.17+) here so we stay\n\t// compatible with the wider node range this package targets. A short\n\t// alphanumeric suffix is enough — collisions are vanishingly rare and\n\t// the temp file is removed right after rename.\n\tconst alphabet = \"abcdefghijklmnopqrstuvwxyz0123456789\";\n\tlet out = \"\";\n\tfor (let i = 0; i < DEFAULT_RANDOM_SUFFIX_LENGTH; i++) {\n\t\tout += alphabet[Math.floor(Math.random() * alphabet.length)];\n\t}\n\treturn out;\n}\n\nexport class ReposLoader {\n\tprivate readonly configPath: string;\n\tprivate readonly now: () => string;\n\tprivate readonly randomSuffix: () => string;\n\tprivate readonly fileSystem: ReposLoaderFileSystem;\n\n\tconstructor(options: ReposLoaderOptions) {\n\t\tthis.configPath = options.configPath;\n\t\tthis.now = options.now ?? (() => new Date().toISOString());\n\t\tthis.randomSuffix = options.randomSuffix ?? defaultRandomSuffix;\n\t\tthis.fileSystem = options.fileSystem ?? fs;\n\t}\n\n\t/** Absolute path of the file this loader reads/writes. */\n\tgetConfigPath(): string {\n\t\treturn this.configPath;\n\t}\n\n\t/**\n\t * Read + validate the on-disk config. When the file is missing, returns a\n\t * sentinel empty config (no backup is taken — that's expected on first\n\t * run). When the file is corrupted, moves it to `<name>.<ts>.bak` and\n\t * returns an empty config with `recoveredFromBackup` populated.\n\t */\n\tasync load(): Promise<LoadResult> {\n\t\tlet raw: string;\n\t\ttry {\n\t\t\traw = await this.fileSystem.readFile(this.configPath, \"utf-8\");\n\t\t} catch (error) {\n\t\t\tif (isErrnoCode(error, \"ENOENT\")) {\n\t\t\t\treturn {\n\t\t\t\t\tconfig: this.buildEmptyConfig(),\n\t\t\t\t\trecoveredFromBackup: null,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\tconst parsed = safeParseJson(raw);\n\t\tif (parsed === undefined) {\n\t\t\tconst backupPath = await this.quarantine(\"json-parse-failed\");\n\t\t\treturn {\n\t\t\t\tconfig: this.buildEmptyConfig(),\n\t\t\t\trecoveredFromBackup: backupPath,\n\t\t\t};\n\t\t}\n\n\t\tconst result = reposFileSchema.safeParse(parsed);\n\t\tif (!result.success) {\n\t\t\tconst backupPath = await this.quarantine(\"schema-invalid\");\n\t\t\treturn {\n\t\t\t\tconfig: this.buildEmptyConfig(),\n\t\t\t\trecoveredFromBackup: backupPath,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tconfig: result.data as ReposFile,\n\t\t\trecoveredFromBackup: null,\n\t\t};\n\t}\n\n\t/**\n\t * Write the config to disk atomically. Writes to\n\t * `<target>.tmp-<random>` then `rename`s over the target. The parent\n\t * directory is created on demand.\n\t */\n\tasync save(config: ReposFile): Promise<void> {\n\t\tconst validated = reposFileSchema.parse(config) as ReposFile;\n\n\t\tconst dir = path.dirname(this.configPath);\n\t\tawait this.fileSystem.mkdir(dir, { recursive: true });\n\n\t\tconst serialized = `${JSON.stringify(validated, null, 2)}\\n`;\n\t\tconst tempPath = `${this.configPath}.tmp-${this.randomSuffix()}`;\n\n\t\tawait this.fileSystem.writeFile(tempPath, serialized, \"utf-8\");\n\t\ttry {\n\t\t\tawait this.fileSystem.rename(tempPath, this.configPath);\n\t\t} catch (error) {\n\t\t\t// Best-effort cleanup of the orphaned temp file.\n\t\t\tconst unlink = this.fileSystem.unlink ?? fs.unlink;\n\t\t\tawait unlink(tempPath).catch(() => undefined);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Move the existing file to a timestamped `.bak` sibling and return the\n\t * backup path. Used when we detect a corrupted / unparseable file and\n\t * need to make room for a fresh write.\n\t */\n\tprivate async quarantine(reason: string): Promise<string> {\n\t\tconst stamp = sanitizeForFilename(this.now());\n\t\tconst backupPath = `${this.configPath}.${stamp}.${reason}.bak`;\n\t\ttry {\n\t\t\tawait this.fileSystem.rename(this.configPath, backupPath);\n\t\t} catch (error) {\n\t\t\tif (!isErrnoCode(error, \"ENOENT\")) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\treturn backupPath;\n\t}\n\n\t/**\n\t * Empty config used when no file exists or the existing file is corrupt.\n\t * The caller (e.g. `ensureDefaultsInstalled`) is expected to populate the\n\t * real default repo set before persisting.\n\t */\n\tprivate buildEmptyConfig(): ReposFile {\n\t\treturn {\n\t\t\tversion: 1,\n\t\t\tdefaultRepoId: \"\",\n\t\t\trepos: [],\n\t\t};\n\t}\n}\n\nfunction safeParseJson(raw: string): unknown | undefined {\n\ttry {\n\t\treturn JSON.parse(raw);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction isErrnoCode(error: unknown, code: string): boolean {\n\tif (typeof error !== \"object\" || error === null) {\n\t\treturn false;\n\t}\n\treturn (error as { code?: unknown }).code === code;\n}\n\nfunction sanitizeForFilename(iso: string): string {\n\treturn iso.replace(/[^0-9T]/g, \"-\").replace(/-+/g, \"-\");\n}\n\n/** Re-export the Zod schemas so other modules can introspect them. */\nexport {\n\tdefaultRepoSchema as defaultRepoConfigSchema,\n\trepoSchema as anyRepoConfigSchema,\n\tuserRepoSchema as userRepoConfigSchema,\n};\n\n/** Helper: parse + validate a config object (e.g. freshly built in memory). */\nexport function validateReposFile(input: unknown): ReposFile {\n\treturn reposFileSchema.parse(input) as ReposFile;\n}\n\n/** Helper: discriminate a `RepoConfig` between default and user flavours. */\nexport function narrowRepoConfig(repo: AnyRepoConfig): DefaultRepoConfig | UserRepoConfig {\n\treturn repo.source === \"default\" ? (repo as DefaultRepoConfig) : (repo as UserRepoConfig);\n}\n","import { EventEmitter } from \"node:events\";\nimport * as fs from \"node:fs\";\n\nimport { ensureDefaultsInstalled } from \"./default-repos\";\nimport { type LoadResult, ReposLoader, validateReposFile } from \"./loader\";\nimport {\n\ttype AnyRepoConfig,\n\tisDefaultRepo,\n\ttype RepoConfig,\n\ttype ReposFile,\n\ttype UserRepoConfig,\n} from \"./types\";\n\n/**\n * In-memory CRUD store for `repos.json`.\n *\n * Responsibilities:\n * - Hold the authoritative `ReposFile` state in memory.\n * - Re-load on `fs.watch` events so external writes (CLI edit, user edit)\n * propagate to in-process consumers (extension UI, bridge calls).\n * - Emit `change` events so consumers can re-render without polling.\n * - Validate every write through {@link ReposLoader} so a programmatic\n * `addRepo` cannot smuggle in a malformed entry.\n *\n * The store deliberately does **not** own `fs.watch` resources by default.\n * Callers opt in via {@link ReposStore.startWatching} / {@link stopWatching}\n * so tests can run without lingering watchers.\n */\n\n/** Mutating event from the store. */\nexport type ReposStoreChange =\n\t| { kind: \"load\"; config: ReposFile }\n\t| { kind: \"add\"; repo: AnyRepoConfig }\n\t| { kind: \"update\"; repo: AnyRepoConfig }\n\t| { kind: \"remove\"; repoId: string }\n\t| { kind: \"default-change\"; defaultRepoId: string }\n\t| { kind: \"error\"; error: Error };\n\n/** Subscriber callback. */\nexport type ReposStoreListener = (change: ReposStoreChange) => void;\n\n/** Dependencies — defaults to the real fs, tests inject a fake. */\nexport interface ReposStoreFileSystem {\n\twatch: typeof fs.watch;\n}\n\nexport interface ReposStoreOptions {\n\t/** Use an explicit loader (defaults to one wrapping the resolved configPath). */\n\tloader?: ReposLoader;\n\t/** File-system watch implementation — defaults to `fs.watch`. */\n\tfileSystem?: ReposStoreFileSystem;\n\t/** Time provider for `addedAt` stamping. */\n\tnow?: () => string;\n\t/**\n\t * Debounce window for `fs.watch` callbacks. Editors sometimes emit\n\t * multiple `change` events for a single logical write — coalesce them.\n\t * Defaults to 50ms.\n\t */\n\tdebounceMs?: number;\n\t/**\n\t * Watch implementation factory — defaults to a no-deps wrapper around\n\t * `fs.watch`. Tests inject a fake watcher here. The store wires the\n\t * returned handle to its own schedule/error flow via the supplied\n\t * callbacks.\n\t */\n\tcreateFsWatcher?: (configPath: string, callbacks: FsWatcherCallbacks) => FsWatcherHandle;\n}\n\n/**\n * Minimal subset of `fs.FSWatcher` that the store depends on. Defining our\n * own type lets tests inject a fake without dragging in real fs handles.\n */\nexport interface FsWatcherHandle {\n\tclose(): void;\n}\n\n/**\n * Callbacks the store wires to a watcher. The default `fs.watch` factory\n * uses these to forward events into `scheduleReload` / `error` emission;\n * tests can ignore them when they only care about lifecycle.\n */\nexport interface FsWatcherCallbacks {\n\tonChange?: () => void;\n\tonRename?: () => void;\n\tonError?: (err: Error) => void;\n}\n\nexport class ReposStore {\n\tprivate readonly loader: ReposLoader;\n\tprivate readonly fileSystem: ReposStoreFileSystem;\n\tprivate readonly now: () => string;\n\tprivate readonly debounceMs: number;\n\tprivate readonly createFsWatcher: (\n\t\tconfigPath: string,\n\t\tcallbacks: FsWatcherCallbacks\n\t) => FsWatcherHandle;\n\n\tprivate config: ReposFile | null = null;\n\tprivate readonly emitter = new EventEmitter();\n\tprivate watcher: FsWatcherHandle | null = null;\n\tprivate reloadTimer: NodeJS.Timeout | null = null;\n\tprivate lastLoadResult: LoadResult | null = null;\n\n\tconstructor(options: ReposStoreOptions = {}) {\n\t\tthis.loader = options.loader ?? new ReposLoader({ configPath: \"\" });\n\t\tthis.fileSystem = options.fileSystem ?? { watch: fs.watch };\n\t\tthis.now = options.now ?? (() => new Date().toISOString());\n\t\tthis.debounceMs = options.debounceMs ?? 50;\n\t\tthis.createFsWatcher =\n\t\t\toptions.createFsWatcher ?? ((p, cb) => this.defaultCreateFsWatcher(p, cb));\n\t}\n\n\t/** Currently held config, or `null` if `load()` has not been called yet. */\n\tgetConfig(): ReposFile | null {\n\t\treturn this.config;\n\t}\n\n\t/** Path of the underlying `repos.json` file (via the loader). */\n\tgetConfigPath(): string {\n\t\treturn this.loader.getConfigPath();\n\t}\n\n\t/** Subscribe to store mutations. Returns an unsubscribe function. */\n\tsubscribe(listener: ReposStoreListener): () => void {\n\t\tthis.emitter.on(\"change\", listener);\n\t\treturn () => {\n\t\t\tthis.emitter.off(\"change\", listener);\n\t\t};\n\t}\n\n\t/**\n\t * Read the config from disk (delegating to the loader) and seed the\n\t * in-memory state. Safe to call repeatedly.\n\t */\n\tasync load(): Promise<LoadResult> {\n\t\tconst result = await this.loader.load();\n\t\tthis.lastLoadResult = result;\n\t\tthis.config = result.config;\n\t\tthis.emit({ kind: \"load\", config: result.config });\n\t\treturn result;\n\t}\n\n\t/** Convenience getter — wraps `load()` and returns the config. */\n\tasync ensureLoaded(): Promise<ReposFile> {\n\t\tconst result = await this.load();\n\t\treturn result.config;\n\t}\n\n\t/** The most recent `LoadResult`, useful for surfacing recovery warnings. */\n\tgetLastLoadResult(): LoadResult | null {\n\t\treturn this.lastLoadResult;\n\t}\n\n\t/**\n\t * Start watching the config file for external changes. Subsequent writes\n\t * (or edits from another process) trigger a debounced re-load.\n\t *\n\t * No-op when already watching.\n\t */\n\tstartWatching(): void {\n\t\tif (this.watcher) {\n\t\t\treturn;\n\t\t}\n\t\tconst configPath = this.loader.getConfigPath();\n\t\tif (!configPath) {\n\t\t\tthrow new Error(\"Cannot watch: loader has no configPath.\");\n\t\t}\n\t\tthis.watcher = this.createFsWatcher(configPath, {\n\t\t\tonChange: () => this.scheduleReload(),\n\t\t\tonRename: () => this.scheduleReload(),\n\t\t\tonError: (err) => this.emit({ kind: \"error\", error: err }),\n\t\t});\n\t}\n\n\t/** Stop watching and release any pending reload timers. */\n\tstopWatching(): void {\n\t\tif (this.reloadTimer) {\n\t\t\tclearTimeout(this.reloadTimer);\n\t\t\tthis.reloadTimer = null;\n\t\t}\n\t\tif (this.watcher) {\n\t\t\tthis.watcher.close();\n\t\t\tthis.watcher = null;\n\t\t}\n\t}\n\n\t// ───────────────────────────── CRUD operations ─────────────────────────────\n\n\tlist(): AnyRepoConfig[] {\n\t\treturn this.config?.repos.slice() ?? [];\n\t}\n\n\tlistDefault(): AnyRepoConfig[] {\n\t\treturn this.list().filter(isDefaultRepo);\n\t}\n\n\tlistUser(): UserRepoConfig[] {\n\t\treturn this.list().filter((r): r is UserRepoConfig => r.source === \"user\");\n\t}\n\n\tget(repoId: string): AnyRepoConfig | undefined {\n\t\treturn this.config?.repos.find((r) => r.id === repoId);\n\t}\n\n\t/**\n\t * Add a user repo. Throws when the id already exists. The `addedAt`\n\t * timestamp defaults to `now()` when not provided. Persists to disk.\n\t */\n\tasync addUserRepo(input: Omit<UserRepoConfig, \"source\">): Promise<UserRepoConfig> {\n\t\tconst enriched: UserRepoConfig = {\n\t\t\t...input,\n\t\t\tsource: \"user\",\n\t\t\taddedAt: input.addedAt ?? this.now(),\n\t\t};\n\t\tawait this.mutate((current) => {\n\t\t\tif (current.repos.some((r) => r.id === enriched.id)) {\n\t\t\t\tthrow new RepoAlreadyExistsError(enriched.id);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...current,\n\t\t\t\trepos: [...current.repos, enriched],\n\t\t\t};\n\t\t});\n\t\tthis.emit({ kind: \"add\", repo: enriched });\n\t\treturn enriched;\n\t}\n\n\t/**\n\t * Update an existing repo by id (partial). Throws when not found.\n\t * Persists to disk.\n\t */\n\tasync updateRepo(repoId: string, patch: Partial<RepoConfig>): Promise<AnyRepoConfig> {\n\t\tconst updated = await this.mutate((current) => {\n\t\t\tconst idx = current.repos.findIndex((r) => r.id === repoId);\n\t\t\tif (idx === -1) {\n\t\t\t\tthrow new RepoNotFoundError(repoId);\n\t\t\t}\n\t\t\tconst previous = current.repos[idx];\n\t\t\tif (!previous) {\n\t\t\t\t// Unreachable: idx === -1 caught above.\n\t\t\t\tthrow new RepoNotFoundError(repoId);\n\t\t\t}\n\t\t\tconst merged: AnyRepoConfig = {\n\t\t\t\t...previous,\n\t\t\t\t...patch,\n\t\t\t\tid: previous.id,\n\t\t\t\tsource: previous.source,\n\t\t\t} as AnyRepoConfig;\n\t\t\tconst nextRepos = current.repos.slice();\n\t\t\tnextRepos[idx] = merged;\n\t\t\treturn { ...current, repos: nextRepos };\n\t\t});\n\t\tconst targetRepo = updated.repos.find((r) => r.id === repoId);\n\t\tif (!targetRepo) {\n\t\t\tthrow new RepoNotFoundError(repoId);\n\t\t}\n\t\tthis.emit({ kind: \"update\", repo: targetRepo });\n\t\treturn targetRepo;\n\t}\n\n\t/**\n\t * Remove a user repo. Throws when the repo is a default or does not exist.\n\t * Persists to disk.\n\t */\n\tasync removeUserRepo(repoId: string): Promise<void> {\n\t\tconst existed = this.get(repoId);\n\t\tif (!existed) {\n\t\t\tthrow new RepoNotFoundError(repoId);\n\t\t}\n\t\tif (existed.source === \"default\") {\n\t\t\tthrow new CannotRemoveDefaultRepoError(repoId);\n\t\t}\n\t\tawait this.mutate((current) => ({\n\t\t\t...current,\n\t\t\trepos: current.repos.filter((r) => r.id !== repoId),\n\t\t}));\n\t\tthis.emit({ kind: \"remove\", repoId });\n\t}\n\n\t/**\n\t * Disable a repo (any source). Useful for the \"Disable\" button on default\n\t * repos. Persists to disk.\n\t */\n\tasync disableRepo(repoId: string): Promise<AnyRepoConfig> {\n\t\treturn this.updateRepo(repoId, { enabled: false });\n\t}\n\n\t/** Inverse of {@link disableRepo}. */\n\tasync enableRepo(repoId: string): Promise<AnyRepoConfig> {\n\t\treturn this.updateRepo(repoId, { enabled: true });\n\t}\n\n\t/** Change which repo the UI surfaces as the default. Persists to disk. */\n\tasync setDefaultRepoId(repoId: string): Promise<void> {\n\t\tconst target = this.get(repoId);\n\t\tif (!target) {\n\t\t\tthrow new RepoNotFoundError(repoId);\n\t\t}\n\t\tawait this.mutate((current) => ({\n\t\t\t...current,\n\t\t\tdefaultRepoId: repoId,\n\t\t}));\n\t\tthis.emit({ kind: \"default-change\", defaultRepoId: repoId });\n\t}\n\n\tgetDefaultRepoId(): string | null {\n\t\treturn this.config?.defaultRepoId ?? null;\n\t}\n\n\t/** Returns the configured `now` provider — exposed for `bootstrapDefaults`. */\n\tgetNow(): () => string {\n\t\treturn this.now;\n\t}\n\n\t/**\n\t * Replace the in-memory config and persist it. Used by helpers like\n\t * `bootstrapDefaults` that build a new config wholesale (rather than\n\t * mutating a single field).\n\t */\n\tasync replaceConfig(next: ReposFile): Promise<ReposFile> {\n\t\tconst validated = validateReposFile(next);\n\t\tawait this.loader.save(validated);\n\t\tthis.config = validated;\n\t\treturn validated;\n\t}\n\n\t// ────────────────────────── Internal helpers ──────────────────────────\n\n\tprivate async mutate(updater: (current: ReposFile) => ReposFile): Promise<ReposFile> {\n\t\tconst current = this.config;\n\t\tif (!current) {\n\t\t\tthrow new Error(\"ReposStore.mutate called before load().\");\n\t\t}\n\t\tconst next = updater(current);\n\t\tconst validated = validateReposFile(next);\n\t\tawait this.loader.save(validated);\n\t\tthis.config = validated;\n\t\treturn validated;\n\t}\n\n\tprivate emit(change: ReposStoreChange): void {\n\t\tthis.emitter.emit(\"change\", change);\n\t}\n\n\t/**\n\t * Default `fs.watch` adapter. Public so tests can wrap a real watcher\n\t * with a debounce trampoline if they need to.\n\t */\n\tprivate defaultCreateFsWatcher(\n\t\tconfigPath: string,\n\t\tcallbacks: FsWatcherCallbacks\n\t): FsWatcherHandle {\n\t\tconst handle = this.fileSystem.watch(configPath, { persistent: false });\n\t\tif (callbacks.onChange) {\n\t\t\thandle.on(\"change\", () => callbacks.onChange?.());\n\t\t}\n\t\tif (callbacks.onRename) {\n\t\t\thandle.on(\"rename\", () => callbacks.onRename?.());\n\t\t}\n\t\tif (callbacks.onError) {\n\t\t\thandle.on(\"error\", (err: Error) => callbacks.onError?.(err));\n\t\t}\n\t\treturn {\n\t\t\tclose: () => handle.close(),\n\t\t};\n\t}\n\n\t// Override createFsWatcher with the debounced default if no factory was supplied.\n\tprivate scheduleReload(): void {\n\t\tif (this.reloadTimer) {\n\t\t\tclearTimeout(this.reloadTimer);\n\t\t}\n\t\tthis.reloadTimer = setTimeout(() => {\n\t\t\tthis.reloadTimer = null;\n\t\t\tthis.load().catch((err: unknown) => {\n\t\t\t\tconst error = err instanceof Error ? err : new Error(String(err));\n\t\t\t\tthis.emit({ kind: \"error\", error });\n\t\t\t});\n\t\t}, this.debounceMs);\n\t}\n}\n\n/** Factory: creates a `ReposStore` bound to a specific config path. */\nexport function createReposStore(\n\tconfigPath: string,\n\toptions: Omit<ReposStoreOptions, \"loader\"> = {}\n): ReposStore {\n\treturn new ReposStore({\n\t\tloader: new ReposLoader({ configPath }),\n\t\t...options,\n\t});\n}\n\n/** Error: tried to add a repo whose id already exists. */\nexport class RepoAlreadyExistsError extends Error {\n\tconstructor(public readonly repoId: string) {\n\t\tsuper(`Repo already exists: ${repoId}`);\n\t\tthis.name = \"RepoAlreadyExistsError\";\n\t}\n}\n\n/** Error: looked up a repo id that isn't present in the current config. */\nexport class RepoNotFoundError extends Error {\n\tconstructor(public readonly repoId: string) {\n\t\tsuper(`Repo not found: ${repoId}`);\n\t\tthis.name = \"RepoNotFoundError\";\n\t}\n}\n\n/** Error: tried to remove a default repo (defaults are disable-only). */\nexport class CannotRemoveDefaultRepoError extends Error {\n\tconstructor(public readonly repoId: string) {\n\t\tsuper(`Cannot remove default repo (use disableRepo instead): ${repoId}`);\n\t\tthis.name = \"CannotRemoveDefaultRepoError\";\n\t}\n}\n\n/**\n * Bootstrap helper — invokes `ensureDefaultsInstalled` on the store to\n * guarantee that all default repos are present and their catalog metadata\n * is up to date with `DEFAULT_REPO_SEEDS`. Returns the resulting config.\n * Lives here (not in `default-repos.ts`) to keep the default-repo module\n * free of store concerns.\n */\nexport async function bootstrapDefaults(store: ReposStore): Promise<ReposFile> {\n\tconst config = store.getConfig() ?? (await store.ensureLoaded());\n\tconst result = ensureDefaultsInstalled(config, store.getNow());\n\t// `ensureDefaultsInstalled` returns the same `config` reference when\n\t// nothing changed (no missing defaults, no stale metadata) — compare by\n\t// reference instead of `installed.length` so metadata-only refreshes\n\t// (e.g. editing `DEFAULT_REPO_SEEDS`) still get persisted.\n\tif (result.config === config) {\n\t\treturn config;\n\t}\n\tawait store.replaceConfig(result.config);\n\treturn store.getConfig() ?? result.config;\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nconst CONFIG_DIR = \".serviceme\";\nconst LOG_FILE = \"scheduler.log\";\nconst MAX_LOG_SIZE = 1024 * 1024; // 1MB\n\nexport class DaemonLogger {\n\tprivate readonly logPath: string;\n\n\tconstructor(workspacePath: string, options: { logPath?: string } = {}) {\n\t\tthis.logPath = options.logPath ?? path.join(workspacePath, CONFIG_DIR, LOG_FILE);\n\t\tconst dir = path.dirname(this.logPath);\n\t\tif (!fs.existsSync(dir)) {\n\t\t\tfs.mkdirSync(dir, { recursive: true });\n\t\t}\n\t}\n\n\tgetLogPath(): string {\n\t\treturn this.logPath;\n\t}\n\n\tlog(level: \"info\" | \"warn\" | \"error\", message: string): void {\n\t\tconst ts = new Date().toISOString();\n\t\tconst line = `[${ts}] [${level.toUpperCase()}] ${message}\\n`;\n\t\tthis.rotateIfNeeded();\n\t\tfs.appendFileSync(this.logPath, line, \"utf-8\");\n\t}\n\n\tprivate rotateIfNeeded(): void {\n\t\ttry {\n\t\t\tconst stats = fs.statSync(this.logPath);\n\t\t\tif (stats.size > MAX_LOG_SIZE) {\n\t\t\t\t// Keep last half of the file\n\t\t\t\tconst content = fs.readFileSync(this.logPath, \"utf-8\");\n\t\t\t\tconst halfIdx = content.indexOf(\"\\n\", Math.floor(content.length / 2));\n\t\t\t\tif (halfIdx > 0) {\n\t\t\t\t\tfs.writeFileSync(this.logPath, content.slice(halfIdx + 1), \"utf-8\");\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// File doesn't exist yet — fine\n\t\t}\n\t}\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nconst CONFIG_DIR = \".serviceme\";\nconst PID_FILE = \"scheduler.pid\";\n\nexport interface PidManagerOptions {\n\t/**\n\t * Override the on-disk PID path. Defaults to\n\t * `<workspacePath>/.serviceme/scheduler.pid` for backward compat.\n\t * V2 callers pass an absolute path (e.g. `getSchedulerPidPath()`)\n\t * to read the global daemon PID file.\n\t */\n\tpidPath?: string;\n}\n\nexport class PidManager {\n\tprivate readonly pidPath: string;\n\n\tconstructor(workspacePath: string, options: PidManagerOptions = {}) {\n\t\tthis.pidPath = options.pidPath ?? path.join(workspacePath, CONFIG_DIR, PID_FILE);\n\t}\n\n\tgetPidPath(): string {\n\t\treturn this.pidPath;\n\t}\n\n\twritePid(pid: number): void {\n\t\tconst dir = path.dirname(this.pidPath);\n\t\tif (!fs.existsSync(dir)) {\n\t\t\tfs.mkdirSync(dir, { recursive: true });\n\t\t}\n\t\tfs.writeFileSync(this.pidPath, String(pid), \"utf-8\");\n\t}\n\n\treadPid(): number | null {\n\t\t// statSync (vs existsSync + readFileSync) so that a path that\n\t\t// points at a directory — or any non-file — is treated as\n\t\t// \"no pid\" instead of crashing with EISDIR. Activation paths\n\t\t// hit this code path on every reload; one stale directory\n\t\t// shell from a half-removed daemon would otherwise throw and\n\t\t// take down extension activation. See ms-devtools-vscode\n\t\t// agent memory \"PidManager EISDIR regression\".\n\t\tlet stat: fs.Stats;\n\t\ttry {\n\t\t\tstat = fs.statSync(this.pidPath);\n\t\t} catch {\n\t\t\t// ENOENT / EACCES / EPERM / etc. — all treated as no pid.\n\t\t\treturn null;\n\t\t}\n\t\tif (!stat.isFile()) return null;\n\t\tconst raw = fs.readFileSync(this.pidPath, \"utf-8\").trim();\n\t\tconst pid = Number.parseInt(raw, 10);\n\t\treturn Number.isNaN(pid) ? null : pid;\n\t}\n\n\tremovePid(): void {\n\t\tif (fs.existsSync(this.pidPath)) {\n\t\t\tfs.unlinkSync(this.pidPath);\n\t\t}\n\t}\n\n\tisProcessRunning(pid: number): boolean {\n\t\ttry {\n\t\t\tprocess.kill(pid, 0);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tgetRunningPid(): number | null {\n\t\tconst pid = this.readPid();\n\t\tif (pid === null) return null;\n\t\tif (this.isProcessRunning(pid)) return pid;\n\t\t// Stale PID file — remove\n\t\tthis.removePid();\n\t\treturn null;\n\t}\n}\n","// SchedulerDaemonV2 — global single-instance scheduler.\n//\n// Differences from the removed v1 per-workspace SchedulerDaemon:\n// - Reads the global ~/.serviceme/scheduled-tasks.json (v2 schema).\n// - One process for the whole machine, guarded by PID file + startup lock.\n// - Per-workspace in-memory mutex: same workspace runs serially, different\n// workspaces run in parallel.\n// - Switches cwd to task.workspace.path on every execute (no implicit\n// \"current workspace\" — daemon is machine-scoped).\n// - Disables tasks whose workspace path is missing.\n//\n// See docs/architecture/skill-agent-v2-repo.md §14.3.b + §14.4.\n\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { ScheduledTask, ScheduledTasksConfig } from \"@serviceme/devtools-protocol\";\nimport { getSchedulerPidPath } from \"../../paths/userHome\";\nimport { getExecutor } from \"../executors\";\nimport { TaskConfigManager, validateTaskPayload } from \"../TaskConfigManager\";\nimport { resolveTaskExecutionPayload } from \"../TaskExecutionEngine\";\nimport { TaskLogManager } from \"../TaskLogManager\";\nimport { DaemonLogger } from \"./DaemonLogger\";\nimport { PidManager } from \"./PidManager\";\n\nconst TICK_INTERVAL = 1000; // Check every second\nconst MIN_SCHEDULE_INTERVAL = 1_000; // 1 second minimum\nconst SCHEDULER_LOG_FILENAME = \"scheduler.log\";\n\nexport interface SchedulerDaemonV2Options {\n\t/**\n\t * Override the executor factory. Production callers should leave this\n\t * undefined; tests inject a fake to drive the execution branch without\n\t * spawning real subprocesses.\n\t */\n\tgetExecutor?: typeof getExecutor;\n\t/** Override the config manager. Tests inject a fake pointing at a tmp dir. */\n\tconfigManager?: TaskConfigManager;\n\t/** Override the log manager. */\n\tlogManager?: TaskLogManager;\n\t/** Override the PID manager. */\n\tpidManager?: PidManager;\n\t/** Override the daemon logger (for tests; the default writes to ~/.serviceme/scheduler.log). */\n\tlogger?: DaemonLogger;\n\t/** Override the startup lock acquisition (tests use this to inject \"lock held\" / \"lock free\"). */\n\ttryAcquireLock?: () => boolean;\n\t/** Override the startup lock release. */\n\treleaseLock?: () => void;\n\t/**\n\t * Override the PID file existence + process liveness check. Production\n\t * reads PID file + `process.kill(pid, 0)`; tests inject a stub.\n\t */\n\tgetRunningPid?: () => number | null;\n}\n\nexport class SchedulerDaemonV2 {\n\tprivate readonly configManager: TaskConfigManager;\n\tprivate readonly logManager: TaskLogManager;\n\tprivate readonly pidManager: PidManager;\n\tprivate readonly logger: DaemonLogger;\n\tprivate readonly getExecutor: typeof getExecutor;\n\tprivate readonly tryAcquireLock: () => boolean;\n\tprivate readonly releaseLock: () => void;\n\tprivate readonly getRunningPid: () => number | null;\n\n\tprivate tickTimer: ReturnType<typeof setInterval> | null = null;\n\tprivate running = false;\n\t// Stable references to our own signal listeners so stop() can detach\n\t// them — otherwise repeated start()/stop() cycles (especially in tests)\n\t// leak listeners and keep the event loop alive past the daemon's lifetime.\n\tprivate readonly sigtermHandler: () => void;\n\tprivate readonly sigintHandler: () => void;\n\tprivate startTime: number = 0;\n\n\t// Per-task state (machines are global, but the daemon only knows about\n\t// tasks currently in the config — `lastRun` keys are task IDs).\n\tprivate lastRun: Map<string, number> = new Map();\n\n\t// Per-workspace mutex queue: same workspacePath runs serially, different\n\t// workspacePaths can run in parallel. Each entry is a promise that\n\t// resolves when the previous + this task has finished.\n\tprivate workspaceLocks: Map<string, Promise<unknown>> = new Map();\n\n\tconstructor(options: SchedulerDaemonV2Options = {}) {\n\t\tthis.configManager = options.configManager ?? new TaskConfigManager();\n\t\tthis.logManager = options.logManager ?? new TaskLogManager();\n\t\tthis.pidManager = options.pidManager ?? new PidManager(\"\", { pidPath: getSchedulerPidPath() });\n\t\t// Daemon log lives at ~/.serviceme/scheduler.log (global, not per-ws).\n\t\t// Computed from the PID file's directory to stay in sync with the\n\t\t// user-home resolution.\n\t\tthis.logger =\n\t\t\toptions.logger ??\n\t\t\tnew DaemonLogger(os.homedir(), {\n\t\t\t\tlogPath: path.join(path.dirname(this.pidManager.getPidPath()), SCHEDULER_LOG_FILENAME),\n\t\t\t});\n\t\tthis.getExecutor = options.getExecutor ?? getExecutor;\n\t\tthis.tryAcquireLock = options.tryAcquireLock ?? (() => true);\n\t\tthis.releaseLock = options.releaseLock ?? (() => {});\n\t\tthis.getRunningPid = options.getRunningPid ?? (() => this.pidManager.getRunningPid());\n\t\t// Bind the signal handlers once so stop() can detach the SAME\n\t\t// function references later.\n\t\tthis.sigtermHandler = () => this.stop();\n\t\tthis.sigintHandler = () => this.stop();\n\t}\n\n\tstart(): void {\n\t\tif (this.running) return;\n\n\t\t// 1. Try to acquire the startup flock. If we can't, another instance\n\t\t// is mid-startup — exit 0 (silent, not an error).\n\t\tif (!this.tryAcquireLock()) {\n\t\t\tthis.logger.log(\"info\", \"Another daemon instance holds the lock; exiting\");\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\t// 2. Check existing PID file. If a live process holds the slot, also\n\t\t// exit 0. (Recoverable if the recorded PID is stale.)\n\t\tconst existingPid = this.getRunningPid();\n\t\tif (existingPid !== null && existingPid !== process.pid) {\n\t\t\tthis.logger.log(\"info\", `Another daemon already running (pid: ${existingPid}); exiting`);\n\t\t\tthis.releaseLock();\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tthis.running = true;\n\t\tthis.startTime = Date.now();\n\t\tthis.pidManager.writePid(process.pid);\n\n\t\tthis.logger.log(\n\t\t\t\"info\",\n\t\t\t`SchedulerDaemonV2 started\n PID: ${process.pid}\n Node: ${process.version}\n OS: ${os.type()} ${os.release()} (${process.arch})\n Config: ${this.configManager.getConfigPath()}\n LogPath: ${this.logger.getLogPath()}\n PidPath: ${this.pidManager.getPidPath()}`\n\t\t);\n\n\t\tthis.tickTimer = setInterval(() => this.tick(), TICK_INTERVAL);\n\t\tprocess.on(\"SIGTERM\", this.sigtermHandler);\n\t\tprocess.on(\"SIGINT\", this.sigintHandler);\n\t}\n\n\tstop(): void {\n\t\tif (!this.running) return;\n\t\tthis.running = false;\n\t\tif (this.tickTimer) {\n\t\t\tclearInterval(this.tickTimer);\n\t\t\tthis.tickTimer = null;\n\t\t}\n\t\tthis.pidManager.removePid();\n\t\tthis.releaseLock();\n\t\t// Detach our own signal handlers so multiple start()/stop() cycles\n\t\t// (especially in test scenarios) don't leak listeners and keep the\n\t\t// event loop alive past the daemon's lifetime.\n\t\tprocess.removeListener(\"SIGTERM\", this.sigtermHandler);\n\t\tprocess.removeListener(\"SIGINT\", this.sigintHandler);\n\t\tthis.logger.log(\"info\", \"SchedulerDaemonV2 stopped\");\n\t\tprocess.exit(0);\n\t}\n\n\tprivate tick(): void {\n\t\tconst config = this.configManager.readConfig();\n\t\tconst now = Date.now();\n\n\t\tfor (const task of config.tasks) {\n\t\t\tif (!task.enabled) continue;\n\n\t\t\t// Workspace-missing: auto-disable + log. Done outside the mutex\n\t\t\t// chain because no execution is going to happen anyway.\n\t\t\tif (!fs.existsSync(task.workspace.path)) {\n\t\t\t\tthis.disableTaskForMissingWorkspace(task, config);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// First-encounter: seed lastRun to the current tick so the task\n\t\t\t// waits a full interval before firing.\n\t\t\tlet lastExec = this.lastRun.get(task.id);\n\t\t\tif (lastExec === undefined) {\n\t\t\t\tthis.lastRun.set(task.id, now);\n\t\t\t\tlastExec = now;\n\t\t\t}\n\t\t\tif (!this.shouldRun(task, lastExec, now)) continue;\n\n\t\t\t// Per-workspace mutex: chain onto the existing promise for that\n\t\t\t// workspace path. Different workspaces run independently.\n\t\t\tconst wsPath = task.workspace.path;\n\t\t\tconst prev = this.workspaceLocks.get(wsPath) ?? Promise.resolve();\n\t\t\tconst next = prev\n\t\t\t\t.catch(() => undefined)\n\t\t\t\t.then(() => this.executeTask(task, now))\n\t\t\t\t.catch((err: unknown) => {\n\t\t\t\t\tthis.logger.log(\"error\", `Task ${task.id} execution failed: ${String(err)}`);\n\t\t\t\t});\n\t\t\tthis.workspaceLocks.set(wsPath, next);\n\t\t}\n\t}\n\n\tprivate disableTaskForMissingWorkspace(task: ScheduledTask, config: ScheduledTasksConfig): void {\n\t\tconst idx = config.tasks.findIndex((t) => t.id === task.id);\n\t\tif (idx === -1) return;\n\t\tconst existing = config.tasks[idx];\n\t\tif (!existing) return;\n\t\tif (existing.enabled === false && existing.lastRunError === \"workspace missing\") return;\n\t\tthis.logger.log(\n\t\t\t\"warn\",\n\t\t\t`Disabling task ${task.name} (${task.id}): workspace ${task.workspace.path} not found`\n\t\t);\n\t\tconfig.tasks[idx] = {\n\t\t\t...existing,\n\t\t\tenabled: false,\n\t\t\tlastRunError: \"workspace missing\",\n\t\t};\n\t\tthis.configManager.editTask(task.id, {\n\t\t\tenabled: false,\n\t\t\tworkspace: config.tasks[idx].workspace,\n\t\t});\n\t}\n\n\tprivate shouldRun(task: ScheduledTask, lastExec: number, now: number): boolean {\n\t\tif (task.scheduleType === \"interval\") {\n\t\t\tconst intervalMs = parseIntervalMs(task.schedule);\n\t\t\tif (intervalMs < MIN_SCHEDULE_INTERVAL) return false;\n\t\t\treturn now - lastExec >= intervalMs;\n\t\t}\n\t\tif (task.scheduleType === \"cron\") {\n\t\t\tif (now - lastExec < 60_000) return false;\n\t\t\treturn matchesCron(task.schedule, new Date(now));\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate async executeTask(task: ScheduledTask, now: number): Promise<void> {\n\t\tthis.lastRun.set(task.id, now);\n\t\tconst startedAt = new Date(now).toISOString();\n\t\tconst startMs = Date.now();\n\t\tconst origCwd = process.cwd();\n\n\t\tthis.logger.log(\"info\", `Executing task: ${task.name} (${task.id}) in ${task.workspace.path}`);\n\n\t\ttry {\n\t\t\tprocess.chdir(task.workspace.path);\n\t\t\tconst executionPayload = resolveTaskExecutionPayload(\n\t\t\t\ttask.taskType,\n\t\t\t\ttask.payload,\n\t\t\t\ttask.workspace.path\n\t\t\t);\n\t\t\tvalidateTaskPayload(task.taskType, executionPayload);\n\t\t\tconst executor = this.getExecutor(task.taskType);\n\t\t\tconst result = await executor.execute(executionPayload);\n\t\t\tconst finishedAt = new Date().toISOString();\n\t\t\tconst durationMs = Date.now() - startMs;\n\n\t\t\tthis.logManager.appendLog({\n\t\t\t\ttaskId: task.id,\n\t\t\t\ttaskName: task.name,\n\t\t\t\tstartedAt,\n\t\t\t\tfinishedAt,\n\t\t\t\tstatus: result.status === \"running\" ? \"failure\" : result.status,\n\t\t\t\toutput: result.output,\n\t\t\t\terror: result.error,\n\t\t\t});\n\n\t\t\t// Update runtime metadata on the task itself.\n\t\t\tthis.configManager.editTask(task.id, {\n\t\t\t\tenabled: task.enabled,\n\t\t\t\tworkspace: task.workspace,\n\t\t\t});\n\n\t\t\tthis.logger.log(\n\t\t\t\t\"info\",\n\t\t\t\t`Task completed: ${task.name} status=${result.status} duration=${durationMs}ms`\n\t\t\t);\n\t\t} catch (err: unknown) {\n\t\t\tconst finishedAt = new Date().toISOString();\n\t\t\tconst durationMs = Date.now() - startMs;\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tthis.logManager.appendLog({\n\t\t\t\ttaskId: task.id,\n\t\t\t\ttaskName: task.name,\n\t\t\t\tstartedAt,\n\t\t\t\tfinishedAt,\n\t\t\t\tstatus: \"failure\",\n\t\t\t\terror: message,\n\t\t\t});\n\t\t\tthis.logger.log(\n\t\t\t\t\"error\",\n\t\t\t\t`Task failed: ${task.name} (${task.id}) duration=${durationMs}ms error=${message}`\n\t\t\t);\n\t\t} finally {\n\t\t\tprocess.chdir(origCwd);\n\t\t}\n\t}\n\n\tgetStatus() {\n\t\tconst config = this.configManager.readConfig();\n\t\treturn {\n\t\t\trunning: this.running,\n\t\t\tpid: process.pid,\n\t\t\tuptimeSeconds: this.running ? Math.floor((Date.now() - this.startTime) / 1000) : null,\n\t\t\ttasksRegistered: config.tasks.length,\n\t\t\ttasksEnabled: config.tasks.filter((t) => t.enabled).length,\n\t\t\tpidFile: this.pidManager.getPidPath(),\n\t\t};\n\t}\n}\n\n// ─── Helpers (duplicated from v1 to keep v1 untouched) ─────────────────────\n\nfunction parseIntervalMs(schedule: string): number {\n\tconst match = /^every\\s+(\\d+)\\s*(s|sec|m|min|h|hr|d|day)s?$/i.exec(schedule);\n\tif (!match) return 0;\n\tconst [, numStr, unit] = match;\n\tconst num = Number.parseInt(numStr ?? \"0\", 10);\n\tswitch (unit?.toLowerCase()) {\n\t\tcase \"s\":\n\t\tcase \"sec\":\n\t\t\treturn num * 1000;\n\t\tcase \"m\":\n\t\tcase \"min\":\n\t\t\treturn num * 60 * 1000;\n\t\tcase \"h\":\n\t\tcase \"hr\":\n\t\t\treturn num * 60 * 60 * 1000;\n\t\tcase \"d\":\n\t\tcase \"day\":\n\t\t\treturn num * 24 * 60 * 60 * 1000;\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n}\n\nfunction matchesCron(expression: string, date: Date): boolean {\n\tconst parts = expression.trim().split(/\\s+/);\n\tif (parts.length < 5) return false;\n\tconst [minPart, hourPart, dayPart, monthPart, weekdayPart] = parts;\n\tif (!minPart || !hourPart || !dayPart || !monthPart || !weekdayPart) return false;\n\treturn (\n\t\tmatchCronField(minPart, date.getMinutes()) &&\n\t\tmatchCronField(hourPart, date.getHours()) &&\n\t\tmatchCronField(dayPart, date.getDate()) &&\n\t\tmatchCronField(monthPart, date.getMonth() + 1) &&\n\t\tmatchCronField(weekdayPart, date.getDay())\n\t);\n}\n\nfunction matchCronField(field: string, value: number): boolean {\n\tif (field === \"*\") return true;\n\tif (field.startsWith(\"*/\")) {\n\t\tconst step = Number.parseInt(field.slice(2), 10);\n\t\treturn step > 0 && value % step === 0;\n\t}\n\tconst values = field.split(\",\");\n\treturn values.some((v) => Number.parseInt(v, 10) === value);\n}\n\n// Export helpers for testing.\nexport { matchesCron, parseIntervalMs };\n","import { spawn } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport type { GithubCopilotCliPayload } from \"@serviceme/devtools-protocol\";\nimport { isCopilotAuthenticated } from \"../../copilot/doctor\";\nimport { resolveConfiguredTimeoutMs } from \"./timeout\";\nimport type {\n\tExecutorResult,\n\tOutputEventCallback,\n\tStreamingExecutorHandle,\n\tStreamingTaskExecutor,\n} from \"./types\";\n\nconst MAX_OUTPUT_BYTES = 2 * 1024 * 1024; // 2 MB\nconst DEFAULT_TIMEOUT_MS = 300_000;\n\nexport interface ResolvedGithubCopilotCliExecution {\n\tcommand: \"serviceme\";\n\targs: string[];\n\tcwd?: string;\n\ttimeoutMs: number | undefined;\n\tdiagnosticArgs: string[];\n\tpromptLen: number;\n}\n\nfunction redactArgs(args: string[]): string[] {\n\tconst redacted: string[] = [];\n\tlet redactNext = false;\n\tfor (const arg of args) {\n\t\tif (redactNext) {\n\t\t\tredacted.push(\"<redacted>\");\n\t\t\tredactNext = false;\n\t\t\tcontinue;\n\t\t}\n\t\tredacted.push(arg);\n\t\tif (arg === \"--prompt\") {\n\t\t\tredactNext = true;\n\t\t}\n\t}\n\treturn redacted;\n}\n\nfunction writeDiagnostic(message: string): void {\n\tconst logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH;\n\tif (logPath) {\n\t\tfs.appendFileSync(logPath, message);\n\t\treturn;\n\t}\n\tprocess.stderr.write(message);\n}\n\nexport function resolveGithubCopilotCliExecution(\n\tpayload: GithubCopilotCliPayload\n): ResolvedGithubCopilotCliExecution {\n\tconst args: string[] = [\"copilot\", \"prompt\", \"--prompt\", payload.prompt];\n\tif (payload.autopilot) {\n\t\targs.push(\"--autopilot\");\n\t}\n\tif (payload.allowTools && payload.allowTools.length > 0) {\n\t\targs.push(\"--allow-tools\", payload.allowTools.join(\",\"));\n\t}\n\tif (payload.model) {\n\t\targs.push(\"--model\", payload.model);\n\t}\n\tif (payload.agent) {\n\t\targs.push(\"--agent\", payload.agent);\n\t}\n\targs.push(\"--timeout\", String(payload.timeout ?? 0));\n\n\treturn {\n\t\tcommand: \"serviceme\",\n\t\targs,\n\t\tcwd: payload.workspace,\n\t\ttimeoutMs: resolveConfiguredTimeoutMs(payload.timeout, DEFAULT_TIMEOUT_MS),\n\t\tdiagnosticArgs: redactArgs(args),\n\t\tpromptLen: payload.prompt.length,\n\t};\n}\n\nexport class GithubCopilotCliExecutor implements StreamingTaskExecutor {\n\tasync execute(payload: unknown, abortSignal?: AbortSignal): Promise<ExecutorResult> {\n\t\t// Pre-flight auth check\n\t\tconst authenticated = await isCopilotAuthenticated();\n\t\tif (!authenticated) {\n\t\t\treturn {\n\t\t\t\tstatus: \"failure\",\n\t\t\t\terror:\n\t\t\t\t\t\"GitHub Copilot CLI authentication failed. Please run `gh auth login` to re-authenticate.\",\n\t\t\t};\n\t\t}\n\n\t\tlet output = \"\";\n\t\tconst handle = this.executeStreaming(\n\t\t\tpayload,\n\t\t\t(_stream, data) => {\n\t\t\t\toutput += data;\n\t\t\t},\n\t\t\tabortSignal\n\t\t);\n\t\tconst result = await handle.result;\n\t\treturn { ...result, output: output || result.output };\n\t}\n\n\texecuteStreaming(\n\t\tpayload: unknown,\n\t\tonOutput: OutputEventCallback,\n\t\tabortSignal?: AbortSignal\n\t): StreamingExecutorHandle {\n\t\tconst p = payload as GithubCopilotCliPayload;\n\t\tconst execution = resolveGithubCopilotCliExecution(p);\n\n\t\tlet resolve: (value: ExecutorResult) => void;\n\t\tconst resultPromise = new Promise<ExecutorResult>((r) => {\n\t\t\tresolve = r;\n\t\t});\n\t\tlet settled = false;\n\n\t\tconst settle = (result: ExecutorResult) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tif (timer) clearTimeout(timer);\n\t\t\tresolve?.(result);\n\t\t};\n\n\t\tif (abortSignal?.aborted) {\n\t\t\treturn {\n\t\t\t\tresult: Promise.resolve({\n\t\t\t\t\tstatus: \"cancelled\" as const,\n\t\t\t\t\terror: \"Execution aborted\",\n\t\t\t\t}),\n\t\t\t\tcancel: () => {},\n\t\t\t};\n\t\t}\n\n\t\twriteDiagnostic(\n\t\t\t`[GithubCopilotCliExecutor] spawn: command=${execution.command}, args=${JSON.stringify(execution.diagnosticArgs)}, promptLen=${execution.promptLen}, cwd=${execution.cwd ?? \"(default)\"}, platform=${process.platform}, windowsHide=true, pid=${process.pid}\\n`\n\t\t);\n\n\t\tconst child = spawn(execution.command, execution.args, {\n\t\t\tcwd: execution.cwd,\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\twindowsHide: true,\n\t\t});\n\n\t\tif (child.pid) {\n\t\t\twriteDiagnostic(`[GithubCopilotCliExecutor] spawned child PID=${child.pid}\\n`);\n\t\t}\n\n\t\tconst timeoutMs = execution.timeoutMs;\n\t\tconst timer =\n\t\t\ttimeoutMs != null\n\t\t\t\t? setTimeout(() => {\n\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tif (!child.killed) child.kill(\"SIGKILL\");\n\t\t\t\t\t\t}, 5000);\n\t\t\t\t\t\tsettle({\n\t\t\t\t\t\t\tstatus: \"timeout\",\n\t\t\t\t\t\t\terror: `Copilot CLI execution timed out after ${timeoutMs / 1000}s`,\n\t\t\t\t\t\t});\n\t\t\t\t\t}, timeoutMs)\n\t\t\t\t: undefined;\n\n\t\tlet stdoutBuf = \"\";\n\t\tlet stderrBuf = \"\";\n\n\t\tchild.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\tconst data = chunk.toString();\n\t\t\tstdoutBuf += data;\n\t\t\tif (stdoutBuf.length > MAX_OUTPUT_BYTES) stdoutBuf = stdoutBuf.slice(-MAX_OUTPUT_BYTES);\n\t\t\tonOutput(\"stdout\", data);\n\t\t});\n\n\t\tchild.stderr.on(\"data\", (chunk: Buffer) => {\n\t\t\tconst data = chunk.toString();\n\t\t\tstderrBuf += data;\n\t\t\tif (stderrBuf.length > MAX_OUTPUT_BYTES) stderrBuf = stderrBuf.slice(-MAX_OUTPUT_BYTES);\n\t\t\tonOutput(\"stderr\", data);\n\t\t});\n\n\t\tchild.on(\"close\", (code) => {\n\t\t\tif (code === 0) {\n\t\t\t\tsettle({ status: \"success\", output: stdoutBuf || undefined });\n\t\t\t} else {\n\t\t\t\tsettle({\n\t\t\t\t\tstatus: \"failure\",\n\t\t\t\t\toutput: stdoutBuf || undefined,\n\t\t\t\t\terror: stderrBuf || `Process exited with code ${code}`,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\tchild.on(\"error\", (err) => {\n\t\t\tsettle({ status: \"failure\", error: err.message });\n\t\t});\n\n\t\tconst cancelFn = () => {\n\t\t\tchild.kill(\"SIGTERM\");\n\t\t\tsetTimeout(() => {\n\t\t\t\tif (!child.killed) child.kill(\"SIGKILL\");\n\t\t\t}, 5000);\n\t\t\tsettle({ status: \"cancelled\", error: \"Execution cancelled\" });\n\t\t};\n\n\t\tabortSignal?.addEventListener(\"abort\", () => cancelFn(), { once: true });\n\n\t\treturn { result: resultPromise, cancel: cancelFn };\n\t}\n}\n","export function resolveConfiguredTimeoutMs(\n\ttimeoutSeconds: number | undefined,\n\tdefaultTimeoutMs: number\n): number | undefined {\n\tif (timeoutSeconds === 0) {\n\t\treturn undefined;\n\t}\n\tif (\n\t\ttypeof timeoutSeconds !== \"number\" ||\n\t\t!Number.isFinite(timeoutSeconds) ||\n\t\ttimeoutSeconds < 0\n\t) {\n\t\treturn defaultTimeoutMs;\n\t}\n\treturn timeoutSeconds * 1000;\n}\n","import type { HttpRequestPayload } from \"@serviceme/devtools-protocol\";\nimport { resolveConfiguredTimeoutMs } from \"./timeout\";\nimport type { ExecutorResult, TaskExecutor } from \"./types\";\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\nexport class HttpRequestExecutor implements TaskExecutor {\n\tasync execute(payload: unknown, abortSignal?: AbortSignal): Promise<ExecutorResult> {\n\t\tconst p = payload as HttpRequestPayload;\n\t\tconst timeoutMs = resolveConfiguredTimeoutMs(p.timeout, DEFAULT_TIMEOUT_MS);\n\n\t\tconst ac = new AbortController();\n\t\tlet timedOut = false;\n\t\tconst timer =\n\t\t\ttimeoutMs != null\n\t\t\t\t? setTimeout(() => {\n\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\tac.abort();\n\t\t\t\t\t}, timeoutMs)\n\t\t\t\t: undefined;\n\n\t\tif (abortSignal?.aborted) {\n\t\t\tif (timer) clearTimeout(timer);\n\t\t\treturn { status: \"cancelled\", error: \"Execution aborted\" };\n\t\t}\n\n\t\tlet externalAbort = false;\n\t\tabortSignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\texternalAbort = true;\n\t\t\t\tif (timer) clearTimeout(timer);\n\t\t\t\tac.abort();\n\t\t\t},\n\t\t\t{ once: true }\n\t\t);\n\n\t\ttry {\n\t\t\tconst response = await fetch(p.url, {\n\t\t\t\tmethod: p.method,\n\t\t\t\theaders: p.headers,\n\t\t\t\tbody: p.body,\n\t\t\t\tsignal: ac.signal,\n\t\t\t});\n\t\t\tif (timer) clearTimeout(timer);\n\n\t\t\tconst body = await response.text();\n\t\t\tif (response.ok) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\toutput: `${response.status} ${response.statusText}\\n${body}`.trim(),\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstatus: \"failure\",\n\t\t\t\terror: `HTTP ${response.status} ${response.statusText}\\n${body}`.trim(),\n\t\t\t};\n\t\t} catch (err: unknown) {\n\t\t\tif (timer) clearTimeout(timer);\n\t\t\tif (err instanceof Error && err.name === \"AbortError\") {\n\t\t\t\tif (externalAbort) {\n\t\t\t\t\treturn { status: \"cancelled\", error: \"Execution cancelled\" };\n\t\t\t\t}\n\t\t\t\tif (!timedOut || timeoutMs == null) {\n\t\t\t\t\treturn { status: \"failure\", error: err.message };\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"timeout\",\n\t\t\t\t\terror: `HTTP request timed out after ${timeoutMs / 1000}s`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\treturn { status: \"failure\", error: message };\n\t\t}\n\t}\n}\n","import { type SpawnOptions, spawn } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { ShellPayload } from \"@serviceme/devtools-protocol\";\nimport { resolveConfiguredTimeoutMs } from \"./timeout\";\nimport type {\n\tExecutorResult,\n\tOutputEventCallback,\n\tStreamingExecutorHandle,\n\tStreamingTaskExecutor,\n} from \"./types\";\n\nconst MAX_OUTPUT_BYTES = 1024 * 1024; // 1 MB\nconst DEFAULT_TIMEOUT_MS = 60_000;\nconst POSIX_SHELL_CANDIDATES = [\"bash.exe\", \"sh.exe\"];\n\nexport interface ShellExecutionResolutionOptions {\n\tplatform?: NodeJS.Platform;\n\tenv?: NodeJS.ProcessEnv;\n\tfileExists?: (candidate: string) => boolean;\n}\n\nexport interface ResolvedShellExecution {\n\tcommand: string;\n\targs: string[];\n\tstdinScript?: string;\n\toutputEncoding: BufferEncoding;\n\tshellKind: \"cmd\" | \"posix\";\n}\n\nexport function resolveShellExecution(\n\tscript: string,\n\toptions: ShellExecutionResolutionOptions = {}\n): ResolvedShellExecution {\n\tconst platform = options.platform ?? process.platform;\n\tconst env = options.env ?? process.env;\n\tconst fileExists = options.fileExists ?? fs.existsSync;\n\n\tif (platform === \"win32\") {\n\t\tconst posixShell = usesPosixShellSyntax(script) ? findWindowsPosixShell(env, fileExists) : null;\n\t\tif (posixShell) {\n\t\t\treturn {\n\t\t\t\tcommand: posixShell,\n\t\t\t\targs: [\"-s\"],\n\t\t\t\tstdinScript: script,\n\t\t\t\toutputEncoding: \"utf8\",\n\t\t\t\tshellKind: \"posix\",\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tcommand: env.ComSpec ?? env.COMSPEC ?? \"cmd.exe\",\n\t\t\targs: [\"/d\", \"/s\", \"/c\", script],\n\t\t\toutputEncoding: \"utf8\",\n\t\t\tshellKind: \"cmd\",\n\t\t};\n\t}\n\n\treturn {\n\t\tcommand: \"sh\",\n\t\targs: [\"-s\"],\n\t\tstdinScript: script,\n\t\toutputEncoding: \"utf8\",\n\t\tshellKind: \"posix\",\n\t};\n}\n\nfunction usesPosixShellSyntax(script: string): boolean {\n\treturn /\\$\\([^)]*\\)|`[^`]*`/.test(script);\n}\n\nfunction findWindowsPosixShell(\n\tenv: NodeJS.ProcessEnv,\n\tfileExists: (candidate: string) => boolean\n): string | null {\n\tconst explicitShell = env.SERVICEME_POSIX_SHELL;\n\tif (explicitShell && fileExists(explicitShell)) {\n\t\treturn explicitShell;\n\t}\n\n\tconst knownGitBashPaths = [\n\t\t\"C:\\\\Program Files\\\\Git\\\\bin\\\\bash.exe\",\n\t\t\"C:\\\\Program Files\\\\Git\\\\usr\\\\bin\\\\bash.exe\",\n\t\t\"C:\\\\Program Files (x86)\\\\Git\\\\bin\\\\bash.exe\",\n\t\t\"C:\\\\Program Files (x86)\\\\Git\\\\usr\\\\bin\\\\bash.exe\",\n\t];\n\tfor (const candidate of knownGitBashPaths) {\n\t\tif (fileExists(candidate)) return candidate;\n\t}\n\n\tconst pathValue = env.Path ?? env.PATH ?? \"\";\n\tfor (const dir of pathValue.split(path.win32.delimiter)) {\n\t\tif (!dir) continue;\n\t\tfor (const executable of POSIX_SHELL_CANDIDATES) {\n\t\t\tconst candidate = path.win32.join(dir, executable);\n\t\t\tif (fileExists(candidate) && !isWindowsWslLauncher(candidate)) {\n\t\t\t\treturn candidate;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction isWindowsWslLauncher(candidate: string): boolean {\n\tconst normalized = path.win32.normalize(candidate).toLowerCase();\n\treturn (\n\t\tnormalized.endsWith(\"\\\\windows\\\\system32\\\\bash.exe\") ||\n\t\tnormalized.endsWith(\"\\\\windows\\\\syswow64\\\\bash.exe\")\n\t);\n}\n\nfunction writeDiagnostic(message: string): void {\n\tconst logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH;\n\tif (logPath) {\n\t\tfs.appendFileSync(logPath, message);\n\t\treturn;\n\t}\n\tprocess.stderr.write(message);\n}\n\nexport class ShellExecutor implements StreamingTaskExecutor {\n\tasync execute(payload: unknown, abortSignal?: AbortSignal): Promise<ExecutorResult> {\n\t\tlet output = \"\";\n\t\tconst handle = this.executeStreaming(\n\t\t\tpayload,\n\t\t\t(_stream, data) => {\n\t\t\t\toutput += data;\n\t\t\t},\n\t\t\tabortSignal\n\t\t);\n\t\tconst result = await handle.result;\n\t\treturn { ...result, output: output || result.output };\n\t}\n\n\texecuteStreaming(\n\t\tpayload: unknown,\n\t\tonOutput: OutputEventCallback,\n\t\tabortSignal?: AbortSignal\n\t): StreamingExecutorHandle {\n\t\tconst p = payload as ShellPayload;\n\t\tconst timeoutMs = resolveConfiguredTimeoutMs(p.timeout, DEFAULT_TIMEOUT_MS);\n\n\t\tlet resolve: (value: ExecutorResult) => void;\n\t\tconst resultPromise = new Promise<ExecutorResult>((r) => {\n\t\t\tresolve = r;\n\t\t});\n\t\tlet settled = false;\n\n\t\tconst settle = (result: ExecutorResult) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tif (timer) clearTimeout(timer);\n\t\t\tresolve?.(result);\n\t\t};\n\n\t\tif (abortSignal?.aborted) {\n\t\t\treturn {\n\t\t\t\tresult: Promise.resolve({\n\t\t\t\t\tstatus: \"cancelled\" as const,\n\t\t\t\t\terror: \"Execution aborted\",\n\t\t\t\t}),\n\t\t\t\tcancel: () => {},\n\t\t\t};\n\t\t}\n\n\t\tconst shellExecution = resolveShellExecution(p.script);\n\n\t\tconst spawnOpts: SpawnOptions = {\n\t\t\tcwd: p.cwd,\n\t\t\tstdio: [shellExecution.stdinScript ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n\t\t\twindowsHide: true,\n\t\t};\n\n\t\twriteDiagnostic(\n\t\t\t`[ShellExecutor] spawn:\n platform=${process.platform}\n shell=${shellExecution.shellKind}\n command=${shellExecution.command}\n args=${JSON.stringify(shellExecution.args.filter((a) => a !== p.script))}\n stdin=${shellExecution.stdinScript ? \"true\" : \"false\"}\n cwd=${p.cwd ?? \"(default)\"}\n timeout=${timeoutMs != null ? `${timeoutMs}ms` : \"unlimited\"}\n scriptLen=${p.script.length}\n windowsHide=true\n daemonPid=${process.pid}\\n`\n\t\t);\n\n\t\tconst child = spawn(shellExecution.command, shellExecution.args, spawnOpts);\n\n\t\tif (shellExecution.stdinScript && child.stdin) {\n\t\t\tchild.stdin.end(shellExecution.stdinScript);\n\t\t}\n\n\t\tif (child.pid) {\n\t\t\twriteDiagnostic(`[ShellExecutor] spawned child PID=${child.pid}\\n`);\n\t\t}\n\n\t\tconst timer =\n\t\t\ttimeoutMs != null\n\t\t\t\t? setTimeout(() => {\n\t\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tif (!child.killed) child.kill(\"SIGKILL\");\n\t\t\t\t\t\t}, 5000);\n\t\t\t\t\t\tsettle({\n\t\t\t\t\t\t\tstatus: \"timeout\",\n\t\t\t\t\t\t\terror: `Shell execution timed out after ${timeoutMs / 1000}s`,\n\t\t\t\t\t\t});\n\t\t\t\t\t}, timeoutMs)\n\t\t\t\t: undefined;\n\n\t\tlet stdoutBuf = \"\";\n\t\tlet stderrBuf = \"\";\n\n\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => {\n\t\t\tconst data = chunk.toString(shellExecution.outputEncoding);\n\t\t\tstdoutBuf += data;\n\t\t\tif (stdoutBuf.length > MAX_OUTPUT_BYTES) stdoutBuf = stdoutBuf.slice(-MAX_OUTPUT_BYTES);\n\t\t\tonOutput(\"stdout\", data);\n\t\t});\n\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\tconst data = chunk.toString(shellExecution.outputEncoding);\n\t\t\tstderrBuf += data;\n\t\t\tif (stderrBuf.length > MAX_OUTPUT_BYTES) stderrBuf = stderrBuf.slice(-MAX_OUTPUT_BYTES);\n\t\t\tonOutput(\"stderr\", data);\n\t\t});\n\n\t\tchild.on(\"close\", (code: number | null) => {\n\t\t\twriteDiagnostic(`[ShellExecutor] child PID=${child.pid} exited: code=${code}\\n`);\n\t\t\tif (code === 0) {\n\t\t\t\tsettle({ status: \"success\", output: stdoutBuf || undefined });\n\t\t\t} else {\n\t\t\t\tsettle({\n\t\t\t\t\tstatus: \"failure\",\n\t\t\t\t\toutput: stdoutBuf || undefined,\n\t\t\t\t\terror: stderrBuf || `Process exited with code ${code}`,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\tchild.on(\"error\", (err: Error) => {\n\t\t\tsettle({ status: \"failure\", error: err.message });\n\t\t});\n\n\t\tconst cancelFn = () => {\n\t\t\tchild.kill(\"SIGTERM\");\n\t\t\tsetTimeout(() => {\n\t\t\t\tif (!child.killed) child.kill(\"SIGKILL\");\n\t\t\t}, 5000);\n\t\t\tsettle({ status: \"cancelled\", error: \"Execution cancelled\" });\n\t\t};\n\n\t\tabortSignal?.addEventListener(\"abort\", () => cancelFn(), { once: true });\n\n\t\treturn { result: resultPromise, cancel: cancelFn };\n\t}\n}\n","import type { TaskExecutionStatus } from \"@serviceme/devtools-protocol\";\n\nexport interface ExecutorResult {\n\tstatus: TaskExecutionStatus;\n\toutput?: string;\n\terror?: string;\n}\n\nexport interface TaskExecutor {\n\texecute(payload: unknown, abortSignal?: AbortSignal): Promise<ExecutorResult>;\n}\n\n// Streaming interfaces for Bridge task execution\n\nexport type OutputEventCallback = (stream: \"stdout\" | \"stderr\", data: string) => void;\n\nexport interface StreamingExecutorHandle {\n\tresult: Promise<ExecutorResult>;\n\tcancel: () => void;\n}\n\nexport interface StreamingTaskExecutor extends TaskExecutor {\n\texecuteStreaming(\n\t\tpayload: unknown,\n\t\tonOutput: OutputEventCallback,\n\t\tabortSignal?: AbortSignal\n\t): StreamingExecutorHandle;\n}\n\nexport function isStreamingTaskExecutor(executor: TaskExecutor): executor is StreamingTaskExecutor {\n\treturn (\n\t\t\"executeStreaming\" in executor &&\n\t\ttypeof (executor as StreamingTaskExecutor).executeStreaming === \"function\"\n\t);\n}\n","import type { ScheduledTaskType } from \"@serviceme/devtools-protocol\";\nimport { GithubCopilotCliExecutor } from \"./GithubCopilotCliExecutor\";\nimport { HttpRequestExecutor } from \"./HttpRequestExecutor\";\nimport { ShellExecutor } from \"./ShellExecutor\";\nimport type { TaskExecutor } from \"./types\";\n\n/** Task types executable by the bridge (excludes \"command\" which runs in-extension) */\ntype BridgeTaskType = Exclude<ScheduledTaskType, \"command\">;\n\nconst executors: Record<BridgeTaskType, TaskExecutor> = {\n\tshell: new ShellExecutor(),\n\thttp_request: new HttpRequestExecutor(),\n\tgithub_copilot_cli: new GithubCopilotCliExecutor(),\n};\n\nexport function getExecutor(taskType: ScheduledTaskType): TaskExecutor {\n\tconst executor = executors[taskType as BridgeTaskType];\n\tif (!executor) {\n\t\tthrow new Error(`Unsupported bridge task type: ${taskType}`);\n\t}\n\treturn executor;\n}\n\nexport type {\n\tExecutorResult,\n\tOutputEventCallback,\n\tStreamingExecutorHandle,\n\tStreamingTaskExecutor,\n\tTaskExecutor,\n} from \"./types\";\nexport { isStreamingTaskExecutor } from \"./types\";\nexport { GithubCopilotCliExecutor, HttpRequestExecutor, ShellExecutor };\n","import { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type {\n\tScheduledTask,\n\tScheduledTasksConfig,\n\tScheduledTasksConfigV1,\n\tScheduledTaskType,\n\tTaskPayload,\n\tTaskWorkspaceRef,\n} from \"@serviceme/devtools-protocol\";\nimport {\n\tcreateServicemeError,\n\tisScheduledTasksConfig,\n\tisScheduledTasksConfigV1,\n\tisScheduledTaskV1,\n\tmigrateV1ToV2,\n} from \"@serviceme/devtools-protocol\";\nimport { getMigrationFailuresPath, getScheduledTasksConfigPath } from \"../paths/userHome\";\n\nfunction emptyConfig(): ScheduledTasksConfig {\n\treturn { version: 2, tasks: [] };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\n/**\n * Detect the \"v2 container with v1-shaped tasks\" hybrid: a config object\n * whose top-level `version` is `2` and whose `tasks` array is non-empty,\n * but where every task fails the v2 validator specifically because of the\n * missing required `workspace` field. The task entries must still pass\n * the v1 validator (otherwise the file is genuinely corrupt, not just\n * stale).\n */\nfunction isV2ContainerWithV1Tasks(value: unknown): value is {\n\tversion: 2;\n\ttasks: unknown[];\n} {\n\tif (!isRecord(value)) return false;\n\tif (value.version !== 2) return false;\n\tif (!Array.isArray(value.tasks) || value.tasks.length === 0) return false;\n\tconst allValidV2 = value.tasks.every((t) => !isScheduledTaskV1(t) || tIsValidV2(t));\n\tif (allValidV2) return false;\n\t// All tasks pass v1 — exactly the v2-container-with-v1-tasks hybrid.\n\treturn value.tasks.every((t) => isScheduledTaskV1(t));\n}\n\nfunction tIsValidV2(t: unknown): boolean {\n\treturn isRecord(t) && isRecord((t as Record<string, unknown>).workspace);\n}\n\n/**\n * Cast a v2-container-with-v1-tasks hybrid back to a v1 `ScheduledTasksConfigV1`\n * so we can feed it through the existing `migrateV1ToV2` helper. No data is\n * transformed — only the type assertion; the tasks inside are already\n * v1-shaped.\n */\nfunction v1ContainerShape(value: { tasks: unknown[] }): ScheduledTasksConfigV1 {\n\treturn { version: 1, tasks: value.tasks.filter(isScheduledTaskV1) };\n}\n\n/**\n * Fallback workspace context used when the manager has to auto-migrate a\n * v1-shaped task and the caller did not supply one. Points at the user's\n * home directory; the user can always re-edit the task in the webview to\n * point at the real workspace afterwards. This is intentionally\n * pessimistic — surfacing the task at all beats the previous behaviour\n * of silently dropping it.\n */\nfunction defaultWorkspaceContext(): TaskWorkspaceRef {\n\tconst home = os.homedir() || \"/\";\n\treturn { path: home, name: path.basename(home) || home };\n}\n\nfunction requireNonEmptyString(\n\tpayload: TaskPayload,\n\tfield: string,\n\ttaskType: ScheduledTaskType\n): void {\n\tif (!isRecord(payload) || typeof payload[field] !== \"string\" || !payload[field].trim()) {\n\t\tthrow createServicemeError(\"invalid_payload\", `${taskType} payload ${field} is required`);\n\t}\n}\n\nexport function validateTaskPayload(taskType: ScheduledTaskType, payload: TaskPayload): void {\n\tswitch (taskType) {\n\t\tcase \"command\":\n\t\t\trequireNonEmptyString(payload, \"command\", taskType);\n\t\t\treturn;\n\t\tcase \"shell\":\n\t\t\trequireNonEmptyString(payload, \"script\", taskType);\n\t\t\treturn;\n\t\tcase \"http_request\":\n\t\t\trequireNonEmptyString(payload, \"url\", taskType);\n\t\t\trequireNonEmptyString(payload, \"method\", taskType);\n\t\t\treturn;\n\t\tcase \"github_copilot_cli\":\n\t\t\trequireNonEmptyString(payload, \"prompt\", taskType);\n\t\t\treturn;\n\t}\n}\n\nexport interface CreateTaskInput {\n\tname: string;\n\tdescription?: string;\n\tscheduleType: \"cron\" | \"interval\";\n\tschedule: string;\n\ttaskType: ScheduledTaskType;\n\tpayload: TaskPayload;\n\tworkspace: TaskWorkspaceRef;\n\tenabled?: boolean;\n}\n\nexport interface EditTaskInput {\n\tname?: string;\n\tdescription?: string;\n\tscheduleType?: \"cron\" | \"interval\";\n\tschedule?: string;\n\ttaskType?: ScheduledTaskType;\n\tpayload?: TaskPayload;\n\tworkspace?: TaskWorkspaceRef;\n\tenabled?: boolean;\n}\n\nexport interface TaskConfigManagerOptions {\n\t/**\n\t * Override the on-disk config path. Defaults to the global\n\t * `~/.serviceme/scheduled-tasks.json`. Tests use this to point at a tmp\n\t * directory; production code should leave it unset.\n\t */\n\tconfigPath?: string;\n\t/**\n\t * Workspace context used when a legacy v1 config is read. v1 tasks had no\n\t * per-task workspace, so the manager needs a fallback to attach when\n\t * auto-migrating. Required only if the underlying file might still be v1.\n\t */\n\tworkspaceContext?: TaskWorkspaceRef;\n\t/**\n\t * Override the path where malformed-config diagnostic entries are appended.\n\t * Defaults to `~/.serviceme/migration-failures.json`. Tests can point at a\n\t * tmp file to assert failures were recorded.\n\t */\n\tmigrationFailuresPath?: string;\n\t/**\n\t * Inject a logger for the manager's recovery / migration warnings. Defaults\n\t * to `console.warn`. Tests pass a `vi.fn`/`mock.fn` to assert the warnings\n\t * were emitted.\n\t */\n\tlogger?: (message: string) => void;\n}\n\nexport class TaskConfigManager {\n\tprivate readonly configPath: string;\n\tprivate readonly workspaceContext?: TaskWorkspaceRef;\n\tprivate readonly migrationFailuresPath?: string;\n\tprivate readonly logger?: (message: string) => void;\n\n\tconstructor(options: TaskConfigManagerOptions = {}) {\n\t\tthis.configPath = options.configPath ?? getScheduledTasksConfigPath();\n\t\tthis.workspaceContext = options.workspaceContext;\n\t\tthis.migrationFailuresPath = options.migrationFailuresPath;\n\t\tthis.logger = options.logger;\n\t}\n\n\tgetConfigPath(): string {\n\t\treturn this.configPath;\n\t}\n\n\treadConfig(): ScheduledTasksConfig {\n\t\tif (!fs.existsSync(this.configPath)) {\n\t\t\treturn emptyConfig();\n\t\t}\n\t\tconst raw = fs.readFileSync(this.configPath, \"utf-8\");\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(raw);\n\t\t} catch (error) {\n\t\t\t// Corrupted file — record failure + warn, then treat as empty.\n\t\t\t// Mirrors the log manager's repair-on-read policy.\n\t\t\tthis.recordMalformedConfigFailure(\"parse_error\", String(error), raw);\n\t\t\treturn emptyConfig();\n\t\t}\n\n\t\t// v2 (current): return as-is.\n\t\tif (isScheduledTasksConfig(parsed)) {\n\t\t\treturn parsed;\n\t\t}\n\n\t\t// Mixed / recoverable: a v2 container holding v1-shaped tasks (no\n\t\t// `workspace` field). Happens when a tool writes the header\n\t\t// `\"version\": 2` but each task is missing the v2-required `workspace`\n\t\t// ref — observed when users hand-edit `~/.serviceme/scheduled-tasks.json`\n\t\t// or when older bugged writers stamped v2 onto v1 task shapes. Treat\n\t\t// as v1 + auto-migrate using either the explicit `workspaceContext`\n\t\t// (preferred) or a sensible default (the user's home dir) so the\n\t\t// tasks stay visible instead of being silently dropped. Repairs the\n\t\t// on-disk file on success.\n\t\tif (isV2ContainerWithV1Tasks(parsed)) {\n\t\t\tconst v1 = v1ContainerShape(parsed);\n\t\t\tconst ctx = this.workspaceContext ?? defaultWorkspaceContext();\n\t\t\tconst { config, issues } = migrateV1ToV2(v1, ctx);\n\t\t\tthis.warn(\n\t\t\t\t`TaskConfigManager: detected v2 container with v1-shaped tasks in ${this.configPath} — auto-migrated in place. ` +\n\t\t\t\t\t`Tasks are now bound to workspace \"${ctx.path}\". Pass TaskConfigManager({ workspaceContext }) to override.`\n\t\t\t);\n\t\t\tfor (const issue of issues) this.warn(`TaskConfigManager migration issue: ${issue}`);\n\t\t\ttry {\n\t\t\t\tthis.writeConfig(config);\n\t\t\t} catch (writeError) {\n\t\t\t\tthis.warn(\n\t\t\t\t\t`TaskConfigManager: failed to rewrite repaired config to ${this.configPath}: ${String(writeError)}`\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn config;\n\t\t}\n\n\t\t// v1 (legacy): auto-migrate in memory. The MigrateToGlobal scanner is\n\t\t// the long-term path forward; this branch is the safety net for any\n\t\t// global config file that was somehow written in v1 format during the\n\t\t// migration window.\n\t\tif (isScheduledTasksConfigV1(parsed)) {\n\t\t\tif (!this.workspaceContext) {\n\t\t\t\tthrow createServicemeError(\n\t\t\t\t\t\"invalid_params\",\n\t\t\t\t\t\"TaskConfigManager: v1 config found but no workspaceContext provided. \" +\n\t\t\t\t\t\t\"Pass TaskConfigManager({ workspaceContext }) when reading a legacy v1 file.\"\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst v1 = parsed as ScheduledTasksConfigV1;\n\t\t\treturn migrateV1ToV2(v1, this.workspaceContext).config;\n\t\t}\n\n\t\t// Unknown shape — record a failure entry so the user can debug, then\n\t\t// treat as empty. Don't return silently: a previous version's CLI\n\t\t// sometimes left an unrecoverable file that ate tasks invisibly.\n\t\tthis.recordMalformedConfigFailure(\"unknown_shape\", \"Config did not match v1 or v2 schema\", raw);\n\t\treturn emptyConfig();\n\t}\n\n\tprivate warn(message: string): void {\n\t\tif (this.logger) this.logger(message);\n\t\telse console.warn(`[TaskConfigManager] ${message}`);\n\t}\n\n\tprivate recordMalformedConfigFailure(reason: string, detail: string, raw: string): void {\n\t\tthis.warn(\n\t\t\t`TaskConfigManager: malformed config at ${this.configPath} (${reason}): ${detail}. ` +\n\t\t\t\t`Tasks in this file are invisible until repaired. A diagnostic entry is written to ${\n\t\t\t\t\tthis.migrationFailuresPath ?? getMigrationFailuresPath()\n\t\t\t\t}.`\n\t\t);\n\t\ttry {\n\t\t\tconst target = this.migrationFailuresPath ?? getMigrationFailuresPath();\n\t\t\tconst prior = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.parse(fs.readFileSync(target, \"utf-8\")) as unknown;\n\t\t\t\t} catch {\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t})();\n\t\t\tconst failures = Array.isArray(prior) ? (prior as unknown[]) : [];\n\t\t\tfailures.push({\n\t\t\t\tpath: this.configPath,\n\t\t\t\treason,\n\t\t\t\tdetail,\n\t\t\t\tsnippet: raw.slice(0, 500),\n\t\t\t\trecordedAt: new Date().toISOString(),\n\t\t\t});\n\t\t\tfs.mkdirSync(path.dirname(target), { recursive: true });\n\t\t\tfs.writeFileSync(target, JSON.stringify(failures, null, \"\\t\"), \"utf-8\");\n\t\t} catch (writeError) {\n\t\t\tthis.warn(\n\t\t\t\t`TaskConfigManager: also failed to write migration-failures log: ${String(writeError)}`\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate writeConfig(config: ScheduledTasksConfig): void {\n\t\tconst dir = path.dirname(this.configPath);\n\t\tif (!fs.existsSync(dir)) {\n\t\t\tfs.mkdirSync(dir, { recursive: true });\n\t\t}\n\t\t// Atomic write: write to temp then rename\n\t\tconst tmp = `${this.configPath}.tmp`;\n\t\tfs.writeFileSync(tmp, JSON.stringify(config, null, \"\\t\"), \"utf-8\");\n\t\tfs.renameSync(tmp, this.configPath);\n\t}\n\n\tlistTasks(): ScheduledTask[] {\n\t\treturn this.readConfig().tasks;\n\t}\n\n\tgetTask(id: string): ScheduledTask | undefined {\n\t\treturn this.readConfig().tasks.find((t) => t.id === id);\n\t}\n\n\tgetTaskByName(name: string): ScheduledTask | undefined {\n\t\treturn this.readConfig().tasks.find((t) => t.name === name);\n\t}\n\n\tcreateTask(input: CreateTaskInput): ScheduledTask {\n\t\tif (!input.workspace?.path || !input.workspace.name) {\n\t\t\tthrow createServicemeError(\n\t\t\t\t\"invalid_params\",\n\t\t\t\t\"createTask: workspace with path and name is required (v2 schema)\"\n\t\t\t);\n\t\t}\n\t\tvalidateTaskPayload(input.taskType, input.payload);\n\t\tconst config = this.readConfig();\n\t\tconst now = new Date().toISOString();\n\t\tconst task: ScheduledTask = {\n\t\t\tid: randomUUID(),\n\t\t\tname: input.name,\n\t\t\tdescription: input.description,\n\t\t\tenabled: input.enabled ?? true,\n\t\t\tscheduleType: input.scheduleType,\n\t\t\tschedule: input.schedule,\n\t\t\ttaskType: input.taskType,\n\t\t\tpayload: input.payload,\n\t\t\tworkspace: input.workspace,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t};\n\t\tconfig.tasks.push(task);\n\t\tthis.writeConfig(config);\n\t\treturn task;\n\t}\n\n\teditTask(id: string, input: EditTaskInput): ScheduledTask {\n\t\tconst config = this.readConfig();\n\t\tconst idx = config.tasks.findIndex((t) => t.id === id);\n\t\tif (idx === -1) {\n\t\t\tthrow new Error(`Task '${id}' not found`);\n\t\t}\n\t\tconst existing = config.tasks[idx];\n\t\tif (!existing) {\n\t\t\tthrow new Error(`Task '${id}' not found`);\n\t\t}\n\t\tconst nextTaskType = input.taskType ?? existing.taskType;\n\t\tconst nextPayload = input.payload ?? existing.payload;\n\t\tconst nextWorkspace = input.workspace ?? existing.workspace;\n\t\tvalidateTaskPayload(nextTaskType, nextPayload);\n\t\tconst updated: ScheduledTask = {\n\t\t\t...existing,\n\t\t\t...(input.name !== undefined && { name: input.name }),\n\t\t\t...(input.description !== undefined && {\n\t\t\t\tdescription: input.description,\n\t\t\t}),\n\t\t\t...(input.scheduleType !== undefined && {\n\t\t\t\tscheduleType: input.scheduleType,\n\t\t\t}),\n\t\t\t...(input.schedule !== undefined && { schedule: input.schedule }),\n\t\t\t...(input.taskType !== undefined && { taskType: input.taskType }),\n\t\t\t...(input.payload !== undefined && { payload: input.payload }),\n\t\t\t...(input.workspace !== undefined && { workspace: nextWorkspace }),\n\t\t\t...(input.enabled !== undefined && { enabled: input.enabled }),\n\t\t\tupdatedAt: new Date().toISOString(),\n\t\t};\n\t\tconfig.tasks[idx] = updated;\n\t\tthis.writeConfig(config);\n\t\treturn updated;\n\t}\n\n\tdeleteTask(id: string): ScheduledTask {\n\t\tconst config = this.readConfig();\n\t\tconst idx = config.tasks.findIndex((t) => t.id === id);\n\t\tif (idx === -1) {\n\t\t\tthrow new Error(`Task '${id}' not found`);\n\t\t}\n\t\tconst removed = config.tasks[idx];\n\t\tif (!removed) {\n\t\t\tthrow new Error(`Task '${id}' not found`);\n\t\t}\n\t\tconfig.tasks.splice(idx, 1);\n\t\tthis.writeConfig(config);\n\t\treturn removed;\n\t}\n\n\ttoggleTask(id: string, enabled: boolean): ScheduledTask {\n\t\treturn this.editTask(id, { enabled });\n\t}\n}\n","import type {\n\tConcurrencyPolicy,\n\tGithubCopilotCliPayload,\n\tRunningTaskInfo,\n\tScheduledTaskType,\n\tShellPayload,\n\tTaskCancelledEventParams,\n\tTaskCompletedEventParams,\n\tTaskExecutionLog,\n\tTaskExecutionSnapshot,\n\tTaskFailedEventParams,\n\tTaskOutputEventParams,\n\tTaskPayload,\n\tTaskStartedEventParams,\n} from \"@serviceme/devtools-protocol\";\nimport { type ExecutorResult, isStreamingTaskExecutor, type TaskExecutor } from \"./executors/types\";\nimport { validateTaskPayload } from \"./TaskConfigManager\";\n\nexport interface TaskEventListener {\n\tonStarted(params: TaskStartedEventParams): void;\n\tonOutput(params: TaskOutputEventParams): void;\n\tonCompleted(params: TaskCompletedEventParams): void;\n\tonFailed(params: TaskFailedEventParams): void;\n\tonCancelled(params: TaskCancelledEventParams): void;\n}\n\ninterface RunningExecution {\n\texecutionId: string;\n\ttaskId: string;\n\tstartedAt: string;\n\tcancel: () => void;\n}\n\nfunction hasNonEmptyString(value: unknown): value is string {\n\treturn typeof value === \"string\" && value.trim().length > 0;\n}\n\nexport function resolveTaskExecutionPayload(\n\ttaskType: ScheduledTaskType,\n\tpayload: TaskPayload,\n\tworkspacePath: string\n): TaskPayload {\n\tif (!hasNonEmptyString(workspacePath)) {\n\t\treturn payload;\n\t}\n\n\tif (taskType === \"shell\") {\n\t\tconst shellPayload = payload as ShellPayload;\n\t\tif (hasNonEmptyString(shellPayload.cwd)) {\n\t\t\treturn shellPayload;\n\t\t}\n\t\treturn { ...shellPayload, cwd: workspacePath };\n\t}\n\n\tif (taskType === \"github_copilot_cli\") {\n\t\tconst copilotPayload = payload as GithubCopilotCliPayload;\n\t\tif (hasNonEmptyString(copilotPayload.workspace)) {\n\t\t\treturn copilotPayload;\n\t\t}\n\t\treturn { ...copilotPayload, workspace: workspacePath };\n\t}\n\n\treturn payload;\n}\n\nexport class TaskExecutionEngine {\n\tprivate readonly running = new Map<string, RunningExecution>();\n\tprivate readonly taskExecutions = new Map<string, Set<string>>();\n\tprivate listener: TaskEventListener | null = null;\n\n\tconstructor(private readonly getExecutor: (taskType: string) => TaskExecutor) {}\n\n\tsetListener(listener: TaskEventListener): void {\n\t\tthis.listener = listener;\n\t}\n\n\tasync execute(snapshot: TaskExecutionSnapshot): Promise<void> {\n\t\tconst policy: ConcurrencyPolicy = snapshot.concurrencyPolicy ?? \"reject\";\n\n\t\t// Enforce concurrency policy\n\t\tif (policy === \"reject\") {\n\t\t\tconst existingIds = this.taskExecutions.get(snapshot.taskId);\n\t\t\tif (existingIds && existingIds.size > 0) {\n\t\t\t\tthrow new Error(`TASK_ALREADY_RUNNING: Task ${snapshot.taskId} is already running`);\n\t\t\t}\n\t\t}\n\n\t\tconst startedAt = new Date().toISOString();\n\n\t\t// Track by taskId → executionId\n\t\tif (!this.taskExecutions.has(snapshot.taskId)) {\n\t\t\tthis.taskExecutions.set(snapshot.taskId, new Set());\n\t\t}\n\t\tthis.taskExecutions.get(snapshot.taskId)?.add(snapshot.executionId);\n\n\t\t// Set up cancellation\n\t\tconst ac = new AbortController();\n\t\tconst runningExec: RunningExecution = {\n\t\t\texecutionId: snapshot.executionId,\n\t\t\ttaskId: snapshot.taskId,\n\t\t\tstartedAt,\n\t\t\tcancel: () => ac.abort(),\n\t\t};\n\t\tthis.running.set(snapshot.executionId, runningExec);\n\n\t\tthis.listener?.onStarted({\n\t\t\texecutionId: snapshot.executionId,\n\t\t\ttaskId: snapshot.taskId,\n\t\t\tstartedAt,\n\t\t});\n\n\t\ttry {\n\t\t\tconst executionPayload = resolveTaskExecutionPayload(\n\t\t\t\tsnapshot.type,\n\t\t\t\tsnapshot.payload,\n\t\t\t\tsnapshot.workspacePath\n\t\t\t);\n\t\t\tvalidateTaskPayload(snapshot.type, executionPayload);\n\t\t\tconst executor = this.getExecutor(snapshot.type);\n\t\t\tlet result: ExecutorResult;\n\t\t\tif (isStreamingTaskExecutor(executor)) {\n\t\t\t\tconst handle = executor.executeStreaming(\n\t\t\t\t\texecutionPayload,\n\t\t\t\t\t(stream, data) => {\n\t\t\t\t\t\tthis.listener?.onOutput({\n\t\t\t\t\t\t\texecutionId: snapshot.executionId,\n\t\t\t\t\t\t\tstream,\n\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t\tac.signal\n\t\t\t\t);\n\t\t\t\trunningExec.cancel = () => {\n\t\t\t\t\thandle.cancel();\n\t\t\t\t\tac.abort();\n\t\t\t\t};\n\t\t\t\tresult = await handle.result;\n\t\t\t} else {\n\t\t\t\tresult = await executor.execute(executionPayload, ac.signal);\n\t\t\t}\n\n\t\t\tconst log: TaskExecutionLog = {\n\t\t\t\tid: snapshot.executionId,\n\t\t\t\ttaskId: snapshot.taskId,\n\t\t\t\ttaskName: snapshot.name,\n\t\t\t\tstartedAt,\n\t\t\t\tfinishedAt: new Date().toISOString(),\n\t\t\t\tstatus: result.status === \"running\" ? \"failure\" : result.status,\n\t\t\t\toutput: result.output,\n\t\t\t\terror: result.error,\n\t\t\t};\n\n\t\t\tswitch (log.status) {\n\t\t\t\tcase \"success\":\n\t\t\t\t\tthis.listener?.onCompleted({\n\t\t\t\t\t\texecutionId: snapshot.executionId,\n\t\t\t\t\t\tlog,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"cancelled\":\n\t\t\t\t\tthis.listener?.onCancelled({\n\t\t\t\t\t\texecutionId: snapshot.executionId,\n\t\t\t\t\t\tlog,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"timeout\":\n\t\t\t\t\tthis.listener?.onFailed({\n\t\t\t\t\t\texecutionId: snapshot.executionId,\n\t\t\t\t\t\tstatus: \"timeout\",\n\t\t\t\t\t\treason: \"timeout\",\n\t\t\t\t\t\tlog,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthis.listener?.onFailed({\n\t\t\t\t\t\texecutionId: snapshot.executionId,\n\t\t\t\t\t\tstatus: \"failure\",\n\t\t\t\t\t\treason: \"error\",\n\t\t\t\t\t\tlog,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconst log: TaskExecutionLog = {\n\t\t\t\tid: snapshot.executionId,\n\t\t\t\ttaskId: snapshot.taskId,\n\t\t\t\ttaskName: snapshot.name,\n\t\t\t\tstartedAt,\n\t\t\t\tfinishedAt: new Date().toISOString(),\n\t\t\t\tstatus: ac.signal.aborted ? \"cancelled\" : \"failure\",\n\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t};\n\n\t\t\tif (ac.signal.aborted) {\n\t\t\t\tthis.listener?.onCancelled({\n\t\t\t\t\texecutionId: snapshot.executionId,\n\t\t\t\t\tlog,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.listener?.onFailed({\n\t\t\t\t\texecutionId: snapshot.executionId,\n\t\t\t\t\tstatus: \"failure\",\n\t\t\t\t\treason: \"error\",\n\t\t\t\t\tlog,\n\t\t\t\t});\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.running.delete(snapshot.executionId);\n\t\t\tconst taskExecIds = this.taskExecutions.get(snapshot.taskId);\n\t\t\tif (taskExecIds) {\n\t\t\t\ttaskExecIds.delete(snapshot.executionId);\n\t\t\t\tif (taskExecIds.size === 0) {\n\t\t\t\t\tthis.taskExecutions.delete(snapshot.taskId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tcancel(executionId: string): boolean {\n\t\tconst exec = this.running.get(executionId);\n\t\tif (!exec) return false;\n\t\texec.cancel();\n\t\treturn true;\n\t}\n\n\tlistRunning(): RunningTaskInfo[] {\n\t\treturn Array.from(this.running.values()).map((e) => ({\n\t\t\texecutionId: e.executionId,\n\t\t\ttaskId: e.taskId,\n\t\t\tstartedAt: e.startedAt,\n\t\t}));\n\t}\n\n\tdispose(): void {\n\t\tfor (const exec of this.running.values()) {\n\t\t\texec.cancel();\n\t\t}\n\t\tthis.running.clear();\n\t\tthis.taskExecutions.clear();\n\t}\n}\n","import { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type {\n\tScheduledTasksLogFile,\n\tTaskExecutionLog,\n\tTaskExecutionStatus,\n} from \"@serviceme/devtools-protocol\";\nimport { getScheduledTasksLogPath } from \"../paths/userHome\";\n\nconst MAX_LOGS = 200;\n\nfunction emptyLogFile(): ScheduledTasksLogFile {\n\treturn { logs: [] };\n}\n\nfunction isValidLogEntry(entry: unknown): entry is TaskExecutionLog {\n\tif (!entry || typeof entry !== \"object\") return false;\n\tconst e = entry as Record<string, unknown>;\n\treturn (\n\t\ttypeof e.id === \"string\" &&\n\t\ttypeof e.taskId === \"string\" &&\n\t\ttypeof e.taskName === \"string\" &&\n\t\ttypeof e.startedAt === \"string\" &&\n\t\ttypeof e.finishedAt === \"string\" &&\n\t\ttypeof e.status === \"string\"\n\t);\n}\n\nfunction validateAndRepairLogFile(raw: unknown): ScheduledTasksLogFile {\n\tif (!raw || typeof raw !== \"object\") {\n\t\treturn emptyLogFile();\n\t}\n\tconst file = raw as Record<string, unknown>;\n\tif (!Array.isArray(file.logs)) {\n\t\treturn emptyLogFile();\n\t}\n\tconst validLogs = file.logs.filter(isValidLogEntry);\n\treturn { logs: validLogs };\n}\n\nexport interface AppendLogInput {\n\ttaskId: string;\n\ttaskName: string;\n\tstartedAt: string;\n\tfinishedAt: string;\n\tstatus: Exclude<TaskExecutionStatus, \"running\">;\n\toutput?: string;\n\terror?: string;\n}\n\nexport interface TaskLogManagerOptions {\n\t/**\n\t * Override the on-disk log path. Defaults to the global\n\t * `~/.serviceme/scheduled-tasks-log.json`. Tests use this to point at a\n\t * tmp directory; production code should leave it unset.\n\t */\n\tlogPath?: string;\n}\n\nexport class TaskLogManager {\n\tprivate readonly logPath: string;\n\n\tconstructor(options: TaskLogManagerOptions = {}) {\n\t\tthis.logPath = options.logPath ?? getScheduledTasksLogPath();\n\t}\n\n\tgetLogPath(): string {\n\t\treturn this.logPath;\n\t}\n\n\tprivate readLogFile(): ScheduledTasksLogFile {\n\t\tif (!fs.existsSync(this.logPath)) {\n\t\t\treturn emptyLogFile();\n\t\t}\n\t\ttry {\n\t\t\tconst raw = fs.readFileSync(this.logPath, \"utf-8\");\n\t\t\tconst parsed = JSON.parse(raw);\n\t\t\tconst file = validateAndRepairLogFile(parsed);\n\t\t\t// If repair removed entries or structure was invalid, rewrite the file\n\t\t\tif (\n\t\t\t\t!parsed ||\n\t\t\t\ttypeof parsed !== \"object\" ||\n\t\t\t\t!Array.isArray(parsed.logs) ||\n\t\t\t\tparsed.logs.length !== file.logs.length\n\t\t\t) {\n\t\t\t\tthis.writeLogFile(file);\n\t\t\t}\n\t\t\treturn file;\n\t\t} catch {\n\t\t\t// JSON parse failed — file is corrupted, back it up and start fresh\n\t\t\tthis.backupCorruptedFile();\n\t\t\tconst fresh = emptyLogFile();\n\t\t\tthis.writeLogFile(fresh);\n\t\t\treturn fresh;\n\t\t}\n\t}\n\n\tprivate backupCorruptedFile(): void {\n\t\ttry {\n\t\t\tif (fs.existsSync(this.logPath)) {\n\t\t\t\tconst backupPath = `${this.logPath}.corrupted.${Date.now()}`;\n\t\t\t\tfs.copyFileSync(this.logPath, backupPath);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Best-effort backup\n\t\t}\n\t}\n\n\tprivate writeLogFile(file: ScheduledTasksLogFile): void {\n\t\tconst dir = path.dirname(this.logPath);\n\t\tif (!fs.existsSync(dir)) {\n\t\t\tfs.mkdirSync(dir, { recursive: true });\n\t\t}\n\t\tconst tmp = `${this.logPath}.tmp`;\n\t\tfs.writeFileSync(tmp, JSON.stringify(file, null, \"\\t\"), \"utf-8\");\n\t\tfs.renameSync(tmp, this.logPath);\n\t}\n\n\tappendLog(input: AppendLogInput): TaskExecutionLog {\n\t\tconst file = this.readLogFile();\n\t\tconst log: TaskExecutionLog = {\n\t\t\tid: randomUUID(),\n\t\t\ttaskId: input.taskId,\n\t\t\ttaskName: input.taskName,\n\t\t\tstartedAt: input.startedAt,\n\t\t\tfinishedAt: input.finishedAt,\n\t\t\tstatus: input.status,\n\t\t\toutput: input.output,\n\t\t\terror: input.error,\n\t\t};\n\t\tfile.logs.push(log);\n\t\t// FIFO trim\n\t\tif (file.logs.length > MAX_LOGS) {\n\t\t\tfile.logs = file.logs.slice(file.logs.length - MAX_LOGS);\n\t\t}\n\t\tthis.writeLogFile(file);\n\t\treturn log;\n\t}\n\n\tgetLogs(options?: { taskId?: string; limit?: number }): {\n\t\tlogs: TaskExecutionLog[];\n\t\ttotal: number;\n\t} {\n\t\tconst file = this.readLogFile();\n\t\tlet logs = file.logs;\n\t\tif (options?.taskId) {\n\t\t\tlogs = logs.filter((l) => l.taskId === options.taskId);\n\t\t}\n\t\tconst total = logs.length;\n\t\t// Return newest first\n\t\tlogs = logs.slice().reverse();\n\t\tif (options?.limit && options.limit > 0) {\n\t\t\tlogs = logs.slice(0, options.limit);\n\t\t}\n\t\treturn { logs, total };\n\t}\n\n\tclearLogs(taskId?: string): number {\n\t\tconst file = this.readLogFile();\n\t\tconst before = file.logs.length;\n\t\tif (taskId) {\n\t\t\tfile.logs = file.logs.filter((l) => l.taskId !== taskId);\n\t\t} else {\n\t\t\tfile.logs = [];\n\t\t}\n\t\tthis.writeLogFile(file);\n\t\treturn before - file.logs.length;\n\t}\n}\n","// MigrateToGlobal — scan known workspaces for legacy per-workspace\n// <workspace>/.serviceme/scheduled-tasks.json, migrate v1 → v2, append\n// to the global ~/.serviceme/scheduled-tasks.json, and DELETE the\n// original file. Failures (parse errors, schema issues) are recorded\n// in ~/.serviceme/migration-failures.json and the original is also\n// deleted — no half-migration state is left on disk.\n// See docs/architecture/skill-agent-v2-repo.md §14.3.a + §14.4 + §14.5.\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type {\n\tScheduledTask,\n\tScheduledTasksConfig,\n\tScheduledTasksConfigV1,\n\tTaskWorkspaceRef,\n} from \"@serviceme/devtools-protocol\";\nimport { isScheduledTasksConfigV1, migrateV1ToV2 } from \"@serviceme/devtools-protocol\";\nimport { getMigrationFailuresPath, getScheduledTasksConfigPath } from \"../../paths/userHome\";\n\nexport interface MigrateToGlobalOptions {\n\t/**\n\t * Workspace paths to scan for legacy v1 files. May include the current\n\t * workspace + any persisted in `~/.serviceme/known-workspaces.json`.\n\t */\n\tworkspacePaths: string[];\n\t/**\n\t * Workspace context to apply to each scanned file. The scanner calls the\n\t * probe for each candidate path to populate path/name/git metadata.\n\t * Tests can inject a stub that returns synthetic data.\n\t */\n\tprobe?: (workspacePath: string) => Promise<TaskWorkspaceRef> | TaskWorkspaceRef;\n\t/**\n\t * Override the global config path. Tests use this; production should\n\t * leave it unset to get the standard ~/.serviceme/scheduled-tasks.json.\n\t */\n\tglobalConfigPath?: string;\n\t/**\n\t * Override the migration-failures path. Same convention.\n\t */\n\tmigrationFailuresPath?: string;\n}\n\nexport interface MigrationResult {\n\t/** Number of v1 files successfully migrated into the global config. */\n\tmigrated: number;\n\t/** Number of v1 files that failed to parse / migrate. */\n\tfailed: number;\n\t/** Task names that had to be disambiguated because they appeared in multiple workspaces. */\n\tconflicts: string[];\n\t/** Soft issues surfaced by migrateV1ToV2 (e.g. unknown taskType). */\n\tissues: string[];\n}\n\ninterface FailureEntry {\n\tworkspacePath: string;\n\tv1Path: string;\n\terror: string;\n\tat: string; // ISO timestamp\n}\n\nconst WORKSPACE_DIR = \".serviceme\";\nconst V1_FILENAME = \"scheduled-tasks.json\";\n\n/**\n * Default workspace probe: returns a minimal TaskWorkspaceRef derived from\n * the path (name = basename). The extension injects the real WorkspaceProbe\n * at startup; tests inject a stub.\n */\nfunction defaultProbe(workspacePath: string): TaskWorkspaceRef {\n\treturn {\n\t\tpath: workspacePath,\n\t\tname: path.basename(workspacePath) || workspacePath,\n\t};\n}\n\nfunction readV1Config(\n\tv1Path: string\n): { ok: true; config: ScheduledTasksConfigV1 } | { ok: false; error: string } {\n\tlet raw: string;\n\ttry {\n\t\traw = fs.readFileSync(v1Path, \"utf-8\");\n\t} catch (err) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: `read failed: ${err instanceof Error ? err.message : String(err)}`,\n\t\t};\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (err) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: `JSON parse failed: ${err instanceof Error ? err.message : String(err)}`,\n\t\t};\n\t}\n\tif (!isScheduledTasksConfigV1(parsed)) {\n\t\treturn { ok: false, error: \"not a valid v1 scheduledTasksConfig\" };\n\t}\n\treturn { ok: true, config: parsed };\n}\n\nfunction safeDelete(filePath: string): void {\n\ttry {\n\t\tfs.unlinkSync(filePath);\n\t} catch {\n\t\t// Best-effort: file may have been already removed or never existed.\n\t}\n}\n\nfunction ensureDir(filePath: string): void {\n\tconst dir = path.dirname(filePath);\n\tif (!fs.existsSync(dir)) {\n\t\tfs.mkdirSync(dir, { recursive: true });\n\t}\n}\n\nfunction readJsonFile<T>(filePath: string): T | null {\n\tif (!fs.existsSync(filePath)) return null;\n\ttry {\n\t\treturn JSON.parse(fs.readFileSync(filePath, \"utf-8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction writeJsonFile(filePath: string, data: unknown): void {\n\tensureDir(filePath);\n\tfs.writeFileSync(filePath, JSON.stringify(data, null, \"\\t\"), \"utf-8\");\n}\n\n/**\n * Disambiguate task names that collide across workspaces by suffixing the\n * workspace name in brackets. Mutates a copy of `task` and returns it.\n */\nfunction disambiguateName(\n\ttask: ScheduledTask,\n\texistingNames: Set<string>,\n\tworkspaceName: string\n): ScheduledTask {\n\tif (!existingNames.has(task.name)) {\n\t\texistingNames.add(task.name);\n\t\treturn task;\n\t}\n\tconst base = `${task.name} [${workspaceName}]`;\n\tlet candidate = base;\n\tlet counter = 2;\n\twhile (existingNames.has(candidate)) {\n\t\tcandidate = `${base} (${counter})`;\n\t\tcounter += 1;\n\t}\n\texistingNames.add(candidate);\n\treturn { ...task, name: candidate };\n}\n\n/**\n * Run the migration. Idempotent: each v1 file is deleted after processing,\n * so re-running this on a partially-migrated state is safe (the\n * already-migrated files no longer exist; only the remainder is processed).\n */\nexport async function migrateToGlobal(options: MigrateToGlobalOptions): Promise<MigrationResult> {\n\tconst globalConfigPath = options.globalConfigPath ?? getScheduledTasksConfigPath();\n\tconst migrationFailuresPath = options.migrationFailuresPath ?? getMigrationFailuresPath();\n\tconst probe = options.probe ?? defaultProbe;\n\n\t// Load or seed the global config.\n\tconst existing = readJsonFile<ScheduledTasksConfig>(globalConfigPath);\n\tconst baseConfig: ScheduledTasksConfig =\n\t\texisting && existing.version === 2 ? existing : { version: 2, tasks: [] };\n\tconst existingNames = new Set(baseConfig.tasks.map((t) => t.name));\n\n\t// Read prior failures (so we accumulate, not overwrite).\n\tconst priorFailures = readJsonFile<FailureEntry[]>(migrationFailuresPath) ?? [];\n\tconst failures: FailureEntry[] = [...priorFailures];\n\n\tlet migrated = 0;\n\tconst conflicts: string[] = [];\n\tconst issues: string[] = [];\n\n\tfor (const workspacePath of options.workspacePaths) {\n\t\tconst v1Path = path.join(workspacePath, WORKSPACE_DIR, V1_FILENAME);\n\t\tif (!fs.existsSync(v1Path)) continue;\n\n\t\tconst v1 = readV1Config(v1Path);\n\t\tif (!v1.ok) {\n\t\t\tfailures.push({\n\t\t\t\tworkspacePath,\n\t\t\t\tv1Path,\n\t\t\t\terror: v1.error,\n\t\t\t\tat: new Date().toISOString(),\n\t\t\t});\n\t\t\t// DELETE the original — no half-migration state.\n\t\t\tsafeDelete(v1Path);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet workspaceRef: TaskWorkspaceRef;\n\t\ttry {\n\t\t\tworkspaceRef = await probe(workspacePath);\n\t\t} catch (err) {\n\t\t\tfailures.push({\n\t\t\t\tworkspacePath,\n\t\t\t\tv1Path,\n\t\t\t\terror: `probe failed: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\tat: new Date().toISOString(),\n\t\t\t});\n\t\t\tsafeDelete(v1Path);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst { config, issues: taskIssues } = migrateV1ToV2(v1.config, workspaceRef);\n\t\tissues.push(...taskIssues);\n\n\t\t// Disambiguate name conflicts.\n\t\tconst newTasks: ScheduledTask[] = config.tasks.map((t) => {\n\t\t\tif (existingNames.has(t.name)) {\n\t\t\t\tconflicts.push(t.name);\n\t\t\t\treturn disambiguateName(t, existingNames, workspaceRef.name);\n\t\t\t}\n\t\t\texistingNames.add(t.name);\n\t\t\treturn t;\n\t\t});\n\n\t\tbaseConfig.tasks.push(...newTasks);\n\t\tmigrated += 1;\n\n\t\t// DELETE the original — no half-migration state.\n\t\tsafeDelete(v1Path);\n\t}\n\n\t// Persist the new global config + failures.\n\tif (migrated > 0) {\n\t\t// Use TaskConfigManager's atomic write semantics via the public\n\t\t// listTasks + createTask path would lose ordering and re-validate\n\t\t// everything, which is wasteful. Write directly using the same\n\t\t// tmp → rename pattern the manager uses.\n\t\tensureDir(globalConfigPath);\n\t\tconst tmp = `${globalConfigPath}.tmp`;\n\t\tfs.writeFileSync(tmp, JSON.stringify(baseConfig, null, \"\\t\"), \"utf-8\");\n\t\tfs.renameSync(tmp, globalConfigPath);\n\t}\n\tif (failures.length > priorFailures.length) {\n\t\twriteJsonFile(migrationFailuresPath, failures);\n\t} else if (failures.length === 0 && priorFailures.length > 0) {\n\t\t// All prior failures cleared; remove the file.\n\t\tsafeDelete(migrationFailuresPath);\n\t}\n\n\treturn {\n\t\tmigrated,\n\t\tfailed: failures.length - priorFailures.length,\n\t\tconflicts,\n\t\tissues,\n\t};\n}\n","// WorkspaceProbe — auto-detects git metadata for a workspace path.\n// Used by extension startup and task creation to populate TaskWorkspaceRef.\n// See docs/architecture/skill-agent-v2-repo.md §14.3.c (workspace auto-probe).\n\nimport { spawn } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { TaskWorkspaceRef } from \"@serviceme/devtools-protocol\";\n\n/** Result of probing a single workspace. */\nexport interface ProbeResult {\n\t/** Always populated; empty fields are left undefined. */\n\tworkspace: TaskWorkspaceRef;\n\t/** null on success; one of the {@link ProbeError} tags on failure. */\n\terror: ProbeError | null;\n}\n\nexport type ProbeError = \"path-not-found\" | \"not-a-git-repo\" | \"git-timeout\" | \"git-error\";\n\nexport interface ProbeOptions {\n\t/** Timeout for each git invocation in ms. Default: 2000. */\n\ttimeoutMs?: number;\n\t/** Override the git binary (default: 'git'). Tests inject a stub. */\n\tgitBinary?: string;\n\t/**\n\t * Override the underlying runner. Receives the args + cwd and returns\n\t * { stdout, stderr, code } on success, or throws on timeout. The default\n\t * implementation spawns `gitBinary` with the given args.\n\t */\n\trunGit?: (\n\t\targs: string[],\n\t\tcwd: string\n\t) => Promise<{\n\t\tstdout: string;\n\t\tstderr: string;\n\t\tcode: number;\n\t}>;\n}\n\nconst DEFAULT_TIMEOUT_MS = 2000;\n\nclass GitTimeoutError extends Error {\n\tconstructor() {\n\t\tsuper(\"git-timeout\");\n\t\tthis.name = \"GitTimeoutError\";\n\t}\n}\n\nfunction isTimeout(err: unknown): boolean {\n\tif (err instanceof GitTimeoutError) return true;\n\tif (err instanceof Error) {\n\t\treturn err.name === \"GitTimeoutError\" || err.message === \"git-timeout\";\n\t}\n\treturn false;\n}\n\nfunction defaultRunGit(\n\tgitBinary: string,\n\targs: string[],\n\tcwd: string,\n\ttimeoutMs: number\n): Promise<{ stdout: string; stderr: string; code: number }> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst child = spawn(gitBinary, args, {\n\t\t\tcwd,\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t});\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\n\t\tconst timer = setTimeout(() => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tchild.kill(\"SIGTERM\");\n\t\t\treject(new GitTimeoutError());\n\t\t}, timeoutMs);\n\n\t\tchild.stdout.on(\"data\", (chunk) => {\n\t\t\tstdout += chunk.toString(\"utf-8\");\n\t\t});\n\t\tchild.stderr.on(\"data\", (chunk) => {\n\t\t\tstderr += chunk.toString(\"utf-8\");\n\t\t});\n\t\tchild.on(\"error\", (err) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\treject(err);\n\t\t});\n\t\tchild.on(\"close\", (code) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\tresolve({ stdout, stderr, code: code ?? 0 });\n\t\t});\n\t});\n}\n\n/**\n * Probes a workspace for git metadata. Returns a {@link ProbeResult} with\n * `error: null` on success and an error tag on failure — the workspace\n * descriptor is always populated so callers can still surface a partial\n * record.\n */\nexport class WorkspaceProbe {\n\tprivate readonly timeoutMs: number;\n\tprivate readonly gitBinary: string;\n\tprivate readonly runGitFn: (\n\t\targs: string[],\n\t\tcwd: string\n\t) => Promise<{ stdout: string; stderr: string; code: number }>;\n\n\tconstructor(options: ProbeOptions = {}) {\n\t\tthis.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\t\tthis.gitBinary = options.gitBinary ?? \"git\";\n\t\tif (options.runGit) {\n\t\t\tthis.runGitFn = options.runGit;\n\t\t} else {\n\t\t\tconst binary = this.gitBinary;\n\t\t\tconst timeoutMs = this.timeoutMs;\n\t\t\tthis.runGitFn = (args, cwd) => defaultRunGit(binary, args, cwd, timeoutMs);\n\t\t}\n\t}\n\n\tasync probe(workspacePath: string): Promise<ProbeResult> {\n\t\tconst name = path.basename(workspacePath) || workspacePath;\n\n\t\tif (!workspacePath || !fs.existsSync(workspacePath)) {\n\t\t\treturn {\n\t\t\t\tworkspace: { path: workspacePath, name },\n\t\t\t\terror: \"path-not-found\",\n\t\t\t};\n\t\t}\n\n\t\tlet gitRemote: string | undefined;\n\t\ttry {\n\t\t\tconst remote = await this.runGitFn([\"remote\", \"get-url\", \"origin\"], workspacePath);\n\t\t\tif (remote.code === 0 && remote.stdout.trim()) {\n\t\t\t\tgitRemote = remote.stdout.trim();\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (isTimeout(err)) {\n\t\t\t\treturn {\n\t\t\t\t\tworkspace: { path: workspacePath, name, gitRemote },\n\t\t\t\t\terror: \"git-timeout\",\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tworkspace: { path: workspacePath, name },\n\t\t\t\terror: \"not-a-git-repo\",\n\t\t\t};\n\t\t}\n\n\t\tlet gitBranch: string | undefined;\n\t\ttry {\n\t\t\tconst branch = await this.runGitFn([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], workspacePath);\n\t\t\tif (branch.code === 0 && branch.stdout.trim()) {\n\t\t\t\tgitBranch = branch.stdout.trim();\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (isTimeout(err)) {\n\t\t\t\treturn {\n\t\t\t\t\tworkspace: { path: workspacePath, name, gitRemote, gitBranch },\n\t\t\t\t\terror: \"git-timeout\",\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tworkspace: { path: workspacePath, name, gitRemote },\n\t\t\t\terror: \"not-a-git-repo\",\n\t\t\t};\n\t\t}\n\n\t\t// If neither command produced output, treat as not-a-git-repo.\n\t\tif (!gitRemote && !gitBranch) {\n\t\t\treturn {\n\t\t\t\tworkspace: { path: workspacePath, name },\n\t\t\t\terror: \"not-a-git-repo\",\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tworkspace: {\n\t\t\t\tpath: workspacePath,\n\t\t\t\tname,\n\t\t\t\tgitRemote,\n\t\t\t\tgitBranch,\n\t\t\t\tlastSeenAt: new Date().toISOString(),\n\t\t\t},\n\t\t\terror: null,\n\t\t};\n\t}\n}\n","import type { SkillMarketplaceEntry } from \"@serviceme/devtools-protocol\";\nimport { createServicemeError } from \"@serviceme/devtools-protocol\";\nimport type { SkillDownloadFile } from \"./types\";\n\nexport interface SkillCatalog {\n\tskills: SkillMarketplaceEntry[];\n\tfetchedAt: string;\n}\n\nexport interface SkillCatalogClientOptions {\n\tfetchImpl?: typeof fetch;\n\tbaseUrl?: string;\n}\n\nexport class SkillCatalogClient {\n\tprivate readonly fetchImpl: typeof fetch;\n\tprivate readonly baseUrl?: string;\n\n\tconstructor(options: SkillCatalogClientOptions = {}) {\n\t\tthis.fetchImpl = options.fetchImpl ?? fetch;\n\t\tthis.baseUrl = options.baseUrl;\n\t}\n\n\tasync getCatalog(): Promise<SkillCatalog> {\n\t\tif (!this.baseUrl) {\n\t\t\tthrow createServicemeError(\"workspace_not_found\", \"Skill catalog baseUrl is not configured.\");\n\t\t}\n\n\t\tconst response = await this.fetchImpl(`${this.baseUrl}/api/v1/marketplace/skills`);\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Failed to fetch skills catalog: ${response.status}`);\n\t\t}\n\n\t\tconst data = (await response.json()) as {\n\t\t\tskills?: SkillMarketplaceEntry[];\n\t\t};\n\t\treturn {\n\t\t\tskills: data.skills ?? [],\n\t\t\tfetchedAt: new Date().toISOString(),\n\t\t};\n\t}\n\n\tasync downloadSkill(remoteId: string): Promise<SkillDownloadFile[]> {\n\t\tif (!this.baseUrl) {\n\t\t\tthrow createServicemeError(\"workspace_not_found\", \"Skill catalog baseUrl is not configured.\");\n\t\t}\n\n\t\tconst response = await this.fetchImpl(\n\t\t\t`${this.baseUrl}/api/v1/marketplace/skills/download/${remoteId}`\n\t\t);\n\t\tif (!response.ok) {\n\t\t\tif (response.status === 404) {\n\t\t\t\tthrow createServicemeError(\"not_found\", `Skill '${remoteId}' not found`);\n\t\t\t}\n\t\t\tthrow new Error(`Failed to download skill ${remoteId}: ${response.status}`);\n\t\t}\n\n\t\tconst payload = (await response.json()) as {\n\t\t\tdata?: { files?: SkillDownloadFile[] };\n\t\t};\n\t\treturn payload.data?.files ?? [];\n\t}\n}\n","import type { SkillMutationRequest } from \"@serviceme/devtools-protocol\";\n\ninterface CatalogSkillLike {\n\tid: string;\n\thasScripts?: boolean;\n\thasHooks?: boolean;\n}\n\ninterface CatalogLike {\n\tskills: CatalogSkillLike[];\n\tfetchedAt: string;\n}\n\ninterface SkillStoreLike {\n\tnormalizeRemoteSkillId(remoteId: string): string;\n\tlistWorkspaceSkillIds(): Promise<string[]>;\n\tlistUserSkillIds(): Promise<string[]>;\n}\n\ninterface SkillCatalogClientLike {\n\tgetCatalog(): Promise<CatalogLike>;\n}\n\nexport interface SkillReconcilerDependencies {\n\tskillStore: SkillStoreLike;\n\tcatalogClient: SkillCatalogClientLike;\n}\n\nexport interface SkillMutateResult {\n\tstatus: \"success\" | \"blocked\" | \"requires_confirmation\";\n\tchanged: boolean;\n\tmessage?: string;\n\thasScripts?: boolean;\n\thasHooks?: boolean;\n}\n\nexport class SkillReconciler {\n\tconstructor(private readonly deps: SkillReconcilerDependencies) {}\n\n\tasync mutate(request: SkillMutationRequest): Promise<SkillMutateResult> {\n\t\tif (request.targetScope !== \"workspace\" && request.targetScope !== \"user\") {\n\t\t\tthrow new Error(`Invalid target scope: ${String(request.targetScope)}`);\n\t\t}\n\n\t\tif (\n\t\t\trequest.action === \"uninstall\" ||\n\t\t\trequest.action === \"move\" ||\n\t\t\trequest.action === \"removeExternal\"\n\t\t) {\n\t\t\treturn {\n\t\t\t\tstatus: \"success\",\n\t\t\t\tchanged: true,\n\t\t\t\tmessage: `Skill ${request.action} completed.`,\n\t\t\t};\n\t\t}\n\n\t\tif (request.action !== \"install\") {\n\t\t\treturn {\n\t\t\t\tstatus: \"blocked\",\n\t\t\t\tchanged: false,\n\t\t\t\tmessage: `Skill action is not supported by bridge reconciler: ${request.action}`,\n\t\t\t};\n\t\t}\n\n\t\tconst catalog = await this.deps.catalogClient.getCatalog();\n\t\tconst remoteSkill = catalog.skills.find(\n\t\t\t(skill) => this.deps.skillStore.normalizeRemoteSkillId(skill.id) === request.skillId\n\t\t);\n\n\t\tif (!remoteSkill) {\n\t\t\treturn {\n\t\t\t\tstatus: \"blocked\",\n\t\t\t\tchanged: false,\n\t\t\t\tmessage: \"Skill not found in catalog.\",\n\t\t\t};\n\t\t}\n\n\t\tif (!request.confirmed && (remoteSkill.hasScripts || remoteSkill.hasHooks)) {\n\t\t\treturn {\n\t\t\t\tstatus: \"requires_confirmation\",\n\t\t\t\tchanged: false,\n\t\t\t\thasScripts: Boolean(remoteSkill.hasScripts),\n\t\t\t\thasHooks: Boolean(remoteSkill.hasHooks),\n\t\t\t\tmessage: \"This skill contains executable scripts that require confirmation.\",\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tchanged: true,\n\t\t\tmessage: \"Skill installed.\",\n\t\t};\n\t}\n}\n","import type { Dirent } from \"node:fs\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type { SkillDownloadFile, SkillStoreFileSystem, SkillStoreOptions } from \"./types\";\n\nconst USER_SKILL_MARKER_FILE = \".serviceme-skill.json\";\nconst LEGACY_USER_SKILL_MARKER_FILE = \".ms-devtools-skill.json\";\nconst WORKSPACE_SKILLS_ROOT_RELATIVE = \".github/skills\";\nconst LEGACY_USER_SKILLS_ROOT_RELATIVE = \".agents/skills\";\nconst WORKSPACE_SKILLS_MARKER_RELATIVE = \".github/.serviceme-skills.yml\";\nconst SAFE_LOCAL_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;\n\nfunction assertSafeLocalSkillId(skillId: string): string {\n\tif (\n\t\ttypeof skillId !== \"string\" ||\n\t\tskillId.length === 0 ||\n\t\tskillId === \".\" ||\n\t\tskillId === \"..\" ||\n\t\tskillId.includes(\"/\") ||\n\t\tskillId.includes(\"\\\\\") ||\n\t\t!SAFE_LOCAL_ID_PATTERN.test(skillId)\n\t) {\n\t\tthrow new Error(`Invalid skill id: ${skillId}`);\n\t}\n\n\treturn skillId;\n}\n\nexport class SkillStore {\n\tprivate readonly workspacePath: string;\n\tprivate readonly userSkillsRoot: string;\n\tprivate readonly fileSystem: SkillStoreFileSystem;\n\n\tconstructor(options: SkillStoreOptions) {\n\t\tthis.workspacePath = options.workspacePath;\n\t\tthis.userSkillsRoot = options.userSkillsRoot;\n\t\tthis.fileSystem = options.fileSystem ?? fs;\n\t}\n\n\tnormalizeRemoteSkillId(remoteId: string): string {\n\t\tif (remoteId.startsWith(\"official/\")) {\n\t\t\treturn assertSafeLocalSkillId(remoteId.slice(\"official/\".length));\n\t\t}\n\t\tif (remoteId.startsWith(\"community/\")) {\n\t\t\tconst lastSlash = remoteId.lastIndexOf(\"/\");\n\t\t\treturn assertSafeLocalSkillId(remoteId.slice(lastSlash + 1));\n\t\t}\n\t\treturn assertSafeLocalSkillId(remoteId);\n\t}\n\n\tgetWorkspaceSkillPath(skillId: string): string {\n\t\treturn `${WORKSPACE_SKILLS_ROOT_RELATIVE}/${skillId}`;\n\t}\n\n\tgetWorkspaceMarkerPath(): string {\n\t\treturn WORKSPACE_SKILLS_MARKER_RELATIVE;\n\t}\n\n\tgetUserSkillPath(skillId: string): string {\n\t\treturn path.join(this.userSkillsRoot, skillId);\n\t}\n\n\tasync listWorkspaceSkillIds(): Promise<string[]> {\n\t\tconst skillsRootPath = path.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE);\n\t\ttry {\n\t\t\tconst entries = await this.fileSystem.readdir(skillsRootPath, {\n\t\t\t\twithFileTypes: true,\n\t\t\t});\n\t\t\treturn entries\n\t\t\t\t.filter((entry) => entry.isDirectory() && !entry.name.startsWith(\".\"))\n\t\t\t\t.map((entry) => entry.name)\n\t\t\t\t.sort();\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tasync listUserSkillIds(): Promise<string[]> {\n\t\ttry {\n\t\t\tconst entries = await this.fileSystem.readdir(this.userSkillsRoot, {\n\t\t\t\twithFileTypes: true,\n\t\t\t});\n\t\t\treturn entries\n\t\t\t\t.filter((entry) => entry.isDirectory() && !entry.name.startsWith(\".\"))\n\t\t\t\t.map((entry) => entry.name)\n\t\t\t\t.sort();\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tasync writeManagedUserSkillMarker(skillId: string): Promise<void> {\n\t\tconst targetDir = this.getUserSkillPath(skillId);\n\t\tawait this.fileSystem.mkdir(targetDir, { recursive: true });\n\t\tawait this.fileSystem.writeFile(\n\t\t\tpath.join(targetDir, USER_SKILL_MARKER_FILE),\n\t\t\tJSON.stringify({ skillId, installedBy: \"serviceme\" }, null, 2),\n\t\t\t\"utf-8\"\n\t\t);\n\t}\n\n\tasync isManagedUserSkill(skillId: string): Promise<boolean> {\n\t\tawait this.migrateLegacyUserSkillMarker(skillId);\n\t\ttry {\n\t\t\tconst marker = await this.fileSystem.readFile(\n\t\t\t\tpath.join(this.getUserSkillPath(skillId), USER_SKILL_MARKER_FILE),\n\t\t\t\t\"utf-8\"\n\t\t\t);\n\t\t\tconst parsed = JSON.parse(marker) as { skillId?: string };\n\t\t\treturn parsed.skillId === skillId;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * One-time migration: the user-scope skill marker used to be named\n\t * `.ms-devtools-skill.json`. If the new `.serviceme-skill.json` doesn't\n\t * exist yet but the legacy marker does, copy it forward so an existing\n\t * skill doesn't lose its \"managed\" status.\n\t */\n\tprivate async migrateLegacyUserSkillMarker(skillId: string): Promise<void> {\n\t\tconst targetDir = this.getUserSkillPath(skillId);\n\t\tconst newPath = path.join(targetDir, USER_SKILL_MARKER_FILE);\n\t\tconst legacyPath = path.join(targetDir, LEGACY_USER_SKILL_MARKER_FILE);\n\t\ttry {\n\t\t\tawait this.fileSystem.readFile(newPath, \"utf-8\");\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// new marker missing — check the legacy marker below\n\t\t}\n\t\ttry {\n\t\t\tconst legacyContent = await this.fileSystem.readFile(legacyPath, \"utf-8\");\n\t\t\tawait this.fileSystem.writeFile(newPath, legacyContent, \"utf-8\");\n\t\t} catch {\n\t\t\t// legacy marker doesn't exist either — nothing to migrate\n\t\t}\n\t}\n\n\tasync writeSkillFiles(\n\t\tskillId: string,\n\t\tscope: \"workspace\" | \"user\",\n\t\tfiles: SkillDownloadFile[]\n\t): Promise<void> {\n\t\tconst root =\n\t\t\tscope === \"workspace\"\n\t\t\t\t? path.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE)\n\t\t\t\t: this.userSkillsRoot;\n\t\tconst targetDir = path.join(root, skillId);\n\t\tawait this.fileSystem.mkdir(targetDir, { recursive: true });\n\n\t\tfor (const file of files) {\n\t\t\tconst filePath = path.join(targetDir, file.path);\n\t\t\tawait this.fileSystem.mkdir(path.dirname(filePath), { recursive: true });\n\t\t\tawait this.fileSystem.writeFile(filePath, file.content, \"utf-8\");\n\t\t\tif (file.executable) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.fileSystem.chmod(filePath, 0o755);\n\t\t\t\t} catch {\n\t\t\t\t\t// ignore chmod failures on unsupported environments\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** One legacy skill directory reported by {@link migrateLegacyUserSkillContent}. */\nexport interface LegacyUserSkillEntry {\n\t/** Legacy skill id (directory name under ~/.agents/skills). */\n\tid: string;\n\t/** Absolute path of the legacy skill directory. */\n\tlegacyPath: string;\n\t/** Target directory under ~/.copilot/skills the migration would create. */\n\ttargetPath: string;\n\t/** Always `migration_available` until the user runs the migration. */\n\tstatus: \"migration_available\";\n}\n\nexport interface LegacyUserSkillMigrationResult {\n\tentries: LegacyUserSkillEntry[];\n}\n\n/**\n * Detect ~/.agents/skills content that can migrate to ~/.copilot/skills.\n *\n * Read-only: legacy directories stay in place until the user explicitly\n * invokes the migration, so detection never breaks tools that still\n * read the old layout.\n */\nexport async function migrateLegacyUserSkillContent(input: {\n\thomeDir: string;\n\tworkspaceDir: string;\n\tfileSystem?: SkillStoreFileSystem;\n}): Promise<LegacyUserSkillMigrationResult> {\n\tconst fileSystem = input.fileSystem ?? fs;\n\tconst legacyRoot = path.join(input.homeDir, ...LEGACY_USER_SKILLS_ROOT_RELATIVE.split(\"/\"));\n\tconst targetRoot = path.join(input.homeDir, \".copilot\", \"skills\");\n\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await fileSystem.readdir(legacyRoot, { withFileTypes: true });\n\t} catch {\n\t\treturn { entries: [] };\n\t}\n\n\tconst result: LegacyUserSkillEntry[] = [];\n\tfor (const entry of entries) {\n\t\tif (!entry.isDirectory() || entry.name.startsWith(\".\")) continue;\n\t\tresult.push({\n\t\t\tid: entry.name,\n\t\t\tlegacyPath: path.join(legacyRoot, entry.name),\n\t\t\ttargetPath: path.join(targetRoot, entry.name),\n\t\t\tstatus: \"migration_available\",\n\t\t});\n\t}\n\treturn { entries: result };\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type { GitClient } from \"../git-client\";\nimport { getRepoDir } from \"../paths/userHome\";\nimport type { SkillFile } from \"../skill-store/types\";\nimport type { SubmitValidationRequest, SubmitValidationResponse } from \"./types\";\nimport { SubmitError } from \"./types\";\n\n// Re-export so consumers (bridge handlers, CLI commands) can detect\n// SubmitError via `instanceof` without reaching into the internal\n// `./types` module.\nexport { SubmitError } from \"./types\";\n\n/**\n * Skill & Agent v2 — SubmitClient (M4)\n *\n * SubmitClient orchestrates the \"validate then push\" flow described\n * in docs/architecture/skill-agent-v2-repo.md §5.7:\n *\n * 1. **validate** — POST the candidate files to the server's\n * `/api/v1/skills/validate` endpoint (5 deny reasons: repo_not_\n * writable, file_too_large, path_traversal, invalid_frontmatter,\n * name_conflict).\n * 2. **write** — materialize the files into the local repo clone at\n * `~/.serviceme/repos/<repoId>/skills/<name>/` (or `agents/`).\n * 3. **commit** — `git add . && git commit -m \"feat(skills): add <name>\"`.\n * 4. **push** — `git push origin <branch>` via the server proxy.\n *\n * The client is intentionally thin: it does NOT do its own validation\n * (the server is the gate), and it does NOT cache anything across\n * calls. The single source of truth for \"is this submission allowed?\"\n * is the server's validate endpoint.\n *\n * @see docs/architecture/skill-agent-v2-repo.md §5.7 SubmitClient\n */\n\nexport interface SubmitOptions {\n\t/** Override the server's validate URL. Defaults to `http://localhost:3000/api/v1`. */\n\tserverBaseUrl?: string;\n\t/** Override the branch to push. Defaults to the repo's `branch` field. */\n\tbranch?: string;\n\t/** Skip the actual push (for tests + dry-runs). When true, step 4 returns a synthetic PushResult. */\n\tskipPush?: boolean;\n}\n\nexport interface SubmitResult {\n\trepoId: string;\n\tskillName: string;\n\tcommitSha: string;\n\tpushedRef?: string;\n\tpushedSha?: string;\n}\n\nexport interface SubmitClientOptions {\n\tgitClient: GitClient;\n\t/** Lookup the default branch for a repo (config-driven). */\n\tgetRepoBranch?: (repoId: string) => string | undefined;\n\t/** HTTP fetch impl (defaults to the global `fetch`). */\n\tfetcher?: typeof fetch;\n\t/** Default server base URL when no override is supplied. */\n\tdefaultServerBaseUrl?: string;\n}\n\nexport class SubmitClient {\n\tprivate readonly git: GitClient;\n\tprivate readonly getRepoBranch: (repoId: string) => string | undefined;\n\tprivate readonly fetcher: typeof fetch;\n\tprivate readonly defaultServerBaseUrl: string;\n\n\tconstructor(opts: SubmitClientOptions) {\n\t\tthis.git = opts.gitClient;\n\t\tthis.getRepoBranch = opts.getRepoBranch ?? (() => undefined);\n\t\tthis.fetcher = opts.fetcher ?? (globalThis.fetch as typeof fetch);\n\t\tthis.defaultServerBaseUrl = opts.defaultServerBaseUrl ?? \"http://localhost:3000\";\n\t}\n\n\t/**\n\t * Validate-only path. Useful for the UI's \"Save Draft\" flow which\n\t * wants to surface validation errors without committing or pushing.\n\t */\n\tasync validate(req: SubmitValidationRequest): Promise<SubmitValidationResponse> {\n\t\tconst url = `${this.defaultServerBaseUrl}/api/v1/skills/validate`;\n\t\tconst res = await this.fetcher(url, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\tbody: JSON.stringify(req),\n\t\t});\n\t\tif (!res.ok) {\n\t\t\tthrow new SubmitError(\"network_error\", `validate request failed: HTTP ${res.status}`);\n\t\t}\n\t\treturn (await res.json()) as SubmitValidationResponse;\n\t}\n\n\t/**\n\t * Full submit pipeline. Throws `SubmitError` on:\n\t * - validate deny (reason echoed)\n\t * - network failure (network_error)\n\t * - local write failure\n\t * - commit/push failure\n\t */\n\tasync submit(\n\t\trepoId: string,\n\t\tskillName: string,\n\t\tfiles: SkillFile[],\n\t\topts: SubmitOptions = {}\n\t): Promise<SubmitResult> {\n\t\t// (1) Validate\n\t\tconst v = await this.validate({ repoId, skillName, files });\n\t\tif (!v.allow) {\n\t\t\tthrow new SubmitError(v.reason ?? \"unknown\", v.detail ?? \"validation denied\");\n\t\t}\n\n\t\t// (2) Write files into the local repo clone\n\t\tconst localRepoPath = getRepoDir(repoId);\n\t\tconst targetDir = path.join(localRepoPath, \"skills\", skillName);\n\t\tawait fs.mkdir(targetDir, { recursive: true });\n\t\tfor (const f of files) {\n\t\t\tconst full = path.join(targetDir, f.path);\n\t\t\tawait fs.mkdir(path.dirname(full), { recursive: true });\n\t\t\tconst tmp = `${full}.${process.pid}.${Date.now()}.tmp`;\n\t\t\tawait fs.writeFile(tmp, f.content, \"utf8\");\n\t\t\tawait fs.rename(tmp, full);\n\t\t}\n\n\t\t// (3) Commit (convention: `feat(skills): add <name>`)\n\t\tconst commitMessage = `feat(skills): add ${skillName}`;\n\t\tconst { commitSha } = await this.git.commit(localRepoPath, commitMessage);\n\n\t\t// (4) Push\n\t\tlet pushedRef: string | undefined;\n\t\tlet pushedSha: string | undefined;\n\t\tif (!opts.skipPush) {\n\t\t\tconst branch = opts.branch ?? this.getRepoBranch(repoId) ?? \"main\";\n\t\t\tconst pushResult = await this.git.push(repoId, localRepoPath, branch);\n\t\t\tpushedRef = pushResult.ref;\n\t\t\tpushedSha = pushResult.commitSha;\n\t\t}\n\n\t\treturn {\n\t\t\trepoId,\n\t\t\tskillName,\n\t\t\tcommitSha,\n\t\t\tpushedRef,\n\t\t\tpushedSha,\n\t\t};\n\t}\n}\n","/**\n * Skill & Agent v2 — SubmitClient Types (M4)\n *\n * Mirrors the server's POST /api/v1/skills/validate contract (M2\n * SubmitApi). Defined here as a separate types file so the test\n * fixtures + the client can both import without circular deps.\n */\n\n/** Reasons the server may deny a submission. */\nexport type DenyReason =\n\t| \"repo_not_writable\"\n\t| \"file_too_large\"\n\t| \"path_traversal\"\n\t| \"invalid_frontmatter\"\n\t| \"name_conflict\"\n\t/** Local-client-side errors that don't come from the server. */\n\t| \"network_error\"\n\t| \"write_error\"\n\t| \"commit_error\"\n\t| \"push_error\"\n\t| \"unknown\";\n\n/** Request payload. Identical to the server's SubmitValidationRequest. */\nexport interface SubmitValidationRequest {\n\trepoId: string;\n\tskillName: string;\n\tfiles: Array<{ path: string; content: string }>;\n}\n\n/** Response payload. Identical to the server's SubmitValidationResponse. */\nexport interface SubmitValidationResponse {\n\tallow: boolean;\n\treason?: DenyReason;\n\tdetail?: string;\n}\n\n/** Sentinel error thrown by SubmitClient.submit() / validate(). */\nexport class SubmitError extends Error {\n\treadonly reason: DenyReason;\n\treadonly detail: string;\n\treadonly status?: number;\n\n\tconstructor(reason: DenyReason, detail: string, options?: { status?: number }) {\n\t\tsuper(`submit failed (${reason}): ${detail}`);\n\t\tthis.name = \"SubmitError\";\n\t\tthis.reason = reason;\n\t\tthis.detail = detail;\n\t\tthis.status = options?.status;\n\t}\n}\n","/**\n * toolbox sort + dedup — pure helpers, no I/O.\n *\n * The Extension's `ToolBoxService` keeps a stable `order` index and\n * surfaces \"recently used\" via the `lastUsedAt` ISO timestamp. These\n * helpers codify that algorithm so the CLI and Extension agree on the\n * \"最近使用置顶\" UX.\n *\n * Refs:\n * - 4.功能规划.md §2.3 — `sort.ts 排序与去重纯函数`\n */\n\nimport type { ExternalTool } from \"@serviceme/devtools-protocol\";\n\n/**\n * Parsed timestamp from `lastUsedAt`. Returns 0 when the field is\n * missing or unparseable (so the entry falls below entries with a\n * real timestamp).\n */\nfunction lastUsedTimestamp(tool: ExternalTool): number {\n\tif (!tool.lastUsedAt) return 0;\n\tconst ms = Date.parse(tool.lastUsedAt);\n\treturn Number.isFinite(ms) ? ms : 0;\n}\n\n/**\n * Sort by \"recently used\" — entries with newer `lastUsedAt` float to\n * the top, entries with no `lastUsedAt` sort to the bottom (preserving\n * their relative `order` index when both are absent).\n *\n * Ties (same `lastUsedAt` or both missing) are broken by `order`, then\n * by `id` for determinism.\n */\nexport function sortByRecentFirst(tools: readonly ExternalTool[]): ExternalTool[] {\n\treturn [...tools].sort((a, b) => {\n\t\tconst tsA = lastUsedTimestamp(a);\n\t\tconst tsB = lastUsedTimestamp(b);\n\t\tif (tsA !== tsB) return tsB - tsA;\n\t\tconst orderA = a.order ?? Number.MAX_SAFE_INTEGER;\n\t\tconst orderB = b.order ?? Number.MAX_SAFE_INTEGER;\n\t\tif (orderA !== orderB) return orderA - orderB;\n\t\treturn a.id.localeCompare(b.id);\n\t});\n}\n\n/**\n * Sort by user-defined `order` field. Entries without `order` are\n * appended in their input order (stable sort via `id` tiebreaker).\n */\nexport function sortByUserOrder(tools: readonly ExternalTool[]): ExternalTool[] {\n\treturn [...tools].sort((a, b) => {\n\t\tconst orderA = a.order ?? Number.MAX_SAFE_INTEGER;\n\t\tconst orderB = b.order ?? Number.MAX_SAFE_INTEGER;\n\t\tif (orderA !== orderB) return orderA - orderB;\n\t\treturn a.id.localeCompare(b.id);\n\t});\n}\n\n/**\n * Deduplicate by `id`. The first occurrence wins; later ones are\n * discarded. Used when merging built-in + user seeds so duplicates\n * from the on-disk file don't shadow the built-in defaults.\n */\nexport function dedupeById(tools: readonly ExternalTool[]): ExternalTool[] {\n\tconst seen = new Set<string>();\n\tconst out: ExternalTool[] = [];\n\tfor (const tool of tools) {\n\t\tif (seen.has(tool.id)) continue;\n\t\tseen.add(tool.id);\n\t\tout.push(tool);\n\t}\n\treturn out;\n}\n\n/**\n * Merge built-in defaults with user-stored tools. Defaults keep\n * `isDefault: true` and their `order`; user tools are appended with\n * their stored metadata. Output is sorted by user order.\n */\nexport function mergeWithDefaults(\n\tdefaults: readonly ExternalTool[],\n\tuserTools: readonly ExternalTool[]\n): ExternalTool[] {\n\tconst defaultIds = new Set(defaults.map((d) => d.id));\n\tconst uniqueUserTools = userTools.filter((t) => !defaultIds.has(t.id));\n\treturn sortByUserOrder([...defaults, ...uniqueUserTools]);\n}\n\n/**\n * Re-number `order` for a list of tool ids. Used by\n * `toolbox.update` when the user drags-and-drops entries in the UI.\n */\nexport function reindexOrder(\n\ttools: ExternalTool[],\n\tnewOrderIds: readonly string[]\n): ExternalTool[] {\n\tconst orderMap = new Map<string, number>();\n\tfor (const [index, id] of newOrderIds.entries()) {\n\t\torderMap.set(id, index);\n\t}\n\tfor (const tool of tools) {\n\t\tconst next = orderMap.get(tool.id);\n\t\tif (next !== undefined) tool.order = next;\n\t}\n\treturn tools;\n}\n\n/**\n * Stamp `lastUsedAt` on the targeted tool (clones the array). Returns\n * a new array; the input is left untouched.\n */\nexport function touchLastUsedAt(\n\ttools: readonly ExternalTool[],\n\tid: string,\n\twhen: Date = new Date()\n): ExternalTool[] {\n\treturn tools.map((tool) => (tool.id === id ? { ...tool, lastUsedAt: when.toISOString() } : tool));\n}\n","/**\n * ToolboxStore — JSON persistence for the toolbox entries.\n *\n * Two scopes:\n * - `user` → `~/.serviceme/toolbox.json`\n * - `workspace` → `<cwd>/.github/.serviceme-toolbox.json`\n *\n * Both files share the `PersistedToolbox` shape and the same atomic\n * write pattern (tmp + rename + fsync) as `IdentityStore`. Concurrent\n * writers are serialized via a mkdir-based file lock (Phase 6+ may\n * upgrade to `proper-lockfile`).\n *\n * Refs:\n * - 4.功能规划.md §2.3 — `ToolboxStore.ts JSON 持久化(user + workspace scope)`\n * - `3.功能拆分.md` §3 — toolbox wire shape\n */\n\nimport * as fsp from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\n\nimport type { ExternalTool, ToolboxScope } from \"@serviceme/devtools-protocol\";\n\nimport { getToolboxJsonPath } from \"../paths/userHome\";\n\nimport { mergeWithDefaults, sortByUserOrder } from \"./sort\";\nimport {\n\tBUILTIN_DEFAULT_TOOLS,\n\ttype DefaultToolSeed,\n\ttype PersistedToolbox,\n\ttype ResolvedToolbox,\n\tTOOLBOX_JSON_SCHEMA_VERSION,\n} from \"./types\";\n\nconst FILE_MODE = 0o600;\nconst LOCK_DIR_MODE = 0o700;\nconst DEFAULT_LOCK_TIMEOUT_MS = 5000;\nconst DEFAULT_LOCK_RETRY_MS = 25;\n// `mkdir` (lock acquisition) and writing the pid file are two separate\n// syscalls, so there's a brief window where the lock dir exists but the\n// pid file doesn't yet. A grace period keeps a concurrent acquirer from\n// mistaking that window for an abandoned lock (see `isStaleLock`).\nconst LOCK_STALE_GRACE_MS = 200;\nconst TMP_SUFFIX = \".tmp\";\nexport const WORKSPACE_TOOLBOX_RELATIVE_PATH = path.join(\".github\", \".serviceme-toolbox.json\");\nconst LEGACY_WORKSPACE_TOOLBOX_FILENAME = \".ms-devtools-toolbox.json\";\n\n/**\n * One-time migration: the workspace-scope toolbox file used to be named\n * `.ms-devtools-toolbox.json`. If the new `.serviceme-toolbox.json` doesn't\n * exist yet but the legacy file does (in the same directory), rename it\n * forward so existing toolbox entries aren't silently lost.\n */\nasync function migrateLegacyWorkspaceToolboxFile(filePath: string | null): Promise<void> {\n\tif (!filePath) return;\n\tconst legacyPath = path.join(path.dirname(filePath), LEGACY_WORKSPACE_TOOLBOX_FILENAME);\n\tif (legacyPath === filePath) return;\n\ttry {\n\t\tawait fsp.access(filePath);\n\t\treturn;\n\t} catch {\n\t\t// new file missing — check the legacy path below\n\t}\n\ttry {\n\t\tawait fsp.rename(legacyPath, filePath);\n\t} catch {\n\t\t// legacy file doesn't exist either — nothing to migrate\n\t}\n}\n\nexport interface ToolboxFileBackend {\n\tread(filePath: string): Promise<PersistedToolbox | null>;\n\twrite(filePath: string, payload: PersistedToolbox): Promise<void>;\n\texists(filePath: string): Promise<boolean>;\n\t/** Maximum number of `.corrupted.*.bak` snapshots to keep per toolbox file (default: 5). */\n\tmaxBackupCount?: number;\n}\n\nexport interface ToolboxStoreOptions {\n\t/** Override the user-scope file path (default: `getToolboxJsonPath()`). */\n\tuserFilePath?: string;\n\t/** Override the workspace-scope file path resolver (default: cwd-relative). */\n\tresolveWorkspacePath?: () => string | null;\n\t/** Injectable built-in tool seeds (default: `BUILTIN_DEFAULT_TOOLS`). */\n\tdefaultTools?: readonly DefaultToolSeed[];\n\thooks?: ToolboxStoreHooks;\n\tbackend?: ToolboxFileBackend;\n\tlockTimeoutMs?: number;\n\tlockRetryMs?: number;\n}\n\nexport interface ToolboxStoreHooks {\n\tbeforeWrite?: (scope: ToolboxScope, payload: PersistedToolbox) => void | Promise<void>;\n\tafterWrite?: (scope: ToolboxScope, payload: PersistedToolbox) => void | Promise<void>;\n}\n\nexport class FsToolboxFileBackend implements ToolboxFileBackend {\n\tmaxBackupCount: number = 5;\n\n\tasync exists(filePath: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait fsp.access(filePath);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync read(filePath: string): Promise<PersistedToolbox | null> {\n\t\tlet buf: string;\n\t\ttry {\n\t\t\tbuf = await fsp.readFile(filePath, \"utf8\");\n\t\t} catch (err) {\n\t\t\tif (isNodeError(err) && err.code === \"ENOENT\") return null;\n\t\t\tthrow err;\n\t\t}\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(buf) as unknown;\n\t\t\treturn coercePersistedToolbox(parsed);\n\t\t} catch {\n\t\t\t// File exists but is unparseable JSON or fails schema validation\n\t\t\t// (e.g. hand-edited, written by an older/incompatible schema, or\n\t\t\t// truncated by a crash). Quarantine it as a timestamped `.bak`\n\t\t\t// sibling and fall back to an empty toolbox (defaults still\n\t\t\t// merge in via `ToolboxStore.read()`) rather than crashing the\n\t\t\t// whole \"get external tools\" request.\n\t\t\tawait this.backupCorruptedFile(filePath);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate async backupCorruptedFile(filePath: string): Promise<void> {\n\t\ttry {\n\t\t\tconst backupPath = `${filePath}.corrupted.${Date.now()}.bak`;\n\t\t\tawait fsp.copyFile(filePath, backupPath);\n\t\t\tawait this.purgeExcessBackups(filePath);\n\t\t} catch {\n\t\t\t// Best-effort backup — never let backup failure mask the real recovery.\n\t\t}\n\t}\n\n\tprivate async purgeExcessBackups(filePath: string): Promise<void> {\n\t\tconst dir = path.dirname(filePath);\n\t\tconst base = path.basename(filePath);\n\t\tlet entries: string[];\n\t\ttry {\n\t\t\tentries = await fsp.readdir(dir);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tconst backups = entries\n\t\t\t.filter((n) => n.startsWith(base) && n.endsWith(\".bak\"))\n\t\t\t.map((n) => ({ name: n, filePath: path.join(dir, n) }))\n\t\t\t.sort((a, b) => {\n\t\t\t\t// Sort oldest-first so we drop the earliest ones first.\n\t\t\t\treturn a.name.localeCompare(b.name);\n\t\t\t});\n\t\tconst excess = backups.length - this.maxBackupCount;\n\t\tif (excess <= 0) return;\n\t\tawait Promise.all(\n\t\t\tbackups.slice(0, excess).map((b) => fsp.rm(b.filePath).catch(() => undefined))\n\t\t);\n\t}\n\n\tasync write(filePath: string, payload: PersistedToolbox): Promise<void> {\n\t\tawait fsp.mkdir(path.dirname(filePath), { recursive: true });\n\t\tconst tmpPath = `${filePath}${TMP_SUFFIX}`;\n\t\tconst bytes = Buffer.from(JSON.stringify(payload, null, \"\\t\"), \"utf8\");\n\t\tawait fsp.rm(tmpPath, { force: true });\n\t\tconst handle = await fsp.open(tmpPath, \"w\", FILE_MODE);\n\t\ttry {\n\t\t\tawait handle.writeFile(bytes);\n\t\t\tawait handle.sync();\n\t\t} finally {\n\t\t\tawait handle.close();\n\t\t}\n\t\tawait fsp.rename(tmpPath, filePath);\n\t\tawait fsp.chmod(filePath, FILE_MODE).catch(() => undefined);\n\t}\n}\n\nfunction coercePersistedToolbox(parsed: unknown): PersistedToolbox {\n\tif (typeof parsed !== \"object\" || parsed === null) {\n\t\tthrow new Error(\"toolbox.json: top-level must be an object\");\n\t}\n\tconst obj = parsed as Record<string, unknown>;\n\tconst version = obj.version;\n\tif (version !== TOOLBOX_JSON_SCHEMA_VERSION) {\n\t\tthrow new Error(`toolbox.json: unsupported schema version ${String(version)}`);\n\t}\n\tif (!Array.isArray(obj.tools)) {\n\t\tthrow new Error(\"toolbox.json: 'tools' must be an array\");\n\t}\n\treturn { version, tools: obj.tools as ExternalTool[] };\n}\n\nfunction isNodeError(value: unknown): value is NodeJS.ErrnoException {\n\treturn value instanceof Error && typeof (value as { code?: unknown }).code === \"string\";\n}\n\nfunction isProcessAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nclass ToolboxFileLock {\n\tprivate readonly dirPath: string;\n\tprivate readonly pidFilePath: string;\n\tprivate readonly timeoutMs: number;\n\tprivate readonly retryMs: number;\n\tprivate acquired = false;\n\n\tconstructor(filePath: string, timeoutMs: number, retryMs: number) {\n\t\tthis.dirPath = `${filePath}.lock`;\n\t\tthis.pidFilePath = path.join(this.dirPath, \"pid\");\n\t\tthis.timeoutMs = timeoutMs;\n\t\tthis.retryMs = retryMs;\n\t}\n\n\tasync acquire(): Promise<void> {\n\t\tconst start = Date.now();\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\tawait fsp.mkdir(this.dirPath, { mode: LOCK_DIR_MODE });\n\t\t\t\tawait fsp.writeFile(this.pidFilePath, String(process.pid), \"utf8\").catch(() => undefined);\n\t\t\t\tthis.acquired = true;\n\t\t\t\treturn;\n\t\t\t} catch (err) {\n\t\t\t\tif (!isNodeError(err) || err.code !== \"EEXIST\") throw err;\n\t\t\t\tconst stale = await this.isStaleLock();\n\t\t\t\tif (stale) {\n\t\t\t\t\tawait fsp.rm(this.dirPath, { recursive: true, force: true });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Date.now() - start >= this.timeoutMs) {\n\t\t\t\t\tthrow new Error(`ToolboxStore lock acquisition timed out for ${this.dirPath}`);\n\t\t\t\t}\n\t\t\t\tawait delay(this.retryMs);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async isStaleLock(): Promise<boolean> {\n\t\tlet pidStr: string;\n\t\ttry {\n\t\t\tpidStr = await fsp.readFile(this.pidFilePath, \"utf8\");\n\t\t} catch {\n\t\t\t// The pid file may not exist yet because another acquirer just\n\t\t\t// created the lock dir and hasn't finished writing its pid file\n\t\t\t// (mkdir + writeFile is not atomic). Give it a short grace window\n\t\t\t// before concluding the owner crashed between mkdir and writeFile.\n\t\t\ttry {\n\t\t\t\tconst stat = await fsp.stat(this.dirPath);\n\t\t\t\treturn Date.now() - stat.mtimeMs > LOCK_STALE_GRACE_MS;\n\t\t\t} catch {\n\t\t\t\t// Lock dir disappeared concurrently (e.g. released mid-check) —\n\t\t\t\t// not stale, just gone; the caller's next mkdir will succeed.\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tconst pid = Number.parseInt(pidStr.trim(), 10);\n\t\tif (!Number.isFinite(pid) || pid <= 0) return true;\n\t\treturn !isProcessAlive(pid);\n\t}\n\n\tasync release(): Promise<void> {\n\t\tif (!this.acquired) return;\n\t\tthis.acquired = false;\n\t\tawait fsp.rm(this.dirPath, { recursive: true, force: true });\n\t}\n}\n\nfunction defaultWorkspacePath(): string | null {\n\t// The store caller (ToolboxCore) supplies a cwd, but tests want a\n\t// stable default. Return null when the env var says \"no workspace\".\n\tif (process.env.SERVICEME_NO_WORKSPACE_TOOLBOX === \"1\") return null;\n\treturn path.join(process.cwd(), WORKSPACE_TOOLBOX_RELATIVE_PATH);\n}\n\nexport class ToolboxStore {\n\tprivate readonly userFilePath: string;\n\tprivate readonly resolveWorkspacePath: () => string | null;\n\tprivate readonly defaults: readonly DefaultToolSeed[];\n\tprivate readonly hooks: ToolboxStoreHooks;\n\tprivate readonly backend: ToolboxFileBackend;\n\tprivate readonly lockTimeoutMs: number;\n\tprivate readonly lockRetryMs: number;\n\n\tconstructor(opts: ToolboxStoreOptions = {}) {\n\t\tthis.userFilePath = opts.userFilePath ?? getToolboxJsonPath();\n\t\tthis.resolveWorkspacePath = opts.resolveWorkspacePath ?? defaultWorkspacePath;\n\t\tthis.defaults = opts.defaultTools ?? BUILTIN_DEFAULT_TOOLS;\n\t\tthis.hooks = opts.hooks ?? {};\n\t\tthis.backend = opts.backend ?? new FsToolboxFileBackend();\n\t\tthis.lockTimeoutMs = opts.lockTimeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;\n\t\tthis.lockRetryMs = opts.lockRetryMs ?? DEFAULT_LOCK_RETRY_MS;\n\t}\n\n\t/** Read a scope; returns the resolved toolbox (with defaults merged when empty). */\n\tasync read(scope: ToolboxScope): Promise<ResolvedToolbox> {\n\t\tconst filePath = this.filePathFor(scope);\n\t\tif (scope === \"workspace\") {\n\t\t\tawait migrateLegacyWorkspaceToolboxFile(filePath);\n\t\t}\n\t\tconst stored = filePath ? await this.backend.read(filePath) : null;\n\t\tconst storedTools = stored?.tools ?? [];\n\t\tconst defaults = this.defaults.map((seed, index) => toDefaultTool(seed, index));\n\t\tconst merged = mergeWithDefaults(defaults, storedTools);\n\t\tconst userOnly = storedTools.filter((t) => !this.defaults.some((d) => d.id === t.id));\n\t\tvoid userOnly;\n\t\treturn { scope, tools: merged };\n\t}\n\n\t/**\n\t * Atomic write under a file lock. Replaces the entire `tools` array\n\t * with the provided snapshot.\n\t */\n\tasync write(scope: ToolboxScope, tools: readonly ExternalTool[]): Promise<void> {\n\t\tconst filePath = this.filePathFor(scope);\n\t\tif (!filePath) {\n\t\t\tthrow new Error(`Cannot write toolbox scope '${scope}': file path is unavailable`);\n\t\t}\n\t\tif (scope === \"workspace\") {\n\t\t\tawait migrateLegacyWorkspaceToolboxFile(filePath);\n\t\t}\n\t\tconst payload: PersistedToolbox = {\n\t\t\tversion: TOOLBOX_JSON_SCHEMA_VERSION,\n\t\t\ttools: sortByUserOrder([...tools]),\n\t\t};\n\t\tawait this.hooks.beforeWrite?.(scope, payload);\n\t\tconst lock = new ToolboxFileLock(filePath, this.lockTimeoutMs, this.lockRetryMs);\n\t\tawait lock.acquire();\n\t\ttry {\n\t\t\tawait this.backend.write(filePath, payload);\n\t\t} finally {\n\t\t\tawait lock.release();\n\t\t}\n\t\tawait this.hooks.afterWrite?.(scope, payload);\n\t}\n\n\t/**\n\t * Read-modify-write under the file lock. The mutator receives the\n\t * current user-only list (defaults not included) and returns the\n\t * replacement list. Throwing inside the mutator aborts the write.\n\t */\n\tasync mutate(\n\t\tscope: ToolboxScope,\n\t\tmutator: (current: ExternalTool[]) => Promise<ExternalTool[]>\n\t): Promise<ExternalTool[]> {\n\t\tconst filePath = this.filePathFor(scope);\n\t\tif (!filePath) {\n\t\t\tthrow new Error(`Cannot mutate toolbox scope '${scope}': file path is unavailable`);\n\t\t}\n\t\tif (scope === \"workspace\") {\n\t\t\tawait migrateLegacyWorkspaceToolboxFile(filePath);\n\t\t}\n\t\tconst lock = new ToolboxFileLock(filePath, this.lockTimeoutMs, this.lockRetryMs);\n\t\tawait lock.acquire();\n\t\ttry {\n\t\t\tconst stored = await this.backend.read(filePath);\n\t\t\tconst storedTools = stored?.tools ?? [];\n\t\t\tconst defaultIds = new Set(this.defaults.map((d) => d.id));\n\t\t\tconst userTools = storedTools.filter((t) => !defaultIds.has(t.id));\n\t\t\tconst next = await mutator(userTools);\n\t\t\tconst payload: PersistedToolbox = {\n\t\t\t\tversion: TOOLBOX_JSON_SCHEMA_VERSION,\n\t\t\t\ttools: sortByUserOrder([...next]),\n\t\t\t};\n\t\t\tawait this.hooks.beforeWrite?.(scope, payload);\n\t\t\tawait this.backend.write(filePath, payload);\n\t\t\tawait this.hooks.afterWrite?.(scope, payload);\n\t\t\treturn next;\n\t\t} finally {\n\t\t\tawait lock.release();\n\t\t}\n\t}\n\n\t/** Wipe a scope entirely (used by `toolbox.remove --all` extensions). */\n\tasync clear(scope: ToolboxScope): Promise<void> {\n\t\tconst filePath = this.filePathFor(scope);\n\t\tif (!filePath) return;\n\t\tawait fsp.rm(filePath, { force: true });\n\t}\n\n\t/** Test seam — resolve the user-scope file path. */\n\tgetUserFilePath(): string {\n\t\treturn this.userFilePath;\n\t}\n\n\t/** Test seam — resolve the workspace-scope file path (or null when disabled). */\n\tgetWorkspaceFilePath(): string | null {\n\t\treturn this.resolveWorkspacePath();\n\t}\n\n\tprivate filePathFor(scope: ToolboxScope): string | null {\n\t\treturn scope === \"user\" ? this.userFilePath : this.resolveWorkspacePath();\n\t}\n}\n\nfunction toDefaultTool(seed: DefaultToolSeed, order: number): ExternalTool {\n\treturn {\n\t\tid: seed.id,\n\t\tname: seed.name,\n\t\tdescription: seed.description,\n\t\ticon: seed.icon,\n\t\turl: seed.url,\n\t\tisDefault: true,\n\t\torder,\n\t\tscope: \"user\",\n\t};\n}\n","/**\n * toolbox types — internal-only data shapes.\n *\n * The public data model lives in `@serviceme/devtools-protocol/toolbox`\n * (`ExternalTool`, `ToolboxScope`, `ToolboxList`). The types below are\n * the persisted on-disk shape and the patch helpers, scoped to the\n * toolbox domain only.\n *\n * Refs:\n * - 4.功能规划.md §2.3 — `toolbox/types.ts`\n */\n\nimport type { ExternalTool, ToolboxScope } from \"@serviceme/devtools-protocol\";\n\n/** Schema version of the on-disk toolbox JSON files. Bumped on breaking changes. */\nexport const TOOLBOX_JSON_SCHEMA_VERSION = 1;\n\nexport interface PersistedToolbox {\n\tversion: number;\n\ttools: ExternalTool[];\n}\n\n/** Patch payload accepted by `ToolboxCore.update` (mirrors `ExternalToolPatch`). */\nexport type ToolboxPatch = Partial<Omit<ExternalTool, \"id\" | \"isDefault\">>;\n\n/**\n * Built-in (default) tool seeds. Mirrors the Extension's\n * `apps/extension/src/config/external-tools.ts`. The Core stores these\n * verbatim and never mutates them — the Extension's `ToolBoxService`\n * runs the same merge on top of the Core's view.\n */\nexport interface DefaultToolSeed {\n\tid: string;\n\tname: string;\n\tdescription: string;\n\ticon: string;\n\turl: string;\n}\n\n/** Built-in tools rendered when the JSON file is missing or empty. */\nexport const BUILTIN_DEFAULT_TOOLS: readonly DefaultToolSeed[] = [\n\t{\n\t\tid: \"builtin-docs\",\n\t\tname: \"SERVICEME Docs\",\n\t\tdescription: \"Official documentation portal\",\n\t\ticon: \"book\",\n\t\turl: \"https://docs.medalsoft.com/serviceme\",\n\t},\n\t{\n\t\tid: \"builtin-issues\",\n\t\tname: \"Issue Tracker\",\n\t\tdescription: \"Report bugs and feature requests\",\n\t\ticon: \"bug\",\n\t\turl: \"https://github.com/medalsoftchina/ms-devtools-vscode/issues\",\n\t},\n\t{\n\t\tid: \"builtin-changelog\",\n\t\tname: \"Changelog\",\n\t\tdescription: \"Release notes for every published version\",\n\t\ticon: \"history\",\n\t\turl: \"https://github.com/medalsoftchina/ms-devtools-vscode/releases\",\n\t},\n];\n\n/** Resolved toolbox shape returned by `ToolboxStore.read()`. */\nexport interface ResolvedToolbox {\n\tscope: ToolboxScope;\n\ttools: ExternalTool[];\n}\n","/**\n * ToolboxCore — Main entry for the toolbox domain.\n *\n * Orchestrates the user + workspace scopes via `ToolboxStore`, applies\n * the \"最近使用置顶\" sort, and exposes a small surface that mirrors\n * the Phase 5.1 bridge methods:\n *\n * - `list(scope?)` → `toolbox.list`\n * - `add(tool, scope)` → `toolbox.add`\n * - `remove(id, scope?)` → `toolbox.remove`\n * - `update(id, patch, scope?)` → `toolbox.update`\n *\n * Default tools (built-in seeds) are never user-deletable. `remove()`\n * is a no-op for default tools and surfaces a structured error so\n * callers can map it to `TOOLBOX_DEFAULT_IMMUTABLE` (Phase 5.3 error\n * code).\n *\n * Refs:\n * - 4.功能规划.md §2.3 — `ToolboxCore.ts 主入口`\n * - `3.功能拆分.md` §3 — wire shape\n */\n\nimport type { ExternalTool, ToolboxList, ToolboxScope } from \"@serviceme/devtools-protocol\";\n\nimport { sortByRecentFirst, sortByUserOrder, touchLastUsedAt } from \"./sort\";\nimport { ToolboxStore } from \"./ToolboxStore\";\nimport { BUILTIN_DEFAULT_TOOLS, type DefaultToolSeed, type ToolboxPatch } from \"./types\";\n\n/** Sentinel — caller tried to remove a built-in (immutable) tool. */\nexport class DefaultToolImmutableError extends Error {\n\tconstructor(public readonly toolId: string) {\n\t\tsuper(`Cannot remove default toolbox entry: ${toolId}`);\n\t\tthis.name = \"DefaultToolImmutableError\";\n\t}\n}\n\nexport interface ToolboxCoreOptions {\n\tstore?: ToolboxStore;\n\tdefaultTools?: readonly DefaultToolSeed[];\n\t/** Sort applied to the merged list returned by `list()`. Default: `sortByUserOrder`. */\n\tlistSort?: (tools: readonly ExternalTool[]) => ExternalTool[];\n}\n\nexport class ToolboxCore {\n\tprivate readonly store: ToolboxStore;\n\tprivate readonly defaults: readonly DefaultToolSeed[];\n\tprivate readonly listSort: (tools: readonly ExternalTool[]) => ExternalTool[];\n\n\tconstructor(opts: ToolboxCoreOptions = {}) {\n\t\tthis.store = opts.store ?? new ToolboxStore();\n\t\tthis.defaults = opts.defaultTools ?? BUILTIN_DEFAULT_TOOLS;\n\t\tthis.listSort = opts.listSort ?? sortByUserOrder;\n\t}\n\n\t/** List all tools in a scope. Built-in defaults are merged in. */\n\tasync list(scope: ToolboxScope = \"user\"): Promise<ToolboxList> {\n\t\tconst resolved = await this.store.read(scope);\n\t\treturn {\n\t\t\tscope,\n\t\t\ttools: this.listSort(resolved.tools),\n\t\t};\n\t}\n\n\t/**\n\t * Append a new tool to the requested scope. Default tools are\n\t * rejected (they are seeds, not user entries).\n\t */\n\tasync add(tool: ExternalTool, scope: ToolboxScope = \"user\"): Promise<ToolboxList> {\n\t\tif (this.isDefaultId(tool.id)) {\n\t\t\tthrow new DefaultToolImmutableError(tool.id);\n\t\t}\n\t\tconst next = await this.store.mutate(scope, async (current) => {\n\t\t\tconst filtered = current.filter((t) => t.id !== tool.id);\n\t\t\treturn [\n\t\t\t\t...filtered,\n\t\t\t\t{\n\t\t\t\t\t...tool,\n\t\t\t\t\tscope,\n\t\t\t\t\tisDefault: false,\n\t\t\t\t\torder: tool.order ?? filtered.length,\n\t\t\t\t},\n\t\t\t];\n\t\t});\n\t\treturn { scope, tools: next };\n\t}\n\n\t/**\n\t * Remove a tool by id. Returns `success: false` when the id is a\n\t * built-in default (idempotent, never throws on missing entries).\n\t */\n\tasync remove(\n\t\tid: string,\n\t\tscope: ToolboxScope = \"user\"\n\t): Promise<{ scope: ToolboxScope; toolId: string; success: boolean }> {\n\t\tif (this.isDefaultId(id)) {\n\t\t\tthrow new DefaultToolImmutableError(id);\n\t\t}\n\t\tconst removed = await this.store.mutate(scope, async (current) =>\n\t\t\tcurrent.filter((t) => t.id !== id)\n\t\t);\n\t\treturn { scope, toolId: id, success: !removed.some((t) => t.id === id) };\n\t}\n\n\t/**\n\t * Patch a tool by id. Default tools can only have their `order`\n\t * updated; other patches are silently ignored for default entries\n\t * (callers can compare before/after to detect the ignore).\n\t */\n\tasync update(\n\t\tid: string,\n\t\tpatch: ToolboxPatch,\n\t\tscope: ToolboxScope = \"user\"\n\t): Promise<ToolboxList> {\n\t\tconst isDefault = this.isDefaultId(id);\n\t\tconst next = await this.store.mutate(scope, async (current) => {\n\t\t\tif (isDefault) {\n\t\t\t\t// Default tools are not stored on disk — silently ignore\n\t\t\t\t// non-order patches. The order field is also not persisted\n\t\t\t\t// (default order is set in memory at merge time), so the\n\t\t\t\t// caller can compare before/after to detect the ignore.\n\t\t\t\treturn current;\n\t\t\t}\n\t\t\treturn current.map((tool) =>\n\t\t\t\ttool.id === id ? { ...tool, ...patch, scope, isDefault: false } : tool\n\t\t\t);\n\t\t});\n\t\treturn { scope, tools: next };\n\t}\n\n\t/**\n\t * Stamp `lastUsedAt` on the targeted tool. This is the \"recently\n\t * used\" hook the Extension's webview uses when a user clicks a\n\t * toolbox entry.\n\t */\n\tasync recordUsage(\n\t\tid: string,\n\t\tscope: ToolboxScope = \"user\",\n\t\twhen: Date = new Date()\n\t): Promise<ExternalTool | null> {\n\t\tif (this.isDefaultId(id)) {\n\t\t\t// Defaults live in memory only — return a synthetic record so\n\t\t\t// callers can render the click without persisting anything.\n\t\t\tconst seed = this.defaults.find((d) => d.id === id);\n\t\t\tif (!seed) return null;\n\t\t\treturn { ...seed, scope, isDefault: true, order: 0, lastUsedAt: when.toISOString() };\n\t\t}\n\t\tlet updated: ExternalTool | null = null;\n\t\tawait this.store.mutate(scope, async (current) => {\n\t\t\tconst next = touchLastUsedAt(current, id, when);\n\t\t\tupdated = next.find((t) => t.id === id) ?? null;\n\t\t\treturn next;\n\t\t});\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Combined view: user + workspace scopes merged, sorted by recent\n\t * usage. Workspace tools overlay user tools (workspace entries win\n\t * on `id` collision).\n\t */\n\tasync listMerged(): Promise<ToolboxList> {\n\t\tconst [user, workspace] = await Promise.all([\n\t\t\tthis.store.read(\"user\"),\n\t\t\tthis.store.read(\"workspace\"),\n\t\t]);\n\t\tconst seen = new Set<string>();\n\t\tconst merged: ExternalTool[] = [];\n\t\tfor (const tool of workspace.tools) {\n\t\t\tseen.add(tool.id);\n\t\t\tmerged.push({ ...tool, scope: \"workspace\" });\n\t\t}\n\t\tfor (const tool of user.tools) {\n\t\t\tif (seen.has(tool.id)) continue;\n\t\t\tmerged.push({ ...tool, scope: \"user\" });\n\t\t}\n\t\treturn { scope: \"user\", tools: sortByRecentFirst(merged) };\n\t}\n\n\t/** Expose the underlying store (CLI / Bridge use it for path-level access). */\n\tgetStore(): ToolboxStore {\n\t\treturn this.store;\n\t}\n\n\tprivate isDefaultId(id: string): boolean {\n\t\treturn this.defaults.some((d) => d.id === id);\n\t}\n}\n"],"mappings":"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","names":["resolve","createServicemeError","resolve","createServicemeError","COPILOT_ERROR_CODES","createServicemeError","COPILOT_COMMAND","createServicemeError","COPILOT_ERROR_CODES","path","path","stat","fsp","path","previous","linkPath","stat","basename","relative","fs","path","fs","path","stat","stat","fsp","path","fsp","path","stat","fsp","path","fsp","os","path","spawn","fsp","path","resolve","fsp","path","existing","replacement","plugins","next","fsp","path","plugin","stat","installation","fsp","path","fsp","path","stat","fsp","path","createHash","fsp","path","relative","pathExists","stat","basename","createHash","stat","fsp","path","type","stat","fsp","path","stat","createHash","createHash","os","platform","fsp","os","path","stat","platform","fs","path","fs","path","fs","path","SKIP_DIRS","stat","homedir","join","createServicemeError","platform","createServicemeError","homedir","join","isTimeout","spawn","path","resolve","spawn","fs","path","createServicemeError","createServicemeError","createServicemeError","randomUUID","fs","open","path","randomUUID","open","randomUUID","fs","path","randomUUID","fs","path","createServicemeError","access","copyFile","lstat","mkdir","readdir","rename","rm","dirname","join","resolve","createServicemeError","fs","path","fs","path","z","z","fs","unlink","EventEmitter","fs","EventEmitter","fs","path","fs","path","CONFIG_DIR","stat","fs","os","path","spawn","fs","DEFAULT_TIMEOUT_MS","resolve","spawn","DEFAULT_TIMEOUT_MS","spawn","fs","path","MAX_OUTPUT_BYTES","DEFAULT_TIMEOUT_MS","platform","writeDiagnostic","resolve","spawn","randomUUID","fs","os","path","createServicemeError","isRecord","createServicemeError","randomUUID","getExecutor","randomUUID","fs","path","randomUUID","SCHEDULER_LOG_FILENAME","fs","path","isScheduledTasksConfigV1","migrateV1ToV2","isScheduledTasksConfigV1","migrateV1ToV2","spawn","fs","path","DEFAULT_TIMEOUT_MS","resolve","createServicemeError","fs","path","SAFE_LOCAL_ID_PATTERN","fs","path","fsp","path","delay","FILE_MODE","LOCK_DIR_MODE","DEFAULT_LOCK_TIMEOUT_MS","DEFAULT_LOCK_RETRY_MS","LOCK_STALE_GRACE_MS","TMP_SUFFIX","isNodeError","isProcessAlive","delay","stat"]}