@nail00749/agent-gvozd 0.1.8 → 0.2.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.
package/dist/cli.js CHANGED
@@ -988,6 +988,7 @@ var i = `${styleText("gray", S_BAR)} `;
988
988
 
989
989
  // src/cli.ts
990
990
  import { realpathSync as realpathSync5 } from "node:fs";
991
+ import { join as join11 } from "node:path";
991
992
  import { fileURLToPath as fileURLToPath2 } from "node:url";
992
993
 
993
994
  // src/config.ts
@@ -7336,9 +7337,9 @@ function buildAgentPermissions(agent, mcpServers) {
7336
7337
 
7337
7338
  // src/release-metadata.ts
7338
7339
  var PACKAGE_NAME = "@nail00749/agent-gvozd";
7339
- var PACKAGE_VERSION = "0.1.8";
7340
+ var PACKAGE_VERSION = "0.2.0";
7340
7341
  var PACKAGE_SPEC = `${PACKAGE_NAME}@${PACKAGE_VERSION}`;
7341
- var SUPPORTED_OPENCODE_VERSION = "2.0.2";
7342
+ var SUPPORTED_OPENCODE_VERSION = "2.0.*";
7342
7343
  var CONFIG_SCHEMA_VERSION = 2;
7343
7344
 
7344
7345
  // src/constants.ts
@@ -7568,6 +7569,18 @@ function parseDebugPaths(output) {
7568
7569
  function parseOpenCodeVersion(output) {
7569
7570
  return output.match(/(?:^|\s)v?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)(?=$|\s)/)?.[1];
7570
7571
  }
7572
+ function satisfiesOpenCodeRange(version, range) {
7573
+ if (!version)
7574
+ return false;
7575
+ const [prerelease] = version.split("-").slice(1);
7576
+ if (prerelease)
7577
+ return false;
7578
+ if (!range.includes("*"))
7579
+ return version === range;
7580
+ const [rangeMajor, rangeMinor] = range.split(".").slice(0, 2);
7581
+ const [major, minor] = version.split(".").slice(0, 2);
7582
+ return major === rangeMajor && minor === rangeMinor;
7583
+ }
7571
7584
  function parseAgentIdentifiers(output) {
7572
7585
  try {
7573
7586
  const parsed = JSON.parse(output);
@@ -7766,7 +7779,7 @@ async function runDoctor(input) {
7766
7779
  const checks = [];
7767
7780
  try {
7768
7781
  const version = await input.client.version();
7769
- checks.push(parseOpenCodeVersion(version) === supportedVersion ? { id: "opencode-version", status: "pass", summary: `OpenCode ${supportedVersion} is available` } : { id: "opencode-version", status: "fail", summary: `unsupported OpenCode version: ${redactDiagnostic(version)}`, remediation: `Install OpenCode ${supportedVersion}` });
7782
+ checks.push(satisfiesOpenCodeRange(parseOpenCodeVersion(version), supportedVersion) ? { id: "opencode-version", status: "pass", summary: `OpenCode ${supportedVersion} is available` } : { id: "opencode-version", status: "fail", summary: `unsupported OpenCode version: ${redactDiagnostic(version)}`, remediation: `Install OpenCode ${supportedVersion}` });
7770
7783
  } catch (error) {
7771
7784
  checks.push({ id: "opencode-version", status: "fail", summary: `OpenCode version check failed: ${redactDiagnostic(error)}`, remediation: `Install OpenCode ${supportedVersion}` });
7772
7785
  }
@@ -7871,9 +7884,9 @@ function doctorOperationalFailure(error) {
7871
7884
  return { schemaVersion: CONFIG_SCHEMA_VERSION, status: "fail", checks };
7872
7885
  }
7873
7886
 
7874
- // src/cli/setup.ts
7875
- import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
7876
- import { dirname as dirname6, join as join8 } from "node:path";
7887
+ // src/cli/agents.ts
7888
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
7889
+ import { join as join7 } from "node:path";
7877
7890
 
7878
7891
  // src/cli/config-store.ts
7879
7892
  import { accessSync, closeSync, constants as fsConstants, existsSync as existsSync4, lstatSync as lstatSync5, mkdirSync, openSync, readFileSync as readFileSync4, renameSync, unlinkSync, writeFileSync } from "node:fs";
@@ -7943,8 +7956,8 @@ function secureCanonicalPath(target, label = "Path") {
7943
7956
  }
7944
7957
 
7945
7958
  // src/cli/config-store.ts
7946
- var FAST_AGENT_IDS = ["back-fast", "front-fast", "review-fast", "researcher", "git", "docs", "verifier"];
7947
- var DEEP_AGENT_IDS = ["master", "planner", "back-deep", "front-deep", "review-deep", "debugger", "security", "devops"];
7959
+ var FAST_AGENT_IDS = ["back-fast", "front-fast", "review-fast", "researcher", "git", "docs"];
7960
+ var DEEP_AGENT_IDS = ["master", "master-trusted", "planner", "back-deep", "front-deep", "review-deep", "debugger", "security", "devops"];
7948
7961
  var ALL_AGENT_IDS = [...DEEP_AGENT_IDS, ...FAST_AGENT_IDS, "explorer"];
7949
7962
  var formattingOptions = { insertSpaces: true, tabSize: 2, eol: `
7950
7963
  ` };
@@ -7969,6 +7982,9 @@ function applyModelProfile(source, profile) {
7969
7982
  updated = setJsonc(updated, ["agents", "explorer", "models"], profile.agentOverrides.explorer ?? profile.fast);
7970
7983
  return updated;
7971
7984
  }
7985
+ function writeManagedGlobalFile(path, content, expected) {
7986
+ atomicWrite(path, content, expected);
7987
+ }
7972
7988
  function matchesSnapshot(path, expected) {
7973
7989
  if (!expected.exists)
7974
7990
  return !existsSync4(path);
@@ -8108,6 +8124,51 @@ function writeGlobalConfig(input) {
8108
8124
  return { configPath, schemaPath, config };
8109
8125
  }
8110
8126
 
8127
+ // src/cli/agents.ts
8128
+ var formattingOptions2 = { insertSpaces: true, tabSize: 2, eol: `
8129
+ ` };
8130
+ function assertValidJsonc2(source, label) {
8131
+ const errors = [];
8132
+ const value = parse2(source, errors, { allowTrailingComma: true, disallowComments: false });
8133
+ if (errors.length > 0 || value === undefined || value === null || Array.isArray(value) || typeof value !== "object") {
8134
+ const details = errors.map((error) => `${printParseErrorCode(error.error)} at offset ${error.offset}`).join(", ");
8135
+ throw new Error(`Invalid JSONC in ${label}${details ? `: ${details}` : ""}`);
8136
+ }
8137
+ }
8138
+ function listAgents(cwd) {
8139
+ const config = loadConfig(cwd, { configRoot: resolveOpenCodeConfigRoot() });
8140
+ return Object.entries(config.agents).map(([id, agent]) => ({
8141
+ id,
8142
+ mode: agent.mode,
8143
+ lease: agent.fileLease,
8144
+ disabled: agent.disabled,
8145
+ model: agent.models[0] ?? ""
8146
+ }));
8147
+ }
8148
+ function setAgentDisabled(source, agentID, disabled) {
8149
+ if (!ALL_AGENT_IDS.includes(agentID)) {
8150
+ throw new Error(`Unknown agent: ${agentID}`);
8151
+ }
8152
+ assertValidJsonc2(source, "global Gvozd config");
8153
+ return applyEdits(source, modify(source, ["agents", agentID, "disabled"], disabled, { formattingOptions: formattingOptions2 }));
8154
+ }
8155
+ function readGlobalConfig(configRoot) {
8156
+ const path = join7(configRoot, "gvozd", "config.jsonc");
8157
+ if (!existsSync5(path))
8158
+ return `{
8159
+ "$schema": "./schema.json",
8160
+ "agents": {}
8161
+ }
8162
+ `;
8163
+ const source = readFileSync5(path, "utf8");
8164
+ assertValidJsonc2(source, path);
8165
+ return source;
8166
+ }
8167
+
8168
+ // src/cli/setup.ts
8169
+ import { existsSync as existsSync8, readFileSync as readFileSync8 } from "node:fs";
8170
+ import { dirname as dirname6, join as join9 } from "node:path";
8171
+
8111
8172
  // src/cli/configure.ts
8112
8173
  function availableProfile(profile, catalog) {
8113
8174
  if (!profile)
@@ -8177,9 +8238,9 @@ function profileFromAgents(agents, catalog) {
8177
8238
  }
8178
8239
 
8179
8240
  // src/cli/global-sync.ts
8180
- import { accessSync as accessSync2, closeSync as closeSync2, constants as fsConstants2, existsSync as existsSync5, lstatSync as lstatSync6, mkdirSync as mkdirSync2, openSync as openSync2, readFileSync as readFileSync5, readdirSync as readdirSync4, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
8241
+ import { accessSync as accessSync2, closeSync as closeSync2, constants as fsConstants2, existsSync as existsSync6, lstatSync as lstatSync6, mkdirSync as mkdirSync2, openSync as openSync2, readFileSync as readFileSync6, readdirSync as readdirSync4, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
8181
8242
  import { randomUUID as randomUUID2 } from "node:crypto";
8182
- import { dirname as dirname4, join as join7 } from "node:path";
8243
+ import { dirname as dirname4, join as join8 } from "node:path";
8183
8244
  function stat(path) {
8184
8245
  try {
8185
8246
  return lstatSync6(path);
@@ -8191,7 +8252,7 @@ function stat(path) {
8191
8252
  }
8192
8253
  function assertWriteable2(path, label) {
8193
8254
  let candidate = path;
8194
- while (!existsSync5(candidate)) {
8255
+ while (!existsSync6(candidate)) {
8195
8256
  const parent = dirname4(candidate);
8196
8257
  if (parent === candidate)
8197
8258
  break;
@@ -8236,7 +8297,7 @@ function replaceFile(path, content) {
8236
8297
  }
8237
8298
  function writeManagedAgents(input) {
8238
8299
  const configRoot = secureCanonicalPath(input.configRoot, "OpenCode config root");
8239
- const agentsDirectory = secureCanonicalPath(join7(configRoot, "agents"), "OpenCode agents path");
8300
+ const agentsDirectory = secureCanonicalPath(join8(configRoot, "agents"), "OpenCode agents path");
8240
8301
  const result = { created: [], updated: [], unchanged: [], conflicts: [], removed: [] };
8241
8302
  const writes = [];
8242
8303
  const removals = new Map;
@@ -8255,21 +8316,21 @@ function writeManagedAgents(input) {
8255
8316
  for (const entry of readdirSync4(agentsDirectory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
8256
8317
  if (!entry.isFile() || !entry.name.endsWith(".md") || enabled.has(entry.name))
8257
8318
  continue;
8258
- const path = join7(agentsDirectory, entry.name);
8319
+ const path = join8(agentsDirectory, entry.name);
8259
8320
  const currentStat = stat(path);
8260
8321
  if (!currentStat || currentStat.isSymbolicLink() || !currentStat.isFile())
8261
8322
  continue;
8262
- if (hasGeneratedAgentMarker(readFileSync5(path, "utf8"))) {
8323
+ if (hasGeneratedAgentMarker(readFileSync6(path, "utf8"))) {
8263
8324
  assertWriteable2(path, "Managed global agent");
8264
8325
  result.removed.push(path);
8265
- removals.set(path, readFileSync5(path, "utf8"));
8326
+ removals.set(path, readFileSync6(path, "utf8"));
8266
8327
  }
8267
8328
  }
8268
8329
  }
8269
8330
  for (const [id, agent] of Object.entries(input.agents).sort(([left], [right]) => left.localeCompare(right))) {
8270
8331
  if (agent.disabled)
8271
8332
  continue;
8272
- const path = join7(agentsDirectory, `${id}.md`);
8333
+ const path = join8(agentsDirectory, `${id}.md`);
8273
8334
  const content = renderAgent(agent);
8274
8335
  const currentStat = stat(path);
8275
8336
  if (!currentStat) {
@@ -8281,7 +8342,7 @@ function writeManagedAgents(input) {
8281
8342
  result.conflicts.push(path);
8282
8343
  continue;
8283
8344
  }
8284
- const current = readFileSync5(path, "utf8");
8345
+ const current = readFileSync6(path, "utf8");
8285
8346
  if (!hasGeneratedAgentMarker(current)) {
8286
8347
  result.conflicts.push(path);
8287
8348
  continue;
@@ -8319,7 +8380,7 @@ function writeManagedAgents(input) {
8319
8380
  if (!currentStat || currentStat.isSymbolicLink() || !currentStat.isFile()) {
8320
8381
  throw new Error(`Refusing to remove a changed or unmanaged global agent: ${path}`);
8321
8382
  }
8322
- const current = readFileSync5(path, "utf8");
8383
+ const current = readFileSync6(path, "utf8");
8323
8384
  if (!hasGeneratedAgentMarker(current) || current !== removals.get(path)) {
8324
8385
  throw new Error(`Refusing to remove a concurrently changed global agent: ${path}`);
8325
8386
  }
@@ -8328,7 +8389,7 @@ function writeManagedAgents(input) {
8328
8389
  for (const write of writes) {
8329
8390
  if (write.replace) {
8330
8391
  const currentStat = stat(write.path);
8331
- const current = currentStat?.isFile() && !currentStat.isSymbolicLink() ? readFileSync5(write.path, "utf8") : undefined;
8392
+ const current = currentStat?.isFile() && !currentStat.isSymbolicLink() ? readFileSync6(write.path, "utf8") : undefined;
8332
8393
  if (!current || !hasGeneratedAgentMarker(current) || current !== write.previous) {
8333
8394
  throw new Error(`Refusing to replace a concurrently changed global agent: ${write.path}`);
8334
8395
  }
@@ -8341,7 +8402,7 @@ function writeManagedAgents(input) {
8341
8402
 
8342
8403
  // src/file-lock.ts
8343
8404
  import { randomUUID as randomUUID3 } from "node:crypto";
8344
- import { accessSync as accessSync3, closeSync as closeSync3, constants, existsSync as existsSync6, fstatSync, lstatSync as lstatSync7, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "node:fs";
8405
+ import { accessSync as accessSync3, closeSync as closeSync3, constants, existsSync as existsSync7, fstatSync, lstatSync as lstatSync7, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync7, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "node:fs";
8345
8406
  import { dirname as dirname5 } from "node:path";
8346
8407
  function assertSecure(path) {
8347
8408
  const stat = lstatSync7(path);
@@ -8377,9 +8438,9 @@ function acquire(path, operation) {
8377
8438
  }
8378
8439
  function release(lock) {
8379
8440
  closeSync3(lock.descriptor);
8380
- if (existsSync6(lock.path)) {
8441
+ if (existsSync7(lock.path)) {
8381
8442
  const current = lstatSync7(lock.path);
8382
- if (current.isFile() && !current.isSymbolicLink() && current.dev === lock.dev && current.ino === lock.ino && readFileSync6(lock.path, "utf8") === lock.nonce)
8443
+ if (current.isFile() && !current.isSymbolicLink() && current.dev === lock.dev && current.ino === lock.ino && readFileSync7(lock.path, "utf8") === lock.nonce)
8383
8444
  unlinkSync3(lock.path);
8384
8445
  }
8385
8446
  }
@@ -8402,14 +8463,14 @@ function withExclusiveFileLockSync(path, callback, operation = "operation") {
8402
8463
 
8403
8464
  // src/cli/setup.ts
8404
8465
  function assertVersion(version) {
8405
- if (parseOpenCodeVersion(version) !== SUPPORTED_OPENCODE_VERSION) {
8466
+ if (!satisfiesOpenCodeRange(parseOpenCodeVersion(version), SUPPORTED_OPENCODE_VERSION)) {
8406
8467
  throw new Error(`Unsupported OpenCode version. Gvozd ${PACKAGE_VERSION} requires ${SUPPORTED_OPENCODE_VERSION}.`);
8407
8468
  }
8408
8469
  }
8409
8470
  async function selectProfile(input, client, configRoot) {
8410
8471
  const catalog = parseModels(await client.models());
8411
8472
  const config = loadConfig(input.cwd, { configRoot, includeProject: false });
8412
- const hasGlobalConfig = existsSync7(join8(configRoot, "gvozd", "config.jsonc"));
8473
+ const hasGlobalConfig = existsSync8(join9(configRoot, "gvozd", "config.jsonc"));
8413
8474
  return chooseModelProfile({
8414
8475
  catalog,
8415
8476
  ui: input.ui,
@@ -8435,6 +8496,22 @@ async function registeredPackageSpecs(client, packageName, target) {
8435
8496
  const specs = [...new Set(output.match(specPattern) ?? [])];
8436
8497
  return specs.filter((spec) => spec !== target);
8437
8498
  }
8499
+ async function awaitRegisteredPlugin(client, timeoutMs = 15000) {
8500
+ const target = PACKAGE_SPEC;
8501
+ const started = Date.now();
8502
+ for (;; ) {
8503
+ try {
8504
+ const output = await client.pluginList();
8505
+ const escapedName = PACKAGE_NAME.replace(/[/@]/g, "\\$&");
8506
+ const registered = new RegExp(`${escapedName}(?:@|\\s+)v?${PACKAGE_VERSION}(?=$|[^0-9.])`, "m").test(output);
8507
+ if (registered)
8508
+ return;
8509
+ } catch {}
8510
+ if (Date.now() - started >= timeoutMs)
8511
+ throw new Error(`OpenCode did not report ${target} within ${timeoutMs}ms after the service restart`);
8512
+ await new Promise((resolve) => setTimeout(resolve, 1000));
8513
+ }
8514
+ }
8438
8515
  async function runSetup(input) {
8439
8516
  const client = await (input.findClient ?? (() => findOpenCode()))();
8440
8517
  const paths = await client.debugPaths();
@@ -8445,8 +8522,8 @@ async function runSetup(input) {
8445
8522
  const runtimeConfigRoot = input.runtimeConfigRoot ?? resolveOpenCodeConfigRoot();
8446
8523
  assertVersion(await client.version());
8447
8524
  const before = loadConfig(input.cwd, { configRoot, includeProject: false });
8448
- const schemaSource = join8(dirname6(before.sources[0]), "schema.json");
8449
- const packagedSchema = readFileSync7(schemaSource, "utf8");
8525
+ const schemaSource = join9(dirname6(before.sources[0]), "schema.json");
8526
+ const packagedSchema = readFileSync8(schemaSource, "utf8");
8450
8527
  const previewSnapshot = preflightGlobalConfig(configRoot, packagedSchema);
8451
8528
  const preview = writeManagedAgents({ configRoot, agents: before.agents, check: true });
8452
8529
  const profile = await selectProfile(input, client, configRoot);
@@ -8454,17 +8531,17 @@ async function runSetup(input) {
8454
8531
  return { status: "cancelled" };
8455
8532
  input.output?.([
8456
8533
  `Register ${PACKAGE_SPEC}`,
8457
- `Write ${join8(configRoot, "gvozd", "config.jsonc")}`,
8458
- `Write managed agents in ${join8(configRoot, "agents")}`,
8534
+ `Write ${join9(configRoot, "gvozd", "config.jsonc")}`,
8535
+ `Write managed agents in ${join9(configRoot, "agents")}`,
8459
8536
  ...preview.removed.length > 0 ? [`Remove ${preview.removed.length} stale or disabled managed agent(s)`] : []
8460
8537
  ].join(`
8461
8538
  `));
8462
8539
  if (!await confirm2(input, "Run setup?"))
8463
8540
  return { status: "cancelled" };
8464
- return withExclusiveFileLock(join8(configRoot, "gvozd", "setup.lock"), async () => {
8541
+ return withExclusiveFileLock(join9(configRoot, "gvozd", "setup.lock"), async () => {
8465
8542
  if (secureCanonicalPath(configRoot, "OpenCode config root") !== configRoot)
8466
8543
  throw new Error("OpenCode config root changed while setup awaited the lock");
8467
- const lockedSchema = readFileSync7(schemaSource, "utf8");
8544
+ const lockedSchema = readFileSync8(schemaSource, "utf8");
8468
8545
  const snapshot = preflightGlobalConfig(configRoot, lockedSchema);
8469
8546
  if (!sameSnapshot(previewSnapshot, snapshot))
8470
8547
  throw new Error("Global Gvozd configuration changed while setup awaited confirmation; review and rerun setup");
@@ -8486,6 +8563,7 @@ async function runSetup(input) {
8486
8563
  const configured = loadConfig(input.cwd, { configRoot, includeProject: false });
8487
8564
  writeManagedAgents({ configRoot, agents: configured.agents });
8488
8565
  await client.serviceRestart();
8566
+ await awaitRegisteredPlugin(client);
8489
8567
  const report = await runDoctor({ client, configRoot, runtimeConfigRoot, cwd: input.cwd });
8490
8568
  return { status: "complete", report };
8491
8569
  } catch (error) {
@@ -8504,16 +8582,16 @@ async function runConfigure(input) {
8504
8582
  const runtimeConfigRoot = input.runtimeConfigRoot ?? resolveOpenCodeConfigRoot();
8505
8583
  assertVersion(await client.version());
8506
8584
  const config = loadConfig(input.cwd, { configRoot, includeProject: false });
8507
- const schemaSource = join8(dirname6(config.sources[0]), "schema.json");
8508
- const packagedSchema = readFileSync7(schemaSource, "utf8");
8585
+ const schemaSource = join9(dirname6(config.sources[0]), "schema.json");
8586
+ const packagedSchema = readFileSync8(schemaSource, "utf8");
8509
8587
  const previewSnapshot = preflightGlobalConfig(configRoot, packagedSchema);
8510
8588
  const profile = await selectProfile(input, client, configRoot);
8511
8589
  if (!profile || !await confirm2(input, "Apply model configuration?"))
8512
8590
  return { status: "cancelled" };
8513
- return withExclusiveFileLock(join8(configRoot, "gvozd", "setup.lock"), async () => {
8591
+ return withExclusiveFileLock(join9(configRoot, "gvozd", "setup.lock"), async () => {
8514
8592
  if (secureCanonicalPath(configRoot, "OpenCode config root") !== configRoot)
8515
8593
  throw new Error("OpenCode config root changed while configuration awaited the lock");
8516
- const lockedSchema = readFileSync7(schemaSource, "utf8");
8594
+ const lockedSchema = readFileSync8(schemaSource, "utf8");
8517
8595
  const snapshot = preflightGlobalConfig(configRoot, lockedSchema);
8518
8596
  if (!sameSnapshot(previewSnapshot, snapshot))
8519
8597
  throw new Error("Global Gvozd configuration changed while configuration awaited confirmation; review and rerun");
@@ -8537,13 +8615,13 @@ function setupExitCode(result) {
8537
8615
  }
8538
8616
 
8539
8617
  // src/sync.ts
8540
- import { closeSync as closeSync4, lstatSync as lstatSync8, mkdirSync as mkdirSync4, openSync as openSync4, readFileSync as readFileSync8, readdirSync as readdirSync5, realpathSync as realpathSync4, renameSync as renameSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync4 } from "node:fs";
8618
+ import { closeSync as closeSync4, lstatSync as lstatSync8, mkdirSync as mkdirSync4, openSync as openSync4, readFileSync as readFileSync9, readdirSync as readdirSync5, realpathSync as realpathSync4, renameSync as renameSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync4 } from "node:fs";
8541
8619
  import { randomUUID as randomUUID4 } from "node:crypto";
8542
- import { basename, dirname as dirname7, join as join9, relative as relative4 } from "node:path";
8620
+ import { basename, dirname as dirname7, join as join10, relative as relative4 } from "node:path";
8543
8621
  function renderPluginEntrypoint(config, destination) {
8544
8622
  let moduleSpecifier = "agent-gvozd/server";
8545
8623
  if (realpathSync4(config.packageRoot) === realpathSync4(config.projectRoot)) {
8546
- moduleSpecifier = relative4(destination, join9(config.packageRoot, "src", "index")).replaceAll("\\", "/");
8624
+ moduleSpecifier = relative4(destination, join10(config.packageRoot, "src", "index")).replaceAll("\\", "/");
8547
8625
  if (!moduleSpecifier.startsWith("."))
8548
8626
  moduleSpecifier = `./${moduleSpecifier}`;
8549
8627
  }
@@ -8588,7 +8666,7 @@ function stat2(path) {
8588
8666
  function safeDirectory(root, segments, create) {
8589
8667
  let current = realpathSync4(root);
8590
8668
  for (const segment of segments) {
8591
- current = join9(current, segment);
8669
+ current = join10(current, segment);
8592
8670
  const currentStat = stat2(current);
8593
8671
  if (currentStat) {
8594
8672
  if (currentStat.isSymbolicLink() || !currentStat.isDirectory()) {
@@ -8632,7 +8710,7 @@ function replaceFile2(path, content) {
8632
8710
  function planProjectTemplate(config, result, check) {
8633
8711
  const directory = safeDirectory(config.projectRoot, ["docs", ".gvozd"], !check);
8634
8712
  const writes = [];
8635
- const configPath = join9(directory, "config.jsonc");
8713
+ const configPath = join10(directory, "config.jsonc");
8636
8714
  if (!assertRegularFile(configPath)) {
8637
8715
  result.created.push(configPath);
8638
8716
  writes.push({
@@ -8649,14 +8727,14 @@ function planProjectTemplate(config, result, check) {
8649
8727
  `)
8650
8728
  });
8651
8729
  }
8652
- const schemaSource = join9(dirname7(config.sources[0]), "schema.json");
8653
- const schemaTarget = join9(directory, "schema.json");
8654
- const schema = readFileSync8(schemaSource, "utf8");
8730
+ const schemaSource = join10(dirname7(config.sources[0]), "schema.json");
8731
+ const schemaTarget = join10(directory, "schema.json");
8732
+ const schema = readFileSync9(schemaSource, "utf8");
8655
8733
  if (!assertRegularFile(schemaTarget)) {
8656
8734
  result.created.push(schemaTarget);
8657
8735
  writes.push({ target: schemaTarget, content: schema, replace: false });
8658
8736
  } else {
8659
- const current = readFileSync8(schemaTarget, "utf8");
8737
+ const current = readFileSync9(schemaTarget, "utf8");
8660
8738
  if (current !== schema) {
8661
8739
  if (!hasGeneratedSchemaMarker(current) && !isEquivalentLegacySchema(current, schema)) {
8662
8740
  throw new Error(`Refusing to overwrite an unmanaged project schema: ${schemaTarget}`);
@@ -8681,8 +8759,8 @@ function syncAgentsUnlocked(config, options) {
8681
8759
  for (const entry of readdirSync5(destination, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
8682
8760
  if (!entry.isFile() || !entry.name.endsWith(".md") || enabled.has(entry.name))
8683
8761
  continue;
8684
- const target = join9(destination, entry.name);
8685
- const current = readFileSync8(target, "utf8");
8762
+ const target = join10(destination, entry.name);
8763
+ const current = readFileSync9(target, "utf8");
8686
8764
  if (!hasGeneratedAgentMarker(current))
8687
8765
  continue;
8688
8766
  result.removed.push(target);
@@ -8693,14 +8771,14 @@ function syncAgentsUnlocked(config, options) {
8693
8771
  for (const [id, agent] of Object.entries(config.agents)) {
8694
8772
  if (agent.disabled)
8695
8773
  continue;
8696
- const target = join9(destination, `${id}.md`);
8774
+ const target = join10(destination, `${id}.md`);
8697
8775
  const content = renderAgent(agent);
8698
8776
  if (!assertRegularFile(target)) {
8699
8777
  result.created.push(target);
8700
8778
  writes.push({ target, content, replace: false });
8701
8779
  continue;
8702
8780
  }
8703
- const current = readFileSync8(target, "utf8");
8781
+ const current = readFileSync9(target, "utf8");
8704
8782
  if (current === content) {
8705
8783
  result.unchanged.push(target);
8706
8784
  continue;
@@ -8712,11 +8790,11 @@ function syncAgentsUnlocked(config, options) {
8712
8790
  options.onDiff?.(renderDiff(target, current, content));
8713
8791
  writes.push({ target, content, replace: true, previous: current });
8714
8792
  }
8715
- const pluginTarget = join9(pluginDestination, "index.ts");
8793
+ const pluginTarget = join10(pluginDestination, "index.ts");
8716
8794
  const pluginContent = renderPluginEntrypoint(config, pluginDestination);
8717
8795
  if (!options.devPlugin) {
8718
8796
  if (assertRegularFile(pluginTarget)) {
8719
- const current = readFileSync8(pluginTarget, "utf8");
8797
+ const current = readFileSync9(pluginTarget, "utf8");
8720
8798
  if (hasGeneratedPluginMarker(current)) {
8721
8799
  result.removed.push(pluginTarget);
8722
8800
  removals.set(pluginTarget, current);
@@ -8727,7 +8805,7 @@ function syncAgentsUnlocked(config, options) {
8727
8805
  result.created.push(pluginTarget);
8728
8806
  pluginWrite = { target: pluginTarget, content: pluginContent, replace: false };
8729
8807
  } else {
8730
- const current = readFileSync8(pluginTarget, "utf8");
8808
+ const current = readFileSync9(pluginTarget, "utf8");
8731
8809
  if (current === pluginContent) {
8732
8810
  result.unchanged.push(pluginTarget);
8733
8811
  } else {
@@ -8746,7 +8824,7 @@ function syncAgentsUnlocked(config, options) {
8746
8824
  if (!assertRegularFile(target)) {
8747
8825
  throw new Error(`Refusing to remove a changed or unsafe generated file: ${target}`);
8748
8826
  }
8749
- const current = readFileSync8(target, "utf8");
8827
+ const current = readFileSync9(target, "utf8");
8750
8828
  const generated = target.endsWith("index.ts") ? hasGeneratedPluginMarker(current) : hasGeneratedAgentMarker(current);
8751
8829
  if (!generated || current !== removals.get(target)) {
8752
8830
  throw new Error(`Refusing to remove a concurrently changed generated file: ${target}`);
@@ -8755,27 +8833,27 @@ function syncAgentsUnlocked(config, options) {
8755
8833
  }
8756
8834
  for (const write of writes) {
8757
8835
  if (!write.replace) {
8758
- createFile2(join9(writableDestination, basename(write.target)), write.content);
8836
+ createFile2(join10(writableDestination, basename(write.target)), write.content);
8759
8837
  continue;
8760
8838
  }
8761
8839
  if (!assertRegularFile(write.target)) {
8762
8840
  throw new Error(`Refusing to replace a changed or unsafe agent file: ${write.target}`);
8763
8841
  }
8764
- const current = readFileSync8(write.target, "utf8");
8842
+ const current = readFileSync9(write.target, "utf8");
8765
8843
  if (!hasGeneratedAgentMarker(current) || current !== write.previous) {
8766
8844
  throw new Error(`Refusing to replace a concurrently changed agent file: ${write.target}`);
8767
8845
  }
8768
8846
  replaceFile2(write.target, write.content);
8769
8847
  }
8770
8848
  if (pluginWrite) {
8771
- const target = join9(writablePluginDestination, basename(pluginWrite.target));
8849
+ const target = join10(writablePluginDestination, basename(pluginWrite.target));
8772
8850
  if (!pluginWrite.replace) {
8773
8851
  createFile2(target, pluginWrite.content);
8774
8852
  } else {
8775
8853
  if (!assertRegularFile(target)) {
8776
8854
  throw new Error(`Refusing to replace a changed or unsafe plugin entrypoint: ${target}`);
8777
8855
  }
8778
- const current = readFileSync8(target, "utf8");
8856
+ const current = readFileSync9(target, "utf8");
8779
8857
  if (!hasGeneratedPluginMarker(current) || current !== pluginWrite.previous) {
8780
8858
  throw new Error(`Refusing to replace a concurrently changed plugin entrypoint: ${target}`);
8781
8859
  }
@@ -8784,11 +8862,11 @@ function syncAgentsUnlocked(config, options) {
8784
8862
  }
8785
8863
  const templateDirectory = safeDirectory(config.projectRoot, ["docs", ".gvozd"], true);
8786
8864
  for (const write of templateWrites) {
8787
- const target = join9(templateDirectory, basename(write.target));
8865
+ const target = join10(templateDirectory, basename(write.target));
8788
8866
  if (!write.replace)
8789
8867
  createFile2(target, write.content);
8790
8868
  else {
8791
- if (!assertRegularFile(target) || readFileSync8(target, "utf8") !== write.previous) {
8869
+ if (!assertRegularFile(target) || readFileSync9(target, "utf8") !== write.previous) {
8792
8870
  throw new Error(`Refusing to replace a concurrently changed project schema: ${target}`);
8793
8871
  }
8794
8872
  replaceFile2(target, write.content);
@@ -8803,7 +8881,7 @@ function syncAgents(config, options = {}) {
8803
8881
  if (options.check)
8804
8882
  return syncAgentsUnlocked(config, options);
8805
8883
  const root = realpathSync4(config.projectRoot);
8806
- return withExclusiveFileLockSync(join9(root, ".agent-gvozd-sync.lock"), () => syncAgentsUnlocked(config, options), "sync");
8884
+ return withExclusiveFileLockSync(join10(root, ".agent-gvozd-sync.lock"), () => syncAgentsUnlocked(config, options), "sync");
8807
8885
  }
8808
8886
  function formatSyncResult(result, check) {
8809
8887
  const lines = [check ? "agent-gvozd sync check" : "agent-gvozd sync complete"];
@@ -8838,6 +8916,8 @@ var HELP = [
8838
8916
  "",
8839
8917
  "Commands:",
8840
8918
  " setup [--yes] Install or upgrade the global agent team",
8919
+ " agents [list] Show the resolved agent team",
8920
+ " agents disable <id> | enable <id> Toggle an agent in the global config",
8841
8921
  " config [--yes] Configure model preferences",
8842
8922
  " doctor [--json] Diagnose the global installation",
8843
8923
  " sync [--check] [--dev-plugin] Maintain the project-local installation",
@@ -8922,6 +9002,30 @@ async function runCli(args, io = defaultIO, commands = { setup: runSetup, config
8922
9002
  io.stdout(formatSyncResult(result, check));
8923
9003
  return check && result.created.length + result.updated.length + result.removed.length > 0 ? 1 : 0;
8924
9004
  }
9005
+ if (command === "agents") {
9006
+ const [action, agentID] = rest;
9007
+ if (!action || action === "list") {
9008
+ if (rest.length > 1)
9009
+ return usage(io);
9010
+ const rows = listAgents(io.cwd());
9011
+ for (const row of rows) {
9012
+ io.stdout(`${row.disabled ? "✗" : " "} ${row.id.padEnd(12)} ${row.mode.padEnd(8)} ${row.lease.padEnd(12)} ${row.model}`);
9013
+ }
9014
+ return 0;
9015
+ }
9016
+ if (action === "disable" || action === "enable") {
9017
+ if (agentID === undefined || agentID.startsWith("--") || rest.length > 2)
9018
+ return usage(io);
9019
+ const configRoot = resolveOpenCodeConfigRoot();
9020
+ preflightGlobalConfig(configRoot);
9021
+ const snapshot2 = snapshot(join11(configRoot, "gvozd", "config.jsonc"));
9022
+ const next = setAgentDisabled(readGlobalConfig(configRoot), agentID, action === "disable");
9023
+ writeManagedGlobalFile(join11(configRoot, "gvozd", "config.jsonc"), next, snapshot2);
9024
+ io.stdout(`${action}d ${agentID}; run gvozd sync and gvozd doctor to apply`);
9025
+ return 0;
9026
+ }
9027
+ return usage(io);
9028
+ }
8925
9029
  if (command === "trust-project") {
8926
9030
  const parsed = parseFlags(rest, []);
8927
9031
  if (!parsed || parsed.positional.length > 1)