@mattli/dotmd 0.1.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 (78) hide show
  1. package/README.md +77 -0
  2. package/dist/cli/commands/init.d.ts +4 -0
  3. package/dist/cli/commands/init.d.ts.map +1 -0
  4. package/dist/cli/commands/init.js +23 -0
  5. package/dist/cli/commands/init.js.map +1 -0
  6. package/dist/cli/commands/install-hook.d.ts +2 -0
  7. package/dist/cli/commands/install-hook.d.ts.map +1 -0
  8. package/dist/cli/commands/install-hook.js +31 -0
  9. package/dist/cli/commands/install-hook.js.map +1 -0
  10. package/dist/cli/commands/scan.d.ts +5 -0
  11. package/dist/cli/commands/scan.d.ts.map +1 -0
  12. package/dist/cli/commands/scan.js +75 -0
  13. package/dist/cli/commands/scan.js.map +1 -0
  14. package/dist/cli/commands/serve.d.ts +4 -0
  15. package/dist/cli/commands/serve.d.ts.map +1 -0
  16. package/dist/cli/commands/serve.js +8 -0
  17. package/dist/cli/commands/serve.js.map +1 -0
  18. package/dist/cli/commands/status.d.ts +2 -0
  19. package/dist/cli/commands/status.d.ts.map +1 -0
  20. package/dist/cli/commands/status.js +70 -0
  21. package/dist/cli/commands/status.js.map +1 -0
  22. package/dist/cli/index.d.ts +3 -0
  23. package/dist/cli/index.d.ts.map +1 -0
  24. package/dist/cli/index.js +38 -0
  25. package/dist/cli/index.js.map +1 -0
  26. package/dist/config/defaults.d.ts +13 -0
  27. package/dist/config/defaults.d.ts.map +1 -0
  28. package/dist/config/defaults.js +36 -0
  29. package/dist/config/defaults.js.map +1 -0
  30. package/dist/config/loader.d.ts +4 -0
  31. package/dist/config/loader.d.ts.map +1 -0
  32. package/dist/config/loader.js +28 -0
  33. package/dist/config/loader.js.map +1 -0
  34. package/dist/dashboard/layout.d.ts +6 -0
  35. package/dist/dashboard/layout.d.ts.map +1 -0
  36. package/dist/dashboard/layout.js +47 -0
  37. package/dist/dashboard/layout.js.map +1 -0
  38. package/dist/dashboard/server.d.ts +5 -0
  39. package/dist/dashboard/server.d.ts.map +1 -0
  40. package/dist/dashboard/server.js +305 -0
  41. package/dist/dashboard/server.js.map +1 -0
  42. package/dist/dashboard/settings-client.d.ts +2 -0
  43. package/dist/dashboard/settings-client.d.ts.map +1 -0
  44. package/dist/dashboard/settings-client.js +310 -0
  45. package/dist/dashboard/settings-client.js.map +1 -0
  46. package/dist/dashboard/settings.d.ts +3 -0
  47. package/dist/dashboard/settings.d.ts.map +1 -0
  48. package/dist/dashboard/settings.js +99 -0
  49. package/dist/dashboard/settings.js.map +1 -0
  50. package/dist/dashboard/views.d.ts +8 -0
  51. package/dist/dashboard/views.d.ts.map +1 -0
  52. package/dist/dashboard/views.js +694 -0
  53. package/dist/dashboard/views.js.map +1 -0
  54. package/dist/dashboard/wizard-client.d.ts +2 -0
  55. package/dist/dashboard/wizard-client.d.ts.map +1 -0
  56. package/dist/dashboard/wizard-client.js +266 -0
  57. package/dist/dashboard/wizard-client.js.map +1 -0
  58. package/dist/dashboard/wizard.d.ts +9 -0
  59. package/dist/dashboard/wizard.d.ts.map +1 -0
  60. package/dist/dashboard/wizard.js +236 -0
  61. package/dist/dashboard/wizard.js.map +1 -0
  62. package/dist/scanner/git.d.ts +8 -0
  63. package/dist/scanner/git.d.ts.map +1 -0
  64. package/dist/scanner/git.js +34 -0
  65. package/dist/scanner/git.js.map +1 -0
  66. package/dist/scanner/index.d.ts +10 -0
  67. package/dist/scanner/index.d.ts.map +1 -0
  68. package/dist/scanner/index.js +60 -0
  69. package/dist/scanner/index.js.map +1 -0
  70. package/dist/storage/db.d.ts +3 -0
  71. package/dist/storage/db.d.ts.map +1 -0
  72. package/dist/storage/db.js +52 -0
  73. package/dist/storage/db.js.map +1 -0
  74. package/dist/storage/snapshots.d.ts +43 -0
  75. package/dist/storage/snapshots.d.ts.map +1 -0
  76. package/dist/storage/snapshots.js +102 -0
  77. package/dist/storage/snapshots.js.map +1 -0
  78. package/package.json +60 -0
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # dotmd
2
+
3
+ Track changes to your AI instruction files.
4
+
5
+ If you use Claude Code, Cursor, Codex, or similar tools, you've accumulated markdown files that control how AI behaves: `CLAUDE.md`, `AGENTS.md`, memory files, skill configs. These files live in scattered locations, get modified by both you and AI agents, and have no change history.
6
+
7
+ dotmd watches these files and keeps a local history of every change, with diffs. A web dashboard lets you see what changed, when, and across which projects.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install -g @mattli/dotmd
13
+ ```
14
+
15
+ Requires Node.js 18+.
16
+
17
+ ## Quick Start
18
+
19
+ ```bash
20
+ dotmd init
21
+ ```
22
+
23
+ This opens a setup wizard in your browser where you pick which folders and file patterns to track. Once you confirm, dotmd takes an initial snapshot of all discovered files.
24
+
25
+ To view your files and change history:
26
+
27
+ ```bash
28
+ dotmd serve
29
+ ```
30
+
31
+ This starts a local dashboard at `http://localhost:3333`.
32
+
33
+ ## Commands
34
+
35
+ | Command | Description |
36
+ |---------|-------------|
37
+ | `dotmd init` | Launch the setup wizard |
38
+ | `dotmd status` | Scan for changes and show a summary in the terminal |
39
+ | `dotmd serve` | Start the web dashboard |
40
+
41
+ ## What It Tracks
42
+
43
+ By default, dotmd looks for:
44
+
45
+ - `CLAUDE.md` (Claude Code)
46
+ - `AGENTS.md` (Codex)
47
+ - `MEMORY.md`
48
+ - `SKILL.md` (Codex skills)
49
+ - `.cursorrules` (Cursor)
50
+ - `.windsurfrules` (Windsurf)
51
+
52
+ You can add custom patterns and folders through the setup wizard or the settings page in the dashboard.
53
+
54
+ ## How It Works
55
+
56
+ dotmd scans your configured folders for matching files and stores snapshots in a local SQLite database at `~/.dotmd/history.db`. When a file changes, it saves the new version and a diff. The dashboard rescans on every page load, so you always see current data.
57
+
58
+ All data stays on your machine. Nothing is sent anywhere.
59
+
60
+ ## Configuration
61
+
62
+ Settings are managed through the dashboard's settings page. Config is stored at `~/.dotmd/config.yaml`.
63
+
64
+ ## Uninstall
65
+
66
+ ```bash
67
+ npm uninstall -g dotmd
68
+ rm -rf ~/.dotmd
69
+ ```
70
+
71
+ ## Feedback
72
+
73
+ Questions, suggestions, or bugs? [Open an issue](https://github.com/mattli/dotmd/issues).
74
+
75
+ ## License
76
+
77
+ MIT
@@ -0,0 +1,4 @@
1
+ export declare function initCommand(options: {
2
+ port?: string;
3
+ }): Promise<void>;
4
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAIA,wBAAsB,WAAW,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA0B3E"}
@@ -0,0 +1,23 @@
1
+ import { exec } from "child_process";
2
+ import chalk from "chalk";
3
+ import { startServer } from "../../dashboard/server.js";
4
+ export async function initCommand(options) {
5
+ const port = options.port ? parseInt(options.port, 10) : 3333;
6
+ const url = `http://localhost:${port}/setup`;
7
+ console.log(chalk.bold("\nStarting setup wizard...\n"));
8
+ startServer(port);
9
+ // Open browser
10
+ const openCmd = process.platform === "darwin"
11
+ ? "open"
12
+ : process.platform === "win32"
13
+ ? "start"
14
+ : "xdg-open";
15
+ exec(`${openCmd} ${url}`, (err) => {
16
+ if (err) {
17
+ console.log(chalk.yellow(`Could not open browser automatically.`));
18
+ }
19
+ });
20
+ console.log(`Setup wizard running at ${chalk.blue(url)}`);
21
+ console.log(chalk.dim("Press Ctrl+C to stop the server.\n"));
22
+ }
23
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAExD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAA0B;IAC1D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,MAAM,GAAG,GAAG,oBAAoB,IAAI,QAAQ,CAAC;IAE7C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC,CAAC;IAExD,WAAW,CAAC,IAAI,CAAC,CAAC;IAElB,eAAe;IACf,MAAM,OAAO,GACX,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAC3B,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;YAC5B,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,UAAU,CAAC;IAEnB,IAAI,CAAC,GAAG,OAAO,IAAI,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE;QAChC,IAAI,GAAG,EAAE,CAAC;YACR,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CAAC,uCAAuC,CAAC,CACtD,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,2BAA2B,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC1D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function installHookCommand(): Promise<void>;
2
+ //# sourceMappingURL=install-hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-hook.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/install-hook.ts"],"names":[],"mappings":"AAgBA,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAqBxD"}
@@ -0,0 +1,31 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import os from "os";
4
+ import chalk from "chalk";
5
+ const HOOK_MARKER = "# dotmd shell hook";
6
+ const HOOK_LINE = `\n${HOOK_MARKER}\nif command -v dotmd &> /dev/null; then dotmd scan --session --quiet 2>/dev/null; fi\n`;
7
+ function getShellRcPath() {
8
+ const shell = process.env.SHELL || "/bin/zsh";
9
+ if (shell.includes("bash")) {
10
+ return path.join(os.homedir(), ".bashrc");
11
+ }
12
+ return path.join(os.homedir(), ".zshrc");
13
+ }
14
+ export async function installHookCommand() {
15
+ const rcPath = getShellRcPath();
16
+ const shellName = rcPath.endsWith(".bashrc") ? "bash" : "zsh";
17
+ console.log(chalk.bold(`\nInstalling dotmd session hook for ${shellName}\n`));
18
+ // Check if already installed
19
+ if (fs.existsSync(rcPath)) {
20
+ const content = fs.readFileSync(rcPath, "utf-8");
21
+ if (content.includes(HOOK_MARKER)) {
22
+ console.log(chalk.yellow("Hook already installed. Skipping."));
23
+ return;
24
+ }
25
+ }
26
+ // Append hook
27
+ fs.appendFileSync(rcPath, HOOK_LINE);
28
+ console.log(chalk.green(`Hook added to ${rcPath.replace(os.homedir(), "~")}`));
29
+ console.log(chalk.dim("Open a new terminal to activate, or run: source " + rcPath.replace(os.homedir(), "~")));
30
+ }
31
+ //# sourceMappingURL=install-hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-hook.js","sourceRoot":"","sources":["../../../src/cli/commands/install-hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,GAAG,oBAAoB,CAAC;AACzC,MAAM,SAAS,GAAG,KAAK,WAAW,yFAAyF,CAAC;AAE5H,SAAS,cAAc;IACrB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,UAAU,CAAC;IAC9C,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAChC,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;IAE9D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,uCAAuC,SAAS,IAAI,CAAC,CAAC,CAAC;IAE9E,6BAA6B;IAC7B,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjD,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,mCAAmC,CAAC,CAAC,CAAC;YAC/D,OAAO;QACT,CAAC;IACH,CAAC;IAED,cAAc;IACd,EAAE,CAAC,cAAc,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/E,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CAAC,kDAAkD,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,CAAC,CAClG,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function scanCommand(options: {
2
+ quiet?: boolean;
3
+ session?: boolean;
4
+ }): Promise<void>;
5
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/scan.ts"],"names":[],"mappings":"AAYA,wBAAsB,WAAW,CAAC,OAAO,EAAE;IACzC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2FhB"}
@@ -0,0 +1,75 @@
1
+ import chalk from "chalk";
2
+ import os from "os";
3
+ import { loadConfig } from "../../config/loader.js";
4
+ import { getDb } from "../../storage/db.js";
5
+ import { scanFiles } from "../../scanner/index.js";
6
+ import { recordSession, getLastSessionTime, getLatestSessionTime, getRecentChanges, } from "../../storage/snapshots.js";
7
+ export async function scanCommand(options) {
8
+ const config = loadConfig();
9
+ const db = getDb();
10
+ // Record session if this is a session-triggered scan
11
+ let lastSession = null;
12
+ if (options.session) {
13
+ // Debounce: skip scan if a session was recorded less than 60 seconds ago
14
+ const latestSession = getLatestSessionTime(db);
15
+ if (latestSession) {
16
+ const elapsed = Date.now() - new Date(latestSession + "Z").getTime();
17
+ if (elapsed < 60_000) {
18
+ recordSession(db);
19
+ db.close();
20
+ return;
21
+ }
22
+ }
23
+ lastSession = getLastSessionTime(db);
24
+ recordSession(db);
25
+ }
26
+ const results = scanFiles(db, config);
27
+ const changed = results.filter((r) => r.result.changed);
28
+ const newFiles = results.filter((r) => r.result.isNew);
29
+ if (options.quiet && changed.length === 0) {
30
+ db.close();
31
+ return;
32
+ }
33
+ if (changed.length === 0 && !options.session) {
34
+ console.log(chalk.dim("No changes detected."));
35
+ db.close();
36
+ return;
37
+ }
38
+ // Session mode: show changes since last session
39
+ if (options.session) {
40
+ if (lastSession) {
41
+ const recentChanges = getRecentChanges(db, lastSession, 10);
42
+ if (recentChanges.length === 0) {
43
+ db.close();
44
+ return; // silent when no changes
45
+ }
46
+ console.log(chalk.bold(`\ndotmd: ${recentChanges.length} file${recentChanges.length === 1 ? "" : "s"} changed since last session`));
47
+ const toShow = recentChanges.slice(0, 5);
48
+ for (const change of toShow) {
49
+ const display = change.path.replace(os.homedir(), "~");
50
+ const lines = (change.diff?.match(/\n/g) || []).length;
51
+ console.log(chalk.dim(` ${display} — ${lines} lines changed`));
52
+ }
53
+ if (recentChanges.length > 5) {
54
+ console.log(chalk.dim(` and ${recentChanges.length - 5} more — run \`dotmd status\` for details`));
55
+ }
56
+ console.log();
57
+ }
58
+ db.close();
59
+ return;
60
+ }
61
+ // Normal scan output
62
+ if (newFiles.length > 0) {
63
+ console.log(chalk.green(`${newFiles.length} new file(s) tracked`));
64
+ }
65
+ if (changed.length > 0) {
66
+ console.log(chalk.yellow(`${changed.length} file(s) changed:`));
67
+ for (const r of changed) {
68
+ const display = r.path.replace(os.homedir(), "~");
69
+ console.log(` ${display}`);
70
+ }
71
+ }
72
+ console.log(chalk.dim(`\nScanned ${results.length} files total.`));
73
+ db.close();
74
+ }
75
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../../src/cli/commands/scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function serveCommand(options: {
2
+ port?: string;
3
+ }): Promise<void>;
4
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/serve.ts"],"names":[],"mappings":"AAGA,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAI5E"}
@@ -0,0 +1,8 @@
1
+ import chalk from "chalk";
2
+ import { startServer } from "../../dashboard/server.js";
3
+ export async function serveCommand(options) {
4
+ const port = options.port ? parseInt(options.port, 10) : 3333;
5
+ console.log(chalk.bold(`\nStarting dotmd dashboard on port ${port}...\n`));
6
+ startServer(port);
7
+ }
8
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../../src/cli/commands/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAExD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAA0B;IAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,sCAAsC,IAAI,OAAO,CAAC,CAAC,CAAC;IAC3E,WAAW,CAAC,IAAI,CAAC,CAAC;AACpB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function statusCommand(): Promise<void>;
2
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"AAUA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAoEnD"}
@@ -0,0 +1,70 @@
1
+ import chalk from "chalk";
2
+ import os from "os";
3
+ import { loadConfig } from "../../config/loader.js";
4
+ import { getDb } from "../../storage/db.js";
5
+ import { scanFiles } from "../../scanner/index.js";
6
+ import { getTrackedFiles, getRecentChanges, } from "../../storage/snapshots.js";
7
+ export async function statusCommand() {
8
+ const db = getDb();
9
+ const config = loadConfig();
10
+ scanFiles(db, config);
11
+ const files = getTrackedFiles(db);
12
+ if (files.length === 0) {
13
+ console.log(chalk.yellow("No tracked files. Run `dotmd init` first."));
14
+ db.close();
15
+ return;
16
+ }
17
+ // Group by category
18
+ const groups = {};
19
+ for (const file of files) {
20
+ if (!groups[file.category])
21
+ groups[file.category] = [];
22
+ groups[file.category].push(file);
23
+ }
24
+ const fixedLabels = {
25
+ global: "Claude Code Global",
26
+ memory: "Claude Code Memory",
27
+ custom: "Other",
28
+ };
29
+ function getCatLabel(cat) {
30
+ if (fixedLabels[cat])
31
+ return fixedLabels[cat];
32
+ if (cat.startsWith("project:"))
33
+ return cat.slice("project:".length);
34
+ return cat;
35
+ }
36
+ function getCatSortKey(cat) {
37
+ if (cat === "global")
38
+ return 0;
39
+ if (cat === "memory")
40
+ return 1;
41
+ if (cat.startsWith("project:"))
42
+ return 2;
43
+ return 3;
44
+ }
45
+ const sortedCategories = Object.keys(groups).sort((a, b) => getCatSortKey(a) - getCatSortKey(b) || a.localeCompare(b));
46
+ console.log(chalk.bold(`\nTracking ${files.length} files:\n`));
47
+ for (const cat of sortedCategories) {
48
+ const catFiles = groups[cat];
49
+ if (!catFiles || catFiles.length === 0)
50
+ continue;
51
+ console.log(chalk.dim(`── ${getCatLabel(cat)} ──`));
52
+ for (const file of catFiles) {
53
+ const display = file.path.replace(os.homedir(), "~");
54
+ console.log(` ${display}`);
55
+ }
56
+ console.log();
57
+ }
58
+ // Recent changes
59
+ const recent = getRecentChanges(db, undefined, 5);
60
+ if (recent.length > 0) {
61
+ console.log(chalk.bold("Recent changes:"));
62
+ for (const change of recent) {
63
+ const display = change.path.replace(os.homedir(), "~");
64
+ console.log(chalk.dim(` ${display} — ${change.created_at}`));
65
+ }
66
+ console.log();
67
+ }
68
+ db.close();
69
+ }
70
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { initCommand } from "./commands/init.js";
4
+ import { scanCommand } from "./commands/scan.js";
5
+ import { statusCommand } from "./commands/status.js";
6
+ import { installHookCommand } from "./commands/install-hook.js";
7
+ import { serveCommand } from "./commands/serve.js";
8
+ const program = new Command();
9
+ program
10
+ .name("dotmd")
11
+ .description("Track changes to AI instruction files")
12
+ .version("0.1.0");
13
+ program
14
+ .command("init")
15
+ .description("Open the setup wizard in your browser")
16
+ .option("-p, --port <port>", "Port for setup wizard", "3333")
17
+ .action(initCommand);
18
+ program
19
+ .command("scan", { hidden: true })
20
+ .description("Scan tracked files for changes")
21
+ .option("-q, --quiet", "Only output if changes detected")
22
+ .option("-s, --session", "Session-triggered scan (shows changes since last session)")
23
+ .action(scanCommand);
24
+ program
25
+ .command("status")
26
+ .description("Show tracked files and recent changes")
27
+ .action(statusCommand);
28
+ program
29
+ .command("install-hook", { hidden: true })
30
+ .description("Install shell hook for session-triggered scans")
31
+ .action(installHookCommand);
32
+ program
33
+ .command("serve")
34
+ .description("Start the web dashboard")
35
+ .option("-p, --port <port>", "Port to listen on", "3333")
36
+ .action(serveCommand);
37
+ program.parse();
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,uCAAuC,CAAC;KACpD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,uCAAuC,CAAC;KACpD,MAAM,CAAC,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,CAAC;KAC5D,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,OAAO;KACJ,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;KACjC,WAAW,CAAC,gCAAgC,CAAC;KAC7C,MAAM,CAAC,aAAa,EAAE,iCAAiC,CAAC;KACxD,MAAM,CAAC,eAAe,EAAE,2DAA2D,CAAC;KACpF,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,uCAAuC,CAAC;KACpD,MAAM,CAAC,aAAa,CAAC,CAAC;AAEzB,OAAO;KACJ,OAAO,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;KACzC,WAAW,CAAC,gDAAgD,CAAC;KAC7D,MAAM,CAAC,kBAAkB,CAAC,CAAC;AAE9B,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,yBAAyB,CAAC;KACtC,MAAM,CAAC,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,CAAC;KACxD,MAAM,CAAC,YAAY,CAAC,CAAC;AAExB,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface DotmdConfig {
2
+ scan_roots: string[];
3
+ patterns: string[];
4
+ exclude: string[];
5
+ }
6
+ export declare const DOTMD_DIR: string;
7
+ export declare const CONFIG_PATH: string;
8
+ export declare const DB_PATH: string;
9
+ export declare const DEFAULT_CONFIG: DotmdConfig;
10
+ export declare const SUGGESTED_ROOTS: string[];
11
+ export declare const SUGGESTED_PATTERNS: string[];
12
+ export declare function expandHome(p: string): string;
13
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,eAAO,MAAM,SAAS,QAAoC,CAAC;AAC3D,eAAO,MAAM,WAAW,QAAsC,CAAC;AAC/D,eAAO,MAAM,OAAO,QAAqC,CAAC;AAE1D,eAAO,MAAM,cAAc,EAAE,WAU5B,CAAC;AAEF,eAAO,MAAM,eAAe,UAI3B,CAAC;AAEF,eAAO,MAAM,kBAAkB,UAO9B,CAAC;AAEF,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK5C"}
@@ -0,0 +1,36 @@
1
+ import os from "os";
2
+ import path from "path";
3
+ export const DOTMD_DIR = path.join(os.homedir(), ".dotmd");
4
+ export const CONFIG_PATH = path.join(DOTMD_DIR, "config.yaml");
5
+ export const DB_PATH = path.join(DOTMD_DIR, "history.db");
6
+ export const DEFAULT_CONFIG = {
7
+ scan_roots: [path.join("~", ".claude")],
8
+ patterns: ["CLAUDE.md", "AGENTS.md", "MEMORY.md"],
9
+ exclude: [
10
+ "**/node_modules/**",
11
+ "**/.git/**",
12
+ "**/vendor/**",
13
+ "**/plugins/**",
14
+ "**/cache/**",
15
+ ],
16
+ };
17
+ export const SUGGESTED_ROOTS = [
18
+ "~/.claude",
19
+ "~/projects",
20
+ "~/dev",
21
+ ];
22
+ export const SUGGESTED_PATTERNS = [
23
+ "CLAUDE.md",
24
+ "AGENTS.md",
25
+ "MEMORY.md",
26
+ "SKILL.md",
27
+ ".cursorrules",
28
+ ".windsurfrules",
29
+ ];
30
+ export function expandHome(p) {
31
+ if (p.startsWith("~")) {
32
+ return path.join(os.homedir(), p.slice(1));
33
+ }
34
+ return p;
35
+ }
36
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAQxB,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC3D,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAC/D,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAE1D,MAAM,CAAC,MAAM,cAAc,GAAgB;IACzC,UAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACvC,QAAQ,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,WAAW,CAAC;IACjD,OAAO,EAAE;QACP,oBAAoB;QACpB,YAAY;QACZ,cAAc;QACd,eAAe;QACf,aAAa;KACd;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,WAAW;IACX,YAAY;IACZ,OAAO;CACR,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,WAAW;IACX,WAAW;IACX,WAAW;IACX,UAAU;IACV,cAAc;IACd,gBAAgB;CACjB,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { DotmdConfig } from "./defaults.js";
2
+ export declare function loadConfig(configPath?: string): DotmdConfig;
3
+ export declare function saveConfig(config: DotmdConfig, configPath?: string): void;
4
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EAIZ,MAAM,eAAe,CAAC;AAEvB,wBAAgB,UAAU,CAAC,UAAU,GAAE,MAAoB,GAAG,WAAW,CAiBxE;AAED,wBAAgB,UAAU,CACxB,MAAM,EAAE,WAAW,EACnB,UAAU,GAAE,MAAoB,GAC/B,IAAI,CAON"}
@@ -0,0 +1,28 @@
1
+ import fs from "fs";
2
+ import { parse, stringify } from "yaml";
3
+ import { DEFAULT_CONFIG, CONFIG_PATH, DOTMD_DIR, } from "./defaults.js";
4
+ export function loadConfig(configPath = CONFIG_PATH) {
5
+ if (!fs.existsSync(configPath)) {
6
+ return { ...DEFAULT_CONFIG };
7
+ }
8
+ const raw = fs.readFileSync(configPath, "utf-8");
9
+ try {
10
+ const parsed = parse(raw);
11
+ return {
12
+ scan_roots: parsed.scan_roots ?? DEFAULT_CONFIG.scan_roots,
13
+ patterns: parsed.patterns ?? DEFAULT_CONFIG.patterns,
14
+ exclude: parsed.exclude ?? DEFAULT_CONFIG.exclude,
15
+ };
16
+ }
17
+ catch (err) {
18
+ throw new Error(`Failed to parse config at ${configPath}: ${err.message}`);
19
+ }
20
+ }
21
+ export function saveConfig(config, configPath = CONFIG_PATH) {
22
+ if (!fs.existsSync(DOTMD_DIR)) {
23
+ fs.mkdirSync(DOTMD_DIR, { recursive: true });
24
+ }
25
+ const content = stringify(config);
26
+ fs.writeFileSync(configPath, content, "utf-8");
27
+ }
28
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,EAEL,cAAc,EACd,WAAW,EACX,SAAS,GACV,MAAM,eAAe,CAAC;AAEvB,MAAM,UAAU,UAAU,CAAC,aAAqB,WAAW;IACzD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;IAC/B,CAAC;IAED,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAEjD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAyB,CAAC;QAClD,OAAO;YACL,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,cAAc,CAAC,UAAU;YAC1D,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,cAAc,CAAC,QAAQ;YACpD,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,cAAc,CAAC,OAAO;SAClD,CAAC;IACJ,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,6BAA6B,UAAU,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,MAAmB,EACnB,aAAqB,WAAW;IAEhC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAClC,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface LayoutOptions {
2
+ scripts?: string;
3
+ hideNav?: boolean;
4
+ }
5
+ export declare function layout(title: string, content: string, options?: LayoutOptions): string;
6
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/dashboard/layout.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CA8CtF"}
@@ -0,0 +1,47 @@
1
+ export function layout(title, content, options) {
2
+ const nav = options?.hideNav
3
+ ? ""
4
+ : `<nav class="bg-white border-b border-gray-200 px-6 py-3">
5
+ <div class="max-w-5xl mx-auto flex items-center gap-6">
6
+ <a href="/" class="font-bold text-lg">dotmd</a>
7
+ <a href="/timeline" class="text-gray-600 hover:text-gray-900">Timeline</a>
8
+ <a href="/files" class="text-gray-600 hover:text-gray-900">Files</a>
9
+ <a href="/settings" class="text-gray-600 hover:text-gray-900">Settings</a>
10
+ <button
11
+ onclick="fetch('/api/scan', { method: 'POST' }).then(() => location.reload())"
12
+ class="ml-auto flex items-center gap-1.5 text-gray-400 hover:text-gray-700 transition-colors"
13
+ title="Scan now"
14
+ >
15
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>
16
+ <span class="text-sm">Refresh</span>
17
+ </button>
18
+ </div>
19
+ </nav>`;
20
+ return `<!DOCTYPE html>
21
+ <html lang="en">
22
+ <head>
23
+ <meta charset="UTF-8">
24
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
25
+ <title>${title} — dotmd</title>
26
+ <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
27
+ <style>
28
+ .diff-add { background: #dcfce7; }
29
+ .diff-del { background: #fee2e2; }
30
+ .diff-hunk { color: #6b7280; background: #f3f4f6; }
31
+ </style>
32
+ ${options?.scripts ?? ""}
33
+ <script>
34
+ window.addEventListener('pageshow', function(e) {
35
+ if (e.persisted) location.reload();
36
+ });
37
+ </script>
38
+ </head>
39
+ <body class="bg-gray-50 text-gray-900 min-h-screen">
40
+ ${nav}
41
+ <main class="max-w-5xl mx-auto px-6 py-8">
42
+ ${content}
43
+ </main>
44
+ </body>
45
+ </html>`;
46
+ }
47
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/dashboard/layout.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,OAAe,EAAE,OAAuB;IAC5E,MAAM,GAAG,GAAG,OAAO,EAAE,OAAO;QAC1B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC;;;;;;;;;;;;;;;SAeG,CAAC;IAER,OAAO;;;;;WAKE,KAAK;;;;;;;IAOZ,OAAO,EAAE,OAAO,IAAI,EAAE;;;;;;;;IAQtB,GAAG;;MAED,OAAO;;;QAGL,CAAC;AACT,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { Hono } from "hono";
2
+ import Database from "better-sqlite3";
3
+ export declare function createApp(db: Database.Database): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
4
+ export declare function startServer(port?: number): void;
5
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AA8BtC,wBAAgB,SAAS,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,8EA4U9C;AAED,wBAAgB,WAAW,CAAC,IAAI,GAAE,MAAa,GAAG,IAAI,CAWrD"}