argos-harness 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/assets/hooks/argos-guard-destructive.sh +1 -1
  2. package/assets/managed/disciplina-skills.md +11 -0
  3. package/assets/managed/formato-respuesta.md +2 -2
  4. package/assets/managed/identidad.md +7 -7
  5. package/assets/managed/operaciones-seguras.md +6 -6
  6. package/assets/output-styles/argos.md +12 -12
  7. package/dist/commands/adopt.d.ts +41 -1
  8. package/dist/commands/adopt.d.ts.map +1 -1
  9. package/dist/commands/adopt.js +243 -6
  10. package/dist/commands/adopt.js.map +1 -1
  11. package/dist/commands/doctor.d.ts.map +1 -1
  12. package/dist/commands/doctor.js +36 -2
  13. package/dist/commands/doctor.js.map +1 -1
  14. package/dist/commands/init.d.ts +56 -0
  15. package/dist/commands/init.d.ts.map +1 -1
  16. package/dist/commands/init.js +330 -64
  17. package/dist/commands/init.js.map +1 -1
  18. package/dist/commands/remove.d.ts +24 -0
  19. package/dist/commands/remove.d.ts.map +1 -1
  20. package/dist/commands/remove.js +94 -4
  21. package/dist/commands/remove.js.map +1 -1
  22. package/dist/commands/workspace.d.ts +32 -0
  23. package/dist/commands/workspace.d.ts.map +1 -1
  24. package/dist/commands/workspace.js +97 -3
  25. package/dist/commands/workspace.js.map +1 -1
  26. package/dist/lib/assets.d.ts +5 -3
  27. package/dist/lib/assets.d.ts.map +1 -1
  28. package/dist/lib/assets.js +5 -2
  29. package/dist/lib/assets.js.map +1 -1
  30. package/dist/lib/managed-files.d.ts +19 -5
  31. package/dist/lib/managed-files.d.ts.map +1 -1
  32. package/dist/lib/managed-files.js +18 -6
  33. package/dist/lib/managed-files.js.map +1 -1
  34. package/dist/lib/prompter.d.ts +63 -0
  35. package/dist/lib/prompter.d.ts.map +1 -0
  36. package/dist/lib/prompter.js +30 -0
  37. package/dist/lib/prompter.js.map +1 -0
  38. package/dist/lib/settings-merge.d.ts +78 -0
  39. package/dist/lib/settings-merge.d.ts.map +1 -1
  40. package/dist/lib/settings-merge.js +204 -0
  41. package/dist/lib/settings-merge.js.map +1 -1
  42. package/package.json +1 -1
@@ -4,7 +4,8 @@ import pc from "picocolors";
4
4
  import { hasConfig, readConfig } from "../lib/config.js";
5
5
  import { getRemoteOriginUrl } from "../lib/git.js";
6
6
  import { OPENCLAW_BINARY, planWorkspaceAgents } from "../lib/openclaw-agents.js";
7
- import { addRemoteMatchRule, linkRepo, loadRegistry, offerMatchRule, resolveWorkspaceForRepo } from "../lib/workspaces.js";
7
+ import { isInteractive, clackPrompter } from "../lib/prompter.js";
8
+ import { addRemoteMatchRule, linkRepo, loadRegistry, offerMatchRule, resolveWorkspaceForRepo, WorkspaceNameCollisionError } from "../lib/workspaces.js";
8
9
  import { hasBinary as hasBinaryDefault } from "../lib/which.js";
9
10
  function errorMessage(err) {
10
11
  return err instanceof Error ? err.message : String(err);
@@ -70,6 +71,18 @@ function errorMessage(err) {
70
71
  force
71
72
  });
72
73
  } catch (err) {
74
+ if (err instanceof WorkspaceNameCollisionError) {
75
+ return {
76
+ exitCode: 1,
77
+ error: errorMessage(err),
78
+ collision: {
79
+ workspaceName: err.workspaceName,
80
+ repoName: err.repoName,
81
+ oldPath: err.oldPath,
82
+ newPath: err.newPath
83
+ }
84
+ };
85
+ }
73
86
  return {
74
87
  exitCode: 1,
75
88
  error: errorMessage(err)
@@ -100,6 +113,87 @@ function errorMessage(err) {
100
113
  matchRuleAdded
101
114
  };
102
115
  }
116
+ /**
117
+ * Interactive layer over `runWorkspaceLink` (spec 0004 F5 "argos workspace
118
+ * link"). A pure additive wrapper — the core `runWorkspaceLink` never
119
+ * changes behavior or contract. Without a real TTY this delegates to
120
+ * `runWorkspaceLink(options)` unchanged (there's no `--yes` on this command:
121
+ * absence of a real TTY is itself sufficient gate, per spec 0004). With a
122
+ * TTY: an ambiguous match-rule resolution is resolved via `select` instead
123
+ * of erroring, and a name collision (`WorkspaceNameCollisionError`) is
124
+ * offered as an overwrite/cancel prompt — the interactive equivalent of
125
+ * `--force`, which stays the non-interactive escape hatch unchanged.
126
+ */ export async function runWorkspaceLinkInteractive(options) {
127
+ if (!isInteractive({})) {
128
+ return runWorkspaceLink(options);
129
+ }
130
+ const prompter = options.prompter ?? clackPrompter;
131
+ const { cwd, explicit, force } = options;
132
+ // Resolve ambiguity read-only first, mirroring runWorkspaceLink's own
133
+ // resolution chain, before ever calling the writing core — an explicit
134
+ // name (whether passed on the CLI or picked here) always short-circuits
135
+ // resolveWorkspaceForRepo straight to "resolved", so no duplicate
136
+ // ambiguity handling happens inside the core call below.
137
+ let effectiveExplicit = explicit;
138
+ if (!explicit) {
139
+ if (!hasConfig(cwd)) return runWorkspaceLink(options); // identical error path, no prompts needed
140
+ let config;
141
+ try {
142
+ config = readConfig(cwd);
143
+ } catch {
144
+ return runWorkspaceLink(options); // identical error path
145
+ }
146
+ const remoteUrl = getRemoteOriginUrl(cwd);
147
+ let registry;
148
+ try {
149
+ registry = loadRegistry();
150
+ } catch {
151
+ return runWorkspaceLink(options); // identical error path
152
+ }
153
+ const resolution = resolveWorkspaceForRepo(registry, {
154
+ configWorkspace: config.workspace,
155
+ remoteUrl,
156
+ repoPath: cwd
157
+ });
158
+ if (resolution.kind === "ambiguous") {
159
+ const chosen = await prompter.select({
160
+ message: `Match ambiguo entre workspaces (${resolution.source}) — elegí uno:`,
161
+ options: resolution.candidates.map((c)=>({
162
+ value: c
163
+ }))
164
+ });
165
+ if (prompter.isCancel(chosen)) {
166
+ prompter.cancel("argos workspace link cancelado — no se tocó nada.");
167
+ return {
168
+ exitCode: 1,
169
+ error: "argos workspace link cancelado por el usuario."
170
+ };
171
+ }
172
+ effectiveExplicit = chosen;
173
+ }
174
+ }
175
+ let report = runWorkspaceLink({
176
+ cwd,
177
+ explicit: effectiveExplicit,
178
+ force
179
+ });
180
+ if (report.collision && !force) {
181
+ const overwrite = await prompter.confirm({
182
+ message: `El repo '${report.collision.repoName}' ya está registrado en el workspace '${report.collision.workspaceName}' ` + `apuntando a otro path.\n actual: ${report.collision.oldPath}\n nuevo: ${report.collision.newPath}\n` + "¿Sobrescribir con el path nuevo?",
183
+ initialValue: false
184
+ });
185
+ if (prompter.isCancel(overwrite) || !overwrite) {
186
+ prompter.cancel("argos workspace link cancelado — no se tocó nada.");
187
+ return report; // original failed report — nothing further was touched
188
+ }
189
+ report = runWorkspaceLink({
190
+ cwd,
191
+ explicit: effectiveExplicit,
192
+ force: true
193
+ });
194
+ }
195
+ return report;
196
+ }
103
197
  const linkSubCommand = defineCommand({
104
198
  meta: {
105
199
  name: "link",
@@ -117,8 +211,8 @@ const linkSubCommand = defineCommand({
117
211
  description: "Sobrescribe un choque de nombre (mismo config.name, repo físico distinto)."
118
212
  }
119
213
  },
120
- run ({ args }) {
121
- const report = runWorkspaceLink({
214
+ async run ({ args }) {
215
+ const report = await runWorkspaceLinkInteractive({
122
216
  cwd: process.cwd(),
123
217
  explicit: args.name?.trim() || undefined,
124
218
  force: Boolean(args.force)
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/workspace.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { existsSync } from \"node:fs\";\nimport pc from \"picocolors\";\nimport { hasConfig, readConfig, type ArgosConfig } from \"../lib/config.js\";\nimport { getRemoteOriginUrl } from \"../lib/git.js\";\nimport {\n OPENCLAW_BINARY,\n planWorkspaceAgents,\n type OpenclawRunner,\n type WorkspaceAgentRow,\n type WorkspaceAgentStatus,\n} from \"../lib/openclaw-agents.js\";\nimport {\n addRemoteMatchRule,\n linkRepo,\n loadRegistry,\n offerMatchRule,\n resolveWorkspaceForRepo,\n type LinkAction,\n} from \"../lib/workspaces.js\";\nimport { hasBinary as hasBinaryDefault } from \"../lib/which.js\";\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n// --- link --------------------------------------------------------------\n\nexport interface WorkspaceLinkOptions {\n cwd: string;\n /** Explicit workspace name passed on the command line, if any. */\n explicit?: string;\n /** Overwrite a name collision (same config.name, different physical repo) instead of refusing. */\n force?: boolean;\n}\n\nexport interface WorkspaceLinkReport {\n exitCode: 0 | 1;\n error?: string;\n ambiguousCandidates?: string[];\n workspaceName?: string;\n action?: LinkAction;\n createdWorkspace?: boolean;\n /** Remote identity persisted as a new match rule, if the offer fired. */\n matchRuleAdded?: string;\n}\n\n/**\n * Core, testable implementation of `argos workspace link [nombre]`: resolves\n * which workspace the repo at `cwd` belongs to (explicit name > config >\n * match rules) and registers it. When a brand-new workspace was created via\n * an explicit name and the repo has a remote, teaches the registry that\n * remote as a match rule so future repos under the same identity auto-link.\n */\nexport function runWorkspaceLink(options: WorkspaceLinkOptions): WorkspaceLinkReport {\n const { cwd, explicit, force } = options;\n\n if (!hasConfig(cwd)) {\n return {\n exitCode: 1,\n error: \"No se encontró argos.config.json en este directorio — corre `argos adopt` primero.\",\n };\n }\n\n let config: ArgosConfig;\n try {\n config = readConfig(cwd);\n } catch (err) {\n return { exitCode: 1, error: `argos.config.json inválido — ${errorMessage(err)}` };\n }\n\n const remoteUrl = getRemoteOriginUrl(cwd);\n let registry;\n try {\n registry = loadRegistry();\n } catch (err) {\n return { exitCode: 1, error: errorMessage(err) };\n }\n const resolution = resolveWorkspaceForRepo(registry, {\n explicit,\n configWorkspace: config.workspace,\n remoteUrl,\n repoPath: cwd,\n });\n\n if (resolution.kind === \"unresolved\") {\n return {\n exitCode: 1,\n error:\n \"No se pudo resolver un workspace para este repo — pasá un nombre explícito: \" +\n \"`argos workspace link <nombre>`.\",\n };\n }\n if (resolution.kind === \"ambiguous\") {\n return {\n exitCode: 1,\n error: `Match ambiguo entre workspaces (${resolution.candidates.join(\", \")}) — pasá un nombre explícito.`,\n ambiguousCandidates: resolution.candidates,\n };\n }\n\n let linkResult;\n try {\n linkResult = linkRepo(resolution.name, { name: config.name, path: cwd }, { force });\n } catch (err) {\n return { exitCode: 1, error: errorMessage(err) };\n }\n\n let matchRuleAdded: string | undefined;\n if (resolution.source === \"explicit\") {\n const currentMatch = loadRegistry()[resolution.name]?.match ?? { remotes: [], paths: [] };\n const offer = offerMatchRule({\n createdWorkspace: linkResult.createdWorkspace,\n viaExplicitName: true,\n remoteUrl,\n currentMatch,\n });\n if (offer.shouldPersist && offer.identity) {\n addRemoteMatchRule(resolution.name, offer.identity);\n matchRuleAdded = offer.identity;\n }\n }\n\n return {\n exitCode: 0,\n workspaceName: resolution.name,\n action: linkResult.action,\n createdWorkspace: linkResult.createdWorkspace,\n matchRuleAdded,\n };\n}\n\nconst linkSubCommand = defineCommand({\n meta: {\n name: \"link\",\n description: \"Vincula el repo actual a un workspace (lo crea si no existe).\",\n },\n args: {\n name: { type: \"positional\", description: \"Nombre de workspace explícito\", required: false },\n force: {\n type: \"boolean\",\n default: false,\n description: \"Sobrescribe un choque de nombre (mismo config.name, repo físico distinto).\",\n },\n },\n run({ args }) {\n const report = runWorkspaceLink({\n cwd: process.cwd(),\n explicit: (args.name as string | undefined)?.trim() || undefined,\n force: Boolean(args.force),\n });\n\n if (report.error) {\n console.error(pc.red(report.error));\n process.exit(report.exitCode);\n }\n\n if (report.createdWorkspace) {\n console.log(pc.dim(`Workspace '${report.workspaceName}' creado.`));\n }\n const actionLabel: Record<LinkAction, string> = {\n added: \"registrado\",\n \"updated-path\": \"path actualizado\",\n unchanged: \"sin cambios\",\n };\n console.log(\n `${pc.green(\"✓\")} repo ${actionLabel[report.action as LinkAction]} en workspace '${report.workspaceName}'.`,\n );\n if (report.matchRuleAdded) {\n console.log(\n pc.dim(`El remote '${report.matchRuleAdded}' se guardó como match rule del workspace.`),\n );\n }\n process.exit(report.exitCode);\n },\n});\n\n// --- show ----------------------------------------------------------------\n\nexport interface WorkspaceShowRepoRow {\n workspace: string;\n name: string;\n path: string;\n missing: boolean;\n}\n\nexport interface WorkspaceShowReport {\n rows: WorkspaceShowRepoRow[];\n exitCode: 0 | 1;\n error?: string;\n}\n\nexport interface WorkspaceShowOptions {\n /** Injectable for tests; defaults to node:fs existsSync. */\n pathExists?: (path: string) => boolean;\n}\n\n/** Core, testable implementation of `argos workspace show [nombre]`. */\nexport function runWorkspaceShow(name?: string, options: WorkspaceShowOptions = {}): WorkspaceShowReport {\n const pathExists = options.pathExists ?? existsSync;\n let registry;\n try {\n registry = loadRegistry();\n } catch (err) {\n return { rows: [], exitCode: 1, error: errorMessage(err) };\n }\n\n if (name) {\n const workspace = registry[name];\n if (!workspace) {\n return { rows: [], exitCode: 1, error: `Workspace '${name}' no encontrado.` };\n }\n const rows = workspace.repos.map((r) => ({\n workspace: name,\n name: r.name,\n path: r.path,\n missing: !pathExists(r.path),\n }));\n return { rows, exitCode: 0 };\n }\n\n const rows: WorkspaceShowRepoRow[] = [];\n for (const [wsName, workspace] of Object.entries(registry)) {\n for (const repo of workspace.repos) {\n rows.push({ workspace: wsName, name: repo.name, path: repo.path, missing: !pathExists(repo.path) });\n }\n }\n return { rows, exitCode: 0 };\n}\n\nconst showSubCommand = defineCommand({\n meta: {\n name: \"show\",\n description: \"Muestra los repos registrados en uno o todos los workspaces.\",\n },\n args: {\n name: { type: \"positional\", description: \"Nombre de workspace (default: todos)\", required: false },\n },\n run({ args }) {\n const name = (args.name as string | undefined)?.trim() || undefined;\n const report = runWorkspaceShow(name);\n\n if (report.error) {\n console.error(pc.red(report.error));\n process.exit(report.exitCode);\n }\n\n if (report.rows.length === 0) {\n console.log(\"Ningún repo registrado todavía. Corre `argos workspace link` desde un repo.\");\n process.exit(0);\n }\n\n for (const row of report.rows) {\n const flag = row.missing ? pc.red(\"(path inexistente)\") : \"\";\n console.log(`${pc.dim(row.workspace.padEnd(16))} ${row.name.padEnd(24)} ${row.path} ${flag}`.trimEnd());\n }\n process.exit(0);\n },\n});\n\n// --- agents ----------------------------------------------------------------\n\nexport interface RunWorkspaceAgentsOptions {\n /** false (default) = preview only; true = actually run `openclaw agents add`. */\n apply?: boolean;\n prefix?: string;\n /** Injectable for tests; defaults to lib/which.ts hasBinary. */\n hasBinary?: (name: string) => boolean;\n /** Injectable for tests; forwarded to planWorkspaceAgents. */\n runner?: OpenclawRunner;\n /** Injectable for tests; forwarded to planWorkspaceAgents. */\n pathExists?: (path: string) => boolean;\n}\n\nexport type RunWorkspaceAgentsEarlyReason = \"workspace-not-found\" | \"no-repos\" | \"binary-missing\";\n\nexport interface RunWorkspaceAgentsResult {\n exitCode: 0 | 1;\n preview: boolean;\n reason?: RunWorkspaceAgentsEarlyReason;\n error?: string;\n rows: WorkspaceAgentRow[];\n}\n\n/**\n * Core logic for `workspace agents <nombre>`. Preview (default) never\n * spawns anything — it only formats the commands that would run. `--apply`\n * requires the `openclaw` binary on PATH; a per-repo failure never aborts\n * the rest of the workspace (planWorkspaceAgents already guarantees this),\n * so the exit code reflects the honest partial summary.\n */\nexport function runWorkspaceAgents(\n name: string,\n options: RunWorkspaceAgentsOptions = {},\n): RunWorkspaceAgentsResult {\n const apply = Boolean(options.apply);\n const preview = !apply;\n\n let registry;\n try {\n registry = loadRegistry();\n } catch (err) {\n return { exitCode: 1, preview, error: errorMessage(err), rows: [] };\n }\n const workspace = registry[name];\n if (!workspace) {\n return {\n exitCode: 1,\n preview,\n reason: \"workspace-not-found\",\n error: `Workspace '${name}' no encontrado.`,\n rows: [],\n };\n }\n\n if (workspace.repos.length === 0) {\n return { exitCode: 0, preview, reason: \"no-repos\", rows: [] };\n }\n\n const checkBinary = options.hasBinary ?? hasBinaryDefault;\n if (apply && !checkBinary(OPENCLAW_BINARY)) {\n return { exitCode: 1, preview, reason: \"binary-missing\", rows: [] };\n }\n\n const rows = planWorkspaceAgents(workspace.repos, {\n preview,\n prefix: options.prefix ?? \"\",\n runner: options.runner,\n pathExists: options.pathExists,\n });\n const failed = rows.filter((r) => r.status === \"error\" || r.status === \"missing\").length;\n return { exitCode: failed > 0 ? 1 : 0, preview, rows };\n}\n\nconst agentsSubCommand = defineCommand({\n meta: {\n name: \"agents\",\n description: \"Crea un agente OpenClaw por repo registrado en el workspace.\",\n },\n args: {\n name: { type: \"positional\", description: \"Nombre de workspace\", required: true },\n apply: {\n type: \"boolean\",\n default: false,\n description: \"Ejecuta 'openclaw agents add' de verdad. Sin esto, todo se previsualiza.\",\n },\n prefix: {\n type: \"string\",\n description: \"Prefijo para cada nombre de agente OpenClaw.\",\n },\n },\n run({ args }) {\n const name = args.name as string;\n const apply = Boolean(args.apply);\n const prefix = (args.prefix as string | undefined) ?? \"\";\n\n const result = runWorkspaceAgents(name, { apply, prefix });\n\n if (result.reason === \"workspace-not-found\") {\n console.error(pc.red(result.error ?? `Workspace '${name}' no encontrado.`));\n process.exit(result.exitCode);\n }\n if (result.reason === \"no-repos\") {\n console.log(\"Ningún repo registrado en este workspace. Corre `argos workspace link` desde un repo.\");\n process.exit(0);\n }\n if (result.reason === \"binary-missing\") {\n console.error(\n pc.red(\n `No se encontró el binario '${OPENCLAW_BINARY}' en PATH. Este comando crea agentes OpenClaw ` +\n `y está pensado para correr donde OpenClaw está instalado (típicamente el VPS de agentes).`,\n ),\n );\n process.exit(result.exitCode);\n }\n\n const marker: Record<WorkspaceAgentStatus, string> = {\n created: pc.green(\"✓\"),\n \"would-create\": pc.yellow(\"•\"),\n exists: pc.dim(\"•\"),\n missing: pc.red(\"✗\"),\n error: pc.red(\"✗\"),\n };\n for (const row of result.rows) {\n console.log(`${marker[row.status]} ${row.name.padEnd(24)} ${pc.dim(row.status)} ${row.detail}`);\n }\n\n const failed = result.rows.filter((r) => r.status === \"error\" || r.status === \"missing\").length;\n const ok = result.rows.length - failed;\n const summary = result.preview\n ? `${ok}/${result.rows.length} ok · ${failed} fallidos`\n : `${ok}/${result.rows.length} ok · ${failed} fallidos`;\n console.log(\"\");\n console.log(result.preview ? pc.yellow(`Preview — ${summary}`) : pc.green(`Listo — ${summary}`));\n process.exit(result.exitCode);\n },\n});\n\nexport const workspaceCommand = defineCommand({\n meta: {\n name: \"workspace\",\n description: \"Registro machine-local de workspaces (match rules + flota OpenClaw).\",\n },\n subCommands: {\n link: linkSubCommand,\n show: showSubCommand,\n agents: agentsSubCommand,\n },\n});\n"],"names":["defineCommand","existsSync","pc","hasConfig","readConfig","getRemoteOriginUrl","OPENCLAW_BINARY","planWorkspaceAgents","addRemoteMatchRule","linkRepo","loadRegistry","offerMatchRule","resolveWorkspaceForRepo","hasBinary","hasBinaryDefault","errorMessage","err","Error","message","String","runWorkspaceLink","options","cwd","explicit","force","exitCode","error","config","remoteUrl","registry","resolution","configWorkspace","workspace","repoPath","kind","candidates","join","ambiguousCandidates","linkResult","name","path","matchRuleAdded","source","currentMatch","match","remotes","paths","offer","createdWorkspace","viaExplicitName","shouldPersist","identity","workspaceName","action","linkSubCommand","meta","description","args","type","required","default","run","report","process","trim","undefined","Boolean","console","red","exit","log","dim","actionLabel","added","unchanged","green","runWorkspaceShow","pathExists","rows","repos","map","r","missing","wsName","Object","entries","repo","push","showSubCommand","length","row","flag","padEnd","trimEnd","runWorkspaceAgents","apply","preview","reason","checkBinary","prefix","runner","failed","filter","status","agentsSubCommand","result","marker","created","yellow","exists","detail","ok","summary","workspaceCommand","subCommands","link","show","agents"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/commands/workspace.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { existsSync } from \"node:fs\";\nimport pc from \"picocolors\";\nimport { hasConfig, readConfig, type ArgosConfig } from \"../lib/config.js\";\nimport { getRemoteOriginUrl } from \"../lib/git.js\";\nimport {\n OPENCLAW_BINARY,\n planWorkspaceAgents,\n type OpenclawRunner,\n type WorkspaceAgentRow,\n type WorkspaceAgentStatus,\n} from \"../lib/openclaw-agents.js\";\nimport { isInteractive, clackPrompter, type Prompter } from \"../lib/prompter.js\";\nimport {\n addRemoteMatchRule,\n linkRepo,\n loadRegistry,\n offerMatchRule,\n resolveWorkspaceForRepo,\n WorkspaceNameCollisionError,\n type LinkAction,\n} from \"../lib/workspaces.js\";\nimport { hasBinary as hasBinaryDefault } from \"../lib/which.js\";\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n// --- link --------------------------------------------------------------\n\nexport interface WorkspaceLinkOptions {\n cwd: string;\n /** Explicit workspace name passed on the command line, if any. */\n explicit?: string;\n /** Overwrite a name collision (same config.name, different physical repo) instead of refusing. */\n force?: boolean;\n}\n\nexport interface WorkspaceLinkCollision {\n workspaceName: string;\n repoName: string;\n oldPath: string;\n newPath: string;\n}\n\nexport interface WorkspaceLinkReport {\n exitCode: 0 | 1;\n error?: string;\n ambiguousCandidates?: string[];\n workspaceName?: string;\n action?: LinkAction;\n createdWorkspace?: boolean;\n /** Remote identity persisted as a new match rule, if the offer fired. */\n matchRuleAdded?: string;\n /**\n * Populated instead of a bare `error` string when `linkRepo` refused with\n * `WorkspaceNameCollisionError` (see lib/workspaces.ts) — lets the\n * interactive layer offer overwrite/cancel (the equivalent of `--force`)\n * without having to string-parse `error`. Additive: `error` is still set\n * alongside it, so any caller only reading `error` behaves exactly as\n * before.\n */\n collision?: WorkspaceLinkCollision;\n}\n\n/**\n * Core, testable implementation of `argos workspace link [nombre]`: resolves\n * which workspace the repo at `cwd` belongs to (explicit name > config >\n * match rules) and registers it. When a brand-new workspace was created via\n * an explicit name and the repo has a remote, teaches the registry that\n * remote as a match rule so future repos under the same identity auto-link.\n */\nexport function runWorkspaceLink(options: WorkspaceLinkOptions): WorkspaceLinkReport {\n const { cwd, explicit, force } = options;\n\n if (!hasConfig(cwd)) {\n return {\n exitCode: 1,\n error: \"No se encontró argos.config.json en este directorio — corre `argos adopt` primero.\",\n };\n }\n\n let config: ArgosConfig;\n try {\n config = readConfig(cwd);\n } catch (err) {\n return { exitCode: 1, error: `argos.config.json inválido — ${errorMessage(err)}` };\n }\n\n const remoteUrl = getRemoteOriginUrl(cwd);\n let registry;\n try {\n registry = loadRegistry();\n } catch (err) {\n return { exitCode: 1, error: errorMessage(err) };\n }\n const resolution = resolveWorkspaceForRepo(registry, {\n explicit,\n configWorkspace: config.workspace,\n remoteUrl,\n repoPath: cwd,\n });\n\n if (resolution.kind === \"unresolved\") {\n return {\n exitCode: 1,\n error:\n \"No se pudo resolver un workspace para este repo — pasá un nombre explícito: \" +\n \"`argos workspace link <nombre>`.\",\n };\n }\n if (resolution.kind === \"ambiguous\") {\n return {\n exitCode: 1,\n error: `Match ambiguo entre workspaces (${resolution.candidates.join(\", \")}) — pasá un nombre explícito.`,\n ambiguousCandidates: resolution.candidates,\n };\n }\n\n let linkResult;\n try {\n linkResult = linkRepo(resolution.name, { name: config.name, path: cwd }, { force });\n } catch (err) {\n if (err instanceof WorkspaceNameCollisionError) {\n return {\n exitCode: 1,\n error: errorMessage(err),\n collision: {\n workspaceName: err.workspaceName,\n repoName: err.repoName,\n oldPath: err.oldPath,\n newPath: err.newPath,\n },\n };\n }\n return { exitCode: 1, error: errorMessage(err) };\n }\n\n let matchRuleAdded: string | undefined;\n if (resolution.source === \"explicit\") {\n const currentMatch = loadRegistry()[resolution.name]?.match ?? { remotes: [], paths: [] };\n const offer = offerMatchRule({\n createdWorkspace: linkResult.createdWorkspace,\n viaExplicitName: true,\n remoteUrl,\n currentMatch,\n });\n if (offer.shouldPersist && offer.identity) {\n addRemoteMatchRule(resolution.name, offer.identity);\n matchRuleAdded = offer.identity;\n }\n }\n\n return {\n exitCode: 0,\n workspaceName: resolution.name,\n action: linkResult.action,\n createdWorkspace: linkResult.createdWorkspace,\n matchRuleAdded,\n };\n}\n\nexport interface WorkspaceLinkInteractiveOptions extends WorkspaceLinkOptions {\n /** Injectable for tests; defaults to the real `@clack/prompts`-backed prompter. */\n prompter?: Prompter;\n}\n\n/**\n * Interactive layer over `runWorkspaceLink` (spec 0004 F5 \"argos workspace\n * link\"). A pure additive wrapper — the core `runWorkspaceLink` never\n * changes behavior or contract. Without a real TTY this delegates to\n * `runWorkspaceLink(options)` unchanged (there's no `--yes` on this command:\n * absence of a real TTY is itself sufficient gate, per spec 0004). With a\n * TTY: an ambiguous match-rule resolution is resolved via `select` instead\n * of erroring, and a name collision (`WorkspaceNameCollisionError`) is\n * offered as an overwrite/cancel prompt — the interactive equivalent of\n * `--force`, which stays the non-interactive escape hatch unchanged.\n */\nexport async function runWorkspaceLinkInteractive(\n options: WorkspaceLinkInteractiveOptions,\n): Promise<WorkspaceLinkReport> {\n if (!isInteractive({})) {\n return runWorkspaceLink(options);\n }\n\n const prompter = options.prompter ?? clackPrompter;\n const { cwd, explicit, force } = options;\n\n // Resolve ambiguity read-only first, mirroring runWorkspaceLink's own\n // resolution chain, before ever calling the writing core — an explicit\n // name (whether passed on the CLI or picked here) always short-circuits\n // resolveWorkspaceForRepo straight to \"resolved\", so no duplicate\n // ambiguity handling happens inside the core call below.\n let effectiveExplicit = explicit;\n if (!explicit) {\n if (!hasConfig(cwd)) return runWorkspaceLink(options); // identical error path, no prompts needed\n let config: ArgosConfig;\n try {\n config = readConfig(cwd);\n } catch {\n return runWorkspaceLink(options); // identical error path\n }\n const remoteUrl = getRemoteOriginUrl(cwd);\n let registry: ReturnType<typeof loadRegistry>;\n try {\n registry = loadRegistry();\n } catch {\n return runWorkspaceLink(options); // identical error path\n }\n const resolution = resolveWorkspaceForRepo(registry, { configWorkspace: config.workspace, remoteUrl, repoPath: cwd });\n if (resolution.kind === \"ambiguous\") {\n const chosen = await prompter.select<string>({\n message: `Match ambiguo entre workspaces (${resolution.source}) — elegí uno:`,\n options: resolution.candidates.map((c) => ({ value: c })),\n });\n if (prompter.isCancel(chosen)) {\n prompter.cancel(\"argos workspace link cancelado — no se tocó nada.\");\n return { exitCode: 1, error: \"argos workspace link cancelado por el usuario.\" };\n }\n effectiveExplicit = chosen;\n }\n }\n\n let report = runWorkspaceLink({ cwd, explicit: effectiveExplicit, force });\n\n if (report.collision && !force) {\n const overwrite = await prompter.confirm({\n message:\n `El repo '${report.collision.repoName}' ya está registrado en el workspace '${report.collision.workspaceName}' ` +\n `apuntando a otro path.\\n actual: ${report.collision.oldPath}\\n nuevo: ${report.collision.newPath}\\n` +\n \"¿Sobrescribir con el path nuevo?\",\n initialValue: false,\n });\n if (prompter.isCancel(overwrite) || !overwrite) {\n prompter.cancel(\"argos workspace link cancelado — no se tocó nada.\");\n return report; // original failed report — nothing further was touched\n }\n report = runWorkspaceLink({ cwd, explicit: effectiveExplicit, force: true });\n }\n\n return report;\n}\n\nconst linkSubCommand = defineCommand({\n meta: {\n name: \"link\",\n description: \"Vincula el repo actual a un workspace (lo crea si no existe).\",\n },\n args: {\n name: { type: \"positional\", description: \"Nombre de workspace explícito\", required: false },\n force: {\n type: \"boolean\",\n default: false,\n description: \"Sobrescribe un choque de nombre (mismo config.name, repo físico distinto).\",\n },\n },\n async run({ args }) {\n const report = await runWorkspaceLinkInteractive({\n cwd: process.cwd(),\n explicit: (args.name as string | undefined)?.trim() || undefined,\n force: Boolean(args.force),\n });\n\n if (report.error) {\n console.error(pc.red(report.error));\n process.exit(report.exitCode);\n }\n\n if (report.createdWorkspace) {\n console.log(pc.dim(`Workspace '${report.workspaceName}' creado.`));\n }\n const actionLabel: Record<LinkAction, string> = {\n added: \"registrado\",\n \"updated-path\": \"path actualizado\",\n unchanged: \"sin cambios\",\n };\n console.log(\n `${pc.green(\"✓\")} repo ${actionLabel[report.action as LinkAction]} en workspace '${report.workspaceName}'.`,\n );\n if (report.matchRuleAdded) {\n console.log(\n pc.dim(`El remote '${report.matchRuleAdded}' se guardó como match rule del workspace.`),\n );\n }\n process.exit(report.exitCode);\n },\n});\n\n// --- show ----------------------------------------------------------------\n\nexport interface WorkspaceShowRepoRow {\n workspace: string;\n name: string;\n path: string;\n missing: boolean;\n}\n\nexport interface WorkspaceShowReport {\n rows: WorkspaceShowRepoRow[];\n exitCode: 0 | 1;\n error?: string;\n}\n\nexport interface WorkspaceShowOptions {\n /** Injectable for tests; defaults to node:fs existsSync. */\n pathExists?: (path: string) => boolean;\n}\n\n/** Core, testable implementation of `argos workspace show [nombre]`. */\nexport function runWorkspaceShow(name?: string, options: WorkspaceShowOptions = {}): WorkspaceShowReport {\n const pathExists = options.pathExists ?? existsSync;\n let registry;\n try {\n registry = loadRegistry();\n } catch (err) {\n return { rows: [], exitCode: 1, error: errorMessage(err) };\n }\n\n if (name) {\n const workspace = registry[name];\n if (!workspace) {\n return { rows: [], exitCode: 1, error: `Workspace '${name}' no encontrado.` };\n }\n const rows = workspace.repos.map((r) => ({\n workspace: name,\n name: r.name,\n path: r.path,\n missing: !pathExists(r.path),\n }));\n return { rows, exitCode: 0 };\n }\n\n const rows: WorkspaceShowRepoRow[] = [];\n for (const [wsName, workspace] of Object.entries(registry)) {\n for (const repo of workspace.repos) {\n rows.push({ workspace: wsName, name: repo.name, path: repo.path, missing: !pathExists(repo.path) });\n }\n }\n return { rows, exitCode: 0 };\n}\n\nconst showSubCommand = defineCommand({\n meta: {\n name: \"show\",\n description: \"Muestra los repos registrados en uno o todos los workspaces.\",\n },\n args: {\n name: { type: \"positional\", description: \"Nombre de workspace (default: todos)\", required: false },\n },\n run({ args }) {\n const name = (args.name as string | undefined)?.trim() || undefined;\n const report = runWorkspaceShow(name);\n\n if (report.error) {\n console.error(pc.red(report.error));\n process.exit(report.exitCode);\n }\n\n if (report.rows.length === 0) {\n console.log(\"Ningún repo registrado todavía. Corre `argos workspace link` desde un repo.\");\n process.exit(0);\n }\n\n for (const row of report.rows) {\n const flag = row.missing ? pc.red(\"(path inexistente)\") : \"\";\n console.log(`${pc.dim(row.workspace.padEnd(16))} ${row.name.padEnd(24)} ${row.path} ${flag}`.trimEnd());\n }\n process.exit(0);\n },\n});\n\n// --- agents ----------------------------------------------------------------\n\nexport interface RunWorkspaceAgentsOptions {\n /** false (default) = preview only; true = actually run `openclaw agents add`. */\n apply?: boolean;\n prefix?: string;\n /** Injectable for tests; defaults to lib/which.ts hasBinary. */\n hasBinary?: (name: string) => boolean;\n /** Injectable for tests; forwarded to planWorkspaceAgents. */\n runner?: OpenclawRunner;\n /** Injectable for tests; forwarded to planWorkspaceAgents. */\n pathExists?: (path: string) => boolean;\n}\n\nexport type RunWorkspaceAgentsEarlyReason = \"workspace-not-found\" | \"no-repos\" | \"binary-missing\";\n\nexport interface RunWorkspaceAgentsResult {\n exitCode: 0 | 1;\n preview: boolean;\n reason?: RunWorkspaceAgentsEarlyReason;\n error?: string;\n rows: WorkspaceAgentRow[];\n}\n\n/**\n * Core logic for `workspace agents <nombre>`. Preview (default) never\n * spawns anything — it only formats the commands that would run. `--apply`\n * requires the `openclaw` binary on PATH; a per-repo failure never aborts\n * the rest of the workspace (planWorkspaceAgents already guarantees this),\n * so the exit code reflects the honest partial summary.\n */\nexport function runWorkspaceAgents(\n name: string,\n options: RunWorkspaceAgentsOptions = {},\n): RunWorkspaceAgentsResult {\n const apply = Boolean(options.apply);\n const preview = !apply;\n\n let registry;\n try {\n registry = loadRegistry();\n } catch (err) {\n return { exitCode: 1, preview, error: errorMessage(err), rows: [] };\n }\n const workspace = registry[name];\n if (!workspace) {\n return {\n exitCode: 1,\n preview,\n reason: \"workspace-not-found\",\n error: `Workspace '${name}' no encontrado.`,\n rows: [],\n };\n }\n\n if (workspace.repos.length === 0) {\n return { exitCode: 0, preview, reason: \"no-repos\", rows: [] };\n }\n\n const checkBinary = options.hasBinary ?? hasBinaryDefault;\n if (apply && !checkBinary(OPENCLAW_BINARY)) {\n return { exitCode: 1, preview, reason: \"binary-missing\", rows: [] };\n }\n\n const rows = planWorkspaceAgents(workspace.repos, {\n preview,\n prefix: options.prefix ?? \"\",\n runner: options.runner,\n pathExists: options.pathExists,\n });\n const failed = rows.filter((r) => r.status === \"error\" || r.status === \"missing\").length;\n return { exitCode: failed > 0 ? 1 : 0, preview, rows };\n}\n\nconst agentsSubCommand = defineCommand({\n meta: {\n name: \"agents\",\n description: \"Crea un agente OpenClaw por repo registrado en el workspace.\",\n },\n args: {\n name: { type: \"positional\", description: \"Nombre de workspace\", required: true },\n apply: {\n type: \"boolean\",\n default: false,\n description: \"Ejecuta 'openclaw agents add' de verdad. Sin esto, todo se previsualiza.\",\n },\n prefix: {\n type: \"string\",\n description: \"Prefijo para cada nombre de agente OpenClaw.\",\n },\n },\n run({ args }) {\n const name = args.name as string;\n const apply = Boolean(args.apply);\n const prefix = (args.prefix as string | undefined) ?? \"\";\n\n const result = runWorkspaceAgents(name, { apply, prefix });\n\n if (result.reason === \"workspace-not-found\") {\n console.error(pc.red(result.error ?? `Workspace '${name}' no encontrado.`));\n process.exit(result.exitCode);\n }\n if (result.reason === \"no-repos\") {\n console.log(\"Ningún repo registrado en este workspace. Corre `argos workspace link` desde un repo.\");\n process.exit(0);\n }\n if (result.reason === \"binary-missing\") {\n console.error(\n pc.red(\n `No se encontró el binario '${OPENCLAW_BINARY}' en PATH. Este comando crea agentes OpenClaw ` +\n `y está pensado para correr donde OpenClaw está instalado (típicamente el VPS de agentes).`,\n ),\n );\n process.exit(result.exitCode);\n }\n\n const marker: Record<WorkspaceAgentStatus, string> = {\n created: pc.green(\"✓\"),\n \"would-create\": pc.yellow(\"•\"),\n exists: pc.dim(\"•\"),\n missing: pc.red(\"✗\"),\n error: pc.red(\"✗\"),\n };\n for (const row of result.rows) {\n console.log(`${marker[row.status]} ${row.name.padEnd(24)} ${pc.dim(row.status)} ${row.detail}`);\n }\n\n const failed = result.rows.filter((r) => r.status === \"error\" || r.status === \"missing\").length;\n const ok = result.rows.length - failed;\n const summary = result.preview\n ? `${ok}/${result.rows.length} ok · ${failed} fallidos`\n : `${ok}/${result.rows.length} ok · ${failed} fallidos`;\n console.log(\"\");\n console.log(result.preview ? pc.yellow(`Preview — ${summary}`) : pc.green(`Listo — ${summary}`));\n process.exit(result.exitCode);\n },\n});\n\nexport const workspaceCommand = defineCommand({\n meta: {\n name: \"workspace\",\n description: \"Registro machine-local de workspaces (match rules + flota OpenClaw).\",\n },\n subCommands: {\n link: linkSubCommand,\n show: showSubCommand,\n agents: agentsSubCommand,\n },\n});\n"],"names":["defineCommand","existsSync","pc","hasConfig","readConfig","getRemoteOriginUrl","OPENCLAW_BINARY","planWorkspaceAgents","isInteractive","clackPrompter","addRemoteMatchRule","linkRepo","loadRegistry","offerMatchRule","resolveWorkspaceForRepo","WorkspaceNameCollisionError","hasBinary","hasBinaryDefault","errorMessage","err","Error","message","String","runWorkspaceLink","options","cwd","explicit","force","exitCode","error","config","remoteUrl","registry","resolution","configWorkspace","workspace","repoPath","kind","candidates","join","ambiguousCandidates","linkResult","name","path","collision","workspaceName","repoName","oldPath","newPath","matchRuleAdded","source","currentMatch","match","remotes","paths","offer","createdWorkspace","viaExplicitName","shouldPersist","identity","action","runWorkspaceLinkInteractive","prompter","effectiveExplicit","chosen","select","map","c","value","isCancel","cancel","report","overwrite","confirm","initialValue","linkSubCommand","meta","description","args","type","required","default","run","process","trim","undefined","Boolean","console","red","exit","log","dim","actionLabel","added","unchanged","green","runWorkspaceShow","pathExists","rows","repos","r","missing","wsName","Object","entries","repo","push","showSubCommand","length","row","flag","padEnd","trimEnd","runWorkspaceAgents","apply","preview","reason","checkBinary","prefix","runner","failed","filter","status","agentsSubCommand","result","marker","created","yellow","exists","detail","ok","summary","workspaceCommand","subCommands","link","show","agents"],"mappings":"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"}
@@ -1,9 +1,11 @@
1
1
  /**
2
- * Ordered ids of the 5 managed CLAUDE.md blocks. Order defines the order the
2
+ * Ordered ids of the 6 managed CLAUDE.md blocks. Order defines the order the
3
3
  * blocks appear in the rendered file. Ids are the basenames (without `.md`)
4
- * of `assets/managed/*.md`.
4
+ * of `assets/managed/*.md`. This array is the single shared source for both
5
+ * `init.ts` (installs/injects each block, in this order) and `doctor.ts`
6
+ * (audits presence/version drift for each block) — never duplicate it.
5
7
  */
6
- export declare const MANAGED_BLOCK_IDS: readonly ["identidad", "formato-respuesta", "aterrizaje", "orquestacion", "operaciones-seguras"];
8
+ export declare const MANAGED_BLOCK_IDS: readonly ["identidad", "formato-respuesta", "disciplina-skills", "aterrizaje", "orquestacion", "operaciones-seguras"];
7
9
  /**
8
10
  * Resolve the Argos assets directory (`managed/`, `agents/`, `skills/`,
9
11
  * `output-styles/`) relative to the currently running module. See
@@ -1 +1 @@
1
- {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/lib/assets.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,kGAMpB,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAE1E;AAED,+DAA+D;AAC/D,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAEzE;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAKxD;AAED,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAKxD;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAY3E"}
1
+ {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/lib/assets.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,uHAOpB,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,MAAwB,GAAG,MAAM,CAE1E;AAED,+DAA+D;AAC/D,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAEzE;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAKxD;AAED,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAKxD;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAY3E"}
@@ -2,12 +2,15 @@ import { readdirSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { resolvePackageRoot } from "./package-root.js";
4
4
  /**
5
- * Ordered ids of the 5 managed CLAUDE.md blocks. Order defines the order the
5
+ * Ordered ids of the 6 managed CLAUDE.md blocks. Order defines the order the
6
6
  * blocks appear in the rendered file. Ids are the basenames (without `.md`)
7
- * of `assets/managed/*.md`.
7
+ * of `assets/managed/*.md`. This array is the single shared source for both
8
+ * `init.ts` (installs/injects each block, in this order) and `doctor.ts`
9
+ * (audits presence/version drift for each block) — never duplicate it.
8
10
  */ export const MANAGED_BLOCK_IDS = [
9
11
  "identidad",
10
12
  "formato-respuesta",
13
+ "disciplina-skills",
11
14
  "aterrizaje",
12
15
  "orquestacion",
13
16
  "operaciones-seguras"
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/assets.ts"],"sourcesContent":["import { readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { resolvePackageRoot } from \"./package-root.js\";\n\n/**\n * Ordered ids of the 5 managed CLAUDE.md blocks. Order defines the order the\n * blocks appear in the rendered file. Ids are the basenames (without `.md`)\n * of `assets/managed/*.md`.\n */\nexport const MANAGED_BLOCK_IDS = [\n \"identidad\",\n \"formato-respuesta\",\n \"aterrizaje\",\n \"orquestacion\",\n \"operaciones-seguras\",\n] as const;\n\n/**\n * Resolve the Argos assets directory (`managed/`, `agents/`, `skills/`,\n * `output-styles/`) relative to the currently running module. See\n * `resolvePackageRoot` for the resolution strategy.\n */\nexport function resolveAssetsDir(fromUrl: string = import.meta.url): string {\n return join(resolvePackageRoot(fromUrl), \"assets\");\n}\n\n/** Read a text asset file relative to the assets directory. */\nexport function readAsset(assetsDir: string, ...relPath: string[]): string {\n return readFileSync(join(assetsDir, ...relPath), \"utf-8\");\n}\n\n/** List agent ids (basenames without `.md`) found under `assets/agents/`. */\nexport function listAgentIds(assetsDir: string): string[] {\n return readdirSync(join(assetsDir, \"agents\"))\n .filter((f) => f.endsWith(\".md\"))\n .map((f) => f.slice(0, -3))\n .sort();\n}\n\n/** List skill ids (directory names) found under `assets/skills/`. */\nexport function listSkillIds(assetsDir: string): string[] {\n return readdirSync(join(assetsDir, \"skills\"), { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name)\n .sort();\n}\n\n/**\n * Recursively list every file shipped under a skill's asset directory\n * (`assets/skills/<skillId>/`), as relative paths from that directory with\n * forward slashes regardless of platform (e.g. `SKILL.md`,\n * `references/core.md`, `phases/0-product.md`). Sorted for deterministic\n * output. Some skills are a single `SKILL.md`; ~15 also ship load-bearing\n * subdirectories (`references/`, `phases/`, `assets/`) that this walks too —\n * without it, only `SKILL.md` would ever reach the user's `~/.claude/skills`.\n */\nexport function listSkillFiles(assetsDir: string, skillId: string): string[] {\n const root = join(assetsDir, \"skills\", skillId);\n const out: string[] = [];\n const walk = (dir: string, prefix: string) => {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const rel = prefix ? `${prefix}/${entry.name}` : entry.name;\n if (entry.isDirectory()) walk(join(dir, entry.name), rel);\n else out.push(rel);\n }\n };\n walk(root, \"\");\n return out.sort();\n}\n"],"names":["readdirSync","readFileSync","join","resolvePackageRoot","MANAGED_BLOCK_IDS","resolveAssetsDir","fromUrl","url","readAsset","assetsDir","relPath","listAgentIds","filter","f","endsWith","map","slice","sort","listSkillIds","withFileTypes","entry","isDirectory","name","listSkillFiles","skillId","root","out","walk","dir","prefix","rel","push"],"mappings":"AAAA,SAASA,WAAW,EAAEC,YAAY,QAAQ,UAAU;AACpD,SAASC,IAAI,QAAQ,YAAY;AACjC,SAASC,kBAAkB,QAAQ,oBAAoB;AAEvD;;;;CAIC,GACD,OAAO,MAAMC,oBAAoB;IAC/B;IACA;IACA;IACA;IACA;CACD,CAAU;AAEX;;;;CAIC,GACD,OAAO,SAASC,iBAAiBC,UAAkB,YAAYC,GAAG;IAChE,OAAOL,KAAKC,mBAAmBG,UAAU;AAC3C;AAEA,6DAA6D,GAC7D,OAAO,SAASE,UAAUC,SAAiB,EAAE,GAAGC,OAAiB;IAC/D,OAAOT,aAAaC,KAAKO,cAAcC,UAAU;AACnD;AAEA,2EAA2E,GAC3E,OAAO,SAASC,aAAaF,SAAiB;IAC5C,OAAOT,YAAYE,KAAKO,WAAW,WAChCG,MAAM,CAAC,CAACC,IAAMA,EAAEC,QAAQ,CAAC,QACzBC,GAAG,CAAC,CAACF,IAAMA,EAAEG,KAAK,CAAC,GAAG,CAAC,IACvBC,IAAI;AACT;AAEA,mEAAmE,GACnE,OAAO,SAASC,aAAaT,SAAiB;IAC5C,OAAOT,YAAYE,KAAKO,WAAW,WAAW;QAAEU,eAAe;IAAK,GACjEP,MAAM,CAAC,CAACQ,QAAUA,MAAMC,WAAW,IACnCN,GAAG,CAAC,CAACK,QAAUA,MAAME,IAAI,EACzBL,IAAI;AACT;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASM,eAAed,SAAiB,EAAEe,OAAe;IAC/D,MAAMC,OAAOvB,KAAKO,WAAW,UAAUe;IACvC,MAAME,MAAgB,EAAE;IACxB,MAAMC,OAAO,CAACC,KAAaC;QACzB,KAAK,MAAMT,SAASpB,YAAY4B,KAAK;YAAET,eAAe;QAAK,GAAI;YAC7D,MAAMW,MAAMD,SAAS,GAAGA,OAAO,CAAC,EAAET,MAAME,IAAI,EAAE,GAAGF,MAAME,IAAI;YAC3D,IAAIF,MAAMC,WAAW,IAAIM,KAAKzB,KAAK0B,KAAKR,MAAME,IAAI,GAAGQ;iBAChDJ,IAAIK,IAAI,CAACD;QAChB;IACF;IACAH,KAAKF,MAAM;IACX,OAAOC,IAAIT,IAAI;AACjB"}
1
+ {"version":3,"sources":["../../src/lib/assets.ts"],"sourcesContent":["import { readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { resolvePackageRoot } from \"./package-root.js\";\n\n/**\n * Ordered ids of the 6 managed CLAUDE.md blocks. Order defines the order the\n * blocks appear in the rendered file. Ids are the basenames (without `.md`)\n * of `assets/managed/*.md`. This array is the single shared source for both\n * `init.ts` (installs/injects each block, in this order) and `doctor.ts`\n * (audits presence/version drift for each block) — never duplicate it.\n */\nexport const MANAGED_BLOCK_IDS = [\n \"identidad\",\n \"formato-respuesta\",\n \"disciplina-skills\",\n \"aterrizaje\",\n \"orquestacion\",\n \"operaciones-seguras\",\n] as const;\n\n/**\n * Resolve the Argos assets directory (`managed/`, `agents/`, `skills/`,\n * `output-styles/`) relative to the currently running module. See\n * `resolvePackageRoot` for the resolution strategy.\n */\nexport function resolveAssetsDir(fromUrl: string = import.meta.url): string {\n return join(resolvePackageRoot(fromUrl), \"assets\");\n}\n\n/** Read a text asset file relative to the assets directory. */\nexport function readAsset(assetsDir: string, ...relPath: string[]): string {\n return readFileSync(join(assetsDir, ...relPath), \"utf-8\");\n}\n\n/** List agent ids (basenames without `.md`) found under `assets/agents/`. */\nexport function listAgentIds(assetsDir: string): string[] {\n return readdirSync(join(assetsDir, \"agents\"))\n .filter((f) => f.endsWith(\".md\"))\n .map((f) => f.slice(0, -3))\n .sort();\n}\n\n/** List skill ids (directory names) found under `assets/skills/`. */\nexport function listSkillIds(assetsDir: string): string[] {\n return readdirSync(join(assetsDir, \"skills\"), { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name)\n .sort();\n}\n\n/**\n * Recursively list every file shipped under a skill's asset directory\n * (`assets/skills/<skillId>/`), as relative paths from that directory with\n * forward slashes regardless of platform (e.g. `SKILL.md`,\n * `references/core.md`, `phases/0-product.md`). Sorted for deterministic\n * output. Some skills are a single `SKILL.md`; ~15 also ship load-bearing\n * subdirectories (`references/`, `phases/`, `assets/`) that this walks too —\n * without it, only `SKILL.md` would ever reach the user's `~/.claude/skills`.\n */\nexport function listSkillFiles(assetsDir: string, skillId: string): string[] {\n const root = join(assetsDir, \"skills\", skillId);\n const out: string[] = [];\n const walk = (dir: string, prefix: string) => {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const rel = prefix ? `${prefix}/${entry.name}` : entry.name;\n if (entry.isDirectory()) walk(join(dir, entry.name), rel);\n else out.push(rel);\n }\n };\n walk(root, \"\");\n return out.sort();\n}\n"],"names":["readdirSync","readFileSync","join","resolvePackageRoot","MANAGED_BLOCK_IDS","resolveAssetsDir","fromUrl","url","readAsset","assetsDir","relPath","listAgentIds","filter","f","endsWith","map","slice","sort","listSkillIds","withFileTypes","entry","isDirectory","name","listSkillFiles","skillId","root","out","walk","dir","prefix","rel","push"],"mappings":"AAAA,SAASA,WAAW,EAAEC,YAAY,QAAQ,UAAU;AACpD,SAASC,IAAI,QAAQ,YAAY;AACjC,SAASC,kBAAkB,QAAQ,oBAAoB;AAEvD;;;;;;CAMC,GACD,OAAO,MAAMC,oBAAoB;IAC/B;IACA;IACA;IACA;IACA;IACA;CACD,CAAU;AAEX;;;;CAIC,GACD,OAAO,SAASC,iBAAiBC,UAAkB,YAAYC,GAAG;IAChE,OAAOL,KAAKC,mBAAmBG,UAAU;AAC3C;AAEA,6DAA6D,GAC7D,OAAO,SAASE,UAAUC,SAAiB,EAAE,GAAGC,OAAiB;IAC/D,OAAOT,aAAaC,KAAKO,cAAcC,UAAU;AACnD;AAEA,2EAA2E,GAC3E,OAAO,SAASC,aAAaF,SAAiB;IAC5C,OAAOT,YAAYE,KAAKO,WAAW,WAChCG,MAAM,CAAC,CAACC,IAAMA,EAAEC,QAAQ,CAAC,QACzBC,GAAG,CAAC,CAACF,IAAMA,EAAEG,KAAK,CAAC,GAAG,CAAC,IACvBC,IAAI;AACT;AAEA,mEAAmE,GACnE,OAAO,SAASC,aAAaT,SAAiB;IAC5C,OAAOT,YAAYE,KAAKO,WAAW,WAAW;QAAEU,eAAe;IAAK,GACjEP,MAAM,CAAC,CAACQ,QAAUA,MAAMC,WAAW,IACnCN,GAAG,CAAC,CAACK,QAAUA,MAAME,IAAI,EACzBL,IAAI;AACT;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASM,eAAed,SAAiB,EAAEe,OAAe;IAC/D,MAAMC,OAAOvB,KAAKO,WAAW,UAAUe;IACvC,MAAME,MAAgB,EAAE;IACxB,MAAMC,OAAO,CAACC,KAAaC;QACzB,KAAK,MAAMT,SAASpB,YAAY4B,KAAK;YAAET,eAAe;QAAK,GAAI;YAC7D,MAAMW,MAAMD,SAAS,GAAGA,OAAO,CAAC,EAAET,MAAME,IAAI,EAAE,GAAGF,MAAME,IAAI;YAC3D,IAAIF,MAAMC,WAAW,IAAIM,KAAKzB,KAAK0B,KAAKR,MAAME,IAAI,GAAGQ;iBAChDJ,IAAIK,IAAI,CAACD;QAChB;IACF;IACAH,KAAKF,MAAM;IACX,OAAOC,IAAIT,IAAI;AACjB"}
@@ -8,15 +8,27 @@ export declare function hasArgosFileMarker(content: string): boolean;
8
8
  * Falls back to prefixing the marker when there is no frontmatter.
9
9
  */
10
10
  export declare function withFileMarker(content: string, version: string): string;
11
- export type FileStatus = "created" | "updated" | "unchanged" | "skipped-foreign";
11
+ export type FileStatus = "created" | "updated" | "unchanged" | "skipped-foreign" | "overwritten-foreign";
12
+ export interface WriteManagedFileOptions {
13
+ /**
14
+ * `argos init --force`: when the destination exists WITHOUT the
15
+ * ownership marker (foreign), overwrite it with the motor version instead
16
+ * of skipping it. The marker lands with this write, so a subsequent
17
+ * (non-forced) run treats the file as owned from then on. Default `false`
18
+ * — the pre-existing skip-foreign behavior.
19
+ */
20
+ force?: boolean;
21
+ }
12
22
  /**
13
23
  * Write a full-file Argos-owned asset (agent, skill, output-style) to
14
24
  * `destPath`, honoring the ownership marker policy:
15
25
  * - absent → write it (created)
16
26
  * - present with the argos:file marker → overwrite (updated/unchanged)
17
- * - present without the marker (foreign) → never touch it (skipped-foreign)
27
+ * - present without the marker (foreign) → never touch it (skipped-foreign),
28
+ * unless `options.force` is set, in which case it's overwritten
29
+ * (overwritten-foreign) and stamped with the marker.
18
30
  */
19
- export declare function writeManagedFile(destPath: string, sourceContent: string, version: string): FileStatus;
31
+ export declare function writeManagedFile(destPath: string, sourceContent: string, version: string, options?: WriteManagedFileOptions): FileStatus;
20
32
  /** True when `content` carries a shell-comment `# argos:file` ownership marker (any version). */
21
33
  export declare function hasArgosShellFileMarker(content: string): boolean;
22
34
  /**
@@ -29,7 +41,9 @@ export declare function hasArgosShellFileMarker(content: string): boolean;
29
41
  * the result executable (0o755). Same ownership policy as `writeManagedFile`:
30
42
  * - absent → write it (created)
31
43
  * - present with the shell marker → overwrite (updated/unchanged)
32
- * - present without the marker (foreign) → never touch it (skipped-foreign)
44
+ * - present without the marker (foreign) → never touch it (skipped-foreign),
45
+ * unless `options.force` is set, in which case it's overwritten
46
+ * (overwritten-foreign) and stamped with the marker.
33
47
  */
34
- export declare function writeManagedShellFile(destPath: string, sourceContent: string, version: string): FileStatus;
48
+ export declare function writeManagedShellFile(destPath: string, sourceContent: string, version: string, options?: WriteManagedFileOptions): FileStatus;
35
49
  //# sourceMappingURL=managed-files.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"managed-files.d.ts","sourceRoot":"","sources":["../../src/lib/managed-files.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,yBAAyB,sBAAsB,CAAC;AAM7D,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAOvE;AAED,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,iBAAiB,CAAC;AAEjF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAerG;AAED,iGAAiG;AACjG,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CA6B1G"}
1
+ {"version":3,"file":"managed-files.d.ts","sourceRoot":"","sources":["../../src/lib/managed-files.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,yBAAyB,sBAAsB,CAAC;AAM7D,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAOvE;AAED,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,iBAAiB,GAAG,qBAAqB,CAAC;AAEzG,MAAM,WAAW,uBAAuB;IACtC;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,uBAA4B,GACpC,UAAU,CAmBZ;AAED,iGAAiG;AACjG,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,uBAA4B,GACpC,UAAU,CAiCZ"}
@@ -35,8 +35,10 @@ function fileMarker(version) {
35
35
  * `destPath`, honoring the ownership marker policy:
36
36
  * - absent → write it (created)
37
37
  * - present with the argos:file marker → overwrite (updated/unchanged)
38
- * - present without the marker (foreign) → never touch it (skipped-foreign)
39
- */ export function writeManagedFile(destPath, sourceContent, version) {
38
+ * - present without the marker (foreign) → never touch it (skipped-foreign),
39
+ * unless `options.force` is set, in which case it's overwritten
40
+ * (overwritten-foreign) and stamped with the marker.
41
+ */ export function writeManagedFile(destPath, sourceContent, version, options = {}) {
40
42
  const finalContent = withFileMarker(sourceContent, version);
41
43
  if (!existsSync(destPath)) {
42
44
  mkdirSync(dirname(destPath), {
@@ -46,7 +48,11 @@ function fileMarker(version) {
46
48
  return "created";
47
49
  }
48
50
  const current = readFileSync(destPath, "utf-8");
49
- if (!hasArgosFileMarker(current)) return "skipped-foreign";
51
+ if (!hasArgosFileMarker(current)) {
52
+ if (!options.force) return "skipped-foreign";
53
+ writeFileAtomic(destPath, finalContent);
54
+ return "overwritten-foreign";
55
+ }
50
56
  if (current === finalContent) return "unchanged";
51
57
  writeFileAtomic(destPath, finalContent);
52
58
  return "updated";
@@ -64,8 +70,10 @@ function fileMarker(version) {
64
70
  * the result executable (0o755). Same ownership policy as `writeManagedFile`:
65
71
  * - absent → write it (created)
66
72
  * - present with the shell marker → overwrite (updated/unchanged)
67
- * - present without the marker (foreign) → never touch it (skipped-foreign)
68
- */ export function writeManagedShellFile(destPath, sourceContent, version) {
73
+ * - present without the marker (foreign) → never touch it (skipped-foreign),
74
+ * unless `options.force` is set, in which case it's overwritten
75
+ * (overwritten-foreign) and stamped with the marker.
76
+ */ export function writeManagedShellFile(destPath, sourceContent, version, options = {}) {
69
77
  const finalContent = sourceContent.replaceAll(SHELL_VERSION_PLACEHOLDER, version);
70
78
  if (!existsSync(destPath)) {
71
79
  mkdirSync(dirname(destPath), {
@@ -75,7 +83,11 @@ function fileMarker(version) {
75
83
  return "created";
76
84
  }
77
85
  const current = readFileSync(destPath, "utf-8");
78
- if (!hasArgosShellFileMarker(current)) return "skipped-foreign";
86
+ if (!hasArgosShellFileMarker(current)) {
87
+ if (!options.force) return "skipped-foreign";
88
+ writeFileAtomic(destPath, finalContent, 0o755);
89
+ return "overwritten-foreign";
90
+ }
79
91
  if (current === finalContent) {
80
92
  // Re-assert the executable bit even when content is unchanged, in case
81
93
  // it drifted (e.g. a user ran `chmod -x` on it by hand). Best-effort:
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/managed-files.ts"],"sourcesContent":["import { chmodSync, existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\n\nconst MARKER_PREFIX = '<!-- argos:file v=\"';\nconst FRONTMATTER_RE = /^---\\n[\\s\\S]*?\\n---\\n/;\n\n// Shell assets (hooks) can't carry the HTML-comment marker above (`.sh` has\n// no such syntax) or YAML frontmatter, so they get their own shell-comment\n// marker form: `# argos:file v=\"<version>\"`, hardcoded as the literal first\n// line after the shebang in the asset source (see assets/hooks/*.sh) with a\n// `SHELL_VERSION_PLACEHOLDER` token in place of the version — stamped with\n// the real version at install time by `writeManagedShellFile`.\nconst SHELL_MARKER_PREFIX = '# argos:file v=\"';\nexport const SHELL_VERSION_PLACEHOLDER = \"__ARGOS_VERSION__\";\n\nfunction fileMarker(version: string): string {\n return `${MARKER_PREFIX}${version}\" -->`;\n}\n\n/** True when `content` carries an `argos:file` ownership marker (any version). */\nexport function hasArgosFileMarker(content: string): boolean {\n return content.includes(MARKER_PREFIX);\n}\n\n/**\n * Insert the `argos:file` marker immediately after the leading YAML\n * frontmatter block, so the frontmatter stays the very first thing in the\n * file (required by tools that parse it, e.g. Claude Code agents/skills).\n * Falls back to prefixing the marker when there is no frontmatter.\n */\nexport function withFileMarker(content: string, version: string): string {\n const marker = fileMarker(version);\n const fmMatch = FRONTMATTER_RE.exec(content);\n if (!fmMatch) return `${marker}\\n\\n${content}`;\n const frontmatter = fmMatch[0];\n const rest = content.slice(frontmatter.length);\n return `${frontmatter}${marker}\\n${rest}`;\n}\n\nexport type FileStatus = \"created\" | \"updated\" | \"unchanged\" | \"skipped-foreign\";\n\n/**\n * Write a full-file Argos-owned asset (agent, skill, output-style) to\n * `destPath`, honoring the ownership marker policy:\n * - absent → write it (created)\n * - present with the argos:file marker → overwrite (updated/unchanged)\n * - present without the marker (foreign) → never touch it (skipped-foreign)\n */\nexport function writeManagedFile(destPath: string, sourceContent: string, version: string): FileStatus {\n const finalContent = withFileMarker(sourceContent, version);\n\n if (!existsSync(destPath)) {\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileAtomic(destPath, finalContent);\n return \"created\";\n }\n\n const current = readFileSync(destPath, \"utf-8\");\n if (!hasArgosFileMarker(current)) return \"skipped-foreign\";\n if (current === finalContent) return \"unchanged\";\n\n writeFileAtomic(destPath, finalContent);\n return \"updated\";\n}\n\n/** True when `content` carries a shell-comment `# argos:file` ownership marker (any version). */\nexport function hasArgosShellFileMarker(content: string): boolean {\n return content.includes(SHELL_MARKER_PREFIX);\n}\n\n/**\n * Write a full-file Argos-owned shell script (a hook) to `destPath`.\n * `sourceContent` must already contain the literal\n * `# argos:file v=\"__ARGOS_VERSION__\"` marker line as the first line after\n * the shebang (see assets/hooks/*.sh) — this stamps it with the real\n * `version` (simple placeholder replace, no structural splicing needed since\n * the marker line is already in the right place in the source) and chmods\n * the result executable (0o755). Same ownership policy as `writeManagedFile`:\n * - absent → write it (created)\n * - present with the shell marker → overwrite (updated/unchanged)\n * - present without the marker (foreign) → never touch it (skipped-foreign)\n */\nexport function writeManagedShellFile(destPath: string, sourceContent: string, version: string): FileStatus {\n const finalContent = sourceContent.replaceAll(SHELL_VERSION_PLACEHOLDER, version);\n\n if (!existsSync(destPath)) {\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileAtomic(destPath, finalContent, 0o755);\n return \"created\";\n }\n\n const current = readFileSync(destPath, \"utf-8\");\n if (!hasArgosShellFileMarker(current)) return \"skipped-foreign\";\n\n if (current === finalContent) {\n // Re-assert the executable bit even when content is unchanged, in case\n // it drifted (e.g. a user ran `chmod -x` on it by hand). Best-effort:\n // hooks are invoked as `bash \"<path>\"` (see lib/settings-merge.ts), so\n // the executable bit is inert for Claude Code's own purposes — a chmod\n // failure here (e.g. EPERM on an unusual filesystem) shouldn't fail the\n // whole `argos init` run over a cosmetic permission bit.\n try {\n chmodSync(destPath, 0o755);\n } catch {\n // Swallowed on purpose — see comment above.\n }\n return \"unchanged\";\n }\n\n writeFileAtomic(destPath, finalContent, 0o755);\n return \"updated\";\n}\n"],"names":["chmodSync","existsSync","mkdirSync","readFileSync","dirname","writeFileAtomic","MARKER_PREFIX","FRONTMATTER_RE","SHELL_MARKER_PREFIX","SHELL_VERSION_PLACEHOLDER","fileMarker","version","hasArgosFileMarker","content","includes","withFileMarker","marker","fmMatch","exec","frontmatter","rest","slice","length","writeManagedFile","destPath","sourceContent","finalContent","recursive","current","hasArgosShellFileMarker","writeManagedShellFile","replaceAll"],"mappings":"AAAA,SAASA,SAAS,EAAEC,UAAU,EAAEC,SAAS,EAAEC,YAAY,QAAQ,UAAU;AACzE,SAASC,OAAO,QAAQ,YAAY;AACpC,SAASC,eAAe,QAAQ,oBAAoB;AAEpD,MAAMC,gBAAgB;AACtB,MAAMC,iBAAiB;AAEvB,4EAA4E;AAC5E,2EAA2E;AAC3E,4EAA4E;AAC5E,4EAA4E;AAC5E,2EAA2E;AAC3E,+DAA+D;AAC/D,MAAMC,sBAAsB;AAC5B,OAAO,MAAMC,4BAA4B,oBAAoB;AAE7D,SAASC,WAAWC,OAAe;IACjC,OAAO,GAAGL,gBAAgBK,QAAQ,KAAK,CAAC;AAC1C;AAEA,gFAAgF,GAChF,OAAO,SAASC,mBAAmBC,OAAe;IAChD,OAAOA,QAAQC,QAAQ,CAACR;AAC1B;AAEA;;;;;CAKC,GACD,OAAO,SAASS,eAAeF,OAAe,EAAEF,OAAe;IAC7D,MAAMK,SAASN,WAAWC;IAC1B,MAAMM,UAAUV,eAAeW,IAAI,CAACL;IACpC,IAAI,CAACI,SAAS,OAAO,GAAGD,OAAO,IAAI,EAAEH,SAAS;IAC9C,MAAMM,cAAcF,OAAO,CAAC,EAAE;IAC9B,MAAMG,OAAOP,QAAQQ,KAAK,CAACF,YAAYG,MAAM;IAC7C,OAAO,GAAGH,cAAcH,OAAO,EAAE,EAAEI,MAAM;AAC3C;AAIA;;;;;;CAMC,GACD,OAAO,SAASG,iBAAiBC,QAAgB,EAAEC,aAAqB,EAAEd,OAAe;IACvF,MAAMe,eAAeX,eAAeU,eAAed;IAEnD,IAAI,CAACV,WAAWuB,WAAW;QACzBtB,UAAUE,QAAQoB,WAAW;YAAEG,WAAW;QAAK;QAC/CtB,gBAAgBmB,UAAUE;QAC1B,OAAO;IACT;IAEA,MAAME,UAAUzB,aAAaqB,UAAU;IACvC,IAAI,CAACZ,mBAAmBgB,UAAU,OAAO;IACzC,IAAIA,YAAYF,cAAc,OAAO;IAErCrB,gBAAgBmB,UAAUE;IAC1B,OAAO;AACT;AAEA,+FAA+F,GAC/F,OAAO,SAASG,wBAAwBhB,OAAe;IACrD,OAAOA,QAAQC,QAAQ,CAACN;AAC1B;AAEA;;;;;;;;;;;CAWC,GACD,OAAO,SAASsB,sBAAsBN,QAAgB,EAAEC,aAAqB,EAAEd,OAAe;IAC5F,MAAMe,eAAeD,cAAcM,UAAU,CAACtB,2BAA2BE;IAEzE,IAAI,CAACV,WAAWuB,WAAW;QACzBtB,UAAUE,QAAQoB,WAAW;YAAEG,WAAW;QAAK;QAC/CtB,gBAAgBmB,UAAUE,cAAc;QACxC,OAAO;IACT;IAEA,MAAME,UAAUzB,aAAaqB,UAAU;IACvC,IAAI,CAACK,wBAAwBD,UAAU,OAAO;IAE9C,IAAIA,YAAYF,cAAc;QAC5B,uEAAuE;QACvE,sEAAsE;QACtE,uEAAuE;QACvE,uEAAuE;QACvE,wEAAwE;QACxE,yDAAyD;QACzD,IAAI;YACF1B,UAAUwB,UAAU;QACtB,EAAE,OAAM;QACN,4CAA4C;QAC9C;QACA,OAAO;IACT;IAEAnB,gBAAgBmB,UAAUE,cAAc;IACxC,OAAO;AACT"}
1
+ {"version":3,"sources":["../../src/lib/managed-files.ts"],"sourcesContent":["import { chmodSync, existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\n\nconst MARKER_PREFIX = '<!-- argos:file v=\"';\nconst FRONTMATTER_RE = /^---\\n[\\s\\S]*?\\n---\\n/;\n\n// Shell assets (hooks) can't carry the HTML-comment marker above (`.sh` has\n// no such syntax) or YAML frontmatter, so they get their own shell-comment\n// marker form: `# argos:file v=\"<version>\"`, hardcoded as the literal first\n// line after the shebang in the asset source (see assets/hooks/*.sh) with a\n// `SHELL_VERSION_PLACEHOLDER` token in place of the version — stamped with\n// the real version at install time by `writeManagedShellFile`.\nconst SHELL_MARKER_PREFIX = '# argos:file v=\"';\nexport const SHELL_VERSION_PLACEHOLDER = \"__ARGOS_VERSION__\";\n\nfunction fileMarker(version: string): string {\n return `${MARKER_PREFIX}${version}\" -->`;\n}\n\n/** True when `content` carries an `argos:file` ownership marker (any version). */\nexport function hasArgosFileMarker(content: string): boolean {\n return content.includes(MARKER_PREFIX);\n}\n\n/**\n * Insert the `argos:file` marker immediately after the leading YAML\n * frontmatter block, so the frontmatter stays the very first thing in the\n * file (required by tools that parse it, e.g. Claude Code agents/skills).\n * Falls back to prefixing the marker when there is no frontmatter.\n */\nexport function withFileMarker(content: string, version: string): string {\n const marker = fileMarker(version);\n const fmMatch = FRONTMATTER_RE.exec(content);\n if (!fmMatch) return `${marker}\\n\\n${content}`;\n const frontmatter = fmMatch[0];\n const rest = content.slice(frontmatter.length);\n return `${frontmatter}${marker}\\n${rest}`;\n}\n\nexport type FileStatus = \"created\" | \"updated\" | \"unchanged\" | \"skipped-foreign\" | \"overwritten-foreign\";\n\nexport interface WriteManagedFileOptions {\n /**\n * `argos init --force`: when the destination exists WITHOUT the\n * ownership marker (foreign), overwrite it with the motor version instead\n * of skipping it. The marker lands with this write, so a subsequent\n * (non-forced) run treats the file as owned from then on. Default `false`\n * — the pre-existing skip-foreign behavior.\n */\n force?: boolean;\n}\n\n/**\n * Write a full-file Argos-owned asset (agent, skill, output-style) to\n * `destPath`, honoring the ownership marker policy:\n * - absent → write it (created)\n * - present with the argos:file marker → overwrite (updated/unchanged)\n * - present without the marker (foreign) → never touch it (skipped-foreign),\n * unless `options.force` is set, in which case it's overwritten\n * (overwritten-foreign) and stamped with the marker.\n */\nexport function writeManagedFile(\n destPath: string,\n sourceContent: string,\n version: string,\n options: WriteManagedFileOptions = {},\n): FileStatus {\n const finalContent = withFileMarker(sourceContent, version);\n\n if (!existsSync(destPath)) {\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileAtomic(destPath, finalContent);\n return \"created\";\n }\n\n const current = readFileSync(destPath, \"utf-8\");\n if (!hasArgosFileMarker(current)) {\n if (!options.force) return \"skipped-foreign\";\n writeFileAtomic(destPath, finalContent);\n return \"overwritten-foreign\";\n }\n if (current === finalContent) return \"unchanged\";\n\n writeFileAtomic(destPath, finalContent);\n return \"updated\";\n}\n\n/** True when `content` carries a shell-comment `# argos:file` ownership marker (any version). */\nexport function hasArgosShellFileMarker(content: string): boolean {\n return content.includes(SHELL_MARKER_PREFIX);\n}\n\n/**\n * Write a full-file Argos-owned shell script (a hook) to `destPath`.\n * `sourceContent` must already contain the literal\n * `# argos:file v=\"__ARGOS_VERSION__\"` marker line as the first line after\n * the shebang (see assets/hooks/*.sh) — this stamps it with the real\n * `version` (simple placeholder replace, no structural splicing needed since\n * the marker line is already in the right place in the source) and chmods\n * the result executable (0o755). Same ownership policy as `writeManagedFile`:\n * - absent → write it (created)\n * - present with the shell marker → overwrite (updated/unchanged)\n * - present without the marker (foreign) → never touch it (skipped-foreign),\n * unless `options.force` is set, in which case it's overwritten\n * (overwritten-foreign) and stamped with the marker.\n */\nexport function writeManagedShellFile(\n destPath: string,\n sourceContent: string,\n version: string,\n options: WriteManagedFileOptions = {},\n): FileStatus {\n const finalContent = sourceContent.replaceAll(SHELL_VERSION_PLACEHOLDER, version);\n\n if (!existsSync(destPath)) {\n mkdirSync(dirname(destPath), { recursive: true });\n writeFileAtomic(destPath, finalContent, 0o755);\n return \"created\";\n }\n\n const current = readFileSync(destPath, \"utf-8\");\n if (!hasArgosShellFileMarker(current)) {\n if (!options.force) return \"skipped-foreign\";\n writeFileAtomic(destPath, finalContent, 0o755);\n return \"overwritten-foreign\";\n }\n\n if (current === finalContent) {\n // Re-assert the executable bit even when content is unchanged, in case\n // it drifted (e.g. a user ran `chmod -x` on it by hand). Best-effort:\n // hooks are invoked as `bash \"<path>\"` (see lib/settings-merge.ts), so\n // the executable bit is inert for Claude Code's own purposes — a chmod\n // failure here (e.g. EPERM on an unusual filesystem) shouldn't fail the\n // whole `argos init` run over a cosmetic permission bit.\n try {\n chmodSync(destPath, 0o755);\n } catch {\n // Swallowed on purpose — see comment above.\n }\n return \"unchanged\";\n }\n\n writeFileAtomic(destPath, finalContent, 0o755);\n return \"updated\";\n}\n"],"names":["chmodSync","existsSync","mkdirSync","readFileSync","dirname","writeFileAtomic","MARKER_PREFIX","FRONTMATTER_RE","SHELL_MARKER_PREFIX","SHELL_VERSION_PLACEHOLDER","fileMarker","version","hasArgosFileMarker","content","includes","withFileMarker","marker","fmMatch","exec","frontmatter","rest","slice","length","writeManagedFile","destPath","sourceContent","options","finalContent","recursive","current","force","hasArgosShellFileMarker","writeManagedShellFile","replaceAll"],"mappings":"AAAA,SAASA,SAAS,EAAEC,UAAU,EAAEC,SAAS,EAAEC,YAAY,QAAQ,UAAU;AACzE,SAASC,OAAO,QAAQ,YAAY;AACpC,SAASC,eAAe,QAAQ,oBAAoB;AAEpD,MAAMC,gBAAgB;AACtB,MAAMC,iBAAiB;AAEvB,4EAA4E;AAC5E,2EAA2E;AAC3E,4EAA4E;AAC5E,4EAA4E;AAC5E,2EAA2E;AAC3E,+DAA+D;AAC/D,MAAMC,sBAAsB;AAC5B,OAAO,MAAMC,4BAA4B,oBAAoB;AAE7D,SAASC,WAAWC,OAAe;IACjC,OAAO,GAAGL,gBAAgBK,QAAQ,KAAK,CAAC;AAC1C;AAEA,gFAAgF,GAChF,OAAO,SAASC,mBAAmBC,OAAe;IAChD,OAAOA,QAAQC,QAAQ,CAACR;AAC1B;AAEA;;;;;CAKC,GACD,OAAO,SAASS,eAAeF,OAAe,EAAEF,OAAe;IAC7D,MAAMK,SAASN,WAAWC;IAC1B,MAAMM,UAAUV,eAAeW,IAAI,CAACL;IACpC,IAAI,CAACI,SAAS,OAAO,GAAGD,OAAO,IAAI,EAAEH,SAAS;IAC9C,MAAMM,cAAcF,OAAO,CAAC,EAAE;IAC9B,MAAMG,OAAOP,QAAQQ,KAAK,CAACF,YAAYG,MAAM;IAC7C,OAAO,GAAGH,cAAcH,OAAO,EAAE,EAAEI,MAAM;AAC3C;AAeA;;;;;;;;CAQC,GACD,OAAO,SAASG,iBACdC,QAAgB,EAChBC,aAAqB,EACrBd,OAAe,EACfe,UAAmC,CAAC,CAAC;IAErC,MAAMC,eAAeZ,eAAeU,eAAed;IAEnD,IAAI,CAACV,WAAWuB,WAAW;QACzBtB,UAAUE,QAAQoB,WAAW;YAAEI,WAAW;QAAK;QAC/CvB,gBAAgBmB,UAAUG;QAC1B,OAAO;IACT;IAEA,MAAME,UAAU1B,aAAaqB,UAAU;IACvC,IAAI,CAACZ,mBAAmBiB,UAAU;QAChC,IAAI,CAACH,QAAQI,KAAK,EAAE,OAAO;QAC3BzB,gBAAgBmB,UAAUG;QAC1B,OAAO;IACT;IACA,IAAIE,YAAYF,cAAc,OAAO;IAErCtB,gBAAgBmB,UAAUG;IAC1B,OAAO;AACT;AAEA,+FAA+F,GAC/F,OAAO,SAASI,wBAAwBlB,OAAe;IACrD,OAAOA,QAAQC,QAAQ,CAACN;AAC1B;AAEA;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASwB,sBACdR,QAAgB,EAChBC,aAAqB,EACrBd,OAAe,EACfe,UAAmC,CAAC,CAAC;IAErC,MAAMC,eAAeF,cAAcQ,UAAU,CAACxB,2BAA2BE;IAEzE,IAAI,CAACV,WAAWuB,WAAW;QACzBtB,UAAUE,QAAQoB,WAAW;YAAEI,WAAW;QAAK;QAC/CvB,gBAAgBmB,UAAUG,cAAc;QACxC,OAAO;IACT;IAEA,MAAME,UAAU1B,aAAaqB,UAAU;IACvC,IAAI,CAACO,wBAAwBF,UAAU;QACrC,IAAI,CAACH,QAAQI,KAAK,EAAE,OAAO;QAC3BzB,gBAAgBmB,UAAUG,cAAc;QACxC,OAAO;IACT;IAEA,IAAIE,YAAYF,cAAc;QAC5B,uEAAuE;QACvE,sEAAsE;QACtE,uEAAuE;QACvE,uEAAuE;QACvE,wEAAwE;QACxE,yDAAyD;QACzD,IAAI;YACF3B,UAAUwB,UAAU;QACtB,EAAE,OAAM;QACN,4CAA4C;QAC9C;QACA,OAAO;IACT;IAEAnB,gBAAgBmB,UAAUG,cAAc;IACxC,OAAO;AACT"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Interactive CLI layer (spec 0004, F5). The `Prompter` interface is the
3
+ * injectable seam every interactive surface (`argos init`, `argos adopt`,
4
+ * `argos remove`, `argos workspace link`) depends on instead of importing
5
+ * `@clack/prompts` directly — same idiom as `OpenclawRunner` in
6
+ * `lib/openclaw-agents.ts`: a typed interface, a real default implementation
7
+ * that's a thin pass-through over the real library, and every surface takes
8
+ * an optional `prompter?: Prompter` that defaults to it. Tests then inject a
9
+ * trivial plain-object fake instead of depending on `@clack` internals.
10
+ *
11
+ * Shaped to mirror `@clack/prompts`' actual API 1:1 (same option names,
12
+ * same `Value | symbol` return convention for cancellable prompts) so the
13
+ * default implementation below is a pure pass-through.
14
+ */
15
+ export interface SelectOption<Value extends string> {
16
+ value: Value;
17
+ label?: string;
18
+ hint?: string;
19
+ }
20
+ export interface SelectPromptOptions<Value extends string> {
21
+ message: string;
22
+ options: SelectOption<Value>[];
23
+ initialValue?: Value;
24
+ }
25
+ export interface ConfirmPromptOptions {
26
+ message: string;
27
+ initialValue?: boolean;
28
+ }
29
+ export interface TextPromptOptions {
30
+ message: string;
31
+ placeholder?: string;
32
+ defaultValue?: string;
33
+ initialValue?: string;
34
+ /**
35
+ * Signature matches `@clack/prompts`' own `Validate<string>` (accepts
36
+ * `undefined` too — the prompt hasn't been submitted yet on first render)
37
+ * so the default implementation below is a pure pass-through.
38
+ */
39
+ validate?: (value: string | undefined) => string | Error | undefined;
40
+ }
41
+ export interface Prompter {
42
+ select<Value extends string>(opts: SelectPromptOptions<Value>): Promise<Value | symbol>;
43
+ confirm(opts: ConfirmPromptOptions): Promise<boolean | symbol>;
44
+ text(opts: TextPromptOptions): Promise<string | symbol>;
45
+ isCancel(value: unknown): value is symbol;
46
+ cancel(message?: string): void;
47
+ note(message?: string, title?: string): void;
48
+ intro(title?: string): void;
49
+ outro(message?: string): void;
50
+ }
51
+ /** Real default implementation, backed by `@clack/prompts`. */
52
+ export declare const clackPrompter: Prompter;
53
+ /**
54
+ * TTY gate every interactive surface checks independently before layering
55
+ * prompts on top of its own flag-driven defaults (spec 0004's hard rule):
56
+ * both stdout and stdin must be a real TTY, AND `--yes` must not have been
57
+ * passed — `--yes` forces non-interactive behavior even under a real TTY,
58
+ * since that's how a human operator opts out of the wizard on demand.
59
+ */
60
+ export declare function isInteractive(options: {
61
+ yes?: boolean;
62
+ }): boolean;
63
+ //# sourceMappingURL=prompter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompter.d.ts","sourceRoot":"","sources":["../../src/lib/prompter.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAQH,MAAM,WAAW,YAAY,CAAC,KAAK,SAAS,MAAM;IAChD,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB,CAAC,KAAK,SAAS,MAAM;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;IAC/B,YAAY,CAAC,EAAE,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;CACtE;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,KAAK,SAAS,MAAM,EAAE,IAAI,EAAE,mBAAmB,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC;IACxF,OAAO,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC,CAAC;IAC/D,IAAI,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACxD,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC;IAC1C,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,+DAA+D;AAC/D,eAAO,MAAM,aAAa,EAAE,QAkB3B,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAEjE"}
@@ -0,0 +1,30 @@
1
+ import { cancel, confirm, intro, isCancel, note, outro, select, text } from "@clack/prompts";
2
+ /** Real default implementation, backed by `@clack/prompts`. */ export const clackPrompter = {
3
+ // `@clack/prompts`' `Option<Value>` is a conditional type keyed on
4
+ // `Value extends Primitive` — TS can't reduce that conditional for our
5
+ // still-generic `Value` type parameter here (it only resolves once a
6
+ // caller picks a concrete `Value`), so it refuses to unify
7
+ // `SelectOption<Value>[]` with `Option<Value>[]` even though every
8
+ // concrete instantiation (`Value extends string`) is structurally
9
+ // identical. Narrow, single-purpose cast at this one boundary — not `any`
10
+ // on the whole function — since `opts` is otherwise fully typed above.
11
+ select: (opts)=>select(opts),
12
+ confirm: (opts)=>confirm(opts),
13
+ text: (opts)=>text(opts),
14
+ isCancel: (value)=>isCancel(value),
15
+ cancel: (message)=>cancel(message),
16
+ note: (message, title)=>note(message, title),
17
+ intro: (title)=>intro(title),
18
+ outro: (message)=>outro(message)
19
+ };
20
+ /**
21
+ * TTY gate every interactive surface checks independently before layering
22
+ * prompts on top of its own flag-driven defaults (spec 0004's hard rule):
23
+ * both stdout and stdin must be a real TTY, AND `--yes` must not have been
24
+ * passed — `--yes` forces non-interactive behavior even under a real TTY,
25
+ * since that's how a human operator opts out of the wizard on demand.
26
+ */ export function isInteractive(options) {
27
+ return process.stdout.isTTY === true && process.stdin.isTTY === true && !options.yes;
28
+ }
29
+
30
+ //# sourceMappingURL=prompter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/prompter.ts"],"sourcesContent":["import { cancel, confirm, intro, isCancel, note, outro, select, text } from \"@clack/prompts\";\n\n/**\n * Interactive CLI layer (spec 0004, F5). The `Prompter` interface is the\n * injectable seam every interactive surface (`argos init`, `argos adopt`,\n * `argos remove`, `argos workspace link`) depends on instead of importing\n * `@clack/prompts` directly — same idiom as `OpenclawRunner` in\n * `lib/openclaw-agents.ts`: a typed interface, a real default implementation\n * that's a thin pass-through over the real library, and every surface takes\n * an optional `prompter?: Prompter` that defaults to it. Tests then inject a\n * trivial plain-object fake instead of depending on `@clack` internals.\n *\n * Shaped to mirror `@clack/prompts`' actual API 1:1 (same option names,\n * same `Value | symbol` return convention for cancellable prompts) so the\n * default implementation below is a pure pass-through.\n */\n\n// `Value extends string` (rather than unconstrained) deliberately mirrors\n// every actual use of `select` in this codebase (language codes, workspace\n// names, match candidates) — it's also what makes `SelectOption` line up\n// with `@clack/prompts`' own `Option<Value>` (whose `label` is optional only\n// for `Value extends Readonly<string | boolean | number>`), so the default\n// implementation below can pass options straight through with no adapter.\nexport interface SelectOption<Value extends string> {\n value: Value;\n label?: string;\n hint?: string;\n}\n\nexport interface SelectPromptOptions<Value extends string> {\n message: string;\n options: SelectOption<Value>[];\n initialValue?: Value;\n}\n\nexport interface ConfirmPromptOptions {\n message: string;\n initialValue?: boolean;\n}\n\nexport interface TextPromptOptions {\n message: string;\n placeholder?: string;\n defaultValue?: string;\n initialValue?: string;\n /**\n * Signature matches `@clack/prompts`' own `Validate<string>` (accepts\n * `undefined` too — the prompt hasn't been submitted yet on first render)\n * so the default implementation below is a pure pass-through.\n */\n validate?: (value: string | undefined) => string | Error | undefined;\n}\n\nexport interface Prompter {\n select<Value extends string>(opts: SelectPromptOptions<Value>): Promise<Value | symbol>;\n confirm(opts: ConfirmPromptOptions): Promise<boolean | symbol>;\n text(opts: TextPromptOptions): Promise<string | symbol>;\n isCancel(value: unknown): value is symbol;\n cancel(message?: string): void;\n note(message?: string, title?: string): void;\n intro(title?: string): void;\n outro(message?: string): void;\n}\n\n/** Real default implementation, backed by `@clack/prompts`. */\nexport const clackPrompter: Prompter = {\n // `@clack/prompts`' `Option<Value>` is a conditional type keyed on\n // `Value extends Primitive` — TS can't reduce that conditional for our\n // still-generic `Value` type parameter here (it only resolves once a\n // caller picks a concrete `Value`), so it refuses to unify\n // `SelectOption<Value>[]` with `Option<Value>[]` even though every\n // concrete instantiation (`Value extends string`) is structurally\n // identical. Narrow, single-purpose cast at this one boundary — not `any`\n // on the whole function — since `opts` is otherwise fully typed above.\n select: <Value extends string>(opts: SelectPromptOptions<Value>) =>\n select(opts as Parameters<typeof select>[0]) as Promise<Value | symbol>,\n confirm: (opts) => confirm(opts),\n text: (opts) => text(opts),\n isCancel: (value): value is symbol => isCancel(value),\n cancel: (message) => cancel(message),\n note: (message, title) => note(message, title),\n intro: (title) => intro(title),\n outro: (message) => outro(message),\n};\n\n/**\n * TTY gate every interactive surface checks independently before layering\n * prompts on top of its own flag-driven defaults (spec 0004's hard rule):\n * both stdout and stdin must be a real TTY, AND `--yes` must not have been\n * passed — `--yes` forces non-interactive behavior even under a real TTY,\n * since that's how a human operator opts out of the wizard on demand.\n */\nexport function isInteractive(options: { yes?: boolean }): boolean {\n return process.stdout.isTTY === true && process.stdin.isTTY === true && !options.yes;\n}\n"],"names":["cancel","confirm","intro","isCancel","note","outro","select","text","clackPrompter","opts","value","message","title","isInteractive","options","process","stdout","isTTY","stdin","yes"],"mappings":"AAAA,SAASA,MAAM,EAAEC,OAAO,EAAEC,KAAK,EAAEC,QAAQ,EAAEC,IAAI,EAAEC,KAAK,EAAEC,MAAM,EAAEC,IAAI,QAAQ,iBAAiB;AAgE7F,6DAA6D,GAC7D,OAAO,MAAMC,gBAA0B;IACrC,mEAAmE;IACnE,uEAAuE;IACvE,qEAAqE;IACrE,2DAA2D;IAC3D,mEAAmE;IACnE,kEAAkE;IAClE,0EAA0E;IAC1E,uEAAuE;IACvEF,QAAQ,CAAuBG,OAC7BH,OAAOG;IACTR,SAAS,CAACQ,OAASR,QAAQQ;IAC3BF,MAAM,CAACE,OAASF,KAAKE;IACrBN,UAAU,CAACO,QAA2BP,SAASO;IAC/CV,QAAQ,CAACW,UAAYX,OAAOW;IAC5BP,MAAM,CAACO,SAASC,QAAUR,KAAKO,SAASC;IACxCV,OAAO,CAACU,QAAUV,MAAMU;IACxBP,OAAO,CAACM,UAAYN,MAAMM;AAC5B,EAAE;AAEF;;;;;;CAMC,GACD,OAAO,SAASE,cAAcC,OAA0B;IACtD,OAAOC,QAAQC,MAAM,CAACC,KAAK,KAAK,QAAQF,QAAQG,KAAK,CAACD,KAAK,KAAK,QAAQ,CAACH,QAAQK,GAAG;AACtF"}