fraude-code 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matthew Branning
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/dist/index.js CHANGED
@@ -419,6 +419,7 @@ var init_useSettingsStore = __esm(() => {
419
419
  useSettingsStore = create()((set) => {
420
420
  return {
421
421
  ...DEFAULTS,
422
+ updateAvailable: null,
422
423
  setOllamaUrl: (url) => {
423
424
  try {
424
425
  UpdateSettings({ ollamaUrl: url });
@@ -434,7 +435,8 @@ var init_useSettingsStore = __esm(() => {
434
435
  } catch (e) {
435
436
  console.error("Failed to sync settings:", e);
436
437
  }
437
- }
438
+ },
439
+ setUpdateAvailable: (version) => set({ updateAvailable: version })
438
440
  };
439
441
  });
440
442
  useSettingsStore_default = useSettingsStore;
@@ -173992,9 +173994,51 @@ var command2 = {
173992
173994
  }
173993
173995
  };
173994
173996
  var visualize_default = command2;
173997
+ // package.json
173998
+ var version = "0.1.2";
173999
+
174000
+ // src/utils/updateCheck.ts
174001
+ import semver from "semver";
174002
+ async function checkForUpdate(currentVersion) {
174003
+ try {
174004
+ const response = await fetch("https://registry.npmjs.org/fraude-code/latest");
174005
+ if (!response.ok)
174006
+ return null;
174007
+ const data = await response.json();
174008
+ const latestVersion = data.version;
174009
+ if (semver.gt(latestVersion, currentVersion)) {
174010
+ return latestVersion;
174011
+ }
174012
+ } catch (error) {}
174013
+ return null;
174014
+ }
174015
+
174016
+ // src/commands/update.ts
174017
+ init_useFraudeStore();
174018
+ var { updateOutput: updateOutput18 } = useFraudeStore_default.getState();
174019
+ var updateCommand = {
174020
+ name: "update",
174021
+ description: "Check for the latest version of FraudeCode",
174022
+ usage: "/update",
174023
+ action: async () => {
174024
+ updateOutput18("log", "Checking for updates...");
174025
+ const latest = await checkForUpdate(version);
174026
+ if (!latest) {
174027
+ updateOutput18("log", "\uD83C\uDF89 You are already using the latest version of FraudeCode (v" + version + ").");
174028
+ return;
174029
+ }
174030
+ updateOutput18("log", "\uD83D\uDE80 A new version of FraudeCode is available: v" + latest);
174031
+ updateOutput18("log", "-----------------------------------------");
174032
+ updateOutput18("log", "To update to the latest version, run:");
174033
+ updateOutput18("log", " npm install -g fraude-code");
174034
+ updateOutput18("log", "-----------------------------------------");
174035
+ }
174036
+ };
174037
+ var update_default = updateCommand;
173995
174038
 
173996
174039
  // src/commands/COMMANDS.ts
173997
174040
  var COMMANDS = [
174041
+ update_default,
173998
174042
  usage_default,
173999
174043
  log_default,
174000
174044
  serve_default,
@@ -174278,7 +174322,7 @@ init_useFraudeStore();
174278
174322
  import { tool } from "ai";
174279
174323
  import { z as z3 } from "zod";
174280
174324
  import path11 from "path";
174281
- var { updateOutput: updateOutput18 } = useFraudeStore_default.getState();
174325
+ var { updateOutput: updateOutput19 } = useFraudeStore_default.getState();
174282
174326
  var FRAUDE_DIR = path11.join(process.cwd(), ".fraude");
174283
174327
  var PLAN_FILE = path11.join(FRAUDE_DIR, "plan.md");
174284
174328
  async function ensureDir() {
@@ -174318,7 +174362,7 @@ Operations:
174318
174362
  switch (operation) {
174319
174363
  case "read": {
174320
174364
  const plan = await readPlan();
174321
- updateOutput18("toolCall", JSON.stringify({
174365
+ updateOutput19("toolCall", JSON.stringify({
174322
174366
  action: "Read Plan",
174323
174367
  details: `${plan.split(`
174324
174368
  `).length} lines`,
@@ -174330,7 +174374,7 @@ Operations:
174330
174374
  if (!content)
174331
174375
  throw new Error("Content required for write operation");
174332
174376
  await writePlan(content);
174333
- updateOutput18("toolCall", JSON.stringify({
174377
+ updateOutput19("toolCall", JSON.stringify({
174334
174378
  action: "Wrote Plan",
174335
174379
  details: `${content.split(`
174336
174380
  `).length} lines`,
@@ -174344,7 +174388,7 @@ Operations:
174344
174388
  const existing = await readPlan();
174345
174389
  await writePlan(existing + `
174346
174390
  ` + content);
174347
- updateOutput18("toolCall", JSON.stringify({
174391
+ updateOutput19("toolCall", JSON.stringify({
174348
174392
  action: "Appended to Plan",
174349
174393
  details: `+${content.split(`
174350
174394
  `).length} lines`,
@@ -174357,7 +174401,7 @@ Operations:
174357
174401
 
174358
174402
  *No plan yet.*
174359
174403
  `);
174360
- updateOutput18("toolCall", JSON.stringify({
174404
+ updateOutput19("toolCall", JSON.stringify({
174361
174405
  action: "Cleared Plan",
174362
174406
  details: "Reset to empty",
174363
174407
  result: "\u2713"
@@ -174391,7 +174435,7 @@ Task context includes:
174391
174435
  `;
174392
174436
 
174393
174437
  // src/agent/tools/todoTool.ts
174394
- var { updateOutput: updateOutput19 } = useFraudeStore_default.getState();
174438
+ var { updateOutput: updateOutput20 } = useFraudeStore_default.getState();
174395
174439
  var FRAUDE_DIR2 = path12.join(process.cwd(), ".fraude");
174396
174440
  var TODOS_FILE = path12.join(FRAUDE_DIR2, "todos.json");
174397
174441
  async function readTodos() {
@@ -174440,7 +174484,7 @@ var todoTool = tool2({
174440
174484
  };
174441
174485
  state2.todos.push(newTodo);
174442
174486
  await writeTodos(state2);
174443
- updateOutput19("toolCall", JSON.stringify({
174487
+ updateOutput20("toolCall", JSON.stringify({
174444
174488
  action: "Added Task",
174445
174489
  details: description,
174446
174490
  result: newTodo.id
@@ -174459,7 +174503,7 @@ var todoTool = tool2({
174459
174503
  todo.notes.push(note);
174460
174504
  todo.updatedAt = now;
174461
174505
  await writeTodos(state2);
174462
- updateOutput19("toolCall", JSON.stringify({
174506
+ updateOutput20("toolCall", JSON.stringify({
174463
174507
  action: "Updated Task",
174464
174508
  details: todo.description,
174465
174509
  result: status || "note added"
@@ -174477,7 +174521,7 @@ var todoTool = tool2({
174477
174521
  todo.notes.push(`[Done] ${note}`);
174478
174522
  todo.updatedAt = now;
174479
174523
  await writeTodos(state2);
174480
- updateOutput19("toolCall", JSON.stringify({
174524
+ updateOutput20("toolCall", JSON.stringify({
174481
174525
  action: "Completed Task",
174482
174526
  details: todo.description,
174483
174527
  result: "\u2713"
@@ -174491,7 +174535,7 @@ var todoTool = tool2({
174491
174535
  inProgress: state2.todos.filter((t) => t.status === "in-progress").length,
174492
174536
  completed: state2.todos.filter((t) => t.status === "completed").length
174493
174537
  };
174494
- updateOutput19("toolCall", JSON.stringify({
174538
+ updateOutput20("toolCall", JSON.stringify({
174495
174539
  action: "Listed Tasks",
174496
174540
  details: `${summary.pending} pending, ${summary.inProgress} in-progress`,
174497
174541
  result: `${summary.total} total`
@@ -174502,7 +174546,7 @@ var todoTool = tool2({
174502
174546
  const before = state2.todos.length;
174503
174547
  state2.todos = state2.todos.filter((t) => t.status !== "completed");
174504
174548
  await writeTodos(state2);
174505
- updateOutput19("toolCall", JSON.stringify({
174549
+ updateOutput20("toolCall", JSON.stringify({
174506
174550
  action: "Cleared Completed",
174507
174551
  details: `Removed ${before - state2.todos.length} tasks`,
174508
174552
  result: "\u2713"
@@ -174557,7 +174601,7 @@ Usage:
174557
174601
  - The limit parameter is the number of lines to read (inclusive).`;
174558
174602
 
174559
174603
  // src/agent/tools/readTool.ts
174560
- var { updateOutput: updateOutput20 } = useFraudeStore_default.getState();
174604
+ var { updateOutput: updateOutput21 } = useFraudeStore_default.getState();
174561
174605
  var readTool = tool3({
174562
174606
  description: read_default,
174563
174607
  strict: true,
@@ -174577,7 +174621,7 @@ var readTool = tool3({
174577
174621
  const file = Bun.file(filePath);
174578
174622
  const isStaged = pendingChanges_default.getChanges().some((c) => c.path === filePath);
174579
174623
  if (!isStaged && !await file.exists()) {
174580
- updateOutput20("toolCall", JSON.stringify({
174624
+ updateOutput21("toolCall", JSON.stringify({
174581
174625
  action: "Error Reading " + projectPath(filePath),
174582
174626
  details: "File doesn't exist",
174583
174627
  result: ""
@@ -174590,7 +174634,7 @@ var readTool = tool3({
174590
174634
  const lastLine = Math.min(offset + limit, lines.length);
174591
174635
  const result = lines.slice(offset, lastLine).join(`
174592
174636
  `);
174593
- updateOutput20("toolCall", JSON.stringify({
174637
+ updateOutput21("toolCall", JSON.stringify({
174594
174638
  action: "Analyzing " + projectPath(filePath),
174595
174639
  details: "#L" + (offset + 1) + "-" + lastLine,
174596
174640
  result
@@ -174661,7 +174705,7 @@ Usage notes:
174661
174705
  </bad-example>`;
174662
174706
 
174663
174707
  // src/agent/tools/bashTool.ts
174664
- var { updateOutput: updateOutput21 } = useFraudeStore_default.getState();
174708
+ var { updateOutput: updateOutput22 } = useFraudeStore_default.getState();
174665
174709
  var bashTool = tool4({
174666
174710
  description: bash_default,
174667
174711
  strict: true,
@@ -174693,7 +174737,7 @@ Output: Creates directory 'foo'`)
174693
174737
  throw new Error("Command blocked by safety policy");
174694
174738
  }
174695
174739
  try {
174696
- updateOutput21("toolCall", JSON.stringify({
174740
+ updateOutput22("toolCall", JSON.stringify({
174697
174741
  action: "Executing Bash",
174698
174742
  details: command3,
174699
174743
  result: ""
@@ -174712,7 +174756,7 @@ Output: Creates directory 'foo'`)
174712
174756
  }
174713
174757
  const [stdout, stderr] = await Promise.all([outputPromise, errorPromise]);
174714
174758
  const exitCode = await proc.exited;
174715
- updateOutput21("toolCall", JSON.stringify({
174759
+ updateOutput22("toolCall", JSON.stringify({
174716
174760
  action: "Bash",
174717
174761
  details: command3,
174718
174762
  result: stdout.trim()
@@ -174747,7 +174791,7 @@ var grep_default = `Search for a chunk of text in the codebase using grep. Use t
174747
174791
  - Use this tool when you need to find files containing specific patterns`;
174748
174792
 
174749
174793
  // src/agent/tools/grepTool.ts
174750
- var { updateOutput: updateOutput22 } = useFraudeStore_default.getState();
174794
+ var { updateOutput: updateOutput23 } = useFraudeStore_default.getState();
174751
174795
  var grepTool = tool5({
174752
174796
  description: grep_default,
174753
174797
  strict: true,
@@ -174761,7 +174805,7 @@ var grepTool = tool5({
174761
174805
  path: path14,
174762
174806
  include
174763
174807
  }) => {
174764
- updateOutput22("toolCall", JSON.stringify({
174808
+ updateOutput23("toolCall", JSON.stringify({
174765
174809
  action: "Searching For " + pattern,
174766
174810
  details: path14
174767
174811
  }), { dontOverride: true });
@@ -174801,7 +174845,7 @@ var grepTool = tool5({
174801
174845
  }
174802
174846
  }
174803
174847
  results.sort((a, b) => b.mtime - a.mtime);
174804
- updateOutput22("toolCall", JSON.stringify({
174848
+ updateOutput23("toolCall", JSON.stringify({
174805
174849
  action: "Found " + results.length + " Match(es)",
174806
174850
  details: pattern
174807
174851
  }), { dontOverride: true });
@@ -174955,7 +174999,7 @@ var glob_default = `- Fast file pattern matching tool that works with any codeba
174955
174999
 
174956
175000
  // src/agent/tools/globTool.ts
174957
175001
  var {Glob } = globalThis.Bun;
174958
- var { updateOutput: updateOutput23 } = useFraudeStore_default.getState();
175002
+ var { updateOutput: updateOutput24 } = useFraudeStore_default.getState();
174959
175003
  var globTool = tool6({
174960
175004
  description: glob_default,
174961
175005
  strict: true,
@@ -174964,7 +175008,7 @@ var globTool = tool6({
174964
175008
  path: z8.string().optional().describe(`The directory to search in. If not specified, the current directory will be searched. DO NOT enter "undefined" or "null" - simply omit it for the default behavior. Must be a valid directory path if provided.`)
174965
175009
  }),
174966
175010
  execute: async ({ pattern, path: path14 }) => {
174967
- updateOutput23("toolCall", JSON.stringify({
175011
+ updateOutput24("toolCall", JSON.stringify({
174968
175012
  action: "Searching Files in " + path14,
174969
175013
  details: pattern
174970
175014
  }), { dontOverride: true });
@@ -174983,7 +175027,7 @@ var globTool = tool6({
174983
175027
  break;
174984
175028
  }
174985
175029
  }
174986
- updateOutput23("toolCall", JSON.stringify({
175030
+ updateOutput24("toolCall", JSON.stringify({
174987
175031
  action: "Found " + files.length + " Files",
174988
175032
  details: pattern
174989
175033
  }));
@@ -175030,7 +175074,7 @@ TypeScript, JavaScript, Python, Rust, Go, C/C++, JSON, CSS, HTML
175030
175074
  `;
175031
175075
 
175032
175076
  // src/agent/tools/lspTool.ts
175033
- var { updateOutput: updateOutput24 } = useFraudeStore_default.getState();
175077
+ var { updateOutput: updateOutput25 } = useFraudeStore_default.getState();
175034
175078
  function findSymbolPosition(content, symbolName, occurrence = 1) {
175035
175079
  const lines = content.split(`
175036
175080
  `);
@@ -175318,7 +175362,7 @@ ${lines.join(`
175318
175362
  default:
175319
175363
  return "Unknown command.";
175320
175364
  }
175321
- updateOutput24("toolCall", JSON.stringify({
175365
+ updateOutput25("toolCall", JSON.stringify({
175322
175366
  action: `LSP: ${command3}`,
175323
175367
  details: `${projectPath(filePath)} ${symbol ? `(${symbol})` : ""}`,
175324
175368
  result
@@ -175340,14 +175384,14 @@ var researchSubAgentTool = tool8({
175340
175384
  question: z10.string().describe("The specific question about the code to answer.")
175341
175385
  }),
175342
175386
  execute: async ({ question }) => {
175343
- const { updateOutput: updateOutput25, researchCache } = useFraudeStore_default.getState();
175344
- updateOutput25("toolCall", JSON.stringify({
175387
+ const { updateOutput: updateOutput26, researchCache } = useFraudeStore_default.getState();
175388
+ updateOutput26("toolCall", JSON.stringify({
175345
175389
  action: "Searching for context",
175346
175390
  details: question,
175347
175391
  result: ""
175348
175392
  }), { dontOverride: true });
175349
175393
  if (researchCache?.[question]) {
175350
- updateOutput25("toolCall", JSON.stringify({
175394
+ updateOutput26("toolCall", JSON.stringify({
175351
175395
  action: "Explored context",
175352
175396
  details: question,
175353
175397
  result: researchCache[question]
@@ -175363,7 +175407,7 @@ var researchSubAgentTool = tool8({
175363
175407
  useIsolatedContext: true
175364
175408
  });
175365
175409
  const result = await subagent.chat(question);
175366
- updateOutput25("toolCall", JSON.stringify({
175410
+ updateOutput26("toolCall", JSON.stringify({
175367
175411
  action: "Explored context",
175368
175412
  details: question,
175369
175413
  result: result.text
@@ -175479,7 +175523,7 @@ Usage:
175479
175523
  - Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.`;
175480
175524
 
175481
175525
  // src/agent/tools/writeTool.ts
175482
- var { updateOutput: updateOutput25 } = useFraudeStore_default.getState();
175526
+ var { updateOutput: updateOutput26 } = useFraudeStore_default.getState();
175483
175527
  var writeTool = tool9({
175484
175528
  description: write_default,
175485
175529
  strict: true,
@@ -175490,7 +175534,7 @@ var writeTool = tool9({
175490
175534
  execute: async ({ path: path15, content }) => {
175491
175535
  const change = await pendingChanges_default.addChange(path15, content, "write");
175492
175536
  const stats = pendingChanges_default.getDiffStats(change.diff);
175493
- updateOutput25("toolCall", JSON.stringify({
175537
+ updateOutput26("toolCall", JSON.stringify({
175494
175538
  action: "Created File",
175495
175539
  details: path15,
175496
175540
  result: `(+${stats.added} / -${stats.removed})`
@@ -175515,7 +175559,7 @@ Usage:
175515
175559
  - If removing, new content should be empty.`;
175516
175560
 
175517
175561
  // src/agent/tools/editTool.ts
175518
- var { updateOutput: updateOutput26 } = useFraudeStore_default.getState();
175562
+ var { updateOutput: updateOutput27 } = useFraudeStore_default.getState();
175519
175563
  var editTool = tool10({
175520
175564
  description: edit_default,
175521
175565
  strict: true,
@@ -175532,7 +175576,7 @@ var editTool = tool10({
175532
175576
  const newFileContent = fileContent.replace(old_content, new_content);
175533
175577
  const change = await pendingChanges_default.addChange(path15, newFileContent, "edit");
175534
175578
  const stats = pendingChanges_default.getDiffStats(change.diff);
175535
- updateOutput26("toolCall", JSON.stringify({
175579
+ updateOutput27("toolCall", JSON.stringify({
175536
175580
  action: "Edited File",
175537
175581
  details: path15,
175538
175582
  result: `(+${stats.added} / -${stats.removed})`
@@ -175616,7 +175660,7 @@ init_useFraudeStore();
175616
175660
  import { exec as exec2 } from "child_process";
175617
175661
  import { promisify } from "util";
175618
175662
  var execAsync = promisify(exec2);
175619
- var { updateOutput: updateOutput27 } = useFraudeStore_default.getState();
175663
+ var { updateOutput: updateOutput28 } = useFraudeStore_default.getState();
175620
175664
  var testRunnerTool = tool11({
175621
175665
  description: `Run a shell command to verify the codebase state, INCLUDING pending changes.
175622
175666
  Crucial for testing changes before they are permanently applied.
@@ -175632,7 +175676,7 @@ var testRunnerTool = tool11({
175632
175676
  command: z13.string().describe("The shell command to run (e.g., 'bun test tests/my.test.ts', 'python3 tests/test_app.py')")
175633
175677
  }),
175634
175678
  execute: async ({ command: command3 }) => {
175635
- updateOutput27("toolCall", JSON.stringify({
175679
+ updateOutput28("toolCall", JSON.stringify({
175636
175680
  action: "Running Test on Pending State",
175637
175681
  details: command3,
175638
175682
  result: "..."
@@ -175646,7 +175690,7 @@ ${stdout}
175646
175690
 
175647
175691
  STDERR:
175648
175692
  ${stderr}`;
175649
- updateOutput27("toolCall", JSON.stringify({
175693
+ updateOutput28("toolCall", JSON.stringify({
175650
175694
  action: "Test Result",
175651
175695
  details: command3,
175652
175696
  result: stdout.slice(0, 100) + (stdout.length > 100 ? "..." : "")
@@ -175661,7 +175705,7 @@ ${error.stdout}
175661
175705
 
175662
175706
  STDERR:
175663
175707
  ${error.stderr}`;
175664
- updateOutput27("toolCall", JSON.stringify({
175708
+ updateOutput28("toolCall", JSON.stringify({
175665
175709
  action: "Test Failed",
175666
175710
  details: command3,
175667
175711
  result: "Exit Code: " + error.code
@@ -175679,7 +175723,7 @@ var testRunnerTool_default = testRunnerTool;
175679
175723
  init_useFraudeStore();
175680
175724
  import { tool as tool12 } from "ai";
175681
175725
  import { z as z14 } from "zod";
175682
- var { updateOutput: updateOutput28 } = useFraudeStore_default.getState();
175726
+ var { updateOutput: updateOutput29 } = useFraudeStore_default.getState();
175683
175727
  var testTool = tool12({
175684
175728
  description: `Create OR UPDATE a TEMPORARY test file to verify changes.
175685
175729
  This file will NOT be saved to the project permanently and will NOT appear in the user's pending changes list.
@@ -175694,7 +175738,7 @@ var testTool = tool12({
175694
175738
  const change = await pendingChanges_default.addChange(path15, content, "write", {
175695
175739
  hidden: true
175696
175740
  });
175697
- updateOutput28("toolCall", JSON.stringify({
175741
+ updateOutput29("toolCall", JSON.stringify({
175698
175742
  action: "Created Temporary Test",
175699
175743
  details: path15,
175700
175744
  result: "(Hidden from pending changes)"
@@ -175911,7 +175955,7 @@ function getAskAgent() {
175911
175955
  var askAgent_default = getAskAgent;
175912
175956
 
175913
175957
  // src/utils/queryHandler.ts
175914
- var { updateOutput: updateOutput29 } = useFraudeStore_default.getState();
175958
+ var { updateOutput: updateOutput30 } = useFraudeStore_default.getState();
175915
175959
  var checkAbort = () => {
175916
175960
  const abortController = useFraudeStore_default.getState().abortController;
175917
175961
  if (abortController?.signal.aborted) {
@@ -175962,7 +176006,7 @@ var planMode = async (query) => {
175962
176006
  while (!nextTask.done && nextTask.task) {
175963
176007
  checkAbort();
175964
176008
  let taskContext = getTaskContext(nextTask.task);
175965
- updateOutput29("log", "Working on task: " + nextTask.task.description);
176009
+ updateOutput30("log", "Working on task: " + nextTask.task.description);
175966
176010
  const response2 = await getWorkerSubAgent().stream(taskContext, {
175967
176011
  abortSignal: abortController.signal
175968
176012
  });
@@ -175975,11 +176019,11 @@ var planMode = async (query) => {
175975
176019
  }
175976
176020
  const getUpdatedTask = await getTodoById(nextTask.task.id);
175977
176021
  if (!getUpdatedTask) {
175978
- updateOutput29("error", "Task not found");
176022
+ updateOutput30("error", "Task not found");
175979
176023
  continue;
175980
176024
  }
175981
176025
  taskContext = getTaskContext(getUpdatedTask);
175982
- updateOutput29("log", "Reviewing changes for task: " + nextTask.task.description);
176026
+ updateOutput30("log", "Reviewing changes for task: " + nextTask.task.description);
175983
176027
  const reviewResponse = await getReviewerSubAgent().stream(taskContext, {
175984
176028
  abortSignal: abortController.signal
175985
176029
  });
@@ -176010,7 +176054,7 @@ async function QueryHandler(query) {
176010
176054
  if (query === "exit") {
176011
176055
  process.exit(0);
176012
176056
  }
176013
- updateOutput29("command", query);
176057
+ updateOutput30("command", query);
176014
176058
  if (query.startsWith("/")) {
176015
176059
  useFraudeStore_default.setState({
176016
176060
  status: 2
@@ -176054,9 +176098,9 @@ async function QueryHandler(query) {
176054
176098
  }
176055
176099
  if (pendingChanges_default.hasChanges()) {
176056
176100
  useFraudeStore_default.setState({ status: 3, statusText: "Reviewing Changes" });
176057
- updateOutput29("confirmation", "");
176101
+ updateOutput30("confirmation", "");
176058
176102
  } else {
176059
- updateOutput29("done", `Task Completed in ${(useFraudeStore_default.getState().elapsedTime / 10).toFixed(1)}s`);
176103
+ updateOutput30("done", `Task Completed in ${(useFraudeStore_default.getState().elapsedTime / 10).toFixed(1)}s`);
176060
176104
  }
176061
176105
  } catch (e) {
176062
176106
  if (e?.name === "AbortError" || e?.message === "Aborted") {
@@ -176920,10 +176964,10 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
176920
176964
  return 3;
176921
176965
  }
176922
176966
  if ("TERM_PROGRAM" in env) {
176923
- const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
176967
+ const version2 = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
176924
176968
  switch (env.TERM_PROGRAM) {
176925
176969
  case "iTerm.app": {
176926
- return version >= 3 ? 3 : 2;
176970
+ return version2 >= 3 ? 3 : 2;
176927
176971
  }
176928
176972
  case "Apple_Terminal": {
176929
176973
  return 2;
@@ -178872,10 +178916,7 @@ init_useFraudeStore();
178872
178916
  // src/components/IntroComponent.tsx
178873
178917
  import { Box as Box18, Text as Text18 } from "ink";
178874
178918
  import Gradient from "ink-gradient";
178875
- // package.json
178876
- var version = "0.1.1";
178877
-
178878
- // src/components/IntroComponent.tsx
178919
+ init_useSettingsStore();
178879
178920
  import { jsxDEV as jsxDEV20 } from "react/jsx-dev-runtime";
178880
178921
  var INTRO_THEME = {
178881
178922
  primary: "#FFB6C1",
@@ -178978,7 +179019,35 @@ function IntroComponent() {
178978
179019
  "to start your journey..."
178979
179020
  ]
178980
179021
  }, undefined, true, undefined, this)
178981
- }, undefined, false, undefined, this)
179022
+ }, undefined, false, undefined, this),
179023
+ useSettingsStore_default((state2) => state2.updateAvailable) && /* @__PURE__ */ jsxDEV20(Box18, {
179024
+ marginTop: 1,
179025
+ borderStyle: "round",
179026
+ borderColor: "yellow",
179027
+ paddingX: 1,
179028
+ children: [
179029
+ /* @__PURE__ */ jsxDEV20(Text18, {
179030
+ color: "yellow",
179031
+ children: [
179032
+ "Update available!",
179033
+ " ",
179034
+ /* @__PURE__ */ jsxDEV20(Text18, {
179035
+ bold: true,
179036
+ children: [
179037
+ "v",
179038
+ useSettingsStore_default((state2) => state2.updateAvailable)
179039
+ ]
179040
+ }, undefined, true, undefined, this),
179041
+ " ",
179042
+ "is now live."
179043
+ ]
179044
+ }, undefined, true, undefined, this),
179045
+ /* @__PURE__ */ jsxDEV20(Text18, {
179046
+ dimColor: true,
179047
+ children: " Run `npm install -g fraude-code` to update."
179048
+ }, undefined, false, undefined, this)
179049
+ ]
179050
+ }, undefined, true, undefined, this)
178982
179051
  ]
178983
179052
  }, undefined, true, undefined, this)
178984
179053
  ]
@@ -179047,7 +179116,7 @@ var LoaderComponent_default = LoaderComponent;
179047
179116
  // src/components/App.tsx
179048
179117
  import { jsxDEV as jsxDEV22 } from "react/jsx-dev-runtime";
179049
179118
  function App() {
179050
- const { status, started, updateOutput: updateOutput30 } = useFraudeStore_default();
179119
+ const { status, started, updateOutput: updateOutput31 } = useFraudeStore_default();
179051
179120
  const [exitPending, setExitPending] = useState5(false);
179052
179121
  useInput3((input, key) => {
179053
179122
  if (key.return && !started) {
@@ -179066,7 +179135,7 @@ function App() {
179066
179135
  if (key.escape && status === 1) {
179067
179136
  useFraudeStore_default.getState().interruptAgent();
179068
179137
  logger_default("User pressed escape - interrupting agent");
179069
- updateOutput30("interrupted", (useFraudeStore_default.getState().elapsedTime / 10).toFixed(1));
179138
+ updateOutput31("interrupted", (useFraudeStore_default.getState().elapsedTime / 10).toFixed(1));
179070
179139
  }
179071
179140
  });
179072
179141
  return !started ? /* @__PURE__ */ jsxDEV22(IntroComponent, {}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV22(Box20, {
@@ -179297,6 +179366,11 @@ async function main() {
179297
179366
  await commands_default.loadPlugins();
179298
179367
  syncModels();
179299
179368
  const { waitUntilExit } = render(/* @__PURE__ */ jsxDEV23(App, {}, undefined, false, undefined, this), { exitOnCtrlC: false });
179369
+ checkForUpdate(version).then((newVersion) => {
179370
+ if (newVersion) {
179371
+ useSettingsStore_default.getState().setUpdateAvailable(newVersion);
179372
+ }
179373
+ });
179300
179374
  getKnowledgeOrchestrator().indexProject(process.cwd()).catch((e) => logger_default(`Background indexing failed: ${e}`));
179301
179375
  const exitHandler = () => {
179302
179376
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraude-code",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "FraudeCode - An AI coding agent powered by Graph + Vector memory",
5
5
  "author": "Matthew Branning",
6
6
  "license": "MIT",
@@ -33,7 +33,8 @@
33
33
  "devDependencies": {
34
34
  "@types/bun": "latest",
35
35
  "@types/diff": "^8.0.0",
36
- "@types/react": "^19.2.7"
36
+ "@types/react": "^19.2.7",
37
+ "@types/semver": "^7.7.1"
37
38
  },
38
39
  "peerDependencies": {
39
40
  "typescript": "^5"
@@ -67,6 +68,7 @@
67
68
  "react": "^19.2.4",
68
69
  "react-devtools-core": "^7.0.1",
69
70
  "react-dom": "^19.2.4",
71
+ "semver": "^7.7.4",
70
72
  "vscode-jsonrpc": "^8.2.1",
71
73
  "vscode-languageserver-protocol": "^3.17.5",
72
74
  "vscode-languageserver-types": "^3.17.5",
@@ -15,8 +15,10 @@ import rememberCommand from "./remember";
15
15
  import knowledgeCommand from "./knowledge";
16
16
  import forgetCommand from "./forget";
17
17
  import visualizeCommand from "./visualize";
18
+ import updateCommand from "./update";
18
19
 
19
20
  const COMMANDS: Command[] = [
21
+ updateCommand,
20
22
  usageCommand,
21
23
  logCommand,
22
24
  serveCommand,
@@ -0,0 +1,36 @@
1
+ import type { Command } from "@/types/CommandDefinition";
2
+ import { version } from "../../package.json";
3
+ import { checkForUpdate } from "../utils/updateCheck";
4
+ import useFraudeStore from "@/store/useFraudeStore";
5
+ const { updateOutput } = useFraudeStore.getState();
6
+
7
+ const updateCommand: Command = {
8
+ name: "update",
9
+ description: "Check for the latest version of FraudeCode",
10
+ usage: "/update",
11
+ action: async () => {
12
+ updateOutput("log", "Checking for updates...");
13
+ const latest = await checkForUpdate(version);
14
+
15
+ if (!latest) {
16
+ updateOutput(
17
+ "log",
18
+ "🎉 You are already using the latest version of FraudeCode (v" +
19
+ version +
20
+ ").",
21
+ );
22
+ return;
23
+ }
24
+
25
+ updateOutput(
26
+ "log",
27
+ "🚀 A new version of FraudeCode is available: v" + latest,
28
+ );
29
+ updateOutput("log", "-----------------------------------------");
30
+ updateOutput("log", "To update to the latest version, run:");
31
+ updateOutput("log", " npm install -g fraude-code");
32
+ updateOutput("log", "-----------------------------------------");
33
+ },
34
+ };
35
+
36
+ export default updateCommand;
@@ -1,6 +1,7 @@
1
1
  import { Box, Text } from "ink";
2
2
  import Gradient from "ink-gradient";
3
3
  import { version } from "../../package.json";
4
+ import useSettingsStore from "../store/useSettingsStore";
4
5
 
5
6
  const INTRO_THEME = {
6
7
  primary: "#FFB6C1",
@@ -66,6 +67,23 @@ export default function IntroComponent() {
66
67
  to start your journey...
67
68
  </Text>
68
69
  </Box>
70
+ {useSettingsStore((state) => state.updateAvailable) && (
71
+ <Box
72
+ marginTop={1}
73
+ borderStyle="round"
74
+ borderColor="yellow"
75
+ paddingX={1}
76
+ >
77
+ <Text color="yellow">
78
+ Update available!{" "}
79
+ <Text bold>
80
+ v{useSettingsStore((state) => state.updateAvailable)}
81
+ </Text>{" "}
82
+ is now live.
83
+ </Text>
84
+ <Text dimColor> Run `npm install -g fraude-code` to update.</Text>
85
+ </Box>
86
+ )}
69
87
  </Box>
70
88
  </Box>
71
89
  );
package/src/index.tsx CHANGED
@@ -11,6 +11,8 @@ import CerebrasClient from "@/services/cerebras";
11
11
  import GoogleClient from "@/services/google";
12
12
  import CommandCenter from "@/commands";
13
13
  import { getKnowledgeOrchestrator } from "@/services/knowledgeOrchestrator";
14
+ import { checkForUpdate } from "./utils/updateCheck";
15
+ import { version } from "../package.json";
14
16
 
15
17
  // Global error handlers to catch and suppress AbortErrors
16
18
  process.on("unhandledRejection", (reason) => {
@@ -81,6 +83,13 @@ async function main() {
81
83
  syncModels();
82
84
  const { waitUntilExit } = render(<App />, { exitOnCtrlC: false });
83
85
 
86
+ // Asynchronous update check
87
+ checkForUpdate(version).then((newVersion) => {
88
+ if (newVersion) {
89
+ useSettingsStore.getState().setUpdateAvailable(newVersion);
90
+ }
91
+ });
92
+
84
93
  // Background index the project (fire-and-forget)
85
94
  getKnowledgeOrchestrator()
86
95
  .indexProject(process.cwd())
@@ -5,15 +5,18 @@ import { SettingsSchema, type Config } from "../config/schema";
5
5
  interface SettingsActions {
6
6
  setOllamaUrl: (url: string) => void;
7
7
  syncWithSettings: () => void;
8
+ setUpdateAvailable: (version: string | null) => void;
8
9
  }
9
10
 
10
- type SettingsState = Config & SettingsActions;
11
+ type SettingsState = Config &
12
+ SettingsActions & { updateAvailable: string | null };
11
13
 
12
14
  const DEFAULTS = SettingsSchema.parse({});
13
15
 
14
16
  const useSettingsStore = create<SettingsState>()((set) => {
15
17
  return {
16
18
  ...DEFAULTS,
19
+ updateAvailable: null,
17
20
 
18
21
  setOllamaUrl: (url) => {
19
22
  try {
@@ -32,6 +35,7 @@ const useSettingsStore = create<SettingsState>()((set) => {
32
35
  console.error("Failed to sync settings:", e);
33
36
  }
34
37
  },
38
+ setUpdateAvailable: (version) => set({ updateAvailable: version }),
35
39
  } as SettingsState;
36
40
  });
37
41
 
@@ -0,0 +1,20 @@
1
+ import semver from "semver";
2
+ export async function checkForUpdate(
3
+ currentVersion: string,
4
+ ): Promise<string | null> {
5
+ try {
6
+ const response = await fetch(
7
+ "https://registry.npmjs.org/fraude-code/latest",
8
+ );
9
+ if (!response.ok) return null;
10
+ const data = (await response.json()) as { version: string };
11
+ const latestVersion = data.version;
12
+
13
+ if (semver.gt(latestVersion, currentVersion)) {
14
+ return latestVersion;
15
+ }
16
+ } catch (error) {
17
+ // Silently fail to not disrupt user experience
18
+ }
19
+ return null;
20
+ }