stellavault 0.3.0 → 0.4.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.
Files changed (46) hide show
  1. package/Autonomous_Knowledge_Foundry.pdf +0 -0
  2. package/CHANGELOG.md +60 -0
  3. package/README.md +140 -95
  4. package/package.json +1 -1
  5. package/packages/cli/dist/commands/ask-cmd.d.ts +4 -0
  6. package/packages/cli/dist/commands/ask-cmd.js +35 -0
  7. package/packages/cli/dist/commands/autopilot-cmd.d.ts +4 -0
  8. package/packages/cli/dist/commands/autopilot-cmd.js +76 -0
  9. package/packages/cli/dist/commands/compile-cmd.d.ts +6 -0
  10. package/packages/cli/dist/commands/compile-cmd.js +30 -0
  11. package/packages/cli/dist/commands/digest-cmd.d.ts +1 -0
  12. package/packages/cli/dist/commands/digest-cmd.js +57 -0
  13. package/packages/cli/dist/commands/fleeting-cmd.d.ts +4 -0
  14. package/packages/cli/dist/commands/fleeting-cmd.js +45 -0
  15. package/packages/cli/dist/commands/graph-cmd.js +13 -1
  16. package/packages/cli/dist/commands/ingest-cmd.d.ts +9 -0
  17. package/packages/cli/dist/commands/ingest-cmd.js +131 -0
  18. package/packages/cli/dist/commands/init-cmd.js +39 -1
  19. package/packages/cli/dist/commands/lint-cmd.d.ts +2 -0
  20. package/packages/cli/dist/commands/lint-cmd.js +61 -0
  21. package/packages/cli/dist/index.js +46 -1
  22. package/packages/cli/package.json +1 -1
  23. package/packages/core/dist/api/server.js +284 -0
  24. package/packages/core/dist/i18n/note-strings.d.ts +5 -0
  25. package/packages/core/dist/i18n/note-strings.js +94 -0
  26. package/packages/core/dist/index.d.ts +9 -0
  27. package/packages/core/dist/index.js +5 -0
  28. package/packages/core/dist/intelligence/ask-engine.d.ts +23 -0
  29. package/packages/core/dist/intelligence/ask-engine.js +108 -0
  30. package/packages/core/dist/intelligence/ingest-pipeline.d.ts +31 -0
  31. package/packages/core/dist/intelligence/ingest-pipeline.js +192 -0
  32. package/packages/core/dist/intelligence/knowledge-lint.d.ts +27 -0
  33. package/packages/core/dist/intelligence/knowledge-lint.js +132 -0
  34. package/packages/core/dist/intelligence/wiki-compiler.d.ts +30 -0
  35. package/packages/core/dist/intelligence/wiki-compiler.js +222 -0
  36. package/packages/core/dist/intelligence/youtube-extractor.d.ts +29 -0
  37. package/packages/core/dist/intelligence/youtube-extractor.js +311 -0
  38. package/packages/core/dist/intelligence/zettelkasten.d.ts +59 -0
  39. package/packages/core/dist/intelligence/zettelkasten.js +234 -0
  40. package/packages/core/dist/mcp/server.d.ts +2 -0
  41. package/packages/core/dist/mcp/server.js +18 -1
  42. package/packages/core/dist/mcp/tools/agentic-graph.d.ts +6 -0
  43. package/packages/core/dist/mcp/tools/agentic-graph.js +35 -7
  44. package/packages/core/dist/mcp/tools/ask.d.ts +29 -0
  45. package/packages/core/dist/mcp/tools/ask.js +40 -0
  46. package/packages/core/package.json +5 -1
@@ -0,0 +1,45 @@
1
+ // stellavault fleeting — 찰나 메모 즉시 캡처
2
+ // "떠오른 생각을 raw/ 폴더에 즉시 저장"
3
+ import chalk from 'chalk';
4
+ import { loadConfig } from '@stellavault/core';
5
+ import { writeFileSync, mkdirSync, existsSync } from 'node:fs';
6
+ import { join, resolve } from 'node:path';
7
+ export async function fleetingCommand(text, options) {
8
+ if (!text || text.trim().length < 2) {
9
+ console.error(chalk.yellow('Usage: stellavault fleeting "your idea here" [--tags tag1,tag2]'));
10
+ process.exit(1);
11
+ }
12
+ const config = loadConfig();
13
+ const rawDir = resolve(config.vaultPath, 'raw');
14
+ if (!existsSync(rawDir))
15
+ mkdirSync(rawDir, { recursive: true });
16
+ const now = new Date();
17
+ const timestamp = now.toISOString().replace(/[:.]/g, '-').slice(0, 19);
18
+ const slug = text.slice(0, 40).replace(/[^a-zA-Z0-9가-힣\s]/g, '').replace(/\s+/g, '-').toLowerCase();
19
+ const filename = `${timestamp}-${slug}.md`;
20
+ const filePath = join(rawDir, filename);
21
+ // path traversal 방지
22
+ if (!resolve(filePath).startsWith(resolve(rawDir))) {
23
+ console.error(chalk.red('Invalid file path'));
24
+ process.exit(1);
25
+ }
26
+ const tags = options.tags ? options.tags.split(',').map(t => t.trim()) : [];
27
+ const content = [
28
+ '---',
29
+ `title: "${text.slice(0, 80)}"`,
30
+ 'type: fleeting',
31
+ `created: ${now.toISOString()}`,
32
+ `tags: [${tags.map(t => `"${t}"`).join(', ')}]`,
33
+ '---',
34
+ '',
35
+ text,
36
+ '',
37
+ '---',
38
+ `*Captured via \`stellavault fleeting\` at ${now.toLocaleString('ko-KR')}*`,
39
+ ].join('\n');
40
+ writeFileSync(filePath, content, 'utf-8');
41
+ console.log(chalk.green(`Captured: ${filename}`));
42
+ console.log(chalk.dim(`Location: raw/${filename}`));
43
+ console.log(chalk.dim('Run `stellavault compile` to process into wiki.'));
44
+ }
45
+ //# sourceMappingURL=fleeting-cmd.js.map
@@ -27,8 +27,20 @@ export async function graphCommand() {
27
27
  searchEngine: hub.searchEngine,
28
28
  port,
29
29
  vaultName,
30
+ vaultPath: config.vaultPath,
30
31
  });
31
- await api.start();
32
+ try {
33
+ await api.start();
34
+ }
35
+ catch (err) {
36
+ if (err?.code === 'EADDRINUSE') {
37
+ console.error(chalk.red(`Port ${port} is already in use.`));
38
+ console.error(chalk.dim(`Stop the other process or use a different port:`));
39
+ console.error(chalk.dim(` Edit .stellavault.json: { "mcp": { "port": ${port + 1} } }`));
40
+ process.exit(1);
41
+ }
42
+ throw err;
43
+ }
32
44
  console.error(chalk.green('🧠 Stellavault — Neural Knowledge Graph'));
33
45
  console.error(` 📚 ${stats.documentCount} documents | ${stats.chunkCount} chunks`);
34
46
  console.error(` 🌐 API: http://127.0.0.1:${port}`);
@@ -0,0 +1,9 @@
1
+ export declare function ingestCommand(input: string, options: {
2
+ tags?: string;
3
+ stage?: string;
4
+ title?: string;
5
+ }): Promise<void>;
6
+ export declare function promoteCommand(filePath: string, options: {
7
+ to: string;
8
+ }): Promise<void>;
9
+ //# sourceMappingURL=ingest-cmd.d.ts.map
@@ -0,0 +1,131 @@
1
+ // stellavault ingest — 통합 인제스트 (URL, 텍스트, 파일 → 자동 분류 저장)
2
+ import chalk from 'chalk';
3
+ import { loadConfig, ingest, promoteNote } from '@stellavault/core';
4
+ import { readFileSync, existsSync } from 'node:fs';
5
+ import { extname } from 'node:path';
6
+ export async function ingestCommand(input, options) {
7
+ if (!input) {
8
+ console.error(chalk.yellow('Usage: stellavault ingest <url|file|text> [--tags t1,t2] [--stage fleeting|literature|permanent]'));
9
+ process.exit(1);
10
+ }
11
+ const config = loadConfig();
12
+ const tags = options.tags?.split(',').map(t => t.trim()) ?? [];
13
+ const stage = (options.stage ?? 'fleeting');
14
+ // 입력 타입 감지
15
+ let ingestInput;
16
+ if (/^https?:\/\//.test(input)) {
17
+ // URL
18
+ const isYouTube = /youtube\.com\/watch|youtu\.be\//.test(input);
19
+ // SSRF 방지: private IP 차단
20
+ try {
21
+ const url = new URL(input);
22
+ const host = url.hostname;
23
+ if (/^(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|0\.|localhost|::1)/i.test(host)) {
24
+ console.error(chalk.yellow('Private/local URLs are not allowed for security.'));
25
+ process.exit(1);
26
+ }
27
+ }
28
+ catch { /* invalid URL, will fail at fetch */ }
29
+ if (isYouTube) {
30
+ // YouTube: 전용 추출기 사용 (메타데이터 + 자막 + 타임스탬프)
31
+ try {
32
+ const { extractYouTubeContent, formatYouTubeNote } = await import('@stellavault/core/intelligence/youtube-extractor');
33
+ const ytContent = await extractYouTubeContent(input);
34
+ const body = formatYouTubeNote(ytContent);
35
+ ingestInput = {
36
+ type: 'youtube',
37
+ content: input + '\n' + body,
38
+ tags: [...tags, ...ytContent.tags.filter((t) => !tags.includes(t))],
39
+ stage: stage === 'fleeting' ? 'literature' : stage, // YouTube는 literature로 자동 승격
40
+ title: options.title ?? ytContent.title,
41
+ source: input,
42
+ };
43
+ }
44
+ catch (err) {
45
+ console.error(chalk.yellow(`YouTube extraction failed, falling back to basic URL. (${err instanceof Error ? err.message : 'error'})`));
46
+ ingestInput = {
47
+ type: 'youtube',
48
+ content: input + '\n',
49
+ tags,
50
+ stage,
51
+ title: options.title,
52
+ source: input,
53
+ };
54
+ }
55
+ }
56
+ else {
57
+ // 일반 URL: HTML → 텍스트 변환
58
+ let content = input + '\n';
59
+ try {
60
+ const resp = await fetch(input);
61
+ const html = await resp.text();
62
+ const text = html
63
+ .replace(/<script[\s\S]*?<\/script>/gi, '')
64
+ .replace(/<style[\s\S]*?<\/style>/gi, '')
65
+ .replace(/<[^>]+>/g, ' ')
66
+ .replace(/&nbsp;/g, ' ')
67
+ .replace(/\s+/g, ' ')
68
+ .trim()
69
+ .slice(0, 5000);
70
+ content += text;
71
+ }
72
+ catch (err) {
73
+ console.error(chalk.yellow(`Web fetch failed: saving URL only. (${err instanceof Error ? err.message : 'network error'})`));
74
+ }
75
+ ingestInput = {
76
+ type: 'url',
77
+ content,
78
+ tags,
79
+ stage,
80
+ title: options.title,
81
+ source: input,
82
+ };
83
+ }
84
+ }
85
+ else if (existsSync(input)) {
86
+ // 파일
87
+ const ext = extname(input).toLowerCase();
88
+ const fileContent = readFileSync(input, 'utf-8');
89
+ ingestInput = {
90
+ type: ext === '.pdf' ? 'pdf-text' : 'file',
91
+ content: fileContent,
92
+ tags,
93
+ stage,
94
+ title: options.title,
95
+ source: input,
96
+ };
97
+ }
98
+ else {
99
+ // 플레인 텍스트
100
+ ingestInput = {
101
+ type: 'text',
102
+ content: input,
103
+ tags,
104
+ stage,
105
+ title: options.title,
106
+ };
107
+ }
108
+ const result = ingest(config.vaultPath, ingestInput);
109
+ console.log(chalk.green(`Ingested: ${result.title}`));
110
+ console.log(chalk.dim(` Stage: ${result.stage}`));
111
+ console.log(chalk.dim(` Saved: ${result.savedTo}`));
112
+ console.log(chalk.dim(` Words: ${result.wordCount}`));
113
+ if (result.indexCode)
114
+ console.log(chalk.dim(` Index: ${result.indexCode}`));
115
+ if (result.tags.length > 0)
116
+ console.log(chalk.dim(` Tags: ${result.tags.join(', ')}`));
117
+ console.log('');
118
+ console.log(chalk.dim('Run `stellavault compile` to process into wiki.'));
119
+ console.log(chalk.dim('Run `stellavault autopilot` for full pipeline.'));
120
+ }
121
+ export async function promoteCommand(filePath, options) {
122
+ const config = loadConfig();
123
+ const target = options.to;
124
+ if (!['fleeting', 'literature', 'permanent'].includes(target)) {
125
+ console.error(chalk.red('--to must be: fleeting, literature, or permanent'));
126
+ process.exit(1);
127
+ }
128
+ const newPath = promoteNote(config.vaultPath, filePath, target);
129
+ console.log(chalk.green(`Promoted to ${target}: ${newPath}`));
130
+ }
131
+ //# sourceMappingURL=ingest-cmd.js.map
@@ -56,7 +56,7 @@ export async function initCommand() {
56
56
  console.log('');
57
57
  console.log(chalk.cyan(' Step 2/3') + ' — Indexing your vault');
58
58
  console.log(chalk.dim(' Vectorizing notes with local AI (no data leaves your machine).\n'));
59
- const spinner = ora({ text: ' Loading embedding model...', indent: 2 }).start();
59
+ const spinner = ora({ text: ' Loading embedding model (first run downloads ~30MB, please wait)...', indent: 2 }).start();
60
60
  const store = createSqliteVecStore(dbPath);
61
61
  await store.initialize();
62
62
  const embedder = createLocalEmbedder('all-MiniLM-L6-v2');
@@ -113,6 +113,44 @@ export async function initCommand() {
113
113
  console.log(` ${chalk.cyan('stellavault brief')} Get your daily knowledge briefing`);
114
114
  console.log(` ${chalk.cyan('stellavault serve')} Connect AI agents via MCP`);
115
115
  console.log('');
116
+ // Day 2 Experience — 습관화 제안
117
+ console.log(chalk.cyan(' ─── Build a habit ───\n'));
118
+ const setupCron = await ask(rl, ' Auto-run daily briefing? (adds cron job) [Y/n]', 'Y');
119
+ if (setupCron.toLowerCase() !== 'n') {
120
+ const cronLine = `0 9 * * * cd "${vaultPath}" && stellavault brief >> ~/.stellavault/daily.log 2>&1`;
121
+ const platform = process.platform;
122
+ if (platform === 'win32') {
123
+ console.log(chalk.dim('\n Windows: Add this to Task Scheduler:'));
124
+ console.log(chalk.dim(` Action: stellavault brief`));
125
+ console.log(chalk.dim(` Trigger: Daily at 9:00 AM`));
126
+ console.log(chalk.dim(` Start in: ${vaultPath}\n`));
127
+ }
128
+ else {
129
+ console.log(chalk.dim('\n Add this to your crontab (crontab -e):'));
130
+ console.log(chalk.dim(` ${cronLine}\n`));
131
+ const autoAdd = await ask(rl, ' Add to crontab now? [Y/n]', 'Y');
132
+ if (autoAdd.toLowerCase() !== 'n') {
133
+ try {
134
+ const { execSync } = await import('node:child_process');
135
+ const existing = execSync('crontab -l 2>/dev/null || true', { encoding: 'utf-8' });
136
+ if (!existing.includes('stellavault brief')) {
137
+ execSync(`(crontab -l 2>/dev/null; echo "${cronLine}") | crontab -`, { encoding: 'utf-8' });
138
+ console.log(chalk.green(' ✓ Daily briefing scheduled at 9:00 AM\n'));
139
+ }
140
+ else {
141
+ console.log(chalk.dim(' Already scheduled.\n'));
142
+ }
143
+ }
144
+ catch {
145
+ console.log(chalk.yellow(' Could not auto-add. Please add manually.\n'));
146
+ }
147
+ }
148
+ }
149
+ }
150
+ console.log(chalk.dim(' Tomorrow morning, run:'));
151
+ console.log(` ${chalk.cyan('stellavault brief')} See what changed overnight`);
152
+ console.log(` ${chalk.cyan('stellavault decay')} Review fading knowledge`);
153
+ console.log('');
116
154
  console.log(chalk.dim(' Your knowledge is now alive. ✦'));
117
155
  console.log('');
118
156
  }
@@ -0,0 +1,2 @@
1
+ export declare function lintCommand(): Promise<void>;
2
+ //# sourceMappingURL=lint-cmd.d.ts.map
@@ -0,0 +1,61 @@
1
+ // stellavault lint — 지식 건강 검사 CLI
2
+ import chalk from 'chalk';
3
+ import { loadConfig, createKnowledgeHub, lintKnowledge } from '@stellavault/core';
4
+ export async function lintCommand() {
5
+ const config = loadConfig();
6
+ const hub = createKnowledgeHub(config);
7
+ console.error(chalk.dim('Scanning your knowledge base...'));
8
+ await hub.store.initialize();
9
+ const result = await lintKnowledge(hub.store);
10
+ // 건강도 점수
11
+ const scoreColor = result.score >= 80 ? chalk.green : result.score >= 50 ? chalk.yellow : chalk.red;
12
+ console.log('');
13
+ console.log(chalk.bold('Knowledge Health Report'));
14
+ console.log('─'.repeat(40));
15
+ console.log(`Score: ${scoreColor(result.score + '/100')}`);
16
+ console.log(`Documents: ${result.stats.totalDocs}`);
17
+ console.log('');
18
+ // 이슈
19
+ if (result.issues.length > 0) {
20
+ const critical = result.issues.filter(i => i.severity === 'critical');
21
+ const warnings = result.issues.filter(i => i.severity === 'warning');
22
+ const info = result.issues.filter(i => i.severity === 'info');
23
+ if (critical.length > 0) {
24
+ console.log(chalk.red(`Critical: ${critical.length}`));
25
+ for (const i of critical) {
26
+ console.log(chalk.red(` ✗ ${i.message}`));
27
+ if (i.suggestion)
28
+ console.log(chalk.dim(` → ${i.suggestion}`));
29
+ }
30
+ console.log('');
31
+ }
32
+ if (warnings.length > 0) {
33
+ console.log(chalk.yellow(`Warnings: ${warnings.length}`));
34
+ for (const i of warnings.slice(0, 10)) {
35
+ console.log(chalk.yellow(` ! ${i.message}`));
36
+ if (i.suggestion)
37
+ console.log(chalk.dim(` → ${i.suggestion}`));
38
+ }
39
+ if (warnings.length > 10)
40
+ console.log(chalk.dim(` ... and ${warnings.length - 10} more`));
41
+ console.log('');
42
+ }
43
+ if (info.length > 0) {
44
+ console.log(chalk.dim(`Info: ${info.length}`));
45
+ for (const i of info.slice(0, 5)) {
46
+ console.log(chalk.dim(` ℹ ${i.message}`));
47
+ }
48
+ console.log('');
49
+ }
50
+ }
51
+ // 제안
52
+ if (result.suggestions.length > 0) {
53
+ console.log(chalk.cyan('Suggestions:'));
54
+ for (const s of result.suggestions) {
55
+ console.log(` → ${s}`);
56
+ }
57
+ }
58
+ console.log('');
59
+ await hub.store.close?.();
60
+ }
61
+ //# sourceMappingURL=lint-cmd.js.map
@@ -21,11 +21,17 @@ import { federateJoinCommand, federateStatusCommand } from './commands/federate-
21
21
  import { cloudSyncCommand, cloudRestoreCommand, cloudStatusCommand } from './commands/cloud-cmd.js';
22
22
  import { vaultAddCommand, vaultListCommand, vaultRemoveCommand, vaultSearchAllCommand } from './commands/vault-cmd.js';
23
23
  import { captureCommand } from './commands/capture-cmd.js';
24
+ import { askCommand } from './commands/ask-cmd.js';
25
+ import { compileCommand } from './commands/compile-cmd.js';
26
+ import { lintCommand } from './commands/lint-cmd.js';
27
+ import { fleetingCommand } from './commands/fleeting-cmd.js';
28
+ import { ingestCommand, promoteCommand } from './commands/ingest-cmd.js';
29
+ import { autopilotCommand } from './commands/autopilot-cmd.js';
24
30
  const program = new Command();
25
31
  program
26
32
  .name('stellavault')
27
33
  .description('Stellavault — Turn your Obsidian vault into a 3D neural knowledge graph')
28
- .version('0.1.0')
34
+ .version('0.3.0')
29
35
  .option('--json', 'Output in JSON format (for scripting)')
30
36
  .option('--quiet', 'Suppress non-essential output');
31
37
  program
@@ -78,6 +84,7 @@ program
78
84
  .command('digest')
79
85
  .description('주간 지식 활동 리포트')
80
86
  .option('-d, --days <n>', '기간 (일)', '7')
87
+ .option('-v, --visual', 'Save as .md with Mermaid charts for Obsidian')
81
88
  .action(digestCommand);
82
89
  program
83
90
  .command('clip <url>')
@@ -120,6 +127,44 @@ program
120
127
  .option('-t, --tags <tags>', 'Comma-separated tags')
121
128
  .option('-f, --folder <folder>', 'Vault subfolder', '01_Knowledge/voice')
122
129
  .action(captureCommand);
130
+ program
131
+ .command('ask <question>')
132
+ .description('Ask a question about your knowledge base — search, compose answer, optionally save')
133
+ .option('-s, --save', 'Save answer as a new note in your vault')
134
+ .action((question, opts) => askCommand(question, opts));
135
+ program
136
+ .command('compile')
137
+ .description('Compile raw/ documents into a structured wiki')
138
+ .option('-r, --raw <dir>', 'Raw documents directory (default: raw/)')
139
+ .option('-w, --wiki <dir>', 'Wiki output directory (default: _wiki/)')
140
+ .option('-f, --force', 'Overwrite existing wiki files')
141
+ .action((opts) => compileCommand(opts));
142
+ program
143
+ .command('lint')
144
+ .description('Knowledge health check — find gaps, duplicates, contradictions, stale notes')
145
+ .action(() => lintCommand());
146
+ program
147
+ .command('fleeting <text>')
148
+ .description('Capture a fleeting idea instantly to raw/ folder')
149
+ .option('-t, --tags <tags>', 'Comma-separated tags')
150
+ .action((text, opts) => fleetingCommand(text, opts));
151
+ program
152
+ .command('ingest <input>')
153
+ .description('Ingest any input (URL, file, text) into your knowledge base')
154
+ .option('-t, --tags <tags>', 'Comma-separated tags')
155
+ .option('-s, --stage <stage>', 'Note stage: fleeting, literature, permanent', 'fleeting')
156
+ .option('--title <title>', 'Override title')
157
+ .action((input, opts) => ingestCommand(input, opts));
158
+ program
159
+ .command('promote <file>')
160
+ .description('Promote a note: fleeting → literature → permanent')
161
+ .requiredOption('--to <stage>', 'Target stage: literature or permanent')
162
+ .action((file, opts) => promoteCommand(file, opts));
163
+ program
164
+ .command('autopilot')
165
+ .description('Run the full knowledge flywheel: inbox → compile → lint')
166
+ .option('--once', 'Run once (default)')
167
+ .action((opts) => autopilotCommand(opts));
123
168
  const vault = program.command('vault').description('Multi-Vault — manage and search across vaults');
124
169
  vault.command('add <id> <path>').description('Register a vault').option('-n, --name <name>', 'Display name').option('-s, --shared', 'Allow federation sharing').action(vaultAddCommand);
125
170
  vault.command('list').description('List registered vaults').action(vaultListCommand);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stellavault/cli",
3
- "version": "0.1.0",
3
+ "version": "0.4.0",
4
4
  "description": "Stellavault CLI — stellavault index, search, serve, graph",
5
5
  "type": "module",
6
6
  "bin": {