@fugui200/llmwiki 0.1.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/bin/llmwiki.js +251 -0
- package/package.json +28 -0
- package/scripts/classify-project.js +403 -0
- package/scripts/codex-hook.js +127 -0
- package/scripts/init-project.js +704 -0
- package/skill/llmwiki-connector/.skill-meta.json +5 -0
- package/skill/llmwiki-connector/SKILL.md +123 -0
- package/skill/llmwiki-connector/scripts/wiki.sh +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# llmwiki
|
|
2
|
+
|
|
3
|
+
CLI for connecting AI coding agents to a project-scoped markdown knowledge base.
|
|
4
|
+
|
|
5
|
+
`llmwiki` does not upload or index your files. It writes local project markers and
|
|
6
|
+
agent context so Codex CLI, Claude Code, and Gemini CLI can decide when to recall
|
|
7
|
+
or archive wiki knowledge.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @fugui200/llmwiki@beta
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Initialize the current project:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
llmwiki init
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Non-interactive examples:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
llmwiki init --profile personal --agent codex
|
|
27
|
+
llmwiki init --profile local --agent claude-code
|
|
28
|
+
llmwiki init --workspace --profile team --wiki-root /path/to/team-wiki --agent all
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Check the current project routing:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
llmwiki status
|
|
35
|
+
llmwiki classify
|
|
36
|
+
llmwiki classify path/to/file path/to/another-file
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Sync a git-backed wiki root:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
llmwiki pull
|
|
43
|
+
llmwiki push "archive: update project notes"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Profiles
|
|
47
|
+
|
|
48
|
+
- `personal`: defaults to `~/.llmwiki`.
|
|
49
|
+
- `team`: requires an explicit `--wiki-root`.
|
|
50
|
+
- `local`: defaults to `.llmwiki-local` and disables `push`/`pull`.
|
|
51
|
+
|
|
52
|
+
## Agents
|
|
53
|
+
|
|
54
|
+
- `codex`: writes `.llmwiki.json` and installs a global Codex hook.
|
|
55
|
+
- `claude-code`: writes `.claude/settings.json` plus a lightweight `CLAUDE.md` block.
|
|
56
|
+
- `gemini`: writes a lightweight `GEMINI.md` block.
|
|
57
|
+
- `all`: configures all supported agents.
|
|
58
|
+
|
|
59
|
+
Agent integrations only inject routing metadata and recall rules. They do not
|
|
60
|
+
load the whole wiki on every prompt.
|
package/bin/llmwiki.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { execFileSync, execSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const HOME = require('os').homedir();
|
|
9
|
+
const LLMWIKI_DIR = process.env.LLMWIKI_DIR
|
|
10
|
+
? path.resolve(process.env.LLMWIKI_DIR)
|
|
11
|
+
: path.join(HOME, '.llmwiki');
|
|
12
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
13
|
+
const PKG_JSON = path.resolve(__dirname, '..', 'package.json');
|
|
14
|
+
|
|
15
|
+
function resolveRuntimeRoot() {
|
|
16
|
+
if (fs.existsSync(path.join(PACKAGE_ROOT, 'scripts', 'init-project.js'))) return PACKAGE_ROOT;
|
|
17
|
+
if (fs.existsSync(path.join(LLMWIKI_DIR, 'scripts', 'init-project.js'))) return LLMWIKI_DIR;
|
|
18
|
+
return LLMWIKI_DIR;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const RUNTIME_ROOT = resolveRuntimeRoot();
|
|
22
|
+
|
|
23
|
+
async function main() {
|
|
24
|
+
const args = process.argv.slice(2);
|
|
25
|
+
|
|
26
|
+
if (args[0] === '--version' || args[0] === '-v') {
|
|
27
|
+
const pkg = JSON.parse(fs.readFileSync(PKG_JSON, 'utf8'));
|
|
28
|
+
console.log(pkg.version);
|
|
29
|
+
} else if (!args[0] || args[0] === 'help' || args[0] === '--help' || args[0] === '-h') {
|
|
30
|
+
showHelp();
|
|
31
|
+
} else if (args[0] === 'init') {
|
|
32
|
+
if (isHelpArg(args[1])) {
|
|
33
|
+
showInitHelp();
|
|
34
|
+
} else {
|
|
35
|
+
await initProject();
|
|
36
|
+
}
|
|
37
|
+
} else if (args[0] === 'classify') {
|
|
38
|
+
classifyProject(args.slice(1));
|
|
39
|
+
} else if (args[0] === 'status') {
|
|
40
|
+
showStatus();
|
|
41
|
+
} else if (args[0] === 'push') {
|
|
42
|
+
pushWiki(args.slice(1).join(' '));
|
|
43
|
+
} else if (args[0] === 'pull') {
|
|
44
|
+
pullWiki();
|
|
45
|
+
} else {
|
|
46
|
+
showHelp();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isHelpArg(arg) {
|
|
51
|
+
return arg === 'help' || arg === '--help' || arg === '-h';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function showHelp() {
|
|
55
|
+
console.log('用法: llmwiki <command>');
|
|
56
|
+
console.log('');
|
|
57
|
+
console.log('命令:');
|
|
58
|
+
console.log(' init [options] 交互式导入 Agent、workspace/profile/wikiRoot 配置');
|
|
59
|
+
console.log(' classify [paths...] 根据 .llmwiki.json 判断归档项目归属');
|
|
60
|
+
console.log(' status 显示当前项目的 wiki 同步边界');
|
|
61
|
+
console.log(' push [msg] 提交并推送当前 wikiRoot 的变更');
|
|
62
|
+
console.log(' pull 拉取当前 wikiRoot 的最新内容');
|
|
63
|
+
console.log(' help 显示帮助信息');
|
|
64
|
+
console.log('');
|
|
65
|
+
console.log('Profile:');
|
|
66
|
+
console.log(' personal 个人知识库,默认 wikiRoot=~/.llmwiki');
|
|
67
|
+
console.log(' team 公司或团队共享知识库,必须显式指定 --wiki-root');
|
|
68
|
+
console.log(' local 仅本地沉淀,不允许 push/pull');
|
|
69
|
+
console.log('');
|
|
70
|
+
console.log('选项:');
|
|
71
|
+
console.log(' --version, -v 显示版本号');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function showInitHelp() {
|
|
75
|
+
console.log('用法: llmwiki init [--workspace] [--profile personal|team|local] [--wiki-root <path>] [--agent <agent>]');
|
|
76
|
+
console.log('');
|
|
77
|
+
console.log('说明:');
|
|
78
|
+
console.log(' 为当前项目导入指定 Agent 的 LLM Wiki 接入配置。');
|
|
79
|
+
console.log('');
|
|
80
|
+
console.log('Agent:');
|
|
81
|
+
console.log(' claude-code 写入 .claude/settings.json hooks');
|
|
82
|
+
console.log(' codex 写入 .llmwiki.json 项目标记,并安装全局 Codex hooks');
|
|
83
|
+
console.log(' gemini 写入 GEMINI.md 项目上下文入口');
|
|
84
|
+
console.log(' all 同时导入 claude-code、codex、gemini');
|
|
85
|
+
console.log('');
|
|
86
|
+
console.log('选项:');
|
|
87
|
+
console.log(' --workspace 生成 workspace 配置 .llmwiki.json,用于多子项目归属判断');
|
|
88
|
+
console.log(' --profile 设置知识库同步边界: personal | team | local');
|
|
89
|
+
console.log(' --wiki-root 设置当前项目使用的物理 wiki 根目录');
|
|
90
|
+
console.log(' --branch 设置 git 同步分支,默认 main');
|
|
91
|
+
console.log(' --help, -h 显示 init 帮助信息');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function loadClassifier() {
|
|
95
|
+
const classifier = path.join(RUNTIME_ROOT, 'scripts', 'classify-project.js');
|
|
96
|
+
if (!fs.existsSync(classifier)) {
|
|
97
|
+
console.error('错误: classify-project.js 不存在');
|
|
98
|
+
console.error(`期望路径: ${classifier}`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
return require(classifier);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function classifyProject(paths) {
|
|
105
|
+
const { classifyFromCwd } = loadClassifier();
|
|
106
|
+
const result = classifyFromCwd({ paths });
|
|
107
|
+
console.log(JSON.stringify(result, null, 2));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function defaultWikiContext(cwd = process.cwd()) {
|
|
111
|
+
const config = {
|
|
112
|
+
type: 'project',
|
|
113
|
+
name: 'default',
|
|
114
|
+
profile: 'personal',
|
|
115
|
+
wikiRoot: '~/.llmwiki',
|
|
116
|
+
sync: {
|
|
117
|
+
type: 'git',
|
|
118
|
+
branch: 'main',
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
return {
|
|
122
|
+
cwd,
|
|
123
|
+
config,
|
|
124
|
+
configDir: cwd,
|
|
125
|
+
configPath: null,
|
|
126
|
+
classification: {
|
|
127
|
+
workspace: null,
|
|
128
|
+
matchedProjects: [],
|
|
129
|
+
archiveProjects: [],
|
|
130
|
+
archiveType: 'unconfigured',
|
|
131
|
+
suggestedPath: null,
|
|
132
|
+
},
|
|
133
|
+
wikiRoot: LLMWIKI_DIR,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function getWikiContext(cwd = process.cwd()) {
|
|
138
|
+
const {
|
|
139
|
+
classifyConfig,
|
|
140
|
+
findEffectiveConfig,
|
|
141
|
+
resolveWikiRoot,
|
|
142
|
+
} = loadClassifier();
|
|
143
|
+
const match = findEffectiveConfig(cwd);
|
|
144
|
+
if (!match) return defaultWikiContext(cwd);
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
cwd,
|
|
148
|
+
config: match.config,
|
|
149
|
+
configDir: match.dir,
|
|
150
|
+
configPath: match.path,
|
|
151
|
+
classification: classifyConfig(match.config, match.dir, { cwd }),
|
|
152
|
+
wikiRoot: resolveWikiRoot(match.config, match.dir),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function formatSync(sync = {}) {
|
|
157
|
+
if (sync.type === 'none') return 'none';
|
|
158
|
+
return `${sync.type || 'git'} ${sync.branch || 'main'}`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function showStatus() {
|
|
162
|
+
const context = getWikiContext();
|
|
163
|
+
const { config, classification } = context;
|
|
164
|
+
const project = (classification.archiveProjects || [])[0] || config.name || '(none)';
|
|
165
|
+
console.log(`project: ${project}`);
|
|
166
|
+
console.log(`profile: ${config.profile || 'personal'}`);
|
|
167
|
+
console.log(`wikiRoot: ${context.wikiRoot}`);
|
|
168
|
+
console.log(`sync: ${formatSync(config.sync)}`);
|
|
169
|
+
console.log(`archiveTarget: ${classification.suggestedPath || '(none)'}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function ensureGitSync(context) {
|
|
173
|
+
const sync = context.config.sync || { type: 'git', branch: 'main' };
|
|
174
|
+
if (sync.type === 'none') {
|
|
175
|
+
console.error('错误: 当前 wiki 实例未启用 git 同步');
|
|
176
|
+
console.error(`wikiRoot: ${context.wikiRoot}`);
|
|
177
|
+
console.error(`profile: ${context.config.profile || 'personal'}`);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
type: sync.type || 'git',
|
|
182
|
+
branch: sync.branch || 'main',
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function pullWiki() {
|
|
187
|
+
const context = getWikiContext();
|
|
188
|
+
const sync = ensureGitSync(context);
|
|
189
|
+
if (!fs.existsSync(path.join(context.wikiRoot, '.git'))) {
|
|
190
|
+
console.error(`错误: ${context.wikiRoot} 不是 git 仓库`);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
console.log(`wikiRoot: ${context.wikiRoot}`);
|
|
194
|
+
console.log(`profile: ${context.config.profile || 'personal'}`);
|
|
195
|
+
console.log(`sync: ${formatSync(sync)}`);
|
|
196
|
+
execFileSync('git', ['pull', '--rebase', 'origin', sync.branch], { cwd: context.wikiRoot, stdio: 'inherit' });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function pushWiki(msg) {
|
|
200
|
+
const context = getWikiContext();
|
|
201
|
+
const sync = ensureGitSync(context);
|
|
202
|
+
if (!fs.existsSync(path.join(context.wikiRoot, '.git'))) {
|
|
203
|
+
console.error(`错误: ${context.wikiRoot} 不是 git 仓库`);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const status = execSync('git status --porcelain', { cwd: context.wikiRoot, encoding: 'utf8' });
|
|
208
|
+
if (!status.trim()) {
|
|
209
|
+
console.log('没有需要提交的变更。');
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const commitMsg = msg || '[archive] wiki update';
|
|
214
|
+
console.log(`wikiRoot: ${context.wikiRoot}`);
|
|
215
|
+
console.log(`profile: ${context.config.profile || 'personal'}`);
|
|
216
|
+
console.log(`sync: ${formatSync(sync)}`);
|
|
217
|
+
console.log(`变更文件:\n${status}`);
|
|
218
|
+
console.log(`提交信息: ${commitMsg}`);
|
|
219
|
+
|
|
220
|
+
execFileSync('git', ['add', '-A'], { cwd: context.wikiRoot, stdio: 'inherit' });
|
|
221
|
+
execFileSync('git', ['commit', '-m', commitMsg], { cwd: context.wikiRoot, stdio: 'inherit' });
|
|
222
|
+
execFileSync('git', ['push', 'origin', sync.branch], { cwd: context.wikiRoot, stdio: 'inherit' });
|
|
223
|
+
|
|
224
|
+
console.log('推送完成。');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function initProject() {
|
|
228
|
+
if (!fs.existsSync(RUNTIME_ROOT)) {
|
|
229
|
+
console.error('错误: ~/.llmwiki 不存在');
|
|
230
|
+
console.error('');
|
|
231
|
+
console.error('解决方法:');
|
|
232
|
+
console.error(' npm install -g llmwiki@beta');
|
|
233
|
+
console.error(' 或设置 LLMWIKI_DIR=/path/to/llmwiki-runtime');
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const initScript = path.join(RUNTIME_ROOT, 'scripts', 'init-project.js');
|
|
238
|
+
if (fs.existsSync(initScript)) {
|
|
239
|
+
const init = require(initScript);
|
|
240
|
+
await init.main();
|
|
241
|
+
} else {
|
|
242
|
+
console.error('错误: init-project.js 不存在');
|
|
243
|
+
console.error(`期望路径: ${initScript}`);
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
main().catch(error => {
|
|
249
|
+
console.error(error.message);
|
|
250
|
+
process.exit(1);
|
|
251
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fugui200/llmwiki",
|
|
3
|
+
"version": "0.1.2-beta.0",
|
|
4
|
+
"description": "Project-scoped LLM wiki CLI for Codex, Claude Code, and Gemini",
|
|
5
|
+
"bin": {
|
|
6
|
+
"llmwiki": "bin/llmwiki.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "node --test test/*.test.js",
|
|
10
|
+
"wiki:lint": "node scripts/wiki-lint.js",
|
|
11
|
+
"wiki:overview": "node scripts/wiki-build-overview.js",
|
|
12
|
+
"wiki:graph": "node scripts/wiki-build-graph.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"bin/llmwiki.js",
|
|
16
|
+
"scripts/classify-project.js",
|
|
17
|
+
"scripts/codex-hook.js",
|
|
18
|
+
"scripts/init-project.js",
|
|
19
|
+
"skill/llmwiki-connector/"
|
|
20
|
+
],
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"registry": "https://registry.npmjs.org/",
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"private": false,
|
|
27
|
+
"type": "commonjs"
|
|
28
|
+
}
|