@codecell-germany/company-agent-wiki-skill 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1189,8 +1189,8 @@ var require_command = __commonJS({
1189
1189
  "node_modules/commander/lib/command.js"(exports2) {
1190
1190
  var EventEmitter = require("node:events").EventEmitter;
1191
1191
  var childProcess = require("node:child_process");
1192
- var path8 = require("node:path");
1193
- var fs8 = require("node:fs");
1192
+ var path9 = require("node:path");
1193
+ var fs9 = require("node:fs");
1194
1194
  var process2 = require("node:process");
1195
1195
  var { Argument: Argument2, humanReadableArgName } = require_argument();
1196
1196
  var { CommanderError: CommanderError2 } = require_error();
@@ -2184,7 +2184,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2184
2184
  * @param {string} subcommandName
2185
2185
  */
2186
2186
  _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
2187
- if (fs8.existsSync(executableFile)) return;
2187
+ if (fs9.existsSync(executableFile)) return;
2188
2188
  const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
2189
2189
  const executableMissing = `'${executableFile}' does not exist
2190
2190
  - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
@@ -2202,11 +2202,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
2202
2202
  let launchWithNode = false;
2203
2203
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2204
2204
  function findFile(baseDir, baseName) {
2205
- const localBin = path8.resolve(baseDir, baseName);
2206
- if (fs8.existsSync(localBin)) return localBin;
2207
- if (sourceExt.includes(path8.extname(baseName))) return void 0;
2205
+ const localBin = path9.resolve(baseDir, baseName);
2206
+ if (fs9.existsSync(localBin)) return localBin;
2207
+ if (sourceExt.includes(path9.extname(baseName))) return void 0;
2208
2208
  const foundExt = sourceExt.find(
2209
- (ext) => fs8.existsSync(`${localBin}${ext}`)
2209
+ (ext) => fs9.existsSync(`${localBin}${ext}`)
2210
2210
  );
2211
2211
  if (foundExt) return `${localBin}${foundExt}`;
2212
2212
  return void 0;
@@ -2218,21 +2218,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
2218
2218
  if (this._scriptPath) {
2219
2219
  let resolvedScriptPath;
2220
2220
  try {
2221
- resolvedScriptPath = fs8.realpathSync(this._scriptPath);
2221
+ resolvedScriptPath = fs9.realpathSync(this._scriptPath);
2222
2222
  } catch {
2223
2223
  resolvedScriptPath = this._scriptPath;
2224
2224
  }
2225
- executableDir = path8.resolve(
2226
- path8.dirname(resolvedScriptPath),
2225
+ executableDir = path9.resolve(
2226
+ path9.dirname(resolvedScriptPath),
2227
2227
  executableDir
2228
2228
  );
2229
2229
  }
2230
2230
  if (executableDir) {
2231
2231
  let localFile = findFile(executableDir, executableFile);
2232
2232
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2233
- const legacyName = path8.basename(
2233
+ const legacyName = path9.basename(
2234
2234
  this._scriptPath,
2235
- path8.extname(this._scriptPath)
2235
+ path9.extname(this._scriptPath)
2236
2236
  );
2237
2237
  if (legacyName !== this._name) {
2238
2238
  localFile = findFile(
@@ -2243,7 +2243,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2243
2243
  }
2244
2244
  executableFile = localFile || executableFile;
2245
2245
  }
2246
- launchWithNode = sourceExt.includes(path8.extname(executableFile));
2246
+ launchWithNode = sourceExt.includes(path9.extname(executableFile));
2247
2247
  let proc;
2248
2248
  if (process2.platform !== "win32") {
2249
2249
  if (launchWithNode) {
@@ -3158,7 +3158,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3158
3158
  * @return {Command}
3159
3159
  */
3160
3160
  nameFromFilename(filename) {
3161
- this._name = path8.basename(filename, path8.extname(filename));
3161
+ this._name = path9.basename(filename, path9.extname(filename));
3162
3162
  return this;
3163
3163
  }
3164
3164
  /**
@@ -3172,9 +3172,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3172
3172
  * @param {string} [path]
3173
3173
  * @return {(string|null|Command)}
3174
3174
  */
3175
- executableDir(path9) {
3176
- if (path9 === void 0) return this._executableDir;
3177
- this._executableDir = path9;
3175
+ executableDir(path10) {
3176
+ if (path10 === void 0) return this._executableDir;
3177
+ this._executableDir = path10;
3178
3178
  return this;
3179
3179
  }
3180
3180
  /**
@@ -6823,7 +6823,7 @@ var require_parse = __commonJS({
6823
6823
  var require_gray_matter = __commonJS({
6824
6824
  "node_modules/gray-matter/index.js"(exports2, module2) {
6825
6825
  "use strict";
6826
- var fs8 = require("fs");
6826
+ var fs9 = require("fs");
6827
6827
  var sections = require_section_matter();
6828
6828
  var defaults = require_defaults();
6829
6829
  var stringify = require_stringify();
@@ -6907,7 +6907,7 @@ var require_gray_matter = __commonJS({
6907
6907
  return stringify(file, data, options3);
6908
6908
  };
6909
6909
  matter2.read = function(filepath, options3) {
6910
- const str2 = fs8.readFileSync(filepath, "utf8");
6910
+ const str2 = fs9.readFileSync(filepath, "utf8");
6911
6911
  const file = matter2(str2, options3);
6912
6912
  file.path = filepath;
6913
6913
  return file;
@@ -6936,8 +6936,8 @@ var require_gray_matter = __commonJS({
6936
6936
  });
6937
6937
 
6938
6938
  // src/index.ts
6939
- var import_node_fs7 = __toESM(require("node:fs"));
6940
- var import_node_path7 = __toESM(require("node:path"));
6939
+ var import_node_fs8 = __toESM(require("node:fs"));
6940
+ var import_node_path8 = __toESM(require("node:path"));
6941
6941
 
6942
6942
  // node_modules/commander/esm.mjs
6943
6943
  var import_index = __toESM(require_commander(), 1);
@@ -6963,8 +6963,10 @@ var WORKSPACE_INTERNAL_DIR = ".company-agent-wiki";
6963
6963
  var WORKSPACE_CONFIG_FILE = "workspace.json";
6964
6964
  var INDEX_DB_FILE = "index.sqlite";
6965
6965
  var INDEX_MANIFEST_FILE = "index-manifest.json";
6966
+ var GLOBAL_REGISTRY_DIR_NAME = "company-agent-wiki";
6967
+ var GLOBAL_REGISTRY_FILE = "workspaces.json";
6966
6968
  var WORKSPACE_LAYOUT_VERSION = 1;
6967
- var CLI_SCHEMA_VERSION = "2026-04-12";
6969
+ var CLI_SCHEMA_VERSION = "2026-04-13";
6968
6970
  var INDEX_SCHEMA_VERSION = 1;
6969
6971
  var DEFAULT_MANAGED_ROOT_ID = "canonical";
6970
6972
  var DEFAULT_MANAGED_ROOT_PATH = "knowledge/canonical";
@@ -6979,6 +6981,7 @@ var EXIT_CODES = {
6979
6981
  notFound: 6,
6980
6982
  git: 7,
6981
6983
  sqliteLocked: 8,
6984
+ workspaceBusy: 9,
6982
6985
  runtime: 10
6983
6986
  };
6984
6987
 
@@ -7139,8 +7142,8 @@ function getGitDiff(filePath, baseRef, compareRef) {
7139
7142
  }
7140
7143
 
7141
7144
  // src/lib/indexer.ts
7142
- var import_node_fs4 = __toESM(require("node:fs"));
7143
- var import_node_path5 = __toESM(require("node:path"));
7145
+ var import_node_fs5 = __toESM(require("node:fs"));
7146
+ var import_node_path6 = __toESM(require("node:path"));
7144
7147
  var import_better_sqlite3 = __toESM(require("better-sqlite3"));
7145
7148
 
7146
7149
  // src/lib/fs-utils.ts
@@ -7159,6 +7162,7 @@ function writeJsonFile(targetPath, value) {
7159
7162
  }
7160
7163
  function writeJsonAtomic(targetPath, value) {
7161
7164
  const tempPath = `${targetPath}.tmp`;
7165
+ ensureDir(import_node_path2.default.dirname(targetPath));
7162
7166
  writeJsonFile(tempPath, value);
7163
7167
  import_node_fs2.default.renameSync(tempPath, targetPath);
7164
7168
  }
@@ -7340,8 +7344,29 @@ var import_node_path4 = __toESM(require("node:path"));
7340
7344
  function getDefaultCodexHome() {
7341
7345
  return process.env.CODEX_HOME || import_node_path4.default.join(import_node_os.default.homedir(), ".codex");
7342
7346
  }
7347
+ function getGlobalRegistryDir() {
7348
+ const explicit = process.env.COMPANY_AGENT_WIKI_CONFIG_HOME;
7349
+ if (explicit?.trim()) {
7350
+ return import_node_path4.default.resolve(explicit);
7351
+ }
7352
+ if (process.env.VITEST || process.env.NODE_ENV === "test") {
7353
+ return import_node_path4.default.join(import_node_os.default.tmpdir(), GLOBAL_REGISTRY_DIR_NAME, "vitest");
7354
+ }
7355
+ if (process.platform === "darwin") {
7356
+ return import_node_path4.default.join(import_node_os.default.homedir(), "Library", "Application Support", GLOBAL_REGISTRY_DIR_NAME);
7357
+ }
7358
+ if (process.platform === "win32") {
7359
+ const roaming = process.env.APPDATA || import_node_path4.default.join(import_node_os.default.homedir(), "AppData", "Roaming");
7360
+ return import_node_path4.default.join(roaming, GLOBAL_REGISTRY_DIR_NAME);
7361
+ }
7362
+ const xdgConfig = process.env.XDG_CONFIG_HOME || import_node_path4.default.join(import_node_os.default.homedir(), ".config");
7363
+ return import_node_path4.default.join(xdgConfig, GLOBAL_REGISTRY_DIR_NAME);
7364
+ }
7365
+ function getGlobalRegistryPath() {
7366
+ return import_node_path4.default.join(getGlobalRegistryDir(), GLOBAL_REGISTRY_FILE);
7367
+ }
7343
7368
  function resolveWorkspacePaths(workspaceRoot) {
7344
- const absoluteRoot = import_node_path4.default.resolve(workspaceRoot);
7369
+ const absoluteRoot = normalizeWorkspaceRootPath(workspaceRoot);
7345
7370
  const internalDir = import_node_path4.default.join(absoluteRoot, WORKSPACE_INTERNAL_DIR);
7346
7371
  return {
7347
7372
  workspaceRoot: absoluteRoot,
@@ -7367,6 +7392,160 @@ function detectWorkspaceRoot(startDir = process.cwd()) {
7367
7392
  current = parent;
7368
7393
  }
7369
7394
  }
7395
+ function createDefaultGlobalRegistry() {
7396
+ return {
7397
+ schemaVersion: WORKSPACE_LAYOUT_VERSION,
7398
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
7399
+ workspaces: []
7400
+ };
7401
+ }
7402
+ function normalizeWorkspaceRootPath(candidatePath) {
7403
+ const resolved = import_node_path4.default.resolve(candidatePath);
7404
+ try {
7405
+ return import_node_fs3.default.realpathSync.native ? import_node_fs3.default.realpathSync.native(resolved) : import_node_fs3.default.realpathSync(resolved);
7406
+ } catch {
7407
+ return resolved;
7408
+ }
7409
+ }
7410
+ function loadGlobalWorkspaceRegistry() {
7411
+ const registryPath = getGlobalRegistryPath();
7412
+ if (!fileExists(registryPath)) {
7413
+ return createDefaultGlobalRegistry();
7414
+ }
7415
+ const raw = readJsonFile(registryPath);
7416
+ const registry = createDefaultGlobalRegistry();
7417
+ registry.schemaVersion = raw.schemaVersion || registry.schemaVersion;
7418
+ registry.updatedAt = raw.updatedAt || registry.updatedAt;
7419
+ const deduped = /* @__PURE__ */ new Map();
7420
+ for (const entry of raw.workspaces || []) {
7421
+ const normalizedPath = normalizeWorkspaceRootPath(entry.path);
7422
+ if (!fileExists(resolveWorkspacePaths(normalizedPath).configPath)) {
7423
+ continue;
7424
+ }
7425
+ const normalizedEntry = {
7426
+ workspaceId: entry.workspaceId,
7427
+ path: normalizedPath,
7428
+ label: entry.label || import_node_path4.default.basename(normalizedPath),
7429
+ registeredAt: entry.registeredAt,
7430
+ lastUsedAt: entry.lastUsedAt,
7431
+ source: entry.source
7432
+ };
7433
+ const existing = deduped.get(normalizedPath);
7434
+ if (!existing || existing.lastUsedAt < normalizedEntry.lastUsedAt) {
7435
+ deduped.set(normalizedPath, normalizedEntry);
7436
+ }
7437
+ }
7438
+ registry.workspaces = Array.from(deduped.values()).sort((left, right) => left.label.localeCompare(right.label));
7439
+ if (raw.defaultWorkspace) {
7440
+ const normalizedDefault = normalizeWorkspaceRootPath(raw.defaultWorkspace);
7441
+ if (deduped.has(normalizedDefault)) {
7442
+ registry.defaultWorkspace = normalizedDefault;
7443
+ }
7444
+ }
7445
+ const serializedRaw = JSON.stringify(raw);
7446
+ const serializedNormalized = JSON.stringify(registry);
7447
+ if (serializedRaw !== serializedNormalized) {
7448
+ saveGlobalWorkspaceRegistry(registry);
7449
+ }
7450
+ return registry;
7451
+ }
7452
+ function saveGlobalWorkspaceRegistry(registry) {
7453
+ writeJsonAtomic(getGlobalRegistryPath(), registry);
7454
+ }
7455
+ function buildRegisteredWorkspace(workspaceRoot, source) {
7456
+ const resolvedRoot = normalizeWorkspaceRootPath(workspaceRoot);
7457
+ const config = loadWorkspaceConfig(resolvedRoot);
7458
+ const now = (/* @__PURE__ */ new Date()).toISOString();
7459
+ return {
7460
+ workspaceId: config.workspaceId,
7461
+ path: resolvedRoot,
7462
+ label: import_node_path4.default.basename(resolvedRoot),
7463
+ registeredAt: now,
7464
+ lastUsedAt: now,
7465
+ source
7466
+ };
7467
+ }
7468
+ function registerWorkspaceGlobally(workspaceRoot, options3) {
7469
+ const resolvedRoot = import_node_path4.default.resolve(workspaceRoot);
7470
+ const nextEntry = buildRegisteredWorkspace(resolvedRoot, options3?.source || "manual");
7471
+ const registry = loadGlobalWorkspaceRegistry();
7472
+ const existing = registry.workspaces.find((item) => item.path === resolvedRoot);
7473
+ if (existing) {
7474
+ existing.workspaceId = nextEntry.workspaceId;
7475
+ existing.label = nextEntry.label;
7476
+ existing.lastUsedAt = nextEntry.lastUsedAt;
7477
+ existing.source = nextEntry.source;
7478
+ } else {
7479
+ registry.workspaces.push(nextEntry);
7480
+ }
7481
+ if (options3?.setDefault || !registry.defaultWorkspace) {
7482
+ registry.defaultWorkspace = resolvedRoot;
7483
+ }
7484
+ registry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
7485
+ saveGlobalWorkspaceRegistry(registry);
7486
+ return registry.workspaces.find((item) => item.path === resolvedRoot) || nextEntry;
7487
+ }
7488
+ function rememberWorkspaceGlobally(workspaceRoot, options3) {
7489
+ const configPath = resolveWorkspacePaths(workspaceRoot).configPath;
7490
+ if (!fileExists(configPath)) {
7491
+ return void 0;
7492
+ }
7493
+ try {
7494
+ return registerWorkspaceGlobally(workspaceRoot, options3);
7495
+ } catch {
7496
+ return void 0;
7497
+ }
7498
+ }
7499
+ function listRegisteredWorkspaces() {
7500
+ const registry = loadGlobalWorkspaceRegistry();
7501
+ return {
7502
+ registryPath: getGlobalRegistryPath(),
7503
+ defaultWorkspace: registry.defaultWorkspace,
7504
+ workspaces: registry.workspaces.map((item) => ({
7505
+ ...item,
7506
+ exists: fileExists(resolveWorkspacePaths(item.path).configPath)
7507
+ }))
7508
+ };
7509
+ }
7510
+ function resolveWorkspaceSelection(startDir = process.cwd()) {
7511
+ const registryPath = getGlobalRegistryPath();
7512
+ const cwdWorkspace = detectWorkspaceRoot(startDir);
7513
+ if (cwdWorkspace) {
7514
+ rememberWorkspaceGlobally(cwdWorkspace, { setDefault: true, source: "detected" });
7515
+ return {
7516
+ workspaceRoot: cwdWorkspace,
7517
+ source: "cwd",
7518
+ registryPath,
7519
+ defaultWorkspace: loadGlobalWorkspaceRegistry().defaultWorkspace
7520
+ };
7521
+ }
7522
+ const registry = loadGlobalWorkspaceRegistry();
7523
+ const existingWorkspaces = registry.workspaces.filter((item) => fileExists(resolveWorkspacePaths(item.path).configPath));
7524
+ const defaultWorkspace = registry.defaultWorkspace;
7525
+ if (defaultWorkspace && fileExists(resolveWorkspacePaths(defaultWorkspace).configPath)) {
7526
+ rememberWorkspaceGlobally(defaultWorkspace, { setDefault: true, source: "runtime" });
7527
+ return {
7528
+ workspaceRoot: defaultWorkspace,
7529
+ source: "global-default",
7530
+ registryPath,
7531
+ defaultWorkspace
7532
+ };
7533
+ }
7534
+ if (existingWorkspaces.length === 1) {
7535
+ const onlyWorkspace = existingWorkspaces[0].path;
7536
+ rememberWorkspaceGlobally(onlyWorkspace, { setDefault: true, source: "runtime" });
7537
+ return {
7538
+ workspaceRoot: onlyWorkspace,
7539
+ source: "single-registered",
7540
+ registryPath,
7541
+ defaultWorkspace: onlyWorkspace
7542
+ };
7543
+ }
7544
+ return {
7545
+ registryPath,
7546
+ defaultWorkspace
7547
+ };
7548
+ }
7370
7549
  function templateWorkspaceReadme() {
7371
7550
  return `# Private Company Knowledge Workspace
7372
7551
 
@@ -7734,6 +7913,7 @@ function setupWorkspace(options3) {
7734
7913
  }
7735
7914
  }
7736
7915
  }
7916
+ registerWorkspaceGlobally(paths.workspaceRoot, { setDefault: true, source: "setup" });
7737
7917
  return {
7738
7918
  workspaceRoot: paths.workspaceRoot,
7739
7919
  configPath: paths.configPath,
@@ -7743,7 +7923,8 @@ function setupWorkspace(options3) {
7743
7923
  `company-agent-wiki-cli doctor --workspace ${paths.workspaceRoot} --json`,
7744
7924
  `company-agent-wiki-cli index rebuild --workspace ${paths.workspaceRoot} --json`,
7745
7925
  `company-agent-wiki-cli verify --workspace ${paths.workspaceRoot} --json`,
7746
- `company-agent-wiki-cli serve --workspace ${paths.workspaceRoot} --port 4187`
7926
+ `company-agent-wiki-cli serve --workspace ${paths.workspaceRoot} --port 4187`,
7927
+ "Other agents can now discover this workspace automatically via the global workspace registry."
7747
7928
  ]
7748
7929
  };
7749
7930
  }
@@ -7776,6 +7957,7 @@ function addRoot(workspaceRoot, rootDefinition) {
7776
7957
  };
7777
7958
  config.roots.push(root);
7778
7959
  saveWorkspaceConfig(workspaceRoot, config);
7960
+ rememberWorkspaceGlobally(workspaceRoot, { setDefault: true, source: "runtime" });
7779
7961
  return root;
7780
7962
  }
7781
7963
  function listRoots(workspaceRoot) {
@@ -7797,6 +7979,8 @@ function doctor(workspaceRoot) {
7797
7979
  const codexBinDir = import_node_path4.default.join(codexHome, "bin");
7798
7980
  const codexShimPath = import_node_path4.default.join(codexBinDir, CLI_NAME);
7799
7981
  const pathEntries = (process.env.PATH || "").split(import_node_path4.default.delimiter).filter(Boolean);
7982
+ const registryPath = getGlobalRegistryPath();
7983
+ const registry = loadGlobalWorkspaceRegistry();
7800
7984
  checks.push({
7801
7985
  name: "workspace-root",
7802
7986
  ok: isDirectory(paths.workspaceRoot),
@@ -7822,6 +8006,16 @@ function doctor(workspaceRoot) {
7822
8006
  ok: pathEntries.includes(codexBinDir),
7823
8007
  message: pathEntries.includes(codexBinDir) ? `Codex bin directory is available in PATH: ${codexBinDir}` : `Codex bin directory is not in PATH: ${codexBinDir}`
7824
8008
  });
8009
+ checks.push({
8010
+ name: "global-workspace-registry",
8011
+ ok: fileExists(registryPath),
8012
+ message: fileExists(registryPath) ? `Global workspace registry found: ${registryPath}` : `Global workspace registry missing: ${registryPath}`
8013
+ });
8014
+ checks.push({
8015
+ name: "global-default-workspace",
8016
+ ok: typeof registry.defaultWorkspace === "string" && fileExists(resolveWorkspacePaths(registry.defaultWorkspace).configPath),
8017
+ message: typeof registry.defaultWorkspace === "string" ? `Global default workspace: ${registry.defaultWorkspace}` : "No global default workspace registered yet."
8018
+ });
7825
8019
  if (fileExists(paths.configPath)) {
7826
8020
  const config = loadWorkspaceConfig(workspaceRoot);
7827
8021
  for (const root of config.roots) {
@@ -7849,6 +8043,121 @@ function doctor(workspaceRoot) {
7849
8043
  };
7850
8044
  }
7851
8045
 
8046
+ // src/lib/write-lock.ts
8047
+ var import_node_fs4 = __toESM(require("node:fs"));
8048
+ var import_node_path5 = __toESM(require("node:path"));
8049
+ var LOCK_FILE_NAME = "write.lock";
8050
+ var LOCK_WAIT_TIMEOUT_MS = 6e4;
8051
+ var LOCK_POLL_INTERVAL_MS = 125;
8052
+ var LOCK_STALE_AFTER_MS = 10 * 6e4;
8053
+ function sleepMs(durationMs) {
8054
+ const shared = new SharedArrayBuffer(4);
8055
+ const array = new Int32Array(shared);
8056
+ Atomics.wait(array, 0, 0, durationMs);
8057
+ }
8058
+ function getLockPath(workspaceRoot) {
8059
+ return import_node_path5.default.join(import_node_path5.default.resolve(workspaceRoot), WORKSPACE_INTERNAL_DIR, LOCK_FILE_NAME);
8060
+ }
8061
+ function readLockPayload(lockPath) {
8062
+ if (!fileExists(lockPath)) {
8063
+ return void 0;
8064
+ }
8065
+ try {
8066
+ return readJsonFile(lockPath);
8067
+ } catch {
8068
+ return void 0;
8069
+ }
8070
+ }
8071
+ function isProcessAlive(pid) {
8072
+ if (!pid || pid <= 0) {
8073
+ return false;
8074
+ }
8075
+ try {
8076
+ process.kill(pid, 0);
8077
+ return true;
8078
+ } catch (error) {
8079
+ const code = error.code;
8080
+ return code !== "ESRCH";
8081
+ }
8082
+ }
8083
+ function canBreakLock(payload) {
8084
+ if (!payload) {
8085
+ return true;
8086
+ }
8087
+ const acquiredAt = Date.parse(payload.acquiredAt);
8088
+ const ageMs = Number.isNaN(acquiredAt) ? Number.POSITIVE_INFINITY : Date.now() - acquiredAt;
8089
+ if (ageMs > LOCK_STALE_AFTER_MS) {
8090
+ return true;
8091
+ }
8092
+ return !isProcessAlive(payload.pid);
8093
+ }
8094
+ function writeLockPayload(lockPath, payload) {
8095
+ const tempPath = `${lockPath}.${payload.token}.tmp`;
8096
+ writeJsonAtomic(tempPath, payload);
8097
+ import_node_fs4.default.renameSync(tempPath, lockPath);
8098
+ }
8099
+ function createLockPayload(workspaceRoot, reason) {
8100
+ return {
8101
+ token: newBuildId(),
8102
+ pid: process.pid,
8103
+ reason,
8104
+ workspaceRoot: import_node_path5.default.resolve(workspaceRoot),
8105
+ acquiredAt: (/* @__PURE__ */ new Date()).toISOString()
8106
+ };
8107
+ }
8108
+ function withWorkspaceWriteLock(workspaceRoot, reason, callback, options3) {
8109
+ const lockPath = getLockPath(workspaceRoot);
8110
+ const timeoutMs = options3?.timeoutMs ?? LOCK_WAIT_TIMEOUT_MS;
8111
+ const deadline = Date.now() + timeoutMs;
8112
+ const payload = createLockPayload(workspaceRoot, reason);
8113
+ ensureDir(import_node_path5.default.dirname(lockPath));
8114
+ while (true) {
8115
+ try {
8116
+ const fileDescriptor = import_node_fs4.default.openSync(lockPath, "wx");
8117
+ import_node_fs4.default.closeSync(fileDescriptor);
8118
+ writeLockPayload(lockPath, payload);
8119
+ break;
8120
+ } catch (error) {
8121
+ const code = error.code;
8122
+ if (code !== "EEXIST") {
8123
+ throw error;
8124
+ }
8125
+ const existing = readLockPayload(lockPath);
8126
+ if (canBreakLock(existing)) {
8127
+ import_node_fs4.default.rmSync(lockPath, { force: true });
8128
+ continue;
8129
+ }
8130
+ if (Date.now() >= deadline) {
8131
+ throw new CliError(
8132
+ "WORKSPACE_BUSY",
8133
+ "Another write operation is already running for this workspace.",
8134
+ EXIT_CODES.workspaceBusy,
8135
+ {
8136
+ hint: "Parallel reads should continue to work. For writes or auto-rebuilds, wait for the active write to finish and retry.",
8137
+ details: {
8138
+ lockPath,
8139
+ holder: existing
8140
+ }
8141
+ }
8142
+ );
8143
+ }
8144
+ sleepMs(LOCK_POLL_INTERVAL_MS);
8145
+ }
8146
+ }
8147
+ const artificialDelay = Number(process.env.COMPANY_AGENT_WIKI_TEST_WRITE_LOCK_DELAY_MS || "0");
8148
+ if (Number.isFinite(artificialDelay) && artificialDelay > 0) {
8149
+ sleepMs(artificialDelay);
8150
+ }
8151
+ try {
8152
+ return callback();
8153
+ } finally {
8154
+ const current = readLockPayload(lockPath);
8155
+ if (current && current.token === payload.token) {
8156
+ import_node_fs4.default.rmSync(lockPath, { force: true });
8157
+ }
8158
+ }
8159
+ }
8160
+
7852
8161
  // src/lib/indexer.ts
7853
8162
  function openDatabase(databasePath, options3) {
7854
8163
  try {
@@ -7857,6 +8166,12 @@ function openDatabase(databasePath, options3) {
7857
8166
  options3?.readonly ? { readonly: true, fileMustExist: true } : void 0
7858
8167
  );
7859
8168
  database.pragma("busy_timeout = 5000");
8169
+ if (options3?.readonly) {
8170
+ database.pragma("query_only = 1");
8171
+ } else {
8172
+ database.pragma("journal_mode = WAL");
8173
+ database.pragma("synchronous = NORMAL");
8174
+ }
7860
8175
  return database;
7861
8176
  } catch (error) {
7862
8177
  throw coerceCliError(error, {
@@ -7875,11 +8190,11 @@ function closeDatabaseQuietly(database) {
7875
8190
  }
7876
8191
  function throwKnownDatabaseError(error, workspaceRoot) {
7877
8192
  const cliError = coerceCliError(error, {
7878
- sqliteLockHint: `Retry in a moment, serialize parallel CLI reads against ${import_node_path5.default.resolve(
8193
+ sqliteLockHint: `Retry in a moment, serialize parallel CLI reads against ${import_node_path6.default.resolve(
7879
8194
  workspaceRoot
7880
8195
  )}, or rerun with --auto-rebuild after the current write finishes.`,
7881
8196
  sqliteLockDetails: {
7882
- workspaceRoot: import_node_path5.default.resolve(workspaceRoot)
8197
+ workspaceRoot: import_node_path6.default.resolve(workspaceRoot)
7883
8198
  }
7884
8199
  });
7885
8200
  if (cliError) {
@@ -7961,8 +8276,8 @@ function collectRootSnapshot(rootId, rootPath, kind) {
7961
8276
  const entries = [];
7962
8277
  let latestMtimeMs = 0;
7963
8278
  for (const filePath of markdownFiles) {
7964
- const stats = import_node_fs4.default.statSync(filePath);
7965
- const relPath = import_node_path5.default.relative(rootPath, filePath);
8279
+ const stats = import_node_fs5.default.statSync(filePath);
8280
+ const relPath = import_node_path6.default.relative(rootPath, filePath);
7966
8281
  latestMtimeMs = Math.max(latestMtimeMs, Math.trunc(stats.mtimeMs));
7967
8282
  entries.push(`${relPath}|${stats.size}|${Math.trunc(stats.mtimeMs)}`);
7968
8283
  }
@@ -8150,13 +8465,13 @@ function matchesFilters(metadata, filters) {
8150
8465
  }
8151
8466
  return true;
8152
8467
  }
8153
- function rebuildIndex(workspaceRoot) {
8468
+ function rebuildIndexUnlocked(workspaceRoot) {
8154
8469
  const config = loadWorkspaceConfig(workspaceRoot);
8155
8470
  const paths = resolveWorkspacePaths(workspaceRoot);
8156
- const tempDbPath = `${paths.indexDbPath}.next`;
8471
+ const tempDbPath = `${paths.indexDbPath}.${process.pid}.${Date.now()}.${newBuildId()}.next`;
8157
8472
  const indexedAt = (/* @__PURE__ */ new Date()).toISOString();
8158
8473
  if (fileExists(tempDbPath)) {
8159
- import_node_fs4.default.unlinkSync(tempDbPath);
8474
+ import_node_fs5.default.unlinkSync(tempDbPath);
8160
8475
  }
8161
8476
  let database;
8162
8477
  try {
@@ -8186,9 +8501,9 @@ function rebuildIndex(workspaceRoot) {
8186
8501
  insertRoot(database, snapshot, indexedAt);
8187
8502
  const markdownFiles = walkMarkdownFiles(rootPath);
8188
8503
  for (const filePath of markdownFiles) {
8189
- const rawContent = import_node_fs4.default.readFileSync(filePath, "utf8");
8190
- const stats = import_node_fs4.default.statSync(filePath);
8191
- const relPath = import_node_path5.default.relative(rootPath, filePath);
8504
+ const rawContent = import_node_fs5.default.readFileSync(filePath, "utf8");
8505
+ const stats = import_node_fs5.default.statSync(filePath);
8506
+ const relPath = import_node_path6.default.relative(rootPath, filePath);
8192
8507
  const parsed = parseMarkdownDocument(filePath, relPath, root.id, rawContent, Math.trunc(stats.mtimeMs));
8193
8508
  insertDocument(database, parsed.document);
8194
8509
  insertSections(database, parsed.document, parsed.sections);
@@ -8199,8 +8514,8 @@ function rebuildIndex(workspaceRoot) {
8199
8514
  database.exec("INSERT INTO sections_fts(sections_fts) VALUES('optimize');");
8200
8515
  closeDatabaseQuietly(database);
8201
8516
  database = void 0;
8202
- replaceFileAtomic(paths.indexDbPath, import_node_fs4.default.readFileSync(tempDbPath));
8203
- import_node_fs4.default.unlinkSync(tempDbPath);
8517
+ replaceFileAtomic(paths.indexDbPath, import_node_fs5.default.readFileSync(tempDbPath));
8518
+ import_node_fs5.default.unlinkSync(tempDbPath);
8204
8519
  const manifest = {
8205
8520
  buildId,
8206
8521
  schemaVersion: INDEX_SCHEMA_VERSION,
@@ -8215,11 +8530,14 @@ function rebuildIndex(workspaceRoot) {
8215
8530
  } catch (error) {
8216
8531
  closeDatabaseQuietly(database);
8217
8532
  if (fileExists(tempDbPath)) {
8218
- import_node_fs4.default.rmSync(tempDbPath, { force: true });
8533
+ import_node_fs5.default.rmSync(tempDbPath, { force: true });
8219
8534
  }
8220
8535
  throwKnownDatabaseError(error, workspaceRoot);
8221
8536
  }
8222
8537
  }
8538
+ function rebuildIndex(workspaceRoot) {
8539
+ return withWorkspaceWriteLock(workspaceRoot, "index-rebuild", () => rebuildIndexUnlocked(workspaceRoot));
8540
+ }
8223
8541
  function readManifest(workspaceRoot) {
8224
8542
  const paths = resolveWorkspacePaths(workspaceRoot);
8225
8543
  if (!fileExists(paths.indexManifestPath)) {
@@ -8297,7 +8615,7 @@ function requireFreshIndex(workspaceRoot, options3) {
8297
8615
  "The workspace has not been indexed yet.",
8298
8616
  EXIT_CODES.indexMissing,
8299
8617
  {
8300
- hint: verification.hint || `Run: company-agent-wiki-cli index rebuild --workspace ${import_node_path5.default.resolve(workspaceRoot)}`
8618
+ hint: verification.hint || `Run: company-agent-wiki-cli index rebuild --workspace ${import_node_path6.default.resolve(workspaceRoot)}`
8301
8619
  }
8302
8620
  );
8303
8621
  }
@@ -8310,7 +8628,7 @@ function requireFreshIndex(workspaceRoot, options3) {
8310
8628
  "The indexed snapshot no longer matches the current roots.",
8311
8629
  EXIT_CODES.indexStale,
8312
8630
  {
8313
- hint: `Run: company-agent-wiki-cli index rebuild --workspace ${import_node_path5.default.resolve(workspaceRoot)}`,
8631
+ hint: `Run: company-agent-wiki-cli index rebuild --workspace ${import_node_path6.default.resolve(workspaceRoot)}`,
8314
8632
  details: verification.roots.filter((root) => !root.ok)
8315
8633
  }
8316
8634
  );
@@ -8619,8 +8937,8 @@ function getDocumentHeadings(workspaceRoot, docId, options3) {
8619
8937
  }
8620
8938
 
8621
8939
  // src/lib/onboarding.ts
8622
- var import_node_fs5 = __toESM(require("node:fs"));
8623
- var import_node_path6 = __toESM(require("node:path"));
8940
+ var import_node_fs6 = __toESM(require("node:fs"));
8941
+ var import_node_path7 = __toESM(require("node:path"));
8624
8942
  var COMPANY_ONBOARDING_DE_V1 = {
8625
8943
  profileId: "de-company-v1",
8626
8944
  locale: "de-DE",
@@ -9212,8 +9530,8 @@ function ensureKnownAnswerKeys(payload) {
9212
9530
  }
9213
9531
  }
9214
9532
  function isPathInsideWorkspace2(workspaceRoot, candidatePath) {
9215
- const relative = import_node_path6.default.relative(import_node_path6.default.resolve(workspaceRoot), import_node_path6.default.resolve(candidatePath));
9216
- return relative === "" || !relative.startsWith("..") && !import_node_path6.default.isAbsolute(relative);
9533
+ const relative = import_node_path7.default.relative(import_node_path7.default.resolve(workspaceRoot), import_node_path7.default.resolve(candidatePath));
9534
+ return relative === "" || !relative.startsWith("..") && !import_node_path7.default.isAbsolute(relative);
9217
9535
  }
9218
9536
  function resolveManagedRoot(workspaceRoot) {
9219
9537
  const config = loadWorkspaceConfig(workspaceRoot);
@@ -9236,12 +9554,12 @@ function resolveManagedRoot(workspaceRoot) {
9236
9554
  return resolvedPath;
9237
9555
  }
9238
9556
  function createDocument(workspaceRoot, managedRoot, relPath, id, title, type, tags, body, answeredAt, answeredBy) {
9239
- const absPath = import_node_path6.default.join(managedRoot, relPath);
9557
+ const absPath = import_node_path7.default.join(managedRoot, relPath);
9240
9558
  return {
9241
9559
  docId: id,
9242
9560
  title,
9243
9561
  absPath,
9244
- relPath: import_node_path6.default.relative(workspaceRoot, absPath),
9562
+ relPath: import_node_path7.default.relative(workspaceRoot, absPath),
9245
9563
  existed: false,
9246
9564
  content: `${renderFrontmatter({ id, title, type, tags, answeredAt, answeredBy })}${body.trimEnd()}
9247
9565
  `
@@ -9440,7 +9758,7 @@ function buildCompanyDocuments(workspaceRoot, normalized) {
9440
9758
  function loadCompanyOnboardingAnswers(answerFile) {
9441
9759
  const payload = readJsonFile(answerFile);
9442
9760
  ensureKnownAnswerKeys(payload);
9443
- const stats = import_node_fs5.default.statSync(answerFile);
9761
+ const stats = import_node_fs6.default.statSync(answerFile);
9444
9762
  const normalized = normalizeAnswers(payload, stats.mtime.toISOString());
9445
9763
  if (normalized.profileId !== COMPANY_ONBOARDING_DE_V1.profileId) {
9446
9764
  throw new CliError(
@@ -9464,7 +9782,7 @@ function previewCompanyOnboarding(workspaceRoot, answerFile) {
9464
9782
  );
9465
9783
  }
9466
9784
  for (const document of materialized.documents) {
9467
- document.existed = import_node_fs5.default.existsSync(document.absPath);
9785
+ document.existed = import_node_fs6.default.existsSync(document.absPath);
9468
9786
  }
9469
9787
  return {
9470
9788
  documents: materialized.documents,
@@ -9473,39 +9791,41 @@ function previewCompanyOnboarding(workspaceRoot, answerFile) {
9473
9791
  };
9474
9792
  }
9475
9793
  function applyCompanyOnboarding(options3) {
9476
- const workspaceRoot = import_node_path6.default.resolve(options3.workspaceRoot);
9477
- const answerFile = import_node_path6.default.resolve(options3.answerFile);
9794
+ const workspaceRoot = import_node_path7.default.resolve(options3.workspaceRoot);
9795
+ const answerFile = import_node_path7.default.resolve(options3.answerFile);
9478
9796
  const preview = previewCompanyOnboarding(workspaceRoot, answerFile);
9479
9797
  const warnings = [...preview.warnings];
9480
9798
  let indexBuildId;
9481
9799
  if (options3.execute) {
9482
- const seenPaths = /* @__PURE__ */ new Set();
9483
- for (const document of preview.documents) {
9484
- if (seenPaths.has(document.absPath)) {
9485
- throw new CliError(
9486
- "ONBOARDING_TARGET_CONFLICT",
9487
- `Multiple onboarding documents resolve to the same target path: ${document.relPath}`,
9488
- EXIT_CODES.validation
9489
- );
9490
- }
9491
- seenPaths.add(document.absPath);
9492
- if (document.existed && !options3.force) {
9493
- throw new CliError(
9494
- "ONBOARDING_TARGET_EXISTS",
9495
- `Target file already exists: ${document.relPath}`,
9496
- EXIT_CODES.validation,
9497
- { hint: "Use --force to overwrite generated onboarding files." }
9498
- );
9800
+ const manifest = withWorkspaceWriteLock(workspaceRoot, "onboarding-apply", () => {
9801
+ const seenPaths = /* @__PURE__ */ new Set();
9802
+ for (const document of preview.documents) {
9803
+ if (seenPaths.has(document.absPath)) {
9804
+ throw new CliError(
9805
+ "ONBOARDING_TARGET_CONFLICT",
9806
+ `Multiple onboarding documents resolve to the same target path: ${document.relPath}`,
9807
+ EXIT_CODES.validation
9808
+ );
9809
+ }
9810
+ seenPaths.add(document.absPath);
9811
+ if (document.existed && !options3.force) {
9812
+ throw new CliError(
9813
+ "ONBOARDING_TARGET_EXISTS",
9814
+ `Target file already exists: ${document.relPath}`,
9815
+ EXIT_CODES.validation,
9816
+ { hint: "Use --force to overwrite generated onboarding files." }
9817
+ );
9818
+ }
9499
9819
  }
9500
- }
9501
- for (const document of preview.documents) {
9502
- ensureDir(import_node_path6.default.dirname(document.absPath));
9503
- if (document.existed) {
9504
- warnings.push(`Overwriting existing file: ${document.relPath}`);
9820
+ for (const document of preview.documents) {
9821
+ ensureDir(import_node_path7.default.dirname(document.absPath));
9822
+ if (document.existed) {
9823
+ warnings.push(`Overwriting existing file: ${document.relPath}`);
9824
+ }
9825
+ replaceFileAtomic(document.absPath, document.content);
9505
9826
  }
9506
- replaceFileAtomic(document.absPath, document.content);
9507
- }
9508
- const manifest = rebuildIndex(workspaceRoot);
9827
+ return rebuildIndexUnlocked(workspaceRoot);
9828
+ });
9509
9829
  indexBuildId = manifest.buildId;
9510
9830
  }
9511
9831
  return {
@@ -9568,7 +9888,7 @@ function printJson(value) {
9568
9888
  }
9569
9889
 
9570
9890
  // src/lib/server.ts
9571
- var import_node_fs6 = __toESM(require("node:fs"));
9891
+ var import_node_fs7 = __toESM(require("node:fs"));
9572
9892
  var import_node_http = __toESM(require("node:http"));
9573
9893
  var import_node_url = require("node:url");
9574
9894
 
@@ -12027,6 +12347,7 @@ function getHttpStatusCode(error) {
12027
12347
  case "INDEX_STALE":
12028
12348
  return 409;
12029
12349
  case "SQLITE_LOCKED":
12350
+ case "WORKSPACE_BUSY":
12030
12351
  return 423;
12031
12352
  default:
12032
12353
  return 500;
@@ -12133,7 +12454,7 @@ function startServer(workspaceRoot, port, options3) {
12133
12454
  return;
12134
12455
  }
12135
12456
  const resolved = resolveDocumentById(workspaceRoot, docId, { autoRebuild: options3?.autoRebuild });
12136
- const rawMarkdown = import_node_fs6.default.readFileSync(resolved.absPath, "utf8");
12457
+ const rawMarkdown = import_node_fs7.default.readFileSync(resolved.absPath, "utf8");
12137
12458
  sendJson(response, {
12138
12459
  ok: true,
12139
12460
  data: {
@@ -12189,14 +12510,16 @@ function startServer(workspaceRoot, port, options3) {
12189
12510
  // src/index.ts
12190
12511
  function assertWorkspace(workspacePath) {
12191
12512
  if (workspacePath?.trim()) {
12192
- return import_node_path7.default.resolve(workspacePath);
12513
+ const resolved = import_node_path8.default.resolve(workspacePath);
12514
+ rememberWorkspaceGlobally(resolved, { setDefault: true, source: "runtime" });
12515
+ return resolved;
12193
12516
  }
12194
- const detected = detectWorkspaceRoot(process.cwd());
12195
- if (detected) {
12196
- return detected;
12517
+ const selection = resolveWorkspaceSelection(process.cwd());
12518
+ if (selection.workspaceRoot) {
12519
+ return selection.workspaceRoot;
12197
12520
  }
12198
12521
  throw new CliError("WORKSPACE_REQUIRED", "Missing --workspace option and no workspace was detected from the current directory.", EXIT_CODES.usage, {
12199
- hint: "Pass --workspace /absolute/path or run the command from a directory inside the private workspace."
12522
+ hint: `Pass --workspace /absolute/path, run the command from a directory inside the private workspace, or register one globally in ${getGlobalRegistryPath()}.`
12200
12523
  });
12201
12524
  }
12202
12525
  function printHumanChecks(checks) {
@@ -12281,8 +12604,10 @@ program2.command("about").description("Show CLI runtime metadata and common Code
12281
12604
  cliName: CLI_NAME,
12282
12605
  schemaVersion: CLI_SCHEMA_VERSION,
12283
12606
  codexHome,
12284
- codexShimPath: import_node_path7.default.join(codexHome, "bin", CLI_NAME),
12285
- cwdWorkspace: detectWorkspaceRoot(process.cwd()) || null
12607
+ codexShimPath: import_node_path8.default.join(codexHome, "bin", CLI_NAME),
12608
+ cwdWorkspace: detectWorkspaceRoot(process.cwd()) || null,
12609
+ globalRegistryPath: getGlobalRegistryPath(),
12610
+ resolvedWorkspace: resolveWorkspaceSelection(process.cwd())
12286
12611
  };
12287
12612
  if (options3.json) {
12288
12613
  printJson(envelope("about", data));
@@ -12298,10 +12623,101 @@ program2.command("about").description("Show CLI runtime metadata and common Code
12298
12623
  process.stdout.write(` detected workspace: ${data.cwdWorkspace}
12299
12624
  `);
12300
12625
  }
12626
+ if (data.resolvedWorkspace.workspaceRoot && data.resolvedWorkspace.source !== "cwd") {
12627
+ process.stdout.write(` resolved workspace: ${data.resolvedWorkspace.workspaceRoot} (${data.resolvedWorkspace.source})
12628
+ `);
12629
+ }
12630
+ process.stdout.write(` global registry: ${data.globalRegistryPath}
12631
+ `);
12632
+ });
12633
+ var workspace = new Command("workspace").description("Manage global workspace discovery");
12634
+ workspace.command("current").description("Show the currently resolved workspace and discovery source").option("--json", "Emit JSON output", false).action((options3) => {
12635
+ const selection = resolveWorkspaceSelection(process.cwd());
12636
+ const data = {
12637
+ workspaceRoot: selection.workspaceRoot || null,
12638
+ source: selection.source || null,
12639
+ registryPath: selection.registryPath,
12640
+ defaultWorkspace: selection.defaultWorkspace || null
12641
+ };
12642
+ if (options3.json) {
12643
+ printJson(envelope("workspace current", data));
12644
+ return;
12645
+ }
12646
+ if (data.workspaceRoot) {
12647
+ process.stdout.write(`Resolved workspace: ${data.workspaceRoot}
12648
+ `);
12649
+ process.stdout.write(`Source: ${data.source}
12650
+ `);
12651
+ } else {
12652
+ process.stdout.write("No workspace resolved.\n");
12653
+ }
12654
+ process.stdout.write(`Global registry: ${data.registryPath}
12655
+ `);
12656
+ if (data.defaultWorkspace) {
12657
+ process.stdout.write(`Default workspace: ${data.defaultWorkspace}
12658
+ `);
12659
+ }
12660
+ });
12661
+ workspace.command("list").description("List globally registered workspaces").option("--json", "Emit JSON output", false).action((options3) => {
12662
+ const result = listRegisteredWorkspaces();
12663
+ if (options3.json) {
12664
+ printJson(envelope("workspace list", result));
12665
+ return;
12666
+ }
12667
+ process.stdout.write(`Global registry: ${result.registryPath}
12668
+ `);
12669
+ if (result.workspaces.length === 0) {
12670
+ process.stdout.write("No registered workspaces.\n");
12671
+ return;
12672
+ }
12673
+ for (const item of result.workspaces) {
12674
+ const defaultMarker = result.defaultWorkspace === item.path ? " [default]" : "";
12675
+ const existsMarker = item.exists ? "" : " [missing]";
12676
+ process.stdout.write(`- ${item.label}${defaultMarker}${existsMarker}
12677
+ `);
12678
+ process.stdout.write(` ${item.path}
12679
+ `);
12680
+ }
12681
+ });
12682
+ workspace.command("register").description("Register an existing workspace globally for other agents").requiredOption("--workspace <path>", "Absolute or relative workspace path").option("--default", "Also mark this workspace as the global default", false).option("--json", "Emit JSON output", false).action((options3) => {
12683
+ const entry = registerWorkspaceGlobally(import_node_path8.default.resolve(options3.workspace), {
12684
+ setDefault: Boolean(options3.default),
12685
+ source: "manual"
12686
+ });
12687
+ const result = {
12688
+ registryPath: getGlobalRegistryPath(),
12689
+ entry
12690
+ };
12691
+ if (options3.json) {
12692
+ printJson(envelope("workspace register", result));
12693
+ return;
12694
+ }
12695
+ process.stdout.write(`Registered workspace: ${entry.path}
12696
+ `);
12697
+ if (options3.default) {
12698
+ process.stdout.write("This workspace is now the global default.\n");
12699
+ }
12700
+ });
12701
+ workspace.command("use").description("Set a registered workspace as the global default").requiredOption("--workspace <path>", "Absolute or relative workspace path").option("--json", "Emit JSON output", false).action((options3) => {
12702
+ const entry = registerWorkspaceGlobally(import_node_path8.default.resolve(options3.workspace), {
12703
+ setDefault: true,
12704
+ source: "manual"
12705
+ });
12706
+ const result = {
12707
+ registryPath: getGlobalRegistryPath(),
12708
+ defaultWorkspace: entry.path
12709
+ };
12710
+ if (options3.json) {
12711
+ printJson(envelope("workspace use", result));
12712
+ return;
12713
+ }
12714
+ process.stdout.write(`Global default workspace: ${entry.path}
12715
+ `);
12301
12716
  });
12717
+ program2.addCommand(workspace);
12302
12718
  program2.command("setup").description("Workspace setup commands").addCommand(
12303
12719
  new Command("workspace").requiredOption("--workspace <path>", "Absolute or relative workspace path").option("--git-init", "Initialize a local Git repository", false).option("--git-remote <url>", "Configure a Git remote URL").option("--no-starter-docs", "Skip creation of starter Markdown documents").option("--force", "Rewrite an existing scaffold", false).option("--json", "Emit JSON output", false).action((options3) => {
12304
- const workspaceRoot = import_node_path7.default.resolve(options3.workspace);
12720
+ const workspaceRoot = import_node_path8.default.resolve(options3.workspace);
12305
12721
  const result = setupWorkspace({
12306
12722
  workspaceRoot,
12307
12723
  gitInit: Boolean(options3.gitInit),
@@ -12555,8 +12971,8 @@ program2.command("read").option("--workspace <path>", "Workspace path. Optional
12555
12971
  const metadataResult = options3.docId ? getDocumentMetadataById(workspaceRoot, options3.docId, {
12556
12972
  autoRebuild: Boolean(options3.autoRebuild)
12557
12973
  }) : (() => {
12558
- const candidatePath = import_node_path7.default.isAbsolute(options3.path) ? options3.path : import_node_path7.default.join(workspaceRoot, options3.path);
12559
- return getDocumentMetadataByPath(workspaceRoot, import_node_path7.default.resolve(candidatePath), {
12974
+ const candidatePath = import_node_path8.default.isAbsolute(options3.path) ? options3.path : import_node_path8.default.join(workspaceRoot, options3.path);
12975
+ return getDocumentMetadataByPath(workspaceRoot, import_node_path8.default.resolve(candidatePath), {
12560
12976
  autoRebuild: Boolean(options3.autoRebuild)
12561
12977
  });
12562
12978
  })();
@@ -12584,7 +13000,7 @@ program2.command("read").option("--workspace <path>", "Workspace path. Optional
12584
13000
  }
12585
13001
  return;
12586
13002
  }
12587
- const rawMarkdown = import_node_fs7.default.readFileSync(metadataResult.metadata.absPath, "utf8");
13003
+ const rawMarkdown = import_node_fs8.default.readFileSync(metadataResult.metadata.absPath, "utf8");
12588
13004
  if (options3.json) {
12589
13005
  printJson(
12590
13006
  envelope(
@@ -12606,7 +13022,7 @@ program2.command("read").option("--workspace <path>", "Workspace path. Optional
12606
13022
  });
12607
13023
  program2.command("history").option("--workspace <path>", "Workspace path. Optional when current directory is already inside a workspace.").option("--doc-id <id>", "Indexed document identifier").option("--path <path>", "Absolute or workspace-relative document path").option("--limit <number>", "Maximum number of commits", "20").option("--json", "Emit JSON output", false).action((options3) => {
12608
13024
  const workspaceRoot = assertWorkspace(options3.workspace);
12609
- const resolvedPath = options3.docId ? resolveDocumentById(workspaceRoot, options3.docId).absPath : import_node_path7.default.resolve(import_node_path7.default.isAbsolute(options3.path) ? options3.path : import_node_path7.default.join(workspaceRoot, options3.path));
13025
+ const resolvedPath = options3.docId ? resolveDocumentById(workspaceRoot, options3.docId).absPath : import_node_path8.default.resolve(import_node_path8.default.isAbsolute(options3.path) ? options3.path : import_node_path8.default.join(workspaceRoot, options3.path));
12610
13026
  const history = getGitHistory(resolvedPath, Number(options3.limit));
12611
13027
  if (options3.json) {
12612
13028
  printJson(envelope("history", { path: resolvedPath, history }));
@@ -12619,7 +13035,7 @@ program2.command("history").option("--workspace <path>", "Workspace path. Option
12619
13035
  });
12620
13036
  program2.command("diff").option("--workspace <path>", "Workspace path. Optional when current directory is already inside a workspace.").option("--doc-id <id>", "Indexed document identifier").option("--path <path>", "Absolute or workspace-relative document path").option("--base <ref>", "Base Git ref", "HEAD").option("--compare <ref>", "Optional compare ref").option("--json", "Emit JSON output", false).action((options3) => {
12621
13037
  const workspaceRoot = assertWorkspace(options3.workspace);
12622
- const resolvedPath = options3.docId ? resolveDocumentById(workspaceRoot, options3.docId).absPath : import_node_path7.default.resolve(import_node_path7.default.isAbsolute(options3.path) ? options3.path : import_node_path7.default.join(workspaceRoot, options3.path));
13038
+ const resolvedPath = options3.docId ? resolveDocumentById(workspaceRoot, options3.docId).absPath : import_node_path8.default.resolve(import_node_path8.default.isAbsolute(options3.path) ? options3.path : import_node_path8.default.join(workspaceRoot, options3.path));
12623
13039
  const diff = getGitDiff(resolvedPath, options3.base, options3.compare);
12624
13040
  if (options3.json) {
12625
13041
  printJson(envelope("diff", { path: resolvedPath, diff }));