llm-wiki-kit 0.1.6 → 0.1.8
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 +26 -29
- package/docs/concepts.md +8 -5
- package/docs/integrations/claude-code.md +7 -3
- package/docs/integrations/codex.md +9 -3
- package/docs/operations.md +36 -18
- package/docs/research/baseline.md +2 -0
- package/docs/research/future-large-scale.md +27 -0
- package/docs/troubleshooting.md +22 -11
- package/package.json +1 -1
- package/src/cli.js +32 -9
- package/src/doctor.js +49 -19
- package/src/hook.js +9 -3
- package/src/install.js +98 -8
- package/src/project-state.js +265 -9
- package/src/project.js +6 -3
- package/src/projects.js +16 -4
- package/src/state.js +5 -0
- package/src/templates.js +133 -29
- package/src/update.js +73 -12
- package/src/wiki-lint.js +11 -1
- package/src/wiki-search.js +3 -0
package/src/projects.js
CHANGED
|
@@ -50,6 +50,11 @@ export async function recordProject(projectRoot, source = 'unknown') {
|
|
|
50
50
|
return registry.projects[workspace];
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
async function looksLikeProjectRoot(root) {
|
|
54
|
+
return (await exists(join(root, 'llm-wiki', '.kit-state.json'))) ||
|
|
55
|
+
(await exists(join(root, 'llm-wiki', 'wiki', 'index.md')));
|
|
56
|
+
}
|
|
57
|
+
|
|
53
58
|
export async function discoverProjectRoots(searchRoot, options = {}) {
|
|
54
59
|
const root = resolve(searchRoot || process.cwd());
|
|
55
60
|
const maxDirs = options.maxDirs || 5000;
|
|
@@ -60,7 +65,7 @@ export async function discoverProjectRoots(searchRoot, options = {}) {
|
|
|
60
65
|
if (seen >= maxDirs) return;
|
|
61
66
|
seen += 1;
|
|
62
67
|
|
|
63
|
-
if (await
|
|
68
|
+
if (await looksLikeProjectRoot(dir)) {
|
|
64
69
|
roots.add(dir);
|
|
65
70
|
}
|
|
66
71
|
|
|
@@ -73,8 +78,15 @@ export async function discoverProjectRoots(searchRoot, options = {}) {
|
|
|
73
78
|
|
|
74
79
|
for (const entry of entries) {
|
|
75
80
|
if (seen >= maxDirs) return;
|
|
76
|
-
if (!entry.isDirectory()
|
|
77
|
-
|
|
81
|
+
if (!entry.isDirectory()) continue;
|
|
82
|
+
const child = join(dir, entry.name);
|
|
83
|
+
if (SKIP_DIRS.has(entry.name)) {
|
|
84
|
+
if (entry.name === 'llm-wiki' && await looksLikeProjectRoot(child)) {
|
|
85
|
+
await walk(child);
|
|
86
|
+
}
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
await walk(child);
|
|
78
90
|
}
|
|
79
91
|
}
|
|
80
92
|
|
|
@@ -92,7 +104,7 @@ export async function knownProjectRoots(options = {}) {
|
|
|
92
104
|
|
|
93
105
|
const existing = [];
|
|
94
106
|
for (const root of [...roots].sort()) {
|
|
95
|
-
if (await isDirectory(root) && await
|
|
107
|
+
if (await isDirectory(root) && await looksLikeProjectRoot(root)) {
|
|
96
108
|
existing.push(root);
|
|
97
109
|
}
|
|
98
110
|
}
|
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,27 @@
|
|
|
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
|
+
- hook이 주입한 context를 우선 사용한다. 수동 확인이나 정리에는 \`llm-wiki context\`, \`llm-wiki lint\`, \`llm-wiki consolidate\`를 agent 보조 도구로 사용한다.
|
|
17
|
+
- hook은 redacted live Q&A와 query/decision 후보를 기록한다. 재사용 가능한 지식은 agent가 기존 정식 wiki 문서에 합친다.
|
|
18
|
+
- 새 문서를 만들기 전에 기존 wiki 문서를 먼저 찾아 갱신한다. 반복해서 쓸 사실은 \`outputs/questions/\`에만 두지 말고 적절한 wiki 문서에 합친다.
|
|
19
|
+
- 일회성 작업 기록은 \`llm-wiki/outputs/questions/\`에 보존하고, 재사용 가능한 결론은 \`wiki/architecture/\`, \`wiki/debugging/\`, \`wiki/decisions/\`, \`wiki/concepts/\`, \`procedures/\`에 반영한다.
|
|
20
|
+
- 검증 명령, 근거 파일, 불확실한 점을 함께 남긴다. 추론은 추론이라고 표시하고, 모순은 지우지 말고 Open Questions 또는 Contradictions에 남긴다.
|
|
21
|
+
- 인증값, token, password, private key, \`.env\` 원문은 저장하지 않는다. 필요한 경우 redacted summary만 남긴다.
|
|
22
|
+
|
|
23
|
+
<!-- llm-wiki-kit:end -->
|
|
24
|
+
`;
|
|
5
25
|
}
|
|
6
26
|
|
|
7
27
|
export function llmWikiAgents() {
|
|
@@ -10,23 +30,26 @@ export function llmWikiAgents() {
|
|
|
10
30
|
Generated by llm-wiki-kit ${runtimeVersion()}.
|
|
11
31
|
|
|
12
32
|
## Purpose
|
|
13
|
-
|
|
14
|
-
|
|
33
|
+
Codex와 Claude Code를 평소처럼 사용하는 동안 living Markdown LLM Wiki를 자연스럽게 유지한다.
|
|
34
|
+
사용자가 많은 \`llm-wiki\` 명령을 직접 실행하는 방식이 아니라, agent가 작업 중 필요한 wiki 조회와 정리를 수행하는 것이 기본이다.
|
|
35
|
+
이 규칙은 오래된 OMX/OMC/\`omx_wiki/\` 규칙을 대체한다.
|
|
15
36
|
|
|
16
37
|
## Directories
|
|
17
|
-
- \`raw/\`:
|
|
18
|
-
- \`wiki/\`:
|
|
19
|
-
- \`outputs/\`: live Q&A
|
|
20
|
-
- \`procedures/\`:
|
|
38
|
+
- \`raw/\`: 원본 또는 redacted 근거 저장소. hook이 redacted envelope를 append하는 경우 외에는 원본 capture를 수정하지 않는다.
|
|
39
|
+
- \`wiki/\`: agent가 관리하는 정식 지식 문서. \`wiki/memory.md\`는 hook context에 들어가는 짧은 핵심 기억이다.
|
|
40
|
+
- \`outputs/\`: live Q&A, 보고서, 긴 생성물 저장소. 일회성 기록은 여기에 둔다.
|
|
41
|
+
- \`procedures/\`: ingest, query, lint, security 같은 운영 규칙.
|
|
21
42
|
|
|
22
43
|
## Core Rules
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
44
|
+
- 사용자는 Claude Code/Codex를 평소처럼 사용한다. agent가 필요한 wiki 조회와 정리를 자연스럽게 수행한다.
|
|
45
|
+
- \`raw/\` 원본은 수정하지 않는다. 안전한 hook envelope append만 예외다.
|
|
46
|
+
- 근거 없는 내용을 사실처럼 쓰지 않는다. 추론은 명시한다.
|
|
47
|
+
- 중요한 주장에는 \`source_ids\`, 파일 경로, 검증 명령 중 하나 이상을 남긴다.
|
|
48
|
+
- 오래 남길 내용이 생기면 새 문서부터 만들지 말고 기존 \`wiki/\` 문서를 먼저 찾아 갱신한다.
|
|
49
|
+
- 반복해서 쓸 지식은 \`outputs/questions/\`에만 두지 말고 \`wiki/architecture/\`, \`wiki/debugging/\`, \`wiki/decisions/\`, \`wiki/concepts/\`, \`procedures/\`에 합친다.
|
|
50
|
+
- \`wiki/memory.md\`는 짧게 유지한다. 긴 설명 대신 현재 상태와 중요한 문서 링크를 둔다.
|
|
51
|
+
- 모순은 덮어쓰지 말고 \`Contradictions\` 또는 \`Open Questions\`에 보존한다.
|
|
52
|
+
- 인증값, token, password, private key, \`.env\` 원문은 wiki에 저장하지 않는다.
|
|
30
53
|
|
|
31
54
|
## Page Format
|
|
32
55
|
Use YAML frontmatter when creating wiki pages:
|
|
@@ -36,7 +59,7 @@ Use YAML frontmatter when creating wiki pages:
|
|
|
36
59
|
title: ""
|
|
37
60
|
type: "source | concept | entity | decision | architecture | debugging | context | query | session-log | convention"
|
|
38
61
|
source_ids: []
|
|
39
|
-
status: "draft | reviewed | stale"
|
|
62
|
+
status: "draft | reviewed | stale | archived"
|
|
40
63
|
last_updated: "YYYY-MM-DD"
|
|
41
64
|
confidence: "high | medium | low"
|
|
42
65
|
memory_type: "semantic | episodic | procedural"
|
|
@@ -48,15 +71,47 @@ superseded_by: []
|
|
|
48
71
|
\`\`\`
|
|
49
72
|
|
|
50
73
|
## Operations
|
|
51
|
-
- ingest:
|
|
52
|
-
- query:
|
|
53
|
-
- lint:
|
|
54
|
-
- consolidate:
|
|
74
|
+
- ingest: \`wiki/memory.md\`와 \`wiki/index.md\`를 먼저 읽고, 새 근거를 확인한 뒤 기존 wiki 문서를 우선 갱신한다.
|
|
75
|
+
- query: hook이 주입한 context를 우선 사용한다. 수동 명령은 점검용이며 일반 사용 흐름의 필수 단계가 아니다.
|
|
76
|
+
- lint: agent가 필요할 때 wiki 건강 상태를 점검하는 보조 도구다. 사용자가 매번 직접 실행해야 하는 명령이 아니다.
|
|
77
|
+
- consolidate: agent가 \`memory.md\`/\`index.md\` generated block을 안전하게 갱신할 때 쓰는 보조 도구다. 손글씨 영역과 정식 문서 본문은 덮어쓰지 않는다.
|
|
55
78
|
`;
|
|
56
79
|
}
|
|
57
80
|
|
|
58
81
|
export function indexPage() {
|
|
59
|
-
return `# LLM Wiki Index
|
|
82
|
+
return `# LLM Wiki Index
|
|
83
|
+
|
|
84
|
+
Generated by llm-wiki-kit.
|
|
85
|
+
|
|
86
|
+
## Overview
|
|
87
|
+
|
|
88
|
+
이 파일은 living wiki의 짧은 지도다. 자세한 설명을 길게 쓰기보다, agent가 다음 작업에서 빠르게 찾아갈 entry point를 유지한다.
|
|
89
|
+
|
|
90
|
+
## Main Areas
|
|
91
|
+
|
|
92
|
+
- [Memory](memory.md) - 짧은 핵심 기억과 주요 entry point.
|
|
93
|
+
- [Sources](sources/) - source summary와 source id.
|
|
94
|
+
- [Concepts](concepts/) - 재사용 가능한 개념과 용어.
|
|
95
|
+
- [Entities](entities/) - 시스템, 모듈, 도구, 서비스, 주요 객체.
|
|
96
|
+
- [Decisions](decisions/) - 결정과 근거.
|
|
97
|
+
- [Architecture](architecture/) - 구조와 데이터/제어 흐름.
|
|
98
|
+
- [Debugging](debugging/) - 원인, 수정, 검증 evidence.
|
|
99
|
+
- [Context](context/) - 세션 연속성과 프로젝트 맥락.
|
|
100
|
+
- [Queries](queries/) - 재사용 가능한 질문 답변. 일회성 기록은 outputs/questions에 둔다.
|
|
101
|
+
|
|
102
|
+
## Operating Notes
|
|
103
|
+
|
|
104
|
+
- 넓은 질문은 memory와 이 index에서 시작한다.
|
|
105
|
+
- 새 문서를 만들기 전에 관련 기존 문서 3-7개를 먼저 확인한다.
|
|
106
|
+
- 오래 쓸 결론은 기존 정식 문서에 합치고, 일회성 기록은 outputs/questions에 둔다.
|
|
107
|
+
- 관련 페이지가 생기면 \`[[page-or-topic]]\` 링크를 추가한다.
|
|
108
|
+
|
|
109
|
+
<!-- llm-wiki-kit:index-start -->
|
|
110
|
+
## Generated Page Map
|
|
111
|
+
|
|
112
|
+
Agent가 필요할 때 \`llm-wiki consolidate --workspace <project>\`로 이 generated block을 갱신한다.
|
|
113
|
+
<!-- llm-wiki-kit:index-end -->
|
|
114
|
+
`;
|
|
60
115
|
}
|
|
61
116
|
|
|
62
117
|
export function memoryPage() {
|
|
@@ -76,11 +131,11 @@ superseded_by: []
|
|
|
76
131
|
|
|
77
132
|
# LLM Wiki Memory
|
|
78
133
|
|
|
79
|
-
|
|
134
|
+
짧은 핵심 기억이다. hook context에 들어갈 만큼 작게 유지하고, 긴 설명을 복사하지 말고 깊은 문서로 링크한다.
|
|
80
135
|
|
|
81
136
|
## Current Focus
|
|
82
137
|
|
|
83
|
-
-
|
|
138
|
+
- 오래 남길 현재 프로젝트 초점을 여기에 짧게 추가한다.
|
|
84
139
|
|
|
85
140
|
## Durable Entry Points
|
|
86
141
|
|
|
@@ -90,25 +145,74 @@ Short hot index for project memory. Keep this page compact enough for hook conte
|
|
|
90
145
|
<!-- llm-wiki-kit:memory-start -->
|
|
91
146
|
## Generated Memory Map
|
|
92
147
|
|
|
93
|
-
|
|
148
|
+
Agent가 필요할 때 \`llm-wiki consolidate --workspace <project>\`로 이 generated block을 갱신한다.
|
|
94
149
|
<!-- llm-wiki-kit:memory-end -->
|
|
95
150
|
`;
|
|
96
151
|
}
|
|
97
152
|
|
|
98
153
|
export function logPage() {
|
|
99
|
-
return `# LLM Wiki Log
|
|
154
|
+
return `# LLM Wiki Log
|
|
155
|
+
|
|
156
|
+
Append-only operating history for this project wiki.
|
|
157
|
+
`;
|
|
100
158
|
}
|
|
101
159
|
|
|
102
160
|
export function procedure(name) {
|
|
103
161
|
const procedures = {
|
|
104
|
-
'ingest.md': `# Ingest Procedure
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
162
|
+
'ingest.md': `# Ingest Procedure
|
|
163
|
+
|
|
164
|
+
1. \`wiki/memory.md\`와 \`wiki/index.md\`를 먼저 읽는다.
|
|
165
|
+
2. 새 근거가 있으면 \`raw/inbox/\` 또는 \`raw/sources/\`를 확인한다.
|
|
166
|
+
3. source별 요약이 필요하면 \`wiki/sources/<slug>.md\`를 만들거나 갱신한다.
|
|
167
|
+
4. 새 문서부터 만들지 말고 관련 concept/entity/decision/architecture/debugging/context 문서를 먼저 찾아 갱신한다.
|
|
168
|
+
5. 중복 문서를 만들지 않는다. 같은 사실이 이미 있으면 기존 문서에 합친다.
|
|
169
|
+
6. 중요한 주장에는 source reference, confidence, memory type, importance, verification status를 남긴다.
|
|
170
|
+
7. durable entry point가 바뀌면 \`wiki/memory.md\`를 짧게 갱신하거나 agent가 \`llm-wiki consolidate\`를 사용한다.
|
|
171
|
+
8. 의미 있는 변화는 \`wiki/log.md\`에 짧게 남긴다.
|
|
172
|
+
`,
|
|
173
|
+
'query.md': `# Query Procedure
|
|
174
|
+
|
|
175
|
+
1. hook이 주입한 context를 먼저 사용한다. 수동으로 확인할 때만 \`llm-wiki context "<query>"\`를 쓴다.
|
|
176
|
+
2. \`wiki/memory.md\`와 \`wiki/index.md\`에서 시작한다.
|
|
177
|
+
3. 관련 \`wiki/\` 문서를 최소한으로 읽는다.
|
|
178
|
+
4. 정확한 근거가 필요할 때만 raw source를 확인한다.
|
|
179
|
+
5. 검증된 사실과 추론을 분리한다.
|
|
180
|
+
6. 일회성 답변은 \`outputs/questions/\`에 남기고, 재사용 가능한 답변은 기존 \`wiki/queries/\` 또는 관련 정식 문서에 연결한다.
|
|
181
|
+
7. 반복해서 쓸 결론은 \`wiki/architecture/\`, \`wiki/debugging/\`, \`wiki/decisions/\`, \`wiki/concepts/\`, \`procedures/\`에 합친다.
|
|
182
|
+
`,
|
|
183
|
+
'lint.md': `# Lint Procedure
|
|
184
|
+
|
|
185
|
+
\`llm-wiki lint --workspace <project>\`는 agent가 필요할 때 쓰는 wiki 건강 점검 도구다. 사용자가 매번 실행해야 하는 명령이 아니다.
|
|
186
|
+
|
|
187
|
+
점검 대상: 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.
|
|
188
|
+
|
|
189
|
+
자동 수정은 확실히 kit가 관리하는 영역에만 적용한다. 사용자 편집 가능성이 있는 문서는 덮어쓰지 말고 다음 작업 context에 정리 필요성을 올린다.
|
|
190
|
+
`,
|
|
191
|
+
'security.md': `# Security Procedure
|
|
192
|
+
|
|
193
|
+
- 로컬 프로젝트 wiki에 유용한 작업 맥락은 보존한다.
|
|
194
|
+
- 입력이 민감해 보인다는 이유만으로 read/tool call을 막지 않는다.
|
|
195
|
+
- token, password, bearer credential, private key, \`.env\` 원문 같은 인증값은 hook payload, summary, context pack, wiki page에 저장하기 전에 redaction한다.
|
|
196
|
+
- 민감정보가 wiki에 들어갔을 가능성이 있으면 agent가 \`llm-wiki lint --workspace <project>\`를 사용해 점검한다.
|
|
197
|
+
- full raw transcript capture는 기본 비활성이다. 프로젝트가 명시적으로 opt-in하고 redaction 경로가 있을 때만 허용한다.
|
|
198
|
+
`,
|
|
108
199
|
};
|
|
109
200
|
return procedures[name] || '';
|
|
110
201
|
}
|
|
111
202
|
|
|
112
203
|
export function gitignore() {
|
|
113
|
-
return `# llm-wiki-kit safety defaults
|
|
204
|
+
return `# llm-wiki-kit safety defaults
|
|
205
|
+
raw/sessions/*.jsonl
|
|
206
|
+
raw/sessions/*.ndjson
|
|
207
|
+
raw/private/
|
|
208
|
+
raw_private/
|
|
209
|
+
secrets/
|
|
210
|
+
*.key
|
|
211
|
+
*.pem
|
|
212
|
+
*.p12
|
|
213
|
+
*.pfx
|
|
214
|
+
.env
|
|
215
|
+
.env.*
|
|
216
|
+
.cache/
|
|
217
|
+
`;
|
|
114
218
|
}
|
package/src/update.js
CHANGED
|
@@ -4,7 +4,7 @@ import { exists } from './fs-utils.js';
|
|
|
4
4
|
import { appendWikiLog } from './project.js';
|
|
5
5
|
import { install } from './install.js';
|
|
6
6
|
import { applyProjectTemplateUpdate, inspectProjectState } from './project-state.js';
|
|
7
|
-
import { knownProjectRoots } from './projects.js';
|
|
7
|
+
import { knownProjectRoots, recordProject } from './projects.js';
|
|
8
8
|
import { binPath, detectInstallSource, packageName, runtimeVersion } from './version.js';
|
|
9
9
|
|
|
10
10
|
function runCommand(command, args, options = {}) {
|
|
@@ -39,10 +39,61 @@ async function hasProjectWiki(projectRoot) {
|
|
|
39
39
|
return exists(join(projectRoot, 'llm-wiki', 'wiki'));
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
function shouldUpdateAllProjects(options = {}) {
|
|
43
|
+
return options.all || !options.currentOnly;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function projectRootsForUpdate(workspace, options = {}) {
|
|
47
|
+
if (!shouldUpdateAllProjects(options)) return [workspace];
|
|
48
|
+
const roots = await knownProjectRoots({ workspace });
|
|
49
|
+
return roots.length > 0 ? roots : [workspace];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function applyTemplatesToProject(projectRoot, options = {}) {
|
|
53
|
+
const projectResult = options.noProject
|
|
54
|
+
? null
|
|
55
|
+
: await applyProjectTemplateUpdate(projectRoot, { dryRun: options.dryRun });
|
|
56
|
+
if (!options.noProject && !options.dryRun && await hasProjectWiki(projectRoot)) {
|
|
57
|
+
await recordProject(projectRoot, options.source || 'post-update').catch(() => {});
|
|
58
|
+
}
|
|
59
|
+
return projectResult;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function parseJsonOutput(output) {
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(output);
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
42
70
|
export function parseRegistryVersion(output) {
|
|
43
71
|
return String(output || '').trim().split(/\s+/).at(-1) || '';
|
|
44
72
|
}
|
|
45
73
|
|
|
74
|
+
export function compareVersions(a, b) {
|
|
75
|
+
const parse = (value) => {
|
|
76
|
+
const [core, prerelease = ''] = String(value || '').trim().replace(/^v/, '').split('-', 2);
|
|
77
|
+
return {
|
|
78
|
+
nums: core.split('.').slice(0, 3).map((part) => {
|
|
79
|
+
const parsed = Number.parseInt(part, 10);
|
|
80
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
81
|
+
}),
|
|
82
|
+
prerelease,
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
const left = parse(a);
|
|
86
|
+
const right = parse(b);
|
|
87
|
+
for (let i = 0; i < 3; i += 1) {
|
|
88
|
+
const diff = (left.nums[i] || 0) - (right.nums[i] || 0);
|
|
89
|
+
if (diff !== 0) return diff > 0 ? 1 : -1;
|
|
90
|
+
}
|
|
91
|
+
if (left.prerelease && !right.prerelease) return -1;
|
|
92
|
+
if (!left.prerelease && right.prerelease) return 1;
|
|
93
|
+
if (left.prerelease === right.prerelease) return 0;
|
|
94
|
+
return left.prerelease > right.prerelease ? 1 : -1;
|
|
95
|
+
}
|
|
96
|
+
|
|
46
97
|
export async function checkForUpdate(options = {}) {
|
|
47
98
|
const target = options.to || 'latest';
|
|
48
99
|
const installedVersion = runtimeVersion();
|
|
@@ -55,7 +106,7 @@ export async function checkForUpdate(options = {}) {
|
|
|
55
106
|
installedVersion,
|
|
56
107
|
latestVersion,
|
|
57
108
|
target,
|
|
58
|
-
updateAvailable: latestVersion
|
|
109
|
+
updateAvailable: compareVersions(latestVersion, installedVersion) > 0,
|
|
59
110
|
};
|
|
60
111
|
}
|
|
61
112
|
|
|
@@ -72,9 +123,7 @@ export async function postUpdate(options = {}) {
|
|
|
72
123
|
const workspaces = await knownProjectRoots({ workspace });
|
|
73
124
|
const projects = [];
|
|
74
125
|
for (const projectRoot of workspaces) {
|
|
75
|
-
const projectResult = options
|
|
76
|
-
? null
|
|
77
|
-
: await applyProjectTemplateUpdate(projectRoot);
|
|
126
|
+
const projectResult = await applyTemplatesToProject(projectRoot, options);
|
|
78
127
|
if (!options.noProject && await hasProjectWiki(projectRoot)) {
|
|
79
128
|
await appendWikiLog(projectRoot, `llm-wiki-kit post-update applied runtime ${runtimeVersion()}; changed templates: ${projectResult.changed.length}`);
|
|
80
129
|
}
|
|
@@ -91,9 +140,7 @@ export async function postUpdate(options = {}) {
|
|
|
91
140
|
};
|
|
92
141
|
}
|
|
93
142
|
|
|
94
|
-
const projectResult = options
|
|
95
|
-
? null
|
|
96
|
-
: await applyProjectTemplateUpdate(workspace);
|
|
143
|
+
const projectResult = await applyTemplatesToProject(workspace, options);
|
|
97
144
|
|
|
98
145
|
if (!options.noProject && await hasProjectWiki(workspace)) {
|
|
99
146
|
await appendWikiLog(workspace, `llm-wiki-kit post-update applied runtime ${runtimeVersion()}; changed templates: ${projectResult.changed.length}`);
|
|
@@ -125,12 +172,22 @@ export async function update(options = {}) {
|
|
|
125
172
|
}
|
|
126
173
|
|
|
127
174
|
if (options.dryRun) {
|
|
175
|
+
const workspaces = await projectRootsForUpdate(workspace, options);
|
|
176
|
+
const projects = [];
|
|
177
|
+
for (const projectRoot of workspaces) {
|
|
178
|
+
projects.push({
|
|
179
|
+
workspace: projectRoot,
|
|
180
|
+
project: options.noProject ? null : await applyProjectTemplateUpdate(projectRoot, { dryRun: true }),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
128
183
|
return {
|
|
129
184
|
mode: 'dry-run',
|
|
130
185
|
workspace,
|
|
131
186
|
...check,
|
|
132
187
|
installSource: detectInstallSource(),
|
|
133
|
-
|
|
188
|
+
scope: shouldUpdateAllProjects(options) ? 'all' : 'current',
|
|
189
|
+
project: projects.find((item) => item.workspace === workspace)?.project || projects[0]?.project || null,
|
|
190
|
+
projects: shouldUpdateAllProjects(options) ? projects : undefined,
|
|
134
191
|
};
|
|
135
192
|
}
|
|
136
193
|
|
|
@@ -140,8 +197,8 @@ export async function update(options = {}) {
|
|
|
140
197
|
});
|
|
141
198
|
assertCommandOk(installResult, 'npm install -g');
|
|
142
199
|
|
|
143
|
-
const postArgs = [binCommand(), 'post-update', '--workspace', workspace];
|
|
144
|
-
if (options
|
|
200
|
+
const postArgs = [binCommand(), 'post-update', '--workspace', workspace, '--json'];
|
|
201
|
+
if (shouldUpdateAllProjects(options)) postArgs.push('--all');
|
|
145
202
|
if (options.profile) postArgs.push('--profile', options.profile);
|
|
146
203
|
if (options.noProject) postArgs.push('--no-project');
|
|
147
204
|
if (options.codex === false) postArgs.push('--no-codex');
|
|
@@ -150,16 +207,20 @@ export async function update(options = {}) {
|
|
|
150
207
|
timeout: options.timeout || 120000,
|
|
151
208
|
});
|
|
152
209
|
assertCommandOk(postResult, 'post-update');
|
|
210
|
+
const parsedPostUpdate = parseJsonOutput(postResult.stdout);
|
|
153
211
|
|
|
154
212
|
return {
|
|
155
213
|
mode: 'update',
|
|
156
214
|
workspace,
|
|
157
215
|
before: check.installedVersion,
|
|
158
216
|
target,
|
|
217
|
+
scope: shouldUpdateAllProjects(options) ? 'all' : 'current',
|
|
159
218
|
npmInstall: {
|
|
160
219
|
stdout: installResult.stdout.trim(),
|
|
161
220
|
stderr: installResult.stderr.trim(),
|
|
162
221
|
},
|
|
163
|
-
postUpdate: postResult.stdout.trim(),
|
|
222
|
+
postUpdate: parsedPostUpdate || postResult.stdout.trim(),
|
|
223
|
+
project: parsedPostUpdate?.project,
|
|
224
|
+
projects: parsedPostUpdate?.projects,
|
|
164
225
|
};
|
|
165
226
|
}
|
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}"`);
|