@tienne/gestalt 0.106.0 → 0.108.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +1 -0
- package/README.ko.md +1 -0
- package/README.md +1 -0
- package/dist/package.json +1 -1
- package/dist/plugin/role-agents/lens-context/AGENT.md +94 -0
- package/dist/plugin/role-agents/lens-link/AGENT.md +83 -0
- package/dist/plugin/role-agents/lens-reader/AGENT.md +230 -0
- package/dist/plugin/skills/_shared/proactive-routing.md +1 -0
- package/dist/plugin/skills/interview/SKILL.md +47 -8
- package/dist/plugin/skills/lens/SKILL.md +375 -0
- package/dist/plugin/skills/solve/SKILL.md +3 -3
- package/dist/plugin/skills/spec/SKILL.md +4 -4
- package/dist/schemas/gestalt.schema.json +19 -0
- package/dist/src/agent/role-consensus-engine.d.ts.map +1 -1
- package/dist/src/agent/role-consensus-engine.js +1 -1
- package/dist/src/agent/role-consensus-engine.js.map +1 -1
- package/dist/src/agent/role-prompt-generator.d.ts.map +1 -1
- package/dist/src/agent/role-prompt-generator.js +1 -2
- package/dist/src/agent/role-prompt-generator.js.map +1 -1
- package/dist/src/cli/commands/digest.d.ts +2 -0
- package/dist/src/cli/commands/digest.d.ts.map +1 -1
- package/dist/src/cli/commands/digest.js +6 -0
- package/dist/src/cli/commands/digest.js.map +1 -1
- package/dist/src/cli/commands/interview.d.ts +1 -0
- package/dist/src/cli/commands/interview.d.ts.map +1 -1
- package/dist/src/cli/commands/interview.js +5 -3
- package/dist/src/cli/commands/interview.js.map +1 -1
- package/dist/src/cli/commands/lens.d.ts +27 -0
- package/dist/src/cli/commands/lens.d.ts.map +1 -0
- package/dist/src/cli/commands/lens.js +200 -0
- package/dist/src/cli/commands/lens.js.map +1 -0
- package/dist/src/cli/commands/spec.d.ts.map +1 -1
- package/dist/src/cli/commands/spec.js +2 -2
- package/dist/src/cli/commands/spec.js.map +1 -1
- package/dist/src/cli/index.d.ts.map +1 -1
- package/dist/src/cli/index.js +37 -0
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/core/config.d.ts +19 -0
- package/dist/src/core/config.d.ts.map +1 -1
- package/dist/src/core/config.js +23 -15
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/constants.d.ts +5 -0
- package/dist/src/core/constants.d.ts.map +1 -1
- package/dist/src/core/constants.js +5 -0
- package/dist/src/core/constants.js.map +1 -1
- package/dist/src/core/errors.d.ts +3 -1
- package/dist/src/core/errors.d.ts.map +1 -1
- package/dist/src/core/errors.js +6 -2
- package/dist/src/core/errors.js.map +1 -1
- package/dist/src/core/types.d.ts +51 -2
- package/dist/src/core/types.d.ts.map +1 -1
- package/dist/src/digest/format.d.ts.map +1 -1
- package/dist/src/digest/format.js +1 -0
- package/dist/src/digest/format.js.map +1 -1
- package/dist/src/digest/frames.d.ts.map +1 -1
- package/dist/src/digest/frames.js +1 -1
- package/dist/src/digest/frames.js.map +1 -1
- package/dist/src/digest/ingest.d.ts +8 -1
- package/dist/src/digest/ingest.d.ts.map +1 -1
- package/dist/src/digest/ingest.js +2 -1
- package/dist/src/digest/ingest.js.map +1 -1
- package/dist/src/digest/lens-schema.d.ts +542 -0
- package/dist/src/digest/lens-schema.d.ts.map +1 -0
- package/dist/src/digest/lens-schema.js +108 -0
- package/dist/src/digest/lens-schema.js.map +1 -0
- package/dist/src/digest/lens-source.d.ts +260 -0
- package/dist/src/digest/lens-source.d.ts.map +1 -0
- package/dist/src/digest/lens-source.js +92 -0
- package/dist/src/digest/lens-source.js.map +1 -0
- package/dist/src/digest/lens-widget-schema.d.ts +1225 -0
- package/dist/src/digest/lens-widget-schema.d.ts.map +1 -0
- package/dist/src/digest/lens-widget-schema.js +80 -0
- package/dist/src/digest/lens-widget-schema.js.map +1 -0
- package/dist/src/digest/lens-widgets.d.ts +25 -0
- package/dist/src/digest/lens-widgets.d.ts.map +1 -0
- package/dist/src/digest/lens-widgets.js +225 -0
- package/dist/src/digest/lens-widgets.js.map +1 -0
- package/dist/src/digest/lint-lens-context.d.ts +102 -0
- package/dist/src/digest/lint-lens-context.d.ts.map +1 -0
- package/dist/src/digest/lint-lens-context.js +192 -0
- package/dist/src/digest/lint-lens-context.js.map +1 -0
- package/dist/src/digest/lint-lens.d.ts +17 -0
- package/dist/src/digest/lint-lens.d.ts.map +1 -0
- package/dist/src/digest/lint-lens.js +237 -0
- package/dist/src/digest/lint-lens.js.map +1 -0
- package/dist/src/digest/lint.d.ts +14 -0
- package/dist/src/digest/lint.d.ts.map +1 -1
- package/dist/src/digest/lint.js +156 -2
- package/dist/src/digest/lint.js.map +1 -1
- package/dist/src/digest/merge.d.ts.map +1 -1
- package/dist/src/digest/merge.js +11 -2
- package/dist/src/digest/merge.js.map +1 -1
- package/dist/src/digest/prose.d.ts.map +1 -1
- package/dist/src/digest/prose.js +8 -0
- package/dist/src/digest/prose.js.map +1 -1
- package/dist/src/digest/render-html.d.ts +5 -0
- package/dist/src/digest/render-html.d.ts.map +1 -1
- package/dist/src/digest/render-html.js +93 -18
- package/dist/src/digest/render-html.js.map +1 -1
- package/dist/src/digest/render-lens.d.ts +61 -0
- package/dist/src/digest/render-lens.d.ts.map +1 -0
- package/dist/src/digest/render-lens.js +273 -0
- package/dist/src/digest/render-lens.js.map +1 -0
- package/dist/src/digest/render-md.d.ts +5 -0
- package/dist/src/digest/render-md.d.ts.map +1 -1
- package/dist/src/digest/render-md.js +116 -7
- package/dist/src/digest/render-md.js.map +1 -1
- package/dist/src/digest/render-widgets.d.ts +13 -0
- package/dist/src/digest/render-widgets.d.ts.map +1 -0
- package/dist/src/digest/render-widgets.js +197 -0
- package/dist/src/digest/render-widgets.js.map +1 -0
- package/dist/src/digest/render.d.ts +2 -0
- package/dist/src/digest/render.d.ts.map +1 -1
- package/dist/src/digest/render.js +17 -3
- package/dist/src/digest/render.js.map +1 -1
- package/dist/src/digest/schema.d.ts +14683 -2022
- package/dist/src/digest/schema.d.ts.map +1 -1
- package/dist/src/digest/schema.js +67 -6
- package/dist/src/digest/schema.js.map +1 -1
- package/dist/src/digest/server.d.ts +2 -1
- package/dist/src/digest/server.d.ts.map +1 -1
- package/dist/src/digest/server.js +26 -11
- package/dist/src/digest/server.js.map +1 -1
- package/dist/src/digest/viewer-js.d.ts.map +1 -1
- package/dist/src/digest/viewer-js.js +120 -4
- package/dist/src/digest/viewer-js.js.map +1 -1
- package/dist/src/events/types.d.ts +2 -0
- package/dist/src/events/types.d.ts.map +1 -1
- package/dist/src/events/types.js +2 -0
- package/dist/src/events/types.js.map +1 -1
- package/dist/src/execute/orchestrators/evolution.d.ts.map +1 -1
- package/dist/src/execute/orchestrators/evolution.js +4 -0
- package/dist/src/execute/orchestrators/evolution.js.map +1 -1
- package/dist/src/execute/passthrough-engine.d.ts +6 -0
- package/dist/src/execute/passthrough-engine.d.ts.map +1 -1
- package/dist/src/execute/passthrough-engine.js +10 -0
- package/dist/src/execute/passthrough-engine.js.map +1 -1
- package/dist/src/execute/termination-detector.d.ts +3 -0
- package/dist/src/execute/termination-detector.d.ts.map +1 -1
- package/dist/src/execute/termination-detector.js +2 -3
- package/dist/src/execute/termination-detector.js.map +1 -1
- package/dist/src/gestalt/analyzer.d.ts +1 -1
- package/dist/src/gestalt/analyzer.d.ts.map +1 -1
- package/dist/src/gestalt/analyzer.js +3 -2
- package/dist/src/gestalt/analyzer.js.map +1 -1
- package/dist/src/gestalt/signals.d.ts +20 -0
- package/dist/src/gestalt/signals.d.ts.map +1 -0
- package/dist/src/gestalt/signals.js +112 -0
- package/dist/src/gestalt/signals.js.map +1 -0
- package/dist/src/interview/engine.d.ts +9 -1
- package/dist/src/interview/engine.d.ts.map +1 -1
- package/dist/src/interview/engine.js +40 -11
- package/dist/src/interview/engine.js.map +1 -1
- package/dist/src/interview/passthrough-engine.d.ts +31 -7
- package/dist/src/interview/passthrough-engine.d.ts.map +1 -1
- package/dist/src/interview/passthrough-engine.js +87 -28
- package/dist/src/interview/passthrough-engine.js.map +1 -1
- package/dist/src/interview/repository.d.ts +6 -1
- package/dist/src/interview/repository.d.ts.map +1 -1
- package/dist/src/interview/repository.js +20 -2
- package/dist/src/interview/repository.js.map +1 -1
- package/dist/src/interview/rescore.d.ts +35 -0
- package/dist/src/interview/rescore.d.ts.map +1 -0
- package/dist/src/interview/rescore.js +92 -0
- package/dist/src/interview/rescore.js.map +1 -0
- package/dist/src/interview/resolution.d.ts +5 -1
- package/dist/src/interview/resolution.d.ts.map +1 -1
- package/dist/src/interview/resolution.js +15 -4
- package/dist/src/interview/resolution.js.map +1 -1
- package/dist/src/interview/session.d.ts +12 -2
- package/dist/src/interview/session.d.ts.map +1 -1
- package/dist/src/interview/session.js +57 -5
- package/dist/src/interview/session.js.map +1 -1
- package/dist/src/lens/attach.d.ts +436 -0
- package/dist/src/lens/attach.d.ts.map +1 -0
- package/dist/src/lens/attach.js +46 -0
- package/dist/src/lens/attach.js.map +1 -0
- package/dist/src/lens/ingest.d.ts +75 -0
- package/dist/src/lens/ingest.d.ts.map +1 -0
- package/dist/src/lens/ingest.js +558 -0
- package/dist/src/lens/ingest.js.map +1 -0
- package/dist/src/lens/kind.d.ts +11 -0
- package/dist/src/lens/kind.d.ts.map +1 -0
- package/dist/src/lens/kind.js +59 -0
- package/dist/src/lens/kind.js.map +1 -0
- package/dist/src/lens/knowledge.d.ts +53 -0
- package/dist/src/lens/knowledge.d.ts.map +1 -0
- package/dist/src/lens/knowledge.js +202 -0
- package/dist/src/lens/knowledge.js.map +1 -0
- package/dist/src/lens/notes.d.ts +73 -0
- package/dist/src/lens/notes.d.ts.map +1 -0
- package/dist/src/lens/notes.js +276 -0
- package/dist/src/lens/notes.js.map +1 -0
- package/dist/src/lens/panels.d.ts +10 -0
- package/dist/src/lens/panels.d.ts.map +1 -0
- package/dist/src/lens/panels.js +25 -0
- package/dist/src/lens/panels.js.map +1 -0
- package/dist/src/lens/pdf-extract-py.d.ts +15 -0
- package/dist/src/lens/pdf-extract-py.d.ts.map +1 -0
- package/dist/src/lens/pdf-extract-py.js +414 -0
- package/dist/src/lens/pdf-extract-py.js.map +1 -0
- package/dist/src/lens/podcast.d.ts +36 -0
- package/dist/src/lens/podcast.d.ts.map +1 -0
- package/dist/src/lens/podcast.js +104 -0
- package/dist/src/lens/podcast.js.map +1 -0
- package/dist/src/lens/python.d.ts +33 -0
- package/dist/src/lens/python.d.ts.map +1 -0
- package/dist/src/lens/python.js +61 -0
- package/dist/src/lens/python.js.map +1 -0
- package/dist/src/lens/web-extract-py.d.ts +9 -0
- package/dist/src/lens/web-extract-py.d.ts.map +1 -0
- package/dist/src/lens/web-extract-py.js +114 -0
- package/dist/src/lens/web-extract-py.js.map +1 -0
- package/dist/src/llm/prompts.d.ts +8 -0
- package/dist/src/llm/prompts.d.ts.map +1 -1
- package/dist/src/llm/prompts.js +7 -0
- package/dist/src/llm/prompts.js.map +1 -1
- package/dist/src/mcp/schemas.d.ts +29 -20
- package/dist/src/mcp/schemas.d.ts.map +1 -1
- package/dist/src/mcp/schemas.js +15 -3
- package/dist/src/mcp/schemas.js.map +1 -1
- package/dist/src/mcp/server.d.ts.map +1 -1
- package/dist/src/mcp/server.js +11 -5
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/mcp/tools/digest-passthrough.d.ts.map +1 -1
- package/dist/src/mcp/tools/digest-passthrough.js +5 -0
- package/dist/src/mcp/tools/digest-passthrough.js.map +1 -1
- package/dist/src/mcp/tools/execute/evaluate.d.ts.map +1 -1
- package/dist/src/mcp/tools/execute/evaluate.js +3 -3
- package/dist/src/mcp/tools/execute/evaluate.js.map +1 -1
- package/dist/src/mcp/tools/interview-passthrough.d.ts.map +1 -1
- package/dist/src/mcp/tools/interview-passthrough.js +40 -34
- package/dist/src/mcp/tools/interview-passthrough.js.map +1 -1
- package/dist/src/mcp/tools/spec-passthrough.d.ts.map +1 -1
- package/dist/src/mcp/tools/spec-passthrough.js +7 -0
- package/dist/src/mcp/tools/spec-passthrough.js.map +1 -1
- package/dist/src/mcp/tools/status.d.ts.map +1 -1
- package/dist/src/mcp/tools/status.js +6 -1
- package/dist/src/mcp/tools/status.js.map +1 -1
- package/dist/src/research/schema.d.ts +3 -3
- package/dist/src/research/source-create.d.ts +6 -1
- package/dist/src/research/source-create.d.ts.map +1 -1
- package/dist/src/research/source-create.js +41 -10
- package/dist/src/research/source-create.js.map +1 -1
- package/dist/src/spec/extractor.js +1 -1
- package/dist/src/spec/extractor.js.map +1 -1
- package/dist/src/spec/generator.d.ts +3 -1
- package/dist/src/spec/generator.d.ts.map +1 -1
- package/dist/src/spec/generator.js +6 -4
- package/dist/src/spec/generator.js.map +1 -1
- package/dist/src/spec/passthrough-generator.d.ts +3 -1
- package/dist/src/spec/passthrough-generator.d.ts.map +1 -1
- package/dist/src/spec/passthrough-generator.js +6 -4
- package/dist/src/spec/passthrough-generator.js.map +1 -1
- package/dist/src/spec/schema.d.ts +5 -5
- package/dist/src/spec/schema.js +1 -1
- package/dist/src/spec/schema.js.map +1 -1
- package/package.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.mcp.json +1 -1
- package/plugin/commands/gestalt-interview.md +2 -1
- package/plugin/mcp.json +1 -1
- package/plugin/role-agents/lens-context/AGENT.md +94 -0
- package/plugin/role-agents/lens-link/AGENT.md +83 -0
- package/plugin/role-agents/lens-reader/AGENT.md +230 -0
- package/plugin/skills/_shared/proactive-routing.md +1 -0
- package/plugin/skills/interview/SKILL.md +47 -8
- package/plugin/skills/lens/SKILL.md +375 -0
- package/plugin/skills/solve/SKILL.md +3 -3
- package/plugin/skills/spec/SKILL.md +4 -4
- package/schemas/gestalt.schema.json +19 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, realpathSync, statSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { basename, dirname, join, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { writeJsonAtomic } from '../core/json-file.js';
|
|
6
|
+
import { readRaw } from '../digest/store.js';
|
|
7
|
+
/**
|
|
8
|
+
* 사용자 노트 폴더 연결. 노트 폴더는 사람마다 다른 개인 경로라 레포의 gestalt.json이 아니라
|
|
9
|
+
* 사용자 설정(~/.gestalt/lens.json)에 둔다. 한 번 연결하면 어느 레포에서 렌즈를 돌려도 같은 노트를 본다
|
|
10
|
+
*/
|
|
11
|
+
export const userLensFileSchema = z.object({
|
|
12
|
+
knowledgeSources: z.array(z.string()).default([]),
|
|
13
|
+
/** 노션 MCP로도 찾는다. 노션은 파일이 아니라 스킬이 페이지를 받아 스냅샷으로 둔다 */
|
|
14
|
+
notion: z.boolean().default(false),
|
|
15
|
+
/** 연결할 노트 폴더가 없다고 답했다. 다음 실행부터 다시 묻지 않는다 */
|
|
16
|
+
declined: z.boolean().default(false),
|
|
17
|
+
});
|
|
18
|
+
export function userLensFilePath(home = homedir()) {
|
|
19
|
+
return join(home, '.gestalt', 'lens.json');
|
|
20
|
+
}
|
|
21
|
+
export function readUserLens(path = userLensFilePath()) {
|
|
22
|
+
const raw = readRaw(path);
|
|
23
|
+
const empty = { knowledgeSources: [], notion: false, declined: false };
|
|
24
|
+
if (raw.kind !== 'ok')
|
|
25
|
+
return empty;
|
|
26
|
+
const parsed = userLensFileSchema.safeParse(raw.value);
|
|
27
|
+
return parsed.success ? parsed.data : empty;
|
|
28
|
+
}
|
|
29
|
+
function save(path, file) {
|
|
30
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
31
|
+
writeJsonAtomic(path, file);
|
|
32
|
+
}
|
|
33
|
+
/** 폴더를 연결한다. 실제 경로로 바꿔 겹치지 않게 넣고 없는 폴더는 거절한다 */
|
|
34
|
+
export function addNoteSources(dirs, path = userLensFilePath()) {
|
|
35
|
+
const file = readUserLens(path);
|
|
36
|
+
for (const d of dirs) {
|
|
37
|
+
const abs = resolve(d);
|
|
38
|
+
if (!existsSync(abs) || !statSync(abs).isDirectory())
|
|
39
|
+
throw new Error(`폴더가 아니에요: ${d}`);
|
|
40
|
+
const real = realpathSync(abs);
|
|
41
|
+
if (!file.knowledgeSources.includes(real))
|
|
42
|
+
file.knowledgeSources.push(real);
|
|
43
|
+
}
|
|
44
|
+
const next = { ...file, declined: false };
|
|
45
|
+
save(path, next);
|
|
46
|
+
return next;
|
|
47
|
+
}
|
|
48
|
+
export function removeNoteSource(dir, path = userLensFilePath()) {
|
|
49
|
+
const file = readUserLens(path);
|
|
50
|
+
const abs = resolve(dir);
|
|
51
|
+
const real = existsSync(abs) ? realpathSync(abs) : abs;
|
|
52
|
+
const next = {
|
|
53
|
+
...file,
|
|
54
|
+
knowledgeSources: file.knowledgeSources.filter((s) => s !== real && s !== abs),
|
|
55
|
+
};
|
|
56
|
+
save(path, next);
|
|
57
|
+
return next;
|
|
58
|
+
}
|
|
59
|
+
export function setNotion(on, path = userLensFilePath()) {
|
|
60
|
+
const next = { ...readUserLens(path), notion: on, ...(on ? { declined: false } : {}) };
|
|
61
|
+
save(path, next);
|
|
62
|
+
return next;
|
|
63
|
+
}
|
|
64
|
+
export function declineNoteSources(path = userLensFilePath()) {
|
|
65
|
+
const next = { ...readUserLens(path), declined: true };
|
|
66
|
+
save(path, next);
|
|
67
|
+
return next;
|
|
68
|
+
}
|
|
69
|
+
// research는 흔한 이름이라 노트가 아닌 폴더(스킬 자료 같은)까지 걸려서 뺐다
|
|
70
|
+
const NOTE_DIR = /^(notes?|kb|knowledge|knowledge-base|wiki|journal|memos?|til|zettelkasten|vault)$/i;
|
|
71
|
+
const NOTE_EXTS = ['.md', '.markdown', '.txt'];
|
|
72
|
+
/** 노트가 아닌 빌드 산출물과 의존성 폴더. knowledge.ts의 노트 찾기도 같은 목록으로 건너뛴다 */
|
|
73
|
+
export const SKIP_DIRS = new Set([
|
|
74
|
+
'node_modules',
|
|
75
|
+
'dist',
|
|
76
|
+
'build',
|
|
77
|
+
'out',
|
|
78
|
+
'vendor',
|
|
79
|
+
'target',
|
|
80
|
+
'coverage',
|
|
81
|
+
]);
|
|
82
|
+
const NOTE_CAP = 2000;
|
|
83
|
+
const MIN_NOTES = 3;
|
|
84
|
+
function listDirs(dir) {
|
|
85
|
+
try {
|
|
86
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
87
|
+
.filter((e) => e.isDirectory() && !e.name.startsWith('.') && !SKIP_DIRS.has(e.name))
|
|
88
|
+
.map((e) => join(dir, e.name));
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** 노트 파일 수와 최근 노트. 3단 아래까지만 보고 상한을 넘으면 멈추고 멈췄다고 표시한다 */
|
|
95
|
+
function noteStats(dir) {
|
|
96
|
+
const files = [];
|
|
97
|
+
let capped = false;
|
|
98
|
+
const walk = (d, depth) => {
|
|
99
|
+
if (capped || depth > 3)
|
|
100
|
+
return;
|
|
101
|
+
let entries;
|
|
102
|
+
try {
|
|
103
|
+
entries = readdirSync(d, { withFileTypes: true });
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
for (const e of entries) {
|
|
109
|
+
if (capped)
|
|
110
|
+
return;
|
|
111
|
+
if (e.name.startsWith('.') || SKIP_DIRS.has(e.name))
|
|
112
|
+
continue;
|
|
113
|
+
const p = join(d, e.name);
|
|
114
|
+
if (e.isDirectory())
|
|
115
|
+
walk(p, depth + 1);
|
|
116
|
+
else if (e.isFile() && NOTE_EXTS.some((x) => e.name.toLowerCase().endsWith(x))) {
|
|
117
|
+
// 상한을 채운 뒤에 노트가 하나 더 나오면 거기서 멈춘다. 딱 상한만큼 있으면 멈춤 표시를 안 단다
|
|
118
|
+
if (files.length >= NOTE_CAP) {
|
|
119
|
+
capped = true;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
files.push({ name: e.name, mtime: statSync(p).mtimeMs });
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// 읽다 사라진 파일은 건너뛴다
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
walk(dir, 0);
|
|
132
|
+
files.sort((a, b) => b.mtime - a.mtime);
|
|
133
|
+
return {
|
|
134
|
+
notes: files.length,
|
|
135
|
+
...(capped ? { notesCapped: true } : {}),
|
|
136
|
+
recent: files.slice(0, 3).map((f) => f.name),
|
|
137
|
+
updatedAt: files[0]?.mtime ?? 0,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/** 노트 앱이 남기는 표식. 이 표식이 있는 폴더는 이름과 상관없이 노트 폴더로 본다 */
|
|
141
|
+
function appMarker(dir) {
|
|
142
|
+
if (existsSync(join(dir, '.obsidian')))
|
|
143
|
+
return 'obsidian';
|
|
144
|
+
if (existsSync(join(dir, 'logseq', 'config.edn')))
|
|
145
|
+
return 'logseq';
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
// 노션 내보내기 파일은 "제목 <32자리 id>.md" 꼴이다
|
|
149
|
+
const NOTION_EXPORT_FILE = / [0-9a-f]{32}\.md$/i;
|
|
150
|
+
function isNotionExport(dir) {
|
|
151
|
+
try {
|
|
152
|
+
return readdirSync(dir).filter((f) => NOTION_EXPORT_FILE.test(f)).length >= MIN_NOTES;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 한 번 훑을 때 볼 폴더 수 상한. 홈 아래 큰 폴더에서 탐지가 오래 붙잡히지 않게 한다.
|
|
160
|
+
* 바탕 폴더마다 나눠 줘서 앞쪽 폴더가 다 써 버려도 뒤쪽 폴더를 훑는다
|
|
161
|
+
*/
|
|
162
|
+
const VISIT_CAP = 30000;
|
|
163
|
+
/**
|
|
164
|
+
* 폴더를 3단 아래까지 훑어 노트 폴더 후보를 모은다. 이름이 notes, kb, wiki 꼴인 폴더,
|
|
165
|
+
* Obsidian 보관함(.obsidian), Logseq 그래프(logseq/config.edn), 노션 내보내기 폴더를 찾는다.
|
|
166
|
+
* 후보를 찾은 폴더 안으로는 더 내려가지 않는다. 받은 몫(cap)만큼 폴더를 보면 멈추고 stopped로 알린다
|
|
167
|
+
*/
|
|
168
|
+
function scanBase(base, found, cap) {
|
|
169
|
+
let visited = 0;
|
|
170
|
+
let stopped = false;
|
|
171
|
+
const walk = (d, depth) => {
|
|
172
|
+
if (depth > 3 || stopped)
|
|
173
|
+
return;
|
|
174
|
+
for (const sub of listDirs(d)) {
|
|
175
|
+
if (visited >= cap) {
|
|
176
|
+
stopped = true;
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
visited++;
|
|
180
|
+
const marker = appMarker(sub);
|
|
181
|
+
const reason = marker ??
|
|
182
|
+
(NOTE_DIR.test(basename(sub)) ? 'name' : isNotionExport(sub) ? 'notion-export' : null);
|
|
183
|
+
if (reason) {
|
|
184
|
+
try {
|
|
185
|
+
found.set(realpathSync.native(sub), reason);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// 심링크가 깨졌으면 건너뛴다
|
|
189
|
+
}
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
walk(sub, depth + 1);
|
|
193
|
+
if (stopped)
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
const top = appMarker(base);
|
|
198
|
+
if (top)
|
|
199
|
+
found.set(realpathSync.native(base), top);
|
|
200
|
+
else
|
|
201
|
+
walk(base, 0);
|
|
202
|
+
return { visited, stopped };
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* 탐지할 바탕 폴더. 이 레포와 그 옆 레포들(상위 폴더), 넘겨받은 폴더, 홈의 흔한 작업 폴더와 문서 폴더다.
|
|
206
|
+
* Orca가 없어도 이 목록만으로 돈다. Orca가 있으면 스킬이 등록된 레포를 scan으로 더 넘긴다
|
|
207
|
+
*/
|
|
208
|
+
export function detectBases(repoRoot, home, scan = []) {
|
|
209
|
+
const common = [
|
|
210
|
+
'Documents',
|
|
211
|
+
'Desktop',
|
|
212
|
+
'Downloads',
|
|
213
|
+
'notes',
|
|
214
|
+
'Notes',
|
|
215
|
+
'Obsidian',
|
|
216
|
+
'work',
|
|
217
|
+
'projects',
|
|
218
|
+
'code',
|
|
219
|
+
'dev',
|
|
220
|
+
'src',
|
|
221
|
+
'repos',
|
|
222
|
+
'github',
|
|
223
|
+
join('Library', 'Mobile Documents', 'iCloud~md~obsidian', 'Documents'),
|
|
224
|
+
].map((d) => join(home, d));
|
|
225
|
+
const bases = [
|
|
226
|
+
resolve(repoRoot),
|
|
227
|
+
dirname(resolve(repoRoot)),
|
|
228
|
+
...scan.map((s) => resolve(s)),
|
|
229
|
+
...common,
|
|
230
|
+
];
|
|
231
|
+
// macOS는 대소문자를 안 가려서 ~/notes와 ~/Notes가 같은 폴더다. OS가 돌려주는 실제 경로로 겹침을 지운다
|
|
232
|
+
const real = bases.filter((b) => existsSync(b)).map((b) => realpathSync.native(b));
|
|
233
|
+
return [...new Set(real)];
|
|
234
|
+
}
|
|
235
|
+
/** 노션 앱이 깔려 있는지. 깔려 있으면 스킬이 노션 MCP로 찾을지 묻는다 */
|
|
236
|
+
export function notionAppInstalled(home = homedir()) {
|
|
237
|
+
return ['/Applications/Notion.app', join(home, 'Applications', 'Notion.app')].some((p) => existsSync(p));
|
|
238
|
+
}
|
|
239
|
+
const inside = (child, parent) => {
|
|
240
|
+
const rel = relative(parent, child);
|
|
241
|
+
return rel !== '' && !rel.startsWith('..') && !rel.startsWith(sep);
|
|
242
|
+
};
|
|
243
|
+
/**
|
|
244
|
+
* 연결할 만한 노트 폴더를 찾는다. 노트가 3개 넘게 든 폴더만 후보로 내고 바깥 폴더에 든 안쪽 후보는 뺀다.
|
|
245
|
+
* 찾기만 하고 연결은 하지 않는다. 연결은 사용자가 고른 뒤 addNoteSources로 한다
|
|
246
|
+
*/
|
|
247
|
+
export function detectNoteFolders(opts) {
|
|
248
|
+
const home = opts.home ?? homedir();
|
|
249
|
+
const configured = new Set(readUserLens(opts.userFile).knowledgeSources);
|
|
250
|
+
const found = new Map();
|
|
251
|
+
const bases = detectBases(opts.repoRoot, home, opts.scan);
|
|
252
|
+
const partialBases = [];
|
|
253
|
+
// 남은 상한을 남은 바탕 폴더 수로 나눠 준다. 앞 폴더가 덜 쓰고 남긴 몫은 뒤 폴더가 받고 합계는 상한을 안 넘는다
|
|
254
|
+
let left = opts.visitCap ?? VISIT_CAP;
|
|
255
|
+
bases.forEach((base, i) => {
|
|
256
|
+
const { visited, stopped } = scanBase(base, found, Math.floor(left / (bases.length - i)));
|
|
257
|
+
left -= visited;
|
|
258
|
+
if (stopped)
|
|
259
|
+
partialBases.push(base);
|
|
260
|
+
});
|
|
261
|
+
const paths = [...found.keys()];
|
|
262
|
+
const outer = paths.filter((p) => !paths.some((q) => inside(p, q)));
|
|
263
|
+
const out = [];
|
|
264
|
+
for (const p of outer) {
|
|
265
|
+
const s = noteStats(p);
|
|
266
|
+
if (s.notes < MIN_NOTES)
|
|
267
|
+
continue;
|
|
268
|
+
out.push({ path: p, reason: found.get(p), ...s, configured: configured.has(p) });
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
candidates: out.sort((a, b) => b.updatedAt - a.updatedAt).slice(0, 10),
|
|
272
|
+
totalCandidates: out.length,
|
|
273
|
+
partialBases,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=notes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notes.js","sourceRoot":"","sources":["../../../src/lens/notes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 원문 그림의 장면 수를 source.json에 적는다. C 재구성의 칸 수 상한이 이 값이다.
|
|
3
|
+
* 장면 수는 크롭을 본 세션이 정한다. 추출기는 비트맵 한 장에 여러 장면이 든 그림을 못 센다
|
|
4
|
+
*/
|
|
5
|
+
export declare function setFigurePanels(repoRoot: string, slug: string, figureId: string, panels: number): {
|
|
6
|
+
path: string;
|
|
7
|
+
figure: string;
|
|
8
|
+
panels: number;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=panels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panels.d.ts","sourceRoot":"","sources":["../../../src/lens/panels.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAoBlD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { writeJsonAtomic } from '../core/json-file.js';
|
|
2
|
+
import { lensSourcePath, readLensSource } from '../digest/lens-source.js';
|
|
3
|
+
import { DigestStore } from '../digest/store.js';
|
|
4
|
+
/**
|
|
5
|
+
* 원문 그림의 장면 수를 source.json에 적는다. C 재구성의 칸 수 상한이 이 값이다.
|
|
6
|
+
* 장면 수는 크롭을 본 세션이 정한다. 추출기는 비트맵 한 장에 여러 장면이 든 그림을 못 센다
|
|
7
|
+
*/
|
|
8
|
+
export function setFigurePanels(repoRoot, slug, figureId, panels) {
|
|
9
|
+
if (!Number.isInteger(panels) || panels < 1 || panels > 12) {
|
|
10
|
+
throw new Error(`장면 수는 1~12 정수여야 해요: ${panels}`);
|
|
11
|
+
}
|
|
12
|
+
const store = new DigestStore(repoRoot);
|
|
13
|
+
const source = readLensSource(store, slug);
|
|
14
|
+
if (!source)
|
|
15
|
+
throw new Error(`source.json이 없거나 깨졌어요. gestalt lens ingest를 다시 돌려 주세요: ${slug}`);
|
|
16
|
+
const fig = source.figures.find((f) => f.id === figureId);
|
|
17
|
+
if (!fig) {
|
|
18
|
+
throw new Error(`그림 ${figureId}가 없어요. 있는 그림: ${source.figures.map((f) => f.id).join(', ') || '없음'}`);
|
|
19
|
+
}
|
|
20
|
+
fig.panels = panels;
|
|
21
|
+
const path = lensSourcePath(store, slug);
|
|
22
|
+
writeJsonAtomic(path, source);
|
|
23
|
+
return { path, figure: figureId, panels };
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=panels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panels.js","sourceRoot":"","sources":["../../../src/lens/panels.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAgB,EAChB,IAAY,EACZ,QAAgB,EAChB,MAAc;IAEd,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,EAAE,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,uBAAuB,MAAM,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM;QACT,MAAM,IAAI,KAAK,CACb,0DAA0D,IAAI,EAAE,CACjE,CAAC;IACJ,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;IAC1D,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,MAAM,QAAQ,iBAAiB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CACpF,CAAC;IACJ,CAAC;IACD,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;IACpB,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACzC,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF에서 문단과 표, 그림을 좌표와 함께 뽑는 파이썬 스크립트. pdfplumber를 쓴다(pypdfium2로 쪽 그림을 그린다).
|
|
3
|
+
*
|
|
4
|
+
* - 문단: 단을 나눠 읽고 줄 간격과 들여쓰기로 끊는다. 단이나 쪽을 넘는 문단은 사각형을 여럿 단다.
|
|
5
|
+
* 아래첨자 줄은 버리고 수식 조각은 앞 문단에 붙인다.
|
|
6
|
+
* - 표: "Table N:" 캡션과 같은 단에서 가장 가까운 가로줄 묶음(booktabs)을 표 자리로 잡는다.
|
|
7
|
+
* 모든 줄에서 비어 있는 세로 띠를 칸 경계로 본다. 칸이 하나뿐이거나 빈칸이 30%를 넘으면 grid를 loose로 적는다.
|
|
8
|
+
* - 그림: "Figure N:" 캡션 위나 아래의 비트맵을 묶어 자르고 벡터 그림은 캡션 위 빈자리를 자른다.
|
|
9
|
+
*
|
|
10
|
+
* 결과 JSON은 stdout 마지막 줄 하나로 낸다. 쪽 그림과 크롭은 두 번째 인자 폴더의 pages/, figures/, tables/에 쓴다.
|
|
11
|
+
* 세 번째 인자는 쪽 수 상한이다. 넘으면 stderr에 too-many-pages를 남기고 멈춘다.
|
|
12
|
+
* 백틱과 달러 중괄호가 없어야 String.raw 안에 그대로 실린다
|
|
13
|
+
*/
|
|
14
|
+
export declare const PDF_EXTRACT_PY: string;
|
|
15
|
+
//# sourceMappingURL=pdf-extract-py.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-extract-py.d.ts","sourceRoot":"","sources":["../../../src/lens/pdf-extract-py.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,QA+Y1B,CAAC"}
|