@dev_desh/flux-cap 0.1.2 → 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.
Files changed (3) hide show
  1. package/README.md +140 -32
  2. package/dist/index.js +244 -116
  3. package/package.json +11 -2
package/README.md CHANGED
@@ -6,6 +6,46 @@
6
6
 
7
7
  flux-cap is a terminal-native tool that captures your thoughts, tracks your context, and integrates seamlessly with your git workflow. Built specifically for developers who context-switch frequently.
8
8
 
9
+ ## Installation
10
+
11
+ Install flux-cap globally using your preferred package manager:
12
+
13
+ ```bash
14
+ # Using npm
15
+ npm install -g @dev_desh/flux-cap
16
+
17
+ # Using pnpm
18
+ pnpm install -g @dev_desh/flux-cap
19
+
20
+ # Using bun
21
+ bun install -g @dev_desh/flux-cap
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ### 1. Initialize flux-cap in your project root folder
27
+ ```bash
28
+ flux init
29
+ ```
30
+ *Interactive setup will ask about your privacy preferences*
31
+
32
+ ### 2. Start capturing thoughts
33
+ ```bash
34
+ flux dump "remember to add error handling to auth module"
35
+ flux dump "bug in user validation - check line 42"
36
+ flux dump "idea: add dark mode toggle"
37
+ ```
38
+
39
+ ### 3. Search your brain dumps
40
+ ```bash
41
+ # Search with a query
42
+ flux search "auth"
43
+
44
+ # List recent dumps (no query)
45
+ flux search
46
+ ```
47
+
48
+
9
49
  ## Features
10
50
 
11
51
  ### Brain Dump System
@@ -32,53 +72,96 @@ flux-cap is a terminal-native tool that captures your thoughts, tracks your cont
32
72
  - .gitignore management
33
73
  - Works in non-git directories too
34
74
 
35
- ## Installation
75
+ ### Parent Directory Support
76
+ - Initialize flux-cap once in your project root and use it from any subdirectory
77
+ - Automatically discovers `.flux` configuration by traversing up the directory tree
78
+ - No need to initialize in every subfolder - works project-wide
79
+ - Seamlessly handles monorepos and complex project structures
36
80
 
37
- ```bash
38
- # Clone the repository
39
- git clone https://github.com/yourusername/flux-cap
40
- cd flux-cap
41
81
 
42
- # Install dependencies
43
- bun install
82
+ ## Commands
44
83
 
45
- # Run locally
46
- bun run dev <command>
47
- ```
84
+ | Command | Description | Example |
85
+ |---------|-------------|---------|
86
+ | `flux init` | Initialize flux-cap with privacy setup | `flux init` |
87
+ | `flux dump <message...>` | Capture a brain dump | `flux dump "fix the bug in auth.ts"` |
88
+ | `flux search [query...]` | Search brain dumps or list recent ones | `flux search "authentication"` |
89
+ | `flux config <fields...>` | View or update configuration | `flux config` |
90
+ | `flux reset` | Complete reset (deletes all data) | `flux reset` |
48
91
 
49
- ## Quick Start
92
+ ## Use Cases
93
+
94
+ ### Context Switching
95
+ ```bash
96
+ # Before switching tasks
97
+ flux dump "was working on user auth, next: add validation to login form"
98
+
99
+ # After interruption
100
+ flux search "auth" # Quickly find where you left off
101
+ ```
50
102
 
51
- ### 1. Initialize flux-cap in your project
103
+ ### Bug Tracking
52
104
  ```bash
53
- bun run dev init
105
+ flux dump "weird bug in payment flow - users can't checkout"
106
+ flux dump "bug seems related to session timeout"
107
+
108
+ # Later...
109
+ flux search "payment bug"
54
110
  ```
55
- *Interactive setup will ask about your privacy preferences*
56
111
 
57
- ### 2. Start capturing thoughts
112
+ ### Idea Capture
58
113
  ```bash
59
- bun run dev dump "remember to add error handling to auth module"
60
- bun run dev dump "bug in user validation - check line 42"
61
- bun run dev dump "idea: add dark mode toggle"
114
+ flux dump "idea: add keyboard shortcuts to dashboard"
115
+ flux dump "maybe use React.memo for performance optimization"
62
116
  ```
63
117
 
64
- ### 3. Search your brain dumps
118
+ ## Automated Versioning
119
+
120
+ flux-cap uses [Changesets](https://github.com/changesets/changesets) for automated semantic versioning:
121
+
122
+ ### What happens when you merge a PR:
123
+ 1. **Automatic Analysis**: GitHub Actions analyzes your PR changes
124
+ 2. **Smart Version Bumping**: Determines appropriate version (major/minor/patch) based on:
125
+ - PR title and description
126
+ - Commit messages
127
+ - Files changed
128
+ 3. **Changelog Generation**: Creates detailed changelog entries
129
+ 4. **Version Updates**: Updates `package.json` automatically
130
+ 5. **Git Integration**: Commits changes back to main branch
131
+
132
+ ### Version Bump Rules:
133
+ - **Major** (`1.0.0 → 2.0.0`): Breaking changes, removed features, incompatible API changes
134
+ - **Minor** (`1.0.0 → 1.1.0`): New features, new commands, backwards-compatible enhancements
135
+ - **Patch** (`1.0.0 → 1.0.1`): Bug fixes, documentation updates, refactoring, performance improvements
136
+
137
+ ### Manual Changesets:
65
138
  ```bash
66
- # Search with a query
67
- bun run dev search "auth"
139
+ # Add a changeset manually (if needed)
140
+ bun run changeset
68
141
 
69
- # List recent dumps (no query)
70
- bun run dev search
71
- ```
142
+ # Check pending changesets
143
+ bun run changeset:status
72
144
 
73
- ## Commands
145
+ # Apply version changes locally
146
+ bun run changeset:version
74
147
 
75
- | Command | Description | Example |
76
- |---------|-------------|---------|
77
- | `flux init` | Initialize flux-cap with privacy setup | `bun run dev init` |
78
- | `flux dump <message...>` | Capture a brain dump | `bun run dev dump "fix the bug in auth.ts"` |
79
- | `flux search [query...]` | Search brain dumps or list recent ones | `bun run dev search "authentication"` |
80
- | `flux config <fields...>` | View or update configuration | `bun run dev config` |
81
- | `flux reset` | Complete reset (deletes all data) | `bun run dev reset` |
148
+ ## Development
149
+
150
+ Want to contribute or run locally?
151
+
152
+ ```bash
153
+ # Clone and setup
154
+ git clone https://github.com/yourusername/flux-cap
155
+ cd flux-cap
156
+ bun install
157
+
158
+ # Run in development mode
159
+ bun run dev <command>
160
+
161
+ # Build and test locally
162
+ bun run build
163
+ npm link
164
+ ```
82
165
 
83
166
  ## Configuration
84
167
 
@@ -211,6 +294,31 @@ src/
211
294
 
212
295
  This is currently a personal learning project, but feedback and suggestions are welcome!
213
296
 
297
+ How to control version bumps:
298
+
299
+ ### Method 1: Use GitHub Labels
300
+ Add these labels to your repository and apply them to PRs:
301
+ - `major` or `breaking` → Major version bump
302
+ - `minor` or `feature` → Minor version bump
303
+ - `patch` or `bugfix` → Patch version bump
304
+
305
+ ### Method 2: Use PR Title Syntax
306
+ Start your PR title with the version type in brackets:
307
+ - `[major] Remove deprecated API endpoints`
308
+ - `[minor] Add new search command`
309
+ - `[patch] Fix memory leak in dump command`
310
+
311
+ ### Method 3: Automatic Detection (Conservative)
312
+ The system will now only auto-detect major bumps with very explicit indicators like:
313
+ - "breaking change"
314
+ - "breaking:"
315
+ - "major:"
316
+ - "!breaking"
317
+ - "remove api"
318
+ - "delete command"
319
+
320
+ **Everything else defaults to patch unless you have clear feature indicators for minor.**
321
+
214
322
  ## License
215
323
 
216
324
  MIT
package/dist/index.js CHANGED
@@ -7861,10 +7861,10 @@ var require_lib2 = __commonJS((exports) => {
7861
7861
  exports.analyse = analyse;
7862
7862
  var detectFile = (filepath, opts = {}) => new Promise((resolve, reject) => {
7863
7863
  let fd;
7864
- const fs = (0, node_1.default)();
7864
+ const fs2 = (0, node_1.default)();
7865
7865
  const handler = (err, buffer) => {
7866
7866
  if (fd) {
7867
- fs.closeSync(fd);
7867
+ fs2.closeSync(fd);
7868
7868
  }
7869
7869
  if (err) {
7870
7870
  reject(err);
@@ -7876,9 +7876,9 @@ var require_lib2 = __commonJS((exports) => {
7876
7876
  };
7877
7877
  const sampleSize = (opts === null || opts === undefined ? undefined : opts.sampleSize) || 0;
7878
7878
  if (sampleSize > 0) {
7879
- fd = fs.openSync(filepath, "r");
7879
+ fd = fs2.openSync(filepath, "r");
7880
7880
  let sample = Buffer.allocUnsafe(sampleSize);
7881
- fs.read(fd, sample, 0, sampleSize, opts.offset, (err, bytesRead) => {
7881
+ fs2.read(fd, sample, 0, sampleSize, opts.offset, (err, bytesRead) => {
7882
7882
  if (err) {
7883
7883
  handler(err, null);
7884
7884
  } else {
@@ -7890,22 +7890,22 @@ var require_lib2 = __commonJS((exports) => {
7890
7890
  });
7891
7891
  return;
7892
7892
  }
7893
- fs.readFile(filepath, handler);
7893
+ fs2.readFile(filepath, handler);
7894
7894
  });
7895
7895
  exports.detectFile = detectFile;
7896
7896
  var detectFileSync = (filepath, opts = {}) => {
7897
- const fs = (0, node_1.default)();
7897
+ const fs2 = (0, node_1.default)();
7898
7898
  if (opts && opts.sampleSize) {
7899
- const fd = fs.openSync(filepath, "r");
7899
+ const fd = fs2.openSync(filepath, "r");
7900
7900
  let sample = Buffer.allocUnsafe(opts.sampleSize);
7901
- const bytesRead = fs.readSync(fd, sample, 0, opts.sampleSize, opts.offset);
7901
+ const bytesRead = fs2.readSync(fd, sample, 0, opts.sampleSize, opts.offset);
7902
7902
  if (bytesRead < opts.sampleSize) {
7903
7903
  sample = sample.subarray(0, bytesRead);
7904
7904
  }
7905
- fs.closeSync(fd);
7905
+ fs2.closeSync(fd);
7906
7906
  return (0, exports.detect)(sample);
7907
7907
  }
7908
- return (0, exports.detect)(fs.readFileSync(filepath));
7908
+ return (0, exports.detect)(fs2.readFileSync(filepath));
7909
7909
  };
7910
7910
  exports.detectFileSync = detectFileSync;
7911
7911
  exports.default = {
@@ -20625,7 +20625,7 @@ var {
20625
20625
  } = import__.default;
20626
20626
 
20627
20627
  // src/commands/init.command.ts
20628
- import fs from "fs";
20628
+ import fs2 from "fs";
20629
20629
 
20630
20630
  // src/utils/constants.ts
20631
20631
  var FLUX_FOLDER_PATH = ".flux/";
@@ -20645,7 +20645,7 @@ var FLUX_DEFAULT_CONFIG = {
20645
20645
  hideUncommittedChanges: false
20646
20646
  },
20647
20647
  search: {
20648
- searchFields: ["message", "workingDir", "branch", "tags"],
20648
+ searchFields: ["message", "workingDir", "branch", "tags", "id"],
20649
20649
  resultLimit: 10,
20650
20650
  fuseOptions: {
20651
20651
  threshold: 0.3,
@@ -20681,21 +20681,40 @@ function getCurrentBranch(config) {
20681
20681
  }
20682
20682
  }
20683
20683
  // src/utils/lib.ts
20684
+ import fs from "fs";
20685
+ import path from "path";
20686
+ async function getFluxPath() {
20687
+ const cwd = process.cwd();
20688
+ let fullPath = cwd.split(path.sep);
20689
+ while (true) {
20690
+ let parentPath = fullPath.join(path.sep) + "/.flux";
20691
+ if (fs.existsSync(parentPath)) {
20692
+ return parentPath.split(".flux")[0];
20693
+ break;
20694
+ }
20695
+ fullPath.pop();
20696
+ if (fullPath.length === 0) {
20697
+ break;
20698
+ }
20699
+ }
20700
+ console.error("No .flux directory found in the current or parent directories. Please run 'flux init' to initialize.");
20701
+ process.exit(1);
20702
+ }
20684
20703
  async function createIfNotExists(folderPath, type, data) {
20685
20704
  try {
20686
- const fs = await import("fs");
20687
- if (!fs.existsSync(folderPath)) {
20705
+ const fs2 = await import("fs");
20706
+ if (!fs2.existsSync(folderPath)) {
20688
20707
  if (type === "file") {
20689
- fs.writeFileSync(folderPath, data || "");
20708
+ fs2.writeFileSync(folderPath, data || "");
20690
20709
  return;
20691
20710
  }
20692
- fs.mkdirSync(folderPath, { recursive: true });
20711
+ fs2.mkdirSync(folderPath, { recursive: true });
20693
20712
  } else {
20694
20713
  if (type === "file") {
20695
20714
  try {
20696
- const stats = fs.statSync(folderPath);
20715
+ const stats = fs2.statSync(folderPath);
20697
20716
  if (stats.size === 0 && data) {
20698
- fs.writeFileSync(folderPath, data);
20717
+ fs2.writeFileSync(folderPath, data);
20699
20718
  }
20700
20719
  } catch (writeError) {
20701
20720
  if (writeError.code === "EACCES" || writeError.code === "EPERM") {
@@ -20705,7 +20724,7 @@ async function createIfNotExists(folderPath, type, data) {
20705
20724
  }
20706
20725
  } else {
20707
20726
  try {
20708
- fs.accessSync(folderPath, fs.constants.W_OK);
20727
+ fs2.accessSync(folderPath, fs2.constants.W_OK);
20709
20728
  } catch (accessError) {
20710
20729
  if (accessError.code === "EACCES" || accessError.code === "EPERM") {
20711
20730
  throw new Error(`No write permissions for directory: ${folderPath}`);
@@ -20717,24 +20736,24 @@ async function createIfNotExists(folderPath, type, data) {
20717
20736
  throw error;
20718
20737
  }
20719
20738
  }
20720
- async function createBrainDumpFileIfNotExists(dateString) {
20721
- await createIfNotExists(`${FLUX_BRAIN_DUMP_PATH}${dateString}.json`, "file", JSON.stringify({
20739
+ async function createBrainDumpFileIfNotExists(dateString, fluxPath) {
20740
+ await createIfNotExists(`${fluxPath}${FLUX_BRAIN_DUMP_PATH}${dateString}.json`, "file", JSON.stringify({
20722
20741
  fluxVersion: "0.0.1",
20723
20742
  month: dateString,
20724
20743
  dumps: []
20725
20744
  }));
20726
20745
  }
20727
- async function getConfigFile() {
20728
- const fs = await import("fs");
20729
- const configPath = `${FLUX_CONFIG_PATH}`;
20730
- let config = JSON.parse(fs.readFileSync(configPath, "utf8"));
20746
+ async function getConfigFile(fluxPath) {
20747
+ const fs2 = await import("fs");
20748
+ const configPath = `${fluxPath}${FLUX_CONFIG_PATH}`;
20749
+ let config = JSON.parse(fs2.readFileSync(configPath, "utf8"));
20731
20750
  return config;
20732
20751
  }
20733
- async function getAllBrainDumpFilePaths() {
20734
- const fs = await import("fs");
20735
- const path = await import("path");
20736
- const files = fs.readdirSync(FLUX_BRAIN_DUMP_PATH);
20737
- return files.filter((file) => file.endsWith(".json")).map((file) => path.join(FLUX_BRAIN_DUMP_PATH, file));
20752
+ async function getAllBrainDumpFilePaths(fluxPath) {
20753
+ const fs2 = await import("fs");
20754
+ const path2 = await import("path");
20755
+ const files = fs2.readdirSync(fluxPath + FLUX_BRAIN_DUMP_PATH);
20756
+ return files.filter((file) => file.endsWith(".json")).map((file) => path2.join(fluxPath + FLUX_BRAIN_DUMP_PATH, file));
20738
20757
  }
20739
20758
  // src/utils/helper.ts
20740
20759
  function getMonthString() {
@@ -20743,9 +20762,79 @@ function getMonthString() {
20743
20762
  const month = String(currentDate.getMonth() + 1).padStart(2, "0");
20744
20763
  return `${year}-${month}`;
20745
20764
  }
20746
- function searchResultFormat({ message, timestamp, score, index }) {
20747
- const formattedTimestamp = new Date(timestamp).toLocaleString();
20748
- return `${index !== undefined ? index + 1 : ""}. [${score || "0.00"}] [${formattedTimestamp}] ${message}`;
20765
+ function displaySearchResults(results, query) {
20766
+ if (results.length === 0) {
20767
+ if (query) {
20768
+ console.log(`No brain dumps found matching "${query}"`);
20769
+ } else {
20770
+ console.log("No brain dumps found. Try 'flux dump' to create your first one!");
20771
+ }
20772
+ return;
20773
+ }
20774
+ const queryText = query ? ` for "${query}"` : "";
20775
+ console.log(`
20776
+ Found ${results.length} brain dump${results.length === 1 ? "" : "s"}${queryText}:
20777
+ `);
20778
+ const terminalWidth = process.stdout.columns || 80;
20779
+ const maxIndexWidth = results.length.toString().length;
20780
+ results.forEach((result, index) => {
20781
+ const dump = result.item;
20782
+ const score = result.score?.toFixed(2) || "0.00";
20783
+ const shortId = dump.id.substring(0, 8);
20784
+ const indexStr = `${(index + 1).toString().padStart(maxIndexWidth)}`;
20785
+ const scoreStr = `[${score}]`;
20786
+ const idStr = `${shortId}`;
20787
+ const headerLine = `${indexStr} ${idStr} ${scoreStr}`;
20788
+ console.log(headerLine);
20789
+ const messageIndent = " ".repeat(maxIndexWidth + 1);
20790
+ const lines = dump.message.split(`
20791
+ `).map((l) => l.trim()).filter((l) => l.length > 0);
20792
+ const availableWidth = terminalWidth - messageIndent.length - 2;
20793
+ if (lines.length === 0) {
20794
+ console.log(`${messageIndent}(empty message)`);
20795
+ } else {
20796
+ lines.forEach((line, lineIndex) => {
20797
+ if (lineIndex < 3) {
20798
+ const truncatedLine = line.length > availableWidth ? line.substring(0, availableWidth - 3) + "..." : line;
20799
+ console.log(`${messageIndent}${truncatedLine}`);
20800
+ }
20801
+ });
20802
+ if (lines.length > 3) {
20803
+ console.log(`${messageIndent}... (+${lines.length - 3} more line${lines.length - 3 === 1 ? "" : "s"})`);
20804
+ }
20805
+ }
20806
+ const contextInfo = [];
20807
+ const date = new Date(dump.timestamp);
20808
+ const timeAgo = getTimeAgo(date);
20809
+ contextInfo.push("----------------");
20810
+ contextInfo.push(`${timeAgo}`);
20811
+ contextInfo.push("----------------");
20812
+ contextInfo.push(`
20813
+ `);
20814
+ if (dump.branch && dump.branch !== "main") {
20815
+ contextInfo.push(`${dump.branch}${dump.hasUncommittedChanges ? " (uncommitted)" : ""}`);
20816
+ }
20817
+ if (contextInfo.length > 0) {
20818
+ console.log(`${messageIndent}${contextInfo.join(" • ")}`);
20819
+ }
20820
+ console.log("");
20821
+ });
20822
+ console.log(`!! Use the 8-character ID (like ${results[0]?.item.id.substring(0, 8)}) to reference specific dumps
20823
+ `);
20824
+ }
20825
+ function getTimeAgo(date) {
20826
+ const now = new Date;
20827
+ const diffMs = now.getTime() - date.getTime();
20828
+ const diffMins = Math.floor(diffMs / (1000 * 60));
20829
+ const diffHours = Math.floor(diffMins / 60);
20830
+ const diffDays = Math.floor(diffHours / 24);
20831
+ if (diffMins < 60)
20832
+ return `${diffMins}m ago`;
20833
+ if (diffHours < 24)
20834
+ return `${diffHours}h ago`;
20835
+ if (diffDays < 7)
20836
+ return `${diffDays}d ago`;
20837
+ return date.toLocaleDateString();
20749
20838
  }
20750
20839
  // node_modules/@inquirer/core/dist/lib/key.js
20751
20840
  var isUpKey = (key, keybindings = []) => key.name === "up" || keybindings.includes("vim") && key.name === "k" || keybindings.includes("emacs") && key.ctrl && key.name === "p";
@@ -22328,16 +22417,16 @@ var dist_default4 = createPrompt((config, done) => {
22328
22417
  `).trimEnd();
22329
22418
  return `${lines}${cursorHide}`;
22330
22419
  });
22331
- // node_modules/@inquirer/external-editor/dist/index.js
22420
+ // node_modules/@inquirer/editor/node_modules/@inquirer/external-editor/dist/index.js
22332
22421
  var import_chardet = __toESM(require_lib2(), 1);
22333
22422
  var import_iconv_lite = __toESM(require_lib3(), 1);
22334
22423
  import { spawn, spawnSync } from "child_process";
22335
22424
  import { readFileSync, unlinkSync, writeFileSync } from "fs";
22336
- import path from "node:path";
22425
+ import path2 from "node:path";
22337
22426
  import os from "node:os";
22338
22427
  import { randomUUID } from "node:crypto";
22339
22428
 
22340
- // node_modules/@inquirer/external-editor/dist/errors/CreateFileError.js
22429
+ // node_modules/@inquirer/editor/node_modules/@inquirer/external-editor/dist/errors/CreateFileError.js
22341
22430
  class CreateFileError extends Error {
22342
22431
  originalError;
22343
22432
  constructor(originalError) {
@@ -22346,7 +22435,7 @@ class CreateFileError extends Error {
22346
22435
  }
22347
22436
  }
22348
22437
 
22349
- // node_modules/@inquirer/external-editor/dist/errors/LaunchEditorError.js
22438
+ // node_modules/@inquirer/editor/node_modules/@inquirer/external-editor/dist/errors/LaunchEditorError.js
22350
22439
  class LaunchEditorError extends Error {
22351
22440
  originalError;
22352
22441
  constructor(originalError) {
@@ -22355,7 +22444,7 @@ class LaunchEditorError extends Error {
22355
22444
  }
22356
22445
  }
22357
22446
 
22358
- // node_modules/@inquirer/external-editor/dist/errors/ReadFileError.js
22447
+ // node_modules/@inquirer/editor/node_modules/@inquirer/external-editor/dist/errors/ReadFileError.js
22359
22448
  class ReadFileError extends Error {
22360
22449
  originalError;
22361
22450
  constructor(originalError) {
@@ -22364,7 +22453,7 @@ class ReadFileError extends Error {
22364
22453
  }
22365
22454
  }
22366
22455
 
22367
- // node_modules/@inquirer/external-editor/dist/errors/RemoveFileError.js
22456
+ // node_modules/@inquirer/editor/node_modules/@inquirer/external-editor/dist/errors/RemoveFileError.js
22368
22457
  class RemoveFileError extends Error {
22369
22458
  originalError;
22370
22459
  constructor(originalError) {
@@ -22373,7 +22462,7 @@ class RemoveFileError extends Error {
22373
22462
  }
22374
22463
  }
22375
22464
 
22376
- // node_modules/@inquirer/external-editor/dist/index.js
22465
+ // node_modules/@inquirer/editor/node_modules/@inquirer/external-editor/dist/index.js
22377
22466
  function editAsync(text = "", callback, fileOptions) {
22378
22467
  const editor = new ExternalEditor(text, fileOptions);
22379
22468
  editor.runAsync((err, result) => {
@@ -22469,8 +22558,8 @@ class ExternalEditor {
22469
22558
  const prefix = sanitizeAffix(this.fileOptions.prefix);
22470
22559
  const postfix = sanitizeAffix(this.fileOptions.postfix);
22471
22560
  const filename = `${prefix}${id}${postfix}`;
22472
- const candidate = path.resolve(baseDir, filename);
22473
- const baseResolved = path.resolve(baseDir) + path.sep;
22561
+ const candidate = path2.resolve(baseDir, filename);
22562
+ const baseResolved = path2.resolve(baseDir) + path2.sep;
22474
22563
  if (!candidate.startsWith(baseResolved)) {
22475
22564
  throw new Error("Resolved temporary file escaped the base directory");
22476
22565
  }
@@ -23408,9 +23497,9 @@ var import_run_async = __toESM(require_run_async(), 1);
23408
23497
  var import_mute_stream2 = __toESM(require_lib(), 1);
23409
23498
  import readline3 from "node:readline";
23410
23499
  var _ = {
23411
- set: (obj, path2 = "", value) => {
23500
+ set: (obj, path3 = "", value) => {
23412
23501
  let pointer = obj;
23413
- path2.split(".").forEach((key, index, arr) => {
23502
+ path3.split(".").forEach((key, index, arr) => {
23414
23503
  if (key === "__proto__" || key === "constructor")
23415
23504
  return;
23416
23505
  if (index === arr.length - 1) {
@@ -23421,8 +23510,8 @@ var _ = {
23421
23510
  pointer = pointer[key];
23422
23511
  });
23423
23512
  },
23424
- get: (obj, path2 = "", defaultValue) => {
23425
- const travel = (regexp) => String.prototype.split.call(path2, regexp).filter(Boolean).reduce((res, key) => res == null ? res : res[key], obj);
23513
+ get: (obj, path3 = "", defaultValue) => {
23514
+ const travel = (regexp) => String.prototype.split.call(path3, regexp).filter(Boolean).reduce((res, key) => res == null ? res : res[key], obj);
23426
23515
  const result = travel(/[,[\]]+?/) || travel(/[,.[\]]+?/);
23427
23516
  return result === undefined || result === obj ? defaultValue : result;
23428
23517
  }
@@ -23706,22 +23795,22 @@ async function initFluxCommand() {
23706
23795
  process.exit(1);
23707
23796
  }
23708
23797
  try {
23709
- if (fs.existsSync(".git/")) {
23798
+ if (fs2.existsSync(".git/")) {
23710
23799
  console.log("Git repository detected.");
23711
23800
  } else {
23712
23801
  console.log("Not a git repository. Skipping git integration.");
23713
23802
  }
23714
- if (fs.existsSync(".gitignore")) {
23803
+ if (fs2.existsSync(".gitignore")) {
23715
23804
  console.log("Gitignore file exists");
23716
- const gitignoreContent = fs.readFileSync(".gitignore", "utf8");
23805
+ const gitignoreContent = fs2.readFileSync(".gitignore", "utf8");
23717
23806
  if (gitignoreContent.includes(FLUX_FOLDER_PATH)) {
23718
23807
  console.log(".flux is already in .gitignore");
23719
23808
  } else {
23720
- fs.appendFileSync(".gitignore", `
23809
+ fs2.appendFileSync(".gitignore", `
23721
23810
  ${FLUX_FOLDER_PATH}`);
23722
23811
  }
23723
23812
  } else {
23724
- fs.writeFileSync(".gitignore", ".flux");
23813
+ fs2.writeFileSync(".gitignore", ".flux");
23725
23814
  console.log("Created .gitignore file.");
23726
23815
  }
23727
23816
  } catch (error) {
@@ -23733,6 +23822,7 @@ ${FLUX_FOLDER_PATH}`);
23733
23822
  }
23734
23823
  var resetFluxCommand = async () => {
23735
23824
  console.log("Resetting Flux Capacitor...");
23825
+ const fluxPath = await getFluxPath() + FLUX_FOLDER_PATH;
23736
23826
  const { confirmed } = await dist_default14.prompt([{
23737
23827
  type: "confirm",
23738
23828
  name: "confirmed",
@@ -23744,8 +23834,8 @@ var resetFluxCommand = async () => {
23744
23834
  return;
23745
23835
  }
23746
23836
  try {
23747
- if (fs.existsSync(FLUX_FOLDER_PATH)) {
23748
- fs.rmSync(FLUX_FOLDER_PATH, { recursive: true, force: true });
23837
+ if (fs2.existsSync(fluxPath)) {
23838
+ fs2.rmSync(fluxPath, { recursive: true, force: true });
23749
23839
  console.log("Removed .flux directory and all its contents.");
23750
23840
  } else {
23751
23841
  console.log("Flux Capacitor is not initialized in this repository.");
@@ -23759,27 +23849,61 @@ var resetFluxCommand = async () => {
23759
23849
 
23760
23850
  // src/commands/dump.command.ts
23761
23851
  import { randomUUID as randomUUID2 } from "crypto";
23762
- async function brainDumpAddCommand(message) {
23763
- const fs2 = await import("fs");
23852
+ async function handleBrainDump(message, options) {
23853
+ try {
23854
+ let finalMessage;
23855
+ if (options.multiline) {
23856
+ console.log("Opening editor for multiline input...");
23857
+ const initialText = message ? message.join(" ") : "";
23858
+ const multilineInput = await dist_default5({
23859
+ message: "Enter your brain dump (save & exit when done):",
23860
+ default: initialText,
23861
+ waitForUserInput: false
23862
+ });
23863
+ if (!multilineInput.trim()) {
23864
+ console.log("Brain dump cancelled - no content provided");
23865
+ return;
23866
+ }
23867
+ finalMessage = multilineInput.trim();
23868
+ } else {
23869
+ if (!message || message.length === 0) {
23870
+ console.log('Please provide a message: flux dump "your message"');
23871
+ return;
23872
+ }
23873
+ finalMessage = message.join(" ");
23874
+ }
23875
+ await brainDumpAddCommand(finalMessage, { multiline: options.multiline });
23876
+ } catch (error) {
23877
+ console.error("Error creating brain dump:", error instanceof Error ? error.message : "Unknown error");
23878
+ process.exit(1);
23879
+ }
23880
+ }
23881
+ async function brainDumpAddCommand(message, options = {}) {
23882
+ const fluxPath = await getFluxPath();
23883
+ const fs3 = await import("fs");
23764
23884
  console.log("Creating brain dump...");
23765
23885
  const monthString = getMonthString();
23766
- await createBrainDumpFileIfNotExists(monthString);
23767
- const config = await getConfigFile();
23886
+ await createBrainDumpFileIfNotExists(monthString, fluxPath);
23887
+ const config = await getConfigFile(fluxPath);
23768
23888
  const workingDir = await getWorkingDir(config);
23769
23889
  const branch = getCurrentBranch(config);
23770
23890
  const hasUncommittedChanges = getGitUncommittedChanges(config);
23771
23891
  const newDump = {
23772
23892
  id: randomUUID2(),
23773
23893
  timestamp: new Date().toISOString(),
23774
- message: message.join(" "),
23894
+ message,
23775
23895
  workingDir,
23776
23896
  branch,
23777
23897
  hasUncommittedChanges
23778
23898
  };
23779
- const data = JSON.parse(fs2.readFileSync(`${FLUX_BRAIN_DUMP_PATH}/${monthString}.json`, "utf8"));
23899
+ const data = JSON.parse(fs3.readFileSync(`${fluxPath}${FLUX_BRAIN_DUMP_PATH}/${monthString}.json`, "utf8"));
23780
23900
  config.sorted ? data.dumps.unshift(newDump) : data.dumps.push(newDump);
23781
- fs2.writeFileSync(`${FLUX_BRAIN_DUMP_PATH}/${monthString}.json`, JSON.stringify(data, null, 2));
23782
- console.log(`✅ Brain dump saved: "${message.join(" ")}"`);
23901
+ fs3.writeFileSync(`${fluxPath}${FLUX_BRAIN_DUMP_PATH}/${monthString}.json`, JSON.stringify(data, null, 2));
23902
+ const displayMessage = message.length > 50 ? message.substring(0, 47) + "..." : message;
23903
+ const preview = message.includes(`
23904
+ `) ? `${message.split(`
23905
+ `)[0]}... (multiline)` : displayMessage;
23906
+ console.log(`✅ Brain dump saved: "${preview}"`);
23783
23907
  }
23784
23908
 
23785
23909
  // node_modules/fuse.js/dist/fuse.mjs
@@ -23854,14 +23978,14 @@ class KeyStore {
23854
23978
  }
23855
23979
  }
23856
23980
  function createKey(key) {
23857
- let path2 = null;
23981
+ let path3 = null;
23858
23982
  let id = null;
23859
23983
  let src = null;
23860
23984
  let weight = 1;
23861
23985
  let getFn = null;
23862
23986
  if (isString(key) || isArray(key)) {
23863
23987
  src = key;
23864
- path2 = createKeyPath(key);
23988
+ path3 = createKeyPath(key);
23865
23989
  id = createKeyId(key);
23866
23990
  } else {
23867
23991
  if (!hasOwn.call(key, "name")) {
@@ -23875,11 +23999,11 @@ function createKey(key) {
23875
23999
  throw new Error(INVALID_KEY_WEIGHT_VALUE(name));
23876
24000
  }
23877
24001
  }
23878
- path2 = createKeyPath(name);
24002
+ path3 = createKeyPath(name);
23879
24003
  id = createKeyId(name);
23880
24004
  getFn = key.getFn;
23881
24005
  }
23882
- return { path: path2, id, weight, src, getFn };
24006
+ return { path: path3, id, weight, src, getFn };
23883
24007
  }
23884
24008
  function createKeyPath(key) {
23885
24009
  return isArray(key) ? key : key.split(".");
@@ -23887,34 +24011,34 @@ function createKeyPath(key) {
23887
24011
  function createKeyId(key) {
23888
24012
  return isArray(key) ? key.join(".") : key;
23889
24013
  }
23890
- function get(obj, path2) {
24014
+ function get(obj, path3) {
23891
24015
  let list = [];
23892
24016
  let arr = false;
23893
- const deepGet = (obj2, path3, index) => {
24017
+ const deepGet = (obj2, path4, index) => {
23894
24018
  if (!isDefined(obj2)) {
23895
24019
  return;
23896
24020
  }
23897
- if (!path3[index]) {
24021
+ if (!path4[index]) {
23898
24022
  list.push(obj2);
23899
24023
  } else {
23900
- let key = path3[index];
24024
+ let key = path4[index];
23901
24025
  const value = obj2[key];
23902
24026
  if (!isDefined(value)) {
23903
24027
  return;
23904
24028
  }
23905
- if (index === path3.length - 1 && (isString(value) || isNumber(value) || isBoolean(value))) {
24029
+ if (index === path4.length - 1 && (isString(value) || isNumber(value) || isBoolean(value))) {
23906
24030
  list.push(toString(value));
23907
24031
  } else if (isArray(value)) {
23908
24032
  arr = true;
23909
24033
  for (let i = 0, len = value.length;i < len; i += 1) {
23910
- deepGet(value[i], path3, index + 1);
24034
+ deepGet(value[i], path4, index + 1);
23911
24035
  }
23912
- } else if (path3.length) {
23913
- deepGet(value, path3, index + 1);
24036
+ } else if (path4.length) {
24037
+ deepGet(value, path4, index + 1);
23914
24038
  }
23915
24039
  }
23916
24040
  };
23917
- deepGet(obj, isString(path2) ? path2.split(".") : path2, 0);
24041
+ deepGet(obj, isString(path3) ? path3.split(".") : path3, 0);
23918
24042
  return arr ? list : list[0];
23919
24043
  }
23920
24044
  var MatchOptions = {
@@ -25086,58 +25210,51 @@ function createFuseInstance(data, config) {
25086
25210
  }
25087
25211
 
25088
25212
  // src/commands/search.command.ts
25089
- import fs2 from "fs";
25213
+ import fs3 from "fs";
25090
25214
  async function searchBrainDumpCommand(query) {
25091
25215
  console.log("Searching all brain dumps...");
25092
- const config = await getConfigFile();
25093
- const monthString = getMonthString();
25216
+ const fluxPath = await getFluxPath();
25217
+ const config = await getConfigFile(fluxPath);
25218
+ const searchQuery = query.join(" ").trim();
25094
25219
  let searchResults = [];
25095
- const allFilePaths = await getAllBrainDumpFilePaths();
25220
+ const allFilePaths = await getAllBrainDumpFilePaths(fluxPath);
25096
25221
  for await (const filePath of allFilePaths) {
25097
- const fileData = JSON.parse(fs2.readFileSync(filePath, "utf8"));
25098
- const fuse = createFuseInstance(fileData.dumps, config);
25099
- const results = fuse.search(query.join(" "));
25100
- searchResults.push(...results);
25101
- if (searchResults.length > 30) {
25102
- break;
25222
+ const fileData = JSON.parse(fs3.readFileSync(filePath, "utf8"));
25223
+ if (searchQuery) {
25224
+ const fuse = createFuseInstance(fileData.dumps, config);
25225
+ const results = fuse.search(searchQuery);
25226
+ searchResults.push(...results);
25227
+ } else {
25228
+ const recentDumps = fileData.dumps.filter((dump) => dump && dump.message && dump.message.trim() !== "").map((dump) => ({
25229
+ item: dump,
25230
+ score: 0,
25231
+ timestamp: new Date(dump.timestamp).getTime()
25232
+ }));
25233
+ searchResults.push(...recentDumps);
25103
25234
  }
25104
25235
  }
25105
- if (query.length > 0) {
25106
- if (searchResults.length === 0) {
25107
- console.log("No brain dumps found matching the query.");
25108
- return;
25109
- }
25110
- const resultLimit = config?.search?.resultLimit || 10;
25111
- const limitedResults = searchResults.slice(0, resultLimit);
25112
- console.log(`Found ${searchResults.length} brain dumps matching the query${searchResults.length > resultLimit ? ` (showing first ${resultLimit})` : ""}:`);
25113
- limitedResults.forEach((result, index) => {
25114
- const dump = result.item;
25115
- console.log(searchResultFormat({ index, timestamp: dump.timestamp, message: dump.message, score: result.score?.toFixed(2) }));
25116
- });
25236
+ if (searchQuery) {
25237
+ searchResults.sort((a, b) => (a.score || 0) - (b.score || 0));
25117
25238
  } else {
25118
- const resultLimit = config?.search?.resultLimit || 3;
25119
- let totalCount = 0;
25120
- for await (const filePath of allFilePaths) {
25121
- if (totalCount >= resultLimit) {
25122
- break;
25123
- }
25124
- const fileData = JSON.parse(fs2.readFileSync(filePath, "utf8"));
25125
- for (let i = 0;i < fileData.dumps.length && totalCount < resultLimit; i++) {
25126
- const dump = fileData.dumps[i];
25127
- if (!dump || !dump.message || dump.message.trim() === "") {
25128
- continue;
25129
- }
25130
- totalCount += 1;
25131
- console.log(searchResultFormat({ index: totalCount, timestamp: dump.timestamp, message: dump.message, score: "0.00" }));
25132
- }
25133
- }
25239
+ searchResults.sort((a, b) => {
25240
+ const timeA = new Date(a.item.timestamp).getTime();
25241
+ const timeB = new Date(b.item.timestamp).getTime();
25242
+ return timeB - timeA;
25243
+ });
25134
25244
  }
25245
+ const resultLimit = config?.search?.resultLimit || (searchQuery ? 10 : 5);
25246
+ const limitedResults = searchResults.slice(0, resultLimit);
25247
+ if (searchResults.length > limitedResults.length) {
25248
+ console.log(`
25249
+ (Showing ${limitedResults.length} of ${searchResults.length} results)`);
25250
+ }
25251
+ displaySearchResults(limitedResults, searchQuery || undefined);
25135
25252
  }
25136
25253
 
25137
25254
  // src/commands/config.command.ts
25138
25255
  async function configCommand(fields) {
25139
25256
  console.log("This command is still to be implemented");
25140
- const fs3 = await import("fs");
25257
+ const fs4 = await import("fs");
25141
25258
  const config = await getConfigFile();
25142
25259
  const value = fields[fields.length - 1];
25143
25260
  const keys = fields.slice(0, -1)[0]?.split(".").map((k) => k.trim()) || [];
@@ -25151,7 +25268,8 @@ async function configCommand(fields) {
25151
25268
  // package.json
25152
25269
  var package_default = {
25153
25270
  name: "@dev_desh/flux-cap",
25154
- version: "0.1.2",
25271
+ type: "module",
25272
+ version: "0.2.0",
25155
25273
  description: "Git-aware CLI context manager for ADHD developers",
25156
25274
  bin: {
25157
25275
  flux: "./dist/index.js"
@@ -25165,8 +25283,15 @@ var package_default = {
25165
25283
  build: "bun build src/index.ts --outdir dist --target node --format esm",
25166
25284
  dev: "bun run src/index.ts",
25167
25285
  prepublishOnly: "bun run build",
25286
+ publish: "npm publish --access=public",
25168
25287
  "local-install": "bun run build && npm link",
25169
- "local-uninstall": "npm unlink -g flux-cap"
25288
+ "local-uninstall": "npm unlink -g flux-cap",
25289
+ "local-reinstall": "bun run local-uninstall && bun run local-install",
25290
+ changeset: "changeset",
25291
+ "changeset:add": "changeset add",
25292
+ "changeset:status": "changeset status",
25293
+ "changeset:version": "changeset version",
25294
+ "changeset:publish": "changeset publish"
25170
25295
  },
25171
25296
  engines: {
25172
25297
  node: ">=18.0.0"
@@ -25184,6 +25309,7 @@ var package_default = {
25184
25309
  inquirer: "^13.2.5"
25185
25310
  },
25186
25311
  devDependencies: {
25312
+ "@changesets/cli": "^2.29.8",
25187
25313
  "@types/inquirer": "^9.0.9"
25188
25314
  },
25189
25315
  repository: {
@@ -25197,7 +25323,9 @@ var program2 = new Command;
25197
25323
  program2.name(`flux`).description("Git-aware CLI context manager for ADHD developers").version(package_default.version);
25198
25324
  program2.command("init").description("Initialize flux in the current repository").action(initFluxCommand);
25199
25325
  program2.command("reset").description("Resets flux in the current repository").action(resetFluxCommand);
25200
- program2.command("dump <message...>").description("Add a brain dump with a message. You can also include tags by using #tag in the message.").action(brainDumpAddCommand);
25326
+ program2.command("dump [message...]").option("-m, --multiline", "Enable multiline input mode").description("Add a brain dump with a message. Use --multiline for multi-line input.").action(async (message, options) => {
25327
+ await handleBrainDump(message, options);
25328
+ });
25201
25329
  program2.command("search [query...]").description("Search brain dumps with a query. If no query is provided, lists all brain dumps for the current month.").action((query) => {
25202
25330
  searchBrainDumpCommand(query ? query : [""]);
25203
25331
  });
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@dev_desh/flux-cap",
3
- "version": "0.1.2",
3
+ "type": "module",
4
+ "version": "0.2.0",
4
5
  "description": "Git-aware CLI context manager for ADHD developers",
5
6
  "bin": {
6
7
  "flux": "./dist/index.js"
@@ -14,8 +15,15 @@
14
15
  "build": "bun build src/index.ts --outdir dist --target node --format esm",
15
16
  "dev": "bun run src/index.ts",
16
17
  "prepublishOnly": "bun run build",
18
+ "publish": "npm publish --access=public",
17
19
  "local-install": "bun run build && npm link",
18
- "local-uninstall": "npm unlink -g flux-cap"
20
+ "local-uninstall": "npm unlink -g flux-cap",
21
+ "local-reinstall": "bun run local-uninstall && bun run local-install",
22
+ "changeset": "changeset",
23
+ "changeset:add": "changeset add",
24
+ "changeset:status": "changeset status",
25
+ "changeset:version": "changeset version",
26
+ "changeset:publish": "changeset publish"
19
27
  },
20
28
  "engines": {
21
29
  "node": ">=18.0.0"
@@ -33,6 +41,7 @@
33
41
  "inquirer": "^13.2.5"
34
42
  },
35
43
  "devDependencies": {
44
+ "@changesets/cli": "^2.29.8",
36
45
  "@types/inquirer": "^9.0.9"
37
46
  },
38
47
  "repository": {