@biaoo/tiangong-wiki 0.2.1 → 0.2.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/README.md CHANGED
@@ -24,6 +24,21 @@
24
24
  npm install -g @biaoo/tiangong-wiki
25
25
  ```
26
26
 
27
+ ## Update
28
+
29
+ Upgrade the npm package itself:
30
+
31
+ ```bash
32
+ npm install -g @biaoo/tiangong-wiki@latest
33
+ ```
34
+
35
+ Refresh workspace-local managed skills after upgrading the CLI or when upstream skill content changes:
36
+
37
+ ```bash
38
+ tiangong-wiki skill status
39
+ tiangong-wiki skill update --all
40
+ ```
41
+
27
42
  <details>
28
43
  <summary><strong>Use as an AI Agent Skill</strong></summary>
29
44
 
@@ -47,6 +62,7 @@ To manage workspace-local skills from arbitrary repo/path sources after setup:
47
62
  tiangong-wiki skill add ../my-skills --skill notes
48
63
  tiangong-wiki skill status
49
64
  tiangong-wiki skill update notes
65
+ tiangong-wiki skill update --all
50
66
  ```
51
67
 
52
68
  </details>
@@ -61,7 +77,14 @@ tiangong-wiki init # initialize workspace
61
77
  tiangong-wiki sync # index Markdown pages
62
78
  ```
63
79
 
64
- `tiangong-wiki setup` creates `.wiki.env`. Subsequent commands such as `doctor`, `init`, and `sync` should be run from the workspace root containing that `.wiki.env`, or with `WIKI_ENV_FILE` pointing to it explicitly.
80
+ `tiangong-wiki setup` creates a workspace-local `.wiki.env` and records it as your default workspace config. Command resolution now follows this order:
81
+
82
+ 1. `--env-file <path>`
83
+ 2. `WIKI_ENV_FILE`
84
+ 3. The nearest `.wiki.env` found by walking upward from your current directory
85
+ 4. The global default workspace config written by `tiangong-wiki setup`
86
+
87
+ That means commands still work best from inside a workspace, but they can also run from outside the workspace after setup, or target a specific workspace explicitly with `--env-file`.
65
88
 
66
89
  ```bash
67
90
  tiangong-wiki find --type concept --status active # structured query
@@ -71,11 +94,12 @@ tiangong-wiki graph bayes-theorem --depth 2 # graph traversal
71
94
  ```
72
95
 
73
96
  ```bash
74
- tiangong-wiki daemon run # start dashboard & HTTP API
97
+ tiangong-wiki daemon start # start the daemon in the background
75
98
  tiangong-wiki dashboard # open dashboard in browser
99
+ # or: tiangong-wiki daemon run # run the daemon in the foreground for debugging
76
100
  ```
77
101
 
78
- > Environment variables are managed via `.wiki.env` (created by `tiangong-wiki setup`). The CLI auto-discovers the nearest `.wiki.env` by walking upward from your current directory. See [references/troubleshooting.md](./references/troubleshooting.md) for the full reference.
102
+ > Environment variables are managed via `.wiki.env` (created by `tiangong-wiki setup`). The CLI prefers the nearest local `.wiki.env`, then falls back to the global default workspace config. See [references/troubleshooting.md](./references/troubleshooting.md) for the full reference.
79
103
 
80
104
  ## CLI
81
105
 
package/README.zh-CN.md CHANGED
@@ -24,6 +24,21 @@
24
24
  npm install -g @biaoo/tiangong-wiki
25
25
  ```
26
26
 
27
+ ## 更新
28
+
29
+ 升级 npm 包本身:
30
+
31
+ ```bash
32
+ npm install -g @biaoo/tiangong-wiki@latest
33
+ ```
34
+
35
+ 升级 CLI 后,或上游 skill 内容有更新时,刷新工作区本地 managed skills:
36
+
37
+ ```bash
38
+ tiangong-wiki skill status
39
+ tiangong-wiki skill update --all
40
+ ```
41
+
27
42
  <details>
28
43
  <summary><strong>作为 AI Agent Skill 使用</strong></summary>
29
44
 
@@ -47,6 +62,7 @@ tiangong-wiki setup
47
62
  tiangong-wiki skill add ../my-skills --skill notes
48
63
  tiangong-wiki skill status
49
64
  tiangong-wiki skill update notes
65
+ tiangong-wiki skill update --all
50
66
  ```
51
67
 
52
68
  </details>
@@ -61,7 +77,14 @@ tiangong-wiki init # 初始化工作区
61
77
  tiangong-wiki sync # 索引 Markdown 文件
62
78
  ```
63
79
 
64
- `tiangong-wiki setup` 会创建 `.wiki.env`。后续的 `doctor`、`init`、`sync` 等命令应在包含该 `.wiki.env` 的工作区根目录执行;如果不在该目录执行,则需要显式设置 `WIKI_ENV_FILE` 指向它。
80
+ `tiangong-wiki setup` 会创建工作区本地 `.wiki.env`,并将其记录为默认工作区配置。CLI 的配置解析优先级如下:
81
+
82
+ 1. `--env-file <path>`
83
+ 2. `WIKI_ENV_FILE`
84
+ 3. 从当前目录向上查找最近的 `.wiki.env`
85
+ 4. `tiangong-wiki setup` 写入的全局默认工作区配置
86
+
87
+ 这意味着命令仍然最适合在 workspace 内执行;但 setup 之后,即使在 workspace 外运行,也可以通过默认配置正常工作,或者通过 `--env-file` 显式指定目标工作区。
65
88
 
66
89
  ```bash
67
90
  tiangong-wiki find --type concept --status active # 结构化查询
@@ -71,11 +94,12 @@ tiangong-wiki graph bayes-theorem --depth 2 # 图遍历
71
94
  ```
72
95
 
73
96
  ```bash
74
- tiangong-wiki daemon run # 启动仪表盘和 HTTP API
97
+ tiangong-wiki daemon start # 后台启动 daemon
75
98
  tiangong-wiki dashboard # 在浏览器中打开仪表盘
99
+ # 或者:tiangong-wiki daemon run # 前台运行 daemon,适合调试
76
100
  ```
77
101
 
78
- > 环境变量通过 `.wiki.env` 管理(由 `tiangong-wiki setup` 创建)。CLI 会从当前目录开始向上自动发现最近的 `.wiki.env`。完整参考见 [references/troubleshooting.md](./references/troubleshooting.md)。
102
+ > 环境变量通过 `.wiki.env` 管理(由 `tiangong-wiki setup` 创建)。CLI 会优先使用最近的本地 `.wiki.env`,找不到时再 fallback 到全局默认工作区配置。完整参考见 [references/troubleshooting.md](./references/troubleshooting.md)。
79
103
 
80
104
  ## CLI
81
105
 
@@ -1,4 +1,5 @@
1
1
  import path from "node:path";
2
+ import { readGlobalConfig } from "./global-config.js";
2
3
  import { pathExistsSync, readTextFileSync } from "../utils/fs.js";
3
4
  export const DEFAULT_WIKI_ENV_FILE = ".wiki.env";
4
5
  const EMPTY_INFO = {
@@ -6,6 +7,10 @@ const EMPTY_INFO = {
6
7
  loadedPath: null,
7
8
  autoDiscovered: false,
8
9
  missingRequestedPath: false,
10
+ missingDefaultPath: false,
11
+ source: "none",
12
+ globalConfigPath: null,
13
+ defaultPath: null,
9
14
  loadedKeys: [],
10
15
  };
11
16
  let lastCliEnvInfo = EMPTY_INFO;
@@ -86,20 +91,40 @@ export function applyCliEnvironment(targetEnv = process.env, cwd = process.cwd()
86
91
  const requestedEnvFile = targetEnv.WIKI_ENV_FILE?.trim();
87
92
  const requestedPath = requestedEnvFile ? path.resolve(cwd, requestedEnvFile) : null;
88
93
  if (!requestedPath && hasExplicitCoreRuntimeEnv(targetEnv)) {
89
- lastCliEnvInfo = { ...EMPTY_INFO };
94
+ lastCliEnvInfo = { ...EMPTY_INFO, source: "process-env" };
90
95
  return lastCliEnvInfo;
91
96
  }
92
- const candidatePath = requestedPath ?? findNearestEnvFile(cwd);
97
+ const nearestPath = requestedPath ? null : findNearestEnvFile(cwd);
98
+ const globalConfig = requestedPath || nearestPath ? null : readGlobalConfig(targetEnv);
99
+ const defaultPath = globalConfig ? path.resolve(globalConfig.defaultEnvFile) : null;
100
+ const candidatePath = requestedPath ?? nearestPath ?? defaultPath;
101
+ const source = requestedPath
102
+ ? "explicit-env-file"
103
+ : nearestPath
104
+ ? "nearest-env-file"
105
+ : defaultPath
106
+ ? "global-default-env-file"
107
+ : "none";
93
108
  if (!candidatePath) {
94
- lastCliEnvInfo = { ...EMPTY_INFO, requestedPath };
109
+ lastCliEnvInfo = {
110
+ ...EMPTY_INFO,
111
+ requestedPath,
112
+ source,
113
+ globalConfigPath: globalConfig?.configPath ?? null,
114
+ defaultPath,
115
+ };
95
116
  return lastCliEnvInfo;
96
117
  }
97
118
  if (!pathExistsSync(candidatePath)) {
98
119
  lastCliEnvInfo = {
99
120
  requestedPath: candidatePath,
100
121
  loadedPath: null,
101
- autoDiscovered: false,
122
+ autoDiscovered: source === "nearest-env-file",
102
123
  missingRequestedPath: requestedPath !== null,
124
+ missingDefaultPath: requestedPath === null && source === "global-default-env-file",
125
+ source,
126
+ globalConfigPath: globalConfig?.configPath ?? null,
127
+ defaultPath,
103
128
  loadedKeys: [],
104
129
  };
105
130
  return lastCliEnvInfo;
@@ -118,8 +143,12 @@ export function applyCliEnvironment(targetEnv = process.env, cwd = process.cwd()
118
143
  lastCliEnvInfo = {
119
144
  requestedPath,
120
145
  loadedPath: candidatePath,
121
- autoDiscovered: requestedPath === null,
146
+ autoDiscovered: source === "nearest-env-file",
122
147
  missingRequestedPath: false,
148
+ missingDefaultPath: false,
149
+ source,
150
+ globalConfigPath: globalConfig?.configPath ?? null,
151
+ defaultPath,
123
152
  loadedKeys,
124
153
  };
125
154
  return lastCliEnvInfo;
@@ -0,0 +1,61 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import { AppError } from "../utils/errors.js";
4
+ import { ensureDirSync, pathExistsSync, readTextFileSync, writeTextFileSync } from "../utils/fs.js";
5
+ export const GLOBAL_CONFIG_DIRNAME = "tiangong-wiki";
6
+ export const GLOBAL_CONFIG_FILENAME = "config.json";
7
+ function resolveConfigBaseDir(env = process.env) {
8
+ const xdgConfigHome = env.XDG_CONFIG_HOME?.trim();
9
+ if (xdgConfigHome) {
10
+ return path.resolve(xdgConfigHome);
11
+ }
12
+ const homeDir = env.HOME?.trim() || os.homedir();
13
+ return path.join(homeDir, ".config");
14
+ }
15
+ export function resolveGlobalConfigPath(env = process.env) {
16
+ return path.join(resolveConfigBaseDir(env), GLOBAL_CONFIG_DIRNAME, GLOBAL_CONFIG_FILENAME);
17
+ }
18
+ export function readGlobalConfig(env = process.env) {
19
+ const configPath = resolveGlobalConfigPath(env);
20
+ if (!pathExistsSync(configPath)) {
21
+ return null;
22
+ }
23
+ let parsed;
24
+ try {
25
+ parsed = JSON.parse(readTextFileSync(configPath));
26
+ }
27
+ catch (error) {
28
+ throw new AppError(`Failed to parse global CLI config JSON: ${configPath}`, "config", {
29
+ cause: error instanceof Error ? error.message : String(error),
30
+ });
31
+ }
32
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
33
+ throw new AppError(`Global CLI config must be an object: ${configPath}`, "config");
34
+ }
35
+ const schemaVersion = parsed.schemaVersion;
36
+ if (!Number.isInteger(schemaVersion) || Number(schemaVersion) < 1) {
37
+ throw new AppError(`Global CLI config schemaVersion must be a positive integer: ${configPath}`, "config");
38
+ }
39
+ const defaultEnvFile = parsed.defaultEnvFile;
40
+ if (typeof defaultEnvFile !== "string" || defaultEnvFile.trim().length === 0) {
41
+ throw new AppError(`Global CLI config defaultEnvFile must be a non-empty string: ${configPath}`, "config");
42
+ }
43
+ return {
44
+ configPath,
45
+ defaultEnvFile: path.resolve(defaultEnvFile),
46
+ };
47
+ }
48
+ export function writeGlobalConfig(defaultEnvFile, env = process.env) {
49
+ const configPath = resolveGlobalConfigPath(env);
50
+ const normalizedEnvFile = path.resolve(defaultEnvFile);
51
+ const payload = {
52
+ schemaVersion: 1,
53
+ defaultEnvFile: normalizedEnvFile,
54
+ };
55
+ ensureDirSync(path.dirname(configPath));
56
+ writeTextFileSync(configPath, `${JSON.stringify(payload, null, 2)}\n`);
57
+ return {
58
+ configPath,
59
+ defaultEnvFile: normalizedEnvFile,
60
+ };
61
+ }
@@ -5,6 +5,7 @@ import { confirm, input, password, select } from "@inquirer/prompts";
5
5
  import { DEFAULT_WIKI_ENV_FILE, getCliEnvironmentInfo, parseEnvFile, serializeEnvEntries } from "./cli-env.js";
6
6
  import { resolveTemplateFilePath, loadConfig } from "./config.js";
7
7
  import { EmbeddingClient } from "./embedding.js";
8
+ import { writeGlobalConfig } from "./global-config.js";
8
9
  import { parseVaultHashMode, resolveAgentSettings } from "./paths.js";
9
10
  import { loadSynologyConfigFromEnv, normalizeSynologyRemotePath, withSynologyClient } from "./synology.js";
10
11
  import { ensureWikiSkillInstall, formatParserSkills, inspectSkillInstall, installParserSkill, OPTIONAL_PARSER_SKILLS, parseParserSkillSelection, parseParserSkills, resolveWorkspaceRootFromWikiPath, resolveWorkspaceSkillPath, resolveWorkspaceSkillPaths, } from "./workspace-skills.js";
@@ -672,9 +673,11 @@ export async function runSetupWizard(env = process.env, options = {}) {
672
673
  output,
673
674
  }));
674
675
  writeSetupEnvFile(values);
676
+ const globalConfig = writeGlobalConfig(values.envFilePath, env);
675
677
  output.write([
676
678
  "\ntiangong-wiki setup complete",
677
679
  `configuration file: ${values.envFilePath}`,
680
+ `default workspace config: ${globalConfig.configPath}`,
678
681
  `workspace root: ${workspaceRoot}`,
679
682
  `skills root: ${skillsRoot}`,
680
683
  `tiangong-wiki-skill: ${wikiSkillInstall.status}`,
@@ -687,8 +690,10 @@ export async function runSetupWizard(env = process.env, options = {}) {
687
690
  : []),
688
691
  "",
689
692
  "Next steps:",
690
- `- Run subsequent commands from the workspace root that contains .wiki.env: ${workspaceRoot}`,
693
+ `- Commands inside ${JSON.stringify(workspaceRoot)} will auto-discover the local .wiki.env first.`,
694
+ `- Commands outside the workspace will fall back to the default workspace config at ${globalConfig.configPath}.`,
691
695
  `- Example: cd ${JSON.stringify(workspaceRoot)} && tiangong-wiki doctor`,
696
+ `- Example: tiangong-wiki --env-file ${JSON.stringify(values.envFilePath)} doctor`,
692
697
  `- Example: cd ${JSON.stringify(workspaceRoot)} && tiangong-wiki init`,
693
698
  "- Run `tiangong-wiki doctor` to validate the generated configuration.",
694
699
  "- Run `tiangong-wiki init` to create index.db and perform the first sync.",
@@ -701,6 +706,7 @@ export async function runSetupWizard(env = process.env, options = {}) {
701
706
  ].join("\n"));
702
707
  return {
703
708
  envFilePath: values.envFilePath,
709
+ globalConfigPath: globalConfig.configPath,
704
710
  createdDirectories: bootstrap.createdDirectories,
705
711
  copiedConfig: bootstrap.copiedConfig,
706
712
  copiedTemplates: bootstrap.copiedTemplates,
@@ -1017,11 +1023,26 @@ export async function buildDoctorReport(env = process.env, options = {}) {
1017
1023
  if (envFile.missingRequestedPath && envFile.requestedPath) {
1018
1024
  collectDoctorCheck(checks, "error", "env-file", `Requested env file does not exist: ${envFile.requestedPath}`, "Create the env file or rerun `tiangong-wiki setup`.");
1019
1025
  }
1026
+ else if (envFile.missingDefaultPath && envFile.defaultPath) {
1027
+ collectDoctorCheck(checks, "error", "env-file", `The default workspace config points to a missing env file: ${envFile.defaultPath}`, envFile.globalConfigPath
1028
+ ? `Fix or remove ${envFile.globalConfigPath}, rerun \`tiangong-wiki setup\`, or pass \`--env-file\` explicitly.`
1029
+ : "Fix the default workspace config, rerun `tiangong-wiki setup`, or pass `--env-file` explicitly.");
1030
+ }
1020
1031
  else if (envFile.loadedPath) {
1021
- collectDoctorCheck(checks, "ok", "env-file", `Loaded configuration from ${envFile.loadedPath}${envFile.autoDiscovered ? " (auto-discovered)." : "."}`);
1032
+ const sourceLabel = envFile.source === "explicit-env-file"
1033
+ ? "from --env-file or WIKI_ENV_FILE."
1034
+ : envFile.source === "nearest-env-file"
1035
+ ? "from the current workspace (auto-discovered)."
1036
+ : envFile.source === "global-default-env-file"
1037
+ ? "from the global default workspace config."
1038
+ : ".";
1039
+ collectDoctorCheck(checks, "ok", "env-file", `Loaded configuration from ${envFile.loadedPath} ${sourceLabel}`);
1040
+ }
1041
+ else if (envFile.source === "process-env") {
1042
+ collectDoctorCheck(checks, "ok", "env-file", "Using runtime paths provided directly via process.env; no .wiki.env file was loaded.");
1022
1043
  }
1023
1044
  else {
1024
- collectDoctorCheck(checks, "warn", "env-file", "No .wiki.env file was loaded; using process.env only.", "Run `tiangong-wiki setup` to generate a portable `.wiki.env` file.");
1045
+ collectDoctorCheck(checks, "warn", "env-file", "No workspace configuration was found from --env-file, WIKI_ENV_FILE, the current directory, or the global default workspace config.", "Run `tiangong-wiki setup`, set `WIKI_ENV_FILE`, or pass `--env-file` to point at a workspace explicitly.");
1025
1046
  }
1026
1047
  const wikiPath = env.WIKI_PATH ? path.resolve(env.WIKI_PATH) : null;
1027
1048
  const wikiRoot = wikiPath ? path.resolve(wikiPath, "..") : null;
@@ -1055,6 +1076,10 @@ export async function buildDoctorReport(env = process.env, options = {}) {
1055
1076
  loadedPath: envFile.loadedPath,
1056
1077
  autoDiscovered: envFile.autoDiscovered,
1057
1078
  missingRequestedPath: envFile.missingRequestedPath,
1079
+ missingDefaultPath: envFile.missingDefaultPath,
1080
+ source: envFile.source,
1081
+ globalConfigPath: envFile.globalConfigPath,
1082
+ defaultPath: envFile.defaultPath,
1058
1083
  },
1059
1084
  effectivePaths: {
1060
1085
  wikiPath,
package/dist/index.js CHANGED
@@ -29,12 +29,39 @@ import { loadRuntimeConfig } from "./core/runtime.js";
29
29
  import { embedPendingPages } from "./core/sync.js";
30
30
  import { processVaultQueueBatch } from "./core/vault-processing.js";
31
31
  import { handleCliError, writeJson } from "./utils/output.js";
32
+ function extractEnvFileOption(argv) {
33
+ const nextArgv = argv.slice(0, 2);
34
+ let envFile = null;
35
+ for (let index = 2; index < argv.length; index += 1) {
36
+ const arg = argv[index];
37
+ if (arg === "--env-file") {
38
+ const value = argv[index + 1];
39
+ if (!value || value.startsWith("-")) {
40
+ throw new Error("--env-file requires a value");
41
+ }
42
+ envFile = value;
43
+ index += 1;
44
+ continue;
45
+ }
46
+ if (arg.startsWith("--env-file=")) {
47
+ const value = arg.slice("--env-file=".length);
48
+ if (!value) {
49
+ throw new Error("--env-file requires a value");
50
+ }
51
+ envFile = value;
52
+ continue;
53
+ }
54
+ nextArgv.push(arg);
55
+ }
56
+ return { envFile, argv: nextArgv };
57
+ }
32
58
  function buildProgram() {
33
59
  const program = new Command();
34
60
  program
35
61
  .name("tiangong-wiki")
36
62
  .description("Tiangong Wiki — local-first indexing and query CLI")
37
63
  .version(packageJson.version)
64
+ .option("--env-file <path>", "Load runtime environment from a specific .wiki.env file")
38
65
  .showHelpAfterError();
39
66
  let runtimeConfig;
40
67
  try {
@@ -81,9 +108,13 @@ function buildProgram() {
81
108
  return program;
82
109
  }
83
110
  try {
111
+ const { envFile, argv } = extractEnvFileOption(process.argv);
112
+ if (envFile) {
113
+ process.env.WIKI_ENV_FILE = envFile;
114
+ }
84
115
  applyCliEnvironment(process.env, process.cwd());
85
116
  const program = buildProgram();
86
- await program.parseAsync(process.argv);
117
+ await program.parseAsync(argv);
87
118
  }
88
119
  catch (error) {
89
120
  handleCliError(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biaoo/tiangong-wiki",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Local-first wiki index and query engine for Markdown knowledge pages (Tiangong Wiki).",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -13,6 +13,13 @@ npx @biaoo/tiangong-wiki <command> [options]
13
13
  npm run dev -- <command> [options]
14
14
  ```
15
15
 
16
+ Global workspace resolution priority:
17
+
18
+ 1. `--env-file <path>`
19
+ 2. `WIKI_ENV_FILE`
20
+ 3. nearest `.wiki.env` found by walking upward from the current directory
21
+ 4. the global default workspace config written by `tiangong-wiki setup`
22
+
16
23
  ---
17
24
 
18
25
  ## Command Overview
@@ -57,6 +64,7 @@ Interactive step-by-step wizard that:
57
64
  - Records `WIKI_PATH`, `VAULT_PATH`, `WIKI_DB_PATH`, `WIKI_CONFIG_PATH`, `WIKI_TEMPLATES_PATH`
58
65
  - Optionally configures `EMBEDDING_*` and Synology vault settings
59
66
  - Writes `.wiki.env` in the current working directory
67
+ - Writes a global default workspace config pointing to that `.wiki.env`
60
68
  - Scaffolds `wiki/pages/`, `vault/`, `wiki.config.json`, and `templates/`
61
69
 
62
70
  After setup, run `tiangong-wiki doctor` then `tiangong-wiki init` to complete initialization.
@@ -64,11 +72,12 @@ After setup, run `tiangong-wiki doctor` then `tiangong-wiki init` to complete in
64
72
  ### doctor
65
73
 
66
74
  ```
67
- tiangong-wiki doctor [--probe] [--format text|json]
75
+ tiangong-wiki doctor [--env-file <path>] [--probe] [--format text|json]
68
76
  ```
69
77
 
70
78
  | Option | Description |
71
79
  | --- | --- |
80
+ | `--env-file` | Load a specific `.wiki.env` before running the command |
72
81
  | `--probe` | Additionally test remote services (embedding endpoint, Synology NAS) |
73
82
  | `--format` | Output format: `text` (default) or `json` |
74
83