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/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 exists(join(dir, 'llm-wiki', '.kit-state.json'))) {
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() || SKIP_DIRS.has(entry.name)) continue;
77
- await walk(join(dir, entry.name));
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 exists(join(root, 'llm-wiki', '.kit-state.json'))) {
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 `\n<!-- llm-wiki-kit:start -->\n## LLM Wiki Policy\n\nThis repository uses llm-wiki-kit as a hook-first living Markdown wiki for Codex and Claude Code.\n\n- This block supersedes older OMX/OMC/\`omx_wiki/\` LLM Wiki instructions for this repository.\n- Treat chat memory as temporary; durable project knowledge belongs in \`llm-wiki/\`.\n- \`llm-wiki/raw/\` stores immutable or redacted source material. Do not edit raw source files.\n- \`llm-wiki/wiki/\` stores LLM-maintained knowledge pages such as decisions, architecture, debugging, context, concepts, and queries.\n- \`llm-wiki/wiki/memory.md\` is the short hot index for the most reusable current facts; keep it concise and link to deeper pages.\n- Before non-trivial work, use the injected LLM Wiki context and consult \`llm-wiki/wiki/memory.md\` and \`llm-wiki/wiki/index.md\` when present.\n- Capture reusable decisions, debugging findings, verification commands, and open questions into the wiki.\n- Preserve useful work context, but do not store raw authentication values such as tokens, passwords, or private keys.\n- Mark inference explicitly and preserve contradictions instead of silently overwriting them.\n\n<!-- llm-wiki-kit:end -->\n`;
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
- Maintain a living Markdown LLM Wiki from immutable source files and redacted Codex/Claude Code session events.
14
- These rules supersede older OMX/OMC/\`omx_wiki/\` LLM Wiki rules for this project.
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/\`: immutable or redacted source material. Never edit original source captures.
18
- - \`wiki/\`: AI-maintained knowledge pages. \`wiki/memory.md\` is the short hot index injected into hook context.
19
- - \`outputs/\`: live Q&A summaries, reports, and generated briefs.
20
- - \`procedures/\`: detailed operating rules for ingest, query, lint, and security.
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
- - Never modify \`raw/\` source material except to append redacted session envelopes generated by hooks.
24
- - Do not state unsupported claims as facts.
25
- - Mark inference explicitly.
26
- - Add \`source_ids\` or file references for important claims.
27
- - Update \`wiki/memory.md\`, \`wiki/index.md\`, and \`wiki/log.md\` whenever new durable knowledge changes the active project map.
28
- - Preserve contradictions in \`Contradictions\` or \`Open Questions\` instead of overwriting them.
29
- - Preserve useful work context and redact authentication values before writing durable notes.
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: 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\`.
52
- - query: start from \`llm-wiki context "<query>"\` or \`wiki/memory.md\`, read relevant wiki pages, answer with source references, and save reusable answers.
53
- - 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.
54
- - 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.
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\n\nGenerated by llm-wiki-kit.\n\n## Overview\n\nThis is the navigation map for the project living wiki. Keep it short and useful.\n\n## Main Areas\n\n- [Memory](memory.md) - short hot index and durable entry points.\n- [Sources](sources/) - source summaries and source IDs.\n- [Concepts](concepts/) - reusable concepts and terminology.\n- [Entities](entities/) - people, systems, modules, vendors, services, and tools.\n- [Decisions](decisions/) - decisions and rationale.\n- [Architecture](architecture/) - system structure and data/control flow.\n- [Debugging](debugging/) - root causes, fixes, verification evidence.\n- [Context](context/) - session continuity and project memory.\n- [Queries](queries/) - durable answers to useful questions.\n\n## Operating Notes\n\n- Start from memory and this index before broad project questions.\n- Read 3-7 relevant pages first; inspect raw sources only when precision matters.\n- Add links using \`[[page-or-topic]]\` when pages become related.\n\n<!-- llm-wiki-kit:index-start -->\n## Generated Page Map\n\nRun \`llm-wiki consolidate --workspace <project>\` to refresh this generated block.\n<!-- llm-wiki-kit:index-end -->\n`;
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
- Short hot index for project memory. Keep this page compact enough for hook context. Link to deeper pages instead of copying long notes.
134
+ 짧은 핵심 기억이다. hook context에 들어갈 만큼 작게 유지하고, 설명을 복사하지 말고 깊은 문서로 링크한다.
80
135
 
81
136
  ## Current Focus
82
137
 
83
- - Add the current project focus here when it becomes durable.
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
- Run \`llm-wiki consolidate --workspace <project>\` to refresh this generated block.
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\n\nAppend-only operating history for this project wiki.\n`;
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\n\n1. Read \`wiki/memory.md\` and \`wiki/index.md\` first.\n2. Inspect new material under \`raw/inbox/\` or \`raw/sources/\`.\n3. Create or update \`wiki/sources/<slug>.md\` for each source.\n4. Update related concept, entity, decision, architecture, debugging, or context pages.\n5. Prefer updating existing pages over creating duplicates.\n6. Add source references, confidence, memory type, importance, and verification status.\n7. Update \`wiki/memory.md\` or run \`llm-wiki consolidate\` when durable entry points change.\n8. Update \`wiki/index.md\` and append to \`wiki/log.md\`.\n`,
105
- 'query.md': `# Query Procedure\n\n1. Start from \`llm-wiki context \"<query>\"\` or read \`wiki/memory.md\` and \`wiki/index.md\`.\n2. Use \`--limit\` or \`--no-expand\` when you need a narrower context pack.\n3. Search \`wiki/\` for relevant pages.\n4. Read the smallest useful set of pages first.\n5. Use raw sources only when exact evidence matters.\n6. Separate verified facts from inference.\n7. Save reusable answers into \`wiki/queries/\` and link them from related pages.\n`,
106
- 'lint.md': `# Lint Procedure\n\nRun \`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.\n`,
107
- 'security.md': `# Security Procedure\n\n- Preserve useful work context for the local project wiki.\n- Do not block reads or tool calls only because they look sensitive.\n- Redact authentication values such as tokens, passwords, bearer credentials, and private keys before writing hook payloads, summaries, or context packs.\n- Run \`llm-wiki lint --workspace <project>\` when sensitive material might have entered wiki pages; secret-like content is reported as an error.\n- Full raw transcript capture is disabled by default and must be explicitly enabled by project policy.\n`,
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\nraw/sessions/*.jsonl\nraw/sessions/*.ndjson\nraw/private/\nraw_private/\nsecrets/\n*.key\n*.pem\n*.p12\n*.pfx\n.env\n.env.*\n.cache/\n`;
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 !== installedVersion,
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.noProject
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.noProject
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
- project: options.noProject ? null : await applyProjectTemplateUpdate(workspace, { dryRun: true }),
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.all) postArgs.push('--all');
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
 
@@ -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}"`);