llm-wiki-kit 0.1.6 → 0.1.7
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 +14 -21
- package/docs/concepts.md +6 -3
- package/docs/operations.md +12 -5
- package/docs/research/baseline.md +2 -0
- package/docs/research/future-large-scale.md +27 -0
- package/package.json +1 -1
- package/src/cli.js +4 -0
- package/src/hook.js +9 -3
- package/src/project-state.js +265 -9
- package/src/project.js +6 -3
- package/src/state.js +5 -0
- package/src/templates.js +131 -29
- package/src/update.js +24 -1
- package/src/wiki-lint.js +11 -1
- package/src/wiki-search.js +3 -0
package/README.md
CHANGED
|
@@ -74,28 +74,21 @@ The installed hooks:
|
|
|
74
74
|
- capture decision points, debugging findings, changed files, and verification notes
|
|
75
75
|
- allow tool calls to proceed without secret/PII-based hook blocking
|
|
76
76
|
- update `llm-wiki/outputs/questions/YYYY-MM-DD-live-qa.md`
|
|
77
|
-
-
|
|
77
|
+
- capture reusable-answer candidates into `llm-wiki/wiki/queries/`
|
|
78
78
|
- promote decision-like turns into `llm-wiki/wiki/decisions/`
|
|
79
|
+
- nudge the active LLM to fold reusable facts into existing wiki pages instead of leaving everything as one-off Q&A
|
|
80
|
+
- automatically refresh managed rules/templates for older projects when the current runtime starts a session
|
|
79
81
|
|
|
80
82
|
If you need to think about saving every answer manually, the setup has failed.
|
|
81
83
|
|
|
82
|
-
##
|
|
84
|
+
## Operational Commands
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
llm-wiki
|
|
87
|
-
llm-wiki version
|
|
88
|
-
llm-wiki
|
|
89
|
-
llm-wiki
|
|
90
|
-
llm-wiki update --check --workspace /path/to/project
|
|
91
|
-
llm-wiki update --all --workspace /path/to/search-root
|
|
92
|
-
llm-wiki update --dry-run --workspace /path/to/project
|
|
93
|
-
llm-wiki update --workspace /path/to/project
|
|
94
|
-
llm-wiki context "search phrase" --workspace /path/to/project
|
|
95
|
-
llm-wiki lint --workspace /path/to/project
|
|
96
|
-
llm-wiki consolidate --workspace /path/to/project
|
|
97
|
-
llm-wiki uninstall
|
|
98
|
-
```
|
|
86
|
+
Most users should not need these during daily Claude Code/Codex work. They exist for install, update, diagnostics, and agent-side maintenance.
|
|
87
|
+
|
|
88
|
+
- Install/update: `llm-wiki install`, `llm-wiki update`, `llm-wiki projects`
|
|
89
|
+
- Diagnostics: `llm-wiki doctor`, `llm-wiki status`, `llm-wiki version`
|
|
90
|
+
- Agent maintenance helpers: `llm-wiki context`, `llm-wiki lint`, `llm-wiki consolidate`
|
|
91
|
+
- Cleanup: `llm-wiki uninstall`
|
|
99
92
|
|
|
100
93
|
`llm-wiki status` is an offline consistency check. It reports the installed runtime version, hook targets, and whether the current workspace has the current managed templates applied.
|
|
101
94
|
|
|
@@ -103,15 +96,15 @@ llm-wiki uninstall
|
|
|
103
96
|
|
|
104
97
|
`llm-wiki update` upgrades the global npm package, reinstalls the hook entries, and patches only managed project files such as the marked `AGENTS.md` policy block and generated `llm-wiki/AGENTS.md`/procedure files. Existing wiki content is not overwritten.
|
|
105
98
|
|
|
106
|
-
`llm-wiki context "<query>"` prints the same layered context used by hooks
|
|
99
|
+
`llm-wiki context "<query>"` prints the same layered context used by hooks. It is mainly for debugging what Codex/Claude Code will see; daily use should rely on hook injection.
|
|
107
100
|
|
|
108
|
-
`llm-wiki lint` checks wiki health
|
|
101
|
+
`llm-wiki lint` checks wiki health and detects outdated managed rules from older kit versions. Agents may use it before/after meaningful wiki maintenance.
|
|
109
102
|
|
|
110
|
-
`llm-wiki consolidate` refreshes only generated marker blocks in `wiki/memory.md` and `wiki/index.md`.
|
|
103
|
+
`llm-wiki consolidate` refreshes only generated marker blocks in `wiki/memory.md` and `wiki/index.md`. It is an agent maintenance helper, not a command users should run after every turn.
|
|
111
104
|
|
|
112
105
|
`llm-wiki projects --workspace /apps` lists project roots that already have `llm-wiki-kit` state and shows the update commands to run. `llm-wiki update --all --workspace /apps` updates the global runtime once, then reapplies managed templates across every discovered project root under `/apps`.
|
|
113
106
|
|
|
114
|
-
After a plain `npm install -g llm-wiki-kit@latest`,
|
|
107
|
+
After a plain `npm install -g llm-wiki-kit@latest`, existing hooks keep working when they already point at the global npm package path. The next `SessionStart`/`InstructionsLoaded` hook automatically reapplies safe managed template updates for the active project root. Clearly generated older `llm-wiki/AGENTS.md` and procedure files are refreshed even when old state is missing; user-edited files are not overwritten and are surfaced to the active agent as cleanup work. If hooks point at a source checkout or stale shim, run `llm-wiki post-update --workspace <project>` or `llm-wiki install --workspace <project>` once to reconnect them.
|
|
115
108
|
|
|
116
109
|
On PCs that use nvm or user-local npm, prefer the non-sudo global install and make sure the `llm-wiki` command resolves to that npm package:
|
|
117
110
|
|
package/docs/concepts.md
CHANGED
|
@@ -16,10 +16,11 @@ The important behavior is a loop:
|
|
|
16
16
|
|
|
17
17
|
1. A Claude Code or Codex session starts.
|
|
18
18
|
2. `memory.md`, `index.md`, and relevant wiki context are injected automatically.
|
|
19
|
-
3. The user works normally.
|
|
19
|
+
3. The user works normally; no extra command loop is required.
|
|
20
20
|
4. Hooks gather redacted prompt/tool/result summaries.
|
|
21
21
|
5. At stop/session end, useful knowledge is written back to Markdown.
|
|
22
22
|
6. Future sessions start from the improved wiki instead of relying on long chat history.
|
|
23
|
+
7. When reusable knowledge appears, the active Claude Code/Codex agent folds it into existing durable wiki pages instead of leaving everything as one-off Q&A.
|
|
23
24
|
|
|
24
25
|
The kit is a template/runtime repository. It must not centralize project wiki contents.
|
|
25
26
|
|
|
@@ -27,6 +28,8 @@ Runtime updates and project knowledge are separate:
|
|
|
27
28
|
|
|
28
29
|
- npm updates replace the runtime package and hook targets.
|
|
29
30
|
- project patching updates only managed policy blocks and generated procedure files.
|
|
31
|
+
- old generated `llm-wiki/AGENTS.md` and procedure files are refreshed automatically when they are clearly kit-generated, even if old project state is missing.
|
|
32
|
+
- user-edited files are preserved and surfaced to the agent as cleanup work.
|
|
30
33
|
- `memory.md` is created when missing, then treated as project-owned content.
|
|
31
34
|
- curated wiki pages remain project-owned content and are not overwritten by runtime updates.
|
|
32
35
|
|
|
@@ -35,5 +38,5 @@ The maintenance loop is intentionally layered:
|
|
|
35
38
|
- `memory.md`: short hot index for current durable facts.
|
|
36
39
|
- `index.md`: broad navigation map.
|
|
37
40
|
- MiniSearch + wikilinks: retrieval over the rest of `wiki/**/*.md`, with substring fallback when MiniSearch is not installed in a source checkout.
|
|
38
|
-
- `lint`: finds broken links, stale pages, duplicates, and
|
|
39
|
-
- `consolidate`: refreshes generated blocks in `memory.md` and `index.md` while preserving handwritten notes and keeping default query/context/session pages out of the durable generated maps.
|
|
41
|
+
- `lint`: finds broken links, stale pages, duplicates, metadata gaps, secret-like content, and outdated managed rules.
|
|
42
|
+
- `consolidate`: agent helper that refreshes generated blocks in `memory.md` and `index.md` while preserving handwritten notes and keeping default query/context/session pages out of the durable generated maps.
|
package/docs/operations.md
CHANGED
|
@@ -66,7 +66,9 @@ llm-wiki install --workspace /apps --profile standard
|
|
|
66
66
|
|
|
67
67
|
Do not delete a project `llm-wiki/` tree to reinstall the runtime. `llm-wiki uninstall` removes hook entries only; project knowledge is intentionally left intact.
|
|
68
68
|
|
|
69
|
-
##
|
|
69
|
+
## Maintainer And Agent Reference
|
|
70
|
+
|
|
71
|
+
Daily work should happen through Claude Code/Codex. These commands are for maintainer checks, troubleshooting, and agent-side maintenance.
|
|
70
72
|
|
|
71
73
|
```bash
|
|
72
74
|
llm-wiki version
|
|
@@ -102,11 +104,13 @@ llm-wiki consolidate --workspace /path/to/project
|
|
|
102
104
|
|
|
103
105
|
`update --all --workspace <search-root>` performs the npm runtime update once, then reapplies managed templates to every discovered project root under the search root.
|
|
104
106
|
|
|
105
|
-
After a plain `npm install -g llm-wiki-kit@latest`, existing hooks keep
|
|
107
|
+
After a plain `npm install -g llm-wiki-kit@latest`, existing hooks keep working when they already point at the global npm package path. On the next `SessionStart` or `InstructionsLoaded`, the runtime automatically reapplies safe managed template updates for the active project root. If hooks point at a source checkout or stale shim, run `llm-wiki post-update --workspace <project>` or `llm-wiki install --workspace <project>` once to reconnect them.
|
|
106
108
|
|
|
107
109
|
## Context And Wiki Maintenance
|
|
108
110
|
|
|
109
|
-
`llm-wiki context
|
|
111
|
+
Daily use should be Claude Code/Codex first. The user should not need to run a chain of `llm-wiki` commands while working. Hooks inject the context automatically, and the active agent is expected to update durable wiki pages when reusable project knowledge appears.
|
|
112
|
+
|
|
113
|
+
`llm-wiki context "<query>"` is the manual debug form of hook context injection. It reads:
|
|
110
114
|
|
|
111
115
|
- `llm-wiki/wiki/memory.md` as the short hot index
|
|
112
116
|
- `llm-wiki/wiki/index.md` as the navigation map
|
|
@@ -114,7 +118,7 @@ After a plain `npm install -g llm-wiki-kit@latest`, existing hooks keep pointing
|
|
|
114
118
|
- one-hop wikilink neighbors for the strongest matches
|
|
115
119
|
- redacted output fields for query text, memory/index/log excerpts, hit paths, titles, snippets, matched terms, and link expansion metadata
|
|
116
120
|
|
|
117
|
-
Use it when you want to inspect what the next agent turn should see:
|
|
121
|
+
Use it only when you want to inspect what the next agent turn should see:
|
|
118
122
|
|
|
119
123
|
```bash
|
|
120
124
|
llm-wiki context "auth architecture" --workspace /path/to/project
|
|
@@ -133,6 +137,7 @@ llm-wiki context "auth architecture" --workspace /path/to/project --limit 8 --no
|
|
|
133
137
|
- secret-like content patterns such as tokens, password assignments, bearer credentials, and private keys
|
|
134
138
|
- duplicate aliases or titles
|
|
135
139
|
- stale pages and orphan candidates
|
|
140
|
+
- outdated managed rules/templates from earlier `llm-wiki-kit` versions
|
|
136
141
|
|
|
137
142
|
Broken links, invalid source IDs, and secret-like content are errors and return exit code 1. Metadata and discoverability gaps are warnings.
|
|
138
143
|
|
|
@@ -146,7 +151,9 @@ Broken links, invalid source IDs, and secret-like content are errors and return
|
|
|
146
151
|
- appends a log entry when files change
|
|
147
152
|
- supports `--dry-run`
|
|
148
153
|
|
|
149
|
-
|
|
154
|
+
Agents may run `consolidate` after meaningful wiki growth. Users should not need to run it after every turn.
|
|
155
|
+
|
|
156
|
+
When a new runtime sees an older project, `SessionStart`/`InstructionsLoaded` automatically reapplies safe managed template updates. Files that are clearly generated by older kit versions are refreshed. Files that look user-edited are preserved and surfaced to the active agent as cleanup context instead of being overwritten.
|
|
150
157
|
|
|
151
158
|
## Updating User-Local Or nvm Installs
|
|
152
159
|
|
|
@@ -18,3 +18,5 @@ Important conclusions:
|
|
|
18
18
|
- Obsidian, Graphify, MCP, and RAG can be useful extensions, but v1 must work with plain Markdown and hooks.
|
|
19
19
|
- Retrieval should combine lexical search with explicit Markdown links before adding heavier external infrastructure.
|
|
20
20
|
- Consolidation should be conservative: update generated blocks and navigation surfaces, but preserve human/agent-written page bodies.
|
|
21
|
+
- Separate LLM API integration is not required for the current design. Claude Code and Codex already provide the reasoning/writing layer; the kit should supply context, durable files, guardrails, and safe automatic project-rule updates.
|
|
22
|
+
- Heavy layers such as embeddings, graph databases, MCP tools, and hierarchical summary systems are future considerations only when project wikis become too large for the plain Markdown + hook workflow.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Future Large-Scale Considerations
|
|
2
|
+
|
|
3
|
+
These items are not part of the current implementation plan.
|
|
4
|
+
|
|
5
|
+
The current product direction is plain Markdown plus Codex/Claude Code hooks:
|
|
6
|
+
|
|
7
|
+
- inject the smallest useful wiki context automatically
|
|
8
|
+
- let the active LLM update durable Markdown during normal work
|
|
9
|
+
- keep generated/template updates safe for older projects
|
|
10
|
+
- preserve raw/wiki separation and redaction rules
|
|
11
|
+
|
|
12
|
+
Consider heavier infrastructure only when real project wikis become large enough that this model fails in practice.
|
|
13
|
+
|
|
14
|
+
Deferred options:
|
|
15
|
+
|
|
16
|
+
- embeddings for semantic retrieval
|
|
17
|
+
- graph database storage
|
|
18
|
+
- MCP tools for direct wiki operations
|
|
19
|
+
- GraphRAG/RAPTOR-style hierarchical summaries
|
|
20
|
+
- external LLM API calls for automatic summarization
|
|
21
|
+
|
|
22
|
+
Reasons to defer:
|
|
23
|
+
|
|
24
|
+
- They add setup, cost, data-retention, and failure modes.
|
|
25
|
+
- Codex and Claude Code already provide the LLM reasoning layer.
|
|
26
|
+
- The immediate failure mode is wiki hygiene and old-template drift, not lack of model capacity.
|
|
27
|
+
- Plain Markdown remains inspectable, portable, and easy to repair.
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -223,6 +223,8 @@ async function listProjects(options) {
|
|
|
223
223
|
|
|
224
224
|
function formatStatus(value) {
|
|
225
225
|
const project = value.project || {};
|
|
226
|
+
const autoUpdateCount = (project.managedFiles || []).filter((file) => !file.current && file.patchable).length;
|
|
227
|
+
const attentionCount = (project.managedFiles || []).filter((file) => file.needsAttention).length;
|
|
226
228
|
return [
|
|
227
229
|
'llm-wiki-kit status',
|
|
228
230
|
`- version: ${value.runtimeVersion}`,
|
|
@@ -235,6 +237,8 @@ function formatStatus(value) {
|
|
|
235
237
|
`- claude hook: ${value.claudeInstalled ? 'current' : 'missing/outdated'}`,
|
|
236
238
|
`- project applied runtime: ${project.lastRuntimeVersionApplied || 'unknown'}`,
|
|
237
239
|
`- project templates current: ${project.managedFilesCurrent ? 'yes' : 'no'}`,
|
|
240
|
+
`- project auto-updateable rules: ${autoUpdateCount}`,
|
|
241
|
+
`- project rules needing agent cleanup: ${attentionCount}`,
|
|
238
242
|
].join('\n');
|
|
239
243
|
}
|
|
240
244
|
|
package/src/hook.js
CHANGED
|
@@ -3,7 +3,8 @@ import { bootstrapProject, appendContextNote, appendLiveQa, appendSessionEnvelop
|
|
|
3
3
|
import { applyProjectTemplateUpdate, inspectProjectState } from './project-state.js';
|
|
4
4
|
import { recordProject } from './projects.js';
|
|
5
5
|
import { summarizeForStorage } from './redaction.js';
|
|
6
|
-
import { buildEntryFromState, rememberQuestion, rememberTool } from './state.js';
|
|
6
|
+
import { buildEntryFromState, clearTurnState, rememberQuestion, rememberTool } from './state.js';
|
|
7
|
+
import { relative } from 'path';
|
|
7
8
|
|
|
8
9
|
async function readStdinJson() {
|
|
9
10
|
const chunks = [];
|
|
@@ -45,7 +46,9 @@ async function autoUpdateManagedProject(projectRoot, eventName) {
|
|
|
45
46
|
const state = await inspectProjectState(projectRoot);
|
|
46
47
|
if (state.runtimeUpToDateWithProject && state.managedFilesCurrent) return;
|
|
47
48
|
const result = await applyProjectTemplateUpdate(projectRoot);
|
|
48
|
-
|
|
49
|
+
if (result.changed.length > 0) {
|
|
50
|
+
await appendWikiLog(projectRoot, `llm-wiki-kit auto-update applied runtime ${result.runtimeVersion}; changed templates: ${result.changed.length}; skipped templates: ${result.skipped.length}`);
|
|
51
|
+
}
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
export async function handleHook(provider, explicitEvent) {
|
|
@@ -96,7 +99,10 @@ export async function handleHook(provider, explicitEvent) {
|
|
|
96
99
|
await appendLiveQa(projectRoot, entry);
|
|
97
100
|
const queryPath = await writeQueryPage(projectRoot, entry);
|
|
98
101
|
const decisionPath = await writeDecisionPage(projectRoot, entry);
|
|
99
|
-
await appendWikiLog(projectRoot, `captured ${eventName}${queryPath ? `; query=${queryPath}` : ''}${decisionPath ? `; decision=${decisionPath}` : ''}`);
|
|
102
|
+
await appendWikiLog(projectRoot, `captured ${eventName}${queryPath ? `; query=${relative(projectRoot, queryPath)}` : ''}${decisionPath ? `; decision=${relative(projectRoot, decisionPath)}` : ''}`);
|
|
103
|
+
}
|
|
104
|
+
if (eventName === 'Stop' || eventName === 'SessionEnd') {
|
|
105
|
+
await clearTurnState(projectRoot, payload).catch(() => {});
|
|
100
106
|
}
|
|
101
107
|
return {};
|
|
102
108
|
}
|
package/src/project-state.js
CHANGED
|
@@ -27,6 +27,14 @@ function templateDescriptors() {
|
|
|
27
27
|
path: join('llm-wiki', 'AGENTS.md'),
|
|
28
28
|
content: llmWikiAgents(),
|
|
29
29
|
mode: 'generated-file',
|
|
30
|
+
legacyContents: legacyLlmWikiAgentsContents(),
|
|
31
|
+
legacySignals: [
|
|
32
|
+
/^# LLM Wiki Agent Rules/m,
|
|
33
|
+
/Generated by llm-wiki-kit/m,
|
|
34
|
+
/## Purpose/m,
|
|
35
|
+
/## Core Rules/m,
|
|
36
|
+
/## Operations/m,
|
|
37
|
+
],
|
|
30
38
|
},
|
|
31
39
|
{
|
|
32
40
|
id: 'wiki-memory',
|
|
@@ -39,10 +47,179 @@ function templateDescriptors() {
|
|
|
39
47
|
path: join('llm-wiki', 'procedures', name),
|
|
40
48
|
content: procedure(name),
|
|
41
49
|
mode: 'generated-file',
|
|
50
|
+
legacyContents: legacyProcedureContents(name),
|
|
51
|
+
legacySignals: legacyProcedureSignals(name),
|
|
42
52
|
})),
|
|
43
53
|
];
|
|
44
54
|
}
|
|
45
55
|
|
|
56
|
+
function legacyLlmWikiAgentsContents() {
|
|
57
|
+
return [
|
|
58
|
+
`# LLM Wiki Agent Rules
|
|
59
|
+
|
|
60
|
+
Generated by llm-wiki-kit <version>.
|
|
61
|
+
|
|
62
|
+
## Purpose
|
|
63
|
+
Maintain a living Markdown LLM Wiki from immutable source files and redacted Codex/Claude Code session events.
|
|
64
|
+
These rules supersede older OMX/OMC/\`omx_wiki/\` LLM Wiki rules for this project.
|
|
65
|
+
|
|
66
|
+
## Directories
|
|
67
|
+
- \`raw/\`: immutable or redacted source material. Never edit original source captures.
|
|
68
|
+
- \`wiki/\`: AI-maintained knowledge pages.
|
|
69
|
+
- \`outputs/\`: live Q&A summaries, reports, and generated briefs.
|
|
70
|
+
- \`procedures/\`: detailed operating rules for ingest, query, lint, and security.
|
|
71
|
+
|
|
72
|
+
## Core Rules
|
|
73
|
+
- Never modify \`raw/\` source material except to append redacted session envelopes generated by hooks.
|
|
74
|
+
- Do not state unsupported claims as facts.
|
|
75
|
+
- Mark inference explicitly.
|
|
76
|
+
- Add \`source_ids\` or file references for important claims.
|
|
77
|
+
- Update \`wiki/index.md\` and \`wiki/log.md\` whenever new durable knowledge is created.
|
|
78
|
+
- Preserve contradictions in \`Contradictions\` or \`Open Questions\` instead of overwriting them.
|
|
79
|
+
- Preserve useful work context and redact authentication values before writing durable notes.
|
|
80
|
+
|
|
81
|
+
## Page Format
|
|
82
|
+
Use YAML frontmatter when creating wiki pages:
|
|
83
|
+
|
|
84
|
+
\`\`\`yaml
|
|
85
|
+
---
|
|
86
|
+
title: ""
|
|
87
|
+
type: "source | concept | entity | decision | architecture | debugging | context | query | session-log | convention"
|
|
88
|
+
source_ids: []
|
|
89
|
+
status: "draft | reviewed | stale"
|
|
90
|
+
last_updated: "YYYY-MM-DD"
|
|
91
|
+
confidence: "high | medium | low"
|
|
92
|
+
---
|
|
93
|
+
\`\`\`
|
|
94
|
+
|
|
95
|
+
## Operations
|
|
96
|
+
- ingest: read new raw files, create or update wiki pages, then update \`wiki/index.md\` and \`wiki/log.md\`.
|
|
97
|
+
- query: start from \`wiki/index.md\`, read relevant wiki pages, answer with source references, and save reusable answers.
|
|
98
|
+
- lint: find stale pages, orphan pages, broken wiki links, missing sources, duplicate concepts, contradictions, and missing links.`,
|
|
99
|
+
`# LLM Wiki Agent Rules
|
|
100
|
+
|
|
101
|
+
Generated by llm-wiki-kit <version>.
|
|
102
|
+
|
|
103
|
+
## Purpose
|
|
104
|
+
Maintain a living Markdown LLM Wiki from immutable source files and redacted Codex/Claude Code session events.
|
|
105
|
+
These rules supersede older OMX/OMC/\`omx_wiki/\` LLM Wiki rules for this project.
|
|
106
|
+
|
|
107
|
+
## Directories
|
|
108
|
+
- \`raw/\`: immutable or redacted source material. Never edit original source captures.
|
|
109
|
+
- \`wiki/\`: AI-maintained knowledge pages. \`wiki/memory.md\` is the short hot index injected into hook context.
|
|
110
|
+
- \`outputs/\`: live Q&A summaries, reports, and generated briefs.
|
|
111
|
+
- \`procedures/\`: detailed operating rules for ingest, query, lint, and security.
|
|
112
|
+
|
|
113
|
+
## Core Rules
|
|
114
|
+
- Never modify \`raw/\` source material except to append redacted session envelopes generated by hooks.
|
|
115
|
+
- Do not state unsupported claims as facts.
|
|
116
|
+
- Mark inference explicitly.
|
|
117
|
+
- Add \`source_ids\` or file references for important claims.
|
|
118
|
+
- Update \`wiki/memory.md\`, \`wiki/index.md\`, and \`wiki/log.md\` whenever new durable knowledge changes the active project map.
|
|
119
|
+
- Preserve contradictions in \`Contradictions\` or \`Open Questions\` instead of overwriting them.
|
|
120
|
+
- Preserve useful work context and redact authentication values before writing durable notes.
|
|
121
|
+
|
|
122
|
+
## Page Format
|
|
123
|
+
Use YAML frontmatter when creating wiki pages:
|
|
124
|
+
|
|
125
|
+
\`\`\`yaml
|
|
126
|
+
---
|
|
127
|
+
title: ""
|
|
128
|
+
type: "source | concept | entity | decision | architecture | debugging | context | query | session-log | convention"
|
|
129
|
+
source_ids: []
|
|
130
|
+
status: "draft | reviewed | stale"
|
|
131
|
+
last_updated: "YYYY-MM-DD"
|
|
132
|
+
confidence: "high | medium | low"
|
|
133
|
+
memory_type: "semantic | episodic | procedural"
|
|
134
|
+
importance: 1
|
|
135
|
+
last_verified: "YYYY-MM-DD | unknown"
|
|
136
|
+
supersedes: []
|
|
137
|
+
superseded_by: []
|
|
138
|
+
---
|
|
139
|
+
\`\`\`
|
|
140
|
+
|
|
141
|
+
## Operations
|
|
142
|
+
- ingest: read \`wiki/memory.md\` and \`wiki/index.md\`, ingest new raw files, create or update wiki pages, then update the active map and \`wiki/log.md\`.
|
|
143
|
+
- query: start from \`llm-wiki context "<query>"\` or \`wiki/memory.md\`, read relevant wiki pages, answer with source references, and save reusable answers.
|
|
144
|
+
- lint: run \`llm-wiki lint --workspace <project>\` to find stale pages, orphan pages, broken wiki/Markdown links, unsafe source IDs, secret-like content, missing sources, duplicate concepts, contradictions, and missing links.
|
|
145
|
+
- consolidate: run \`llm-wiki consolidate --workspace <project>\` after meaningful wiki growth to refresh generated memory/index blocks without overwriting hand-written notes. Default query/context/session-log pages are excluded unless explicitly durable.`,
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function legacyProcedureContents(name) {
|
|
150
|
+
const early = {
|
|
151
|
+
'ingest.md': `# Ingest Procedure
|
|
152
|
+
|
|
153
|
+
1. Read \`wiki/index.md\` first.
|
|
154
|
+
2. Inspect new material under \`raw/inbox/\` or \`raw/sources/\`.
|
|
155
|
+
3. Create or update \`wiki/sources/<slug>.md\` for each source.
|
|
156
|
+
4. Update related concept, entity, decision, architecture, debugging, or context pages.
|
|
157
|
+
5. Prefer updating existing pages over creating duplicates.
|
|
158
|
+
6. Add source references and confidence.
|
|
159
|
+
7. Update \`wiki/index.md\` and append to \`wiki/log.md\`.`,
|
|
160
|
+
'query.md': `# Query Procedure
|
|
161
|
+
|
|
162
|
+
1. Start from \`wiki/index.md\`.
|
|
163
|
+
2. Search \`wiki/\` for relevant pages.
|
|
164
|
+
3. Read the smallest useful set of pages first.
|
|
165
|
+
4. Use raw sources only when exact evidence matters.
|
|
166
|
+
5. Separate verified facts from inference.
|
|
167
|
+
6. Save reusable answers into \`wiki/queries/\` and link them from related pages.`,
|
|
168
|
+
'lint.md': `# Lint Procedure
|
|
169
|
+
|
|
170
|
+
Check for stale pages, orphan pages, broken wiki links, missing sources, duplicate concepts, unsupported claims, and unresolved contradictions. Prefer producing a review report before automatic edits.`,
|
|
171
|
+
'security.md': `# Security Procedure
|
|
172
|
+
|
|
173
|
+
- Never store credentials, tokens, private keys, \`.env\` contents, customer identifiers, or private personal data.
|
|
174
|
+
- Redact before writing hook payloads or summaries.
|
|
175
|
+
- Full raw transcript capture is disabled by default and must be explicitly enabled by project policy.
|
|
176
|
+
- If a file or prompt looks secret-bearing, do not persist it and ask for confirmation before reading further.`,
|
|
177
|
+
};
|
|
178
|
+
const layered = {
|
|
179
|
+
'ingest.md': `# Ingest Procedure
|
|
180
|
+
|
|
181
|
+
1. Read \`wiki/memory.md\` and \`wiki/index.md\` first.
|
|
182
|
+
2. Inspect new material under \`raw/inbox/\` or \`raw/sources/\`.
|
|
183
|
+
3. Create or update \`wiki/sources/<slug>.md\` for each source.
|
|
184
|
+
4. Update related concept, entity, decision, architecture, debugging, or context pages.
|
|
185
|
+
5. Prefer updating existing pages over creating duplicates.
|
|
186
|
+
6. Add source references, confidence, memory type, importance, and verification status.
|
|
187
|
+
7. Update \`wiki/memory.md\` or run \`llm-wiki consolidate\` when durable entry points change.
|
|
188
|
+
8. Update \`wiki/index.md\` and append to \`wiki/log.md\`.`,
|
|
189
|
+
'query.md': `# Query Procedure
|
|
190
|
+
|
|
191
|
+
1. Start from \`llm-wiki context "<query>"\` or read \`wiki/memory.md\` and \`wiki/index.md\`.
|
|
192
|
+
2. Use \`--limit\` or \`--no-expand\` when you need a narrower context pack.
|
|
193
|
+
3. Search \`wiki/\` for relevant pages.
|
|
194
|
+
4. Read the smallest useful set of pages first.
|
|
195
|
+
5. Use raw sources only when exact evidence matters.
|
|
196
|
+
6. Separate verified facts from inference.
|
|
197
|
+
7. Save reusable answers into \`wiki/queries/\` and link them from related pages.`,
|
|
198
|
+
'lint.md': `# Lint Procedure
|
|
199
|
+
|
|
200
|
+
Run \`llm-wiki lint --workspace <project>\` to check for stale pages, orphan pages, broken wiki/Markdown links, unsafe source IDs, secret-like content, missing sources, duplicate concepts, unsupported claims, and unresolved contradictions. Prefer producing a review report before automatic edits.`,
|
|
201
|
+
'security.md': `# Security Procedure
|
|
202
|
+
|
|
203
|
+
- Preserve useful work context for the local project wiki.
|
|
204
|
+
- Do not block reads or tool calls only because they look sensitive.
|
|
205
|
+
- Redact authentication values such as tokens, passwords, bearer credentials, and private keys before writing hook payloads, summaries, or context packs.
|
|
206
|
+
- Run \`llm-wiki lint --workspace <project>\` when sensitive material might have entered wiki pages; secret-like content is reported as an error.
|
|
207
|
+
- Full raw transcript capture is disabled by default and must be explicitly enabled by project policy.`,
|
|
208
|
+
};
|
|
209
|
+
return [early[name], layered[name]].filter(Boolean);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function legacyProcedureSignals(name) {
|
|
213
|
+
const common = [/^# .+ Procedure/m];
|
|
214
|
+
const byName = {
|
|
215
|
+
'ingest.md': [/^# Ingest Procedure/m, /wiki\/memory\.md/, /raw\/inbox|raw\/sources/],
|
|
216
|
+
'query.md': [/^# Query Procedure/m, /llm-wiki context/, /Save reusable answers|reusable answers/],
|
|
217
|
+
'lint.md': [/^# Lint Procedure/m, /llm-wiki lint/, /stale pages|orphan pages/],
|
|
218
|
+
'security.md': [/^# Security Procedure/m, /Redact authentication values|token|private keys/, /raw transcript capture/],
|
|
219
|
+
};
|
|
220
|
+
return byName[name] || common;
|
|
221
|
+
}
|
|
222
|
+
|
|
46
223
|
function emptyState() {
|
|
47
224
|
return {
|
|
48
225
|
schemaVersion: PROJECT_STATE_SCHEMA_VERSION,
|
|
@@ -79,18 +256,24 @@ export async function writeProjectState(projectRoot, state) {
|
|
|
79
256
|
}
|
|
80
257
|
|
|
81
258
|
function replaceMarkedBlock(current, replacement) {
|
|
259
|
+
if (markerStatus(current) !== 'complete') return null;
|
|
82
260
|
const start = current.indexOf(ROOT_POLICY_START);
|
|
83
261
|
const end = current.indexOf(ROOT_POLICY_END);
|
|
84
|
-
if (start === -1 || end === -1 || end < start) return null;
|
|
85
262
|
const afterEnd = end + ROOT_POLICY_END.length;
|
|
86
263
|
return `${current.slice(0, start)}${replacement.trimStart().trimEnd()}${current.slice(afterEnd)}`;
|
|
87
264
|
}
|
|
88
265
|
|
|
266
|
+
function markerCount(current, marker) {
|
|
267
|
+
return current.split(marker).length - 1;
|
|
268
|
+
}
|
|
269
|
+
|
|
89
270
|
function markerStatus(current) {
|
|
90
271
|
const start = current.indexOf(ROOT_POLICY_START);
|
|
91
272
|
const end = current.indexOf(ROOT_POLICY_END);
|
|
92
|
-
|
|
93
|
-
|
|
273
|
+
const starts = markerCount(current, ROOT_POLICY_START);
|
|
274
|
+
const ends = markerCount(current, ROOT_POLICY_END);
|
|
275
|
+
if (starts === 0 && ends === 0) return 'missing';
|
|
276
|
+
if (starts !== 1 || ends !== 1 || end < start) return 'malformed';
|
|
94
277
|
return 'complete';
|
|
95
278
|
}
|
|
96
279
|
|
|
@@ -108,10 +291,39 @@ function isKnownGeneratedContent(text, descriptor) {
|
|
|
108
291
|
return templateHash(text) === templateHash(descriptor.content);
|
|
109
292
|
}
|
|
110
293
|
|
|
294
|
+
function normalizeGeneratedText(text) {
|
|
295
|
+
return String(text || '')
|
|
296
|
+
.replace(/\r\n/g, '\n')
|
|
297
|
+
.replace(/Generated by llm-wiki-kit [^\n]+/g, 'Generated by llm-wiki-kit <version>.')
|
|
298
|
+
.trim();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function isLegacyGeneratedContent(text, descriptor) {
|
|
302
|
+
if (descriptor.mode !== 'generated-file') return false;
|
|
303
|
+
if (!Array.isArray(descriptor.legacyContents) || descriptor.legacyContents.length === 0) return false;
|
|
304
|
+
const normalized = normalizeGeneratedText(text);
|
|
305
|
+
return descriptor.legacyContents.some((content) => normalizeGeneratedText(content) === normalized);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function hasLegacyGeneratedSignal(text, descriptor) {
|
|
309
|
+
if (!Array.isArray(descriptor.legacySignals) || descriptor.legacySignals.length === 0) return false;
|
|
310
|
+
return descriptor.legacySignals.every((pattern) => pattern.test(text));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function hasKitManagedSignal(text, descriptor) {
|
|
314
|
+
if (descriptor.mode === 'marker') return markerStatus(text) !== 'missing';
|
|
315
|
+
if (descriptor.mode === 'generated-file') {
|
|
316
|
+
return isLegacyGeneratedContent(text, descriptor) || hasLegacyGeneratedSignal(text, descriptor) || /Generated by llm-wiki-kit/.test(text);
|
|
317
|
+
}
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
|
|
111
321
|
function canPatchDescriptor(state, descriptor, currentText, fileExists = true) {
|
|
112
322
|
if (descriptor.mode === 'create-only') return !fileExists;
|
|
113
323
|
if (descriptor.mode === 'marker') return replaceMarkedBlock(currentText, descriptor.content) !== null;
|
|
114
|
-
return isRecordedManaged(state, descriptor, currentText) ||
|
|
324
|
+
return isRecordedManaged(state, descriptor, currentText) ||
|
|
325
|
+
isKnownGeneratedContent(currentText, descriptor) ||
|
|
326
|
+
isLegacyGeneratedContent(currentText, descriptor);
|
|
115
327
|
}
|
|
116
328
|
|
|
117
329
|
function desiredTextForDescriptor(descriptor, currentText, fileExists = true) {
|
|
@@ -125,6 +337,10 @@ async function createOnlyParentExists(projectRoot, descriptor) {
|
|
|
125
337
|
return exists(dirname(join(projectRoot, descriptor.path)));
|
|
126
338
|
}
|
|
127
339
|
|
|
340
|
+
async function descriptorParentExists(projectRoot, descriptor) {
|
|
341
|
+
return exists(dirname(join(projectRoot, descriptor.path)));
|
|
342
|
+
}
|
|
343
|
+
|
|
128
344
|
export async function inspectProjectState(projectRoot) {
|
|
129
345
|
const state = await readProjectState(projectRoot);
|
|
130
346
|
const runtime = runtimeVersion();
|
|
@@ -139,10 +355,16 @@ export async function inspectProjectState(projectRoot) {
|
|
|
139
355
|
const currentHash = fileExists ? templateHash(currentText) : null;
|
|
140
356
|
const recorded = state.managedTemplates[descriptor.id] || null;
|
|
141
357
|
const markerState = descriptor.mode === 'marker' && fileExists ? markerStatus(currentText) : null;
|
|
142
|
-
const parentExists = await
|
|
358
|
+
const parentExists = await descriptorParentExists(projectRoot, descriptor);
|
|
143
359
|
const patchable = descriptor.mode === 'create-only' && !fileExists && !parentExists
|
|
144
360
|
? false
|
|
145
|
-
: (
|
|
361
|
+
: (!fileExists && descriptor.mode === 'generated-file'
|
|
362
|
+
? parentExists
|
|
363
|
+
: (markerState === 'malformed' ? false : canPatchDescriptor(state, descriptor, currentText, fileExists)));
|
|
364
|
+
const needsAttention = (fileExists || recorded) &&
|
|
365
|
+
!patchable &&
|
|
366
|
+
currentText !== (desiredText || '') &&
|
|
367
|
+
(fileExists ? hasKitManagedSignal(currentText, descriptor) : descriptor.mode !== 'create-only');
|
|
146
368
|
const current = descriptor.mode === 'create-only'
|
|
147
369
|
? fileExists
|
|
148
370
|
: fileExists && desiredText !== null && currentHash === templateHash(desiredText);
|
|
@@ -153,11 +375,12 @@ export async function inspectProjectState(projectRoot) {
|
|
|
153
375
|
exists: fileExists,
|
|
154
376
|
patchable,
|
|
155
377
|
current,
|
|
378
|
+
needsAttention,
|
|
156
379
|
currentHash,
|
|
157
380
|
desiredHash,
|
|
158
381
|
recordedHash: recorded?.hash || null,
|
|
159
382
|
recordedVersion: recorded?.version || null,
|
|
160
|
-
reason: markerState === 'malformed' ? 'malformed-marker' : null,
|
|
383
|
+
reason: markerState === 'malformed' ? 'malformed-marker' : (!fileExists ? 'missing' : null),
|
|
161
384
|
});
|
|
162
385
|
}
|
|
163
386
|
|
|
@@ -167,11 +390,31 @@ export async function inspectProjectState(projectRoot) {
|
|
|
167
390
|
schemaVersion: state.schemaVersion,
|
|
168
391
|
lastRuntimeVersionApplied: state.lastRuntimeVersionApplied,
|
|
169
392
|
runtimeUpToDateWithProject: state.lastRuntimeVersionApplied === runtime,
|
|
170
|
-
managedFilesCurrent: files.every((file) => file.current || !file.patchable),
|
|
393
|
+
managedFilesCurrent: files.every((file) => file.current || (!file.patchable && !file.needsAttention)),
|
|
171
394
|
managedFiles: files,
|
|
172
395
|
};
|
|
173
396
|
}
|
|
174
397
|
|
|
398
|
+
export function formatProjectMaintenanceContext(inspection) {
|
|
399
|
+
const files = inspection?.managedFiles || [];
|
|
400
|
+
const attention = files.filter((file) => file.needsAttention);
|
|
401
|
+
const outdated = files.filter((file) => !file.current && file.patchable);
|
|
402
|
+
if (attention.length === 0 && outdated.length === 0) return '';
|
|
403
|
+
|
|
404
|
+
const lines = [
|
|
405
|
+
'LLM Wiki maintenance note:',
|
|
406
|
+
'- 이전 버전의 llm-wiki-kit 규칙/문서가 남아 있을 수 있다.',
|
|
407
|
+
'- 확실히 kit가 관리하는 영역은 자동 갱신된다. 사용자 편집 가능성이 있는 문서는 덮어쓰지 말고 기존 내용을 보존한 채 현재 규칙에 맞게 자연스럽게 정리한다.',
|
|
408
|
+
];
|
|
409
|
+
if (outdated.length > 0) {
|
|
410
|
+
lines.push(`- 자동 갱신 대상: ${outdated.map((file) => file.path).join(', ')}`);
|
|
411
|
+
}
|
|
412
|
+
if (attention.length > 0) {
|
|
413
|
+
lines.push(`- agent 확인 필요: ${attention.map((file) => file.path).join(', ')}`);
|
|
414
|
+
}
|
|
415
|
+
return lines.join('\n');
|
|
416
|
+
}
|
|
417
|
+
|
|
175
418
|
export async function recordManagedTemplates(projectRoot) {
|
|
176
419
|
const state = await readProjectState(projectRoot);
|
|
177
420
|
const runtime = runtimeVersion();
|
|
@@ -211,7 +454,7 @@ export async function applyProjectTemplateUpdate(projectRoot, options = {}) {
|
|
|
211
454
|
const absolutePath = join(projectRoot, descriptor.path);
|
|
212
455
|
if (!(await exists(absolutePath))) {
|
|
213
456
|
if (descriptor.mode === 'create-only') {
|
|
214
|
-
if (!(await
|
|
457
|
+
if (!(await descriptorParentExists(projectRoot, descriptor))) {
|
|
215
458
|
skipped.push({ id: descriptor.id, path: descriptor.path, reason: 'missing-wiki' });
|
|
216
459
|
continue;
|
|
217
460
|
}
|
|
@@ -227,6 +470,19 @@ export async function applyProjectTemplateUpdate(projectRoot, options = {}) {
|
|
|
227
470
|
};
|
|
228
471
|
continue;
|
|
229
472
|
}
|
|
473
|
+
if (descriptor.mode === 'generated-file' && await descriptorParentExists(projectRoot, descriptor)) {
|
|
474
|
+
if (!options.dryRun) {
|
|
475
|
+
await ensureDir(dirname(absolutePath));
|
|
476
|
+
await writeText(absolutePath, descriptor.content, 'utf8');
|
|
477
|
+
}
|
|
478
|
+
changed.push({ id: descriptor.id, path: descriptor.path });
|
|
479
|
+
state.managedTemplates[descriptor.id] = {
|
|
480
|
+
path: descriptor.path,
|
|
481
|
+
version: runtime,
|
|
482
|
+
hash: templateHash(descriptor.content),
|
|
483
|
+
};
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
230
486
|
skipped.push({ id: descriptor.id, path: descriptor.path, reason: 'missing' });
|
|
231
487
|
continue;
|
|
232
488
|
}
|
package/src/project.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import { LLM_WIKI_DIRS } from './constants.js';
|
|
12
12
|
import { normalizeForStorage, redactText, summarizeForStorage } from './redaction.js';
|
|
13
13
|
import { gitignore, indexPage, llmWikiAgents, logPage, memoryPage, procedure, rootAgentsPolicy } from './templates.js';
|
|
14
|
-
import { recordManagedTemplates } from './project-state.js';
|
|
14
|
+
import { formatProjectMaintenanceContext, inspectProjectState, recordManagedTemplates } from './project-state.js';
|
|
15
15
|
import { buildContextPack, formatContextPack, searchWiki as searchWikiWithIndex } from './wiki-search.js';
|
|
16
16
|
|
|
17
17
|
export async function bootstrapProject(projectRoot, options = {}) {
|
|
@@ -117,7 +117,7 @@ export async function appendLiveQa(projectRoot, entry) {
|
|
|
117
117
|
entry.verification || '(not captured)',
|
|
118
118
|
'',
|
|
119
119
|
'### Follow-up',
|
|
120
|
-
entry.followUp || '
|
|
120
|
+
entry.followUp || '다음 작업에서 이 turn의 reusable fact가 있으면 기존 wiki 문서에 합치고, 일회성 기록은 이 Q&A에만 보존한다.',
|
|
121
121
|
'',
|
|
122
122
|
].join('\n');
|
|
123
123
|
await appendText(path, redactText(block, 12000));
|
|
@@ -163,5 +163,8 @@ export async function buildContextBrief(projectRoot, eventName, query = '') {
|
|
|
163
163
|
includeLog: eventName === 'SessionStart',
|
|
164
164
|
limit: 5,
|
|
165
165
|
});
|
|
166
|
-
|
|
166
|
+
const maintenance = await inspectProjectState(projectRoot)
|
|
167
|
+
.then(formatProjectMaintenanceContext)
|
|
168
|
+
.catch(() => '');
|
|
169
|
+
return [formatContextPack(pack), maintenance].filter(Boolean).join('\n\n');
|
|
167
170
|
}
|
package/src/state.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { join } from 'path';
|
|
2
|
+
import { unlink } from 'fs/promises';
|
|
2
3
|
import { kitDataDir, readJson, sha256, writeJson } from './fs-utils.js';
|
|
3
4
|
import { summarizeForStorage } from './redaction.js';
|
|
4
5
|
|
|
@@ -33,6 +34,10 @@ export async function writeTurnState(projectRoot, payload, state) {
|
|
|
33
34
|
await writeJson(statePath(projectRoot, payload), state);
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
export async function clearTurnState(projectRoot, payload) {
|
|
38
|
+
await unlink(statePath(projectRoot, payload)).catch(() => {});
|
|
39
|
+
}
|
|
40
|
+
|
|
36
41
|
export async function rememberQuestion(projectRoot, payload, prompt) {
|
|
37
42
|
const state = await readTurnState(projectRoot, payload);
|
|
38
43
|
const clean = summarizeForStorage(prompt, 3000);
|
package/src/templates.js
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
import { runtimeVersion } from './version.js';
|
|
2
2
|
|
|
3
3
|
export function rootAgentsPolicy() {
|
|
4
|
-
return
|
|
4
|
+
return `
|
|
5
|
+
<!-- llm-wiki-kit:start -->
|
|
6
|
+
## LLM Wiki Policy
|
|
7
|
+
|
|
8
|
+
This repository uses llm-wiki-kit as a hook-first living Markdown wiki for Codex and Claude Code.
|
|
9
|
+
|
|
10
|
+
- This block supersedes older OMX/OMC/\`omx_wiki/\` LLM Wiki instructions for this repository.
|
|
11
|
+
- 평소처럼 Codex 또는 Claude Code를 사용한다. 사용자가 별도 \`llm-wiki\` 명령을 외워서 실행해야 하는 흐름을 만들지 않는다.
|
|
12
|
+
- 채팅 기억은 임시로 본다. 오래 남길 프로젝트 지식은 \`llm-wiki/\` Markdown에 남긴다.
|
|
13
|
+
- \`llm-wiki/raw/\`는 원본 또는 redacted 근거 저장소다. hook envelope append 외에는 원본 capture를 수정하지 않는다.
|
|
14
|
+
- \`llm-wiki/wiki/\`는 agent가 관리하는 지식층이다. 결정, 구조, 디버깅, 개념, 절차, 맥락을 여기에 정리한다.
|
|
15
|
+
- \`llm-wiki/wiki/memory.md\`는 짧은 핵심 기억이다. 긴 설명 대신 현재 상태와 중요한 문서 링크만 유지한다.
|
|
16
|
+
- 새 문서를 만들기 전에 기존 wiki 문서를 먼저 찾아 갱신한다. 반복해서 쓸 사실은 \`outputs/questions/\`에만 두지 말고 적절한 wiki 문서에 합친다.
|
|
17
|
+
- 일회성 작업 기록은 \`llm-wiki/outputs/questions/\`에 보존하고, 재사용 가능한 결론은 \`wiki/architecture/\`, \`wiki/debugging/\`, \`wiki/decisions/\`, \`wiki/concepts/\`, \`procedures/\`에 반영한다.
|
|
18
|
+
- 검증 명령, 근거 파일, 불확실한 점을 함께 남긴다. 추론은 추론이라고 표시하고, 모순은 지우지 말고 Open Questions 또는 Contradictions에 남긴다.
|
|
19
|
+
- 인증값, token, password, private key, \`.env\` 원문은 저장하지 않는다. 필요한 경우 redacted summary만 남긴다.
|
|
20
|
+
|
|
21
|
+
<!-- llm-wiki-kit:end -->
|
|
22
|
+
`;
|
|
5
23
|
}
|
|
6
24
|
|
|
7
25
|
export function llmWikiAgents() {
|
|
@@ -10,23 +28,26 @@ export function llmWikiAgents() {
|
|
|
10
28
|
Generated by llm-wiki-kit ${runtimeVersion()}.
|
|
11
29
|
|
|
12
30
|
## Purpose
|
|
13
|
-
|
|
14
|
-
|
|
31
|
+
Codex와 Claude Code를 평소처럼 사용하는 동안 living Markdown LLM Wiki를 자연스럽게 유지한다.
|
|
32
|
+
사용자가 많은 \`llm-wiki\` 명령을 직접 실행하는 방식이 아니라, agent가 작업 중 필요한 wiki 조회와 정리를 수행하는 것이 기본이다.
|
|
33
|
+
이 규칙은 오래된 OMX/OMC/\`omx_wiki/\` 규칙을 대체한다.
|
|
15
34
|
|
|
16
35
|
## Directories
|
|
17
|
-
- \`raw/\`:
|
|
18
|
-
- \`wiki/\`:
|
|
19
|
-
- \`outputs/\`: live Q&A
|
|
20
|
-
- \`procedures/\`:
|
|
36
|
+
- \`raw/\`: 원본 또는 redacted 근거 저장소. hook이 redacted envelope를 append하는 경우 외에는 원본 capture를 수정하지 않는다.
|
|
37
|
+
- \`wiki/\`: agent가 관리하는 정식 지식 문서. \`wiki/memory.md\`는 hook context에 들어가는 짧은 핵심 기억이다.
|
|
38
|
+
- \`outputs/\`: live Q&A, 보고서, 긴 생성물 저장소. 일회성 기록은 여기에 둔다.
|
|
39
|
+
- \`procedures/\`: ingest, query, lint, security 같은 운영 규칙.
|
|
21
40
|
|
|
22
41
|
## Core Rules
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
42
|
+
- 사용자는 Claude Code/Codex를 평소처럼 사용한다. agent가 필요한 wiki 조회와 정리를 자연스럽게 수행한다.
|
|
43
|
+
- \`raw/\` 원본은 수정하지 않는다. 안전한 hook envelope append만 예외다.
|
|
44
|
+
- 근거 없는 내용을 사실처럼 쓰지 않는다. 추론은 명시한다.
|
|
45
|
+
- 중요한 주장에는 \`source_ids\`, 파일 경로, 검증 명령 중 하나 이상을 남긴다.
|
|
46
|
+
- 오래 남길 내용이 생기면 새 문서부터 만들지 말고 기존 \`wiki/\` 문서를 먼저 찾아 갱신한다.
|
|
47
|
+
- 반복해서 쓸 지식은 \`outputs/questions/\`에만 두지 말고 \`wiki/architecture/\`, \`wiki/debugging/\`, \`wiki/decisions/\`, \`wiki/concepts/\`, \`procedures/\`에 합친다.
|
|
48
|
+
- \`wiki/memory.md\`는 짧게 유지한다. 긴 설명 대신 현재 상태와 중요한 문서 링크를 둔다.
|
|
49
|
+
- 모순은 덮어쓰지 말고 \`Contradictions\` 또는 \`Open Questions\`에 보존한다.
|
|
50
|
+
- 인증값, token, password, private key, \`.env\` 원문은 wiki에 저장하지 않는다.
|
|
30
51
|
|
|
31
52
|
## Page Format
|
|
32
53
|
Use YAML frontmatter when creating wiki pages:
|
|
@@ -36,7 +57,7 @@ Use YAML frontmatter when creating wiki pages:
|
|
|
36
57
|
title: ""
|
|
37
58
|
type: "source | concept | entity | decision | architecture | debugging | context | query | session-log | convention"
|
|
38
59
|
source_ids: []
|
|
39
|
-
status: "draft | reviewed | stale"
|
|
60
|
+
status: "draft | reviewed | stale | archived"
|
|
40
61
|
last_updated: "YYYY-MM-DD"
|
|
41
62
|
confidence: "high | medium | low"
|
|
42
63
|
memory_type: "semantic | episodic | procedural"
|
|
@@ -48,15 +69,47 @@ superseded_by: []
|
|
|
48
69
|
\`\`\`
|
|
49
70
|
|
|
50
71
|
## Operations
|
|
51
|
-
- ingest:
|
|
52
|
-
- query:
|
|
53
|
-
- lint:
|
|
54
|
-
- consolidate:
|
|
72
|
+
- ingest: \`wiki/memory.md\`와 \`wiki/index.md\`를 먼저 읽고, 새 근거를 확인한 뒤 기존 wiki 문서를 우선 갱신한다.
|
|
73
|
+
- query: hook이 주입한 context를 우선 사용한다. 수동 명령은 점검용이며 일반 사용 흐름의 필수 단계가 아니다.
|
|
74
|
+
- lint: agent가 필요할 때 wiki 건강 상태를 점검하는 보조 도구다. 사용자가 매번 직접 실행해야 하는 명령이 아니다.
|
|
75
|
+
- consolidate: agent가 \`memory.md\`/\`index.md\` generated block을 안전하게 갱신할 때 쓰는 보조 도구다. 손글씨 영역과 정식 문서 본문은 덮어쓰지 않는다.
|
|
55
76
|
`;
|
|
56
77
|
}
|
|
57
78
|
|
|
58
79
|
export function indexPage() {
|
|
59
|
-
return `# LLM Wiki Index
|
|
80
|
+
return `# LLM Wiki Index
|
|
81
|
+
|
|
82
|
+
Generated by llm-wiki-kit.
|
|
83
|
+
|
|
84
|
+
## Overview
|
|
85
|
+
|
|
86
|
+
이 파일은 living wiki의 짧은 지도다. 자세한 설명을 길게 쓰기보다, agent가 다음 작업에서 빠르게 찾아갈 entry point를 유지한다.
|
|
87
|
+
|
|
88
|
+
## Main Areas
|
|
89
|
+
|
|
90
|
+
- [Memory](memory.md) - 짧은 핵심 기억과 주요 entry point.
|
|
91
|
+
- [Sources](sources/) - source summary와 source id.
|
|
92
|
+
- [Concepts](concepts/) - 재사용 가능한 개념과 용어.
|
|
93
|
+
- [Entities](entities/) - 시스템, 모듈, 도구, 서비스, 주요 객체.
|
|
94
|
+
- [Decisions](decisions/) - 결정과 근거.
|
|
95
|
+
- [Architecture](architecture/) - 구조와 데이터/제어 흐름.
|
|
96
|
+
- [Debugging](debugging/) - 원인, 수정, 검증 evidence.
|
|
97
|
+
- [Context](context/) - 세션 연속성과 프로젝트 맥락.
|
|
98
|
+
- [Queries](queries/) - 재사용 가능한 질문 답변. 일회성 기록은 outputs/questions에 둔다.
|
|
99
|
+
|
|
100
|
+
## Operating Notes
|
|
101
|
+
|
|
102
|
+
- 넓은 질문은 memory와 이 index에서 시작한다.
|
|
103
|
+
- 새 문서를 만들기 전에 관련 기존 문서 3-7개를 먼저 확인한다.
|
|
104
|
+
- 오래 쓸 결론은 기존 정식 문서에 합치고, 일회성 기록은 outputs/questions에 둔다.
|
|
105
|
+
- 관련 페이지가 생기면 \`[[page-or-topic]]\` 링크를 추가한다.
|
|
106
|
+
|
|
107
|
+
<!-- llm-wiki-kit:index-start -->
|
|
108
|
+
## Generated Page Map
|
|
109
|
+
|
|
110
|
+
Agent가 필요할 때 \`llm-wiki consolidate --workspace <project>\`로 이 generated block을 갱신한다.
|
|
111
|
+
<!-- llm-wiki-kit:index-end -->
|
|
112
|
+
`;
|
|
60
113
|
}
|
|
61
114
|
|
|
62
115
|
export function memoryPage() {
|
|
@@ -76,11 +129,11 @@ superseded_by: []
|
|
|
76
129
|
|
|
77
130
|
# LLM Wiki Memory
|
|
78
131
|
|
|
79
|
-
|
|
132
|
+
짧은 핵심 기억이다. hook context에 들어갈 만큼 작게 유지하고, 긴 설명을 복사하지 말고 깊은 문서로 링크한다.
|
|
80
133
|
|
|
81
134
|
## Current Focus
|
|
82
135
|
|
|
83
|
-
-
|
|
136
|
+
- 오래 남길 현재 프로젝트 초점을 여기에 짧게 추가한다.
|
|
84
137
|
|
|
85
138
|
## Durable Entry Points
|
|
86
139
|
|
|
@@ -90,25 +143,74 @@ Short hot index for project memory. Keep this page compact enough for hook conte
|
|
|
90
143
|
<!-- llm-wiki-kit:memory-start -->
|
|
91
144
|
## Generated Memory Map
|
|
92
145
|
|
|
93
|
-
|
|
146
|
+
Agent가 필요할 때 \`llm-wiki consolidate --workspace <project>\`로 이 generated block을 갱신한다.
|
|
94
147
|
<!-- llm-wiki-kit:memory-end -->
|
|
95
148
|
`;
|
|
96
149
|
}
|
|
97
150
|
|
|
98
151
|
export function logPage() {
|
|
99
|
-
return `# LLM Wiki Log
|
|
152
|
+
return `# LLM Wiki Log
|
|
153
|
+
|
|
154
|
+
Append-only operating history for this project wiki.
|
|
155
|
+
`;
|
|
100
156
|
}
|
|
101
157
|
|
|
102
158
|
export function procedure(name) {
|
|
103
159
|
const procedures = {
|
|
104
|
-
'ingest.md': `# Ingest Procedure
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
160
|
+
'ingest.md': `# Ingest Procedure
|
|
161
|
+
|
|
162
|
+
1. \`wiki/memory.md\`와 \`wiki/index.md\`를 먼저 읽는다.
|
|
163
|
+
2. 새 근거가 있으면 \`raw/inbox/\` 또는 \`raw/sources/\`를 확인한다.
|
|
164
|
+
3. source별 요약이 필요하면 \`wiki/sources/<slug>.md\`를 만들거나 갱신한다.
|
|
165
|
+
4. 새 문서부터 만들지 말고 관련 concept/entity/decision/architecture/debugging/context 문서를 먼저 찾아 갱신한다.
|
|
166
|
+
5. 중복 문서를 만들지 않는다. 같은 사실이 이미 있으면 기존 문서에 합친다.
|
|
167
|
+
6. 중요한 주장에는 source reference, confidence, memory type, importance, verification status를 남긴다.
|
|
168
|
+
7. durable entry point가 바뀌면 \`wiki/memory.md\`를 짧게 갱신하거나 agent가 \`llm-wiki consolidate\`를 사용한다.
|
|
169
|
+
8. 의미 있는 변화는 \`wiki/log.md\`에 짧게 남긴다.
|
|
170
|
+
`,
|
|
171
|
+
'query.md': `# Query Procedure
|
|
172
|
+
|
|
173
|
+
1. hook이 주입한 context를 먼저 사용한다. 수동으로 확인할 때만 \`llm-wiki context "<query>"\`를 쓴다.
|
|
174
|
+
2. \`wiki/memory.md\`와 \`wiki/index.md\`에서 시작한다.
|
|
175
|
+
3. 관련 \`wiki/\` 문서를 최소한으로 읽는다.
|
|
176
|
+
4. 정확한 근거가 필요할 때만 raw source를 확인한다.
|
|
177
|
+
5. 검증된 사실과 추론을 분리한다.
|
|
178
|
+
6. 일회성 답변은 \`outputs/questions/\`에 남기고, 재사용 가능한 답변은 기존 \`wiki/queries/\` 또는 관련 정식 문서에 연결한다.
|
|
179
|
+
7. 반복해서 쓸 결론은 \`wiki/architecture/\`, \`wiki/debugging/\`, \`wiki/decisions/\`, \`wiki/concepts/\`, \`procedures/\`에 합친다.
|
|
180
|
+
`,
|
|
181
|
+
'lint.md': `# Lint Procedure
|
|
182
|
+
|
|
183
|
+
\`llm-wiki lint --workspace <project>\`는 agent가 필요할 때 쓰는 wiki 건강 점검 도구다. 사용자가 매번 실행해야 하는 명령이 아니다.
|
|
184
|
+
|
|
185
|
+
점검 대상: stale page, orphan page, broken wiki/Markdown link, unsafe source id, secret-like content, missing source, duplicate concept/title, unsupported claim, unresolved contradiction, outdated managed rule.
|
|
186
|
+
|
|
187
|
+
자동 수정은 확실히 kit가 관리하는 영역에만 적용한다. 사용자 편집 가능성이 있는 문서는 덮어쓰지 말고 다음 작업 context에 정리 필요성을 올린다.
|
|
188
|
+
`,
|
|
189
|
+
'security.md': `# Security Procedure
|
|
190
|
+
|
|
191
|
+
- 로컬 프로젝트 wiki에 유용한 작업 맥락은 보존한다.
|
|
192
|
+
- 입력이 민감해 보인다는 이유만으로 read/tool call을 막지 않는다.
|
|
193
|
+
- token, password, bearer credential, private key, \`.env\` 원문 같은 인증값은 hook payload, summary, context pack, wiki page에 저장하기 전에 redaction한다.
|
|
194
|
+
- 민감정보가 wiki에 들어갔을 가능성이 있으면 agent가 \`llm-wiki lint --workspace <project>\`를 사용해 점검한다.
|
|
195
|
+
- full raw transcript capture는 기본 비활성이다. 프로젝트가 명시적으로 opt-in하고 redaction 경로가 있을 때만 허용한다.
|
|
196
|
+
`,
|
|
108
197
|
};
|
|
109
198
|
return procedures[name] || '';
|
|
110
199
|
}
|
|
111
200
|
|
|
112
201
|
export function gitignore() {
|
|
113
|
-
return `# llm-wiki-kit safety defaults
|
|
202
|
+
return `# llm-wiki-kit safety defaults
|
|
203
|
+
raw/sessions/*.jsonl
|
|
204
|
+
raw/sessions/*.ndjson
|
|
205
|
+
raw/private/
|
|
206
|
+
raw_private/
|
|
207
|
+
secrets/
|
|
208
|
+
*.key
|
|
209
|
+
*.pem
|
|
210
|
+
*.p12
|
|
211
|
+
*.pfx
|
|
212
|
+
.env
|
|
213
|
+
.env.*
|
|
214
|
+
.cache/
|
|
215
|
+
`;
|
|
114
216
|
}
|
package/src/update.js
CHANGED
|
@@ -43,6 +43,29 @@ export function parseRegistryVersion(output) {
|
|
|
43
43
|
return String(output || '').trim().split(/\s+/).at(-1) || '';
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
export function compareVersions(a, b) {
|
|
47
|
+
const parse = (value) => {
|
|
48
|
+
const [core, prerelease = ''] = String(value || '').trim().replace(/^v/, '').split('-', 2);
|
|
49
|
+
return {
|
|
50
|
+
nums: core.split('.').slice(0, 3).map((part) => {
|
|
51
|
+
const parsed = Number.parseInt(part, 10);
|
|
52
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
53
|
+
}),
|
|
54
|
+
prerelease,
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
const left = parse(a);
|
|
58
|
+
const right = parse(b);
|
|
59
|
+
for (let i = 0; i < 3; i += 1) {
|
|
60
|
+
const diff = (left.nums[i] || 0) - (right.nums[i] || 0);
|
|
61
|
+
if (diff !== 0) return diff > 0 ? 1 : -1;
|
|
62
|
+
}
|
|
63
|
+
if (left.prerelease && !right.prerelease) return -1;
|
|
64
|
+
if (!left.prerelease && right.prerelease) return 1;
|
|
65
|
+
if (left.prerelease === right.prerelease) return 0;
|
|
66
|
+
return left.prerelease > right.prerelease ? 1 : -1;
|
|
67
|
+
}
|
|
68
|
+
|
|
46
69
|
export async function checkForUpdate(options = {}) {
|
|
47
70
|
const target = options.to || 'latest';
|
|
48
71
|
const installedVersion = runtimeVersion();
|
|
@@ -55,7 +78,7 @@ export async function checkForUpdate(options = {}) {
|
|
|
55
78
|
installedVersion,
|
|
56
79
|
latestVersion,
|
|
57
80
|
target,
|
|
58
|
-
updateAvailable: latestVersion
|
|
81
|
+
updateAvailable: compareVersions(latestVersion, installedVersion) > 0,
|
|
59
82
|
};
|
|
60
83
|
}
|
|
61
84
|
|
package/src/wiki-lint.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readdir } from 'fs/promises';
|
|
2
2
|
import { dirname, isAbsolute, join, parse, relative, resolve, sep } from 'path';
|
|
3
3
|
import { exists, readText } from './fs-utils.js';
|
|
4
|
+
import { inspectProjectState } from './project-state.js';
|
|
4
5
|
import { hasSecretLikeText } from './redaction.js';
|
|
5
6
|
import {
|
|
6
7
|
buildAliasMap,
|
|
@@ -23,7 +24,7 @@ const VALID_TYPES = new Set([
|
|
|
23
24
|
'session-log',
|
|
24
25
|
'convention',
|
|
25
26
|
]);
|
|
26
|
-
const VALID_STATUS = new Set(['draft', 'reviewed', 'stale']);
|
|
27
|
+
const VALID_STATUS = new Set(['draft', 'reviewed', 'stale', 'archived']);
|
|
27
28
|
const VALID_CONFIDENCE = new Set(['high', 'medium', 'low']);
|
|
28
29
|
const VALID_MEMORY_TYPES = new Set(['semantic', 'episodic', 'procedural']);
|
|
29
30
|
const CORE_PAGES = new Set(['wiki/index.md', 'wiki/log.md', 'wiki/memory.md']);
|
|
@@ -234,6 +235,15 @@ export async function runLint(projectRoot, options = {}) {
|
|
|
234
235
|
}
|
|
235
236
|
}
|
|
236
237
|
|
|
238
|
+
const projectState = await inspectProjectState(projectRoot).catch(() => null);
|
|
239
|
+
for (const file of projectState?.managedFiles || []) {
|
|
240
|
+
if (file.needsAttention) {
|
|
241
|
+
issues.push(issue('warning', 'outdated-managed-rule', file.path, 'previous llm-wiki-kit rule file appears customized; preserve user text and align it with current natural-use rules'));
|
|
242
|
+
} else if (!file.current && file.patchable) {
|
|
243
|
+
issues.push(issue('warning', 'outdated-managed-template', file.path, 'managed llm-wiki-kit rule/template can be automatically updated on the next hook or post-update run'));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
237
247
|
return lintResult(projectRoot, pages, issues);
|
|
238
248
|
}
|
|
239
249
|
|
package/src/wiki-search.js
CHANGED
|
@@ -216,6 +216,9 @@ export function formatContextPack(pack) {
|
|
|
216
216
|
'- Treat chat memory as temporary; update project Markdown when knowledge should persist.',
|
|
217
217
|
'- Preserve raw/wiki separation. Do not store secrets, tokens, .env contents, private keys, or personal/customer identifiers.',
|
|
218
218
|
'- Prefer updating existing wiki pages over creating duplicate pages.',
|
|
219
|
+
'- Claude Code/Codex를 평소처럼 사용한다. 사용자가 별도 명령을 실행하지 않아도 agent가 필요한 wiki 조회와 정리를 수행한다.',
|
|
220
|
+
'- 오래 쓸 지식은 outputs/questions에만 두지 말고 기존 wiki/architecture, wiki/debugging, wiki/decisions, wiki/concepts, procedures 문서에 합친다.',
|
|
221
|
+
'- wiki/memory.md는 짧은 핵심 기억으로 유지하고, 긴 설명 대신 관련 정식 문서 링크를 둔다.',
|
|
219
222
|
];
|
|
220
223
|
if (pack.query) {
|
|
221
224
|
lines.push(`- query: "${pack.query}"`);
|