@fouaden/snipit 1.0.0 → 1.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.
package/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Fouad Adeniran
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # snipit 📎
2
+
3
+ <p align="center">
4
+ <strong>A snappy personal code snippet manager for your terminal.</strong><br>
5
+ Save, search, and copy snippets without leaving your workflow.
6
+ </p>
7
+
8
+ <p align="center">
9
+ <a href="https://www.npmjs.com/package/@fouaden/snipit"><img src="https://img.shields.io/npm/v/snipit.svg" alt="npm version"></a>
10
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node.js-18%2B-339933?logo=node.js" alt="Node.js 18+"></a>
11
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="License"></a>
12
+ </p>
13
+
14
+ <p align="center">
15
+ <a href="#install">Install</a> •
16
+ <a href="#quick-start">Quick Start</a> •
17
+ <a href="#commands">Commands</a> •
18
+ <a href="#data">Data</a> •
19
+ </p>
20
+
21
+ ---
22
+
23
+ ## Why snipit?
24
+
25
+ You know that one-liner you Google every time? The `tsconfig` you copy from an old project? The regex you always lose?
26
+
27
+ snipit stores them locally, tagged and searchable, ready to copy to your clipboard in one command. No account, no sync, no internet — just a JSON file on your machine.
28
+
29
+ ---
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ npm install -g snipit
35
+ ```
36
+
37
+ **Requirements**: Node.js 18+
38
+
39
+ ---
40
+
41
+ ## Quick Start
42
+
43
+ ```bash
44
+ # save a snippet
45
+ snipit save "kill port 3000" --code "lsof -ti:3000 | xargs kill" --tag node
46
+
47
+ # find it
48
+ snipit list --tag node
49
+
50
+ # grab it
51
+ snipit get "kill port 3000" --copy
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Commands
57
+
58
+ ### Save a snippet
59
+
60
+ ```bash
61
+ # inline code
62
+ snipit save "kill port 3000" --code "lsof -ti:3000 | xargs kill" --tag node
63
+
64
+ # from a file — reads the file content and stores it
65
+ snipit save "tsconfig base" --file ./tsconfig.json --tag ts
66
+
67
+ # multiline — opens your default editor
68
+ snipit save "async fetch" --tag js
69
+ ```
70
+
71
+ Tags are comma-separated and optional:
72
+
73
+ ```bash
74
+ snipit save "docker prune" --code "docker system prune -af" --tag "docker,ops"
75
+ ```
76
+
77
+ ### List snippets
78
+
79
+ ```bash
80
+ snipit list # all snippets
81
+ snipit list --tag ts # filter by tag
82
+ snipit list --search "docker" # search by title
83
+ ```
84
+
85
+ ### Get a snippet
86
+
87
+ ```bash
88
+ snipit get "kill port 3000" # prints to terminal
89
+ snipit get "kill port 3000" --copy # prints + copies to clipboard
90
+ ```
91
+
92
+ Since output goes to stdout, you can pipe it too:
93
+
94
+ ```bash
95
+ snipit get "tsconfig base" > tsconfig.json
96
+ snipit get "nginx config" | ssh user@server "cat > /etc/nginx/nginx.conf"
97
+ ```
98
+
99
+ ### Delete a snippet
100
+
101
+ ```bash
102
+ snipit delete 3 # use the ID shown in snipit list
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Data
108
+
109
+ Snippets are stored at `~/.snipit/snippets.json` as a plain JSON file. No database, no server, no account.
110
+
111
+ ```json
112
+ {
113
+ "snippets": [
114
+ {
115
+ "id": 1,
116
+ "title": "kill port 3000",
117
+ "code": "lsof -ti:3000 | xargs kill",
118
+ "tags": ["node"],
119
+ "created": "2026-02-25"
120
+ }
121
+ ],
122
+ "lastId": 1
123
+ }
124
+ ```
125
+
126
+ You can back it up, sync it with Dropbox, or commit it to a private repo — it's just a file.
127
+
128
+ ---
129
+
130
+ ## License
131
+
132
+ MIT
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const deleteCommand: Command;
@@ -0,0 +1,20 @@
1
+ import { Command } from 'commander';
2
+ import pc from 'picocolors';
3
+ import { deleteById } from '../utils/storage.js';
4
+ export const deleteCommand = new Command('delete')
5
+ .description('Delete a snippet by ID')
6
+ .argument('<id>', 'The ID of the snippet to delete (get IDs from snipit list)')
7
+ .action((idArg) => {
8
+ const id = parseInt(idArg, 10);
9
+ if (isNaN(id)) {
10
+ console.error(pc.red('✖ ID must be a number. Run snipit list to find IDs.'));
11
+ process.exit(1);
12
+ }
13
+ const deleted = deleteById(id);
14
+ if (!deleted) {
15
+ console.error(pc.red(`✖ No snippet found with ID #${id}`));
16
+ process.exit(1);
17
+ }
18
+ console.log(pc.green(`✔ Deleted snippet #${id}`));
19
+ });
20
+ //# sourceMappingURL=delete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete.js","sourceRoot":"","sources":["../../../src/commands/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,wBAAwB,CAAC;KACrC,QAAQ,CAAC,MAAM,EAAE,4DAA4D,CAAC;KAC9E,MAAM,CAAC,CAAC,KAAa,EAAE,EAAE;IACxB,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAE/B,IAAI,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC,CAAC;QAC7E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;IAE/B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,+BAA+B,EAAE,EAAE,CAAC,CAAC,CAAC;QAC3D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC,CAAC;AACpD,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare const getCommand: Command;
@@ -0,0 +1,27 @@
1
+ import { Command } from "commander";
2
+ import clipboard from "clipboardy";
3
+ import pc from "picocolors";
4
+ import { getByTitle } from "../utils/storage.js";
5
+ export const getCommand = new Command("get")
6
+ .description("Retrieve a snippet by title")
7
+ .argument("<title>", "The title of the snippet to retrive")
8
+ .option("--copy", "Copy the Code to clipboard automatically")
9
+ .action(async (title, options) => {
10
+ const snippet = getByTitle(title);
11
+ if (!snippet) {
12
+ console.error(pc.red(`✖ No snippet found with title "${title}"`));
13
+ console.error(pc.dim(" Run snipit list to see all saved snippets"));
14
+ process.exit(1);
15
+ }
16
+ console.log("");
17
+ console.log(pc.dim(` # ${snippet.title}`));
18
+ console.log("");
19
+ console.log(snippet.code);
20
+ console.log("");
21
+ if (options.copy) {
22
+ await clipboard.write(snippet.code);
23
+ console.log(pc.green(" ✔ Copied to clipboard"));
24
+ console.log("");
25
+ }
26
+ });
27
+ //# sourceMappingURL=get.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get.js","sourceRoot":"","sources":["../../../src/commands/get.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC;KACzC,WAAW,CAAC,6BAA6B,CAAC;KAC1C,QAAQ,CAAC,SAAS,EAAE,qCAAqC,CAAC;KAC1D,MAAM,CAAC,QAAQ,EAAE,0CAA0C,CAAC;KAC5D,MAAM,CAAC,KAAK,EAAE,KAAa,EAAE,OAA2B,EAAE,EAAE;IAC3D,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAElC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,kCAAkC,KAAK,GAAG,CAAC,CAAC,CAAC;QAClE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC,CAAC;QACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const listCommand: Command;
@@ -0,0 +1,36 @@
1
+ import { Command } from 'commander';
2
+ import pc from 'picocolors';
3
+ import { getAll } from '../utils/storage.js';
4
+ export const listCommand = new Command('list')
5
+ .description('List all saved snippets')
6
+ .option('-t, --tag <tag>', 'Filter by tag')
7
+ .option('-s, --search <query>', 'Search snippet titles')
8
+ .action((options) => {
9
+ let snippets = getAll();
10
+ if (options.tag) {
11
+ snippets = snippets.filter((s) => s.tags.includes(options.tag.toLowerCase()));
12
+ }
13
+ if (options.search) {
14
+ const q = options.search.toLowerCase();
15
+ snippets = snippets.filter((s) => s.title.toLowerCase().includes(q));
16
+ }
17
+ if (snippets.length === 0) {
18
+ const hint = options.tag
19
+ ? `No snippets found with tag "${options.tag}"`
20
+ : 'No snippets yet. Use snipit save to add one.';
21
+ console.log(pc.yellow(`\n ${hint}\n`));
22
+ return;
23
+ }
24
+ console.log('');
25
+ for (const s of snippets) {
26
+ const id = pc.bold(pc.cyan(` #${s.id}`));
27
+ const title = pc.white(` ${s.title}`);
28
+ const tags = s.tags.length > 0 ? pc.green(s.tags.join(', ')) + pc.dim(' · ') : '';
29
+ const date = pc.dim(s.createdAt);
30
+ console.log(id);
31
+ console.log(title);
32
+ console.log(` ${tags}${date}`);
33
+ console.log('');
34
+ }
35
+ });
36
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CAAC,yBAAyB,CAAC;KACtC,MAAM,CAAC,iBAAiB,EAAE,eAAe,CAAC;KAC1C,MAAM,CAAC,sBAAsB,EAAE,uBAAuB,CAAC;KACvD,MAAM,CAAC,CAAC,OAA0C,EAAE,EAAE;IACrD,IAAI,QAAQ,GAAc,MAAM,EAAE,CAAC;IAEnC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAChB,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAC/B,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAI,CAAC,WAAW,EAAE,CAAC,CAC5C,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACvC,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG;YACtB,CAAC,CAAC,+BAA+B,OAAO,CAAC,GAAG,GAAG;YAC/C,CAAC,CAAC,8CAA8C,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC;QACxC,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,EAAE,GAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACvC,MAAM,IAAI,GAAI,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnF,MAAM,IAAI,GAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAElC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare const saveCommand: Command;
@@ -0,0 +1,52 @@
1
+ import { Command } from "commander";
2
+ import { readFileSync, existsSync } from "fs";
3
+ import pc from "picocolors";
4
+ import { addSnippet } from "../utils/storage.js";
5
+ import { editor } from "@inquirer/prompts";
6
+ const previewCode = (code) => {
7
+ const lines = code.split("\n");
8
+ const preview = lines.slice(0, 3).join("\n");
9
+ const hasMore = lines.length > 3;
10
+ console.log(pc.dim(preview));
11
+ if (hasMore)
12
+ console.log(pc.dim(` ... +${lines.length - 3} more lines`));
13
+ };
14
+ export const saveCommand = new Command("save")
15
+ .description("Save a new snippet")
16
+ .argument("<title>", "The title of the snippet to save")
17
+ .option("-c, --code <code>", "Inline code string")
18
+ .option("-f, --file <path>", "Read code from a file")
19
+ .option("-t, --tag <tags>", 'Comma-separated tags e.g. "ts,async"')
20
+ .action(async (title, options) => {
21
+ let code;
22
+ if (options.file) {
23
+ if (!existsSync(options.file)) {
24
+ console.error(pc.red(`✖ File not found: ${options.file}`));
25
+ process.exit(1);
26
+ }
27
+ code = readFileSync(options.file, "utf-8").trim();
28
+ }
29
+ else if (options.code) {
30
+ code = options.code.trim();
31
+ }
32
+ else {
33
+ code = await editor({
34
+ message: "Write your snippet (saves when you close the editor):",
35
+ });
36
+ if (!code.trim()) {
37
+ console.error(pc.red("✖ Snippet cannot be empty."));
38
+ process.exit(1);
39
+ }
40
+ }
41
+ const tags = options.tag
42
+ ? options.tag.split(",").map((t) => t.trim().toLowerCase())
43
+ : [];
44
+ const snippet = addSnippet({ title, code, tags });
45
+ console.log(pc.green(`\n✔ Saved "${snippet.title}" as #${snippet.id}`));
46
+ if (tags.length > 0)
47
+ console.log(pc.dim(` Tags: ${tags.join(", ")}`));
48
+ console.log("");
49
+ previewCode(snippet.code);
50
+ console.log("");
51
+ });
52
+ //# sourceMappingURL=save.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"save.js","sourceRoot":"","sources":["../../../src/commands/save.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE;IACnC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACjC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7B,IAAI,OAAO;QAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,KAAK,CAAC,MAAM,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;AAC5E,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CAAC,oBAAoB,CAAC;KACjC,QAAQ,CAAC,SAAS,EAAE,kCAAkC,CAAC;KACvD,MAAM,CAAC,mBAAmB,EAAE,oBAAoB,CAAC;KACjD,MAAM,CAAC,mBAAmB,EAAE,uBAAuB,CAAC;KACpD,MAAM,CAAC,kBAAkB,EAAE,sCAAsC,CAAC;KAClE,MAAM,CACL,KAAK,EACH,KAAa,EACb,OAAuD,EACvD,EAAE;IACF,IAAI,IAAY,CAAC;IAEjB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,qBAAqB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC3D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,CAAC;SAAM,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,MAAM,MAAM,CAAC;YAClB,OAAO,EAAE,uDAAuD;SACjE,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YACjB,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC,CAAC;YACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG;QACtB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC3D,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAElD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,OAAO,CAAC,KAAK,SAAS,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACxE,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IACvE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC,CACF,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import { Command } from "commander";
2
+ import { getCommand } from "./commands/get.js";
3
+ import { saveCommand } from "./commands/save.js";
4
+ import { deleteCommand } from "./commands/delete.js";
5
+ import { listCommand } from "./commands/list.js";
6
+ const program = new Command();
7
+ program
8
+ .name('snipit')
9
+ .description('A local snippet manager')
10
+ .version('1.0.0');
11
+ // Commands
12
+ program.addCommand(saveCommand);
13
+ program.addCommand(getCommand);
14
+ program.addCommand(deleteCommand);
15
+ program.addCommand(listCommand);
16
+ program.parse(process.argv);
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,yBAAyB,CAAC;KACtC,OAAO,CAAC,OAAO,CAAC,CAAA;AAGjB,WAAW;AACX,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAA;AAC9B,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAA;AACjC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAA;AAGjC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ export interface Snippet {
2
+ id: number;
3
+ title: string;
4
+ code: string;
5
+ tags: string[];
6
+ createdAt: string;
7
+ }
8
+ export interface SnipitStore {
9
+ snippets: Snippet[];
10
+ lastId: number;
11
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ import type { Snippet, SnipitStore } from "../types/index.js";
2
+ export declare function readStore(): SnipitStore;
3
+ export declare function writeStore(store: SnipitStore): void;
4
+ export declare function getAll(): Snippet[];
5
+ export declare function getByTitle(title: string): Snippet | undefined;
6
+ export declare function addSnippet(snippet: Omit<Snippet, "id" | "createdAt">): Snippet;
7
+ export declare function deleteById(id: number): boolean;
@@ -0,0 +1,57 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
2
+ import { homedir } from "os";
3
+ import { join } from "path";
4
+ // ~/.snipit/snippets.json
5
+ const SNIPIT_DIR = join(homedir(), ".snipit");
6
+ const STORE_PATH = join(SNIPIT_DIR, "snippets.json");
7
+ const DEFAULT_STORE = {
8
+ snippets: [],
9
+ lastId: 0,
10
+ };
11
+ function ensureDirExists() {
12
+ if (!existsSync(SNIPIT_DIR)) {
13
+ mkdirSync(SNIPIT_DIR, { recursive: true });
14
+ }
15
+ }
16
+ export function readStore() {
17
+ ensureDirExists();
18
+ if (!existsSync(STORE_PATH)) {
19
+ return { ...DEFAULT_STORE };
20
+ }
21
+ const raw = readFileSync(STORE_PATH, "utf-8");
22
+ return JSON.parse(raw);
23
+ }
24
+ export function writeStore(store) {
25
+ ensureDirExists();
26
+ writeFileSync(STORE_PATH, JSON.stringify(store, null, 2), "utf-8");
27
+ }
28
+ export function getAll() {
29
+ return readStore().snippets;
30
+ }
31
+ export function getByTitle(title) {
32
+ const store = readStore();
33
+ return store.snippets.find((s) => s.title.toLowerCase() === title.toLowerCase());
34
+ }
35
+ export function addSnippet(snippet) {
36
+ const store = readStore();
37
+ const newSnippet = {
38
+ ...snippet,
39
+ id: store.lastId + 1,
40
+ createdAt: new Date().toISOString().split("T")[0],
41
+ };
42
+ store.snippets.push(newSnippet);
43
+ store.lastId = newSnippet.id;
44
+ writeStore(store);
45
+ return newSnippet;
46
+ }
47
+ export function deleteById(id) {
48
+ const store = readStore();
49
+ const lastStoreSize = store.snippets.length;
50
+ store.snippets = store.snippets.filter((s) => s.id != id);
51
+ if (store.snippets.length === lastStoreSize) {
52
+ return false;
53
+ }
54
+ writeStore(store);
55
+ return true;
56
+ }
57
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/utils/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAQ,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,0BAA0B;AAC1B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;AAErD,MAAM,aAAa,GAAgB;IACjC,QAAQ,EAAE,EAAE;IACZ,MAAM,EAAE,CAAC;CACV,CAAC;AAEF,SAAS,eAAe;IACtB,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,eAAe,EAAE,CAAC;IAClB,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,GAAG,aAAa,EAAE,CAAC;IAC9B,CAAC;IACD,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAkB;IAC3C,eAAe,EAAE,CAAC;IAClB,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,MAAM;IACpB,OAAO,SAAS,EAAE,CAAC,QAAQ,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAE1B,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,CACxB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,CACrD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAA0C;IACnE,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,UAAU,GAAY;QAC1B,GAAG,OAAO;QACV,EAAE,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC;QACpB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;KAClD,CAAC;IACF,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAChC,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,EAAE,CAAC;IAC7B,UAAU,CAAC,KAAK,CAAC,CAAC;IAClB,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAS;IAChC,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;IACzB,MAAM,aAAa,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAA;IAC3C,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;IACzD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAA;IAChB,CAAC;IACD,UAAU,CAAC,KAAK,CAAC,CAAA;IACjB,OAAO,IAAI,CAAA;AAEf,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,141 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { addSnippet } from "../src/utils/storage.js";
3
+ // Mock the fs module
4
+ vi.mock("fs", () => ({
5
+ existsSync: vi.fn(),
6
+ readFileSync: vi.fn(),
7
+ }));
8
+ // Mock picocolors to return plain strings
9
+ vi.mock("picocolors", () => ({
10
+ default: {
11
+ green: (str) => str,
12
+ red: (str) => str,
13
+ dim: (str) => str,
14
+ },
15
+ green: (str) => str,
16
+ red: (str) => str,
17
+ dim: (str) => str,
18
+ }));
19
+ // Mock the inquirer editor prompt
20
+ vi.mock("@inquirer/prompts", () => ({
21
+ editor: vi.fn(),
22
+ }));
23
+ // Mock storage module - use a function to properly spread the input
24
+ vi.mock("../src/utils/storage.js", () => ({
25
+ addSnippet: vi.fn((snippet) => ({
26
+ ...snippet,
27
+ id: 1,
28
+ createdAt: "2024-01-01",
29
+ })),
30
+ }));
31
+ // Import mocked modules
32
+ import { existsSync, readFileSync } from "fs";
33
+ describe("Save Command", () => {
34
+ let consoleLogSpy;
35
+ let consoleErrorSpy;
36
+ beforeEach(() => {
37
+ vi.clearAllMocks();
38
+ // Spy on console methods
39
+ consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => { });
40
+ consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => { });
41
+ });
42
+ afterEach(() => {
43
+ consoleLogSpy.mockRestore();
44
+ consoleErrorSpy.mockRestore();
45
+ });
46
+ describe("Error handling for file not found", () => {
47
+ it("should error when file path does not exist", async () => {
48
+ // Setup: mock existsSync to return false (file doesn't exist)
49
+ vi.mocked(existsSync).mockReturnValue(false);
50
+ // Create a new program instance for each test to avoid state issues
51
+ const { saveCommand } = await import("../src/commands/save.js");
52
+ // We need to test the error case by calling the action function directly
53
+ // Since commander exits on error, we test the underlying logic
54
+ const filePath = "/non/existent/file.txt";
55
+ const fileExists = existsSync(filePath);
56
+ expect(fileExists).toBe(false);
57
+ });
58
+ it("should not call addSnippet when file does not exist", async () => {
59
+ vi.mocked(existsSync).mockReturnValue(false);
60
+ // Verify that when file doesn't exist, addSnippet should not be called
61
+ // The actual error handling happens inside the command action
62
+ const filePath = "/non/existent/file.txt";
63
+ if (!existsSync(filePath)) {
64
+ // This is the error path
65
+ }
66
+ expect(addSnippet).not.toHaveBeenCalled();
67
+ });
68
+ });
69
+ describe("File-based saving", () => {
70
+ it("should read and save code from file", async () => {
71
+ vi.mocked(existsSync).mockReturnValue(true);
72
+ vi.mocked(readFileSync).mockReturnValue("console.log('hello world')");
73
+ const { readFileSync: rfSync, existsSync: exSync } = await import("fs");
74
+ // Verify mocks are set up correctly
75
+ expect(exSync("/path/to/file.js")).toBe(true);
76
+ expect(rfSync("/path/to/file.js", "utf-8")).toBe("console.log('hello world')");
77
+ });
78
+ });
79
+ describe("addSnippet function", () => {
80
+ it("should add snippet with correct properties", () => {
81
+ const result = addSnippet({
82
+ title: "test-snippet",
83
+ code: "console.log('test')",
84
+ tags: [],
85
+ });
86
+ expect(result).toEqual({
87
+ id: 1,
88
+ title: "test-snippet",
89
+ code: "console.log('test')",
90
+ tags: [],
91
+ createdAt: "2024-01-01",
92
+ });
93
+ });
94
+ it("should generate id and timestamp", () => {
95
+ const result = addSnippet({
96
+ title: "new-snippet",
97
+ code: "code",
98
+ tags: [],
99
+ });
100
+ expect(result.id).toBeDefined();
101
+ expect(result.createdAt).toBeDefined();
102
+ expect(typeof result.id).toBe("number");
103
+ expect(typeof result.createdAt).toBe("string");
104
+ });
105
+ });
106
+ describe("Console output verification", () => {
107
+ it("should log success message format", () => {
108
+ // Test that console.log can be called with expected format
109
+ console.log('✔ Saved "test-snippet" as #1');
110
+ expect(consoleLogSpy).toHaveBeenCalledWith('✔ Saved "test-snippet" as #1');
111
+ });
112
+ it("should log error message format", () => {
113
+ console.error("✖ File not found: /path/to/file.js");
114
+ expect(consoleErrorSpy).toHaveBeenCalledWith("✖ File not found: /path/to/file.js");
115
+ });
116
+ it("should log tag information", () => {
117
+ console.log(" Tags: js, ts, async");
118
+ expect(consoleLogSpy).toHaveBeenCalledWith(" Tags: js, ts, async");
119
+ });
120
+ });
121
+ describe("Tag parsing logic", () => {
122
+ it("should parse comma-separated tags", () => {
123
+ const tagString = "js, ts, async";
124
+ const tags = tagString.split(",").map((t) => t.trim().toLowerCase());
125
+ expect(tags).toEqual(["js", "ts", "async"]);
126
+ });
127
+ it("should handle empty tag string", () => {
128
+ const tagString = "";
129
+ const tags = tagString
130
+ ? tagString.split(",").map((t) => t.trim().toLowerCase())
131
+ : [];
132
+ expect(tags).toEqual([]);
133
+ });
134
+ it("should trim and lowercase tags", () => {
135
+ const tagString = " JAVASCRIPT , TYPESCRIPT ";
136
+ const tags = tagString.split(",").map((t) => t.trim().toLowerCase());
137
+ expect(tags).toEqual(["javascript", "typescript"]);
138
+ });
139
+ });
140
+ });
141
+ //# sourceMappingURL=save.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"save.test.js","sourceRoot":"","sources":["../../test/save.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAEzE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,qBAAqB;AACrB,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IACnB,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE;IACnB,YAAY,EAAE,EAAE,CAAC,EAAE,EAAE;CACtB,CAAC,CAAC,CAAC;AAEJ,0CAA0C;AAC1C,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3B,OAAO,EAAE;QACP,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG;QAC3B,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG;QACzB,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG;KAC1B;IACD,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG;IAC3B,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG;IACzB,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG;CAC1B,CAAC,CAAC,CAAC;AAEJ,kCAAkC;AAClC,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAAC,CAAC;IAClC,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE;CAChB,CAAC,CAAC,CAAC;AAEJ,oEAAoE;AACpE,EAAE,CAAC,IAAI,CAAC,yBAAyB,EAAE,GAAG,EAAE,CAAC,CAAC;IACxC,UAAU,EAAE,EAAE,CAAC,EAAE,CACf,CAAC,OAAwD,EAAE,EAAE,CAAC,CAAC;QAC7D,GAAG,OAAO;QACV,EAAE,EAAE,CAAC;QACL,SAAS,EAAE,YAAY;KACxB,CAAC,CACH;CACF,CAAC,CAAC,CAAC;AAEJ,wBAAwB;AACxB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAE9C,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,IAAI,aAAkB,CAAC;IACvB,IAAI,eAAoB,CAAC;IAEzB,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;QAEnB,yBAAyB;QACzB,aAAa,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACtE,eAAe,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,aAAa,CAAC,WAAW,EAAE,CAAC;QAC5B,eAAe,CAAC,WAAW,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,mCAAmC,EAAE,GAAG,EAAE;QACjD,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;YAC1D,8DAA8D;YAC9D,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;YAE7C,oEAAoE;YACpE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;YAEhE,yEAAyE;YACzE,+DAA+D;YAC/D,MAAM,QAAQ,GAAG,wBAAwB,CAAC;YAC1C,MAAM,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;YAExC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;YACnE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;YAE7C,uEAAuE;YACvE,8DAA8D;YAC9D,MAAM,QAAQ,GAAG,wBAAwB,CAAC;YAE1C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC1B,yBAAyB;YAC3B,CAAC;YAED,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;YACnD,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YAC5C,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,eAAe,CAAC,4BAA4B,CAAC,CAAC;YAEtE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;YAExE,oCAAoC;YACpC,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAC9C,4BAA4B,CAC7B,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;YACpD,MAAM,MAAM,GAAG,UAAU,CAAC;gBACxB,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,qBAAqB;gBAC3B,IAAI,EAAE,EAAE;aACT,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBACrB,EAAE,EAAE,CAAC;gBACL,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,qBAAqB;gBAC3B,IAAI,EAAE,EAAE;gBACR,SAAS,EAAE,YAAY;aACxB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC1C,MAAM,MAAM,GAAG,UAAU,CAAC;gBACxB,KAAK,EAAE,aAAa;gBACpB,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,EAAE;aACT,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;YAChC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;YACvC,MAAM,CAAC,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,CAAC,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;QAC3C,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;YAC3C,2DAA2D;YAC3D,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;YAE5C,MAAM,CAAC,aAAa,CAAC,CAAC,oBAAoB,CACxC,8BAA8B,CAC/B,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;YACzC,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;YAEpD,MAAM,CAAC,eAAe,CAAC,CAAC,oBAAoB,CAC1C,oCAAoC,CACrC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;YACpC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;YAErC,MAAM,CAAC,aAAa,CAAC,CAAC,oBAAoB,CAAC,uBAAuB,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;YAC3C,MAAM,SAAS,GAAG,eAAe,CAAC;YAClC,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;YAErE,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;YACxC,MAAM,SAAS,GAAW,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,SAAS;gBACpB,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;gBACjE,CAAC,CAAC,EAAE,CAAC;YAEP,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;YACxC,MAAM,SAAS,GAAG,8BAA8B,CAAC;YACjD,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;YAErE,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,288 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { addSnippet, getAll, getByTitle, deleteById, readStore, writeStore, } from "../src/utils/storage.js";
3
+ // Import mocked modules
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
5
+ // Mock the fs module
6
+ vi.mock("fs", () => ({
7
+ existsSync: vi.fn(),
8
+ mkdirSync: vi.fn(),
9
+ readFileSync: vi.fn(),
10
+ writeFileSync: vi.fn(),
11
+ }));
12
+ // Mock the os module for homedir
13
+ vi.mock("os", () => ({
14
+ homedir: vi.fn(() => "/mocked/home"),
15
+ }));
16
+ // Mock path to avoid issues with ESM
17
+ vi.mock("path", () => ({
18
+ join: (...args) => args.join("/"),
19
+ }));
20
+ describe("Storage Utility", () => {
21
+ let mockStore;
22
+ beforeEach(() => {
23
+ // Reset mock store before each test
24
+ mockStore = {
25
+ snippets: [],
26
+ lastId: 0,
27
+ };
28
+ // Reset all mocks
29
+ vi.clearAllMocks();
30
+ // Setup default mock implementations
31
+ vi.mocked(existsSync).mockReturnValue(true);
32
+ vi.mocked(mkdirSync).mockReturnValue(undefined);
33
+ vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
34
+ vi.mocked(writeFileSync).mockReturnValue(undefined);
35
+ });
36
+ describe("addSnippet", () => {
37
+ it("should add a snippet with generated id and timestamp", () => {
38
+ const result = addSnippet({
39
+ title: "Test Snippet",
40
+ code: 'console.log("hello")',
41
+ tags: ["js", "test"],
42
+ });
43
+ // Verify id is generated (should be lastId + 1 = 1)
44
+ expect(result.id).toBe(1);
45
+ // Verify timestamp is in YYYY-MM-DD format
46
+ expect(result.createdAt).toMatch(/^\d{4}-\d{2}-\d{2}$/);
47
+ // Verify the snippet object has all expected properties
48
+ expect(result).toEqual({
49
+ id: 1,
50
+ title: "Test Snippet",
51
+ code: 'console.log("hello")',
52
+ tags: ["js", "test"],
53
+ createdAt: expect.any(String),
54
+ });
55
+ // Verify writeStore was called
56
+ expect(writeFileSync).toHaveBeenCalled();
57
+ });
58
+ it("should generate sequential IDs", () => {
59
+ // Mock readFileSync to return updated store after each addSnippet call
60
+ let callCount = 0;
61
+ vi.mocked(readFileSync).mockImplementation(() => {
62
+ callCount++;
63
+ if (callCount === 1) {
64
+ return JSON.stringify({ snippets: [], lastId: 0 });
65
+ }
66
+ else {
67
+ return JSON.stringify({
68
+ snippets: [
69
+ {
70
+ id: 1,
71
+ title: "First Snippet",
72
+ code: "code1",
73
+ tags: [],
74
+ createdAt: "2024-01-01",
75
+ },
76
+ ],
77
+ lastId: 1,
78
+ });
79
+ }
80
+ });
81
+ const snippet1 = addSnippet({
82
+ title: "First Snippet",
83
+ code: "code1",
84
+ tags: [],
85
+ });
86
+ const snippet2 = addSnippet({
87
+ title: "Second Snippet",
88
+ code: "code2",
89
+ tags: [],
90
+ });
91
+ expect(snippet1.id).toBe(1);
92
+ expect(snippet2.id).toBe(2);
93
+ });
94
+ it("should use Omit type - id and createdAt should not be in input", () => {
95
+ // This test verifies the Omit logic by ensuring the function
96
+ // only accepts title, code, and tags (not id or createdAt)
97
+ const input = {
98
+ title: "Test",
99
+ code: "test code",
100
+ tags: ["tag1"],
101
+ };
102
+ // TypeScript should only allow these three properties
103
+ // If Omit is working correctly, we cannot pass id or createdAt
104
+ const result = addSnippet(input);
105
+ // Verify the result has id and createdAt added by the function
106
+ expect(result.id).toBeDefined();
107
+ expect(result.createdAt).toBeDefined();
108
+ expect(result.title).toBe(input.title);
109
+ expect(result.code).toBe(input.code);
110
+ expect(result.tags).toEqual(input.tags);
111
+ });
112
+ it("should handle empty tags array", () => {
113
+ const result = addSnippet({
114
+ title: "No Tags",
115
+ code: "some code",
116
+ tags: [],
117
+ });
118
+ expect(result.tags).toEqual([]);
119
+ });
120
+ it("should write to the store file", () => {
121
+ addSnippet({
122
+ title: "Test",
123
+ code: "code",
124
+ tags: [],
125
+ });
126
+ expect(writeFileSync).toHaveBeenCalledWith("/mocked/home/.snipit/snippets.json", expect.any(String), "utf-8");
127
+ });
128
+ });
129
+ describe("getAll", () => {
130
+ it("should return all snippets from store", () => {
131
+ mockStore.snippets = [
132
+ {
133
+ id: 1,
134
+ title: "Snippet 1",
135
+ code: "code1",
136
+ tags: [],
137
+ createdAt: "2024-01-01",
138
+ },
139
+ {
140
+ id: 2,
141
+ title: "Snippet 2",
142
+ code: "code2",
143
+ tags: ["js"],
144
+ createdAt: "2024-01-02",
145
+ },
146
+ ];
147
+ mockStore.lastId = 2;
148
+ vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
149
+ const result = getAll();
150
+ expect(result).toHaveLength(2);
151
+ expect(result[0].title).toBe("Snippet 1");
152
+ expect(result[1].title).toBe("Snippet 2");
153
+ });
154
+ it("should return empty array when no snippets exist", () => {
155
+ mockStore.snippets = [];
156
+ mockStore.lastId = 0;
157
+ vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
158
+ const result = getAll();
159
+ expect(result).toEqual([]);
160
+ });
161
+ });
162
+ describe("getByTitle", () => {
163
+ it("should find snippet by title (case insensitive)", () => {
164
+ mockStore.snippets = [
165
+ {
166
+ id: 1,
167
+ title: "JavaScript Code",
168
+ code: "console.log",
169
+ tags: [],
170
+ createdAt: "2024-01-01",
171
+ },
172
+ ];
173
+ vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
174
+ const result = getByTitle("javascript code");
175
+ expect(result).toBeDefined();
176
+ expect(result?.title).toBe("JavaScript Code");
177
+ });
178
+ it("should return undefined for non-existent title", () => {
179
+ mockStore.snippets = [
180
+ {
181
+ id: 1,
182
+ title: "Existing",
183
+ code: "code",
184
+ tags: [],
185
+ createdAt: "2024-01-01",
186
+ },
187
+ ];
188
+ vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
189
+ const result = getByTitle("NonExistent");
190
+ expect(result).toBeUndefined();
191
+ });
192
+ });
193
+ describe("deleteById", () => {
194
+ it("should delete snippet by id and return true", () => {
195
+ mockStore.snippets = [
196
+ {
197
+ id: 1,
198
+ title: "To Delete",
199
+ code: "code",
200
+ tags: [],
201
+ createdAt: "2024-01-01",
202
+ },
203
+ {
204
+ id: 2,
205
+ title: "To Keep",
206
+ code: "code",
207
+ tags: [],
208
+ createdAt: "2024-01-02",
209
+ },
210
+ ];
211
+ mockStore.lastId = 2;
212
+ vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
213
+ const result = deleteById(1);
214
+ expect(result).toBe(true);
215
+ expect(writeFileSync).toHaveBeenCalled();
216
+ });
217
+ it("should return false when id does not exist", () => {
218
+ mockStore.snippets = [
219
+ {
220
+ id: 1,
221
+ title: "Existing",
222
+ code: "code",
223
+ tags: [],
224
+ createdAt: "2024-01-01",
225
+ },
226
+ ];
227
+ mockStore.lastId = 1;
228
+ vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
229
+ const result = deleteById(999);
230
+ expect(result).toBe(false);
231
+ expect(writeFileSync).not.toHaveBeenCalled();
232
+ });
233
+ });
234
+ describe("readStore", () => {
235
+ it("should return default store when file does not exist", () => {
236
+ vi.mocked(existsSync).mockReturnValue(false);
237
+ const result = readStore();
238
+ expect(result).toEqual({
239
+ snippets: [],
240
+ lastId: 0,
241
+ });
242
+ });
243
+ it("should parse and return existing store", () => {
244
+ mockStore.snippets = [
245
+ {
246
+ id: 1,
247
+ title: "Test",
248
+ code: "code",
249
+ tags: [],
250
+ createdAt: "2024-01-01",
251
+ },
252
+ ];
253
+ mockStore.lastId = 1;
254
+ vi.mocked(existsSync).mockReturnValue(true);
255
+ vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockStore));
256
+ const result = readStore();
257
+ expect(result.snippets).toHaveLength(1);
258
+ expect(result.lastId).toBe(1);
259
+ });
260
+ });
261
+ describe("writeStore", () => {
262
+ it("should write store to file", () => {
263
+ const store = {
264
+ snippets: [
265
+ {
266
+ id: 1,
267
+ title: "Test",
268
+ code: "code",
269
+ tags: [],
270
+ createdAt: "2024-01-01",
271
+ },
272
+ ],
273
+ lastId: 1,
274
+ };
275
+ writeStore(store);
276
+ expect(writeFileSync).toHaveBeenCalledWith("/mocked/home/.snipit/snippets.json", JSON.stringify(store, null, 2), "utf-8");
277
+ });
278
+ it("should ensure directory exists before writing", () => {
279
+ // Make sure existsSync returns false so ensureDirExists creates the directory
280
+ vi.mocked(existsSync).mockReturnValue(false);
281
+ writeStore({ snippets: [], lastId: 0 });
282
+ expect(mkdirSync).toHaveBeenCalledWith("/mocked/home/.snipit", {
283
+ recursive: true,
284
+ });
285
+ });
286
+ });
287
+ });
288
+ //# sourceMappingURL=storage.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.test.js","sourceRoot":"","sources":["../../test/storage.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vite").UserConfig;
2
+ export default _default;
@@ -0,0 +1,16 @@
1
+ import { defineConfig } from "vitest/config";
2
+ export default defineConfig({
3
+ test: {
4
+ globals: true,
5
+ environment: "node",
6
+ include: ["test/**/*.test.ts"],
7
+ coverage: {
8
+ provider: "v8",
9
+ reporter: ["text", "json", "html"],
10
+ },
11
+ typecheck: {
12
+ enabled: true,
13
+ },
14
+ },
15
+ });
16
+ //# sourceMappingURL=vitest.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.config.js","sourceRoot":"","sources":["../vitest.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,eAAe,YAAY,CAAC;IAC1B,IAAI,EAAE;QACJ,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,MAAM;QACnB,OAAO,EAAE,CAAC,mBAAmB,CAAC;QAC9B,QAAQ,EAAE;YACR,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SACnC;QACD,SAAS,EAAE;YACT,OAAO,EAAE,IAAI;SACd;KACF;CACF,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,18 +1,35 @@
1
1
  {
2
2
  "name": "@fouaden/snipit",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A lightweight CLI tool to save, search and tag code snippets locally. Stop re-typing; start snipping.",
5
5
  "main": "bin/snipit.js",
6
6
  "scripts": {
7
7
  "dev": "tsx bin/snipit.js",
8
8
  "build": "tsc",
9
- "start": "node bin/snipit.js"
9
+ "start": "node bin/snipit.js",
10
+ "test": "vitest",
11
+ "test:run": "vitest run",
12
+ "test:ui": "vitest --ui"
10
13
  },
11
14
  "bin": {
12
15
  "snipit": "./bin/snipit.js"
13
16
  },
14
- "files": ["dist/", "bin/"],
15
- "keywords": [],
17
+ "files": [
18
+ "dist/",
19
+ "bin/"
20
+ ],
21
+ "keywords": [
22
+ "cli",
23
+ "snipit",
24
+ "snippet-manager",
25
+ "developer-tools",
26
+ "typescript",
27
+ "productivity",
28
+ "code-snippets",
29
+ "terminal",
30
+ "local-storage",
31
+ "node-cli"
32
+ ],
16
33
  "author": "fouadbuilds",
17
34
  "license": "MIT",
18
35
  "type": "module",
@@ -20,13 +37,13 @@
20
37
  "@types/node": "^25.3.0",
21
38
  "ts-node": "^10.9.2",
22
39
  "tsx": "^4.21.0",
23
- "typescript": "^5.9.3"
40
+ "typescript": "^5.9.3",
41
+ "vitest": "^4.0.18"
24
42
  },
25
43
  "dependencies": {
26
44
  "@inquirer/prompts": "^8.3.0",
27
45
  "clipboardy": "^5.3.1",
28
46
  "commander": "^14.0.3",
29
- "picocolors": "^1.1.1",
30
- "vitest": "^4.0.18"
47
+ "picocolors": "^1.1.1"
31
48
  }
32
49
  }