@drewpayment/mink 0.1.0 → 0.2.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/README.md +191 -8
- package/dist/cli.js +87465 -0
- package/package.json +7 -3
- package/skills/mink-note/SKILL.md +131 -0
- package/src/cli.ts +46 -0
- package/src/commands/init.ts +77 -4
- package/src/commands/note.ts +267 -0
- package/src/commands/session-start.ts +26 -0
- package/src/commands/session-stop.ts +148 -2
- package/src/commands/skill.ts +186 -0
- package/src/commands/wiki.ts +250 -0
- package/src/core/note-index.ts +262 -0
- package/src/core/note-linker.ts +161 -0
- package/src/core/note-writer.ts +203 -0
- package/src/core/vault-templates.ts +179 -0
- package/src/core/vault.ts +132 -0
- package/src/types/config.ts +7 -0
- package/src/types/note.ts +60 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { join } from "path";
|
|
2
|
+
import { existsSync, writeFileSync, readFileSync, mkdirSync } from "fs";
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_TEMPLATES: Record<string, string> = {
|
|
5
|
+
"quick-capture": `---
|
|
6
|
+
created: "{{created}}"
|
|
7
|
+
updated: "{{updated}}"
|
|
8
|
+
tags: []
|
|
9
|
+
category: inbox
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# {{title}}
|
|
13
|
+
|
|
14
|
+
{{body}}
|
|
15
|
+
`,
|
|
16
|
+
|
|
17
|
+
"daily-note": `---
|
|
18
|
+
created: "{{created}}"
|
|
19
|
+
updated: "{{updated}}"
|
|
20
|
+
tags: [daily]
|
|
21
|
+
category: areas
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# {{date}}
|
|
25
|
+
|
|
26
|
+
## Focus
|
|
27
|
+
|
|
28
|
+
-
|
|
29
|
+
|
|
30
|
+
## Notes
|
|
31
|
+
|
|
32
|
+
-
|
|
33
|
+
|
|
34
|
+
## Tasks
|
|
35
|
+
|
|
36
|
+
- [ ]
|
|
37
|
+
|
|
38
|
+
## Reflections
|
|
39
|
+
|
|
40
|
+
`,
|
|
41
|
+
|
|
42
|
+
meeting: `---
|
|
43
|
+
created: "{{created}}"
|
|
44
|
+
updated: "{{updated}}"
|
|
45
|
+
tags: [meeting]
|
|
46
|
+
category: areas
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
# {{title}}
|
|
50
|
+
|
|
51
|
+
**Date**: {{date}}
|
|
52
|
+
**Attendees**:
|
|
53
|
+
|
|
54
|
+
## Agenda
|
|
55
|
+
|
|
56
|
+
-
|
|
57
|
+
|
|
58
|
+
## Discussion
|
|
59
|
+
|
|
60
|
+
-
|
|
61
|
+
|
|
62
|
+
## Decisions
|
|
63
|
+
|
|
64
|
+
-
|
|
65
|
+
|
|
66
|
+
## Action Items
|
|
67
|
+
|
|
68
|
+
- [ ]
|
|
69
|
+
`,
|
|
70
|
+
|
|
71
|
+
project: `---
|
|
72
|
+
created: "{{created}}"
|
|
73
|
+
updated: "{{updated}}"
|
|
74
|
+
tags: [project]
|
|
75
|
+
category: projects
|
|
76
|
+
status: active
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
# {{title}}
|
|
80
|
+
|
|
81
|
+
## Overview
|
|
82
|
+
|
|
83
|
+
{{body}}
|
|
84
|
+
|
|
85
|
+
## Goals
|
|
86
|
+
|
|
87
|
+
-
|
|
88
|
+
|
|
89
|
+
## Key Decisions
|
|
90
|
+
|
|
91
|
+
-
|
|
92
|
+
|
|
93
|
+
## Links
|
|
94
|
+
|
|
95
|
+
-
|
|
96
|
+
`,
|
|
97
|
+
|
|
98
|
+
area: `---
|
|
99
|
+
created: "{{created}}"
|
|
100
|
+
updated: "{{updated}}"
|
|
101
|
+
tags: [area]
|
|
102
|
+
category: areas
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
# {{title}}
|
|
106
|
+
|
|
107
|
+
## Purpose
|
|
108
|
+
|
|
109
|
+
{{body}}
|
|
110
|
+
|
|
111
|
+
## Standards
|
|
112
|
+
|
|
113
|
+
-
|
|
114
|
+
|
|
115
|
+
## Key Resources
|
|
116
|
+
|
|
117
|
+
-
|
|
118
|
+
`,
|
|
119
|
+
|
|
120
|
+
person: `---
|
|
121
|
+
created: "{{created}}"
|
|
122
|
+
updated: "{{updated}}"
|
|
123
|
+
tags: [person]
|
|
124
|
+
category: resources
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
# {{title}}
|
|
128
|
+
|
|
129
|
+
## Role
|
|
130
|
+
|
|
131
|
+
## Context
|
|
132
|
+
|
|
133
|
+
## 1:1 Notes
|
|
134
|
+
|
|
135
|
+
-
|
|
136
|
+
|
|
137
|
+
## Key Projects
|
|
138
|
+
|
|
139
|
+
-
|
|
140
|
+
`,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export function seedTemplates(templatesDir: string): void {
|
|
144
|
+
mkdirSync(templatesDir, { recursive: true });
|
|
145
|
+
for (const [name, content] of Object.entries(DEFAULT_TEMPLATES)) {
|
|
146
|
+
const filePath = join(templatesDir, `${name}.md`);
|
|
147
|
+
if (!existsSync(filePath)) {
|
|
148
|
+
writeFileSync(filePath, content);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function loadTemplate(
|
|
154
|
+
templatesDir: string,
|
|
155
|
+
templateName: string,
|
|
156
|
+
vars: Record<string, string>
|
|
157
|
+
): string | null {
|
|
158
|
+
const filePath = join(templatesDir, `${templateName}.md`);
|
|
159
|
+
let content: string;
|
|
160
|
+
if (existsSync(filePath)) {
|
|
161
|
+
content = readFileSync(filePath, "utf-8");
|
|
162
|
+
} else if (DEFAULT_TEMPLATES[templateName]) {
|
|
163
|
+
content = DEFAULT_TEMPLATES[templateName];
|
|
164
|
+
} else {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
return fillTemplate(content, vars);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function fillTemplate(
|
|
171
|
+
template: string,
|
|
172
|
+
vars: Record<string, string>
|
|
173
|
+
): string {
|
|
174
|
+
let result = template;
|
|
175
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
176
|
+
result = result.replaceAll(`{{${key}}}`, value);
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { join } from "path";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { existsSync, mkdirSync } from "fs";
|
|
4
|
+
import { resolveConfigValue } from "./global-config";
|
|
5
|
+
import { safeReadJson } from "./fs-utils";
|
|
6
|
+
import type { VaultManifest } from "../types/note";
|
|
7
|
+
|
|
8
|
+
const DEFAULT_VAULT_PATH = join(homedir(), ".mink", "wiki");
|
|
9
|
+
|
|
10
|
+
export function resolveVaultPath(): string {
|
|
11
|
+
const resolved = resolveConfigValue("wiki.path");
|
|
12
|
+
const raw = resolved.value;
|
|
13
|
+
if (raw.startsWith("~/")) {
|
|
14
|
+
return join(homedir(), raw.slice(2));
|
|
15
|
+
}
|
|
16
|
+
return raw;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function vaultRoot(): string {
|
|
20
|
+
return resolveVaultPath();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function vaultInbox(): string {
|
|
24
|
+
return join(resolveVaultPath(), "inbox");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function vaultProjects(slug?: string): string {
|
|
28
|
+
const base = join(resolveVaultPath(), "projects");
|
|
29
|
+
return slug ? join(base, slug) : base;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function vaultAreas(): string {
|
|
33
|
+
return join(resolveVaultPath(), "areas");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function vaultDailyDir(): string {
|
|
37
|
+
return join(resolveVaultPath(), "areas", "daily");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function vaultResources(): string {
|
|
41
|
+
return join(resolveVaultPath(), "resources");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function vaultArchives(): string {
|
|
45
|
+
return join(resolveVaultPath(), "archives");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function vaultTemplates(): string {
|
|
49
|
+
return join(resolveVaultPath(), "templates");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function vaultPatterns(): string {
|
|
53
|
+
return join(resolveVaultPath(), "patterns");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function vaultManifestPath(): string {
|
|
57
|
+
return join(resolveVaultPath(), ".mink-vault.json");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function vaultIndexPath(): string {
|
|
61
|
+
return join(resolveVaultPath(), ".mink-index.json");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function vaultMasterIndexPath(): string {
|
|
65
|
+
return join(resolveVaultPath(), "_index.md");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function isVaultInitialized(): boolean {
|
|
69
|
+
return existsSync(vaultManifestPath());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function isInsideVault(cwd: string): boolean {
|
|
73
|
+
const vault = resolveVaultPath();
|
|
74
|
+
const normalizedCwd = cwd.replace(/\/+$/, "");
|
|
75
|
+
const normalizedVault = vault.replace(/\/+$/, "");
|
|
76
|
+
return (
|
|
77
|
+
normalizedCwd === normalizedVault ||
|
|
78
|
+
normalizedCwd.startsWith(normalizedVault + "/")
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function isWikiEnabled(): boolean {
|
|
83
|
+
const resolved = resolveConfigValue("wiki.enabled");
|
|
84
|
+
return resolved.value === "true";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function loadVaultManifest(): VaultManifest | null {
|
|
88
|
+
const raw = safeReadJson(vaultManifestPath());
|
|
89
|
+
if (raw === null || typeof raw !== "object") return null;
|
|
90
|
+
return raw as VaultManifest;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const VAULT_DIRS = [
|
|
94
|
+
"",
|
|
95
|
+
"inbox",
|
|
96
|
+
"projects",
|
|
97
|
+
"areas",
|
|
98
|
+
"areas/daily",
|
|
99
|
+
"resources",
|
|
100
|
+
"archives",
|
|
101
|
+
"templates",
|
|
102
|
+
"patterns",
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
export function ensureVaultStructure(): void {
|
|
106
|
+
const root = resolveVaultPath();
|
|
107
|
+
for (const dir of VAULT_DIRS) {
|
|
108
|
+
mkdirSync(join(root, dir), { recursive: true });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function categoryToDir(
|
|
113
|
+
category: string,
|
|
114
|
+
projectSlug?: string
|
|
115
|
+
): string {
|
|
116
|
+
const root = resolveVaultPath();
|
|
117
|
+
switch (category) {
|
|
118
|
+
case "projects":
|
|
119
|
+
return projectSlug
|
|
120
|
+
? join(root, "projects", projectSlug)
|
|
121
|
+
: join(root, "projects");
|
|
122
|
+
case "areas":
|
|
123
|
+
return join(root, "areas");
|
|
124
|
+
case "resources":
|
|
125
|
+
return join(root, "resources");
|
|
126
|
+
case "archives":
|
|
127
|
+
return join(root, "archives");
|
|
128
|
+
case "inbox":
|
|
129
|
+
default:
|
|
130
|
+
return join(root, "inbox");
|
|
131
|
+
}
|
|
132
|
+
}
|
package/src/types/config.ts
CHANGED
|
@@ -4,6 +4,7 @@ export interface GlobalConfig {
|
|
|
4
4
|
"wiki.sync-mode"?: string;
|
|
5
5
|
"wiki.git-backup"?: string;
|
|
6
6
|
"wiki.git-remote"?: string;
|
|
7
|
+
"notes.default-category"?: string;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export type ConfigKey = keyof GlobalConfig & string;
|
|
@@ -46,6 +47,12 @@ export const CONFIG_KEYS: ConfigKeyMeta[] = [
|
|
|
46
47
|
envVar: "MINK_WIKI_GIT_REMOTE",
|
|
47
48
|
description: "Git remote name for push",
|
|
48
49
|
},
|
|
50
|
+
{
|
|
51
|
+
key: "notes.default-category",
|
|
52
|
+
default: "inbox",
|
|
53
|
+
envVar: "MINK_NOTES_DEFAULT_CATEGORY",
|
|
54
|
+
description: "Default category for notes captured via CLI",
|
|
55
|
+
},
|
|
49
56
|
];
|
|
50
57
|
|
|
51
58
|
const VALID_KEYS = new Set<string>(CONFIG_KEYS.map((k) => k.key));
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export type NoteCategory =
|
|
2
|
+
| "inbox"
|
|
3
|
+
| "projects"
|
|
4
|
+
| "areas"
|
|
5
|
+
| "resources"
|
|
6
|
+
| "archives";
|
|
7
|
+
|
|
8
|
+
export const NOTE_CATEGORIES: NoteCategory[] = [
|
|
9
|
+
"inbox",
|
|
10
|
+
"projects",
|
|
11
|
+
"areas",
|
|
12
|
+
"resources",
|
|
13
|
+
"archives",
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
export interface NoteMetadata {
|
|
17
|
+
title: string;
|
|
18
|
+
category: NoteCategory;
|
|
19
|
+
tags: string[];
|
|
20
|
+
created: string;
|
|
21
|
+
updated: string;
|
|
22
|
+
template?: string;
|
|
23
|
+
projectSlug?: string;
|
|
24
|
+
sourceProject?: string;
|
|
25
|
+
body: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface NoteFrontmatter {
|
|
29
|
+
created: string;
|
|
30
|
+
updated: string;
|
|
31
|
+
tags: string[];
|
|
32
|
+
category: NoteCategory;
|
|
33
|
+
source_project?: string;
|
|
34
|
+
aliases?: string[];
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface VaultManifest {
|
|
39
|
+
version: number;
|
|
40
|
+
createdAt: string;
|
|
41
|
+
totalNotes: number;
|
|
42
|
+
categories: Record<NoteCategory, number>;
|
|
43
|
+
lastOrganized: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface VaultIndexEntry {
|
|
47
|
+
filePath: string;
|
|
48
|
+
title: string;
|
|
49
|
+
description: string;
|
|
50
|
+
tags: string[];
|
|
51
|
+
category: NoteCategory;
|
|
52
|
+
estimatedTokens: number;
|
|
53
|
+
lastModified: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface VaultIndex {
|
|
57
|
+
lastScanTimestamp: string;
|
|
58
|
+
totalNotes: number;
|
|
59
|
+
entries: Record<string, VaultIndexEntry>;
|
|
60
|
+
}
|