jawere 1.0.13 → 1.5.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/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
1
2
  var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -13338,6 +13339,30 @@ var TOOL_DEFS = [
13338
13339
  }
13339
13340
  }
13340
13341
  },
13342
+ {
13343
+ type: "function",
13344
+ function: {
13345
+ name: "diff",
13346
+ description: "Show a git diff of changes. Useful for reviewing what changed before committing. Supports --staged for staged changes, and optional path/file and base ref.",
13347
+ parameters: {
13348
+ type: "object",
13349
+ properties: {
13350
+ path: {
13351
+ type: "string",
13352
+ description: "Specific file or directory to diff (optional \u2014 diffs everything if omitted)"
13353
+ },
13354
+ staged: {
13355
+ type: "boolean",
13356
+ description: "Show staged changes (git diff --staged). Default: false (working tree)"
13357
+ },
13358
+ base: {
13359
+ type: "string",
13360
+ description: "Base ref to diff against (e.g. HEAD~1, main). Default: HEAD for staged, working tree otherwise"
13361
+ }
13362
+ }
13363
+ }
13364
+ }
13365
+ },
13341
13366
  {
13342
13367
  type: "function",
13343
13368
  function: {
@@ -13756,6 +13781,14 @@ async function statTool(path, workDir) {
13756
13781
  return `Error: [stat-failed] ${fullPath}: ${err.message}`;
13757
13782
  }
13758
13783
  }
13784
+ async function diffTool(path, staged, base, workDir) {
13785
+ const args = ["diff"];
13786
+ if (staged) args.push("--staged");
13787
+ if (base) args.push(base);
13788
+ if (path) args.push("--", path);
13789
+ else args.push("--", ".");
13790
+ return execBash(`git ${args.join(" ")}`, workDir, 30);
13791
+ }
13759
13792
  async function executeTool(call, workDir) {
13760
13793
  const { id, function: fn } = call;
13761
13794
  let args;
@@ -13798,6 +13831,14 @@ async function executeTool(call, workDir) {
13798
13831
  case "find":
13799
13832
  result = await findTool(args.pattern, args.path, workDir);
13800
13833
  break;
13834
+ case "diff":
13835
+ result = await diffTool(
13836
+ args.path,
13837
+ args.staged,
13838
+ args.base,
13839
+ workDir
13840
+ );
13841
+ break;
13801
13842
  case "grep":
13802
13843
  result = await grepTool(
13803
13844
  args.pattern,
@@ -14264,13 +14305,14 @@ async function listSessions(convexUrl) {
14264
14305
 
14265
14306
  // src/agent.ts
14266
14307
  var MAX_TURNS = 500;
14267
- var MAX_OUTPUT_TOKENS = 3e5;
14308
+ var MAX_OUTPUT_TOKENS = 393216;
14268
14309
  var COL = () => process.stdout.columns || 80;
14269
14310
  var GRUVBOX_GREEN = "\x1B[38;2;184;187;3m";
14270
14311
  var GRUVBOX_GRAY = "\x1B[38;2;146;131;116m";
14271
14312
  var GRUVBOX_RED = "\x1B[38;2;251;73;52m";
14272
14313
  var RESET2 = "\x1B[0m";
14273
14314
  var FILE_TOOLS = /* @__PURE__ */ new Set(["read", "write", "edit", "stat"]);
14315
+ var SILENT_TOOLS = /* @__PURE__ */ new Set(["diff"]);
14274
14316
  function toolDetail(name, args) {
14275
14317
  switch (name) {
14276
14318
  case "read": {
@@ -14295,6 +14337,7 @@ function toolDetail(name, args) {
14295
14337
  }
14296
14338
  }
14297
14339
  function displayToolLine(name, args, ok, spin) {
14340
+ if (SILENT_TOOLS.has(name)) return;
14298
14341
  if (spin?.running) {
14299
14342
  spin.stop();
14300
14343
  }
@@ -14462,86 +14505,120 @@ async function runAgent(userMessage, options = {}) {
14462
14505
  return { text: msg2, sessionId, history: messages };
14463
14506
  }
14464
14507
  spin.update("Thinking\u2026");
14465
- const response = await withRetry(() => {
14466
- return client.chat.completions.create({
14467
- model: config.model,
14468
- messages,
14469
- tools: TOOL_DEFS,
14470
- tool_choice: "auto",
14471
- temperature: 0.2,
14472
- max_tokens: MAX_OUTPUT_TOKENS,
14473
- // Enable thinking/reasoning — DeepSeek specific params
14474
- ...{
14475
- thinking: { type: "enabled" },
14476
- reasoning_effort: "max"
14477
- }
14508
+ let streamedContent = "";
14509
+ const streamedToolCalls = /* @__PURE__ */ new Map();
14510
+ let streamUsage;
14511
+ try {
14512
+ const stream = await withRetry(async () => {
14513
+ return client.chat.completions.create({
14514
+ model: config.model,
14515
+ messages,
14516
+ tools: TOOL_DEFS,
14517
+ tool_choice: "auto",
14518
+ temperature: 0.2,
14519
+ max_tokens: MAX_OUTPUT_TOKENS,
14520
+ stream: true,
14521
+ stream_options: { include_usage: true },
14522
+ // Enable thinking/reasoning — DeepSeek specific params
14523
+ ...{
14524
+ thinking: { type: "enabled" },
14525
+ reasoning_effort: "max"
14526
+ }
14527
+ });
14478
14528
  });
14479
- }).catch((err) => {
14529
+ for await (const chunk of stream) {
14530
+ if (options.signal?.aborted) break;
14531
+ const delta = chunk.choices?.[0]?.delta;
14532
+ if (!delta) {
14533
+ if (chunk.usage) {
14534
+ streamUsage = {
14535
+ input: chunk.usage.prompt_tokens || 0,
14536
+ output: chunk.usage.completion_tokens || 0,
14537
+ total: chunk.usage.total_tokens || 0
14538
+ };
14539
+ }
14540
+ continue;
14541
+ }
14542
+ if (delta.content) {
14543
+ streamedContent += delta.content;
14544
+ }
14545
+ if (delta.tool_calls) {
14546
+ for (const tc of delta.tool_calls) {
14547
+ const idx = tc.index ?? 0;
14548
+ if (!streamedToolCalls.has(idx)) {
14549
+ streamedToolCalls.set(idx, { id: "", name: "", arguments: "" });
14550
+ }
14551
+ const entry = streamedToolCalls.get(idx);
14552
+ if (tc.id) entry.id = tc.id;
14553
+ if (tc.function?.name) entry.name += tc.function.name;
14554
+ if (tc.function?.arguments) entry.arguments += tc.function.arguments;
14555
+ }
14556
+ }
14557
+ spin.update("Thinking\u2026");
14558
+ }
14559
+ } catch (err) {
14480
14560
  if (err.name === "AbortError" || err.name === "Canceled") {
14481
- return null;
14561
+ spin.stop();
14562
+ const msg2 = "\n[Cancelled by user]";
14563
+ return { text: msg2, sessionId, history: messages };
14482
14564
  }
14483
14565
  throw err;
14484
- });
14485
- if (!response) {
14566
+ }
14567
+ if (options.signal?.aborted) {
14486
14568
  spin.stop();
14487
14569
  const msg2 = "\n[Cancelled by user]";
14488
14570
  return { text: msg2, sessionId, history: messages };
14489
14571
  }
14490
- const choice = response.choices[0];
14491
- if (!choice) {
14492
- spin.stop();
14493
- safeCall(
14494
- () => appendAssistantMessage(config.convexUrl, sessionId, "(error: no response)", null),
14495
- "appendAssistantMessage"
14496
- );
14497
- return { text: "Error: No response from model.", sessionId, history: messages };
14498
- }
14499
- const { message } = choice;
14500
- const usage = response.usage ? {
14501
- input: response.usage.prompt_tokens || 0,
14502
- output: response.usage.completion_tokens || 0,
14503
- total: response.usage.total_tokens || 0
14504
- } : void 0;
14505
- if (message.tool_calls && message.tool_calls.length > 0) {
14506
- const toolCallsMeta = message.tool_calls.map((tc) => ({
14572
+ const hasToolCalls = streamedToolCalls.size > 0;
14573
+ if (hasToolCalls) {
14574
+ const toolCallArray = Array.from(streamedToolCalls.entries()).sort(([a2], [b2]) => a2 - b2).map(([_2, tc]) => tc);
14575
+ const toolCallsMeta = toolCallArray.map((tc) => ({
14507
14576
  id: tc.id,
14508
- name: tc.function.name,
14509
- arguments: tc.function.arguments
14577
+ name: tc.name,
14578
+ arguments: tc.arguments
14510
14579
  }));
14511
14580
  safeCall(
14512
14581
  () => appendAssistantMessage(
14513
14582
  config.convexUrl,
14514
14583
  sessionId,
14515
- message.content || null,
14584
+ streamedContent || null,
14516
14585
  toolCallsMeta.length > 0 ? toolCallsMeta : null,
14517
- usage
14586
+ streamUsage
14518
14587
  ),
14519
14588
  "appendAssistantMessage"
14520
14589
  );
14590
+ const openaiToolCalls = toolCallArray.map((tc) => ({
14591
+ id: tc.id,
14592
+ type: "function",
14593
+ function: {
14594
+ name: tc.name,
14595
+ arguments: tc.arguments
14596
+ }
14597
+ }));
14521
14598
  messages.push({
14522
14599
  role: "assistant",
14523
- content: message.content || null,
14524
- tool_calls: message.tool_calls
14600
+ content: streamedContent || null,
14601
+ tool_calls: openaiToolCalls
14525
14602
  });
14526
- for (const tc of message.tool_calls) {
14603
+ for (const tc of toolCallArray) {
14527
14604
  let args = {};
14528
14605
  try {
14529
- args = JSON.parse(tc.function.arguments);
14606
+ args = JSON.parse(tc.arguments);
14530
14607
  } catch {
14531
14608
  }
14532
- spin.update(`Running ${tc.function.name}\u2026`);
14609
+ spin.update(`Running ${tc.name}\u2026`);
14533
14610
  const result = await executeTool(
14534
14611
  {
14535
14612
  id: tc.id,
14536
14613
  function: {
14537
- name: tc.function.name,
14538
- arguments: tc.function.arguments
14614
+ name: tc.name,
14615
+ arguments: tc.arguments
14539
14616
  }
14540
14617
  },
14541
14618
  config.workDir
14542
14619
  );
14543
14620
  const ok = !result.content.startsWith("Error");
14544
- displayToolLine(tc.function.name, args, ok, spin);
14621
+ displayToolLine(tc.name, args, ok, spin);
14545
14622
  messages.push({
14546
14623
  role: "tool",
14547
14624
  tool_call_id: result.tool_call_id,
@@ -14553,7 +14630,7 @@ async function runAgent(userMessage, options = {}) {
14553
14630
  config.convexUrl,
14554
14631
  sessionId,
14555
14632
  result.tool_call_id,
14556
- tc.function.name,
14633
+ tc.name,
14557
14634
  result.content,
14558
14635
  isError
14559
14636
  ),
@@ -14563,10 +14640,9 @@ async function runAgent(userMessage, options = {}) {
14563
14640
  continue;
14564
14641
  }
14565
14642
  spin.stop();
14566
- const rawText = message.content || "";
14567
- const text = stripThinking(rawText) || "(empty response)";
14643
+ const text = stripThinking(streamedContent) || "(empty response)";
14568
14644
  safeCall(
14569
- () => appendAssistantMessage(config.convexUrl, sessionId, text, null, usage),
14645
+ () => appendAssistantMessage(config.convexUrl, sessionId, text, null, streamUsage),
14570
14646
  "appendAssistantMessage"
14571
14647
  );
14572
14648
  console.log("");
@@ -14870,20 +14946,32 @@ function getProjectInfo(workDir) {
14870
14946
  }
14871
14947
  return { name, version };
14872
14948
  }
14873
- async function scanCodebase(workDir) {
14949
+ async function scanCodebase(workDir, onProgress) {
14874
14950
  const codebaseDir = resolve2(workDir, CODEBASE_DIR);
14875
14951
  await mkdir3(codebaseDir, { recursive: true });
14952
+ onProgress?.("listing files");
14876
14953
  const allFiles = await getAllFiles(workDir, workDir);
14877
14954
  const files = allFiles.sort();
14955
+ onProgress?.(`found ${files.length} files`);
14956
+ onProgress?.("building tree");
14878
14957
  const tree = buildTree(files);
14958
+ onProgress?.(`summarizing ${files.length} files`);
14879
14959
  const summaries = {};
14880
- for (const file of files) {
14881
- const summary = await summarizeFile(resolve2(workDir, file));
14960
+ let lastReport = 0;
14961
+ for (let i2 = 0; i2 < files.length; i2++) {
14962
+ const summary = await summarizeFile(resolve2(workDir, files[i2]));
14882
14963
  if (summary) {
14883
- summaries[file] = summary;
14964
+ summaries[files[i2]] = summary;
14965
+ }
14966
+ const now = Date.now();
14967
+ if (i2 - lastReport >= 10 || now - lastReport > 200) {
14968
+ onProgress?.(`summarizing ${i2 + 1}/${files.length}`);
14969
+ lastReport = i2;
14884
14970
  }
14885
14971
  }
14972
+ onProgress?.("reading project info");
14886
14973
  const { name, version } = getProjectInfo(workDir);
14974
+ onProgress?.("hashing files");
14887
14975
  const gitHash = await getGitHash(workDir);
14888
14976
  const checksums = {
14889
14977
  scannedAt: Date.now(),
@@ -14901,9 +14989,12 @@ async function scanCodebase(workDir) {
14901
14989
  }
14902
14990
  }
14903
14991
  }
14992
+ onProgress?.("writing checksums");
14904
14993
  await writeFile3(resolve2(workDir, CHECKSUMS_FILE), JSON.stringify(checksums, null, 2) + "\n", "utf-8");
14994
+ onProgress?.("writing tree.yaml");
14905
14995
  const treeYaml = generateTreeYaml(name, version, tree, summaries);
14906
14996
  await writeFile3(resolve2(workDir, TREE_FILE), treeYaml, "utf-8");
14997
+ onProgress?.("writing meta.json");
14907
14998
  const meta = {
14908
14999
  scannedAt: Date.now(),
14909
15000
  fileCount: files.length,
@@ -14991,11 +15082,18 @@ async function runScanner(workDir, force = false) {
14991
15082
  const meta = JSON.parse(
14992
15083
  await readFile3(resolve2(workDir, META_FILE), "utf-8")
14993
15084
  );
15085
+ process.stderr.write(` (cached)`);
14994
15086
  return { fileCount: meta.fileCount, cached: true };
14995
15087
  } catch {
14996
15088
  }
14997
15089
  }
14998
- const { fileCount } = await scanCodebase(workDir);
15090
+ const GRAY2 = "\x1B[38;2;146;131;116m";
15091
+ const RESET3 = "\x1B[0m";
15092
+ const { fileCount } = await scanCodebase(workDir, (phase, detail) => {
15093
+ const msg = detail || phase;
15094
+ process.stderr.write(`\r\x1B[K${GRAY2} scanning: ${msg}${RESET3}`);
15095
+ });
15096
+ process.stderr.write(`\r\x1B[K`);
14999
15097
  return { fileCount, cached: false };
15000
15098
  }
15001
15099
 
@@ -15350,10 +15448,10 @@ async function main() {
15350
15448
  try {
15351
15449
  const scanResult = await runScanner(config.workDir);
15352
15450
  if (scanResult.cached) {
15353
- process.stderr.write(` ${G_GRAY2}(cached, ${scanResult.fileCount} files)${R3}
15451
+ process.stderr.write(` ${G_GRAY2}(${scanResult.fileCount} files)${R3}
15354
15452
  `);
15355
15453
  } else {
15356
- process.stderr.write(` ${G_GRAY2}(${scanResult.fileCount} files)${R3}
15454
+ process.stderr.write(`${G_GRAY2}Done \u2014 ${scanResult.fileCount} files indexed${R3}
15357
15455
  `);
15358
15456
  }
15359
15457
  } catch (err) {
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "jawere",
3
- "version": "1.0.13",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "bin": {
6
- "jawere": "./bin/jawere.js"
6
+ "jawere": "bin/jawere.js"
7
7
  },
8
8
  "files": [
9
9
  "bin",
package/dist/agent.d.ts DELETED
@@ -1,15 +0,0 @@
1
- import type { ChatCompletionMessageParam } from 'openai/resources/chat/completions';
2
- export interface AgentOptions {
3
- sessionId?: string;
4
- title?: string;
5
- history?: ChatCompletionMessageParam[];
6
- signal?: AbortSignal;
7
- }
8
- export interface AgentResult {
9
- text: string;
10
- sessionId: string;
11
- history: ChatCompletionMessageParam[];
12
- }
13
- /** Print the assistant's FINAL response — the summary shown when all work is done. */
14
- export declare function printAssistantResponse(text: string): void;
15
- export declare function runAgent(userMessage: string, options?: AgentOptions): Promise<AgentResult>;