@kybird/llm-wiki 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +168 -0
- package/TROUBLESHOOTING.md +103 -0
- package/bin/llm-wiki.js +102 -0
- package/lib/find-doc-root.js +93 -0
- package/lib/find-qmd.js +52 -0
- package/lib/init.js +236 -0
- package/lib/kanban-cmd.js +828 -0
- package/lib/kanban.js +363 -0
- package/lib/wiki-compile.js +362 -0
- package/lib/wiki-lint.js +272 -0
- package/lib/wiki-search.js +186 -0
- package/package.json +40 -0
- package/skills/kanban-plan/SKILL.md +71 -0
- package/skills/wiki-compile/SKILL.md +115 -0
- package/skills/wiki-lint/SKILL.md +57 -0
- package/skills/wiki-log/SKILL.md +157 -0
- package/skills/wiki-search/SKILL.md +39 -0
- package/skills/work-loop/SKILL.md +88 -0
- package/templates/doc/raw/.gitkeep +2 -0
- package/templates/doc/wiki/index.md +36 -0
- package/templates/githooks/pre-commit +77 -0
- package/templates/scripts/sync_agent_docs.bat +33 -0
- package/templates/scripts/sync_agent_docs.sh +25 -0
- package/templates/scripts/sync_skills.bat +26 -0
- package/templates/scripts/sync_skills.sh +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kybird
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# llm-wiki
|
|
2
|
+
|
|
3
|
+
An **LLM-friendly knowledge graph + kanban board** for AI coding agents. Capture errors, decisions, and discoveries as raw daily logs, then compile them into a searchable wiki of concepts, patterns, and anti-patterns that agents consult before writing code. A file-based kanban (`doc/kanban/`) turns the same repo into an unattended work queue.
|
|
4
|
+
|
|
5
|
+
Inspired by [Karpathy's Agentic Memory](https://github.com/karpathy/llm.c) ideas — designed so the *next* agent session doesn't repeat the *last* agent's mistakes.
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
AI coding agents (Claude Code, Cursor, ZCode, Gemini CLI, …) forget everything between sessions. A project wiki that they actually read — grounded in git hashes, real error strings, and real failure cases — turns one-off debugging pain into durable, reusable knowledge. The kanban adds a convergence loop: cards are picked, resolved, parked for human judgment, or abandoned *with a reason* that feeds back into the wiki as anti-pattern material.
|
|
10
|
+
|
|
11
|
+
The skills (`wiki-search`, `wiki-log`, `wiki-compile`, `wiki-lint`, `kanban-plan`, `work-loop`) are **LLM prompts**: the intelligence lives in the agent's context, not in a server. The CLI is just the plumbing. Planning sessions turn plans into cards (`kanban-plan`); unattended loop sessions resolve them (`work-loop`).
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @kybird/llm-wiki # installs the `llm-wiki` command
|
|
17
|
+
# or run without installing:
|
|
18
|
+
npx @kybird/llm-wiki init
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quickstart (in any repo)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cd my-project
|
|
25
|
+
llm-wiki init # scaffolds doc/ (wiki + kanban), copies skills + hooks + scripts
|
|
26
|
+
|
|
27
|
+
# Enable git hooks (run once per clone):
|
|
28
|
+
git config core.hooksPath githooks
|
|
29
|
+
|
|
30
|
+
# (Optional) QMD adds semantic search on top of grep — grep alone works without it:
|
|
31
|
+
npm install @tobilu/qmd
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Then tell your AI agent (via its instructions file — `CLAUDE.md` / `AGENTS.md` / etc.):
|
|
35
|
+
- **Before any task**: run `llm-wiki search "<task keywords>"` and read matching `status: active` pages.
|
|
36
|
+
- **After fixing a bug / making a design decision**: use the `wiki-log` skill to record a Case in `doc/raw/YYYY-MM-DD.md`.
|
|
37
|
+
- **Periodically**: use `wiki-compile` to promote raw cases into `doc/wiki/` pages.
|
|
38
|
+
|
|
39
|
+
That's it. The agent does the rest.
|
|
40
|
+
|
|
41
|
+
## Commands
|
|
42
|
+
|
|
43
|
+
| Command | What it does |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `llm-wiki init [--check]` | Scaffold `doc/` (wiki + kanban), copy skills + hooks + scripts. Marker-aware — re-running updates copies and preserves your edits. `--check` reports without writing |
|
|
46
|
+
| `llm-wiki search "<query>"` | Grep exact matching + QMD semantic search, **always merged**; ranked by matched-keyword count with line snippets |
|
|
47
|
+
| `llm-wiki compile list` | Show raw logs not yet compiled — header date **or** content hash (`compile-state.json`), so same-day appends are caught too |
|
|
48
|
+
| `llm-wiki compile index` | Rebuild `doc/wiki/index.md` (with aliases and answers), regenerate `compile-state.json`, and sync the QMD index. **This is a "compile complete" declaration** — run it after the wiki-compile skill's phases, not instead of them |
|
|
49
|
+
| `llm-wiki lint` | Broken links, **evidence back-matching** (hash refs & `### Error` quotes must exist verbatim in `doc/raw/`), uncompiled concepts, metadata, staleness |
|
|
50
|
+
| `llm-wiki board` / `board report` | Derived kanban view / dashboard (done:abandoned ratio, trend, QA reverts, waiting queue) |
|
|
51
|
+
| `llm-wiki board video` | Replay `activity.jsonl` into a board timelapse MP4 (requires the `video/` Remotion project; CPU render, no GPU) |
|
|
52
|
+
| `llm-wiki card new/show/edit` | Create and edit cards — the CLI is the only writer (sentinel-safe sections) |
|
|
53
|
+
| `llm-wiki pick --claim <name>` | Atomically claim the next eligible card (lock, WIP limit, dependencies, claim expiry) |
|
|
54
|
+
| `llm-wiki handoff <title> --question "…"` | Park a card for human judgment and release the claim |
|
|
55
|
+
| `llm-wiki done <title> --result "…"` | Complete a card — Result is required |
|
|
56
|
+
| `llm-wiki supersede <title> --by a,b` | Replace a card by children; the parent dissolves into `superseded/` |
|
|
57
|
+
| `llm-wiki abandon <title> --reason "…"` | Discard — reason required, and auto-logged to `doc/raw/` as anti-pattern material |
|
|
58
|
+
| `llm-wiki reopen <title> --why "…"` | QA: revert a fake-done card back to doing |
|
|
59
|
+
|
|
60
|
+
`search`, `lint`, `compile list|index`, `board`, `pick` accept `--json` (a `{schemaVersion: 1, kind: …}` envelope for scripts and skills).
|
|
61
|
+
|
|
62
|
+
## What `init` creates
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
your-repo/
|
|
66
|
+
├── doc/
|
|
67
|
+
│ ├── raw/ # daily logs (YYYY-MM-DD.md) — wiki-log writes here
|
|
68
|
+
│ ├── wiki/ # compiled knowledge
|
|
69
|
+
│ │ ├── index.md # auto-rebuilt by `compile index`
|
|
70
|
+
│ │ └── concepts/ patterns/ antipatterns/ answers/
|
|
71
|
+
│ └── kanban/ # card-per-file kanban
|
|
72
|
+
│ ├── board.yml # statuses, WIP limit, claim timeout
|
|
73
|
+
│ ├── cards/ # active: todo / doing / review (frontmatter status)
|
|
74
|
+
│ ├── done/ superseded/ abandoned/ # termination = the folder
|
|
75
|
+
│ └── activity.jsonl # append-only audit log (10k line cap)
|
|
76
|
+
├── .agents/skills/ # canonical skills (ZCode, Cursor, …)
|
|
77
|
+
├── .claude/skills/ # mirror for Claude Code
|
|
78
|
+
├── scripts/ # doc/skill sync scripts (marker-protected copies)
|
|
79
|
+
└── githooks/pre-commit # CLAUDE.md drift guard + skill mirror + uncompiled-log nudge
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Updating
|
|
83
|
+
|
|
84
|
+
The skills, hooks, and scripts copied into your repo are **marker-protected copies**. Updating is two commands:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm update -g llm-wiki # refresh the global CLI
|
|
88
|
+
llm-wiki init --check # what would change? (ok / stale / user-modified / missing)
|
|
89
|
+
llm-wiki init # apply — stale copies update, your edits survive
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The contract: a copy keeps its version marker (`skill-version:` in skills, `llm-wiki-template-version:` in hooks and scripts) only while it is unmodified. **To customize a copy, delete its marker line** — `init` then treats it as yours and never overwrites it. Repos that keep their own canonical hook can skip the copy entirely: set `"hooksPath": "templates/githooks"` in `llm-wiki.config.json`.
|
|
93
|
+
|
|
94
|
+
## Configuration (optional)
|
|
95
|
+
|
|
96
|
+
Create `llm-wiki.config.json` in your repo root:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"projectName": "my-project",
|
|
101
|
+
"collections": {
|
|
102
|
+
"wiki": "my-project-wiki",
|
|
103
|
+
"raw": "my-project-wiki-raw"
|
|
104
|
+
},
|
|
105
|
+
"hooksPath": "templates/githooks"
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- `projectName` — appears in the `index.md` header (omitted if unset).
|
|
110
|
+
- `collections` — QMD collection names (defaults derive from your repo's folder name; unique names matter — colliding names silently cross-contaminate search across projects).
|
|
111
|
+
- `hooksPath` — set it and `init` won't create a `githooks/` copy; your repo uses that path directly (for repos that vendor their own hook source).
|
|
112
|
+
|
|
113
|
+
You can also set `LLM_WIKI_ROOT=/path/to/doc-parent` to point at a `doc/` outside the repo.
|
|
114
|
+
|
|
115
|
+
## How the knowledge flows
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
agent fixes a bug
|
|
119
|
+
│
|
|
120
|
+
▼ wiki-log skill
|
|
121
|
+
doc/raw/2026-07-24.md (Case: grounding + error + fix + analysis)
|
|
122
|
+
│
|
|
123
|
+
▼ wiki-compile skill (LLM extracts & synthesizes)
|
|
124
|
+
doc/wiki/patterns/foo.md doc/wiki/antipatterns/bar.md
|
|
125
|
+
│
|
|
126
|
+
▼ llm-wiki compile index
|
|
127
|
+
doc/wiki/index.md + QMD embeddings
|
|
128
|
+
│
|
|
129
|
+
▼ next agent session
|
|
130
|
+
llm-wiki search "foo" → reads the pattern, avoids repeating the mistake
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Raw logs are the source of truth; compiled wiki pages are derived. The `status:` field (`active` / `deprecated` / `superseded`) lets knowledge evolve without losing history.
|
|
134
|
+
|
|
135
|
+
## Agent-docs & skill sync (bonus)
|
|
136
|
+
|
|
137
|
+
`init` also installs a `pre-commit` hook that keeps your agent-instruction files in sync:
|
|
138
|
+
|
|
139
|
+
- Edit `CLAUDE.md` (canonical) → `agents.md`, `GEMINI.md` auto-mirror on commit.
|
|
140
|
+
- Edit `.agents/skills/` (canonical) → `.claude/skills/` auto-mirrors.
|
|
141
|
+
- Editing a copy directly is rejected with a clear message.
|
|
142
|
+
|
|
143
|
+
This lets you target multiple agent CLIs from one canonical source. If you don't want it, simply skip `git config core.hooksPath githooks`. Package-level updates of the copied skills/hooks/scripts follow the marker contract — see [Updating](#updating).
|
|
144
|
+
|
|
145
|
+
## QMD / semantic search
|
|
146
|
+
|
|
147
|
+
[`@tobilu/qmd`](https://github.com/tobi/qmd) provides local vector embeddings (no network) for semantic search. It's an **optional** dependency:
|
|
148
|
+
|
|
149
|
+
- Installed → `llm-wiki search` merges QMD semantic results on top of the grep results.
|
|
150
|
+
- Absent → grep-only; exact matching still fully works (ranked by matched keywords, with line snippets).
|
|
151
|
+
|
|
152
|
+
Grep always runs — semantic search is a supplement, never a replacement. First use downloads a ~300 MB embedding model to `~/.cache/qmd/models/`. For CUDA/build issues, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md).
|
|
153
|
+
|
|
154
|
+
## Development (this repo)
|
|
155
|
+
|
|
156
|
+
This repo dogfoods itself:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
npm link # global `llm-wiki` runs this working tree
|
|
160
|
+
llm-wiki init # syncs skills + scripts into this repo (marker-aware)
|
|
161
|
+
git config core.hooksPath templates/githooks
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`llm-wiki.config.json` here declares `"hooksPath": "templates/githooks"`, so `init` never creates a `githooks/` copy — the active hook *is* the canonical template. See [doc/improvement-plan.md](doc/improvement-plan.md) for the roadmap state and [doc/plan.md](doc/plan.md) for the design record.
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
MIT
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Troubleshooting: QMD / node-llama-cpp build-time CUDA failures
|
|
2
|
+
|
|
3
|
+
> This document covers Windows + NVIDIA-specific diagnostics for the optional `@tobilu/qmd` dependency (used by `llm-wiki compile index` for semantic search embeddings). If `compile index` prints `falling back to using Vulkan` or a `CUDA error`, consult this.
|
|
4
|
+
>
|
|
5
|
+
> QMD is **optional**. Without it, `llm-wiki search` falls back to grep and the wiki index still rebuilds correctly.
|
|
6
|
+
|
|
7
|
+
## Runtime CUDA OOM vs build-time compile failure
|
|
8
|
+
|
|
9
|
+
These are two different failure modes:
|
|
10
|
+
|
|
11
|
+
| Symptom | Cause | Fix |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `CUDA error: out of memory` at runtime | Transient driver/VRAM state, GPU asleep | `QMD_LLAMA_GPU=false` for that run |
|
|
14
|
+
| `error STL1002: Unexpected compiler version, expected CUDA 12.4+` | MSVC + CUDA Toolkit mismatch at build time | Upgrade CUDA Toolkit (see below) |
|
|
15
|
+
|
|
16
|
+
## Variable name reminder
|
|
17
|
+
|
|
18
|
+
Use **`QMD_LLAMA_GPU`** to force CPU. Do **not** use `NODE_LLAMA_CPP_GPU` — it is read by nothing in the qmd codebase and has no effect.
|
|
19
|
+
|
|
20
|
+
## node-llama-cpp v3.x prebuilt binary resolution
|
|
21
|
+
|
|
22
|
+
node-llama-cpp v3.x ships prebuilt binaries as npm `optionalDependencies`. `getLlama({build: "autoAttempt"})` priority:
|
|
23
|
+
|
|
24
|
+
1. **Prebuilt binary load** — `node_modules/@node-llama-cpp/<platform>/bins/.../llama-addon.node`. No MSBuild needed; only matching CUDA runtime DLLs required.
|
|
25
|
+
2. **Source build** (CMake/MSBuild) — reached only if (1) fails.
|
|
26
|
+
3. **Other backend fallback** (Vulkan, CPU).
|
|
27
|
+
|
|
28
|
+
**Most common real-world failure**: multiple CUDA versions coexisting → prebuilt DLL conflict. Example: CUDA 11.8 + 13.x coexist → `win-x64-cuda` prebuilt fails to load → falls to source build → exposes `.targets` problems.
|
|
29
|
+
|
|
30
|
+
**Fix (restore prebuilt path)**: keep only one CUDA version installed. Then prebuilt loads cleanly and source build is never attempted.
|
|
31
|
+
|
|
32
|
+
## Required CUDA version for prebuilt binaries
|
|
33
|
+
|
|
34
|
+
The prebuilt ggml-cuda DLL imports a specific `cublas64_XX.dll`. Check which one your installed prebuilt expects:
|
|
35
|
+
|
|
36
|
+
```powershell
|
|
37
|
+
$dll = "node_modules\@node-llama-cpp\win-x64-cuda\bins\win-x64-cuda\ggml-cuda.dll"
|
|
38
|
+
$bytes = [System.IO.File]::ReadAllBytes($dll)
|
|
39
|
+
$ascii = [System.Text.Encoding]::ASCII.GetString($bytes)
|
|
40
|
+
[regex]::Matches($ascii, "cublas64_\w+\.dll") | ForEach-Object { $_.Value } | Sort-Object -Unique
|
|
41
|
+
# cublas64_13.dll → CUDA 13.x required
|
|
42
|
+
# cublas64_12.dll → CUDA 12.x required
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Install the matching CUDA Toolkit. The NVIDIA driver's `nvidia-smi` "CUDA Version" shows the max runtime the driver supports natively; if the Toolkit exceeds it, Forward Compatibility kicks in and some prebuilts break there too.
|
|
46
|
+
|
|
47
|
+
## MSVC / CUDA Toolkit version matrix
|
|
48
|
+
|
|
49
|
+
MSVC's `yvals_core.h` static assertion enforces a minimum CUDA version at compile time:
|
|
50
|
+
|
|
51
|
+
| MSVC version | Required CUDA minimum |
|
|
52
|
+
|---|---|
|
|
53
|
+
| 14.39 and below | CUDA 11.x allowed |
|
|
54
|
+
| 14.40–14.43 | CUDA 12.0+ recommended |
|
|
55
|
+
| **14.44+** | **CUDA 12.4+ required** |
|
|
56
|
+
|
|
57
|
+
VS 2022 auto-updates push MSVC forward, so if you don't upgrade CUDA Toolkit alongside it, build-time compatibility violations occur. **Unrelated to GPU hardware/driver state.**
|
|
58
|
+
|
|
59
|
+
## VS BuildCustomizations (.targets) conflict
|
|
60
|
+
|
|
61
|
+
> If CUDA Toolkit is upgraded but the build still invokes an old `nvcc.exe`, suspect this.
|
|
62
|
+
|
|
63
|
+
**Symptom**: After installing CUDA 13.x, CMake logs `Found CUDAToolkit: ...v13.3` correctly, but MSBuild still calls `v11.8\bin\nvcc.exe` during compilation → same `error STL1002`.
|
|
64
|
+
|
|
65
|
+
**Root cause**: stale CUDA `.targets` files in VS 2022 BuildCustomizations:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
C:\Program Files\Microsoft Visual Studio\2022\<Edition>\MSBuild\Microsoft\VC\v170\BuildCustomizations\
|
|
69
|
+
├── CUDA 11.8.props ← stale
|
|
70
|
+
├── CUDA 11.8.targets ← stale (hardcodes old nvcc path)
|
|
71
|
+
└── CUDA 11.8.xml ← stale
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
MSBuild loads `.targets` regardless of CMake's CUDA detection. Environment variables (`CUDACXX`, `CMAKE_CUDA_COMPILER`, `CUDA_PATH`) cannot override it.
|
|
75
|
+
|
|
76
|
+
**Trigger**: unchecking "Visual Studio Integration" during CUDA Toolkit upgrade leaves the old `.targets` in place.
|
|
77
|
+
|
|
78
|
+
**Fix** (admin PowerShell):
|
|
79
|
+
|
|
80
|
+
```powershell
|
|
81
|
+
$dir = "C:\Program Files\Microsoft Visual Studio\2022\<Edition>\MSBuild\Microsoft\VC\v170\BuildCustomizations"
|
|
82
|
+
Remove-Item "$dir\CUDA 11.8.props" -Force
|
|
83
|
+
Remove-Item "$dir\CUDA 11.8.targets" -Force
|
|
84
|
+
Remove-Item "$dir\CUDA 11.8.xml" -Force
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Or: Control Panel → uninstall "NVIDIA CUDA Visual Studio Integration 11.8".
|
|
88
|
+
|
|
89
|
+
Removing `.targets` does **not** delete the CUDA 11.8 toolkit itself — other projects can still use the v11.8 path explicitly. Side-by-side coexistence is preserved.
|
|
90
|
+
|
|
91
|
+
## Detection priority
|
|
92
|
+
|
|
93
|
+
1. Look for `falling back to using Vulkan` in `compile index` output → prebuilt DLL conflict (stage 1 failed).
|
|
94
|
+
2. Check for `error STL1002` or `expected CUDA 12.4 or newer` in the same output → MSVC/CUDA mismatch.
|
|
95
|
+
3. Compare `nvcc --version` against the VS 2022 MSVC version (`cl.exe` 19.44+ needs CUDA 12.4+).
|
|
96
|
+
4. Final result `✓ Done!` means embedding succeeded (functionally OK, only performance degraded).
|
|
97
|
+
|
|
98
|
+
## Anti-patterns (do not do)
|
|
99
|
+
|
|
100
|
+
- ❌ `NODE_LLAMA_CPP_GPU=false` — read by nothing; ineffective.
|
|
101
|
+
- ❌ "GPU failed so force CPU" for a **build-time** compiler mismatch — runtime env var can't fix a compile error.
|
|
102
|
+
- ❌ "Leave Vulkan fallback, it works" — ~16× slower than GPU; costly if you compile frequently.
|
|
103
|
+
- ❌ "CUDA 11.8 is stable, keep it" — VS 2022 auto-update forces MSVC up, making 11.8 unsustainable.
|
package/bin/llm-wiki.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// llm-wiki — LLM-friendly knowledge graph CLI for AI coding agents.
|
|
3
|
+
// 서브커맨드: search, compile, lint, init.
|
|
4
|
+
// 스킬(SKILL.md)이 이 명령을 호출한다. 에이전트 CLI(Claude/ZCode/Cursor) 무관.
|
|
5
|
+
const { search } = require('../lib/wiki-search');
|
|
6
|
+
const { compile } = require('../lib/wiki-compile');
|
|
7
|
+
const { lint } = require('../lib/wiki-lint');
|
|
8
|
+
const { init } = require('../lib/init');
|
|
9
|
+
const kanbanCmd = require('../lib/kanban-cmd');
|
|
10
|
+
|
|
11
|
+
function printUsage() {
|
|
12
|
+
console.log(`llm-wiki — LLM-friendly knowledge graph + kanban for AI coding agents
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
llm-wiki search "<query>" Semantic+grep merged search over wiki + raw logs
|
|
16
|
+
llm-wiki compile list Show raw logs modified since last compile
|
|
17
|
+
llm-wiki compile index Rebuild wiki index.md + sync QMD search index
|
|
18
|
+
llm-wiki lint Validate wiki integrity (links, metadata, evidence)
|
|
19
|
+
llm-wiki init [--check] Scaffold doc/ + skills/ + hooks (--check: report only)
|
|
20
|
+
|
|
21
|
+
Kanban (cards are files; CLI is the only writer):
|
|
22
|
+
llm-wiki board [--html] [--json] Derived board view (columns, WIP, queue) / static HTML
|
|
23
|
+
llm-wiki board report Dashboard (done:abandoned ratio, trend, reverts)
|
|
24
|
+
llm-wiki board video Timelapse of board activity → MP4 (needs video/ project)
|
|
25
|
+
llm-wiki card new "<title>" Create card (--goal, --ac, --depends)
|
|
26
|
+
llm-wiki card show <title> Print card file
|
|
27
|
+
llm-wiki card edit <title> Sentinel-safe edits (--goal/--ac/--add-ac/--check-ac/--note/--plan)
|
|
28
|
+
llm-wiki pick --claim <name> Atomically claim the next eligible card (locks, WIP, deps)
|
|
29
|
+
llm-wiki handoff <title> --question "…" Park for human judgment, release claim
|
|
30
|
+
llm-wiki done <title> --result "…" Complete (Result required)
|
|
31
|
+
llm-wiki supersede <title> --by a,b Replace by children (parent dissolves)
|
|
32
|
+
llm-wiki abandon <title> --reason "…" Discard (reason required, never deleted)
|
|
33
|
+
llm-wiki reopen <title> --why "…" QA: revert a fake-done card to doing
|
|
34
|
+
llm-wiki resume <title> [--note "…"] Return a review (parked) card to todo
|
|
35
|
+
|
|
36
|
+
Optional:
|
|
37
|
+
npm i @tobilu/qmd Enable semantic search (falls back to grep if absent)
|
|
38
|
+
--json Machine-readable output: {schemaVersion: 1, kind: ...}
|
|
39
|
+
(search, lint, compile list|index)
|
|
40
|
+
LLM_WIKI_ROOT=/path Override doc/ root location
|
|
41
|
+
llm-wiki.config.json { "projectName": "...", "collections": {...},
|
|
42
|
+
"hooksPath": "templates/githooks" }`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const [, , subcommand, ...rest] = process.argv;
|
|
46
|
+
// --json/--html은 어느 위치에 와도 플래그로 뽑아낸다 (검색어 문자열에서 제외).
|
|
47
|
+
const jsonRequested = rest.includes('--json');
|
|
48
|
+
const htmlRequested = rest.includes('--html');
|
|
49
|
+
const args = rest.filter(a => a !== '--json' && a !== '--html');
|
|
50
|
+
|
|
51
|
+
switch (subcommand) {
|
|
52
|
+
case 'search':
|
|
53
|
+
search(args.join(' '), { json: jsonRequested });
|
|
54
|
+
break;
|
|
55
|
+
case 'compile':
|
|
56
|
+
compile(args[0], { json: jsonRequested }); // 'list' | 'index'
|
|
57
|
+
break;
|
|
58
|
+
case 'lint':
|
|
59
|
+
lint({ json: jsonRequested });
|
|
60
|
+
break;
|
|
61
|
+
case 'init':
|
|
62
|
+
init({ check: args.includes('--check') });
|
|
63
|
+
break;
|
|
64
|
+
case 'board':
|
|
65
|
+
if (args[0] === 'report') kanbanCmd.boardReport({ json: jsonRequested });
|
|
66
|
+
else if (args[0] === 'video') kanbanCmd.boardVideo({ rest: args });
|
|
67
|
+
else kanbanCmd.boardView({ rest: args, json: jsonRequested, html: htmlRequested });
|
|
68
|
+
break;
|
|
69
|
+
case 'card':
|
|
70
|
+
kanbanCmd.dispatchCard(args);
|
|
71
|
+
break;
|
|
72
|
+
case 'pick':
|
|
73
|
+
kanbanCmd.pick({ rest: args, json: jsonRequested });
|
|
74
|
+
break;
|
|
75
|
+
case 'handoff':
|
|
76
|
+
kanbanCmd.handoff({ rest: args });
|
|
77
|
+
break;
|
|
78
|
+
case 'done':
|
|
79
|
+
kanbanCmd.doneCard({ rest: args });
|
|
80
|
+
break;
|
|
81
|
+
case 'supersede':
|
|
82
|
+
kanbanCmd.supersede({ rest: args });
|
|
83
|
+
break;
|
|
84
|
+
case 'abandon':
|
|
85
|
+
kanbanCmd.abandon({ rest: args });
|
|
86
|
+
break;
|
|
87
|
+
case 'reopen':
|
|
88
|
+
kanbanCmd.reopen({ rest: args });
|
|
89
|
+
break;
|
|
90
|
+
case 'resume':
|
|
91
|
+
kanbanCmd.resume({ rest: args });
|
|
92
|
+
break;
|
|
93
|
+
case '--help':
|
|
94
|
+
case '-h':
|
|
95
|
+
case undefined:
|
|
96
|
+
printUsage();
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
console.error(`Unknown command: ${subcommand}`);
|
|
100
|
+
printUsage();
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// cwd 기반 doc/ 루트 자동 탐색.
|
|
2
|
+
// llm-wiki는 어느 리포에서 실행되든 동작해야 하므로, 스크립트 자신의 위치(__dirname)
|
|
3
|
+
// 에 의존하지 않고 cwd에서 위로 올라가며 `doc/wiki`를 찾는다.
|
|
4
|
+
//
|
|
5
|
+
// 탐색 우선순위:
|
|
6
|
+
// 1. LLM_WIKI_ROOT 환경변수 (명시적 오버라이드)
|
|
7
|
+
// 2. cwd에서 위로 올라가며 doc/wiki 디렉토리가 있는 첫 조상
|
|
8
|
+
// 3. cwd/doc (init 전이라 없어도 호출 가능하도록 폴백)
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
function findDocRoot(startDir) {
|
|
13
|
+
// 1) 환경변수 오버라이드
|
|
14
|
+
if (process.env.LLM_WIKI_ROOT) {
|
|
15
|
+
return path.resolve(process.env.LLM_WIKI_ROOT);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 2) cwd에서 위로 올라가며 doc/wiki 탐색
|
|
19
|
+
let dir = path.resolve(startDir || process.cwd());
|
|
20
|
+
for (let i = 0; i < 20; i++) {
|
|
21
|
+
const candidate = path.join(dir, 'doc', 'wiki');
|
|
22
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
|
|
23
|
+
return path.join(dir, 'doc');
|
|
24
|
+
}
|
|
25
|
+
const parent = path.dirname(dir);
|
|
26
|
+
if (parent === dir) break; // 파일시스템 루트 도달
|
|
27
|
+
dir = parent;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 3) 폴백: cwd/doc (init 전 상태에서도 호출 허용)
|
|
31
|
+
return path.join(process.cwd(), 'doc');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 리포 폴더명을 QMD 컬렉션 이름으로 안전하게 변환.
|
|
35
|
+
// 소문자화 + 영숫자 외 문자는 '-'로 치환 + 연속 '-' 축약 + 양끝 '-' 제거.
|
|
36
|
+
// 빈 문자열이 되면(폴더명이 특수문자뿐인 극단적 케이스) 'project'로 폴백.
|
|
37
|
+
function sanitizeCollectionName(name) {
|
|
38
|
+
const cleaned = String(name || '')
|
|
39
|
+
.toLowerCase()
|
|
40
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
41
|
+
.replace(/^-+|-+$/g, '');
|
|
42
|
+
return cleaned || 'project';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// docRoot(`<repo>/doc`)에서 리포 폴더명을 뽑아 기본 컬렉션 이름을 만든다.
|
|
46
|
+
// ⚠️ 과거에는 'ttswiki'/'ttswiki-raw'로 하드코딩돼 있었다 — "path-agnostic, drop-in for
|
|
47
|
+
// any repo"라는 이 도구의 목표와 달리, 두 프로젝트가 기본값 그대로 init하면 전역 QMD 설정
|
|
48
|
+
// (~/.config/qmd/index.yml)에서 컬렉션 이름이 충돌해 서로의 로그가 뒤섞여 검색되는 사고가
|
|
49
|
+
// 났다(증상이 조용함 — 에러 없이 "0 new"만 찍히고 엉뚱한 프로젝트 결과가 나옴). 리포 폴더명
|
|
50
|
+
// 기반으로 기본값을 만들면 충돌 확률이 크게 줄어든다.
|
|
51
|
+
function defaultCollectionNames(docRoot) {
|
|
52
|
+
const repoRoot = docRoot ? path.dirname(path.resolve(docRoot)) : process.cwd();
|
|
53
|
+
const base = sanitizeCollectionName(path.basename(repoRoot));
|
|
54
|
+
return { wiki: `${base}-wiki`, raw: `${base}-wiki-raw` };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 설정 파일 로드 (선택). cwd 또는 docRoot의 llm-wiki.config.json.
|
|
58
|
+
// 반환: { projectName, collections: { wiki, raw }, hooksPath } 병합 결과 (기본값 포함).
|
|
59
|
+
// hooksPath: 설정하면 init이 githooks/ 사본을 만들지 않고 그 경로를 그대로 쓴다
|
|
60
|
+
// (레포가 templates/ 등 정본을 직접 참조하는 경우 — drift 원천 제거).
|
|
61
|
+
function loadConfig(docRoot) {
|
|
62
|
+
const defaults = {
|
|
63
|
+
projectName: null, // null이면 index.md 헤더에 프로젝트명 생략
|
|
64
|
+
collections: defaultCollectionNames(docRoot),
|
|
65
|
+
hooksPath: null,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const candidates = [
|
|
69
|
+
path.join(process.cwd(), 'llm-wiki.config.json'),
|
|
70
|
+
docRoot ? path.join(docRoot, '..', 'llm-wiki.config.json') : null,
|
|
71
|
+
].filter(Boolean);
|
|
72
|
+
|
|
73
|
+
for (const p of candidates) {
|
|
74
|
+
if (fs.existsSync(p)) {
|
|
75
|
+
try {
|
|
76
|
+
const user = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
77
|
+
return {
|
|
78
|
+
projectName: user.projectName !== undefined ? user.projectName : defaults.projectName,
|
|
79
|
+
collections: {
|
|
80
|
+
wiki: (user.collections && user.collections.wiki) || defaults.collections.wiki,
|
|
81
|
+
raw: (user.collections && user.collections.raw) || defaults.collections.raw,
|
|
82
|
+
},
|
|
83
|
+
hooksPath: user.hooksPath !== undefined ? user.hooksPath : defaults.hooksPath,
|
|
84
|
+
};
|
|
85
|
+
} catch (e) {
|
|
86
|
+
// 깨진 config는 무시하고 기본값 사용 (사용자에게 에러 띄우지 않음)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return defaults;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = { findDocRoot, loadConfig, sanitizeCollectionName, defaultCollectionNames };
|
package/lib/find-qmd.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// @tobilu/qmd CLI 진입점 탐색.
|
|
2
|
+
// qmd는 optional dependency이므로 설치 위치가 다양할 수 있다.
|
|
3
|
+
// 1. 이 패키지의 node_modules (optionalDependencies로 설치된 경우)
|
|
4
|
+
// 2. cwd의 node_modules (타겟 리포가 별도로 npm i @tobilu/qmd 한 경우)
|
|
5
|
+
// 3. 글로벌 npm root (npm i -g @tobilu/qmd 한 경우)
|
|
6
|
+
// 4. 환경변수 QMD_CLI_PATH (명시적 오버라이드)
|
|
7
|
+
//
|
|
8
|
+
// 어느 곳에서도 찾지 못하면 null 반환 → 호출측에서 grep fallback으로 강하.
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const { execSync } = require('child_process');
|
|
12
|
+
|
|
13
|
+
const QMD_CLI_REL = 'dist/cli/qmd.js';
|
|
14
|
+
|
|
15
|
+
function findQmd() {
|
|
16
|
+
// 1) 환경변수 명시 오버라이드
|
|
17
|
+
if (process.env.QMD_CLI_PATH && fs.existsSync(process.env.QMD_CLI_PATH)) {
|
|
18
|
+
return process.env.QMD_CLI_PATH;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const candidates = [];
|
|
22
|
+
|
|
23
|
+
// 2) 이 패키지 자신의 의존성 트리에서 resolve.
|
|
24
|
+
// qmd package.json이 exports로 서브패스를 제한하므로 './package.json' 직접 resolve는
|
|
25
|
+
// ERR_PACKAGE_PATH_NOT_EXPORTED 실패. main 진입점(dist/index.js)을 resolve한 뒤
|
|
26
|
+
// 패키지 루트로 역추적한다 — main은 exports에 노출되어 있으므로 안전.
|
|
27
|
+
try {
|
|
28
|
+
const mainPath = require.resolve('@tobilu/qmd');
|
|
29
|
+
// main은 <pkgRoot>/dist/index.js 형태 → <pkgRoot>를 얻으려 dist/의 부모.
|
|
30
|
+
candidates.push(path.join(path.dirname(path.dirname(mainPath)), QMD_CLI_REL));
|
|
31
|
+
} catch (e) {
|
|
32
|
+
// optional이므로 없을 수 있음 — 정상
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 3) cwd의 node_modules
|
|
36
|
+
candidates.push(path.join(process.cwd(), 'node_modules/@tobilu/qmd', QMD_CLI_REL));
|
|
37
|
+
|
|
38
|
+
// 4) 글로벌 npm root
|
|
39
|
+
try {
|
|
40
|
+
const globalRoot = execSync('npm root -g', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
|
|
41
|
+
candidates.push(path.join(globalRoot, '@tobilu/qmd', QMD_CLI_REL));
|
|
42
|
+
} catch (e) {
|
|
43
|
+
// npm을 사용할 수 없는 환경
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (const p of candidates) {
|
|
47
|
+
if (p && fs.existsSync(p)) return p;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = { findQmd };
|