@codecell-germany/company-agent-wiki-skill 0.1.0 → 0.1.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.
- package/README.md +22 -3
- package/dist/index.js +506 -91
- package/dist/installer.js +2 -1
- package/knowledge/ARCHITECTURE.md +19 -1
- package/knowledge/KNOWN_LIMITATIONS.md +3 -1
- package/knowledge/RELEASE_CHECKLIST.md +19 -0
- package/package.json +1 -1
- package/skills/company-agent-wiki-cli/SKILL.md +17 -3
- package/skills/company-agent-wiki-cli/references/agent-onboarding.md +6 -0
- package/skills/company-agent-wiki-cli/references/command-cheatsheet.md +5 -0
- package/skills/company-agent-wiki-cli/references/overview.md +11 -7
- package/skills/company-agent-wiki-cli/references/workspace-first-run.md +10 -0
package/README.md
CHANGED
|
@@ -59,6 +59,12 @@ The private knowledge workspace lives outside this public code repository. It ma
|
|
|
59
59
|
The SQLite index is local and derived. It is rebuilt by the CLI and must not be treated as the source of truth.
|
|
60
60
|
It lives inside the private workspace under `.company-agent-wiki/index.sqlite`, but it is intentionally kept out of Git by default because it is rebuildable, binary and noisy in diffs.
|
|
61
61
|
|
|
62
|
+
The workspace path can also be stored globally for other agents. Phase 1 now keeps a per-user workspace registry:
|
|
63
|
+
|
|
64
|
+
- macOS: `~/Library/Application Support/company-agent-wiki/workspaces.json`
|
|
65
|
+
- Windows: `%APPDATA%\\company-agent-wiki\\workspaces.json`
|
|
66
|
+
- Linux: `${XDG_CONFIG_HOME:-~/.config}/company-agent-wiki/workspaces.json`
|
|
67
|
+
|
|
62
68
|
## Install
|
|
63
69
|
|
|
64
70
|
```bash
|
|
@@ -107,6 +113,8 @@ company-agent-wiki-cli setup workspace \
|
|
|
107
113
|
--git-remote git@github.com:your-org/private-company-knowledge.git
|
|
108
114
|
```
|
|
109
115
|
|
|
116
|
+
This now also registers the workspace globally and marks it as the default for later agents.
|
|
117
|
+
|
|
110
118
|
3. Inspect the local state:
|
|
111
119
|
|
|
112
120
|
```bash
|
|
@@ -138,6 +146,16 @@ company-agent-wiki-cli serve --workspace /absolute/path/to/private-company-knowl
|
|
|
138
146
|
The read-only web view is served by the installed CLI process. The private workspace itself contains Markdown, metadata and the local derived index, but no standalone frontend application.
|
|
139
147
|
|
|
140
148
|
If the current shell is already inside a private workspace, runtime commands such as `doctor`, `verify`, `search`, `route`, `read`, `history`, `diff` and `serve` may omit `--workspace`.
|
|
149
|
+
If not, the CLI can now also fall back to the globally registered default workspace.
|
|
150
|
+
|
|
151
|
+
Useful discovery commands:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
company-agent-wiki-cli workspace current --json
|
|
155
|
+
company-agent-wiki-cli workspace list --json
|
|
156
|
+
company-agent-wiki-cli workspace register --workspace /absolute/path/to/private-company-knowledge --default --json
|
|
157
|
+
company-agent-wiki-cli workspace use --workspace /absolute/path/to/private-company-knowledge --json
|
|
158
|
+
```
|
|
141
159
|
|
|
142
160
|
By default `setup workspace` also creates starter Markdown documents such as `wiki-start-here.md`, `company-profile.md`, `organisation-und-rollen.md`, `systeme-und-tools.md`, `kernprozesse.md`, `projekte-und-roadmap.md` and `glossar.md`. Use `--no-starter-docs` only if you intentionally want an almost empty scaffold.
|
|
143
161
|
|
|
@@ -169,15 +187,16 @@ This repository is publishable code only. It must never contain:
|
|
|
169
187
|
|
|
170
188
|
The actual knowledge workspace is separate and private. The human must provide:
|
|
171
189
|
|
|
172
|
-
- the private workspace path
|
|
190
|
+
- the private workspace path at least once
|
|
173
191
|
- if desired, the private Git remote URL
|
|
174
192
|
- access rights to that remote
|
|
175
193
|
|
|
176
|
-
The agent can handle local scaffolding, root registration and index rebuilds, but it should not invent remotes or inject private data into this repository.
|
|
194
|
+
The agent can handle local scaffolding, root registration, global workspace registration and index rebuilds, but it should not invent remotes or inject private data into this repository.
|
|
177
195
|
|
|
178
196
|
## Phase 1 Commands
|
|
179
197
|
|
|
180
198
|
- `setup workspace`: scaffold a private workspace and optionally initialize Git
|
|
199
|
+
- `workspace current|list|register|use`: inspect or manage the global workspace registry for other agents
|
|
181
200
|
- `doctor`: inspect the local runtime and workspace state
|
|
182
201
|
- `verify`: check whether the current roots still match the indexed snapshot
|
|
183
202
|
- `roots add`: register another local Markdown root
|
|
@@ -247,7 +266,7 @@ Then:
|
|
|
247
266
|
|
|
248
267
|
## Concurrency Note
|
|
249
268
|
|
|
250
|
-
The SQLite index is intentionally local and rebuildable.
|
|
269
|
+
The SQLite index is intentionally local and rebuildable. Parallel reads such as `search`, `route`, `read`, `history` and `diff` are now an explicit Phase-1 goal and should work across multiple agents. Write paths such as `index rebuild` and onboarding apply are serialized per workspace through a local write lock, so concurrent writes queue behind the active writer instead of colliding.
|
|
251
270
|
|
|
252
271
|
## What Phase 1 Does Not Do
|
|
253
272
|
|
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
|
|
1193
|
-
var
|
|
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 (
|
|
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 =
|
|
2206
|
-
if (
|
|
2207
|
-
if (sourceExt.includes(
|
|
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) =>
|
|
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 =
|
|
2221
|
+
resolvedScriptPath = fs9.realpathSync(this._scriptPath);
|
|
2222
2222
|
} catch {
|
|
2223
2223
|
resolvedScriptPath = this._scriptPath;
|
|
2224
2224
|
}
|
|
2225
|
-
executableDir =
|
|
2226
|
-
|
|
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 =
|
|
2233
|
+
const legacyName = path9.basename(
|
|
2234
2234
|
this._scriptPath,
|
|
2235
|
-
|
|
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(
|
|
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 =
|
|
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(
|
|
3176
|
-
if (
|
|
3177
|
-
this._executableDir =
|
|
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
|
|
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 =
|
|
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
|
|
6940
|
-
var
|
|
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-
|
|
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
|
|
7143
|
-
var
|
|
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
|
|
@@ -7340,8 +7343,29 @@ var import_node_path4 = __toESM(require("node:path"));
|
|
|
7340
7343
|
function getDefaultCodexHome() {
|
|
7341
7344
|
return process.env.CODEX_HOME || import_node_path4.default.join(import_node_os.default.homedir(), ".codex");
|
|
7342
7345
|
}
|
|
7346
|
+
function getGlobalRegistryDir() {
|
|
7347
|
+
const explicit = process.env.COMPANY_AGENT_WIKI_CONFIG_HOME;
|
|
7348
|
+
if (explicit?.trim()) {
|
|
7349
|
+
return import_node_path4.default.resolve(explicit);
|
|
7350
|
+
}
|
|
7351
|
+
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
|
7352
|
+
return import_node_path4.default.join(import_node_os.default.tmpdir(), GLOBAL_REGISTRY_DIR_NAME, "vitest");
|
|
7353
|
+
}
|
|
7354
|
+
if (process.platform === "darwin") {
|
|
7355
|
+
return import_node_path4.default.join(import_node_os.default.homedir(), "Library", "Application Support", GLOBAL_REGISTRY_DIR_NAME);
|
|
7356
|
+
}
|
|
7357
|
+
if (process.platform === "win32") {
|
|
7358
|
+
const roaming = process.env.APPDATA || import_node_path4.default.join(import_node_os.default.homedir(), "AppData", "Roaming");
|
|
7359
|
+
return import_node_path4.default.join(roaming, GLOBAL_REGISTRY_DIR_NAME);
|
|
7360
|
+
}
|
|
7361
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || import_node_path4.default.join(import_node_os.default.homedir(), ".config");
|
|
7362
|
+
return import_node_path4.default.join(xdgConfig, GLOBAL_REGISTRY_DIR_NAME);
|
|
7363
|
+
}
|
|
7364
|
+
function getGlobalRegistryPath() {
|
|
7365
|
+
return import_node_path4.default.join(getGlobalRegistryDir(), GLOBAL_REGISTRY_FILE);
|
|
7366
|
+
}
|
|
7343
7367
|
function resolveWorkspacePaths(workspaceRoot) {
|
|
7344
|
-
const absoluteRoot =
|
|
7368
|
+
const absoluteRoot = normalizeWorkspaceRootPath(workspaceRoot);
|
|
7345
7369
|
const internalDir = import_node_path4.default.join(absoluteRoot, WORKSPACE_INTERNAL_DIR);
|
|
7346
7370
|
return {
|
|
7347
7371
|
workspaceRoot: absoluteRoot,
|
|
@@ -7367,6 +7391,160 @@ function detectWorkspaceRoot(startDir = process.cwd()) {
|
|
|
7367
7391
|
current = parent;
|
|
7368
7392
|
}
|
|
7369
7393
|
}
|
|
7394
|
+
function createDefaultGlobalRegistry() {
|
|
7395
|
+
return {
|
|
7396
|
+
schemaVersion: WORKSPACE_LAYOUT_VERSION,
|
|
7397
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7398
|
+
workspaces: []
|
|
7399
|
+
};
|
|
7400
|
+
}
|
|
7401
|
+
function normalizeWorkspaceRootPath(candidatePath) {
|
|
7402
|
+
const resolved = import_node_path4.default.resolve(candidatePath);
|
|
7403
|
+
try {
|
|
7404
|
+
return import_node_fs3.default.realpathSync.native ? import_node_fs3.default.realpathSync.native(resolved) : import_node_fs3.default.realpathSync(resolved);
|
|
7405
|
+
} catch {
|
|
7406
|
+
return resolved;
|
|
7407
|
+
}
|
|
7408
|
+
}
|
|
7409
|
+
function loadGlobalWorkspaceRegistry() {
|
|
7410
|
+
const registryPath = getGlobalRegistryPath();
|
|
7411
|
+
if (!fileExists(registryPath)) {
|
|
7412
|
+
return createDefaultGlobalRegistry();
|
|
7413
|
+
}
|
|
7414
|
+
const raw = readJsonFile(registryPath);
|
|
7415
|
+
const registry = createDefaultGlobalRegistry();
|
|
7416
|
+
registry.schemaVersion = raw.schemaVersion || registry.schemaVersion;
|
|
7417
|
+
registry.updatedAt = raw.updatedAt || registry.updatedAt;
|
|
7418
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
7419
|
+
for (const entry of raw.workspaces || []) {
|
|
7420
|
+
const normalizedPath = normalizeWorkspaceRootPath(entry.path);
|
|
7421
|
+
if (!fileExists(resolveWorkspacePaths(normalizedPath).configPath)) {
|
|
7422
|
+
continue;
|
|
7423
|
+
}
|
|
7424
|
+
const normalizedEntry = {
|
|
7425
|
+
workspaceId: entry.workspaceId,
|
|
7426
|
+
path: normalizedPath,
|
|
7427
|
+
label: entry.label || import_node_path4.default.basename(normalizedPath),
|
|
7428
|
+
registeredAt: entry.registeredAt,
|
|
7429
|
+
lastUsedAt: entry.lastUsedAt,
|
|
7430
|
+
source: entry.source
|
|
7431
|
+
};
|
|
7432
|
+
const existing = deduped.get(normalizedPath);
|
|
7433
|
+
if (!existing || existing.lastUsedAt < normalizedEntry.lastUsedAt) {
|
|
7434
|
+
deduped.set(normalizedPath, normalizedEntry);
|
|
7435
|
+
}
|
|
7436
|
+
}
|
|
7437
|
+
registry.workspaces = Array.from(deduped.values()).sort((left, right) => left.label.localeCompare(right.label));
|
|
7438
|
+
if (raw.defaultWorkspace) {
|
|
7439
|
+
const normalizedDefault = normalizeWorkspaceRootPath(raw.defaultWorkspace);
|
|
7440
|
+
if (deduped.has(normalizedDefault)) {
|
|
7441
|
+
registry.defaultWorkspace = normalizedDefault;
|
|
7442
|
+
}
|
|
7443
|
+
}
|
|
7444
|
+
const serializedRaw = JSON.stringify(raw);
|
|
7445
|
+
const serializedNormalized = JSON.stringify(registry);
|
|
7446
|
+
if (serializedRaw !== serializedNormalized) {
|
|
7447
|
+
saveGlobalWorkspaceRegistry(registry);
|
|
7448
|
+
}
|
|
7449
|
+
return registry;
|
|
7450
|
+
}
|
|
7451
|
+
function saveGlobalWorkspaceRegistry(registry) {
|
|
7452
|
+
writeJsonAtomic(getGlobalRegistryPath(), registry);
|
|
7453
|
+
}
|
|
7454
|
+
function buildRegisteredWorkspace(workspaceRoot, source) {
|
|
7455
|
+
const resolvedRoot = normalizeWorkspaceRootPath(workspaceRoot);
|
|
7456
|
+
const config = loadWorkspaceConfig(resolvedRoot);
|
|
7457
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7458
|
+
return {
|
|
7459
|
+
workspaceId: config.workspaceId,
|
|
7460
|
+
path: resolvedRoot,
|
|
7461
|
+
label: import_node_path4.default.basename(resolvedRoot),
|
|
7462
|
+
registeredAt: now,
|
|
7463
|
+
lastUsedAt: now,
|
|
7464
|
+
source
|
|
7465
|
+
};
|
|
7466
|
+
}
|
|
7467
|
+
function registerWorkspaceGlobally(workspaceRoot, options3) {
|
|
7468
|
+
const resolvedRoot = import_node_path4.default.resolve(workspaceRoot);
|
|
7469
|
+
const nextEntry = buildRegisteredWorkspace(resolvedRoot, options3?.source || "manual");
|
|
7470
|
+
const registry = loadGlobalWorkspaceRegistry();
|
|
7471
|
+
const existing = registry.workspaces.find((item) => item.path === resolvedRoot);
|
|
7472
|
+
if (existing) {
|
|
7473
|
+
existing.workspaceId = nextEntry.workspaceId;
|
|
7474
|
+
existing.label = nextEntry.label;
|
|
7475
|
+
existing.lastUsedAt = nextEntry.lastUsedAt;
|
|
7476
|
+
existing.source = nextEntry.source;
|
|
7477
|
+
} else {
|
|
7478
|
+
registry.workspaces.push(nextEntry);
|
|
7479
|
+
}
|
|
7480
|
+
if (options3?.setDefault || !registry.defaultWorkspace) {
|
|
7481
|
+
registry.defaultWorkspace = resolvedRoot;
|
|
7482
|
+
}
|
|
7483
|
+
registry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7484
|
+
saveGlobalWorkspaceRegistry(registry);
|
|
7485
|
+
return registry.workspaces.find((item) => item.path === resolvedRoot) || nextEntry;
|
|
7486
|
+
}
|
|
7487
|
+
function rememberWorkspaceGlobally(workspaceRoot, options3) {
|
|
7488
|
+
const configPath = resolveWorkspacePaths(workspaceRoot).configPath;
|
|
7489
|
+
if (!fileExists(configPath)) {
|
|
7490
|
+
return void 0;
|
|
7491
|
+
}
|
|
7492
|
+
try {
|
|
7493
|
+
return registerWorkspaceGlobally(workspaceRoot, options3);
|
|
7494
|
+
} catch {
|
|
7495
|
+
return void 0;
|
|
7496
|
+
}
|
|
7497
|
+
}
|
|
7498
|
+
function listRegisteredWorkspaces() {
|
|
7499
|
+
const registry = loadGlobalWorkspaceRegistry();
|
|
7500
|
+
return {
|
|
7501
|
+
registryPath: getGlobalRegistryPath(),
|
|
7502
|
+
defaultWorkspace: registry.defaultWorkspace,
|
|
7503
|
+
workspaces: registry.workspaces.map((item) => ({
|
|
7504
|
+
...item,
|
|
7505
|
+
exists: fileExists(resolveWorkspacePaths(item.path).configPath)
|
|
7506
|
+
}))
|
|
7507
|
+
};
|
|
7508
|
+
}
|
|
7509
|
+
function resolveWorkspaceSelection(startDir = process.cwd()) {
|
|
7510
|
+
const registryPath = getGlobalRegistryPath();
|
|
7511
|
+
const cwdWorkspace = detectWorkspaceRoot(startDir);
|
|
7512
|
+
if (cwdWorkspace) {
|
|
7513
|
+
rememberWorkspaceGlobally(cwdWorkspace, { setDefault: true, source: "detected" });
|
|
7514
|
+
return {
|
|
7515
|
+
workspaceRoot: cwdWorkspace,
|
|
7516
|
+
source: "cwd",
|
|
7517
|
+
registryPath,
|
|
7518
|
+
defaultWorkspace: loadGlobalWorkspaceRegistry().defaultWorkspace
|
|
7519
|
+
};
|
|
7520
|
+
}
|
|
7521
|
+
const registry = loadGlobalWorkspaceRegistry();
|
|
7522
|
+
const existingWorkspaces = registry.workspaces.filter((item) => fileExists(resolveWorkspacePaths(item.path).configPath));
|
|
7523
|
+
const defaultWorkspace = registry.defaultWorkspace;
|
|
7524
|
+
if (defaultWorkspace && fileExists(resolveWorkspacePaths(defaultWorkspace).configPath)) {
|
|
7525
|
+
rememberWorkspaceGlobally(defaultWorkspace, { setDefault: true, source: "runtime" });
|
|
7526
|
+
return {
|
|
7527
|
+
workspaceRoot: defaultWorkspace,
|
|
7528
|
+
source: "global-default",
|
|
7529
|
+
registryPath,
|
|
7530
|
+
defaultWorkspace
|
|
7531
|
+
};
|
|
7532
|
+
}
|
|
7533
|
+
if (existingWorkspaces.length === 1) {
|
|
7534
|
+
const onlyWorkspace = existingWorkspaces[0].path;
|
|
7535
|
+
rememberWorkspaceGlobally(onlyWorkspace, { setDefault: true, source: "runtime" });
|
|
7536
|
+
return {
|
|
7537
|
+
workspaceRoot: onlyWorkspace,
|
|
7538
|
+
source: "single-registered",
|
|
7539
|
+
registryPath,
|
|
7540
|
+
defaultWorkspace: onlyWorkspace
|
|
7541
|
+
};
|
|
7542
|
+
}
|
|
7543
|
+
return {
|
|
7544
|
+
registryPath,
|
|
7545
|
+
defaultWorkspace
|
|
7546
|
+
};
|
|
7547
|
+
}
|
|
7370
7548
|
function templateWorkspaceReadme() {
|
|
7371
7549
|
return `# Private Company Knowledge Workspace
|
|
7372
7550
|
|
|
@@ -7734,6 +7912,7 @@ function setupWorkspace(options3) {
|
|
|
7734
7912
|
}
|
|
7735
7913
|
}
|
|
7736
7914
|
}
|
|
7915
|
+
registerWorkspaceGlobally(paths.workspaceRoot, { setDefault: true, source: "setup" });
|
|
7737
7916
|
return {
|
|
7738
7917
|
workspaceRoot: paths.workspaceRoot,
|
|
7739
7918
|
configPath: paths.configPath,
|
|
@@ -7743,7 +7922,8 @@ function setupWorkspace(options3) {
|
|
|
7743
7922
|
`company-agent-wiki-cli doctor --workspace ${paths.workspaceRoot} --json`,
|
|
7744
7923
|
`company-agent-wiki-cli index rebuild --workspace ${paths.workspaceRoot} --json`,
|
|
7745
7924
|
`company-agent-wiki-cli verify --workspace ${paths.workspaceRoot} --json`,
|
|
7746
|
-
`company-agent-wiki-cli serve --workspace ${paths.workspaceRoot} --port 4187
|
|
7925
|
+
`company-agent-wiki-cli serve --workspace ${paths.workspaceRoot} --port 4187`,
|
|
7926
|
+
"Other agents can now discover this workspace automatically via the global workspace registry."
|
|
7747
7927
|
]
|
|
7748
7928
|
};
|
|
7749
7929
|
}
|
|
@@ -7776,6 +7956,7 @@ function addRoot(workspaceRoot, rootDefinition) {
|
|
|
7776
7956
|
};
|
|
7777
7957
|
config.roots.push(root);
|
|
7778
7958
|
saveWorkspaceConfig(workspaceRoot, config);
|
|
7959
|
+
rememberWorkspaceGlobally(workspaceRoot, { setDefault: true, source: "runtime" });
|
|
7779
7960
|
return root;
|
|
7780
7961
|
}
|
|
7781
7962
|
function listRoots(workspaceRoot) {
|
|
@@ -7797,6 +7978,8 @@ function doctor(workspaceRoot) {
|
|
|
7797
7978
|
const codexBinDir = import_node_path4.default.join(codexHome, "bin");
|
|
7798
7979
|
const codexShimPath = import_node_path4.default.join(codexBinDir, CLI_NAME);
|
|
7799
7980
|
const pathEntries = (process.env.PATH || "").split(import_node_path4.default.delimiter).filter(Boolean);
|
|
7981
|
+
const registryPath = getGlobalRegistryPath();
|
|
7982
|
+
const registry = loadGlobalWorkspaceRegistry();
|
|
7800
7983
|
checks.push({
|
|
7801
7984
|
name: "workspace-root",
|
|
7802
7985
|
ok: isDirectory(paths.workspaceRoot),
|
|
@@ -7822,6 +8005,16 @@ function doctor(workspaceRoot) {
|
|
|
7822
8005
|
ok: pathEntries.includes(codexBinDir),
|
|
7823
8006
|
message: pathEntries.includes(codexBinDir) ? `Codex bin directory is available in PATH: ${codexBinDir}` : `Codex bin directory is not in PATH: ${codexBinDir}`
|
|
7824
8007
|
});
|
|
8008
|
+
checks.push({
|
|
8009
|
+
name: "global-workspace-registry",
|
|
8010
|
+
ok: fileExists(registryPath),
|
|
8011
|
+
message: fileExists(registryPath) ? `Global workspace registry found: ${registryPath}` : `Global workspace registry missing: ${registryPath}`
|
|
8012
|
+
});
|
|
8013
|
+
checks.push({
|
|
8014
|
+
name: "global-default-workspace",
|
|
8015
|
+
ok: typeof registry.defaultWorkspace === "string" && fileExists(resolveWorkspacePaths(registry.defaultWorkspace).configPath),
|
|
8016
|
+
message: typeof registry.defaultWorkspace === "string" ? `Global default workspace: ${registry.defaultWorkspace}` : "No global default workspace registered yet."
|
|
8017
|
+
});
|
|
7825
8018
|
if (fileExists(paths.configPath)) {
|
|
7826
8019
|
const config = loadWorkspaceConfig(workspaceRoot);
|
|
7827
8020
|
for (const root of config.roots) {
|
|
@@ -7849,6 +8042,121 @@ function doctor(workspaceRoot) {
|
|
|
7849
8042
|
};
|
|
7850
8043
|
}
|
|
7851
8044
|
|
|
8045
|
+
// src/lib/write-lock.ts
|
|
8046
|
+
var import_node_fs4 = __toESM(require("node:fs"));
|
|
8047
|
+
var import_node_path5 = __toESM(require("node:path"));
|
|
8048
|
+
var LOCK_FILE_NAME = "write.lock";
|
|
8049
|
+
var LOCK_WAIT_TIMEOUT_MS = 6e4;
|
|
8050
|
+
var LOCK_POLL_INTERVAL_MS = 125;
|
|
8051
|
+
var LOCK_STALE_AFTER_MS = 10 * 6e4;
|
|
8052
|
+
function sleepMs(durationMs) {
|
|
8053
|
+
const shared = new SharedArrayBuffer(4);
|
|
8054
|
+
const array = new Int32Array(shared);
|
|
8055
|
+
Atomics.wait(array, 0, 0, durationMs);
|
|
8056
|
+
}
|
|
8057
|
+
function getLockPath(workspaceRoot) {
|
|
8058
|
+
return import_node_path5.default.join(import_node_path5.default.resolve(workspaceRoot), WORKSPACE_INTERNAL_DIR, LOCK_FILE_NAME);
|
|
8059
|
+
}
|
|
8060
|
+
function readLockPayload(lockPath) {
|
|
8061
|
+
if (!fileExists(lockPath)) {
|
|
8062
|
+
return void 0;
|
|
8063
|
+
}
|
|
8064
|
+
try {
|
|
8065
|
+
return readJsonFile(lockPath);
|
|
8066
|
+
} catch {
|
|
8067
|
+
return void 0;
|
|
8068
|
+
}
|
|
8069
|
+
}
|
|
8070
|
+
function isProcessAlive(pid) {
|
|
8071
|
+
if (!pid || pid <= 0) {
|
|
8072
|
+
return false;
|
|
8073
|
+
}
|
|
8074
|
+
try {
|
|
8075
|
+
process.kill(pid, 0);
|
|
8076
|
+
return true;
|
|
8077
|
+
} catch (error) {
|
|
8078
|
+
const code = error.code;
|
|
8079
|
+
return code !== "ESRCH";
|
|
8080
|
+
}
|
|
8081
|
+
}
|
|
8082
|
+
function canBreakLock(payload) {
|
|
8083
|
+
if (!payload) {
|
|
8084
|
+
return true;
|
|
8085
|
+
}
|
|
8086
|
+
const acquiredAt = Date.parse(payload.acquiredAt);
|
|
8087
|
+
const ageMs = Number.isNaN(acquiredAt) ? Number.POSITIVE_INFINITY : Date.now() - acquiredAt;
|
|
8088
|
+
if (ageMs > LOCK_STALE_AFTER_MS) {
|
|
8089
|
+
return true;
|
|
8090
|
+
}
|
|
8091
|
+
return !isProcessAlive(payload.pid);
|
|
8092
|
+
}
|
|
8093
|
+
function writeLockPayload(lockPath, payload) {
|
|
8094
|
+
const tempPath = `${lockPath}.${payload.token}.tmp`;
|
|
8095
|
+
writeJsonAtomic(tempPath, payload);
|
|
8096
|
+
import_node_fs4.default.renameSync(tempPath, lockPath);
|
|
8097
|
+
}
|
|
8098
|
+
function createLockPayload(workspaceRoot, reason) {
|
|
8099
|
+
return {
|
|
8100
|
+
token: newBuildId(),
|
|
8101
|
+
pid: process.pid,
|
|
8102
|
+
reason,
|
|
8103
|
+
workspaceRoot: import_node_path5.default.resolve(workspaceRoot),
|
|
8104
|
+
acquiredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8105
|
+
};
|
|
8106
|
+
}
|
|
8107
|
+
function withWorkspaceWriteLock(workspaceRoot, reason, callback, options3) {
|
|
8108
|
+
const lockPath = getLockPath(workspaceRoot);
|
|
8109
|
+
const timeoutMs = options3?.timeoutMs ?? LOCK_WAIT_TIMEOUT_MS;
|
|
8110
|
+
const deadline = Date.now() + timeoutMs;
|
|
8111
|
+
const payload = createLockPayload(workspaceRoot, reason);
|
|
8112
|
+
ensureDir(import_node_path5.default.dirname(lockPath));
|
|
8113
|
+
while (true) {
|
|
8114
|
+
try {
|
|
8115
|
+
const fileDescriptor = import_node_fs4.default.openSync(lockPath, "wx");
|
|
8116
|
+
import_node_fs4.default.closeSync(fileDescriptor);
|
|
8117
|
+
writeLockPayload(lockPath, payload);
|
|
8118
|
+
break;
|
|
8119
|
+
} catch (error) {
|
|
8120
|
+
const code = error.code;
|
|
8121
|
+
if (code !== "EEXIST") {
|
|
8122
|
+
throw error;
|
|
8123
|
+
}
|
|
8124
|
+
const existing = readLockPayload(lockPath);
|
|
8125
|
+
if (canBreakLock(existing)) {
|
|
8126
|
+
import_node_fs4.default.rmSync(lockPath, { force: true });
|
|
8127
|
+
continue;
|
|
8128
|
+
}
|
|
8129
|
+
if (Date.now() >= deadline) {
|
|
8130
|
+
throw new CliError(
|
|
8131
|
+
"WORKSPACE_BUSY",
|
|
8132
|
+
"Another write operation is already running for this workspace.",
|
|
8133
|
+
EXIT_CODES.workspaceBusy,
|
|
8134
|
+
{
|
|
8135
|
+
hint: "Parallel reads should continue to work. For writes or auto-rebuilds, wait for the active write to finish and retry.",
|
|
8136
|
+
details: {
|
|
8137
|
+
lockPath,
|
|
8138
|
+
holder: existing
|
|
8139
|
+
}
|
|
8140
|
+
}
|
|
8141
|
+
);
|
|
8142
|
+
}
|
|
8143
|
+
sleepMs(LOCK_POLL_INTERVAL_MS);
|
|
8144
|
+
}
|
|
8145
|
+
}
|
|
8146
|
+
const artificialDelay = Number(process.env.COMPANY_AGENT_WIKI_TEST_WRITE_LOCK_DELAY_MS || "0");
|
|
8147
|
+
if (Number.isFinite(artificialDelay) && artificialDelay > 0) {
|
|
8148
|
+
sleepMs(artificialDelay);
|
|
8149
|
+
}
|
|
8150
|
+
try {
|
|
8151
|
+
return callback();
|
|
8152
|
+
} finally {
|
|
8153
|
+
const current = readLockPayload(lockPath);
|
|
8154
|
+
if (current && current.token === payload.token) {
|
|
8155
|
+
import_node_fs4.default.rmSync(lockPath, { force: true });
|
|
8156
|
+
}
|
|
8157
|
+
}
|
|
8158
|
+
}
|
|
8159
|
+
|
|
7852
8160
|
// src/lib/indexer.ts
|
|
7853
8161
|
function openDatabase(databasePath, options3) {
|
|
7854
8162
|
try {
|
|
@@ -7857,6 +8165,12 @@ function openDatabase(databasePath, options3) {
|
|
|
7857
8165
|
options3?.readonly ? { readonly: true, fileMustExist: true } : void 0
|
|
7858
8166
|
);
|
|
7859
8167
|
database.pragma("busy_timeout = 5000");
|
|
8168
|
+
if (options3?.readonly) {
|
|
8169
|
+
database.pragma("query_only = 1");
|
|
8170
|
+
} else {
|
|
8171
|
+
database.pragma("journal_mode = WAL");
|
|
8172
|
+
database.pragma("synchronous = NORMAL");
|
|
8173
|
+
}
|
|
7860
8174
|
return database;
|
|
7861
8175
|
} catch (error) {
|
|
7862
8176
|
throw coerceCliError(error, {
|
|
@@ -7875,11 +8189,11 @@ function closeDatabaseQuietly(database) {
|
|
|
7875
8189
|
}
|
|
7876
8190
|
function throwKnownDatabaseError(error, workspaceRoot) {
|
|
7877
8191
|
const cliError = coerceCliError(error, {
|
|
7878
|
-
sqliteLockHint: `Retry in a moment, serialize parallel CLI reads against ${
|
|
8192
|
+
sqliteLockHint: `Retry in a moment, serialize parallel CLI reads against ${import_node_path6.default.resolve(
|
|
7879
8193
|
workspaceRoot
|
|
7880
8194
|
)}, or rerun with --auto-rebuild after the current write finishes.`,
|
|
7881
8195
|
sqliteLockDetails: {
|
|
7882
|
-
workspaceRoot:
|
|
8196
|
+
workspaceRoot: import_node_path6.default.resolve(workspaceRoot)
|
|
7883
8197
|
}
|
|
7884
8198
|
});
|
|
7885
8199
|
if (cliError) {
|
|
@@ -7961,8 +8275,8 @@ function collectRootSnapshot(rootId, rootPath, kind) {
|
|
|
7961
8275
|
const entries = [];
|
|
7962
8276
|
let latestMtimeMs = 0;
|
|
7963
8277
|
for (const filePath of markdownFiles) {
|
|
7964
|
-
const stats =
|
|
7965
|
-
const relPath =
|
|
8278
|
+
const stats = import_node_fs5.default.statSync(filePath);
|
|
8279
|
+
const relPath = import_node_path6.default.relative(rootPath, filePath);
|
|
7966
8280
|
latestMtimeMs = Math.max(latestMtimeMs, Math.trunc(stats.mtimeMs));
|
|
7967
8281
|
entries.push(`${relPath}|${stats.size}|${Math.trunc(stats.mtimeMs)}`);
|
|
7968
8282
|
}
|
|
@@ -8150,13 +8464,13 @@ function matchesFilters(metadata, filters) {
|
|
|
8150
8464
|
}
|
|
8151
8465
|
return true;
|
|
8152
8466
|
}
|
|
8153
|
-
function
|
|
8467
|
+
function rebuildIndexUnlocked(workspaceRoot) {
|
|
8154
8468
|
const config = loadWorkspaceConfig(workspaceRoot);
|
|
8155
8469
|
const paths = resolveWorkspacePaths(workspaceRoot);
|
|
8156
|
-
const tempDbPath = `${paths.indexDbPath}.next`;
|
|
8470
|
+
const tempDbPath = `${paths.indexDbPath}.${process.pid}.${Date.now()}.${newBuildId()}.next`;
|
|
8157
8471
|
const indexedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8158
8472
|
if (fileExists(tempDbPath)) {
|
|
8159
|
-
|
|
8473
|
+
import_node_fs5.default.unlinkSync(tempDbPath);
|
|
8160
8474
|
}
|
|
8161
8475
|
let database;
|
|
8162
8476
|
try {
|
|
@@ -8186,9 +8500,9 @@ function rebuildIndex(workspaceRoot) {
|
|
|
8186
8500
|
insertRoot(database, snapshot, indexedAt);
|
|
8187
8501
|
const markdownFiles = walkMarkdownFiles(rootPath);
|
|
8188
8502
|
for (const filePath of markdownFiles) {
|
|
8189
|
-
const rawContent =
|
|
8190
|
-
const stats =
|
|
8191
|
-
const relPath =
|
|
8503
|
+
const rawContent = import_node_fs5.default.readFileSync(filePath, "utf8");
|
|
8504
|
+
const stats = import_node_fs5.default.statSync(filePath);
|
|
8505
|
+
const relPath = import_node_path6.default.relative(rootPath, filePath);
|
|
8192
8506
|
const parsed = parseMarkdownDocument(filePath, relPath, root.id, rawContent, Math.trunc(stats.mtimeMs));
|
|
8193
8507
|
insertDocument(database, parsed.document);
|
|
8194
8508
|
insertSections(database, parsed.document, parsed.sections);
|
|
@@ -8199,8 +8513,8 @@ function rebuildIndex(workspaceRoot) {
|
|
|
8199
8513
|
database.exec("INSERT INTO sections_fts(sections_fts) VALUES('optimize');");
|
|
8200
8514
|
closeDatabaseQuietly(database);
|
|
8201
8515
|
database = void 0;
|
|
8202
|
-
replaceFileAtomic(paths.indexDbPath,
|
|
8203
|
-
|
|
8516
|
+
replaceFileAtomic(paths.indexDbPath, import_node_fs5.default.readFileSync(tempDbPath));
|
|
8517
|
+
import_node_fs5.default.unlinkSync(tempDbPath);
|
|
8204
8518
|
const manifest = {
|
|
8205
8519
|
buildId,
|
|
8206
8520
|
schemaVersion: INDEX_SCHEMA_VERSION,
|
|
@@ -8215,11 +8529,14 @@ function rebuildIndex(workspaceRoot) {
|
|
|
8215
8529
|
} catch (error) {
|
|
8216
8530
|
closeDatabaseQuietly(database);
|
|
8217
8531
|
if (fileExists(tempDbPath)) {
|
|
8218
|
-
|
|
8532
|
+
import_node_fs5.default.rmSync(tempDbPath, { force: true });
|
|
8219
8533
|
}
|
|
8220
8534
|
throwKnownDatabaseError(error, workspaceRoot);
|
|
8221
8535
|
}
|
|
8222
8536
|
}
|
|
8537
|
+
function rebuildIndex(workspaceRoot) {
|
|
8538
|
+
return withWorkspaceWriteLock(workspaceRoot, "index-rebuild", () => rebuildIndexUnlocked(workspaceRoot));
|
|
8539
|
+
}
|
|
8223
8540
|
function readManifest(workspaceRoot) {
|
|
8224
8541
|
const paths = resolveWorkspacePaths(workspaceRoot);
|
|
8225
8542
|
if (!fileExists(paths.indexManifestPath)) {
|
|
@@ -8297,7 +8614,7 @@ function requireFreshIndex(workspaceRoot, options3) {
|
|
|
8297
8614
|
"The workspace has not been indexed yet.",
|
|
8298
8615
|
EXIT_CODES.indexMissing,
|
|
8299
8616
|
{
|
|
8300
|
-
hint: verification.hint || `Run: company-agent-wiki-cli index rebuild --workspace ${
|
|
8617
|
+
hint: verification.hint || `Run: company-agent-wiki-cli index rebuild --workspace ${import_node_path6.default.resolve(workspaceRoot)}`
|
|
8301
8618
|
}
|
|
8302
8619
|
);
|
|
8303
8620
|
}
|
|
@@ -8310,7 +8627,7 @@ function requireFreshIndex(workspaceRoot, options3) {
|
|
|
8310
8627
|
"The indexed snapshot no longer matches the current roots.",
|
|
8311
8628
|
EXIT_CODES.indexStale,
|
|
8312
8629
|
{
|
|
8313
|
-
hint: `Run: company-agent-wiki-cli index rebuild --workspace ${
|
|
8630
|
+
hint: `Run: company-agent-wiki-cli index rebuild --workspace ${import_node_path6.default.resolve(workspaceRoot)}`,
|
|
8314
8631
|
details: verification.roots.filter((root) => !root.ok)
|
|
8315
8632
|
}
|
|
8316
8633
|
);
|
|
@@ -8619,8 +8936,8 @@ function getDocumentHeadings(workspaceRoot, docId, options3) {
|
|
|
8619
8936
|
}
|
|
8620
8937
|
|
|
8621
8938
|
// src/lib/onboarding.ts
|
|
8622
|
-
var
|
|
8623
|
-
var
|
|
8939
|
+
var import_node_fs6 = __toESM(require("node:fs"));
|
|
8940
|
+
var import_node_path7 = __toESM(require("node:path"));
|
|
8624
8941
|
var COMPANY_ONBOARDING_DE_V1 = {
|
|
8625
8942
|
profileId: "de-company-v1",
|
|
8626
8943
|
locale: "de-DE",
|
|
@@ -9212,8 +9529,8 @@ function ensureKnownAnswerKeys(payload) {
|
|
|
9212
9529
|
}
|
|
9213
9530
|
}
|
|
9214
9531
|
function isPathInsideWorkspace2(workspaceRoot, candidatePath) {
|
|
9215
|
-
const relative =
|
|
9216
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
9532
|
+
const relative = import_node_path7.default.relative(import_node_path7.default.resolve(workspaceRoot), import_node_path7.default.resolve(candidatePath));
|
|
9533
|
+
return relative === "" || !relative.startsWith("..") && !import_node_path7.default.isAbsolute(relative);
|
|
9217
9534
|
}
|
|
9218
9535
|
function resolveManagedRoot(workspaceRoot) {
|
|
9219
9536
|
const config = loadWorkspaceConfig(workspaceRoot);
|
|
@@ -9236,12 +9553,12 @@ function resolveManagedRoot(workspaceRoot) {
|
|
|
9236
9553
|
return resolvedPath;
|
|
9237
9554
|
}
|
|
9238
9555
|
function createDocument(workspaceRoot, managedRoot, relPath, id, title, type, tags, body, answeredAt, answeredBy) {
|
|
9239
|
-
const absPath =
|
|
9556
|
+
const absPath = import_node_path7.default.join(managedRoot, relPath);
|
|
9240
9557
|
return {
|
|
9241
9558
|
docId: id,
|
|
9242
9559
|
title,
|
|
9243
9560
|
absPath,
|
|
9244
|
-
relPath:
|
|
9561
|
+
relPath: import_node_path7.default.relative(workspaceRoot, absPath),
|
|
9245
9562
|
existed: false,
|
|
9246
9563
|
content: `${renderFrontmatter({ id, title, type, tags, answeredAt, answeredBy })}${body.trimEnd()}
|
|
9247
9564
|
`
|
|
@@ -9440,7 +9757,7 @@ function buildCompanyDocuments(workspaceRoot, normalized) {
|
|
|
9440
9757
|
function loadCompanyOnboardingAnswers(answerFile) {
|
|
9441
9758
|
const payload = readJsonFile(answerFile);
|
|
9442
9759
|
ensureKnownAnswerKeys(payload);
|
|
9443
|
-
const stats =
|
|
9760
|
+
const stats = import_node_fs6.default.statSync(answerFile);
|
|
9444
9761
|
const normalized = normalizeAnswers(payload, stats.mtime.toISOString());
|
|
9445
9762
|
if (normalized.profileId !== COMPANY_ONBOARDING_DE_V1.profileId) {
|
|
9446
9763
|
throw new CliError(
|
|
@@ -9464,7 +9781,7 @@ function previewCompanyOnboarding(workspaceRoot, answerFile) {
|
|
|
9464
9781
|
);
|
|
9465
9782
|
}
|
|
9466
9783
|
for (const document of materialized.documents) {
|
|
9467
|
-
document.existed =
|
|
9784
|
+
document.existed = import_node_fs6.default.existsSync(document.absPath);
|
|
9468
9785
|
}
|
|
9469
9786
|
return {
|
|
9470
9787
|
documents: materialized.documents,
|
|
@@ -9473,39 +9790,41 @@ function previewCompanyOnboarding(workspaceRoot, answerFile) {
|
|
|
9473
9790
|
};
|
|
9474
9791
|
}
|
|
9475
9792
|
function applyCompanyOnboarding(options3) {
|
|
9476
|
-
const workspaceRoot =
|
|
9477
|
-
const answerFile =
|
|
9793
|
+
const workspaceRoot = import_node_path7.default.resolve(options3.workspaceRoot);
|
|
9794
|
+
const answerFile = import_node_path7.default.resolve(options3.answerFile);
|
|
9478
9795
|
const preview = previewCompanyOnboarding(workspaceRoot, answerFile);
|
|
9479
9796
|
const warnings = [...preview.warnings];
|
|
9480
9797
|
let indexBuildId;
|
|
9481
9798
|
if (options3.execute) {
|
|
9482
|
-
const
|
|
9483
|
-
|
|
9484
|
-
|
|
9485
|
-
|
|
9486
|
-
|
|
9487
|
-
|
|
9488
|
-
|
|
9489
|
-
|
|
9490
|
-
|
|
9491
|
-
|
|
9492
|
-
|
|
9493
|
-
|
|
9494
|
-
|
|
9495
|
-
|
|
9496
|
-
|
|
9497
|
-
|
|
9498
|
-
|
|
9799
|
+
const manifest = withWorkspaceWriteLock(workspaceRoot, "onboarding-apply", () => {
|
|
9800
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
9801
|
+
for (const document of preview.documents) {
|
|
9802
|
+
if (seenPaths.has(document.absPath)) {
|
|
9803
|
+
throw new CliError(
|
|
9804
|
+
"ONBOARDING_TARGET_CONFLICT",
|
|
9805
|
+
`Multiple onboarding documents resolve to the same target path: ${document.relPath}`,
|
|
9806
|
+
EXIT_CODES.validation
|
|
9807
|
+
);
|
|
9808
|
+
}
|
|
9809
|
+
seenPaths.add(document.absPath);
|
|
9810
|
+
if (document.existed && !options3.force) {
|
|
9811
|
+
throw new CliError(
|
|
9812
|
+
"ONBOARDING_TARGET_EXISTS",
|
|
9813
|
+
`Target file already exists: ${document.relPath}`,
|
|
9814
|
+
EXIT_CODES.validation,
|
|
9815
|
+
{ hint: "Use --force to overwrite generated onboarding files." }
|
|
9816
|
+
);
|
|
9817
|
+
}
|
|
9499
9818
|
}
|
|
9500
|
-
|
|
9501
|
-
|
|
9502
|
-
|
|
9503
|
-
|
|
9504
|
-
|
|
9819
|
+
for (const document of preview.documents) {
|
|
9820
|
+
ensureDir(import_node_path7.default.dirname(document.absPath));
|
|
9821
|
+
if (document.existed) {
|
|
9822
|
+
warnings.push(`Overwriting existing file: ${document.relPath}`);
|
|
9823
|
+
}
|
|
9824
|
+
replaceFileAtomic(document.absPath, document.content);
|
|
9505
9825
|
}
|
|
9506
|
-
|
|
9507
|
-
}
|
|
9508
|
-
const manifest = rebuildIndex(workspaceRoot);
|
|
9826
|
+
return rebuildIndexUnlocked(workspaceRoot);
|
|
9827
|
+
});
|
|
9509
9828
|
indexBuildId = manifest.buildId;
|
|
9510
9829
|
}
|
|
9511
9830
|
return {
|
|
@@ -9568,7 +9887,7 @@ function printJson(value) {
|
|
|
9568
9887
|
}
|
|
9569
9888
|
|
|
9570
9889
|
// src/lib/server.ts
|
|
9571
|
-
var
|
|
9890
|
+
var import_node_fs7 = __toESM(require("node:fs"));
|
|
9572
9891
|
var import_node_http = __toESM(require("node:http"));
|
|
9573
9892
|
var import_node_url = require("node:url");
|
|
9574
9893
|
|
|
@@ -12027,6 +12346,7 @@ function getHttpStatusCode(error) {
|
|
|
12027
12346
|
case "INDEX_STALE":
|
|
12028
12347
|
return 409;
|
|
12029
12348
|
case "SQLITE_LOCKED":
|
|
12349
|
+
case "WORKSPACE_BUSY":
|
|
12030
12350
|
return 423;
|
|
12031
12351
|
default:
|
|
12032
12352
|
return 500;
|
|
@@ -12133,7 +12453,7 @@ function startServer(workspaceRoot, port, options3) {
|
|
|
12133
12453
|
return;
|
|
12134
12454
|
}
|
|
12135
12455
|
const resolved = resolveDocumentById(workspaceRoot, docId, { autoRebuild: options3?.autoRebuild });
|
|
12136
|
-
const rawMarkdown =
|
|
12456
|
+
const rawMarkdown = import_node_fs7.default.readFileSync(resolved.absPath, "utf8");
|
|
12137
12457
|
sendJson(response, {
|
|
12138
12458
|
ok: true,
|
|
12139
12459
|
data: {
|
|
@@ -12189,14 +12509,16 @@ function startServer(workspaceRoot, port, options3) {
|
|
|
12189
12509
|
// src/index.ts
|
|
12190
12510
|
function assertWorkspace(workspacePath) {
|
|
12191
12511
|
if (workspacePath?.trim()) {
|
|
12192
|
-
|
|
12512
|
+
const resolved = import_node_path8.default.resolve(workspacePath);
|
|
12513
|
+
rememberWorkspaceGlobally(resolved, { setDefault: true, source: "runtime" });
|
|
12514
|
+
return resolved;
|
|
12193
12515
|
}
|
|
12194
|
-
const
|
|
12195
|
-
if (
|
|
12196
|
-
return
|
|
12516
|
+
const selection = resolveWorkspaceSelection(process.cwd());
|
|
12517
|
+
if (selection.workspaceRoot) {
|
|
12518
|
+
return selection.workspaceRoot;
|
|
12197
12519
|
}
|
|
12198
12520
|
throw new CliError("WORKSPACE_REQUIRED", "Missing --workspace option and no workspace was detected from the current directory.", EXIT_CODES.usage, {
|
|
12199
|
-
hint:
|
|
12521
|
+
hint: `Pass --workspace /absolute/path, run the command from a directory inside the private workspace, or register one globally in ${getGlobalRegistryPath()}.`
|
|
12200
12522
|
});
|
|
12201
12523
|
}
|
|
12202
12524
|
function printHumanChecks(checks) {
|
|
@@ -12281,8 +12603,10 @@ program2.command("about").description("Show CLI runtime metadata and common Code
|
|
|
12281
12603
|
cliName: CLI_NAME,
|
|
12282
12604
|
schemaVersion: CLI_SCHEMA_VERSION,
|
|
12283
12605
|
codexHome,
|
|
12284
|
-
codexShimPath:
|
|
12285
|
-
cwdWorkspace: detectWorkspaceRoot(process.cwd()) || null
|
|
12606
|
+
codexShimPath: import_node_path8.default.join(codexHome, "bin", CLI_NAME),
|
|
12607
|
+
cwdWorkspace: detectWorkspaceRoot(process.cwd()) || null,
|
|
12608
|
+
globalRegistryPath: getGlobalRegistryPath(),
|
|
12609
|
+
resolvedWorkspace: resolveWorkspaceSelection(process.cwd())
|
|
12286
12610
|
};
|
|
12287
12611
|
if (options3.json) {
|
|
12288
12612
|
printJson(envelope("about", data));
|
|
@@ -12298,10 +12622,101 @@ program2.command("about").description("Show CLI runtime metadata and common Code
|
|
|
12298
12622
|
process.stdout.write(` detected workspace: ${data.cwdWorkspace}
|
|
12299
12623
|
`);
|
|
12300
12624
|
}
|
|
12625
|
+
if (data.resolvedWorkspace.workspaceRoot && data.resolvedWorkspace.source !== "cwd") {
|
|
12626
|
+
process.stdout.write(` resolved workspace: ${data.resolvedWorkspace.workspaceRoot} (${data.resolvedWorkspace.source})
|
|
12627
|
+
`);
|
|
12628
|
+
}
|
|
12629
|
+
process.stdout.write(` global registry: ${data.globalRegistryPath}
|
|
12630
|
+
`);
|
|
12631
|
+
});
|
|
12632
|
+
var workspace = new Command("workspace").description("Manage global workspace discovery");
|
|
12633
|
+
workspace.command("current").description("Show the currently resolved workspace and discovery source").option("--json", "Emit JSON output", false).action((options3) => {
|
|
12634
|
+
const selection = resolveWorkspaceSelection(process.cwd());
|
|
12635
|
+
const data = {
|
|
12636
|
+
workspaceRoot: selection.workspaceRoot || null,
|
|
12637
|
+
source: selection.source || null,
|
|
12638
|
+
registryPath: selection.registryPath,
|
|
12639
|
+
defaultWorkspace: selection.defaultWorkspace || null
|
|
12640
|
+
};
|
|
12641
|
+
if (options3.json) {
|
|
12642
|
+
printJson(envelope("workspace current", data));
|
|
12643
|
+
return;
|
|
12644
|
+
}
|
|
12645
|
+
if (data.workspaceRoot) {
|
|
12646
|
+
process.stdout.write(`Resolved workspace: ${data.workspaceRoot}
|
|
12647
|
+
`);
|
|
12648
|
+
process.stdout.write(`Source: ${data.source}
|
|
12649
|
+
`);
|
|
12650
|
+
} else {
|
|
12651
|
+
process.stdout.write("No workspace resolved.\n");
|
|
12652
|
+
}
|
|
12653
|
+
process.stdout.write(`Global registry: ${data.registryPath}
|
|
12654
|
+
`);
|
|
12655
|
+
if (data.defaultWorkspace) {
|
|
12656
|
+
process.stdout.write(`Default workspace: ${data.defaultWorkspace}
|
|
12657
|
+
`);
|
|
12658
|
+
}
|
|
12659
|
+
});
|
|
12660
|
+
workspace.command("list").description("List globally registered workspaces").option("--json", "Emit JSON output", false).action((options3) => {
|
|
12661
|
+
const result = listRegisteredWorkspaces();
|
|
12662
|
+
if (options3.json) {
|
|
12663
|
+
printJson(envelope("workspace list", result));
|
|
12664
|
+
return;
|
|
12665
|
+
}
|
|
12666
|
+
process.stdout.write(`Global registry: ${result.registryPath}
|
|
12667
|
+
`);
|
|
12668
|
+
if (result.workspaces.length === 0) {
|
|
12669
|
+
process.stdout.write("No registered workspaces.\n");
|
|
12670
|
+
return;
|
|
12671
|
+
}
|
|
12672
|
+
for (const item of result.workspaces) {
|
|
12673
|
+
const defaultMarker = result.defaultWorkspace === item.path ? " [default]" : "";
|
|
12674
|
+
const existsMarker = item.exists ? "" : " [missing]";
|
|
12675
|
+
process.stdout.write(`- ${item.label}${defaultMarker}${existsMarker}
|
|
12676
|
+
`);
|
|
12677
|
+
process.stdout.write(` ${item.path}
|
|
12678
|
+
`);
|
|
12679
|
+
}
|
|
12680
|
+
});
|
|
12681
|
+
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) => {
|
|
12682
|
+
const entry = registerWorkspaceGlobally(import_node_path8.default.resolve(options3.workspace), {
|
|
12683
|
+
setDefault: Boolean(options3.default),
|
|
12684
|
+
source: "manual"
|
|
12685
|
+
});
|
|
12686
|
+
const result = {
|
|
12687
|
+
registryPath: getGlobalRegistryPath(),
|
|
12688
|
+
entry
|
|
12689
|
+
};
|
|
12690
|
+
if (options3.json) {
|
|
12691
|
+
printJson(envelope("workspace register", result));
|
|
12692
|
+
return;
|
|
12693
|
+
}
|
|
12694
|
+
process.stdout.write(`Registered workspace: ${entry.path}
|
|
12695
|
+
`);
|
|
12696
|
+
if (options3.default) {
|
|
12697
|
+
process.stdout.write("This workspace is now the global default.\n");
|
|
12698
|
+
}
|
|
12699
|
+
});
|
|
12700
|
+
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) => {
|
|
12701
|
+
const entry = registerWorkspaceGlobally(import_node_path8.default.resolve(options3.workspace), {
|
|
12702
|
+
setDefault: true,
|
|
12703
|
+
source: "manual"
|
|
12704
|
+
});
|
|
12705
|
+
const result = {
|
|
12706
|
+
registryPath: getGlobalRegistryPath(),
|
|
12707
|
+
defaultWorkspace: entry.path
|
|
12708
|
+
};
|
|
12709
|
+
if (options3.json) {
|
|
12710
|
+
printJson(envelope("workspace use", result));
|
|
12711
|
+
return;
|
|
12712
|
+
}
|
|
12713
|
+
process.stdout.write(`Global default workspace: ${entry.path}
|
|
12714
|
+
`);
|
|
12301
12715
|
});
|
|
12716
|
+
program2.addCommand(workspace);
|
|
12302
12717
|
program2.command("setup").description("Workspace setup commands").addCommand(
|
|
12303
12718
|
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 =
|
|
12719
|
+
const workspaceRoot = import_node_path8.default.resolve(options3.workspace);
|
|
12305
12720
|
const result = setupWorkspace({
|
|
12306
12721
|
workspaceRoot,
|
|
12307
12722
|
gitInit: Boolean(options3.gitInit),
|
|
@@ -12555,8 +12970,8 @@ program2.command("read").option("--workspace <path>", "Workspace path. Optional
|
|
|
12555
12970
|
const metadataResult = options3.docId ? getDocumentMetadataById(workspaceRoot, options3.docId, {
|
|
12556
12971
|
autoRebuild: Boolean(options3.autoRebuild)
|
|
12557
12972
|
}) : (() => {
|
|
12558
|
-
const candidatePath =
|
|
12559
|
-
return getDocumentMetadataByPath(workspaceRoot,
|
|
12973
|
+
const candidatePath = import_node_path8.default.isAbsolute(options3.path) ? options3.path : import_node_path8.default.join(workspaceRoot, options3.path);
|
|
12974
|
+
return getDocumentMetadataByPath(workspaceRoot, import_node_path8.default.resolve(candidatePath), {
|
|
12560
12975
|
autoRebuild: Boolean(options3.autoRebuild)
|
|
12561
12976
|
});
|
|
12562
12977
|
})();
|
|
@@ -12584,7 +12999,7 @@ program2.command("read").option("--workspace <path>", "Workspace path. Optional
|
|
|
12584
12999
|
}
|
|
12585
13000
|
return;
|
|
12586
13001
|
}
|
|
12587
|
-
const rawMarkdown =
|
|
13002
|
+
const rawMarkdown = import_node_fs8.default.readFileSync(metadataResult.metadata.absPath, "utf8");
|
|
12588
13003
|
if (options3.json) {
|
|
12589
13004
|
printJson(
|
|
12590
13005
|
envelope(
|
|
@@ -12606,7 +13021,7 @@ program2.command("read").option("--workspace <path>", "Workspace path. Optional
|
|
|
12606
13021
|
});
|
|
12607
13022
|
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
13023
|
const workspaceRoot = assertWorkspace(options3.workspace);
|
|
12609
|
-
const resolvedPath = options3.docId ? resolveDocumentById(workspaceRoot, options3.docId).absPath :
|
|
13024
|
+
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
13025
|
const history = getGitHistory(resolvedPath, Number(options3.limit));
|
|
12611
13026
|
if (options3.json) {
|
|
12612
13027
|
printJson(envelope("history", { path: resolvedPath, history }));
|
|
@@ -12619,7 +13034,7 @@ program2.command("history").option("--workspace <path>", "Workspace path. Option
|
|
|
12619
13034
|
});
|
|
12620
13035
|
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
13036
|
const workspaceRoot = assertWorkspace(options3.workspace);
|
|
12622
|
-
const resolvedPath = options3.docId ? resolveDocumentById(workspaceRoot, options3.docId).absPath :
|
|
13037
|
+
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
13038
|
const diff = getGitDiff(resolvedPath, options3.base, options3.compare);
|
|
12624
13039
|
if (options3.json) {
|
|
12625
13040
|
printJson(envelope("diff", { path: resolvedPath, diff }));
|
package/dist/installer.js
CHANGED
|
@@ -3473,7 +3473,7 @@ var {
|
|
|
3473
3473
|
var CLI_NAME = "company-agent-wiki-cli";
|
|
3474
3474
|
var INSTALLER_NAME = "company-agent-wiki-skill";
|
|
3475
3475
|
var SKILL_NAME = "company-agent-wiki-cli";
|
|
3476
|
-
var CLI_SCHEMA_VERSION = "2026-04-
|
|
3476
|
+
var CLI_SCHEMA_VERSION = "2026-04-13";
|
|
3477
3477
|
var EXIT_CODES = {
|
|
3478
3478
|
ok: 0,
|
|
3479
3479
|
usage: 1,
|
|
@@ -3484,6 +3484,7 @@ var EXIT_CODES = {
|
|
|
3484
3484
|
notFound: 6,
|
|
3485
3485
|
git: 7,
|
|
3486
3486
|
sqliteLocked: 8,
|
|
3487
|
+
workspaceBusy: 9,
|
|
3487
3488
|
runtime: 10
|
|
3488
3489
|
};
|
|
3489
3490
|
|
|
@@ -45,7 +45,8 @@ The index and manifest are derived artifacts and should stay ignored in the priv
|
|
|
45
45
|
7. `read --metadata --headings` supports a metadata-first retrieval pass before the full Markdown is loaded.
|
|
46
46
|
8. `search`, `route` and `read` either enforce a fresh index or can explicitly auto-rebuild when `--auto-rebuild` is set.
|
|
47
47
|
9. Runtime commands may detect the current workspace automatically when the shell is already inside a private workspace.
|
|
48
|
-
10.
|
|
48
|
+
10. A global per-user workspace registry stores known workspace paths and a default workspace so other agents can resolve the knowledge location automatically on macOS, Windows and Linux.
|
|
49
|
+
11. `serve` exposes the same read-only data through a local web view and now distinguishes `missing`, `stale` and `ok` states with a rebuild action.
|
|
49
50
|
|
|
50
51
|
## Onboarding Model
|
|
51
52
|
|
|
@@ -73,6 +74,8 @@ Reads are pinned to the latest successful `build_id`. If current root snapshots
|
|
|
73
74
|
|
|
74
75
|
The SQLite runtime also uses a busy timeout and returns a specific `SQLITE_LOCKED` error for transient contention instead of surfacing a generic runtime failure.
|
|
75
76
|
|
|
77
|
+
Phase 1 now also adds a workspace-local write lock. The lock serializes rebuilds and other write flows per workspace, while parallel readers continue against the current derived index.
|
|
78
|
+
|
|
76
79
|
## Metadata-First Retrieval
|
|
77
80
|
|
|
78
81
|
Phase 1 now explicitly supports a two-step retrieval model:
|
|
@@ -82,6 +85,21 @@ Phase 1 now explicitly supports a two-step retrieval model:
|
|
|
82
85
|
|
|
83
86
|
This keeps the agent loop lighter and encourages stronger filenames plus front matter without forcing a rigid folder taxonomy.
|
|
84
87
|
|
|
88
|
+
## Global Workspace Discovery
|
|
89
|
+
|
|
90
|
+
Phase 1 now persists workspace discovery outside the private workspace itself:
|
|
91
|
+
|
|
92
|
+
- macOS: `~/Library/Application Support/company-agent-wiki/workspaces.json`
|
|
93
|
+
- Windows: `%APPDATA%\\company-agent-wiki\\workspaces.json`
|
|
94
|
+
- Linux: `${XDG_CONFIG_HOME:-~/.config}/company-agent-wiki/workspaces.json`
|
|
95
|
+
|
|
96
|
+
The registry stores known workspace paths plus a default workspace. `setup workspace` registers the workspace automatically. Runtime commands prefer:
|
|
97
|
+
|
|
98
|
+
1. explicit `--workspace`
|
|
99
|
+
2. current-directory detection
|
|
100
|
+
3. global default workspace
|
|
101
|
+
4. the single registered workspace, if exactly one exists
|
|
102
|
+
|
|
85
103
|
## Git Model
|
|
86
104
|
|
|
87
105
|
Phase 1 uses Git for:
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
- Auto-rebuild is opt-in. Without `--auto-rebuild`, stale or missing indexes still block retrieval on purpose.
|
|
16
16
|
- Search quality depends on document structure and heading hygiene in the source Markdown.
|
|
17
17
|
- Front-matter filters are currently focused on common fields such as `type`, `status`, `tags`, `project`, `department`, `owners` and `systems`; there is not yet a generic arbitrary-field query language.
|
|
18
|
-
-
|
|
18
|
+
- Parallel reads are supported, but there is still only one active write path per workspace at a time.
|
|
19
|
+
- Long-running rebuilds can delay later auto-rebuild requests because they queue behind the same workspace lock.
|
|
19
20
|
- Search JSON now exposes a normalized `score` plus `rawScore`; the normalized value is better for agents, but it is still only a ranking aid, not a calibrated relevance percentage.
|
|
20
21
|
|
|
21
22
|
## Git Model
|
|
@@ -28,3 +29,4 @@
|
|
|
28
29
|
|
|
29
30
|
- The CLI can initialize a private Git remote URL, but it does not validate remote policy or access controls.
|
|
30
31
|
- The package does not enforce OS-level filesystem permissions; the workspace owner must place the private workspace in a properly protected location.
|
|
32
|
+
- The global workspace registry is only a discovery layer, not an access-control boundary. Any agent running as the same local user can read the registered workspace path.
|
|
@@ -118,6 +118,25 @@ npm_config_cache="$CACHE" npx -y @codecell-germany/company-agent-wiki-skill@0.1.
|
|
|
118
118
|
"$TMP/codex/bin/company-agent-wiki-cli" --help
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
+
Falls `npm view` direkt nach dem Publish kurzfristig noch `404` liefert, zusätzlich die Registry-Tarball-URL prüfen und daraus den Install-Smoke fahren:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
curl -I https://registry.npmjs.org/@codecell-germany/company-agent-wiki-skill/-/company-agent-wiki-skill-0.1.0.tgz
|
|
125
|
+
TMP="$(mktemp -d)"
|
|
126
|
+
cd "$TMP"
|
|
127
|
+
npx -y -p https://registry.npmjs.org/@codecell-germany/company-agent-wiki-skill/-/company-agent-wiki-skill-0.1.0.tgz company-agent-wiki-cli --help
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Praktisches Learning aus diesem Release:
|
|
131
|
+
|
|
132
|
+
- `npm dist-tag ls` und die direkte Tarball-URL können bereits korrekt funktionieren, während `npm view` bzw. die Packument-Auflösung noch kurz hinterherhinkt.
|
|
133
|
+
- Für die Veröffentlichungsfreigabe reicht deshalb im Zweifel:
|
|
134
|
+
- erfolgreicher `npm publish`
|
|
135
|
+
- `npm access get status <package>` = `public`
|
|
136
|
+
- `npm dist-tag ls <package>` zeigt `latest`
|
|
137
|
+
- Tarball-URL liefert `200`
|
|
138
|
+
- CLI-/Installer-Smoke direkt von der Tarball-URL funktioniert
|
|
139
|
+
|
|
121
140
|
## skills.sh Verification
|
|
122
141
|
|
|
123
142
|
Nach GitHub-Publish:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codecell-germany/company-agent-wiki-skill",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Context is king: agent-first local company knowledge workspace with metadata-first retrieval, Markdown as truth, SQLite-indexed front matter, Git-aware verification, and a Codex skill installer.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,7 +17,7 @@ Use this skill when the task is about a private company knowledge workspace buil
|
|
|
17
17
|
- The public CLI binary is `company-agent-wiki-cli`.
|
|
18
18
|
- The actual company knowledge workspace is private and lives outside the public code repository.
|
|
19
19
|
- The private workspace may be the current dedicated local folder; it just must not be the public skill/CLI repo.
|
|
20
|
-
- The human should provide the workspace path and, if desired, the private Git remote URL.
|
|
20
|
+
- The human should provide the workspace path at least once and, if desired, the private Git remote URL. After setup or manual registration, the CLI stores the workspace path in a global per-user registry so later agents can resolve it automatically.
|
|
21
21
|
- Runtime discovery matters. Before relying on the CLI, verify which path is actually available.
|
|
22
22
|
- In Codex, the most reliable fallback is usually the installed shim under `$CODEX_HOME/bin` or `~/.codex/bin`.
|
|
23
23
|
- The `npx -p @codecell-germany/company-agent-wiki-skill ...` path only works after the npm package is really published.
|
|
@@ -58,7 +58,15 @@ npx -p @codecell-germany/company-agent-wiki-skill company-agent-wiki-cli --help
|
|
|
58
58
|
company-agent-wiki-cli setup workspace --workspace /absolute/path/to/private-company-knowledge --git-init
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
This creates starter Markdown documents by default. Use `--no-starter-docs` only when you explicitly want a nearly empty workspace.
|
|
61
|
+
This creates starter Markdown documents by default and registers the workspace globally for future agents. Use `--no-starter-docs` only when you explicitly want a nearly empty workspace.
|
|
62
|
+
|
|
63
|
+
If the workspace already exists, inspect or register it explicitly:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
company-agent-wiki-cli workspace current --json
|
|
67
|
+
company-agent-wiki-cli workspace list --json
|
|
68
|
+
company-agent-wiki-cli workspace register --workspace /absolute/path/to/private-company-knowledge --default --json
|
|
69
|
+
```
|
|
62
70
|
|
|
63
71
|
3. Run health checks:
|
|
64
72
|
|
|
@@ -234,13 +242,19 @@ company-agent-wiki-cli onboarding company \
|
|
|
234
242
|
- The local SQLite file lives in the workspace, but should stay out of Git by default.
|
|
235
243
|
- If the CLI reports `INDEX_STALE`, do not ignore it. Run `index rebuild` or use an explicit `--auto-rebuild` path.
|
|
236
244
|
- For agent workflows, prefer `--auto-rebuild` on `search`, `route`, `read` and `serve` unless you explicitly want strict stale-index failures.
|
|
237
|
-
-
|
|
245
|
+
- Parallel `search`, `route`, `read`, `history` and `diff` calls against the same workspace are now intended to work.
|
|
246
|
+
- Write paths are serialized per workspace. If one agent is rebuilding the index or applying onboarding writes, other write paths wait behind that workspace lock instead of colliding.
|
|
238
247
|
- Do not put private company knowledge into the public code repository.
|
|
239
248
|
- Use the read-only web view only for browsing, not editing.
|
|
240
249
|
- The company onboarding questionnaire is optional. Every answer may be skipped, answered with “nein” or marked as unknown.
|
|
241
250
|
- Onboarding writes are explicit. Do not assume preview mode changes files; only `--execute` writes draft onboarding Markdown and rebuilds the derived index.
|
|
242
251
|
- If CLI discovery fails, do not pretend the documented command works. First resolve a real executable path.
|
|
243
252
|
- If the current folder already contains `.company-agent-wiki/workspace.json`, you may omit `--workspace` and let the CLI detect the workspace root automatically.
|
|
253
|
+
- If the current folder is not inside the workspace, the CLI may still resolve the globally registered default workspace.
|
|
254
|
+
- The global workspace registry lives per user:
|
|
255
|
+
- macOS: `~/Library/Application Support/company-agent-wiki/workspaces.json`
|
|
256
|
+
- Windows: `%APPDATA%\\company-agent-wiki\\workspaces.json`
|
|
257
|
+
- Linux: `${XDG_CONFIG_HOME:-~/.config}/company-agent-wiki/workspaces.json`
|
|
244
258
|
|
|
245
259
|
## References
|
|
246
260
|
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
"$HOME/.codex/bin/company-agent-wiki-cli" --help
|
|
8
8
|
company-agent-wiki-cli --help
|
|
9
9
|
company-agent-wiki-cli setup workspace --workspace /absolute/path --git-init
|
|
10
|
+
company-agent-wiki-cli workspace current --json
|
|
11
|
+
company-agent-wiki-cli workspace list --json
|
|
10
12
|
company-agent-wiki-cli onboarding company
|
|
11
13
|
company-agent-wiki-cli onboarding company --workspace /absolute/path --answers-file /absolute/path/to/answers.json
|
|
12
14
|
company-agent-wiki-cli onboarding company --workspace /absolute/path --answers-file /absolute/path/to/answers.json --execute
|
|
@@ -24,6 +26,8 @@ Only after the company profile is clear enough and these checks succeed should y
|
|
|
24
26
|
company-agent-wiki-cli setup workspace --workspace /absolute/path --git-init
|
|
25
27
|
```
|
|
26
28
|
|
|
29
|
+
This setup step also registers the workspace globally so later agents can find it without asking for the same path again.
|
|
30
|
+
|
|
27
31
|
If the human has a private Git remote ready:
|
|
28
32
|
|
|
29
33
|
```bash
|
|
@@ -49,6 +53,8 @@ For frequent edits, the authoring loop can also use the guarded auto-rebuild pat
|
|
|
49
53
|
company-agent-wiki-cli search "KI-Telefonassistent" --workspace /absolute/path --auto-rebuild --json
|
|
50
54
|
```
|
|
51
55
|
|
|
56
|
+
Parallel reads are allowed. If a write path such as `index rebuild` is already running, later writes wait behind the same workspace lock.
|
|
57
|
+
|
|
52
58
|
After candidate routing, prefer metadata-first reading:
|
|
53
59
|
|
|
54
60
|
```bash
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
"$HOME/.codex/bin/company-agent-wiki-cli" --help
|
|
8
8
|
company-agent-wiki-cli about --json
|
|
9
9
|
company-agent-wiki-cli setup workspace --workspace /absolute/path --git-init
|
|
10
|
+
company-agent-wiki-cli workspace current --json
|
|
11
|
+
company-agent-wiki-cli workspace list --json
|
|
12
|
+
company-agent-wiki-cli workspace register --workspace /absolute/path --default --json
|
|
13
|
+
company-agent-wiki-cli workspace use --workspace /absolute/path --json
|
|
10
14
|
company-agent-wiki-cli onboarding company
|
|
11
15
|
company-agent-wiki-cli onboarding company --workspace /absolute/path --answers-file /absolute/path/to/answers.json
|
|
12
16
|
company-agent-wiki-cli onboarding company --workspace /absolute/path --answers-file /absolute/path/to/answers.json --execute
|
|
@@ -23,6 +27,7 @@ company-agent-wiki-cli verify --workspace /absolute/path --json
|
|
|
23
27
|
```
|
|
24
28
|
|
|
25
29
|
If you are already inside the private workspace, `doctor`, `verify`, `search`, `route`, `read`, `history`, `diff` and `serve` may omit `--workspace`.
|
|
30
|
+
If not, the CLI can also use the globally registered default workspace.
|
|
26
31
|
|
|
27
32
|
## Retrieval
|
|
28
33
|
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
## Main Commands
|
|
17
17
|
|
|
18
18
|
- `setup workspace`
|
|
19
|
+
- `workspace current|list|register|use`
|
|
19
20
|
- `onboarding company`
|
|
20
21
|
- `doctor`
|
|
21
22
|
- `verify`
|
|
@@ -32,10 +33,13 @@
|
|
|
32
33
|
## Expected Workflow
|
|
33
34
|
|
|
34
35
|
1. Set up the private workspace.
|
|
35
|
-
2.
|
|
36
|
-
3.
|
|
37
|
-
4.
|
|
38
|
-
5.
|
|
39
|
-
6.
|
|
40
|
-
7.
|
|
41
|
-
8.
|
|
36
|
+
2. The workspace is registered globally so other agents can discover it automatically.
|
|
37
|
+
3. Use the starter documents or run `onboarding company`, then preview or apply the generated onboarding Markdown from an answers file.
|
|
38
|
+
4. Register any additional Markdown roots.
|
|
39
|
+
5. Rebuild the index.
|
|
40
|
+
6. Verify that the snapshot is fresh. On a brand-new workspace `verify` reports `missing` instead of failing hard.
|
|
41
|
+
7. Search or route to the right document. For active authoring loops, prefer `--auto-rebuild` and front-matter filters such as `--type`, `--project` or `--department`.
|
|
42
|
+
8. Inspect metadata and headings with `read --metadata --headings --auto-rebuild`.
|
|
43
|
+
9. Read the full source document or use the read-only web view.
|
|
44
|
+
|
|
45
|
+
Parallel reads are supported. Writes are serialized per workspace.
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
- a private workspace folder path
|
|
6
6
|
- if desired, a private Git remote URL
|
|
7
7
|
|
|
8
|
+
The workspace path only has to be provided once. After setup or manual registration, the CLI stores it in a per-user global registry for later agents.
|
|
9
|
+
|
|
8
10
|
## What the Agent Can Safely Do
|
|
9
11
|
|
|
10
12
|
- scaffold the local workspace
|
|
@@ -13,6 +15,7 @@
|
|
|
13
15
|
- create the default starter documents for the first useful company knowledge
|
|
14
16
|
- preview or apply onboarding-generated starter Markdown from an answers file
|
|
15
17
|
- register Markdown roots
|
|
18
|
+
- register the workspace globally for later agents
|
|
16
19
|
- rebuild the derived index
|
|
17
20
|
- verify freshness or a still-missing first index
|
|
18
21
|
- run the read-only web view via `company-agent-wiki-cli serve --workspace /absolute/path --port 4187`
|
|
@@ -30,6 +33,13 @@ company-agent-wiki-cli --help
|
|
|
30
33
|
The package-based `npx` path is only valid once the npm package is published.
|
|
31
34
|
|
|
32
35
|
If the current folder is already inside a private workspace, runtime commands may omit `--workspace`.
|
|
36
|
+
If not, inspect or update the global registry:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
company-agent-wiki-cli workspace current --json
|
|
40
|
+
company-agent-wiki-cli workspace list --json
|
|
41
|
+
company-agent-wiki-cli workspace register --workspace /absolute/path --default --json
|
|
42
|
+
```
|
|
33
43
|
|
|
34
44
|
## What Must Stay Out of This Public Repo
|
|
35
45
|
|