@cad0p/napkin 0.8.1
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/LICENSE +21 -0
- package/README.md +342 -0
- package/dist/commands/aliases.d.ts +7 -0
- package/dist/commands/aliases.js +25 -0
- package/dist/commands/bases.d.ts +23 -0
- package/dist/commands/bases.js +139 -0
- package/dist/commands/bookmarks.d.ts +15 -0
- package/dist/commands/bookmarks.js +51 -0
- package/dist/commands/canvas.d.ts +49 -0
- package/dist/commands/canvas.js +186 -0
- package/dist/commands/config.d.ts +13 -0
- package/dist/commands/config.js +48 -0
- package/dist/commands/crud.d.ts +40 -0
- package/dist/commands/crud.js +195 -0
- package/dist/commands/daily.d.ts +20 -0
- package/dist/commands/daily.js +58 -0
- package/dist/commands/files.d.ts +23 -0
- package/dist/commands/files.js +132 -0
- package/dist/commands/graph.d.ts +4 -0
- package/dist/commands/graph.js +461 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.js +52 -0
- package/dist/commands/links.d.ts +26 -0
- package/dist/commands/links.js +119 -0
- package/dist/commands/outline.d.ts +7 -0
- package/dist/commands/outline.js +48 -0
- package/dist/commands/overview.d.ts +6 -0
- package/dist/commands/overview.js +40 -0
- package/dist/commands/properties.d.ts +24 -0
- package/dist/commands/properties.js +115 -0
- package/dist/commands/search.d.ts +13 -0
- package/dist/commands/search.js +48 -0
- package/dist/commands/tags.d.ts +13 -0
- package/dist/commands/tags.js +51 -0
- package/dist/commands/tasks.d.ts +22 -0
- package/dist/commands/tasks.js +106 -0
- package/dist/commands/templates.d.ts +16 -0
- package/dist/commands/templates.js +70 -0
- package/dist/commands/vault.d.ts +4 -0
- package/dist/commands/vault.js +17 -0
- package/dist/commands/wordcount.d.ts +7 -0
- package/dist/commands/wordcount.js +43 -0
- package/dist/core/aliases.d.ts +5 -0
- package/dist/core/aliases.js +26 -0
- package/dist/core/bases.d.ts +29 -0
- package/dist/core/bases.js +67 -0
- package/dist/core/bookmarks.d.ts +14 -0
- package/dist/core/bookmarks.js +34 -0
- package/dist/core/canvas.d.ts +74 -0
- package/dist/core/canvas.js +125 -0
- package/dist/core/config.d.ts +7 -0
- package/dist/core/config.js +35 -0
- package/dist/core/crud.d.ts +32 -0
- package/dist/core/crud.js +119 -0
- package/dist/core/daily.d.ts +12 -0
- package/dist/core/daily.js +102 -0
- package/dist/core/files.d.ts +15 -0
- package/dist/core/files.js +30 -0
- package/dist/core/init.d.ts +31 -0
- package/dist/core/init.js +119 -0
- package/dist/core/links.d.ts +11 -0
- package/dist/core/links.js +66 -0
- package/dist/core/outline.d.ts +3 -0
- package/dist/core/outline.js +12 -0
- package/dist/core/overview.d.ts +15 -0
- package/dist/core/overview.js +384 -0
- package/dist/core/properties.d.ts +14 -0
- package/dist/core/properties.js +60 -0
- package/dist/core/search.d.ts +17 -0
- package/dist/core/search.js +153 -0
- package/dist/core/tags.d.ts +11 -0
- package/dist/core/tags.js +40 -0
- package/dist/core/tasks.d.ts +35 -0
- package/dist/core/tasks.js +97 -0
- package/dist/core/templates.d.ts +14 -0
- package/dist/core/templates.js +55 -0
- package/dist/core/vault.d.ts +10 -0
- package/dist/core/vault.js +37 -0
- package/dist/core/wordcount.d.ts +5 -0
- package/dist/core/wordcount.js +16 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +715 -0
- package/dist/sdk.d.ts +179 -0
- package/dist/sdk.js +232 -0
- package/dist/templates/coding.d.ts +2 -0
- package/dist/templates/coding.js +104 -0
- package/dist/templates/company.d.ts +2 -0
- package/dist/templates/company.js +121 -0
- package/dist/templates/index.d.ts +4 -0
- package/dist/templates/index.js +15 -0
- package/dist/templates/personal.d.ts +2 -0
- package/dist/templates/personal.js +91 -0
- package/dist/templates/product.d.ts +2 -0
- package/dist/templates/product.js +123 -0
- package/dist/templates/research.d.ts +2 -0
- package/dist/templates/research.js +114 -0
- package/dist/templates/types.d.ts +7 -0
- package/dist/templates/types.js +1 -0
- package/dist/utils/bases.d.ts +61 -0
- package/dist/utils/bases.js +661 -0
- package/dist/utils/config.d.ts +42 -0
- package/dist/utils/config.js +112 -0
- package/dist/utils/exit-codes.d.ts +5 -0
- package/dist/utils/exit-codes.js +5 -0
- package/dist/utils/files.d.ts +135 -0
- package/dist/utils/files.js +299 -0
- package/dist/utils/formula.d.ts +28 -0
- package/dist/utils/formula.js +462 -0
- package/dist/utils/frontmatter.d.ts +17 -0
- package/dist/utils/frontmatter.js +34 -0
- package/dist/utils/markdown.d.ts +31 -0
- package/dist/utils/markdown.js +80 -0
- package/dist/utils/output.d.ts +28 -0
- package/dist/utils/output.js +48 -0
- package/dist/utils/search-cache.d.ts +29 -0
- package/dist/utils/search-cache.js +41 -0
- package/dist/utils/test-helpers.d.ts +13 -0
- package/dist/utils/test-helpers.js +40 -0
- package/dist/utils/vault.d.ts +21 -0
- package/dist/utils/vault.js +144 -0
- package/package.json +76 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
export const success = (msg) => console.log(chalk.green("✓"), msg);
|
|
3
|
+
export const info = (msg) => console.log(chalk.blue("ℹ"), msg);
|
|
4
|
+
export const warn = (msg) => console.log(chalk.yellow("⚠"), msg);
|
|
5
|
+
export const error = (msg) => console.error(chalk.red("✗"), msg);
|
|
6
|
+
export const errorWithHint = (msg, hint) => {
|
|
7
|
+
console.error(chalk.red("✗"), msg);
|
|
8
|
+
console.error(chalk.dim(` ${hint}`));
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Standard "file not found" error with suggestions.
|
|
12
|
+
* Import suggestFile where needed and pass results here.
|
|
13
|
+
*/
|
|
14
|
+
export function fileNotFound(ref, suggestions) {
|
|
15
|
+
error(`File not found: ${ref}`);
|
|
16
|
+
if (suggestions && suggestions.length > 0) {
|
|
17
|
+
console.error(chalk.dim(` Did you mean: ${suggestions.join(", ")}?`));
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
console.error(chalk.dim(" Run napkin file list to see all files."));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export const bold = (s) => chalk.bold(s);
|
|
24
|
+
export const dim = (s) => chalk.dim(s);
|
|
25
|
+
export const cmd = (s) => chalk.cyan(s);
|
|
26
|
+
export const bullet = (msg) => console.log(chalk.green("●"), msg);
|
|
27
|
+
export const bulletDim = (msg) => console.log(chalk.dim("●"), msg);
|
|
28
|
+
export const hint = (msg) => console.log(chalk.dim(` ${msg}`));
|
|
29
|
+
export const nextStep = (command) => console.log(` ${cmd(command)}`);
|
|
30
|
+
export const header = (title) => {
|
|
31
|
+
console.log();
|
|
32
|
+
console.log(chalk.bold(title));
|
|
33
|
+
console.log();
|
|
34
|
+
};
|
|
35
|
+
export function jsonOutput(data) {
|
|
36
|
+
console.log(JSON.stringify(data, null, 2));
|
|
37
|
+
}
|
|
38
|
+
export function output(options, handlers) {
|
|
39
|
+
if (options.json && handlers.json) {
|
|
40
|
+
jsonOutput(handlers.json());
|
|
41
|
+
}
|
|
42
|
+
else if (options.quiet && handlers.quiet) {
|
|
43
|
+
handlers.quiet();
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
handlers.human();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface CachedDoc {
|
|
2
|
+
id: number;
|
|
3
|
+
file: string;
|
|
4
|
+
basename: string;
|
|
5
|
+
mtime: number;
|
|
6
|
+
}
|
|
7
|
+
export interface SearchCacheData {
|
|
8
|
+
fingerprint: string;
|
|
9
|
+
/** JSON-serialized MiniSearch index */
|
|
10
|
+
index: string;
|
|
11
|
+
/** Doc metadata (without content — content is re-read for snippets) */
|
|
12
|
+
docs: CachedDoc[];
|
|
13
|
+
/** file -> inbound link count */
|
|
14
|
+
backlinkCounts: Record<string, number>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Compute a fingerprint of all .md files in the vault based on paths and mtimes.
|
|
18
|
+
* Changes when files are added, removed, or modified.
|
|
19
|
+
*/
|
|
20
|
+
export declare function computeFingerprint(contentPath: string, folder?: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Load cached search index if the fingerprint matches.
|
|
23
|
+
* Returns null if no cache, fingerprint mismatch, or corrupted data.
|
|
24
|
+
*/
|
|
25
|
+
export declare function loadSearchCache(configPath: string, currentFingerprint: string): SearchCacheData | null;
|
|
26
|
+
/**
|
|
27
|
+
* Save search index cache to disk.
|
|
28
|
+
*/
|
|
29
|
+
export declare function saveSearchCache(configPath: string, data: SearchCacheData): void;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { listFiles } from "./files.js";
|
|
5
|
+
const CACHE_FILE = "search-cache.json";
|
|
6
|
+
/**
|
|
7
|
+
* Compute a fingerprint of all .md files in the vault based on paths and mtimes.
|
|
8
|
+
* Changes when files are added, removed, or modified.
|
|
9
|
+
*/
|
|
10
|
+
export function computeFingerprint(contentPath, folder) {
|
|
11
|
+
const files = listFiles(contentPath, { folder, ext: "md" });
|
|
12
|
+
const entries = [];
|
|
13
|
+
for (const file of files) {
|
|
14
|
+
const stat = fs.statSync(path.join(contentPath, file));
|
|
15
|
+
entries.push(`${file}:${stat.mtimeMs}`);
|
|
16
|
+
}
|
|
17
|
+
return crypto.createHash("md5").update(entries.join("\n")).digest("hex");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Load cached search index if the fingerprint matches.
|
|
21
|
+
* Returns null if no cache, fingerprint mismatch, or corrupted data.
|
|
22
|
+
*/
|
|
23
|
+
export function loadSearchCache(configPath, currentFingerprint) {
|
|
24
|
+
const cachePath = path.join(configPath, CACHE_FILE);
|
|
25
|
+
try {
|
|
26
|
+
const raw = fs.readFileSync(cachePath, "utf-8");
|
|
27
|
+
const data = JSON.parse(raw);
|
|
28
|
+
if (data.fingerprint !== currentFingerprint)
|
|
29
|
+
return null;
|
|
30
|
+
return data;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Save search index cache to disk.
|
|
38
|
+
*/
|
|
39
|
+
export function saveSearchCache(configPath, data) {
|
|
40
|
+
fs.writeFileSync(path.join(configPath, CACHE_FILE), JSON.stringify(data));
|
|
41
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a temporary vault for testing.
|
|
3
|
+
* .napkin/ is the vault root — all content lives inside it.
|
|
4
|
+
* Returns:
|
|
5
|
+
* - path: parent dir (pass to --vault for commands, findVault walks up from here)
|
|
6
|
+
* - vaultPath: the .napkin/ dir (pass directly to utility functions like listFiles)
|
|
7
|
+
* - cleanup: removes everything
|
|
8
|
+
*/
|
|
9
|
+
export declare function createTempVault(files?: Record<string, string>): {
|
|
10
|
+
path: string;
|
|
11
|
+
vaultPath: string;
|
|
12
|
+
cleanup: () => void;
|
|
13
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { DEFAULT_CONFIG, saveConfig } from "./config.js";
|
|
5
|
+
/**
|
|
6
|
+
* Create a temporary vault for testing.
|
|
7
|
+
* .napkin/ is the vault root — all content lives inside it.
|
|
8
|
+
* Returns:
|
|
9
|
+
* - path: parent dir (pass to --vault for commands, findVault walks up from here)
|
|
10
|
+
* - vaultPath: the .napkin/ dir (pass directly to utility functions like listFiles)
|
|
11
|
+
* - cleanup: removes everything
|
|
12
|
+
*/
|
|
13
|
+
export function createTempVault(files) {
|
|
14
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "napkin-test-"));
|
|
15
|
+
// .napkin/ IS the vault root
|
|
16
|
+
const napkinDir = path.join(tmpDir, ".napkin");
|
|
17
|
+
fs.mkdirSync(napkinDir, { recursive: true });
|
|
18
|
+
// Write config.json which also syncs .obsidian/
|
|
19
|
+
const testConfig = {
|
|
20
|
+
...DEFAULT_CONFIG,
|
|
21
|
+
daily: {
|
|
22
|
+
...DEFAULT_CONFIG.daily,
|
|
23
|
+
folder: "Inbox/Daily",
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
saveConfig(napkinDir, testConfig);
|
|
27
|
+
// Write files inside .napkin/ (the vault root)
|
|
28
|
+
if (files) {
|
|
29
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
30
|
+
const full = path.join(napkinDir, filePath);
|
|
31
|
+
fs.mkdirSync(path.dirname(full), { recursive: true });
|
|
32
|
+
fs.writeFileSync(full, content);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
path: tmpDir,
|
|
37
|
+
vaultPath: napkinDir,
|
|
38
|
+
cleanup: () => fs.rmSync(tmpDir, { recursive: true, force: true }),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface VaultInfo {
|
|
2
|
+
/** Vault display name (derived from content root directory) */
|
|
3
|
+
name: string;
|
|
4
|
+
/** Where vault content lives (project root, parent of .napkin/) */
|
|
5
|
+
contentPath: string;
|
|
6
|
+
/** Where config.json lives (always the .napkin/ directory) */
|
|
7
|
+
configPath: string;
|
|
8
|
+
/** Where .obsidian/ directory lives */
|
|
9
|
+
obsidianPath: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Walk up from startDir looking for .napkin/ (or .obsidian/.napkin/ for nested layout).
|
|
13
|
+
* Falls back to the global vault configured in $XDG_CONFIG_HOME/napkin/config.json.
|
|
14
|
+
* Creates a bare vault at the starting directory as a last resort.
|
|
15
|
+
*/
|
|
16
|
+
export declare function findVault(startDir?: string): VaultInfo;
|
|
17
|
+
/**
|
|
18
|
+
* Read a JSON config file from .obsidian/ directory.
|
|
19
|
+
* Returns parsed JSON or null if file doesn't exist.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getVaultConfig(obsidianPath: string, configFile: string): Record<string, unknown> | null;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Walk up from startDir looking for .napkin/ (or .obsidian/.napkin/ for nested layout).
|
|
6
|
+
* Falls back to the global vault configured in $XDG_CONFIG_HOME/napkin/config.json.
|
|
7
|
+
* Creates a bare vault at the starting directory as a last resort.
|
|
8
|
+
*/
|
|
9
|
+
export function findVault(startDir) {
|
|
10
|
+
let dir = path.resolve(startDir || process.cwd());
|
|
11
|
+
const root = path.parse(dir).root;
|
|
12
|
+
const startingDir = dir;
|
|
13
|
+
while (true) {
|
|
14
|
+
const napkinDir = path.join(dir, ".napkin");
|
|
15
|
+
if (fs.existsSync(napkinDir) && fs.statSync(napkinDir).isDirectory()) {
|
|
16
|
+
return resolveVaultLayout(napkinDir, dir);
|
|
17
|
+
}
|
|
18
|
+
// Check for nested layout: .obsidian/.napkin/
|
|
19
|
+
const nestedNapkin = path.join(dir, ".obsidian", ".napkin");
|
|
20
|
+
if (fs.existsSync(nestedNapkin) &&
|
|
21
|
+
fs.statSync(nestedNapkin).isDirectory()) {
|
|
22
|
+
return resolveVaultLayout(nestedNapkin, dir);
|
|
23
|
+
}
|
|
24
|
+
const parent = path.dirname(dir);
|
|
25
|
+
if (parent === dir || dir === root) {
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
dir = parent;
|
|
29
|
+
}
|
|
30
|
+
// Fall back to global vault from user config
|
|
31
|
+
const globalVault = getGlobalConfigVault();
|
|
32
|
+
if (globalVault) {
|
|
33
|
+
return resolveVaultLayout(globalVault, path.dirname(globalVault));
|
|
34
|
+
}
|
|
35
|
+
// No vault found — create a bare one at the starting directory
|
|
36
|
+
return createBareVault(startingDir);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check for a global vault configured in the user's config directory.
|
|
40
|
+
* Reads the `vault` field from $XDG_CONFIG_HOME/napkin/config.json
|
|
41
|
+
* (defaults to ~/.config/napkin/config.json).
|
|
42
|
+
*
|
|
43
|
+
* Returns the .napkin/ path if a valid vault is configured, null otherwise.
|
|
44
|
+
*/
|
|
45
|
+
function getGlobalConfigVault() {
|
|
46
|
+
const configHome = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
|
|
47
|
+
const configPath = path.join(configHome, "napkin", "config.json");
|
|
48
|
+
if (!fs.existsSync(configPath))
|
|
49
|
+
return null;
|
|
50
|
+
try {
|
|
51
|
+
const raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
52
|
+
if (!raw.vault)
|
|
53
|
+
return null;
|
|
54
|
+
const vaultPath = raw.vault === "~" || raw.vault.startsWith("~/")
|
|
55
|
+
? path.join(os.homedir(), raw.vault.slice(1))
|
|
56
|
+
: path.resolve(path.dirname(configPath), raw.vault);
|
|
57
|
+
const napkinDir = path.join(vaultPath, ".napkin");
|
|
58
|
+
if (fs.existsSync(napkinDir))
|
|
59
|
+
return napkinDir;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// invalid config
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Create a bare vault at the given directory.
|
|
68
|
+
* Sibling layout: .napkin/ (config) + .obsidian/ + NAPKIN.md all in projectDir.
|
|
69
|
+
*/
|
|
70
|
+
function createBareVault(projectDir) {
|
|
71
|
+
const napkinDir = path.join(projectDir, ".napkin");
|
|
72
|
+
fs.mkdirSync(napkinDir, { recursive: true });
|
|
73
|
+
const configFile = path.join(napkinDir, "config.json");
|
|
74
|
+
if (!fs.existsSync(configFile)) {
|
|
75
|
+
fs.writeFileSync(configFile, JSON.stringify({
|
|
76
|
+
overview: { depth: 3, keywords: 8 },
|
|
77
|
+
search: { limit: 30, snippetLines: 0 },
|
|
78
|
+
daily: { folder: "daily", format: "YYYY-MM-DD" },
|
|
79
|
+
vault: { root: "..", obsidian: "../.obsidian" },
|
|
80
|
+
}, null, 2));
|
|
81
|
+
}
|
|
82
|
+
const napkinMd = path.join(projectDir, "NAPKIN.md");
|
|
83
|
+
if (!fs.existsSync(napkinMd)) {
|
|
84
|
+
fs.writeFileSync(napkinMd, "");
|
|
85
|
+
}
|
|
86
|
+
const obsidianDir = path.join(projectDir, ".obsidian");
|
|
87
|
+
if (!fs.existsSync(obsidianDir)) {
|
|
88
|
+
fs.mkdirSync(obsidianDir, { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
name: path.basename(projectDir),
|
|
92
|
+
contentPath: projectDir,
|
|
93
|
+
configPath: napkinDir,
|
|
94
|
+
obsidianPath: obsidianDir,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Resolve vault layout from .napkin/config.json vault paths.
|
|
99
|
+
* If no vault config exists, defaults to sibling layout (content in project dir).
|
|
100
|
+
*/
|
|
101
|
+
function resolveVaultLayout(napkinDir, projectDir) {
|
|
102
|
+
const configPath = path.join(napkinDir, "config.json");
|
|
103
|
+
let vaultConfig;
|
|
104
|
+
try {
|
|
105
|
+
const raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
106
|
+
vaultConfig = raw.vault;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// no config or invalid — use defaults
|
|
110
|
+
}
|
|
111
|
+
if (vaultConfig?.root) {
|
|
112
|
+
const contentPath = path.resolve(napkinDir, vaultConfig.root);
|
|
113
|
+
const obsidianPath = vaultConfig.obsidian
|
|
114
|
+
? path.resolve(napkinDir, vaultConfig.obsidian)
|
|
115
|
+
: path.join(contentPath, ".obsidian");
|
|
116
|
+
return {
|
|
117
|
+
name: path.basename(contentPath),
|
|
118
|
+
contentPath,
|
|
119
|
+
configPath: napkinDir,
|
|
120
|
+
obsidianPath,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
// Legacy: embedded layout — .napkin/ is the vault root (no vault.root in config)
|
|
124
|
+
return {
|
|
125
|
+
name: path.basename(projectDir),
|
|
126
|
+
contentPath: napkinDir,
|
|
127
|
+
configPath: napkinDir,
|
|
128
|
+
obsidianPath: path.join(napkinDir, ".obsidian"),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Read a JSON config file from .obsidian/ directory.
|
|
133
|
+
* Returns parsed JSON or null if file doesn't exist.
|
|
134
|
+
*/
|
|
135
|
+
export function getVaultConfig(obsidianPath, configFile) {
|
|
136
|
+
const configPath = path.join(obsidianPath, configFile);
|
|
137
|
+
try {
|
|
138
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
139
|
+
return JSON.parse(content);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cad0p/napkin",
|
|
3
|
+
"version": "0.8.1",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public",
|
|
6
|
+
"registry": "https://registry.npmjs.org"
|
|
7
|
+
},
|
|
8
|
+
"description": "🧻 Knowledge system for agents. Local-first, file-based, progressively disclosed.",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"main": "dist/index.js",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"napkin": "./dist/main.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"prepare": "npm run build",
|
|
23
|
+
"prepublishOnly": "npm run build",
|
|
24
|
+
"dev": "bun run src/main.ts",
|
|
25
|
+
"build:bun": "bun build --compile src/main.ts --outfile napkin",
|
|
26
|
+
"build:bun-linux": "bun build --compile --target=bun-linux-x64 src/main.ts --outfile napkin-linux-x64",
|
|
27
|
+
"build:bun-mac-arm": "bun build --compile --target=bun-darwin-arm64 src/main.ts --outfile napkin-darwin-arm64",
|
|
28
|
+
"build:bun-mac-x64": "bun build --compile --target=bun-darwin-x64 src/main.ts --outfile napkin-darwin-x64",
|
|
29
|
+
"test": "bun test",
|
|
30
|
+
"format": "bunx biome format --write src/",
|
|
31
|
+
"lint": "bunx biome lint src/",
|
|
32
|
+
"check": "bunx biome check src/"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/cad0p/napkin.git"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/cad0p/napkin#readme",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/cad0p/napkin/issues"
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"obsidian",
|
|
44
|
+
"cli",
|
|
45
|
+
"markdown",
|
|
46
|
+
"vault",
|
|
47
|
+
"notes",
|
|
48
|
+
"tasks",
|
|
49
|
+
"tags",
|
|
50
|
+
"search",
|
|
51
|
+
"local-first"
|
|
52
|
+
],
|
|
53
|
+
"license": "MIT",
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@biomejs/biome": "^2.3.14",
|
|
56
|
+
"@types/bun": "latest",
|
|
57
|
+
"@types/js-yaml": "^4.0.9",
|
|
58
|
+
"@types/node": "^25.6.0",
|
|
59
|
+
"typescript": "^5.8.0"
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"chalk": "^5.6.2",
|
|
63
|
+
"commander": "^14.0.3",
|
|
64
|
+
"gray-matter": "^4.0.3",
|
|
65
|
+
"jexl": "^2.3.0",
|
|
66
|
+
"js-yaml": "^4.1.0",
|
|
67
|
+
"minisearch": "^7.2.0",
|
|
68
|
+
"sql.js": "^1.14.0"
|
|
69
|
+
},
|
|
70
|
+
"optionalDependencies": {
|
|
71
|
+
"glimpseui": "^0.3.7"
|
|
72
|
+
},
|
|
73
|
+
"trustedDependencies": [
|
|
74
|
+
"glimpseui"
|
|
75
|
+
]
|
|
76
|
+
}
|